libreccm-legacy/ccm-navigation/src/com/arsdigita/navigation/NavigationFileResolver.java

459 lines
17 KiB
Java
Executable File

/*
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.arsdigita.navigation;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import com.arsdigita.categorization.Category;
import com.arsdigita.categorization.CategoryCollection;
import com.arsdigita.categorization.CategoryLocalizationCollection;
import com.arsdigita.cms.CMSConfig;
import com.arsdigita.cms.TemplateContext;
import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.domain.DomainCollection;
import com.arsdigita.london.terms.Domain;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.SessionManager;
import com.arsdigita.kernel.Kernel;
import com.arsdigita.util.Assert;
import com.arsdigita.util.StringUtils;
import com.arsdigita.web.Application;
import com.arsdigita.web.DefaultApplicationFileResolver;
import com.arsdigita.web.Web;
import com.arsdigita.globalization.GlobalizationHelper;
import com.arsdigita.kernel.KernelConfig;
import java.io.IOException;
/**
* Manages the processing of URLs in the Navigation application.
*
*/
public class NavigationFileResolver extends DefaultApplicationFileResolver {
private static final Logger s_log = Logger.getLogger(
NavigationFileResolver.class);
private static final String CATEGORY_PATH_ATTR
= NavigationFileResolver.class
+ ".categoryPath";
// path is set in a cookie, which navigation models may use if they wish
public static final String PATH_COOKIE_NAME = "ad_path";
public static final char PATH_COOKIE_SEPARATOR = '|';
@Override
public RequestDispatcher resolve(String templatePath,
HttpServletRequest sreq,
HttpServletResponse sresp,
Application app) {
String path = sreq.getPathInfo();
if (s_log.isDebugEnabled()) {
s_log.debug("Resolving " + path);
}
if (CMSConfig.getInstanceOf().getUseLanguageExtension()
&& path.matches("(.*)/index\\.[a-zA-Z]{2}")) {
final String lang = path.substring(path.length() - 2);
path = path.substring(0, path.length() - "index.$$".length());
GlobalizationHelper.setSelectedLocale(lang);
} else {
String lang;
if (GlobalizationHelper.getSelectedLocale(sreq) == null) {
lang = GlobalizationHelper.getNegotiatedLocale().getLanguage();
} else {
lang = GlobalizationHelper.getSelectedLocale(sreq).getLanguage();
}
final StringBuffer redirectTo = new StringBuffer();
redirectTo
.append(DispatcherHelper.getRequest().getScheme())
.append("://")
.append(DispatcherHelper.getRequest().getServerName());
if (DispatcherHelper.getRequest().getServerPort() != 80
&& DispatcherHelper.getRequest().getServerPort() != 443) {
redirectTo
.append(":")
.append(DispatcherHelper.getRequest().getServerPort());
}
if (DispatcherHelper.getWebappContext() != null
&& !DispatcherHelper.getWebappContext().trim().isEmpty()) {
redirectTo.append(DispatcherHelper.getWebappContext());
}
//Is category available in lang? If not change lang to default language
final Category[] cats = resolvePath(getRootCategory(), path);
if (cats == null) {
lang = KernelConfig.getConfig().getDefaultLanguage();
} else {
final CategoryLocalizationCollection langs = cats[cats.length
- 1]
.getCategoryLocalizationCollection();
if (!langs.localizationExists(lang)) {
lang = KernelConfig.getConfig().getDefaultLanguage();
}
}
redirectTo
.append("/ccm")
.append(app.getPath())
.append(path)
.append("index.")
.append(lang);
sresp.setHeader("Location", redirectTo.toString());
try {
sresp.sendError(HttpServletResponse.SC_MOVED_PERMANENTLY);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return null;
}
if (path.equals("/category.jsp")) {
Navigation nav = (Navigation) Web.getWebContext().getApplication();
String id = sreq.getParameter("categoryID");
if (s_log.isDebugEnabled()) {
s_log.debug("dispatching to category " + id);
}
String useContext = sreq.getParameter("useContext");
if (s_log.isDebugEnabled()) {
s_log.debug("Using use context " + useContext);
}
if (null == useContext) {
useContext = Template.DEFAULT_USE_CONTEXT;
}
Category cat = null;
// if the category doesn't exist, send a 404, nicer
try {
cat = new Category(new BigDecimal(id));
} catch (Exception e) {
s_log.warn("Could not load category for id " + id);
return null;
}
// check that the category is enabled, otherwise 404
if (!cat.isEnabled()) {
s_log.warn("Category is disabled.");
return null;
}
// check that the category is in the tree of categories
Category root = null;
DataCollection objs = SessionManager.getSession()
.retrieve(Domain.BASE_DATA_OBJECT_TYPE);
objs.addEqualsFilter("model.ownerUseContext.categoryOwner.id", nav
.getID());
String dispatcherContext = null;
TemplateContext tc = Navigation.getContext().getTemplateContext();
if (tc != null) {
dispatcherContext = tc.getContext();
}
objs.addEqualsFilter("model.ownerUseContext.useContext",
dispatcherContext);
DomainCollection domains = new DomainCollection(objs);
if (domains.next()) {
root = ((Domain) domains.getDomainObject()).getModel();
} else {
// can't find domain, 404
s_log.warn("Category domain does not exist.");
return null;
}
domains.close();
if (root == null) {
// no root category, 404
s_log.warn("Category domain does not have a root category.");
return null;
}
if (!root.isMemberOfSubtree(cat)) {
// it is not in the tree, send 404
s_log.warn("Category doesn't belong to navigation tree.");
return null;
}
CategoryCollection parents = cat.getDefaultAscendants();
parents.addOrder(Category.DEFAULT_ANCESTORS);
List cats = new ArrayList();
while (parents.next()) {
cats.add(parents.getCategory());
}
Category[] catsArray = (Category[]) cats.toArray(new Category[cats
.size()]);
sreq.setAttribute(CATEGORY_PATH_ATTR,
catsArray);
setPathCookie(sresp, catsArray);
return resolveTemplate(catsArray[catsArray.length - 1], useContext);
} else {
String useContext = Template.DEFAULT_USE_CONTEXT;
if (path.endsWith(".jsp")) {
int lastSlash = path.lastIndexOf('/');
useContext = path.substring(lastSlash + 1, path.length() - 4);
path = path.substring(0, lastSlash);
if (s_log.isDebugEnabled()) {
s_log.debug("useContext=" + useContext + ",path=" + path);
}
}
Category root = getRootCategory();
Category[] cats = resolvePath(root, path);
if (cats == null || cats.length == 0) {
if (s_log.isDebugEnabled()) {
s_log.debug("No category found");
}
sreq.setAttribute(CATEGORY_PATH_ATTR,
new Category[]{root});
setPathCookie(sresp, new Category[]{root});
return super.resolve(templatePath, sreq, sresp, app);
} else {
Category cat = cats[cats.length - 1];
if (s_log.isDebugEnabled()) {
s_log.debug("Got cat " + cat);
}
setPathCookie(sresp, cats);
sreq.setAttribute(CATEGORY_PATH_ATTR,
cats);
RequestDispatcher rd = resolveTemplate(cat, useContext);
if (rd == null) {
return super.resolve(templatePath, sreq, sresp, app);
}
return rd;
}
}
}
/**
* sets current path in a cookie
*
* @param catsArray
*/
private void setPathCookie(HttpServletResponse resp, Category[] catsArray) {
// 1st part of cookie value is website - if cookie domain covers several
// Aplaws sites, and the navigation model retains the cookie for more
// than one request, we could potentially link to one site with a path
// relating to another site. A check on this part of the cookie will
// prevent problems
StringBuffer path = new StringBuffer(Web.getConfig().getSiteName());
// 2nd part of cookie value is the application that set it. Again may
// be used if a navigation model retains the cookie. If we link to
// another application, it's navigation model may use this when
// deciding whether to trust the given path.
path.append(PATH_COOKIE_SEPARATOR + Kernel.getContext().getResource()
.getID().toString());
for (int i = 0; i < catsArray.length; i++) {
Category cat = catsArray[i];
path.append(PATH_COOKIE_SEPARATOR + cat.getID().toString());
}
Cookie cookie = new Cookie(PATH_COOKIE_NAME, path.toString());
s_log.debug("setting cookie with value: " + path);
cookie.setMaxAge(-1);
cookie.setPath("/");
String domain = Kernel.getSecurityConfig().getCookieDomain();
if (domain != null) {
cookie.setDomain(domain);
}
resp.addCookie(cookie);
}
public static Category[] getCategoryPath(HttpServletRequest req) {
return (Category[]) req.getAttribute(CATEGORY_PATH_ATTR);
}
private String getTemplateContext() {
TemplateContext ctx = Navigation.getContext().getTemplateContext();
if (ctx == null) {
return Template.DEFAULT_DISPATCHER_CONTEXT;
} else {
return ctx.getContext();
}
}
/**
*
* @param cat
* @param useContext
*
* @return
*/
private RequestDispatcher resolveTemplate(Category cat, String useContext) {
Template template = null;
if (Navigation.getConfig().inheritTemplates()) {
template = Template.matchBest(cat,
getTemplateContext(),
useContext);
} else {
template = Template.matchExact(cat,
getTemplateContext(),
useContext);
}
// If there's an explicit use context which doesn't exist, give a 404
if (!Template.DEFAULT_USE_CONTEXT.equals(useContext) && null == template) {
s_log.debug("No template found in context " + getTemplateContext()
+ " for category " + cat.getID()
+ " with use context " + useContext);
return null;
}
String path = null;
if (template == null) {
if (s_log.isDebugEnabled()) {
s_log.debug("Using default template");
}
path = Navigation.getConfig().getDefaultTemplate();
} else {
path = template.getURL();
}
// Old style, no longer valid. App may be installed into any arbitrary
// context and by default all modules are installed into one context.
// RequestDispatcher rd = Web.findResourceDispatcher(
// new String[]{"ROOT"},
// path);
// new style, will lookup path in the current context (formerly used to
// be ROOT)
RequestDispatcher rd = Web.findResourceDispatcher(path);
if (s_log.isDebugEnabled()) {
s_log.debug("Got dispatcher " + rd);
}
return rd;
}
private Category getRootCategory() {
Navigation nav = (Navigation) Web.getWebContext().getApplication();
TemplateContext ctx = Navigation.getContext().getTemplateContext();
if (s_log.isDebugEnabled()) {
s_log.debug("Finding root for " + nav + " in context " + ctx);
}
String dispatcherContext = ctx == null ? null : ctx.getContext();
Category root = Category.getRootForObject(nav,
dispatcherContext);
if (root == null && dispatcherContext != null) {
s_log.debug("No specific root found, trying generic context");
root = Category.getRootForObject(nav, null);
}
Assert.exists(root, Category.class);
if (s_log.isDebugEnabled()) {
s_log.debug("Got root category " + root);
}
return root;
}
/**
*
* category resolution retained as an instance method to allow it to be
* overridden. Default functionality contained in static resolveCategory
* method.
*
* @param root
* @param path
*
* @return
*/
protected Category[] resolvePath(Category root, String path) {
return NavigationFileResolver.resolveCategory(root, path);
}
/**
* Match a URL with the category tree and return the requested category if
* exists.
*
* Quasimodo: Originally addEqualsFilter has been used to filter the
* appropriate category directly inside the SQL query. This isn't possible
* anymore due to the localised URLs of the new localised categories (or at
* least: not found it). Therefore we do the filtering in Java now.
*
*/
public static Category[] resolveCategory(Category root,
String path) {
String[] bits = StringUtils.split(path, '/');
List cats = new ArrayList();
cats.add(root);
Category cat = root;
for (int i = 0; i < bits.length; i++) {
if ("".equals(bits[i])) {
continue;
}
if (s_log.isDebugEnabled()) {
s_log.debug("Looking for match to " + bits[i]);
}
CategoryCollection children = cat.getChildren();
// children.addEqualsFilter(Category.URL, bits[i]);
// children.addEqualsFilter(Category.IS_ENABLED, Boolean.TRUE);
// if (children.next()) {
boolean found = false;
while (children.next()) {
cat = children.getCategory();
if (cat.getURL().equals(bits[i]) && cat.isEnabled() == true) {
if (s_log.isDebugEnabled()) {
s_log.debug("Got category " + cat);
}
cats.add(cat);
children.close();
found = true;
break;
}
}
// } else {
if (found == false) {
if (s_log.isDebugEnabled()) {
s_log.debug("No category found ");
}
return null;
}
}
return (Category[]) cats.toArray(new Category[cats.size()]);
}
}