001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2007
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.pages.mapping;
025:
026: import java.io.IOException;
027: import java.util.Collection;
028: import java.util.Iterator;
029:
030: import javax.servlet.ServletException;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033:
034: import org.riotfamily.common.web.filter.FilterPlugin;
035: import org.riotfamily.common.web.filter.PluginChain;
036: import org.riotfamily.common.web.mapping.AttributePattern;
037: import org.riotfamily.common.web.util.ServletUtils;
038: import org.riotfamily.pages.dao.PageDao;
039: import org.riotfamily.pages.model.Page;
040: import org.riotfamily.pages.model.Site;
041: import org.riotfamily.riot.security.AccessController;
042: import org.springframework.transaction.PlatformTransactionManager;
043: import org.springframework.transaction.TransactionDefinition;
044: import org.springframework.transaction.TransactionStatus;
045: import org.springframework.transaction.support.DefaultTransactionDefinition;
046: import org.springframework.util.AntPathMatcher;
047: import org.springframework.util.PathMatcher;
048:
049: /**
050: * FilterPlugin that provides folder support. Normally the website-servlet is
051: * mapped using a suffix-mapping like <code>*.html</code> and therefore can't
052: * handle requests like </code>/some/folder</code>. In case a page with the
053: * requested path exists and is {@link Page#isFolder() marked as folder}, this
054: * plugin will send a redirect to the first <i>requestable</i> child-page.
055: * A page is requestable if it is {@link Page#isEnabled() enabled} <em>or</em>
056: * a Riot user {@link AccessController#isAuthenticatedUser() is logged in}.
057: *
058: * @author Felix Gnass [fgnass at neteye dot de]
059: * @since 6.5
060: */
061: public class FolderFilterPlugin extends FilterPlugin {
062:
063: private static final TransactionDefinition TX_DEF = new DefaultTransactionDefinition();
064:
065: private PathMatcher pathMatcher = new AntPathMatcher();
066:
067: private PageDao pageDao;
068:
069: private PageUrlBuilder pageUrlBuilder;
070:
071: private PlatformTransactionManager tx;
072:
073: private String siteChooserUrl;
074:
075: public FolderFilterPlugin(PageDao pageDao,
076: PageUrlBuilder pageUrlBuilder, PlatformTransactionManager tx) {
077:
078: this .pageDao = pageDao;
079: this .pageUrlBuilder = pageUrlBuilder;
080: this .tx = tx;
081: }
082:
083: /**
084: * Sets an URL to which the user will be redirected if no site matches.
085: * Default is <code>null</code>, which means that no redirect is sent and
086: * the request is handed on to the next plugin in the chain.
087: * @param url A context-relative URL
088: */
089: public void setSiteChooserUrl(String url) {
090: this .siteChooserUrl = url;
091: }
092:
093: public void doFilter(HttpServletRequest request,
094: HttpServletResponse response, PluginChain pluginChain)
095: throws IOException, ServletException {
096:
097: boolean requestHandled = false;
098: String path = ServletUtils
099: .getOriginatingPathWithinApplication(request);
100: if (path.lastIndexOf('.') < path.lastIndexOf('/')) {
101: TransactionStatus status = tx.getTransaction(TX_DEF);
102: try {
103: requestHandled = sendRedirect(request.getServerName(),
104: path, request, response);
105: } catch (Exception ex) {
106: tx.rollback(status);
107: throw new ServletException(ex);
108: }
109: tx.commit(status);
110: }
111: if (!requestHandled) {
112: pluginChain.doFilter(request, response);
113: }
114: }
115:
116: private boolean sendRedirect(String hostName, String path,
117: HttpServletRequest request, HttpServletResponse response)
118: throws IOException {
119:
120: Site site = pageDao.findSite(hostName, path);
121: if (site == null) {
122: if (siteChooserUrl != null) {
123: response.sendRedirect(response
124: .encodeRedirectURL(request.getContextPath()
125: + siteChooserUrl));
126:
127: return true;
128: }
129: return false;
130: }
131: path = site.stripPrefix(path);
132: if (path.endsWith("/")) {
133: path = path.substring(0, path.length() - 1);
134: }
135: if (path.length() == 0) {
136: Collection topLevelPages = pageDao.getRootNode()
137: .getChildPages(site);
138: sendRedirect(topLevelPages, request, response);
139: return true;
140: }
141:
142: Page page = pageDao.findPage(site, path);
143: if (page == null) {
144: page = findWildcardPage(site, path);
145: }
146: if (page == null || !page.isFolder()) {
147: return false;
148: }
149:
150: sendRedirect(page.getChildPages(), request, response);
151:
152: return true;
153: }
154:
155: protected Page findWildcardPage(Site site, String urlPath) {
156: Page page = null;
157: String bestMatch = null;
158: for (Iterator it = pageDao.getWildcardPaths(site).iterator(); it
159: .hasNext();) {
160: String path = (String) it.next();
161: String antPattern = AttributePattern
162: .convertToAntPattern(path);
163: if (pathMatcher.match(antPattern, urlPath)
164: && (bestMatch == null || bestMatch.length() <= path
165: .length())) {
166:
167: bestMatch = path;
168: }
169: }
170: if (bestMatch != null) {
171: page = pageDao.findPage(site, bestMatch);
172: }
173: return page;
174: }
175:
176: private void sendRedirect(Collection pages,
177: HttpServletRequest request, HttpServletResponse response)
178: throws IOException {
179:
180: String url = getFirstRequestablePageUrl(pages);
181: if (url != null) {
182: response.sendRedirect(response.encodeRedirectURL(request
183: .getContextPath()
184: + url));
185: } else {
186: response.sendError(HttpServletResponse.SC_NOT_FOUND);
187: }
188: }
189:
190: private boolean isRequestable(Page page) {
191: return page.isEnabled()
192: || AccessController.isAuthenticatedUser();
193: }
194:
195: private String getFirstRequestablePageUrl(Collection pages) {
196: Iterator it = pages.iterator();
197: while (it.hasNext()) {
198: Page page = (Page) it.next();
199: if (isRequestable(page)) {
200: return pageUrlBuilder.getUrl(page);
201: }
202: }
203: return null;
204: }
205:
206: }
|