Legacy Site Redirects Filter
If your legacy URLs patterns are too complex for a Vanity Redirect, it may be simpler to handle these redirects in a Filter.
The logic to process these legacy URLs redirects will be different for every project, but here’s an example to get you started.
This one handles two different use cases, which is not at all uncommon when migrating from a legacy site.
Note: This code is an example only, and will need customization by a developer before it is useful in a project.
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.psddev.cms.db.Content;
import com.psddev.cms.db.PageFilter;
import com.psddev.dari.db.Predicate;
import com.psddev.dari.db.PredicateParser;
import com.psddev.dari.db.Query;
import com.psddev.dari.util.AbstractFilter;
/**
* Redirect Legacy URLs to the appropriate permalink.
*/
public class LegacyUrlRedirectFilter extends AbstractFilter implements AbstractFilter.Auto {
// Legacy URL was /story/normalized-headline/123456
// "/story" is the prefix
// "normalized-headline" can be ignored
// "123456" has been saved on every migrated object using the field "migration.legacyId" with a prefix of "story:"
private static final Pattern LEGACY_STORY_URI_PATTERN = Pattern.compile("^/story/[^/]+/(.*)$");
private static final String STORY_ID_PREFIX = "story:";
// Legacy section path was /section.php?id=789
// "789" has been saved on every migrated object using the field "migration.legacyId" with a prefix of "section:"
private static final String LEGACY_SECTION_PATH = "/section.php";
private static final String LEGACY_SECTION_ID_PARAMETER = "id";
private static final String SECTION_ID_PREFIX = "section:";
private static final String LEGACY_ID_FIELD = "migration.legacyId";
// Helper method to build legacy story ID predicate
private static Predicate legacyStoryIdPredicate(String storyId) {
return PredicateParser.Static.parse(LEGACY_ID_FIELD + " = ?", STORY_ID_PREFIX + storyId);
}
// Helper method to build legacy section ID predicate
private static Predicate legacySectionIdPredicate(String sectionId) {
return PredicateParser.Static.parse(LEGACY_ID_FIELD + " = ?", SECTION_ID_PREFIX + sectionId);
}
// Helper method to redirect
private static void sendRedirect(HttpServletResponse response, String location) {
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", location);
}
// This should run *before* PageFilter.
@Override
public void updateDependencies(Class<? extends AbstractFilter> filterClass, List<Class<? extends Filter>> dependencies) {
if (PageFilter.class == filterClass) {
dependencies.add(LegacyUrlRedirectFilter.class);
}
}
@Override
protected void doRequest(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws Exception {
Object mainObject = PageFilter.Static.getMainObject(request);
// Only run if a mainObject cannot be found
if (mainObject == null) {
String requestPath = request.getRequestURI();
// Attempt to match the legacy section path
if (LEGACY_SECTION_PATH.equals(requestPath)) {
String legacySectionId = request.getParameter(LEGACY_SECTION_ID_PARAMETER);
if (legacySectionId != null && !legacySectionId.isEmpty()) {
Content content = Query.from(Content.class)
.where(legacySectionIdPredicate(legacySectionId))
.first();
if (content != null) {
sendRedirect(response, content.getPermalink());
return;
}
}
}
// Attempt to match the legacy story path and extract the story ID from the path
Matcher storyPathMatcher = LEGACY_STORY_URI_PATTERN.matcher(requestPath);
if (storyPathMatcher.matches()) {
String legacyStoryId = storyPathMatcher.group(1);
if (legacyStoryId != null && !legacyStoryId.isEmpty()) {
Content content = Query.from(Content.class)
.where(legacyStoryIdPredicate(legacyStoryId))
.first();
if (content != null) {
sendRedirect(response, content.getPermalink());
return;
}
}
}
}
// Otherwise, continue processing the request.
chain.doFilter(request, response);
}
}