CCM NG: Current status of porting the CMS UI to CCM NG
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4209 8810af33-2d31-482b-a856-94f89814c4dfpull/2/head
parent
e4640cd6d7
commit
61bea04c13
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-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.cms;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A central location for commonly used CMS services and their accessories.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>Context.</b> {@link #getContext()} fetches the context record ({@link
|
||||||
|
* com.arsdigita.kernel.KernelContext}) of the current thread.</p>
|
||||||
|
*
|
||||||
|
* @author Daniel Berrange
|
||||||
|
* @see com.arsdigita.kernel.Kernel
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class CMS {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private Logger instance for debugging purpose.
|
||||||
|
*/
|
||||||
|
private static final Logger s_log = Logger.getLogger(CMS.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CMS XML namespace.
|
||||||
|
*/
|
||||||
|
public final static String CMS_XML_NS = "http://www.arsdigita.com/cms/1.0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant string used as key for creating Workspace (content-center) as a
|
||||||
|
* legacy application.
|
||||||
|
*/
|
||||||
|
public static final String WORKSPACE_PACKAGE_KEY = "content-center";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant string used as key for creating service package as a legacy
|
||||||
|
* application.
|
||||||
|
*/
|
||||||
|
public final static String SERVICE_PACKAGE_KEY = "cms-service";
|
||||||
|
|
||||||
|
static final CMSContext s_initialContext = new CMSContext();
|
||||||
|
|
||||||
|
private static final ThreadLocal s_context = new ThreadLocal() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object initialValue() {
|
||||||
|
return s_initialContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the context record of the current thread.
|
||||||
|
*
|
||||||
|
* @post return != null
|
||||||
|
*/
|
||||||
|
public static final CMSContext getContext() {
|
||||||
|
return (CMSContext) s_context.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final void setContext(CMSContext context) {
|
||||||
|
s_context.set(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-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.cms;
|
||||||
|
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The entry point into all the global state that CCM CMS code expects to
|
||||||
|
* have available to it when running, e.g. the current content section,
|
||||||
|
* current item
|
||||||
|
*
|
||||||
|
* <p>This is a session object that provides an environment in which
|
||||||
|
* code can execute. The CMSContext contains all session-specific
|
||||||
|
* variables. One session object is maintained per thread.</p>
|
||||||
|
*
|
||||||
|
* <p>Accessors of this class will assert that the item it returned is
|
||||||
|
* not null. If the caller wants to handle the case where an item is
|
||||||
|
* null explicitly, then use the hasContentItem and hasContentSection
|
||||||
|
* methods first.
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.kernel.KernelContext
|
||||||
|
* @see com.arsdigita.cms.CMS
|
||||||
|
*
|
||||||
|
* @author Daniel Berrange
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public final class CMSContext {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(CMSContext.class);
|
||||||
|
|
||||||
|
private ContentSection m_section = null;
|
||||||
|
private ContentItem m_item = null;
|
||||||
|
private SecurityManager m_security = null;
|
||||||
|
|
||||||
|
CMSContext() {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getDebugInfo() {
|
||||||
|
final String info = "Current state of " + this + ":\n" +
|
||||||
|
" getContentSection() -> " + getContentSection() + "\n" +
|
||||||
|
" getContentItem() -> " + getContentItem() + "\n" +
|
||||||
|
" getSecurityManager() -> " + getSecurityManager();
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CMSContext copy() {
|
||||||
|
final CMSContext result = new CMSContext();
|
||||||
|
|
||||||
|
result.m_section = m_section;
|
||||||
|
result.m_item = m_item;
|
||||||
|
result.m_security = m_security;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a content section is available
|
||||||
|
* @return true if a content section is available
|
||||||
|
*/
|
||||||
|
public final boolean hasContentSection() {
|
||||||
|
return m_section != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current content section
|
||||||
|
* not anymore: hasContentSection() == true
|
||||||
|
* @return the currently selected content section
|
||||||
|
*/
|
||||||
|
public final ContentSection getContentSection() {
|
||||||
|
// removing this which is not true when viewing category pages
|
||||||
|
//Assert.exists(m_section, "section");
|
||||||
|
return m_section;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current content section
|
||||||
|
* @param section the new content section
|
||||||
|
*/
|
||||||
|
public final void setContentSection(final ContentSection section) {
|
||||||
|
m_section = section;
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Content section set to " + section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a content item is available
|
||||||
|
* @return true if a content item is available
|
||||||
|
*/
|
||||||
|
public final boolean hasContentItem() {
|
||||||
|
return m_item != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current content item
|
||||||
|
* @pre hasContentItem() == true
|
||||||
|
* @return the current content item
|
||||||
|
*/
|
||||||
|
public final ContentItem getContentItem() {
|
||||||
|
// removing this which is necessarily true in ContentList
|
||||||
|
//Assert.exists(m_item, "item");
|
||||||
|
if (s_log.isDebugEnabled() && m_item == null) {
|
||||||
|
s_log.debug("Content item is null");
|
||||||
|
}
|
||||||
|
return m_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current content item
|
||||||
|
* @param item the new content item
|
||||||
|
*/
|
||||||
|
public final void setContentItem(final ContentItem item) {
|
||||||
|
m_item = item;
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Content item set to " + item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there is a CMS <code>SecurityManager</code> for this
|
||||||
|
* session.
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.cms.SecurityManager
|
||||||
|
* @return <code>true</code> if a security manager is available
|
||||||
|
*/
|
||||||
|
public final boolean hasSecurityManager() {
|
||||||
|
return m_security != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current security manager.
|
||||||
|
*
|
||||||
|
* @return the current security manager
|
||||||
|
*/
|
||||||
|
public final SecurityManager getSecurityManager() {
|
||||||
|
Assert.exists(m_security, SecurityManager.class);
|
||||||
|
|
||||||
|
return m_security;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setSecurityManager(final SecurityManager security) {
|
||||||
|
m_security = security;
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Security manager set to " + security);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package com.arsdigita.cms;
|
||||||
|
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class ContentCenter extends CcmApplication {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,307 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package com.arsdigita.cms;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Page;
|
||||||
|
import com.arsdigita.cms.dispatcher.CMSPage;
|
||||||
|
import com.arsdigita.cms.ui.CMSApplicationPage;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.kernel.security.Util;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.templating.Templating;
|
||||||
|
import com.arsdigita.ui.login.LoginHelper;
|
||||||
|
import com.arsdigita.web.ApplicationFileResolver;
|
||||||
|
import com.arsdigita.web.BaseApplicationServlet;
|
||||||
|
import com.arsdigita.web.LoginSignal;
|
||||||
|
import com.arsdigita.web.WebConfig;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.shiro.authz.AuthorizationException;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionRepository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.annotation.WebServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CMS ContentCenter (content-center) application servlet serves all request
|
||||||
|
* made within the Content Center application.
|
||||||
|
*
|
||||||
|
* @author Peter Boy <pboy@barkhof.uni-bremen.de>
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@WebServlet(urlPatterns = "/content-center/*")
|
||||||
|
public class ContentCenterServlet extends BaseApplicationServlet {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 16543266935651171L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL (pathinfo) -> Page object mapping. Based on it (and the http request
|
||||||
|
* url) the doService method to selects a page to display
|
||||||
|
*/
|
||||||
|
private final Map<String, Page> pages = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to directory containg ccm-cms template files
|
||||||
|
*/
|
||||||
|
private String m_templatePath;
|
||||||
|
/**
|
||||||
|
* Resolvers to find templates (JSP) and other stuff stored in file system.
|
||||||
|
*/
|
||||||
|
private ApplicationFileResolver m_resolver;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
ContentCenterServlet.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use parent's class initialisation extension point to perform additional
|
||||||
|
* initialisation tasks.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doInit() {
|
||||||
|
LOGGER.info("starting doInit method");
|
||||||
|
|
||||||
|
// NEW STUFF here used to process the pages in this servlet
|
||||||
|
// Addresses previously noted in WEB-INF/resources/content-center-map.xml
|
||||||
|
// Obviously not required.
|
||||||
|
|
||||||
|
//ToDo
|
||||||
|
// addPage("/", new MainPage()); // index page at address ~/cc
|
||||||
|
// addPage("/index", new MainPage());
|
||||||
|
//ToDo End
|
||||||
|
|
||||||
|
// addPage("/item-search", new CMSItemSearchPage());
|
||||||
|
// Old style
|
||||||
|
//addPage("/item-search", new ItemSearchPage());
|
||||||
|
//addPage("/searchredirect", new CMSSearchResultRedirector());
|
||||||
|
|
||||||
|
// STUFF to use for JSP extension, i.e. jsp's to try for URLs which are not
|
||||||
|
// handled by the this servlet directly.
|
||||||
|
/**
|
||||||
|
* Set Template base path for JSP's
|
||||||
|
*/
|
||||||
|
// ToDo: Make it configurable by an appropriate config registry entry!
|
||||||
|
// m_templatePath = CMS.getConfig().getTemplateRoot();
|
||||||
|
m_templatePath = "/templates/ccm-cms/content-center";
|
||||||
|
/**
|
||||||
|
* Set TemplateResolver class
|
||||||
|
*/
|
||||||
|
m_resolver = WebConfig.getConfig().getResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doService(final HttpServletRequest sreq,
|
||||||
|
final HttpServletResponse sresp,
|
||||||
|
final CcmApplication app) throws ServletException,
|
||||||
|
IOException {
|
||||||
|
LOGGER.info("starting doService method");
|
||||||
|
|
||||||
|
// ContentCenter workspace = (ContentCenter) app;
|
||||||
|
|
||||||
|
/* Check user and privilegies */
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final Shiro shiro = cdiUtil.findBean(Shiro.class);
|
||||||
|
if (shiro.getSubject().isAuthenticated()) {
|
||||||
|
throw new LoginSignal(sreq); // send to login page
|
||||||
|
}
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
final ContentSectionRepository sectionRepo = cdiUtil.findBean(
|
||||||
|
ContentSectionRepository.class);
|
||||||
|
final List<ContentSection> sections = sectionRepo.findAll();
|
||||||
|
boolean hasAccess = false;
|
||||||
|
for (final ContentSection section : sections) {
|
||||||
|
if (permissionChecker.isPermitted(CmsConstants.PRIVILEGE_ITEMS_EDIT,
|
||||||
|
section.getRootDocumentsFolder())) {
|
||||||
|
hasAccess = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAccess) { // user has no access privilege
|
||||||
|
throw new AuthorizationException(
|
||||||
|
"User is not entitled to access any content section");
|
||||||
|
// throw new LoginSignal(sreq); // send to login page
|
||||||
|
}
|
||||||
|
|
||||||
|
// New way to fetch the page
|
||||||
|
String pathInfo = sreq.getPathInfo();
|
||||||
|
if (pathInfo.length() > 1 && pathInfo.endsWith("/")) {
|
||||||
|
/* NOTE: ServletAPI specifies, pathInfo may be empty or will
|
||||||
|
* start with a '/' character. It currently carries a
|
||||||
|
* trailing '/' if a "virtual" page, i.e. not a real jsp, but
|
||||||
|
* result of a servlet mapping. But Application requires url
|
||||||
|
* NOT to end with a trailing '/' for legacy free applications. */
|
||||||
|
pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// An empty remaining URL or a URL which doesn't end in trailing slash:
|
||||||
|
// probably want to redirect.
|
||||||
|
// Probably DEPRECATED with new access method or only relevant for jsp
|
||||||
|
// extension
|
||||||
|
// if (m_trailingSlashList.contains(url) && !originalUrl.endsWith("/")) {
|
||||||
|
// DispatcherHelper.sendRedirect(sresp, originalUrl + "/");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
final Page page = (Page) pages.get(pathInfo);
|
||||||
|
if (page != null) {
|
||||||
|
|
||||||
|
// Check user access.
|
||||||
|
checkUserAccess(sreq, sresp);
|
||||||
|
|
||||||
|
if (page instanceof CMSPage) {
|
||||||
|
// backwards compatibility fix until migration completed
|
||||||
|
final CMSPage cmsPage = (CMSPage) page;
|
||||||
|
final RequestContext ctx = DispatcherHelper.getRequestContext();
|
||||||
|
cmsPage.init();
|
||||||
|
cmsPage.dispatch(sreq, sresp, ctx);
|
||||||
|
} else {
|
||||||
|
final CMSApplicationPage cmsAppPage = (CMSApplicationPage) page;
|
||||||
|
cmsAppPage.init(sreq, sresp, app);
|
||||||
|
// Serve the page.
|
||||||
|
final Document doc = cmsAppPage.buildDocument(sreq, sresp);
|
||||||
|
|
||||||
|
PresentationManager pm = Templating.getPresentationManager();
|
||||||
|
pm.servePage(doc, sreq, sresp);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Fall back on the JSP application dispatcher.
|
||||||
|
// NOTE: The JSP must ensure the proper authentication and
|
||||||
|
// authorisation if required!
|
||||||
|
LOGGER.info("NO page registered to serve the requst url.");
|
||||||
|
|
||||||
|
RequestDispatcher rd = m_resolver.resolve(m_templatePath,
|
||||||
|
sreq, sresp, app);
|
||||||
|
if (rd != null) {
|
||||||
|
LOGGER.debug("Got dispatcher " + rd);
|
||||||
|
|
||||||
|
final HttpServletRequest origreq = DispatcherHelper
|
||||||
|
.restoreOriginalRequest(sreq);
|
||||||
|
rd.forward(origreq, sresp);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
sresp.sendError(404, sreq.getRequestURI()
|
||||||
|
+ " not found on this server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info("doService method completed");
|
||||||
|
|
||||||
|
} // END doService()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal service mechod, adds one pair of Url - Page to the internal hash
|
||||||
|
* map, used as a cache.
|
||||||
|
*
|
||||||
|
* @param pathInfo url stub for a page to display
|
||||||
|
* @param page Page object to display
|
||||||
|
*/
|
||||||
|
private void addPage(final String pathInfo, final Page page) {
|
||||||
|
|
||||||
|
// Current Implementation requires pathInfo to start with a leading '/'
|
||||||
|
// SUN Servlet API specifies: "PathInfo *may be empty* or will start
|
||||||
|
// with a '/' character."
|
||||||
|
pages.put(pathInfo, page);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Service Method returns the URL stub for the class name, can return null
|
||||||
|
// * if not mapped
|
||||||
|
// */
|
||||||
|
// // Currently still in use by c.ad.cms.ui.ItemSearchWidget
|
||||||
|
// public static String getURLStubForClass(String classname) {
|
||||||
|
// LOGGER.debug("Getting URL Stub for : " + classname);
|
||||||
|
// Iterator itr = s_pageURLs.keySet().iterator();
|
||||||
|
// while (itr.hasNext()) {
|
||||||
|
// String classname2 = (String) itr.next();
|
||||||
|
// s_log.debug("key: " + classname + " value: "
|
||||||
|
// + (String) s_pageURLs.get(classname2));
|
||||||
|
// }
|
||||||
|
// String url = (String) s_pageURLs.get(classname);
|
||||||
|
// return url;
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Verify that the user is logged in and is able to view the page.
|
||||||
|
* Subclasses can override this method if they need to, but should always be
|
||||||
|
* sure to call super.checkUserAccess(...)
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @param response The HTTP response
|
||||||
|
* @param actx The request context
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void checkUserAccess(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response //,
|
||||||
|
/// final RequestContext actx
|
||||||
|
)
|
||||||
|
throws ServletException {
|
||||||
|
|
||||||
|
if (CdiUtil.createCdiUtil().findBean(Shiro.class).getSubject()
|
||||||
|
.isAuthenticated()) {
|
||||||
|
throw new LoginSignal(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects the client to the login page, setting the return url to the
|
||||||
|
* current request URI.
|
||||||
|
*
|
||||||
|
* @exception ServletException If there is an exception thrown while trying
|
||||||
|
* to redirect, wrap that exception in a
|
||||||
|
* ServletException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void redirectToLoginPage(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp)
|
||||||
|
throws ServletException {
|
||||||
|
String url = Util.getSecurityHelper()
|
||||||
|
.getLoginURL(req)
|
||||||
|
+ "?" + LoginHelper.RETURN_URL_PARAM_NAME
|
||||||
|
+ "=" + DispatcherHelper.encodeReturnURL(req);
|
||||||
|
try {
|
||||||
|
LoginHelper.sendRedirect(req, resp, url);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("IO Exception", e);
|
||||||
|
throw new ServletException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.arsdigita.cms;
|
||||||
|
|
||||||
|
import com.arsdigita.cms.contenttypes.GenericAddress;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.domain.DomainObject;
|
||||||
|
import com.arsdigita.domain.DomainObjectTraversalAdapter;
|
||||||
|
import com.arsdigita.domain.DomainObjectXMLRenderer;
|
||||||
|
import com.arsdigita.globalization.GlobalizationHelper;
|
||||||
|
import com.arsdigita.persistence.metadata.Property;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a special ContentItemXMLRenderer for CMS to get a more transparent
|
||||||
|
* way to handle ContentBundles during XML output.
|
||||||
|
*
|
||||||
|
* The problem was to change RelatedLinks and therefore Link to always link to
|
||||||
|
* the corresponding ContentBundle instead of the content item. To get the
|
||||||
|
* corresponding content item during XML generation, I have to test for
|
||||||
|
* ContentBundle and negotiate the language version.
|
||||||
|
* This is not possible in com.arsdigita.ccm
|
||||||
|
*
|
||||||
|
* @author quasi
|
||||||
|
*/
|
||||||
|
public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
|
||||||
|
|
||||||
|
private static final Logger logger =
|
||||||
|
Logger.getLogger(ContentItemXMLRenderer.class);
|
||||||
|
private String m_propertyName = "";
|
||||||
|
private String m_keyName = "";
|
||||||
|
private String m_relationAttribute = "";
|
||||||
|
|
||||||
|
public ContentItemXMLRenderer(final Element root) {
|
||||||
|
super(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will be called by DomainObjectTraversal.walk()
|
||||||
|
// It's purpose is to test for ContentBundle objects and if found, replace
|
||||||
|
// that object with the negotiated version of the content item.
|
||||||
|
// Otherwise this methd will do nothing.
|
||||||
|
@Override
|
||||||
|
protected void walk(final DomainObjectTraversalAdapter adapter,
|
||||||
|
final DomainObject obj,
|
||||||
|
final String path,
|
||||||
|
final String context,
|
||||||
|
final DomainObject linkObject) {
|
||||||
|
//final long start = System.nanoTime();
|
||||||
|
|
||||||
|
DomainObject nObj = obj;
|
||||||
|
|
||||||
|
if (nObj instanceof ContentBundle) {
|
||||||
|
|
||||||
|
nObj = ((ContentBundle) obj).getInstance(GlobalizationHelper.getNegotiatedLocale(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.walk(adapter, nObj, path, context, linkObject);
|
||||||
|
|
||||||
|
//System.out.printf("Walked object in %d ms\n", (System.nanoTime() - start) / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleAttribute(final DomainObject obj, final String path, final Property property) {
|
||||||
|
final String propertyName = property.getName();
|
||||||
|
|
||||||
|
// Special handling for the isoCountryCode field in GenericAddress
|
||||||
|
if (obj instanceof GenericAddress && "isoCountryCode".equals(propertyName)) {
|
||||||
|
//if (propertyName.equals("isoCountryCode")) {
|
||||||
|
super.handleAttribute(obj, path, property);
|
||||||
|
|
||||||
|
final Element element = newElement(m_element, "country");
|
||||||
|
element.setText(GenericAddress.getCountryNameFromIsoCode(((GenericAddress) obj).getIsoCountryCode()));
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special handling for the relation attribute keys
|
||||||
|
if (!m_relationAttribute.isEmpty()) {
|
||||||
|
String key = "";
|
||||||
|
|
||||||
|
// The RelationAttribute is part of this domain object as field
|
||||||
|
if (obj instanceof RelationAttributeInterface
|
||||||
|
&& ((RelationAttributeInterface) obj).
|
||||||
|
hasRelationAttributeProperty(propertyName)) {
|
||||||
|
|
||||||
|
final RelationAttributeInterface relationAttributeObject = (RelationAttributeInterface) obj;
|
||||||
|
key = relationAttributeObject.getRelationAttributeKey(
|
||||||
|
propertyName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This RelationAttribute is part of an n:m association as link attribute
|
||||||
|
if (obj instanceof LinkDomainObject
|
||||||
|
&& propertyName.equals(m_keyName)) {
|
||||||
|
key = (String) ((LinkDomainObject) obj).get(m_keyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace value of the property defined in RELATION_ATTRIBUTES string
|
||||||
|
// of the primary domain object with the localized String.
|
||||||
|
if (!key.isEmpty()) {
|
||||||
|
// logger.debug(String.format(
|
||||||
|
// "Getting relation attribute value for key '%s' of relation attribute '%s'",
|
||||||
|
// key, m_relationAttribute));
|
||||||
|
final RelationAttributeCollection relationAttributeCollection = new RelationAttributeCollection(
|
||||||
|
m_relationAttribute, key);
|
||||||
|
relationAttributeCollection.addLanguageFilter(GlobalizationHelper.
|
||||||
|
getNegotiatedLocale().getLanguage());
|
||||||
|
if (!relationAttributeCollection.isEmpty()) {
|
||||||
|
relationAttributeCollection.next();
|
||||||
|
final Element element = newElement(m_element, m_keyName);
|
||||||
|
element.setText(relationAttributeCollection.getName());
|
||||||
|
final Element elementId = newElement(m_element, m_keyName + "Id");
|
||||||
|
elementId.setText(relationAttributeCollection.getKey());
|
||||||
|
relationAttributeCollection.close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.handleAttribute(obj, path, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void beginAssociation(final DomainObject obj, final String path, final Property property) {
|
||||||
|
super.beginAssociation(obj, path, property);
|
||||||
|
|
||||||
|
final String propertyName = property.getName();
|
||||||
|
|
||||||
|
if (obj instanceof RelationAttributeInterface
|
||||||
|
&& ((RelationAttributeInterface) obj).hasRelationAttributeProperty(
|
||||||
|
propertyName)) {
|
||||||
|
|
||||||
|
final RelationAttributeInterface relationAttributeObject = (RelationAttributeInterface) obj;
|
||||||
|
|
||||||
|
m_propertyName = propertyName;
|
||||||
|
m_keyName = relationAttributeObject.getRelationAttributeKeyName(propertyName);
|
||||||
|
m_relationAttribute = relationAttributeObject.getRelationAttributeName(propertyName);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endAssociation(final DomainObject obj, final String path, final Property property) {
|
||||||
|
|
||||||
|
m_propertyName = "";
|
||||||
|
m_keyName = "";
|
||||||
|
m_relationAttribute = "";
|
||||||
|
|
||||||
|
super.endAssociation(obj, path, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,757 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-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.cms;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Page;
|
||||||
|
import com.arsdigita.cms.dispatcher.CMSDispatcher;
|
||||||
|
import com.arsdigita.cms.dispatcher.CMSPage;
|
||||||
|
import com.arsdigita.cms.dispatcher.ContentItemDispatcher;
|
||||||
|
import com.arsdigita.cms.dispatcher.ItemResolver;
|
||||||
|
import com.arsdigita.cms.ui.CMSApplicationPage;
|
||||||
|
import com.arsdigita.dispatcher.AccessDeniedException;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.templating.Templating;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.util.Classes;
|
||||||
|
import com.arsdigita.web.ApplicationFileResolver;
|
||||||
|
import com.arsdigita.web.BaseApplicationServlet;
|
||||||
|
import com.arsdigita.web.LoginSignal;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.web.WebConfig;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.arsdigita.cms.CMSConfig;
|
||||||
|
import org.hibernate.secure.spi.PermissibleAction;
|
||||||
|
import org.hibernate.secure.spi.PermissionCheckEntityInformation;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.security.User;
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentItemManager;
|
||||||
|
import org.librecms.contentsection.ContentItemRepository;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionConfig;
|
||||||
|
import org.librecms.lifecycle.Lifecycle;
|
||||||
|
|
||||||
|
import javax.enterprise.inject.spi.CDI;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE:
|
||||||
|
* Repaired ItemURLCache to save multilingual items with automatic
|
||||||
|
* language negotiation. The cache now uses the remaining url part
|
||||||
|
* and the language concatinated as a hash table key. The delimiter
|
||||||
|
* is CACHE_KEY_DELIMITER.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE 2:
|
||||||
|
* In a process of refactoring from legacy compatible to legacy free applications.
|
||||||
|
* TODO:
|
||||||
|
* - replace url check using RequestContext which resolves to SiteNodeRequest
|
||||||
|
* implementation (due to SiteNodeRequest used in BaseApplicationServlet).
|
||||||
|
* - Refactor content item UI bebop ApplicationPage or PageFactory instead of
|
||||||
|
* legacy infected sitenode / package dispatchers.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Content Section's Application Servlet according CCM core web application
|
||||||
|
* structure {
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.web.Application} implements the content section UI.
|
||||||
|
*
|
||||||
|
* It handles the UI for content items and delegates the UI for sections and
|
||||||
|
* folders to jsp templates.
|
||||||
|
*
|
||||||
|
* @author unknown
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
* @author Peter Boy <pboy@barkhof.uni-bremen.de>
|
||||||
|
*/
|
||||||
|
public class ContentSectionServlet extends BaseApplicationServlet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal logger instance to faciliate debugging. Enable logging output by
|
||||||
|
* editing /WEB-INF/conf/log4j.properties int hte runtime environment and
|
||||||
|
* set com.arsdigita.cms.ContentSectionServlet=DEBUG by uncommenting or
|
||||||
|
* adding the line.
|
||||||
|
*/
|
||||||
|
private static final Logger s_log = Logger.getLogger(
|
||||||
|
ContentSectionServlet.class);
|
||||||
|
/**
|
||||||
|
* Stringarray of file name patterns for index files.
|
||||||
|
*/
|
||||||
|
// private static final String[] WELCOME_FILES = new String[]{
|
||||||
|
// "index.jsp", "index.html"
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Some literals
|
||||||
|
/**
|
||||||
|
* Literal for the prefix (in url) for previewing items
|
||||||
|
*/
|
||||||
|
public static final String PREVIEW = "/preview";
|
||||||
|
/**
|
||||||
|
* Literal Template files suffix
|
||||||
|
*/
|
||||||
|
public static final String FILE_SUFFIX = ".jsp";
|
||||||
|
/**
|
||||||
|
* Literal of URL Stub for index file name (includes leading slash)
|
||||||
|
*/
|
||||||
|
public static final String INDEX_FILE = "/index";
|
||||||
|
public static final String XML_SUFFIX = ".xml";
|
||||||
|
public static final String XML_MODE = "xmlMode";
|
||||||
|
public static final String MEDIA_TYPE = "templateContext";
|
||||||
|
private static final String CACHE_KEY_DELIMITER = "%";
|
||||||
|
|
||||||
|
public static final String CONTENT_ITEM
|
||||||
|
= "com.arsdigita.cms.dispatcher.item";
|
||||||
|
public static final String CONTENT_SECTION
|
||||||
|
= "com.arsdigita.cms.dispatcher.section";
|
||||||
|
|
||||||
|
private final ContentItemDispatcher m_disp = new ContentItemDispatcher();
|
||||||
|
public static Map s_itemResolverCache = Collections
|
||||||
|
.synchronizedMap(new HashMap());
|
||||||
|
// private static Map s_itemURLCacheMap = null;
|
||||||
|
/**
|
||||||
|
* Whether to cache the content items
|
||||||
|
*/
|
||||||
|
private static final boolean s_cacheItems = true;
|
||||||
|
// NEW STUFF here used to process the pages in this servlet
|
||||||
|
/**
|
||||||
|
* URL (pathinfo) -> Page object mapping. Based on it (and the http request
|
||||||
|
* url) the doService method selects a page to display
|
||||||
|
*/
|
||||||
|
private final Map m_pages = new HashMap();
|
||||||
|
/**
|
||||||
|
* Path to directory containg ccm-cms template (jsp) files
|
||||||
|
*/
|
||||||
|
private String m_templatePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolver to actually use to find templates (JSP). JSP may be stored in
|
||||||
|
* file system or otherwise, depends on resolver. Resolver is retrieved from
|
||||||
|
* configuration. (probably used for other stuff as JSP's as well)
|
||||||
|
*/
|
||||||
|
private ApplicationFileResolver m_resolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init method overwrites parents init to pass in optional parameters
|
||||||
|
* {@link com.arsdigita.web.BaseServlet}. If not specified system wide
|
||||||
|
* defaults are used.
|
||||||
|
*
|
||||||
|
* @param config
|
||||||
|
*
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void init(ServletConfig config) throws ServletException {
|
||||||
|
|
||||||
|
super.init(config);
|
||||||
|
|
||||||
|
// optional init-param named template-path from ~/WEB-INF/web.xml
|
||||||
|
// may overwrite configuration parameters
|
||||||
|
String templatePath = config.getInitParameter("template-path");
|
||||||
|
if (templatePath == null) {
|
||||||
|
m_templatePath = CMSConfig.getConfig().getTemplateRootPath();
|
||||||
|
} else {
|
||||||
|
m_templatePath = config.getInitParameter("template-path");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(m_templatePath, String.class);
|
||||||
|
Assert.isTrue(m_templatePath.startsWith("/"),
|
||||||
|
"template-path must start with '/'");
|
||||||
|
Assert.isTrue(!m_templatePath.endsWith("/"),
|
||||||
|
"template-path must not end with '/'");
|
||||||
|
|
||||||
|
// optional init-param named file-resolver from ~/WEB-INF/web.xml
|
||||||
|
String resolverName = config.getInitParameter("file-resolver");
|
||||||
|
if (resolverName == null) {
|
||||||
|
m_resolver = WebConfig.getConfig().getResolver();
|
||||||
|
} else {
|
||||||
|
m_resolver = (ApplicationFileResolver) Classes.newInstance(
|
||||||
|
resolverName);
|
||||||
|
}
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Template path is " + m_templatePath + " with resolver "
|
||||||
|
+ m_resolver.
|
||||||
|
getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW STUFF here will be used to process the pages in this servlet
|
||||||
|
// Currently NOT working
|
||||||
|
// addPage("/admin", new MainPage()); // index page at address ~/cs
|
||||||
|
// addPage("/admin/index.jsp", new MainPage());
|
||||||
|
// addPage("/admin/item.jsp", new MainPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal service method, adds one pair of Url - Page to the internal hash
|
||||||
|
* map, used as a cache.
|
||||||
|
*
|
||||||
|
* @param pathInfo url stub for a page to display
|
||||||
|
* @param page Page object to display
|
||||||
|
*/
|
||||||
|
private void addPage(final String pathInfo, final Page page) {
|
||||||
|
|
||||||
|
Assert.exists(pathInfo, String.class);
|
||||||
|
Assert.exists(page, Page.class);
|
||||||
|
// Current Implementation requires pathInfo to start with a leading '/'
|
||||||
|
// SUN Servlet API specifies: "PathInfo *may be empty* or will start
|
||||||
|
// with a '/' character."
|
||||||
|
Assert.isTrue(pathInfo.startsWith("/"), "path starts not with '/'");
|
||||||
|
|
||||||
|
m_pages.put(pathInfo, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of parent's (abstract) doService method checks HTTP
|
||||||
|
* request to determine whether to handle a content item or other stuff
|
||||||
|
* which is delegated to jsp templates. {
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.web.BaseApplicationServlet#doService
|
||||||
|
* (HttpServletRequest, HttpServletResponse, Application)}
|
||||||
|
*
|
||||||
|
* @param sreq
|
||||||
|
* @param sresp
|
||||||
|
* @param app
|
||||||
|
*
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
* @throws java.io.IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doService(HttpServletRequest sreq,
|
||||||
|
HttpServletResponse sresp,
|
||||||
|
CcmApplication app)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
ContentSection section = (ContentSection) app;
|
||||||
|
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
// Prepare OLD style dispatcher based page service
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
* NOTE:
|
||||||
|
* Used to resolve to SiteNodeRequestContext (old style applications)
|
||||||
|
* which has been removed.
|
||||||
|
* Resolves currently to
|
||||||
|
* KernelRequestContext which will be removed as well.
|
||||||
|
*/
|
||||||
|
RequestContext ctx = DispatcherHelper.getRequestContext();
|
||||||
|
String url = ctx.getRemainingURLPart(); // here KernelRequestContext now
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Resolving URL " + url + " and trying as item first.");
|
||||||
|
}
|
||||||
|
final ItemResolver itemResolver = getItemResolver(section);
|
||||||
|
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
// Prepare NEW style servlet based bebob page service
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
String pathInfo = sreq.getPathInfo();
|
||||||
|
|
||||||
|
final ContentItem item = getItem(section, pathInfo, itemResolver);
|
||||||
|
|
||||||
|
Assert.exists(pathInfo, "String pathInfo");
|
||||||
|
if (pathInfo.length() > 1 && pathInfo.endsWith("/")) {
|
||||||
|
/* NOTE: ServletAPI specifies, pathInfo may be empty or will
|
||||||
|
* start with a '/' character. It currently carries a
|
||||||
|
* trailing '/' if a "virtual" page, i.e. not a real jsp, but
|
||||||
|
* result of a servlet mapping. But Application requires url
|
||||||
|
* NOT to end with a trailing '/' for legacy free applications. */
|
||||||
|
pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
|
||||||
|
}
|
||||||
|
final Page page = (Page) m_pages.get(pathInfo);
|
||||||
|
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
// Serve the page
|
||||||
|
// ////////////////////////////////////////////////////////////////////
|
||||||
|
/* FIRST try new style servlet based service */
|
||||||
|
if (page != null) {
|
||||||
|
|
||||||
|
// Check user access.
|
||||||
|
// checkUserAccess(sreq, sresp); // done in individual pages ??
|
||||||
|
if (page instanceof CMSPage) {
|
||||||
|
// backwards compatibility fix until migration completed
|
||||||
|
final CMSPage cmsPage = (CMSPage) page;
|
||||||
|
// final RequestContext ctx = DispatcherHelper.getRequestContext();
|
||||||
|
cmsPage.init();
|
||||||
|
cmsPage.dispatch(sreq, sresp, ctx);
|
||||||
|
} else {
|
||||||
|
final CMSApplicationPage cmsAppPage = (CMSApplicationPage) page;
|
||||||
|
cmsAppPage.init(sreq, sresp, app);
|
||||||
|
// Serve the page.
|
||||||
|
final Document doc = cmsAppPage.buildDocument(sreq, sresp);
|
||||||
|
|
||||||
|
PresentationManager pm = Templating.getPresentationManager();
|
||||||
|
pm.servePage(doc, sreq, sresp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECONDLY try if we have to serve an item (old style dispatcher based */
|
||||||
|
} else if (item != null) {
|
||||||
|
|
||||||
|
serveItem(sreq, sresp, section, item);
|
||||||
|
|
||||||
|
/* OTHERWISE delegate to a JSP in file system */
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* We have to deal with a content-section, folder or another bit */
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("NOT serving content item");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store content section in http request to make it available
|
||||||
|
* for admin/index.jsp */
|
||||||
|
sreq.setAttribute(CONTENT_SECTION, section);
|
||||||
|
|
||||||
|
RequestDispatcher rd = m_resolver.resolve(m_templatePath,
|
||||||
|
sreq, sresp, app);
|
||||||
|
if (rd != null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Got dispatcher " + rd);
|
||||||
|
}
|
||||||
|
sreq = DispatcherHelper.restoreOriginalRequest(sreq);
|
||||||
|
rd.forward(sreq, sresp);
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("No dispatcher found for" + rd);
|
||||||
|
}
|
||||||
|
String requestUri = sreq.getRequestURI(); // same as ctx.getRemainingURLPart()
|
||||||
|
sresp.sendError(404, requestUri + " not found on this server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // END doService
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param sreq
|
||||||
|
* @param sresp
|
||||||
|
* @param section
|
||||||
|
* @param item
|
||||||
|
*
|
||||||
|
* @throws ServletException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void serveItem(HttpServletRequest sreq,
|
||||||
|
HttpServletResponse sresp,
|
||||||
|
ContentSection section,
|
||||||
|
ContentItem item)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("serving content item");
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestContext ctx = DispatcherHelper.getRequestContext();
|
||||||
|
String url = ctx.getRemainingURLPart();
|
||||||
|
|
||||||
|
final ItemResolver itemResolver = getItemResolver(section);
|
||||||
|
|
||||||
|
//set the content item in the request
|
||||||
|
sreq.setAttribute(CONTENT_ITEM, item);
|
||||||
|
|
||||||
|
//set the template context
|
||||||
|
//TemplateResolver templateResolver = m_disp.getTemplateResolver(section);
|
||||||
|
String templateURL = url;
|
||||||
|
if (!templateURL.startsWith("/")) {
|
||||||
|
templateURL = "/" + templateURL;
|
||||||
|
}
|
||||||
|
if (templateURL.startsWith(PREVIEW)) {
|
||||||
|
templateURL = templateURL.substring(PREVIEW.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
String sTemplateContext = itemResolver.getTemplateFromURL(templateURL);
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("setting template context to " + sTemplateContext);
|
||||||
|
}
|
||||||
|
//templateResolver.setTemplateContext(sTemplateContext, sreq);
|
||||||
|
|
||||||
|
// Work out how long to cache for....
|
||||||
|
// We take minimum(default timeout, lifecycle expiry)
|
||||||
|
//ToDo
|
||||||
|
/*Lifecycle cycle = item.getLifecycle();
|
||||||
|
*/
|
||||||
|
int expires = DispatcherHelper.getDefaultCacheExpiry();
|
||||||
|
/*
|
||||||
|
if (cycle != null) {
|
||||||
|
Date endDate = cycle.getEndDate();
|
||||||
|
|
||||||
|
if (endDate != null) {
|
||||||
|
int maxAge = (int) ((endDate.getTime() - System
|
||||||
|
.currentTimeMillis()) / 1000l);
|
||||||
|
if (maxAge < expires) {
|
||||||
|
expires = maxAge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
//ToDo End
|
||||||
|
|
||||||
|
// NB, this is not the same as the security check previously
|
||||||
|
// We are checking if anyone can access - ie can we allow
|
||||||
|
// this page to be publically cached
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final ContentItemManager contentItemManager = cdiUtil.findBean(
|
||||||
|
ContentItemManager.class);
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
if (s_cacheItems && contentItemManager.isLive(item)) {
|
||||||
|
if (permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item)) {
|
||||||
|
DispatcherHelper.cacheForWorld(sresp, expires);
|
||||||
|
} else {
|
||||||
|
DispatcherHelper.cacheForUser(sresp, expires);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatcherHelper.cacheDisable(sresp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//use ContentItemDispatcher
|
||||||
|
m_disp.dispatch(sreq, sresp, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content section from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content section
|
||||||
|
*
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public static ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
return (ContentSection) request.getAttribute(CONTENT_SECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the ItemResolver for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The ItemResolver associated with the content section
|
||||||
|
*/
|
||||||
|
public ItemResolver getItemResolver(ContentSection section) {
|
||||||
|
|
||||||
|
String path = section.getPrimaryUrl();
|
||||||
|
ItemResolver ir = (ItemResolver) s_itemResolverCache.get(path);
|
||||||
|
|
||||||
|
if (ir == null) {
|
||||||
|
final String itemResolverClassName = section.getItemResolverClass();
|
||||||
|
try {
|
||||||
|
ir = (ItemResolver) Class
|
||||||
|
.forName(section.getItemResolverClass()).newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
s_itemResolverCache.put(path, ir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("using ItemResolver " + ir.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param section
|
||||||
|
* @param url
|
||||||
|
* @param itemResolver
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ContentItem getItem(ContentSection section, String url,
|
||||||
|
ItemResolver itemResolver) {
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("getting item at url " + url);
|
||||||
|
}
|
||||||
|
HttpServletRequest request = Web.getRequest();
|
||||||
|
|
||||||
|
//first sanitize the url
|
||||||
|
if (url.endsWith(XML_SUFFIX)) {
|
||||||
|
request.setAttribute(XML_MODE, Boolean.TRUE);
|
||||||
|
s_log.debug("StraightXML Requested");
|
||||||
|
url = "/" + url.substring(0, url.length() - XML_SUFFIX.length());
|
||||||
|
} else {
|
||||||
|
request.setAttribute(XML_MODE, Boolean.FALSE);
|
||||||
|
if (url.endsWith(FILE_SUFFIX)) {
|
||||||
|
url = "/" + url
|
||||||
|
.substring(0, url.length() - FILE_SUFFIX.length());
|
||||||
|
} else if (url.endsWith("/")) {
|
||||||
|
url = "/" + url.substring(0, url.length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!url.startsWith("/")) {
|
||||||
|
url = "/" + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentItem item;
|
||||||
|
// Check if the user has access to view public or preview pages
|
||||||
|
final PermissionChecker permissionChecker = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(PermissionChecker.class);
|
||||||
|
boolean hasPermission = true;
|
||||||
|
|
||||||
|
// If the remaining URL starts with "preview/", then try and
|
||||||
|
// preview this item. Otherwise look for the live item.
|
||||||
|
boolean preview = false;
|
||||||
|
if (url.startsWith(PREVIEW)) {
|
||||||
|
url = url.substring(PREVIEW.length());
|
||||||
|
preview = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preview) {
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Trying to get item for PREVIEW");
|
||||||
|
}
|
||||||
|
|
||||||
|
item = itemResolver.getItem(section, url, CMSDispatcher.PREVIEW);
|
||||||
|
if (item != null) {
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_PREVIEW, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Trying to get LIVE item");
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if this item is in the cache
|
||||||
|
//we only cache live items
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Trying to get content item for URL " + url
|
||||||
|
+ " from cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the negotiated locale
|
||||||
|
String lang = CdiUtil.createCdiUtil().findBean(
|
||||||
|
GlobalizationHelper.class).getNegotiatedLocale()
|
||||||
|
.getLanguage();
|
||||||
|
|
||||||
|
// XXX why assign a value and afterwards null??
|
||||||
|
// Effectively it just ignores the cache and forces a fallback to
|
||||||
|
// itemResover in any case. Maybe otherwise language selection /
|
||||||
|
// negotiation doesn't work correctly?
|
||||||
|
// item = itemURLCacheGet(section, url, lang);
|
||||||
|
item = null;
|
||||||
|
|
||||||
|
if (item == null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Did not find content item in cache, so trying "
|
||||||
|
+ "to retrieve and cache...");
|
||||||
|
}
|
||||||
|
//item not cached, so retreive it and cache it
|
||||||
|
item = itemResolver.getItem(section, url, "live");
|
||||||
|
// itemURLCachePut(section, url, lang, item);
|
||||||
|
} else if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found content item in cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
//ToDo
|
||||||
|
// if (s_log.isDebugEnabled() && item != null) {
|
||||||
|
// s_log.debug("Sanity check: item.getPath() is " + item.getPath());
|
||||||
|
// }
|
||||||
|
if (item != null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Content Item is not null");
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item);
|
||||||
|
|
||||||
|
if (hasPermission) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item == null && url.endsWith(INDEX_FILE)) {
|
||||||
|
|
||||||
|
if (item == null) {
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("no item found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up folder if it's an index
|
||||||
|
url = url.substring(0, url.length() - INDEX_FILE.length());
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Attempting to match folder " + url);
|
||||||
|
}
|
||||||
|
item = itemResolver.getItem(section, url, "live");
|
||||||
|
if (item != null) {
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPermission) {
|
||||||
|
|
||||||
|
// first, check if the user is logged-in
|
||||||
|
// if he isn't, give him a chance to do so...
|
||||||
|
if (!CdiUtil.createCdiUtil().findBean(Shiro.class).getSubject()
|
||||||
|
.isAuthenticated()) {
|
||||||
|
throw new LoginSignal(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentItem getItem(ContentSection section, String url) {
|
||||||
|
ItemResolver itemResolver = getItemResolver(section);
|
||||||
|
|
||||||
|
return getItem(section, url, itemResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
// synchronize access to the item-url cache
|
||||||
|
// private static synchronized void itemURLCachePut(ContentSection section,
|
||||||
|
// String sURL,
|
||||||
|
// String lang,
|
||||||
|
// Long itemID) {
|
||||||
|
//
|
||||||
|
// getItemURLCache(section).put(sURL + CACHE_KEY_DELIMITER + lang, itemID);
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Maps the content item to the URL in a cache
|
||||||
|
*
|
||||||
|
* @param section the content section in which the content item is published
|
||||||
|
* @param sURL the URL at which the content item s published
|
||||||
|
* @param lang
|
||||||
|
* @param item the content item at the URL
|
||||||
|
*/
|
||||||
|
// public static synchronized void itemURLCachePut(ContentSection section,
|
||||||
|
// String sURL,
|
||||||
|
// String lang,
|
||||||
|
// ContentItem item) {
|
||||||
|
// if (sURL == null || item == null) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("adding cached entry for url " + sURL + " and language "
|
||||||
|
// + lang);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// itemURLCachePut(section, sURL, lang, item.getObjectId());
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Removes the cache entry for the URL, sURL
|
||||||
|
*
|
||||||
|
* @param section the content section in which to remove the key
|
||||||
|
* @param sURL the cache entry key to remove
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
// public static synchronized void itemURLCacheRemove(ContentSection section,
|
||||||
|
// String sURL,
|
||||||
|
// String lang) {
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("removing cached entry for url " + sURL
|
||||||
|
// + "and language " + lang);
|
||||||
|
// }
|
||||||
|
// getItemURLCache(section).remove(sURL + CACHE_KEY_DELIMITER + lang);
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Fetches the ContentItem published at that URL from the cache.
|
||||||
|
*
|
||||||
|
* @param section the content section in which the content item is published
|
||||||
|
* @param sURL the URL for the item to fetch
|
||||||
|
* @param lang
|
||||||
|
*
|
||||||
|
* @return the ContentItem in the cache, or null
|
||||||
|
*/
|
||||||
|
// public static ContentItem itemURLCacheGet(ContentSection section,
|
||||||
|
// final String sURL,
|
||||||
|
// final String lang) {
|
||||||
|
// final Long itemID = (Long) getItemURLCache(section).get(
|
||||||
|
// sURL + CACHE_KEY_DELIMITER + lang);
|
||||||
|
//
|
||||||
|
// if (itemID == null) {
|
||||||
|
// return null;
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// final ContentItemRepository itemRepo = CdiUtil.createCdiUtil().findBean(ContentItemRepository.class);
|
||||||
|
// return itemRepo.findById(itemID);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// private static synchronized CacheTable getItemURLCache(
|
||||||
|
// ContentSection section) {
|
||||||
|
// Assert.exists(section, ContentSection.class);
|
||||||
|
// if (s_itemURLCacheMap == null) {
|
||||||
|
// initializeItemURLCache();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (s_itemURLCacheMap.get(section.getPath()) == null) {
|
||||||
|
// final CacheTable cache = new CacheTable(
|
||||||
|
// "ContentSectionServletItemURLCache" + section.getID().toString());
|
||||||
|
// s_itemURLCacheMap.put(section.getPath(), cache);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return (CacheTable) s_itemURLCacheMap.get(section.getPath());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private static synchronized void initializeItemURLCache() {
|
||||||
|
// ContentSectionCollection sections = ContentSection.getAllSections();
|
||||||
|
// s_itemURLCacheMap = new HashMap();
|
||||||
|
// while (sections.next()) {
|
||||||
|
// ContentSection section = sections.getContentSection();
|
||||||
|
// String idStr = section.getID().toString();
|
||||||
|
// String path = section.getPath();
|
||||||
|
// CacheTable itemURLCache = new CacheTable(
|
||||||
|
// "ContentSectionServletItemURLCache" + idStr);
|
||||||
|
// s_itemURLCacheMap.put(path, itemURLCache);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Checks that the current user has permission to access the admin pages.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param section
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static boolean checkAdminAccess(HttpServletRequest request,
|
||||||
|
ContentSection section) {
|
||||||
|
return CdiUtil.createCdiUtil().findBean(PermissionChecker.class)
|
||||||
|
.isPermitted(CmsConstants.PRIVILEGE_ITEMS_EDIT,
|
||||||
|
section.getRootDocumentsFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bche
|
||||||
|
*/
|
||||||
|
public abstract class AbstractItemResolver implements ItemResolver {
|
||||||
|
|
||||||
|
protected static final String TEMPLATE_CONTEXT_PREFIX = "tem_";
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#getItem(
|
||||||
|
* com.arsdigita.cms.ContentSection, java.lang.String, java.lang.String)
|
||||||
|
*/
|
||||||
|
public abstract ContentItem getItem(
|
||||||
|
ContentSection section,
|
||||||
|
String url,
|
||||||
|
String context);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#getCurrentContext(
|
||||||
|
* com.arsdigita.bebop.PageState)
|
||||||
|
*/
|
||||||
|
public abstract String getCurrentContext(PageState state);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#generateItemURL(
|
||||||
|
* com.arsdigita.bebop.PageState, java.math.BigDecimal,
|
||||||
|
* java.lang.String, com.arsdigita.cms.ContentSection,
|
||||||
|
* java.lang.String)
|
||||||
|
*/
|
||||||
|
public abstract String generateItemURL(
|
||||||
|
PageState state,
|
||||||
|
BigDecimal itemId,
|
||||||
|
String name,
|
||||||
|
ContentSection section,
|
||||||
|
String context);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#generateItemURL(
|
||||||
|
* com.arsdigita.bebop.PageState,
|
||||||
|
* java.math.BigDecimal,
|
||||||
|
* java.lang.String,
|
||||||
|
* com.arsdigita.cms.ContentSection,
|
||||||
|
* java.lang.String, java.lang.String)
|
||||||
|
*/
|
||||||
|
public abstract String generateItemURL(
|
||||||
|
PageState state,
|
||||||
|
BigDecimal itemId,
|
||||||
|
String name,
|
||||||
|
ContentSection section,
|
||||||
|
String context,
|
||||||
|
String templateContext);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#generateItemURL(
|
||||||
|
* com.arsdigita.bebop.PageState,
|
||||||
|
* com.arsdigita.cms.ContentItem,
|
||||||
|
* com.arsdigita.cms.ContentSection,
|
||||||
|
* java.lang.String)
|
||||||
|
*/
|
||||||
|
public abstract String generateItemURL(
|
||||||
|
PageState state,
|
||||||
|
ContentItem item,
|
||||||
|
ContentSection section,
|
||||||
|
String context);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#generateItemURL(
|
||||||
|
* com.arsdigita.bebop.PageState,
|
||||||
|
* com.arsdigita.cms.ContentItem,
|
||||||
|
* com.arsdigita.cms.ContentSection,
|
||||||
|
* java.lang.String, java.lang.String)
|
||||||
|
*/
|
||||||
|
public abstract String generateItemURL(
|
||||||
|
PageState state,
|
||||||
|
ContentItem item,
|
||||||
|
ContentSection section,
|
||||||
|
String context,
|
||||||
|
String templateContext);
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see com.arsdigita.cms.dispatcher.ItemResolver#getMasterPage(
|
||||||
|
* com.arsdigita.cms.ContentItem,
|
||||||
|
* javax.servlet.http.HttpServletRequest)
|
||||||
|
*/
|
||||||
|
public abstract CMSPage getMasterPage(ContentItem item,
|
||||||
|
HttpServletRequest request)
|
||||||
|
throws ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the template context from the URL and returns it, if it is there.
|
||||||
|
* Otherwise, returns null.
|
||||||
|
*
|
||||||
|
* @param inUrl the URL from which to get the template context
|
||||||
|
*
|
||||||
|
* @return the template context, or null if there is no template context
|
||||||
|
*/
|
||||||
|
public String getTemplateFromURL(String inUrl) {
|
||||||
|
String tempUrl;
|
||||||
|
String url;
|
||||||
|
if (inUrl.startsWith("/")) {
|
||||||
|
tempUrl = inUrl.substring(1);
|
||||||
|
} else {
|
||||||
|
tempUrl = inUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
String templateContext = null;
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(tempUrl, "/");
|
||||||
|
|
||||||
|
if (tokenizer.hasMoreTokens()) {
|
||||||
|
templateContext = tokenizer.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templateContext != null && templateContext.startsWith(
|
||||||
|
TEMPLATE_CONTEXT_PREFIX)) {
|
||||||
|
return templateContext.substring(TEMPLATE_CONTEXT_PREFIX.length());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the template context from the <code>inUrl</code>.
|
||||||
|
*
|
||||||
|
* @param inUrl URL, possibly including the template context.
|
||||||
|
*
|
||||||
|
* @return <code>inUrl</code> with the template context removed
|
||||||
|
*/
|
||||||
|
public String stripTemplateFromURL(String inUrl) {
|
||||||
|
String sTemplateContext = getTemplateFromURL(inUrl);
|
||||||
|
|
||||||
|
if (sTemplateContext != null) {
|
||||||
|
//there is a template context, so strip it
|
||||||
|
int iTemplateLength = TEMPLATE_CONTEXT_PREFIX.length()
|
||||||
|
+ sTemplateContext.length() + 1;
|
||||||
|
String sStripped = inUrl.substring(iTemplateLength, inUrl.length());
|
||||||
|
return sStripped;
|
||||||
|
} else {
|
||||||
|
return inUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,707 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.ChainedDispatcher;
|
||||||
|
import com.arsdigita.dispatcher.Dispatcher;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.JSPApplicationDispatcher;
|
||||||
|
import com.arsdigita.dispatcher.RedirectException;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.web.LoginSignal;
|
||||||
|
import com.arsdigita.web.URL;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.shiro.authz.AuthorizationException;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.security.User;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The CMS Dispatcher serves all request made within a content section. This
|
||||||
|
* dispatcher is called by the Subsite dispatcher.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Here are the steps for a request to
|
||||||
|
* <tt>http://yourserver/cms/cheese</tt> in excruciating detail:</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li><p>
|
||||||
|
* A client sends a request to the web server, which passes it on to the global
|
||||||
|
* ACS dispatcher.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The global ACS dispatcher examines the first part of the URL, notices that
|
||||||
|
* CMS is mounted at <tt>/cms</tt> and hands the request to the CMS
|
||||||
|
* dispatcher.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The CMS dispatcher determines whether a <tt>Page</tt> has been registered to
|
||||||
|
* the URL <tt>/cheese</tt> in this section via its
|
||||||
|
* {@link com.arsdigita.cms.dispatcher.PageResolver}.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* Since no page is registered to the URL, the CMS dispatcher asks the content
|
||||||
|
* section (via its {@link com.arsdigita.cms.dispatcher.ItemResolver}) for a
|
||||||
|
* content item for <tt>/cheese</tt> in this content section. The result of this
|
||||||
|
* process is a {@link com.arsdigita.cms.ContentItem} object.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The CMS dispatcher asks the content section for a <tt>Page</tt>
|
||||||
|
* to use as the "master template" for this item. The content section may apply
|
||||||
|
* item-, type-, or request-specific rules to make this decision (for example,
|
||||||
|
* check a user preference for normal or accessible style, or a query parameter
|
||||||
|
* for a printable version).</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The CMS dispatcher hands the master <tt>Page</tt> object to the
|
||||||
|
* {@link com.arsdigita.sitenode.SiteNodePresentationManager} to serve the
|
||||||
|
* page.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The presentation manager asks the master <tt>Page</tt> object for an XML
|
||||||
|
* document representing the data for the page.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The master template begins walking through its component hierarchy,
|
||||||
|
* converting each component to XML by calling its
|
||||||
|
* <tt>generateXML</tt> method. The component responsible for rendering the
|
||||||
|
* content item uses an {@link com.arsdigita.cms.dispatcher.XMLGenerator} to
|
||||||
|
* convert the content item to XML.</p></li>
|
||||||
|
*
|
||||||
|
* <li><p>
|
||||||
|
* The presentation manager receives the completed XML document, and selects an
|
||||||
|
* XSL transformer to use for generating the HTML. The stylesheet on which the
|
||||||
|
* transformer is based contains templates for all styles and all content types
|
||||||
|
* in the content section, in particular those from the file
|
||||||
|
* <tt>cms-item.xsl</tt>.</p></li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @author Uday Mathur (umathur@arsdigita.com)
|
||||||
|
* @author Jack Chung (flattop@arsdigita.com)
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CMSDispatcher implements Dispatcher, ChainedDispatcher {
|
||||||
|
|
||||||
|
private static Logger s_log = Logger.getLogger(CMSDispatcher.class);
|
||||||
|
|
||||||
|
public static final String CONTENT_SECTION
|
||||||
|
= "com.arsdigita.cms.dispatcher.section";
|
||||||
|
|
||||||
|
public static final String CONTENT_ITEM
|
||||||
|
= "com.arsdigita.cms.dispatcher.item";
|
||||||
|
|
||||||
|
public static final String[] INDEX_FILES = {
|
||||||
|
"index.jsp", "index.html", "index.htm"};
|
||||||
|
|
||||||
|
private static final String DEBUG = "/debug";
|
||||||
|
private static final String ADMIN_SECTION = "admin";
|
||||||
|
|
||||||
|
public static final String ADMIN_URL = "admin/index";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context for previewing items
|
||||||
|
*/
|
||||||
|
public static final String PREVIEW = "preview";
|
||||||
|
|
||||||
|
// Content section cache
|
||||||
|
private static HashMap s_pageResolverCache = new HashMap();
|
||||||
|
private static HashMap s_itemResolverCache = new HashMap();
|
||||||
|
private static HashMap s_xmlGeneratorCache = new HashMap();
|
||||||
|
|
||||||
|
private boolean m_adminPagesOnly = false;
|
||||||
|
|
||||||
|
public CMSDispatcher() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSDispatcher(boolean adminOnly) {
|
||||||
|
m_adminPagesOnly = adminOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles requests made to a CMS package instance. 1) fetches the current
|
||||||
|
* content section 2) fetches the resource mapped to the current section/URL
|
||||||
|
* 3) if no resource, fetches the item associated with the current
|
||||||
|
* section/URL 4) if no item, passes request to the JSP dispatcher, which
|
||||||
|
* serves JSP's, HTML pages, and media from the cms/packages/www directory
|
||||||
|
*
|
||||||
|
* @param request The request
|
||||||
|
* @param response The response
|
||||||
|
* @param actx The request context
|
||||||
|
*/
|
||||||
|
public void dispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Dispatching request for " + new URL(request)
|
||||||
|
.toDebugString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the path to the current site node.
|
||||||
|
String processedUrl = actx.getProcessedURLPart();
|
||||||
|
String webappURLContext = request.getContextPath();
|
||||||
|
if (processedUrl.startsWith(webappURLContext)) {
|
||||||
|
processedUrl = processedUrl.substring(webappURLContext.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Determined the path to the current site node; it "
|
||||||
|
+ "is '" + processedUrl + "' according to the "
|
||||||
|
+ "request context");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the path within the site node.
|
||||||
|
String remainingUrl = actx.getRemainingURLPart();
|
||||||
|
if (remainingUrl.endsWith("/")) {
|
||||||
|
remainingUrl = remainingUrl.substring(0, remainingUrl.length() - 1);
|
||||||
|
} else if (remainingUrl.endsWith(ItemDispatcher.FILE_SUFFIX)) {
|
||||||
|
remainingUrl = remainingUrl.substring(0, remainingUrl.length()
|
||||||
|
- ItemDispatcher.FILE_SUFFIX
|
||||||
|
.length());
|
||||||
|
} else if (remainingUrl.equals("")) {
|
||||||
|
remainingUrl = "index";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Determined the path after the current site node; "
|
||||||
|
+ "it is '" + remainingUrl + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the current content section.
|
||||||
|
ContentSection section = null;
|
||||||
|
try {
|
||||||
|
section = findContentSection(processedUrl);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new ServletException(ex);
|
||||||
|
}
|
||||||
|
request.setAttribute(CONTENT_SECTION, section);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found content section '" + section + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check user access to this page and deny access if necessary.
|
||||||
|
checkUserAccess(request, response, actx);
|
||||||
|
|
||||||
|
// Look for a site-node-specific asset (if any).
|
||||||
|
// KG: This hack will be replaced by a ChainedDispatcher
|
||||||
|
try {
|
||||||
|
s_log.debug("Looking for a site node asset");
|
||||||
|
|
||||||
|
String siteNodeAssetURL = getSiteNodeAsset(request, actx);
|
||||||
|
if (siteNodeAssetURL != null) {
|
||||||
|
s_log.debug("Site node asset found at '" + siteNodeAssetURL
|
||||||
|
+ "'");
|
||||||
|
|
||||||
|
DispatcherHelper.cacheDisable(response);
|
||||||
|
DispatcherHelper.setRequestContext(request, actx);
|
||||||
|
DispatcherHelper.forwardRequestByPath(siteNodeAssetURL,
|
||||||
|
request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_log.debug("No site node asset found; proceeding with normal "
|
||||||
|
+ "dispatching");
|
||||||
|
} catch (RedirectException e) {
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the requested resource (if any).
|
||||||
|
ResourceHandler resource = getResource(section, remainingUrl);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Got a resource '" + resource + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource != null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found resource '" + remainingUrl + "'; "
|
||||||
|
+ "dispatching to it");
|
||||||
|
}
|
||||||
|
|
||||||
|
s_log.info("resource dispatch for " + remainingUrl);
|
||||||
|
// Found resource, now serve it.
|
||||||
|
// NB, ResouceHandler implementations should take care of caching options
|
||||||
|
resource.dispatch(request, response, actx);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("No resource found at '" + remainingUrl + "'; "
|
||||||
|
+ "searching for a previewable content item at "
|
||||||
|
+ "this path");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the remaining URL starts with "preview/", then try and
|
||||||
|
// preview this item. Otherwise look for the live item.
|
||||||
|
boolean preview = false;
|
||||||
|
if (remainingUrl.startsWith(PREVIEW)) {
|
||||||
|
remainingUrl = remainingUrl.substring(PREVIEW.length());
|
||||||
|
preview = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for published / previewable item.
|
||||||
|
ContentItem item = null;
|
||||||
|
|
||||||
|
// Check if the user has access to view public pages
|
||||||
|
final PermissionChecker permissionChecker = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(PermissionChecker.class);
|
||||||
|
|
||||||
|
if (permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item)) {
|
||||||
|
if (preview) {
|
||||||
|
item = getContentItem(section,
|
||||||
|
remainingUrl,
|
||||||
|
CMSDispatcher.PREVIEW);
|
||||||
|
} else {
|
||||||
|
item = getContentItem(section,
|
||||||
|
remainingUrl,
|
||||||
|
"live");
|
||||||
|
}
|
||||||
|
if (item != null) {
|
||||||
|
request.setAttribute(CONTENT_ITEM, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item != null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found item " + item + "; serving it");
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatcherHelper.cacheDisable(response);
|
||||||
|
preview(request, response, actx);
|
||||||
|
} else {
|
||||||
|
s_log.debug("No item to preview found; falling back to "
|
||||||
|
+ "JSP dispatcher to look for some concrete "
|
||||||
|
+ "resource in the file system");
|
||||||
|
|
||||||
|
// If no resource was found, look for a JSP page.
|
||||||
|
JSPApplicationDispatcher jsp = JSPApplicationDispatcher
|
||||||
|
.getInstance();
|
||||||
|
//DispatcherHelper.cacheDisable(response);
|
||||||
|
jsp.dispatch(request, response, actx);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int chainedDispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (m_adminPagesOnly) {
|
||||||
|
String url = actx.getRemainingURLPart();
|
||||||
|
|
||||||
|
if (url.endsWith(ItemDispatcher.FILE_SUFFIX)) {
|
||||||
|
url = url.substring(0, url.length() - ItemDispatcher.FILE_SUFFIX
|
||||||
|
.length());
|
||||||
|
} else if (url.endsWith("/")) {
|
||||||
|
url = url.substring(0, url.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.equals(ADMIN_URL)) {
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Resolving admin URL '" + url + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(request, response, actx);
|
||||||
|
|
||||||
|
return ChainedDispatcher.DISPATCH_BREAK;
|
||||||
|
} else {
|
||||||
|
return ChainedDispatcher.DISPATCH_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(request, response, actx);
|
||||||
|
return ChainedDispatcher.DISPATCH_BREAK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the user is logged in and is able to view the page.
|
||||||
|
* Subclasses can override this method if they need to, but should always be
|
||||||
|
* sure to call super.checkUserAccess(...)
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @param response The HTTP response
|
||||||
|
* @param actx The request context
|
||||||
|
*
|
||||||
|
* @exception AccessDeniedException if the user does not have access.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void checkUserAccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws ServletException, AuthorizationException {
|
||||||
|
|
||||||
|
final Shiro shiro = CdiUtil.createCdiUtil().findBean(Shiro.class);
|
||||||
|
User user = shiro.getUser();
|
||||||
|
final PermissionChecker permissionChecker = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(PermissionChecker.class);
|
||||||
|
|
||||||
|
ContentSection section = getContentSection(request);
|
||||||
|
|
||||||
|
if (isAdminPage(actx.getRemainingURLPart())) {
|
||||||
|
|
||||||
|
// Handle admin page requests.
|
||||||
|
// If the user is not logged in, redirect to the login page.
|
||||||
|
// Otherwise, perform the Admin Pages access check.
|
||||||
|
if (user == null) {
|
||||||
|
redirectToLoginPage(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//if (!sm.canAccess(user, SecurityManager.ADMIN_PAGES)) {
|
||||||
|
permissionChecker.checkPermission(CmsConstants.PRIVILEGE_ITEMS_EDIT,
|
||||||
|
section.getRootDocumentsFolder());
|
||||||
|
} else {
|
||||||
|
// For public page requests, use the SecurityManager to check access
|
||||||
|
// SecurityManager.canAccess(user, SecurityManager.PUBLIC_PAGES) must
|
||||||
|
permissionChecker.checkPermission(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED,
|
||||||
|
section.getRootDocumentsFolder());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content section from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content section
|
||||||
|
*
|
||||||
|
* @pre ( state != null )
|
||||||
|
*/
|
||||||
|
public static ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
return (ContentSection) request.getAttribute(CONTENT_SECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content item from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content item
|
||||||
|
*
|
||||||
|
* @pre ( state != null )
|
||||||
|
*/
|
||||||
|
public static ContentItem getContentItem(HttpServletRequest request) {
|
||||||
|
return (ContentItem) request.getAttribute(CONTENT_ITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the current content section using the remaining URL stored in
|
||||||
|
* the request context object and the SiteNode class.
|
||||||
|
*
|
||||||
|
* @param url The section URL stub
|
||||||
|
*
|
||||||
|
* @return The current Content Section
|
||||||
|
*/
|
||||||
|
protected ContentSection findContentSection(String url) {
|
||||||
|
|
||||||
|
// MP: This is a hack to get the debugging info in
|
||||||
|
// SiteNodePresentationManager.servePage, but since it's
|
||||||
|
// debugging info...
|
||||||
|
// Remove /debug from the start of the URL if it exists.
|
||||||
|
if (url.startsWith(DEBUG)) {
|
||||||
|
url = url.substring(DEBUG.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String debugXMLString = "/xml";
|
||||||
|
if (url.startsWith(debugXMLString)) {
|
||||||
|
url = url.substring(debugXMLString.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String debugXSLString = "/xsl";
|
||||||
|
if (url.startsWith(debugXSLString)) {
|
||||||
|
url = url.substring(debugXSLString.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the current site node from the URL.
|
||||||
|
final ContentSectionRepository sectionRepo = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(ContentSectionRepository.class);
|
||||||
|
ContentSection section = sectionRepo.findByLabel(url);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a resource based on the URL stub.
|
||||||
|
*
|
||||||
|
* @param section The current content section
|
||||||
|
* @param url The section-relative URL
|
||||||
|
*
|
||||||
|
* @return A ResourceHandler resource or null if none exists.
|
||||||
|
*
|
||||||
|
* @pre (url != null)
|
||||||
|
*/
|
||||||
|
protected ResourceHandler getResource(ContentSection section, String url)
|
||||||
|
throws ServletException {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Searching for a resource for the URL fragment '" + url
|
||||||
|
+ "' under " + section);
|
||||||
|
}
|
||||||
|
|
||||||
|
final PageResolver pageResolver = CMSDispatcher.getPageResolver(section);
|
||||||
|
|
||||||
|
final ResourceHandler handler = pageResolver.getPage(url);
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup a content item by section and URL.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
* @param url The URL relative to the content section
|
||||||
|
* @param context The use context
|
||||||
|
*
|
||||||
|
* @return The item associated with the URL, or null if no such item exists
|
||||||
|
*/
|
||||||
|
protected ContentItem getContentItem(ContentSection section, String url,
|
||||||
|
String context)
|
||||||
|
throws ServletException {
|
||||||
|
|
||||||
|
ItemResolver itemResolver = CMSDispatcher.getItemResolver(section);
|
||||||
|
|
||||||
|
ContentItem item = null;
|
||||||
|
item = itemResolver.getItem(section, url, context);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preview the current content item.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @param response The HTTP response
|
||||||
|
* @param actx The request context
|
||||||
|
*/
|
||||||
|
protected void preview(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
ContentSection section = getContentSection(request);
|
||||||
|
ContentItem item = getContentItem(request);
|
||||||
|
|
||||||
|
ItemResolver itemResolver = CMSDispatcher.getItemResolver(section);
|
||||||
|
CMSPage page = itemResolver.getMasterPage(item, request);
|
||||||
|
page.dispatch(request, response, actx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the page resolver cache.
|
||||||
|
*
|
||||||
|
* @param section The current content section
|
||||||
|
* @param url The section-relative URL
|
||||||
|
*/
|
||||||
|
public static void releaseResource(ContentSection section, String url) {
|
||||||
|
final String pageResolverClassName = section.getPageResolverClass();
|
||||||
|
final PageResolver pageResolver;
|
||||||
|
try {
|
||||||
|
pageResolver = (PageResolver) Class.forName(pageResolverClassName)
|
||||||
|
.newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
pageResolver.releasePage(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the PageResolver for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The PageResolver associated with the content section
|
||||||
|
*/
|
||||||
|
public static PageResolver getPageResolver(ContentSection section) {
|
||||||
|
s_log.debug("Getting the page resolver");
|
||||||
|
|
||||||
|
final String name = section.getLabel();
|
||||||
|
PageResolver pr = (PageResolver) s_pageResolverCache.get(name);
|
||||||
|
|
||||||
|
if (pr == null) {
|
||||||
|
s_log.debug("The page resolver was not cached; fetching a new "
|
||||||
|
+ "one and placing it in the cache");
|
||||||
|
|
||||||
|
final String pageResolverClassName = section.getPageResolverClass();
|
||||||
|
final PageResolver pageResolver;
|
||||||
|
try {
|
||||||
|
pageResolver = (PageResolver) Class.forName(
|
||||||
|
pageResolverClassName)
|
||||||
|
.newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
s_pageResolverCache.put(name, pageResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the ItemResolver for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The ItemResolver associated with the content section
|
||||||
|
*/
|
||||||
|
public static ItemResolver getItemResolver(ContentSection section) {
|
||||||
|
String name = section.getLabel();
|
||||||
|
ItemResolver itemResolver = (ItemResolver) s_itemResolverCache.get(name);
|
||||||
|
if (itemResolver == null) {
|
||||||
|
final String itemResolverClassName = section.getItemResolverClass();
|
||||||
|
try {
|
||||||
|
itemResolver = (ItemResolver) Class.forName(
|
||||||
|
itemResolverClassName).newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
s_itemResolverCache.put(name, itemResolver);
|
||||||
|
}
|
||||||
|
return itemResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the XMLGenerator for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The XMLGenerator associated with the content section
|
||||||
|
*/
|
||||||
|
public static XMLGenerator getXMLGenerator(ContentSection section) {
|
||||||
|
String name = section.getLabel();
|
||||||
|
XMLGenerator xmlGenerator = (XMLGenerator) s_xmlGeneratorCache.get(name);
|
||||||
|
if (xmlGenerator == null) {
|
||||||
|
final String xmlGeneratorClassName = section.getXmlGeneratorClass();
|
||||||
|
try {
|
||||||
|
xmlGenerator = (XMLGenerator) Class.forName(
|
||||||
|
xmlGeneratorClassName).newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
s_xmlGeneratorCache.put(name, xmlGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return xmlGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this URL correspond to an admin page?
|
||||||
|
*/
|
||||||
|
protected boolean isAdminPage(String url) {
|
||||||
|
|
||||||
|
// MP: This is a hack to get the debugging info in
|
||||||
|
// SiteNodePresentationManager.servePage, but since it's
|
||||||
|
// debugging info...
|
||||||
|
// Remove /debug from the start of the URL if it exists.
|
||||||
|
if (url.startsWith(DEBUG)) {
|
||||||
|
url = url.substring(DEBUG.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (url != null && (url.startsWith(ADMIN_SECTION) || url.startsWith(
|
||||||
|
PREVIEW)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects the client to the login page, setting the return url to the
|
||||||
|
* current request URI.
|
||||||
|
*
|
||||||
|
* @exception ServletException If there is an exception thrown while trying
|
||||||
|
* to redirect, wrap that exception in a
|
||||||
|
* ServletException
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected void redirectToLoginPage(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp)
|
||||||
|
throws ServletException {
|
||||||
|
throw new LoginSignal(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
// modified from JSPApplicationDispatcher
|
||||||
|
private String getSiteNodeAsset(HttpServletRequest request,
|
||||||
|
RequestContext actx)
|
||||||
|
throws RedirectException {
|
||||||
|
|
||||||
|
String siteNodeAssetURL = null;
|
||||||
|
|
||||||
|
ServletContext sctx = actx.getServletContext();
|
||||||
|
String processedURL = actx.getProcessedURLPart();
|
||||||
|
String remainingURL = actx.getRemainingURLPart();
|
||||||
|
// REMOVE THIS HACK ONCE we have working publish to file code in the build
|
||||||
|
//String templateRoot = PublishToFile.getDefaultDestinationForType(Template.class);
|
||||||
|
String templateRoot = null;
|
||||||
|
|
||||||
|
/* Allow a graceful early exit if publishToFile is not initialized */
|
||||||
|
if (null == templateRoot) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
File siteNodeAssetRoot = new File(templateRoot, processedURL);
|
||||||
|
File assetFile = new File(siteNodeAssetRoot, remainingURL);
|
||||||
|
|
||||||
|
String contextPath = request.getContextPath();
|
||||||
|
|
||||||
|
if (assetFile.isDirectory()) {
|
||||||
|
|
||||||
|
if (remainingURL.endsWith("/")) {
|
||||||
|
throw new RedirectException(actx.getOriginalURL() + "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < INDEX_FILES.length; i++) {
|
||||||
|
File indexFile = new File(assetFile, INDEX_FILES[i]);
|
||||||
|
if (indexFile.exists()) {
|
||||||
|
assetFile = indexFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assetFile.exists()) {
|
||||||
|
siteNodeAssetURL = contextPath + "/" + templateRoot
|
||||||
|
+ processedURL + remainingURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return siteNodeAssetURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,317 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.BebopConfig;
|
||||||
|
import com.arsdigita.bebop.Container;
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.Page;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.page.PageTransformer;
|
||||||
|
import com.arsdigita.cms.ContentSectionServlet;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.security.User;
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentItemRepository;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A <tt>CMSPage</tt> is a Bebop {@link com.arsdigita.bebop.Page}
|
||||||
|
* implementation of the {@link com.arsdigita.cms.dispatcher.ResourceHandler}
|
||||||
|
* interface.</p>
|
||||||
|
*
|
||||||
|
* <p>It stores the current {@link com.arsdigita.cms.ContentSection} and, if
|
||||||
|
* applicable, the {@link com.arsdigita.cms.ContentItem} in the page state as
|
||||||
|
* request local objects. Components that are part of the <tt>CMSPage</tt>
|
||||||
|
* may access these objects by calling:</p>
|
||||||
|
* <blockquote><code><pre>
|
||||||
|
* getContentSection(PageState state);
|
||||||
|
* </pre></code></blockquote>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @author Uday Mathur (umathur@arsdigita.com)
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class CMSPage extends Page implements ResourceHandler {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(CMSPage.class);
|
||||||
|
|
||||||
|
/** The global assets URL stub XML parameter name. */
|
||||||
|
public final static String ASSETS = "ASSETS";
|
||||||
|
|
||||||
|
/** The XML page class. */
|
||||||
|
public final static String PAGE_CLASS = "CMS";
|
||||||
|
|
||||||
|
/** Map of XML parameters */
|
||||||
|
private HashMap m_params;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
private PageTransformer m_transformer;
|
||||||
|
|
||||||
|
public CMSPage() {
|
||||||
|
super();
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSPage(String title) {
|
||||||
|
super(title);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSPage(String title, Container panel) {
|
||||||
|
super(title, panel);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSPage(Label title) {
|
||||||
|
super(title);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSPage(Label title, Container panel) {
|
||||||
|
super(title, panel);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the page.
|
||||||
|
*/
|
||||||
|
protected void buildPage() {
|
||||||
|
// Set the class attribute value. May be overwritten by child class
|
||||||
|
// to hold a more specific value
|
||||||
|
setClassAttr(PAGE_CLASS);
|
||||||
|
|
||||||
|
// Global XML params.
|
||||||
|
// MP: This only works with older versions of Xalan.
|
||||||
|
m_params = new HashMap();
|
||||||
|
setXMLParameter(ASSETS, Utilities.getGlobalAssetsURL());
|
||||||
|
|
||||||
|
// MP: This is a hack to so that the XML params work with the newer
|
||||||
|
// version of Xalan.
|
||||||
|
// Sets attribute in SimpleComponent, attributes of the same name will
|
||||||
|
// be overweritten.
|
||||||
|
setAttribute(ASSETS, Utilities.getGlobalAssetsURL());
|
||||||
|
|
||||||
|
// Make sure the error display gets rendered.
|
||||||
|
getErrorDisplay().setIdAttr("page-body");
|
||||||
|
|
||||||
|
final Class<PresentationManager> presenterClass = BebopConfig.getConfig().getPresenterClass();
|
||||||
|
final PresentationManager pm;
|
||||||
|
try {
|
||||||
|
pm = presenterClass.newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pm instanceof PageTransformer) {
|
||||||
|
m_transformer = (PageTransformer) pm;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_transformer = new PageTransformer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes and locks the page. If the page is already locked, does nothing.
|
||||||
|
*
|
||||||
|
* This method is called by the {@link com.arsdigita.dispatcher.Dispatcher}
|
||||||
|
* that initializes this page.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized void init() {
|
||||||
|
s_log.debug("Initializing the page");
|
||||||
|
|
||||||
|
if (!isLocked()) {
|
||||||
|
s_log.debug("The page hasn't been locked; locking it now");
|
||||||
|
|
||||||
|
lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the value of the XML parameter.
|
||||||
|
*
|
||||||
|
* @param name The parameter name
|
||||||
|
* @return The parameter value
|
||||||
|
* @pre (name != null)
|
||||||
|
*/
|
||||||
|
public String getXMLParameter(String name) {
|
||||||
|
return (String) m_params.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an XML parameter.
|
||||||
|
*
|
||||||
|
* @param name The parameter name
|
||||||
|
* @param value The parameter value
|
||||||
|
* @pre (name != null)
|
||||||
|
*/
|
||||||
|
public void setXMLParameter(String name, String value) {
|
||||||
|
m_params.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content section.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return The current content section
|
||||||
|
*
|
||||||
|
* @deprecated use com.arsdigita.cms.CMS.getContext().getContentSection()
|
||||||
|
* instead
|
||||||
|
* Despite of being deprecated it can not be removed because it
|
||||||
|
* is required by the interface Resourcehandler which is
|
||||||
|
* implemented by this class.
|
||||||
|
* On the other hand, if deprecated, implementing ResourceHandler
|
||||||
|
* may not be required
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
// Resets all content sections associations.
|
||||||
|
// return ContentSectionDispatcher.getContentSection(request);
|
||||||
|
return ContentSectionServlet.getContentSection(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content section.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @return The current content section
|
||||||
|
*
|
||||||
|
* @deprecated use com.arsdigita.cms.CMS.getContext().getContentSection()
|
||||||
|
* instead
|
||||||
|
* Despite of being deprecated it can not be removed because it
|
||||||
|
* is required by ContentItemPage which extends CMSPage and
|
||||||
|
* uses this method.
|
||||||
|
*/
|
||||||
|
public ContentSection getContentSection(PageState state) {
|
||||||
|
return getContentSection(state.getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content item.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return The current content item
|
||||||
|
*
|
||||||
|
* @deprecated use com.arsdigita.cms.CMS.getContext().getContentItem()
|
||||||
|
* instead
|
||||||
|
* Despite of being deprecated it can not be removed because it
|
||||||
|
* is required by the interface Resourcehandler which is
|
||||||
|
* implemented by this class.
|
||||||
|
* On the other hand, if deprecated, implementing ResourceHandler
|
||||||
|
* may not be required
|
||||||
|
*/
|
||||||
|
public ContentItem getContentItem(HttpServletRequest request) {
|
||||||
|
// resets all content item associations
|
||||||
|
return ContentSectionDispatcher.getContentItem(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content item.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @return The current content item
|
||||||
|
* @deprecated use com.arsdigita.cms.CMS.getContext().getContentItem()
|
||||||
|
* instead.
|
||||||
|
* Despite of being deprecated it can not be removed because it
|
||||||
|
* is required by ContentItemPage which extends CMSPage and
|
||||||
|
* uses this method.
|
||||||
|
*/
|
||||||
|
public ContentItem getContentItem(PageState state) {
|
||||||
|
return getContentItem(state.getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services the Bebop page.
|
||||||
|
*
|
||||||
|
* @param request The servlet request object
|
||||||
|
* @param response the servlet response object
|
||||||
|
* @param actx The request context
|
||||||
|
*
|
||||||
|
* @pre m_transformer != null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void dispatch(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response ,
|
||||||
|
final RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
final CcmApplication app = Web.getWebContext().getApplication();
|
||||||
|
ContentSection section = null;
|
||||||
|
|
||||||
|
if (app == null) {
|
||||||
|
//Nothing to do
|
||||||
|
} else if(app instanceof ContentSection) {
|
||||||
|
section = (ContentSection) app;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String itemId = request.getParameter("item_id");
|
||||||
|
|
||||||
|
if (itemId != null) {
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final ContentItemRepository itemRepo = cdiUtil.findBean(ContentItemRepository.class);
|
||||||
|
final ContentItem item = itemRepo.findById(Long.parseLong("item_id"));
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
permissionChecker.checkPermission(CmsConstants.PRIVILEGE_ITEMS_PREVIEW,
|
||||||
|
item);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Document document = buildDocument(request, response);
|
||||||
|
|
||||||
|
m_transformer.servePage(document, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrites bebop.Page#generateXMLHelper to add the name of the user
|
||||||
|
* logged in to the page (displayed as part of the header).
|
||||||
|
* @param ps
|
||||||
|
* @param parent
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Element generateXMLHelper(PageState ps, Document parent) {
|
||||||
|
Element page = super.generateXMLHelper(ps,parent);
|
||||||
|
final User user = CdiUtil.createCdiUtil().findBean(Shiro.class).getUser();
|
||||||
|
if ( user != null ) {
|
||||||
|
page.addAttribute("name",user.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.cms.ContentSectionServlet;
|
||||||
|
import com.arsdigita.dispatcher.Dispatcher;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.contentsection.ContentSectionConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the dispatcher for content-sections. It maintains a
|
||||||
|
* ContentItem-to-Template cache Code that modifies a published ContentItem's
|
||||||
|
* template must update the cache in this class by calling the appropriate cache
|
||||||
|
* methods.
|
||||||
|
*
|
||||||
|
* @author bche@redhat.com
|
||||||
|
*/
|
||||||
|
public class ContentItemDispatcher implements Dispatcher {
|
||||||
|
|
||||||
|
/** cache for the template resolver */
|
||||||
|
public static Map s_templateResolverCache = Collections
|
||||||
|
.synchronizedMap(new HashMap());
|
||||||
|
|
||||||
|
/** */
|
||||||
|
protected ItemXML m_itemXML;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private Logger instance for debugging purpose.
|
||||||
|
*/
|
||||||
|
private static final Logger s_log = Logger.getLogger(
|
||||||
|
ContentItemDispatcher.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* */
|
||||||
|
public ContentItemDispatcher() {
|
||||||
|
m_itemXML = new ItemXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see com.arsdigita.dispatcher.Dispatcher#dispatch (HttpServletRequest,
|
||||||
|
* HttpServletResponse, RequestContext)
|
||||||
|
*/
|
||||||
|
public void dispatch(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response,
|
||||||
|
final RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
Boolean bXMLMode = (Boolean) request
|
||||||
|
.getAttribute(ContentSectionServlet.XML_MODE);
|
||||||
|
if (bXMLMode != null && bXMLMode.booleanValue()) {
|
||||||
|
//if this is XML mode, then use itemXML
|
||||||
|
m_itemXML.dispatch(request, response, actx);
|
||||||
|
} else {
|
||||||
|
//this is normal dispatching
|
||||||
|
|
||||||
|
//get the Content Item
|
||||||
|
//final ContentItem item = (ContentItem) request.getAttribute
|
||||||
|
// (ContentSectionServlet.CONTENT_ITEM);
|
||||||
|
final ContentItem item = getContentItem(request);
|
||||||
|
//get the Content Section
|
||||||
|
final ContentSection section = (ContentSection) Web.getWebContext()
|
||||||
|
.getApplication();
|
||||||
|
|
||||||
|
Assert.exists(item);
|
||||||
|
|
||||||
|
//get the item's template
|
||||||
|
// final String sTemplateURL = getTemplatePath(item, request, actx);
|
||||||
|
|
||||||
|
//dispatch to the template
|
||||||
|
DispatcherHelper.setRequestContext(request, actx);
|
||||||
|
DispatcherHelper.forwardRequestByPath(null, request,
|
||||||
|
response);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content item from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content item
|
||||||
|
*
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public static ContentItem getContentItem(HttpServletRequest request) {
|
||||||
|
return (ContentItem) request.getAttribute(
|
||||||
|
ContentSectionServlet.CONTENT_ITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
// //synchronize access to the cache
|
||||||
|
// private static synchronized void cachePut(BigDecimal contentItemID,
|
||||||
|
// String sTemplatePath) {
|
||||||
|
// s_cache.put(contentItemID, sTemplatePath);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private static synchronized void cacheRemove(BigDecimal contentItemID) {
|
||||||
|
// s_cache.remove(contentItemID);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Method cacheRemove. Removes the cached template path for the contentItem
|
||||||
|
// * item
|
||||||
|
// *
|
||||||
|
// * @param item
|
||||||
|
// */
|
||||||
|
// public static void cacheRemove(ContentItem item) {
|
||||||
|
// if (item == null) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("removing cached entry for item " + item.getName()
|
||||||
|
// + " with ID " + item.getID());
|
||||||
|
// }
|
||||||
|
// s_cache.remove(item.getID());
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method cachePut. Maps the ContentItem item to the template t in the cache
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @param t
|
||||||
|
*/
|
||||||
|
// public static void cachePut(ContentItem item, Template t) {
|
||||||
|
// ContentSection section = item.getContentSection();
|
||||||
|
// String sPath = getTemplatePath(section, t);
|
||||||
|
//
|
||||||
|
// //only cache live items
|
||||||
|
// if (item == null || item.getVersion().compareTo(ContentItem.LIVE) != 0) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("updating mapping for item " + item.getName()
|
||||||
|
// + " with ID " + item.getID() + " in section " + section
|
||||||
|
// .getName() + " of type " + item.getContentType().getName()
|
||||||
|
// + " to template " + sPath);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// cachePut(item.getID(), sPath);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method cachePut. Maps all the content items of ContentType type and in
|
||||||
|
* ContentSection section that don't have their own templates to the
|
||||||
|
* template t in the cache
|
||||||
|
*
|
||||||
|
* @param section
|
||||||
|
* @param type
|
||||||
|
* @param t
|
||||||
|
*/
|
||||||
|
// public static void cachePut(ContentSection section,
|
||||||
|
// ContentType type,
|
||||||
|
// Template t) {
|
||||||
|
// s_log.debug("updating cache for section " + section.getName()
|
||||||
|
// + " and type " + type.getName());
|
||||||
|
//
|
||||||
|
// //get all the items in the section
|
||||||
|
// ItemCollection items = section.getItems();
|
||||||
|
//
|
||||||
|
// //filter items by content type
|
||||||
|
// BigDecimal typeID = type.getID();
|
||||||
|
// Filter filter = items.addFilter("type.id = :typeID");
|
||||||
|
// filter.set("typeID", typeID);
|
||||||
|
//
|
||||||
|
// //get only live items
|
||||||
|
// Filter liveFilter = items.addFilter("version = '" + ContentItem.LIVE
|
||||||
|
// + "'");
|
||||||
|
//
|
||||||
|
// //filter out content items in ContentSection section of
|
||||||
|
// //ContentType type with a template for the "public" context
|
||||||
|
// Filter itemsFilter = items.addNotInSubqueryFilter("id",
|
||||||
|
// "com.arsdigita.cms.ItemsWithTemplateMapping");
|
||||||
|
// itemsFilter.set("sectionId", section.getID());
|
||||||
|
// itemsFilter.set("typeId", type.getID());
|
||||||
|
//
|
||||||
|
// //TODO: FILTER OUT CONTENT ITEMS IN THIS SECTION OF THIS TYPE
|
||||||
|
// //WITH A TEMPLATE FOR THE "PUBLIC" CONTEXT
|
||||||
|
// /*
|
||||||
|
// * select items.item_id
|
||||||
|
// * from cms_items items, cms_item_template_map map
|
||||||
|
// * where items.item_id = map.item_id
|
||||||
|
// * and use_context = 'public'
|
||||||
|
// * and items.version = 'live'
|
||||||
|
// * and items.section_id = :section_id
|
||||||
|
// * and items.type_id = :type_id
|
||||||
|
// */
|
||||||
|
// synchronized (s_cache) {
|
||||||
|
// //update the cache for all items
|
||||||
|
// while (items.next()) {
|
||||||
|
// cachePut(items.getContentItem(), t);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private static String getTemplatePath(ContentSection section,
|
||||||
|
// Template template) {
|
||||||
|
// //the template path is
|
||||||
|
// // TEMPLATE_ROOT/[content-section-name]/[template-path]
|
||||||
|
// final String sep = java.io.File.separator;
|
||||||
|
// String sPath = ContentSectionConfig.getConfig().getTemplateRoot() + sep
|
||||||
|
// + section.getName() + sep + template.getPath();
|
||||||
|
// return sPath;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private static void updateTemplateCache(ContentSection section,
|
||||||
|
// ContentItem item,
|
||||||
|
// String sTemplatePath) {
|
||||||
|
// //use the live version of the item for the cache
|
||||||
|
// item = item.getLiveVersion();
|
||||||
|
// s_log.debug("updating mapping for item " + item.getName() + " with ID "
|
||||||
|
// + item.getID() + " in section " + item.getContentSection()
|
||||||
|
// .getName() + " of type " + item.getContentType().getName()
|
||||||
|
// + " to template " + sTemplatePath);
|
||||||
|
// cachePut(item.getID(), sTemplatePath);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String cacheGet(BigDecimal key) {
|
||||||
|
// return (String) s_cache.get(key);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private String getTemplatePath(ContentItem item,
|
||||||
|
// HttpServletRequest req,
|
||||||
|
// RequestContext ctx) {
|
||||||
|
//
|
||||||
|
// //check if the template path is cached
|
||||||
|
// //BigDecimal id = item.getID();
|
||||||
|
// //String sPath = cacheGet(id);
|
||||||
|
// //return from cache
|
||||||
|
// // current cache scheme doesn't work when there are
|
||||||
|
// //multiple templates per item, as would happen with
|
||||||
|
// // multiple template contexts or in the case of
|
||||||
|
// //category item resolution, more than one category for
|
||||||
|
// //the item.
|
||||||
|
// //if (sPath != null) {
|
||||||
|
// //s_log.debug("returning template path from cache");
|
||||||
|
// // return sPath;
|
||||||
|
// //}
|
||||||
|
// //s_log.debug("template path not in cache, so fecthing");
|
||||||
|
// //template is not in the cache, so retrieve it and place it in
|
||||||
|
// //the cache
|
||||||
|
// String sPath = fetchTemplateURL(item, req, ctx);
|
||||||
|
// //cachePut(id, sPath);
|
||||||
|
//
|
||||||
|
// return sPath;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the URL of a template for an item. The returned URL is relative
|
||||||
|
* to the webapp context.
|
||||||
|
*/
|
||||||
|
// public String fetchTemplateURL(ContentItem item,
|
||||||
|
// HttpServletRequest request,
|
||||||
|
// RequestContext actx) {
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("fetching URL for item " + item.getName() + " with ID "
|
||||||
|
// + item.getID());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ContentSection section = item.getContentSection();
|
||||||
|
// String templateURL = getTemplateResolver(section).getTemplate(section,
|
||||||
|
// item,
|
||||||
|
// request);
|
||||||
|
//
|
||||||
|
// if (s_log.isDebugEnabled()) {
|
||||||
|
// s_log.debug("templateURL is " + templateURL);
|
||||||
|
// }
|
||||||
|
// return templateURL;
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Fetches the TemplateResolver for a content section. Checks cache first.
|
||||||
|
// *
|
||||||
|
// * @param section The content section
|
||||||
|
// *
|
||||||
|
// * @return The TemplateResolver associated with the content section
|
||||||
|
// */
|
||||||
|
// public TemplateResolver getTemplateResolver(ContentSection section) {
|
||||||
|
//
|
||||||
|
// String name = section.getName();
|
||||||
|
// TemplateResolver ir = (TemplateResolver) s_templateResolverCache.get(
|
||||||
|
// name);
|
||||||
|
//
|
||||||
|
// if (ir == null) {
|
||||||
|
// ir = section.getTemplateResolver();
|
||||||
|
// s_templateResolverCache.put(name, ir);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return ir;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.SimpleComponent;
|
||||||
|
import com.arsdigita.cms.CMS;
|
||||||
|
import com.arsdigita.cms.ContentSectionServlet;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This <code>ContentPanel</code> component fetches the
|
||||||
|
* {@link com.arsdigita.cms.dispatcher.XMLGenerator} for the content
|
||||||
|
* section.</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ContentPanel extends SimpleComponent {
|
||||||
|
|
||||||
|
public ContentPanel() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches an XML Generator. This method can be overridden to fetch any
|
||||||
|
* {@link com.arsdigita.cms.dispatcher.XMLGenerator}, but by default, it
|
||||||
|
* fetches the <code>XMLGenerator</code> registered to the current
|
||||||
|
* {@link com.arsdigita.cms.ContentSection}.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
*
|
||||||
|
* @return The XMLGenerator used by this Content Panel
|
||||||
|
*/
|
||||||
|
protected XMLGenerator getXMLGenerator(PageState state) {
|
||||||
|
ContentSection section = CMS.getContext().getContentSection();
|
||||||
|
Assert.exists(section);
|
||||||
|
try {
|
||||||
|
return (XMLGenerator) Class.forName(section.getXmlGeneratorClass())
|
||||||
|
.newInstance();
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
InstantiationException |
|
||||||
|
IllegalAccessException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates XML that represents a content item.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @param parent The parent DOM element
|
||||||
|
*
|
||||||
|
* @see com.arsdigita.cms.dispatcher.XMLGenerator
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(PageState state, Element parent) {
|
||||||
|
if (isVisible(state)) {
|
||||||
|
Element content = parent.newChildElement("cms:contentPanel",
|
||||||
|
CMS.CMS_XML_NS);
|
||||||
|
exportAttributes(content);
|
||||||
|
|
||||||
|
// Generate path information about the content item
|
||||||
|
generatePathInfoXML(state, content);
|
||||||
|
|
||||||
|
// Take advantage of caching in the CMS Dispatcher.
|
||||||
|
XMLGenerator xmlGenerator = getXMLGenerator(state);
|
||||||
|
|
||||||
|
xmlGenerator.generateXML(state, content, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate information about the path to this content item.
|
||||||
|
*
|
||||||
|
* @param state the page state
|
||||||
|
* @param parent the element that will contain the path info
|
||||||
|
*/
|
||||||
|
protected void generatePathInfoXML(PageState state, Element parent) {
|
||||||
|
Element pathInfo = parent
|
||||||
|
.newChildElement("cms:pathInfo", CMS.CMS_XML_NS);
|
||||||
|
|
||||||
|
if (CMS.getContext().hasContentSection()) {
|
||||||
|
pathInfo.newChildElement("cms:sectionPath", CMS.CMS_XML_NS).setText(
|
||||||
|
CMS.getContext().getContentSection().getPrimaryUrl());
|
||||||
|
}
|
||||||
|
String url = DispatcherHelper.getRequestContext().getRemainingURLPart();
|
||||||
|
if (url.startsWith(CMSDispatcher.PREVIEW)) {
|
||||||
|
pathInfo.newChildElement("cms:previewPath", CMS.CMS_XML_NS).setText(
|
||||||
|
ContentSectionServlet.PREVIEW);
|
||||||
|
}
|
||||||
|
pathInfo.newChildElement("cms:templatePrefix", CMS.CMS_XML_NS).setText(
|
||||||
|
"/" + AbstractItemResolver.TEMPLATE_CONTEXT_PREFIX);
|
||||||
|
|
||||||
|
if (CMS.getContext().hasContentItem()) {
|
||||||
|
ContentItem item = CMS.getContext().getContentItem();
|
||||||
|
pathInfo.newChildElement("cms:itemPath", CMS.CMS_XML_NS).setText("/"
|
||||||
|
+ item.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.Dispatcher;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherChain;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.web.ApplicationManager;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b><font color="red">Unsupported</font></b> Refactored content section
|
||||||
|
* dispatcher (under development).
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein (karlg@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class ContentSectionDispatcher implements Dispatcher {
|
||||||
|
|
||||||
|
public static final String CONTENT_ITEM
|
||||||
|
= "com.arsdigita.cms.dispatcher.item";
|
||||||
|
|
||||||
|
static final String CONTENT_SECTION = "com.arsdigita.cms.dispatcher.section";
|
||||||
|
|
||||||
|
private DispatcherChain dispatcherChain = new DispatcherChain();
|
||||||
|
|
||||||
|
public ContentSectionDispatcher() {
|
||||||
|
|
||||||
|
dispatcherChain.addChainedDispatcher(new CMSDispatcher(true));
|
||||||
|
dispatcherChain.addChainedDispatcher(new FileDispatcher());
|
||||||
|
dispatcherChain.addChainedDispatcher(new ItemDispatcher());
|
||||||
|
dispatcherChain.addChainedDispatcher(new CMSDispatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext context)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
setContentSection(request, context);
|
||||||
|
dispatcherChain.dispatch(request, response, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content section from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content section
|
||||||
|
*
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public static ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
return (ContentSection) request.getAttribute(CONTENT_SECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content item from the request attributes.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The content item
|
||||||
|
*
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public static ContentItem getContentItem(HttpServletRequest request) {
|
||||||
|
return (ContentItem) request.getAttribute(CONTENT_ITEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the current content section using the remaining URL stored in
|
||||||
|
* the request context object and the SiteNode class.
|
||||||
|
*
|
||||||
|
* @param url The section URL stub
|
||||||
|
*
|
||||||
|
* @return The current Content Section
|
||||||
|
*/
|
||||||
|
private void setContentSection(HttpServletRequest request,
|
||||||
|
// SiteNodeRequestContext actx)
|
||||||
|
RequestContext actx)
|
||||||
|
throws ServletException {
|
||||||
|
|
||||||
|
final ContentSection section = (ContentSection) Web.getWebContext()
|
||||||
|
.getApplication();
|
||||||
|
request.setAttribute(CONTENT_SECTION, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the current user has permission to access the admin pages.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param section
|
||||||
|
*/
|
||||||
|
public static boolean checkAdminAccess(HttpServletRequest request,
|
||||||
|
ContentSection section) {
|
||||||
|
|
||||||
|
return CdiUtil.createCdiUtil().findBean(PermissionChecker.class)
|
||||||
|
.isPermitted(CmsConstants.PRIVILEGE_ITEMS_EDIT, section
|
||||||
|
.getRootDocumentsFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,246 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.cms.ContentItem;
|
||||||
|
import com.arsdigita.cms.ContentSection;
|
||||||
|
import com.arsdigita.cms.ContentType;
|
||||||
|
import com.arsdigita.cms.Folder;
|
||||||
|
import com.arsdigita.cms.Template;
|
||||||
|
import com.arsdigita.cms.TemplateManager;
|
||||||
|
import com.arsdigita.cms.TemplateManagerFactory;
|
||||||
|
import com.arsdigita.mimetypes.MimeType;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ------- May be outdated. TemplateResolver has been reworked. ----------
|
||||||
|
* Resolves the JSP template to use for dispatching an
|
||||||
|
* item. This replaces TemplateResolver since the latter
|
||||||
|
* has a useless API.
|
||||||
|
* ------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* <p>In general, the process for resolving a template involves two
|
||||||
|
* steps:</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
*
|
||||||
|
* <li>The template resolver examines specific properties of the
|
||||||
|
* item, the content section, and/or the request itself and selects
|
||||||
|
* an appropriate <em>context</em>. A context is simply a token
|
||||||
|
* such as "plain" or "fancy".
|
||||||
|
*
|
||||||
|
* <li>Based on the selected context, the template resolver
|
||||||
|
* identifies an appropriate template for the item. This is a
|
||||||
|
* three-step process: (1) the resolver queries for an association
|
||||||
|
* between the item and a specific template for the selected
|
||||||
|
* context; (2) if no such association exists, the resolver queries
|
||||||
|
* the item's content type for a default template to use in the
|
||||||
|
* selected context; (3) if a default template is not found, return
|
||||||
|
* null (at which point the dispatcher should probably give up and
|
||||||
|
* return a 404 error).
|
||||||
|
*
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DefaultTemplateResolver extends AbstractTemplateResolver
|
||||||
|
implements TemplateResolver {
|
||||||
|
|
||||||
|
private static Logger s_log = Logger.getLogger(DefaultTemplateResolver.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSP template filename relative to the webapp
|
||||||
|
* root.
|
||||||
|
*
|
||||||
|
* @param section The ContentSection for the request
|
||||||
|
* @param item The ContentItem for the request
|
||||||
|
* @param request The current HttpServletRequest
|
||||||
|
*
|
||||||
|
* @return The path to the jsp template.
|
||||||
|
*/
|
||||||
|
public String getTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
String template = getItemTemplate(section, item, request);
|
||||||
|
MimeType mimeType = MimeType.loadMimeType(Template.JSP_MIME_TYPE);
|
||||||
|
|
||||||
|
if (template == null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("No item template, looking for content type template");
|
||||||
|
}
|
||||||
|
template = getTypeTemplate(section, item, request, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template == null) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("No content type template, looking for default template");
|
||||||
|
}
|
||||||
|
|
||||||
|
template = getDefaultTemplate(section, item, request);
|
||||||
|
|
||||||
|
Assert.exists(template, "default template");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isInfoEnabled()) {
|
||||||
|
s_log.info("Got template " + template + " for item " + item.getOID());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContentSection.getConfig().getTemplateRoot() + template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSP template filename relative to the webapp
|
||||||
|
* root for a given Template reference.
|
||||||
|
*
|
||||||
|
* @param template The Template to resolve the URL for.
|
||||||
|
*
|
||||||
|
* @return The path to the jsp template.
|
||||||
|
*/
|
||||||
|
public String getTemplatePath(Template template) {
|
||||||
|
|
||||||
|
return ContentSection.getConfig().getTemplateRoot() +
|
||||||
|
getTemplateFilename(template, template.getContentSection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XSL template filename relative to the webapp
|
||||||
|
* root for a given Template reference.
|
||||||
|
*
|
||||||
|
* @param template The Template to resolve the URL for.
|
||||||
|
*
|
||||||
|
* @return The path to the xsl template.
|
||||||
|
*/
|
||||||
|
public String getTemplateXSLPath(Template template) {
|
||||||
|
|
||||||
|
return ContentSection.getConfig().getTemplateRoot() +
|
||||||
|
getTemplateXSLFilename(template, template.getContentSection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the template associated with the item (if any)
|
||||||
|
*/
|
||||||
|
protected String getItemTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
TemplateManager manager = TemplateManagerFactory.getInstance();
|
||||||
|
String context = getTemplateContext(request);
|
||||||
|
Template template = manager.getTemplate(item, context);
|
||||||
|
|
||||||
|
return template == null ? null : getTemplateFilename(
|
||||||
|
template, section
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the template associated with the type (if any)
|
||||||
|
* @deprecated Use the version that specifies a mime type
|
||||||
|
*/
|
||||||
|
protected String getTypeTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
MimeType mimeType = MimeType.loadMimeType(Template.JSP_MIME_TYPE);
|
||||||
|
return getTypeTemplate(section, item, request, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the template associated with the type (if any)
|
||||||
|
*/
|
||||||
|
protected String getTypeTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request,
|
||||||
|
MimeType mimeType) {
|
||||||
|
TemplateManager manager = TemplateManagerFactory.getInstance();
|
||||||
|
ContentType type = item.getContentType();
|
||||||
|
|
||||||
|
Template template = null;
|
||||||
|
|
||||||
|
if (type != null ) {
|
||||||
|
String context = getTemplateContext(request);
|
||||||
|
template = manager.getDefaultTemplate(section, type, context, mimeType);
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Item has no content type, not looking for a " +
|
||||||
|
"content type specific template");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return template == null ? null : getTemplateFilename(
|
||||||
|
template, section
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default template
|
||||||
|
*/
|
||||||
|
protected String getDefaultTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
String path = (item instanceof Folder) ?
|
||||||
|
ContentSection.getConfig().getDefaultFolderTemplatePath() :
|
||||||
|
ContentSection.getConfig().getDefaultItemTemplatePath();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename for a Template object
|
||||||
|
*/
|
||||||
|
protected String getTemplateFilename(Template template,
|
||||||
|
ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return getTemplateFilename(template, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename for a Template object
|
||||||
|
*/
|
||||||
|
protected String getTemplateXSLFilename(Template template,
|
||||||
|
ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
return getTemplateXSLFilename(template, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename for a Template object
|
||||||
|
*/
|
||||||
|
protected String getTemplateFilename(Template template,
|
||||||
|
ContentSection section) {
|
||||||
|
|
||||||
|
String templateName = template.getPath();
|
||||||
|
String sectionURL = section.getPath();
|
||||||
|
return sectionURL + "/" + templateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filename for a Template object
|
||||||
|
*/
|
||||||
|
protected String getTemplateXSLFilename(Template template,
|
||||||
|
ContentSection section) {
|
||||||
|
|
||||||
|
String templateName = template.getPathNoJsp() + ".xsl";
|
||||||
|
String sectionURL = section.getPath();
|
||||||
|
|
||||||
|
return sectionURL + "/" + templateName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.ChainedDispatcher;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches to a file stored under the CMS package root
|
||||||
|
* (<code>/packages/cms/www</code>). This includes both unmanaged
|
||||||
|
* files copied or created directly in the file system, as well as
|
||||||
|
* pages and assets published to the file system from CMS.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein (karlg@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
**/
|
||||||
|
public class FileDispatcher implements ChainedDispatcher {
|
||||||
|
|
||||||
|
private static Logger s_log =
|
||||||
|
Logger.getLogger(ChainedDispatcher.class);
|
||||||
|
|
||||||
|
public int chainedDispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext context)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
File jspFile = getPackageFile(context);
|
||||||
|
|
||||||
|
if (jspFile.exists() && !jspFile.isDirectory()) {
|
||||||
|
String packageURL = context.getPageBase() + context.getRemainingURLPart();
|
||||||
|
s_log.debug ("DISPATCHING to " + packageURL);
|
||||||
|
|
||||||
|
// don't match folders, since they don't actually match a file
|
||||||
|
if ( !packageURL.endsWith("/")) {
|
||||||
|
s_log.debug ("DISPATCHING to " + packageURL);
|
||||||
|
// Don't set caching headers - let JSP file do it if required
|
||||||
|
//DispatcherHelper.maybeCacheDisable(response);
|
||||||
|
DispatcherHelper.setRequestContext(request, context);
|
||||||
|
DispatcherHelper.forwardRequestByPath(packageURL, request, response);
|
||||||
|
return ChainedDispatcher.DISPATCH_BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChainedDispatcher.DISPATCH_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches the request URL to a file in the package www directory.
|
||||||
|
**/
|
||||||
|
private File getPackageFile(RequestContext appContext) {
|
||||||
|
|
||||||
|
ServletContext servletContext = appContext.getServletContext();
|
||||||
|
|
||||||
|
String filePath = appContext.getRemainingURLPart();
|
||||||
|
|
||||||
|
String packageDocRoot =
|
||||||
|
servletContext.getRealPath(appContext.getPageBase());
|
||||||
|
|
||||||
|
File jspFile = new File(packageDocRoot, filePath);
|
||||||
|
|
||||||
|
return jspFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,305 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.cms.ContentSectionServlet;
|
||||||
|
import com.arsdigita.dispatcher.ChainedDispatcher;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.web.LoginSignal;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
import org.librecms.lifecycle.Lifecycle;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches to the JSP or Servlet for rendering a content item.
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein (karlg@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ItemDispatcher implements ChainedDispatcher {
|
||||||
|
|
||||||
|
private static Logger s_log = Logger.getLogger(ItemDispatcher.class);
|
||||||
|
|
||||||
|
public static Map s_itemResolverCache = Collections.synchronizedMap(
|
||||||
|
new HashMap());
|
||||||
|
public static Map s_templateResolverCache = Collections.synchronizedMap(
|
||||||
|
new HashMap());
|
||||||
|
|
||||||
|
public static final String FILE_SUFFIX = ".jsp";
|
||||||
|
public static final String INDEX_FILE = "/index";
|
||||||
|
// public static final String TEMPLATE_ROOT =
|
||||||
|
// "/packages/content-section/templates";
|
||||||
|
// public static final String DEFAULT_ITEM_TEMPLATE = "/default/item.jsp";
|
||||||
|
// public static final String DEFAULT_FOLDER_TEMPLATE = "/default/folder.jsp";
|
||||||
|
|
||||||
|
public static final String XML_SUFFIX = ".xml";
|
||||||
|
public static final String XML_MODE = "xmlMode";
|
||||||
|
|
||||||
|
private static boolean m_cacheItems = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context for previewing items
|
||||||
|
*/
|
||||||
|
public static final String PREVIEW = "/preview";
|
||||||
|
|
||||||
|
protected ItemXML m_itemXML;
|
||||||
|
|
||||||
|
public ItemDispatcher() {
|
||||||
|
super();
|
||||||
|
m_itemXML = new ItemXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCacheItems(boolean value) {
|
||||||
|
m_cacheItems = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int chainedDispatch(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response,
|
||||||
|
final RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
String queryString = request.getQueryString();
|
||||||
|
String url = actx.getRemainingURLPart();
|
||||||
|
|
||||||
|
s_log.info("Resolving item URL " + url);
|
||||||
|
|
||||||
|
if (url.endsWith(XML_SUFFIX)) {
|
||||||
|
request.setAttribute(XML_MODE, Boolean.TRUE);
|
||||||
|
s_log.debug("StraightXML Requested");
|
||||||
|
url = "/" + url.substring(0, url.length() - XML_SUFFIX.length());
|
||||||
|
} else {
|
||||||
|
request.setAttribute(XML_MODE, Boolean.FALSE);
|
||||||
|
// it's neither a .jsp or a .xml, thus its an error
|
||||||
|
if (url.endsWith(FILE_SUFFIX)) {
|
||||||
|
url = "/" + url
|
||||||
|
.substring(0, url.length() - FILE_SUFFIX.length());
|
||||||
|
} else if (url.endsWith("/")) {
|
||||||
|
url = "/" + url.substring(0, url.length() - 1);
|
||||||
|
} else {
|
||||||
|
s_log.warn("Fail: URL doesn't have right suffix.");
|
||||||
|
return ChainedDispatcher.DISPATCH_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContentSection section = ContentSectionServlet.getContentSection(
|
||||||
|
request);
|
||||||
|
// ContentSectionDispatcher.getContentSection(request);
|
||||||
|
|
||||||
|
final ContentItem item = getItem(section, url);
|
||||||
|
if (item == null) {
|
||||||
|
s_log.warn("Fail: No live item found matching " + url);
|
||||||
|
return ChainedDispatcher.DISPATCH_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.setAttribute(ContentSectionDispatcher.CONTENT_ITEM, item);
|
||||||
|
|
||||||
|
s_log.debug("MATCHED " + item.getObjectId());
|
||||||
|
|
||||||
|
// Work out how long to cache for....
|
||||||
|
// We take minimum(default timeout, lifecycle expiry)
|
||||||
|
//ToDo
|
||||||
|
// Lifecycle cycle = item.getLifecycle();
|
||||||
|
int expires = DispatcherHelper.getDefaultCacheExpiry();
|
||||||
|
// if (cycle != null) {
|
||||||
|
// Date endDate = cycle.getEndDate();
|
||||||
|
//
|
||||||
|
// if (endDate != null) {
|
||||||
|
// int maxAge = (int) ( ( endDate.getTime() - System.currentTimeMillis() ) / 1000l );
|
||||||
|
// if (maxAge < expires) {
|
||||||
|
// expires = maxAge;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//ToDo end
|
||||||
|
// NB, this is not the same as the security check previously
|
||||||
|
// We are checking if anyone can access - ie can we allow
|
||||||
|
// this page to be publically cached
|
||||||
|
if (m_cacheItems && !url.startsWith(PREVIEW)) {
|
||||||
|
// if (sm.canAccess((User)null, SecurityManager.PUBLIC_PAGES, item)) {
|
||||||
|
if (CdiUtil.createCdiUtil().findBean(PermissionChecker.class)
|
||||||
|
.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item)) {
|
||||||
|
DispatcherHelper.cacheForWorld(response, expires);
|
||||||
|
} else {
|
||||||
|
DispatcherHelper.cacheForUser(response, expires);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatcherHelper.cacheDisable(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((Boolean) request.getAttribute(XML_MODE)).booleanValue()) {
|
||||||
|
m_itemXML.dispatch(request, response, actx);
|
||||||
|
return ChainedDispatcher.DISPATCH_BREAK;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// normal dispatching
|
||||||
|
// This part assumes the template is JSP.
|
||||||
|
// final String templateURL = getTemplateURL(section, item, request,
|
||||||
|
// actx);
|
||||||
|
|
||||||
|
// s_log.debug("TEMPLATE " + templateURL);
|
||||||
|
|
||||||
|
DispatcherHelper.setRequestContext(request, actx);
|
||||||
|
DispatcherHelper.forwardRequestByPath(null, request,
|
||||||
|
response);
|
||||||
|
return ChainedDispatcher.DISPATCH_BREAK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentItem getItem(ContentSection section, String url) {
|
||||||
|
|
||||||
|
ItemResolver itemResolver = getItemResolver(section);
|
||||||
|
ContentItem item;
|
||||||
|
// Check if the user has access to view public or preview pages
|
||||||
|
boolean hasPermission = true;
|
||||||
|
HttpServletRequest request = DispatcherHelper.getRequest();
|
||||||
|
|
||||||
|
// If the remaining URL starts with "preview/", then try and
|
||||||
|
// preview this item. Otherwise look for the live item.
|
||||||
|
boolean preview = false;
|
||||||
|
if (url.startsWith(PREVIEW)) {
|
||||||
|
url = url.substring(PREVIEW.length());
|
||||||
|
preview = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
|
||||||
|
final PermissionChecker permissionChecker = cdiUtil.findBean(
|
||||||
|
PermissionChecker.class);
|
||||||
|
|
||||||
|
if (preview) {
|
||||||
|
item = itemResolver.getItem(section, url, "draft");
|
||||||
|
if (item != null) {
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_PREVIEW, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item = itemResolver.getItem(section, url, "live");
|
||||||
|
if (item != null) {
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item == null && url.endsWith(INDEX_FILE)) {
|
||||||
|
|
||||||
|
// look up folder if it's an index
|
||||||
|
url = url.substring(0, url.length() - INDEX_FILE.length());
|
||||||
|
s_log.info("Attempting to match folder " + url);
|
||||||
|
item = itemResolver.getItem(section, url, "live");
|
||||||
|
if (item != null) {
|
||||||
|
hasPermission = permissionChecker.isPermitted(
|
||||||
|
CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// chris.gilbert@westsussex.gov.uk - if user is not logged in, give them a chance to do that, else show them the door
|
||||||
|
if (!hasPermission && !cdiUtil.findBean(Shiro.class).getSubject()
|
||||||
|
.isAuthenticated()) {
|
||||||
|
throw new LoginSignal(request);
|
||||||
|
}
|
||||||
|
if (!hasPermission) {
|
||||||
|
throw new com.arsdigita.dispatcher.AccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the ItemResolver for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The ItemResolver associated with the content section
|
||||||
|
*/
|
||||||
|
public ItemResolver getItemResolver(ContentSection section) {
|
||||||
|
|
||||||
|
String name = section.getLabel();
|
||||||
|
ItemResolver ir = (ItemResolver) s_itemResolverCache.get(name);
|
||||||
|
|
||||||
|
if (ir == null) {
|
||||||
|
try {
|
||||||
|
ir = (ItemResolver) Class
|
||||||
|
.forName(section.getItemResolverClass()).newInstance();
|
||||||
|
s_itemResolverCache.put(name, ir);
|
||||||
|
} catch (ClassNotFoundException |
|
||||||
|
IllegalAccessException |
|
||||||
|
InstantiationException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the ItemResolver for a content section. Checks cache first.
|
||||||
|
*
|
||||||
|
* @param section The content section
|
||||||
|
*
|
||||||
|
* @return The ItemResolver associated with the content section
|
||||||
|
*/
|
||||||
|
// public TemplateResolver getTemplateResolver(ContentSection section) {
|
||||||
|
//
|
||||||
|
// String name = section.getName();
|
||||||
|
// TemplateResolver ir = (TemplateResolver) s_templateResolverCache.get(
|
||||||
|
// name);
|
||||||
|
//
|
||||||
|
// if (ir == null) {
|
||||||
|
// ir = section.getTemplateResolver();
|
||||||
|
// s_templateResolverCache.put(name, ir);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return ir;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the URL of a template for an item. The returned URL is relative
|
||||||
|
* to the webapp context.
|
||||||
|
*/
|
||||||
|
// public String getTemplateURL(ContentSection section,
|
||||||
|
// ContentItem item,
|
||||||
|
// HttpServletRequest request,
|
||||||
|
// RequestContext actx) {
|
||||||
|
//
|
||||||
|
// String templateURL = getTemplateResolver(section).getTemplate(section,
|
||||||
|
// item,
|
||||||
|
// request);
|
||||||
|
//
|
||||||
|
// return templateURL;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The <tt>ItemResolver</tt> is responsible for mapping a URL in a
|
||||||
|
* particular content section to a content item.</p>
|
||||||
|
*
|
||||||
|
* <p>As an example, here is the item resolution process for a request to
|
||||||
|
* <tt>http://yourserver/cms/cheese</tt>:</p>
|
||||||
|
*
|
||||||
|
* <p>The item resolver would be asked to map the URL stub <tt>/cheese</tt>
|
||||||
|
* in the content section mounted at <tt>/cms</tt> to a content item. To
|
||||||
|
* this end, the dispatcher calls the <tt>getItem</tt> method, passing in
|
||||||
|
* the {@link com.arsdigita.cms.ContentSection} and the URL stub for the
|
||||||
|
* item within the section, <tt>/cheese</tt> in our example. As a final
|
||||||
|
* argument, the dispatcher passes either <tt>ContentItem.DRAFT</tt> or
|
||||||
|
* <tt>ContentItem.LIVE</tt> to the <tt>ItemResolver</tt>, depending on
|
||||||
|
* whether the returned item should be the live version (for public pages)
|
||||||
|
* or the draft version (for previewing).</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @author Stanislav Freidin (sfreidin@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface ItemResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a content item based on section, url, and use context.
|
||||||
|
*
|
||||||
|
* @param section The current content section
|
||||||
|
* @param url The section-relative URL
|
||||||
|
* @param context The use context
|
||||||
|
* @return The content item, or null if no such item exists
|
||||||
|
*/
|
||||||
|
public ContentItem getItem(ContentSection section, String url,
|
||||||
|
String context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current context based on the page state.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @return the context of the current URL, such as "live" or "admin"
|
||||||
|
*/
|
||||||
|
public String getCurrentContext(PageState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param itemId The item ID
|
||||||
|
* @param name The name of the content page
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or "admin"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
public String generateItemURL (
|
||||||
|
PageState state, BigDecimal itemId, String name,
|
||||||
|
ContentSection section, String context
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param itemId The item ID
|
||||||
|
* @param name The name of the content page
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or "admin"
|
||||||
|
* @param templateContext the context for the URL, such as "public"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
public String generateItemURL (
|
||||||
|
PageState state, BigDecimal itemId, String name,
|
||||||
|
ContentSection section, String context, String templateContext
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param item The item
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or "admin"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
public String generateItemURL (
|
||||||
|
PageState state, ContentItem item, ContentSection section, String context
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param item The item
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or "admin"
|
||||||
|
* @param templateContext the context for the URL, such as "public"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
public String generateItemURL (
|
||||||
|
PageState state, ContentItem item, ContentSection section, String context,
|
||||||
|
String templateContext
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a master page based on page state (and content section).
|
||||||
|
*
|
||||||
|
* @param item The content item
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return The master page
|
||||||
|
*/
|
||||||
|
public CMSPage getMasterPage(ContentItem item, HttpServletRequest request)
|
||||||
|
throws ServletException;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Having to stick the following methods, getTemplateFromURL, and
|
||||||
|
* stripTemplateFromURL in the ItemResolver interface is somewhat ugly.
|
||||||
|
* But, the relationship between ItemResolver and TemplateResolver needs
|
||||||
|
* to be cleaned up to fix this. As it is, ItemResolver parses URL's for
|
||||||
|
* template contexts, and TemplateResolver sets the actual template contexts
|
||||||
|
* in the request.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the template context from the URL and returns it, if it is there.
|
||||||
|
* Otherwise, returns null.
|
||||||
|
* @param inUrl the URL from which to get the template context
|
||||||
|
* @return the template context, or null if there is no template context
|
||||||
|
*/
|
||||||
|
public String getTemplateFromURL(String inUrl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the template context from the <code>inUrl</code>.
|
||||||
|
*
|
||||||
|
* @param inUrl URL, possibly including the template context.
|
||||||
|
* @return <code>inUrl</code> with the template context removed
|
||||||
|
*/
|
||||||
|
public String stripTemplateFromURL(String inUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002-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.cms.dispatcher;
|
||||||
|
|
||||||
|
|
||||||
|
import com.arsdigita.cms.CMS;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* XMLPage
|
||||||
|
*
|
||||||
|
* Designed to allow you to output straight XML directly from the ContentItem
|
||||||
|
* that implements XMLGenerator, with none of the surrounding headers, footers, etc
|
||||||
|
*
|
||||||
|
* @author slater@arsdigita.com
|
||||||
|
*
|
||||||
|
***/
|
||||||
|
|
||||||
|
public class ItemXML extends ResourceHandlerImpl {
|
||||||
|
|
||||||
|
public ItemXML() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
ContentItem item = getContentItem(request);
|
||||||
|
|
||||||
|
Element content = new Element("cms:item", CMS.CMS_XML_NS);
|
||||||
|
|
||||||
|
// ContentItemXMLRenderer renderer =
|
||||||
|
// new ContentItemXMLRenderer(content);
|
||||||
|
//ToDo
|
||||||
|
// renderer.setWrapAttributes(true);
|
||||||
|
// renderer.setWrapRoot(false);
|
||||||
|
// renderer.setWrapObjects(false);
|
||||||
|
//
|
||||||
|
// renderer.walk(item, SimpleXMLGenerator.ADAPTER_CONTEXT);
|
||||||
|
//ToDo End
|
||||||
|
|
||||||
|
Document doc;
|
||||||
|
try {
|
||||||
|
doc = new Document(content);
|
||||||
|
} catch (javax.xml.parsers.ParserConfigurationException e) {
|
||||||
|
throw new javax.servlet.ServletException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream out = response.getOutputStream();
|
||||||
|
try {
|
||||||
|
out.write(doc.toString(true).getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServletException(e);
|
||||||
|
} finally {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A {@link com.arsdigita.cms.dispatcher.CMSPage} used for serving
|
||||||
|
* content items.</p>
|
||||||
|
*
|
||||||
|
* <p>This page contains a <code>ContentPanel</code> component which fetches
|
||||||
|
* the {@link com.arsdigita.cms.dispatcher.XMLGenerator} for the content
|
||||||
|
* section.</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class MasterPage extends CMSPage {
|
||||||
|
|
||||||
|
public MasterPage() {
|
||||||
|
super("Master", new SimpleContainer());
|
||||||
|
setIdAttr("master_page");
|
||||||
|
|
||||||
|
add(new ContentPanel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content section.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return The current content section
|
||||||
|
*/
|
||||||
|
public ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
// Resets all content sections associations.
|
||||||
|
ContentSection section = super.getContentSection(request);
|
||||||
|
Assert.exists(section);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,916 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.cms.CMS;
|
||||||
|
import com.arsdigita.cms.ContentCenter;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves items to URLs and URLs to items for multiple language
|
||||||
|
* variants.
|
||||||
|
*
|
||||||
|
* Created Mon Jan 20 14:30:03 2003.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mhanisch@redhat.com">Michael Hanisch</a>
|
||||||
|
* @version $Id: MultilingualItemResolver.java 2090 2010-04-17 08:04:14Z pboy $
|
||||||
|
*/
|
||||||
|
public class MultilingualItemResolver
|
||||||
|
extends AbstractItemResolver implements ItemResolver {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger
|
||||||
|
(MultilingualItemResolver.class);
|
||||||
|
|
||||||
|
private static MasterPage s_masterP = null;
|
||||||
|
private static final String ADMIN_PREFIX = "admin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The string identifying an item's ID in the query string of a
|
||||||
|
* URL.
|
||||||
|
*/
|
||||||
|
protected static final String ITEM_ID = "item_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The separator used in URL query strings; should be either "&"
|
||||||
|
* or ";".
|
||||||
|
*/
|
||||||
|
protected static final String SEPARATOR = "&";
|
||||||
|
|
||||||
|
public MultilingualItemResolver() {
|
||||||
|
s_log.debug("Undergoing creation");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a content item based on section, url, and use context.
|
||||||
|
*
|
||||||
|
* @param section The current content section
|
||||||
|
* @param url The section-relative URL
|
||||||
|
* @param context The use context,
|
||||||
|
* e.g. <code>ContentItem.LIVE</code>,
|
||||||
|
* <code>CMSDispatcher.PREVIEW</code> or
|
||||||
|
* <code>ContentItem.DRAFT</code>. See {@link
|
||||||
|
* #getCurrentContext}.
|
||||||
|
* @return The content item, or null if no such item exists
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ContentItem getItem(final ContentSection section,
|
||||||
|
String url,
|
||||||
|
final String context) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolving the item in content section " + section +
|
||||||
|
" at URL '" + url + "' for context " + context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(section, "ContentSection section");
|
||||||
|
Assert.exists(url, "String url");
|
||||||
|
Assert.exists(context, "String context");
|
||||||
|
|
||||||
|
Category rootFolder = section.getRootDocumentsFolder();
|
||||||
|
url = stripTemplateFromURL(url);
|
||||||
|
|
||||||
|
// nothing to do, if root folder is null
|
||||||
|
if (rootFolder == null) {
|
||||||
|
s_log.debug("The root folder is null; returning no item");
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Using root folder " + rootFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("live".equals(context)) {
|
||||||
|
s_log.debug("The use context is 'live'");
|
||||||
|
|
||||||
|
// We allow for returning null, so the root folder may
|
||||||
|
// not be live.
|
||||||
|
//Assert.isTrue(rootFolder.isLive(),
|
||||||
|
// "live context - root folder of secion must be live");
|
||||||
|
|
||||||
|
// If the context is 'live', we need the live item.
|
||||||
|
|
||||||
|
rootFolder = (Folder) rootFolder.getLiveVersion();
|
||||||
|
|
||||||
|
if (rootFolder == null) {
|
||||||
|
s_log.debug("The live version of the root folder is " +
|
||||||
|
"null; returning no item");
|
||||||
|
} else {
|
||||||
|
s_log.debug("The root folder has a live version; " +
|
||||||
|
"recursing");
|
||||||
|
|
||||||
|
final String prefix =
|
||||||
|
section.getURL() + rootFolder.getPath();
|
||||||
|
|
||||||
|
if (url.startsWith(prefix)) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("The URL starts with prefix '" +
|
||||||
|
prefix + "'; removing it");
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.substring(prefix.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContentItem item = getItemFromLiveURL(url, rootFolder);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolved URL '" + url + "' to item " +
|
||||||
|
item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
} else if (ContentItem.DRAFT.equals(context)) {
|
||||||
|
s_log.debug("The use context is 'draft'");
|
||||||
|
|
||||||
|
// For 'draft' items, 'generateUrl()' method returns
|
||||||
|
// URL like this
|
||||||
|
// '/acs/wcms/admin/item.jsp?item_id=10201&set_tab=1'
|
||||||
|
// Check if URL contains any match of string
|
||||||
|
// 'item_id', then try to instanciate item_id value
|
||||||
|
// and return FIXME: Please hack this if there is
|
||||||
|
// more graceful solution. [aavetyan]
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.isTrue
|
||||||
|
(url.indexOf(ITEM_ID) >= 0,
|
||||||
|
"url must contain parameter " + ITEM_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContentItem item = getItemFromDraftURL(url);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolved URL '" + url + "' to item " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
} else if (CMSDispatcher.PREVIEW.equals(context)) {
|
||||||
|
s_log.debug("The use context is 'preview'");
|
||||||
|
|
||||||
|
final String prefix = CMSDispatcher.PREVIEW + "/";
|
||||||
|
|
||||||
|
if (url.startsWith(prefix)) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("The URL starts with prefix '" +
|
||||||
|
prefix + "'; removing it");
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.substring(prefix.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final ContentItem item = getItemFromLiveURL(url, rootFolder);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolved URL '" + url + "' to item " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Invalid item resolver context " + context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_log.debug("No item resolved; returning null");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current context based on the page state.
|
||||||
|
*
|
||||||
|
* @param state the current page state
|
||||||
|
* @return the context of the current URL, such as
|
||||||
|
* <code>ContentItem.LIVE</code> or <code>ContentItem.DRAFT</code>
|
||||||
|
* @see ContentItem#LIVE
|
||||||
|
* @see ContentItem#DRAFT
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getCurrentContext(final PageState state) {
|
||||||
|
s_log.debug("Getting the current context");
|
||||||
|
|
||||||
|
// XXX need to use Web.getWebContext().getRequestURL() here.
|
||||||
|
String url = state.getRequest().getRequestURI();
|
||||||
|
|
||||||
|
final ContentSection section =
|
||||||
|
CMS.getContext().getContentSection();
|
||||||
|
|
||||||
|
// If this page is associated with a content section,
|
||||||
|
// transform the URL so that it is relative to the content
|
||||||
|
// section site node.
|
||||||
|
|
||||||
|
if (section != null) {
|
||||||
|
final String sectionURL = section.getURL();
|
||||||
|
|
||||||
|
if (url.startsWith(sectionURL)) {
|
||||||
|
url = url.substring(sectionURL.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any template-specific URL components (will only work
|
||||||
|
// if they're first in the URL at this point; verify). XXX but
|
||||||
|
// we don't actually verify?
|
||||||
|
|
||||||
|
url = stripTemplateFromURL(url);
|
||||||
|
|
||||||
|
// Determine if we are under the admin UI.
|
||||||
|
|
||||||
|
if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter.getURL())) {
|
||||||
|
return ContentItem.DRAFT;
|
||||||
|
} else {
|
||||||
|
return ContentItem.LIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param itemId The item ID
|
||||||
|
* @param name The name of the content page
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or
|
||||||
|
* "admin"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateItemURL(final PageState state,
|
||||||
|
final BigDecimal itemId,
|
||||||
|
final String name,
|
||||||
|
final ContentSection section,
|
||||||
|
final String context) {
|
||||||
|
return generateItemURL(state, itemId, name, section, context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param itemId The item ID
|
||||||
|
* @param name The name of the content page
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or
|
||||||
|
* "admin"
|
||||||
|
* @param templateContext the context for the URL, such as
|
||||||
|
* "public"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateItemURL(final PageState state,
|
||||||
|
final BigDecimal itemId,
|
||||||
|
final String name,
|
||||||
|
final ContentSection section,
|
||||||
|
final String context,
|
||||||
|
final String templateContext) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generating an item URL for item id " + itemId +
|
||||||
|
", section " + section + ", and context '" +
|
||||||
|
context + "' with name '" + name + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(itemId, "BigDecimal itemId");
|
||||||
|
Assert.exists(context, "String context");
|
||||||
|
Assert.exists(section, "ContentSection section");
|
||||||
|
|
||||||
|
if (ContentItem.DRAFT.equals(context)) {
|
||||||
|
// No template context here.
|
||||||
|
return generateDraftURL(section, itemId);
|
||||||
|
} else if (CMSDispatcher.PREVIEW.equals(context)) {
|
||||||
|
ContentItem item = new ContentItem(itemId);
|
||||||
|
|
||||||
|
return generatePreviewURL(section, item, templateContext);
|
||||||
|
} else if (ContentItem.LIVE.equals(context)) {
|
||||||
|
ContentItem item = new ContentItem(itemId);
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.exists(item, "item");
|
||||||
|
Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()),
|
||||||
|
"Generating " + ContentItem.LIVE + " " +
|
||||||
|
"URL; this item must be the live version");
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateLiveURL(section, item, templateContext);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException
|
||||||
|
("Unknown context '" + context + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param item The item
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or
|
||||||
|
* "admin"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateItemURL(final PageState state,
|
||||||
|
final ContentItem item,
|
||||||
|
final ContentSection section,
|
||||||
|
final String context) {
|
||||||
|
return generateItemURL(state, item, section, context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a URL for a content item.
|
||||||
|
*
|
||||||
|
* @param item The item
|
||||||
|
* @param state The page state
|
||||||
|
* @param section the content section to which the item belongs
|
||||||
|
* @param context the context of the URL, such as "live" or
|
||||||
|
* "admin"
|
||||||
|
* @param templateContext the context for the URL, such as
|
||||||
|
* "public"
|
||||||
|
* @return The URL of the item
|
||||||
|
* @see #getCurrentContext
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String generateItemURL(final PageState state,
|
||||||
|
final ContentItem item,
|
||||||
|
ContentSection section,
|
||||||
|
final String context,
|
||||||
|
final String templateContext) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generating an item URL for item " + item +
|
||||||
|
", section " + section + ", and context " +
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.exists(item, "ContentItem item");
|
||||||
|
Assert.exists(context, "String context");
|
||||||
|
|
||||||
|
if (section == null) {
|
||||||
|
section = item.getContentSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ContentItem.DRAFT.equals(context)) {
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.isTrue(ContentItem.DRAFT.equals(item.getVersion()),
|
||||||
|
"Generating " + ContentItem.DRAFT +
|
||||||
|
" url: item must be draft version");
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateDraftURL(section, item.getID());
|
||||||
|
} else if (CMSDispatcher.PREVIEW.equals(context)) {
|
||||||
|
return generatePreviewURL(section, item, templateContext);
|
||||||
|
} else if (ContentItem.LIVE.equals(context)) {
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()),
|
||||||
|
"Generating " + ContentItem.LIVE +
|
||||||
|
" url: item must be live version");
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateLiveURL(section, item, templateContext);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unknown context " + context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a master page based on page state (and content
|
||||||
|
* section).
|
||||||
|
*
|
||||||
|
* @param item The content item
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return The master page
|
||||||
|
* @throws javax.servlet.ServletException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CMSPage getMasterPage(final ContentItem item,
|
||||||
|
final HttpServletRequest request)
|
||||||
|
throws ServletException {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Getting the master page for item " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from SimpleItemResolver
|
||||||
|
if (s_masterP == null) {
|
||||||
|
s_masterP = new MasterPage();
|
||||||
|
s_masterP.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Returning master page " + s_masterP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_masterP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns content item's draft version URL
|
||||||
|
*
|
||||||
|
* @param section The content section to which the item belongs
|
||||||
|
* @param itemId The content item's ID
|
||||||
|
* @return generated URL string
|
||||||
|
*/
|
||||||
|
protected String generateDraftURL(final ContentSection section,
|
||||||
|
final BigDecimal itemId) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generating draft URL for item ID " + itemId +
|
||||||
|
" and section " + section);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.isTrue(section != null && itemId != null,
|
||||||
|
"get draft url: neither secion nor item " +
|
||||||
|
"can be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String url = ContentItemPage.getItemURL
|
||||||
|
(section.getPath() + "/", itemId, ContentItemPage.AUTHORING_TAB);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generated draft URL " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a <em>language-independent</em> URL to the
|
||||||
|
* <code>item</code> in the given section.<p> When a client
|
||||||
|
* retrieves this URL, the URL is resolved to point to a specific
|
||||||
|
* language instance of the item referenced here, i.e. this URL
|
||||||
|
* will be resolved to a <em>language-specific</em> URL
|
||||||
|
* internally.
|
||||||
|
*
|
||||||
|
* @param section the <code>ContentSection</code> that contains this item
|
||||||
|
* @param item <code>ContentItem</code> for which a URL should be
|
||||||
|
* constructed.
|
||||||
|
* @param templateContext template context; will be ignored if <code>null</code>
|
||||||
|
*
|
||||||
|
* @return a <em>language-independent</em> URL to the
|
||||||
|
* <code>item</code> in the given <code>section</code>, which will
|
||||||
|
* be presented within the given <code>templateContext</code>
|
||||||
|
*/
|
||||||
|
protected String generateLiveURL(final ContentSection section,
|
||||||
|
final ContentItem item,
|
||||||
|
final String templateContext) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generating live URL for item " + item + " in " +
|
||||||
|
"section " + section);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* URL = URL of section + templateContext + path to the ContentBundle
|
||||||
|
* which contains the item
|
||||||
|
*/
|
||||||
|
final StringBuffer url = new StringBuffer(400);
|
||||||
|
//url.append(section.getURL());
|
||||||
|
url.append(section.getPath()).append("/");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add template context, if one is given
|
||||||
|
*/
|
||||||
|
// This is breaking URL's...not sure why it's here. XXX
|
||||||
|
// this should work with the appropriate logic. trying again.
|
||||||
|
if (!(templateContext == null || templateContext.length() == 0)) {
|
||||||
|
url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to retrieve the bundle.
|
||||||
|
final ContentItem bundle = (ContentItem) item.getParent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It would be nice if we had a ContentPage here, which
|
||||||
|
* supports the getContentBundle() method, but unfortunately
|
||||||
|
* the API sucks and there is no real distinction between mere
|
||||||
|
* ContentItems and top-level items, so we have to use this
|
||||||
|
* hack. TODO: add sanity check that bundle is actually a
|
||||||
|
* ContentItem.
|
||||||
|
*/
|
||||||
|
if (bundle != null && bundle instanceof ContentBundle) {
|
||||||
|
s_log.debug("Found a bundle; building its file name");
|
||||||
|
|
||||||
|
final String fname = bundle.getPath();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Appending the bundle's file name '" +
|
||||||
|
fname + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
url.append(fname);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
s_log.debug("No bundle found; using the item's path directly");
|
||||||
|
|
||||||
|
url.append(item.getPath());
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* This will append the language to the url, which will in turn render
|
||||||
|
* the language negotiation inoperative
|
||||||
|
final String language = item.getLanguage();
|
||||||
|
|
||||||
|
if (language == null) {
|
||||||
|
s_log.debug("The item has no language; omitting the " +
|
||||||
|
"language encoding");
|
||||||
|
} else {
|
||||||
|
s_log.debug("Encoding the language of the item passed in, '" +
|
||||||
|
language + "'");
|
||||||
|
|
||||||
|
url.append("." + language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generated live URL " + url.toString());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a URL which can be used to preview the
|
||||||
|
* <code>item</code>, using the given
|
||||||
|
* <code>templateContext</code>.<p> Only a specific language
|
||||||
|
* instance can be previewed, meaning there <em>no</em> language
|
||||||
|
* negotiation is involved when a request is made to a URL that
|
||||||
|
* has been generated by this method.
|
||||||
|
*
|
||||||
|
* @param section The <code>ContentSection</code> which contains
|
||||||
|
* the <code>item</code>
|
||||||
|
* @param item The <code>ContentItem</code> for which a URL should
|
||||||
|
* be generated.
|
||||||
|
* @param templateContext the context that determines which
|
||||||
|
* template should render the item when it is previewed; ignored
|
||||||
|
* if the argument given here is <code>null</code>
|
||||||
|
* @return a URL which can be used to preview the given
|
||||||
|
* <code>item</code>
|
||||||
|
*/
|
||||||
|
protected String generatePreviewURL(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
String templateContext) {
|
||||||
|
Assert.exists(section, "ContentSection section");
|
||||||
|
Assert.exists(item, "ContentItem item");
|
||||||
|
|
||||||
|
final StringBuffer url = new StringBuffer(100);
|
||||||
|
url.append(section.getPath());
|
||||||
|
url.append("/");
|
||||||
|
url.append(CMSDispatcher.PREVIEW);
|
||||||
|
url.append("/");
|
||||||
|
/*
|
||||||
|
* add template context, if one is given
|
||||||
|
*/
|
||||||
|
// This is breaking URL's...not sure why it's here. XXX
|
||||||
|
// this should work with the appropriate logic. trying again.
|
||||||
|
if (!(templateContext == null || templateContext.length() == 0)) {
|
||||||
|
url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to retrieve the bundle.
|
||||||
|
ContentItem bundle = (ContentItem) item.getParent();
|
||||||
|
|
||||||
|
/* It would be nice if we had a ContentPage here, which
|
||||||
|
* supports the getContentBundle() method, but unfortunately
|
||||||
|
* the API sucks and there is no real distinction between mere
|
||||||
|
* ContentItems and top-level items, so we have to use this
|
||||||
|
* hack. TODO: add sanity check that bundle is actually a
|
||||||
|
* ContentItem.
|
||||||
|
*/
|
||||||
|
if (bundle != null && bundle instanceof ContentBundle) {
|
||||||
|
s_log.debug("Found a bundle; using its path");
|
||||||
|
|
||||||
|
url.append(bundle.getPath());
|
||||||
|
} else {
|
||||||
|
s_log.debug("No bundle found; using the item's path directly");
|
||||||
|
|
||||||
|
url.append(item.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String language = item.getLanguage();
|
||||||
|
|
||||||
|
if (language == null) {
|
||||||
|
s_log.debug("The item has no language; omitting the " +
|
||||||
|
"language encoding");
|
||||||
|
} else {
|
||||||
|
s_log.debug("Encoding the language of the item passed in, '" +
|
||||||
|
language + "'");
|
||||||
|
|
||||||
|
url.append(".").append(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Generated preview URL " + url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves <code>ITEM_ID</code> parameter from URL and
|
||||||
|
* instantiates item according to this ID.
|
||||||
|
*
|
||||||
|
* @param url URL that indicates which item should be retrieved;
|
||||||
|
* must contain the <code>ITEM_ID</code> parameter
|
||||||
|
* @return the <code>ContentItem</code> the given <code>url</code>
|
||||||
|
* points to, or <code>null</code> if no ID has been found in the
|
||||||
|
* <code>url</code>
|
||||||
|
*/
|
||||||
|
protected ContentItem getItemFromDraftURL(final String url) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Looking up the item from draft URL " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = url.indexOf(ITEM_ID);
|
||||||
|
|
||||||
|
// XXX this is wrong: here we abort on not finding the
|
||||||
|
// parameter; below we return null.
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.isTrue(pos >= 0,
|
||||||
|
"Draft URL must contain parameter " + ITEM_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
String item_id = url.substring(pos); // item_id == ITEM_ID=.... ?
|
||||||
|
|
||||||
|
pos = item_id.indexOf("="); // should be exactly after the ITEM_ID string
|
||||||
|
|
||||||
|
if (pos != ITEM_ID.length()) {
|
||||||
|
// item_id seems to be something like ITEM_IDFOO=
|
||||||
|
|
||||||
|
s_log.debug("No suitable item_id parameter found; returning null");
|
||||||
|
|
||||||
|
return null; // no ID found
|
||||||
|
}
|
||||||
|
|
||||||
|
pos++; // skip the "="
|
||||||
|
|
||||||
|
// ID is the string between the equal (at pos) and the next separator
|
||||||
|
int i = item_id.indexOf(SEPARATOR);
|
||||||
|
item_id = item_id.substring(pos, Math.min(i, item_id.length() -1));
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Looking up item using item ID " + item_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal(item_id));
|
||||||
|
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance
|
||||||
|
(oid);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Returning item " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a content item based on URL relative to the root
|
||||||
|
* folder.
|
||||||
|
*
|
||||||
|
* @param url The content item url
|
||||||
|
* @param parentFolder The parent folder object, url must be relevant to it
|
||||||
|
* @return The Content Item instance, it can also be either Bundle
|
||||||
|
* or Folder objects, depending on URL and file language extension
|
||||||
|
*/
|
||||||
|
protected ContentItem getItemFromLiveURL(String url,
|
||||||
|
Folder parentFolder) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolving the item for live URL " + url +
|
||||||
|
" and parent folder " + parentFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentFolder == null || url == null || url.equals("")) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("The url is null or parent folder was null " +
|
||||||
|
"or something else is wrong, so just return " +
|
||||||
|
"the parent folder");
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = url.length();
|
||||||
|
int index = url.indexOf('/');
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
s_log.debug("The URL starts with a slash; paring off the first " +
|
||||||
|
"URL element and recursing");
|
||||||
|
|
||||||
|
// If we got first slash (index == 0), ignore it and go
|
||||||
|
// on, sample '/foo/bar/item.html.en', in next recursion
|
||||||
|
// will have deal with 'foo' folder.
|
||||||
|
|
||||||
|
String name = index > 0 ? url.substring(0, index) : "";
|
||||||
|
parentFolder = "".equals(name) ? parentFolder
|
||||||
|
: (Folder) parentFolder.getItem(URLEncoder.encode(name), true);
|
||||||
|
url = index + 1 < len ? url.substring(index + 1) : "";
|
||||||
|
|
||||||
|
return getItemFromLiveURL(url, parentFolder);
|
||||||
|
} else {
|
||||||
|
s_log.debug("Found a file element in the URL");
|
||||||
|
|
||||||
|
String[] nameAndLang = getNameAndLangFromURLFrag(url);
|
||||||
|
String name = nameAndLang[0];
|
||||||
|
String lang = nameAndLang[1];
|
||||||
|
|
||||||
|
ContentItem item = parentFolder.getItem(URLEncoder.encode(name), false);
|
||||||
|
return getItemFromLangAndBundle(lang, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array containing the the item's name and lang based
|
||||||
|
* on the URL fragment.
|
||||||
|
*
|
||||||
|
* @return a two-element string array, the first element
|
||||||
|
* containing the bundle name, and the second element containing
|
||||||
|
* the lang string
|
||||||
|
*/
|
||||||
|
protected String[] getNameAndLangFromURLFrag(String url) {
|
||||||
|
String name = null;
|
||||||
|
String lang = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find out if there's an extension with the language code
|
||||||
|
* 1 Get a list of all "extensions", i.e. parts of the url
|
||||||
|
* which are separated by colons
|
||||||
|
* 2 If one or more extensions have been found, compare them against
|
||||||
|
* the list of known language codes
|
||||||
|
* 2a if a match is found, this language is used to retrieve an instance
|
||||||
|
* from a bundle
|
||||||
|
* 2b if no match is found
|
||||||
|
*/
|
||||||
|
|
||||||
|
final ArrayList exts = new ArrayList(5);
|
||||||
|
final StringTokenizer tok = new StringTokenizer(url, ".");
|
||||||
|
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
exts.add(tok.nextToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exts.size() > 0) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found some file extensions to look at; they " +
|
||||||
|
"are " + exts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have found at least one extension, so we can
|
||||||
|
* proceed. Now try to find out if one of the
|
||||||
|
* extensions looks like a language code (we only
|
||||||
|
* support 2-letter language codes!).
|
||||||
|
* If so, use this as the language to look for.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First element is the NAME of the item, not an extension!
|
||||||
|
*/
|
||||||
|
name = (String) exts.get(0);
|
||||||
|
String ext = null;
|
||||||
|
Collection supportedLangs =
|
||||||
|
LanguageUtil.getSupportedLanguages2LA();
|
||||||
|
Iterator supportedLangIt = null;
|
||||||
|
|
||||||
|
for (int i = 1; i < exts.size(); i++) {
|
||||||
|
ext = (String) exts.get(i);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Examining extension " + ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop through all extensions, but discard the
|
||||||
|
* first one, which is the name of the item.
|
||||||
|
*/
|
||||||
|
if (ext != null && ext.length() == 2) {
|
||||||
|
/* Only check extensions consisting of 2
|
||||||
|
* characters.
|
||||||
|
*
|
||||||
|
* Compare current extension with known
|
||||||
|
* languages; if it matches, we've found the
|
||||||
|
* language we should use!
|
||||||
|
*/
|
||||||
|
supportedLangIt = supportedLangs.iterator();
|
||||||
|
while (supportedLangIt.hasNext()) {
|
||||||
|
if (ext.equals(supportedLangIt.next())) {
|
||||||
|
lang = ext;
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found a match; using " +
|
||||||
|
"language " + lang);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Discarding extension " + ext + "; " +
|
||||||
|
"it is too short");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_log.debug("The file has no extensions; no language was " +
|
||||||
|
"encoded");
|
||||||
|
name = url; // no extension, so we just have a name here
|
||||||
|
lang = null; // no extension, so we cannot guess the language
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Assert.isEnabled()) {
|
||||||
|
Assert.exists(name, "String name");
|
||||||
|
Assert.exists(lang == null || lang.length() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("File name resolved to " + name);
|
||||||
|
s_log.debug("File language resolved to " + lang);
|
||||||
|
}
|
||||||
|
String[] returnArray = new String[2];
|
||||||
|
returnArray[0] = name;
|
||||||
|
returnArray[1] = lang;
|
||||||
|
return returnArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a language instance of a content item given the bundle,
|
||||||
|
* name, and lang string
|
||||||
|
*
|
||||||
|
* @param lang The lang string from the URL
|
||||||
|
* @param item The content bundle
|
||||||
|
*
|
||||||
|
* @return The negotiated lang instance for the current request.
|
||||||
|
*/
|
||||||
|
protected ContentItem getItemFromLangAndBundle(String lang, ContentItem item) {
|
||||||
|
if (item != null && item instanceof ContentBundle) {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Found content bundle " + item);
|
||||||
|
}
|
||||||
|
if (lang == null) {
|
||||||
|
s_log.debug("The URL has no language encoded in it; " +
|
||||||
|
"negotiating the language");
|
||||||
|
// There is no language, so we get the negotiated locale and call
|
||||||
|
// this method again with a proper language
|
||||||
|
return this.getItemFromLangAndBundle(GlobalizationHelper.getNegotiatedLocale().getLanguage(), item);
|
||||||
|
} else {
|
||||||
|
s_log.debug("The URL is encoded with a langauge; " +
|
||||||
|
"fetching the appropriate item from " +
|
||||||
|
"the bundle");
|
||||||
|
/*
|
||||||
|
* So the request contains a language code as an
|
||||||
|
* extension of the "name" ==>go ahead and try to
|
||||||
|
* find the item from its ContentBundle. Fail if
|
||||||
|
* the bundle does not contain an instance for the
|
||||||
|
* given language.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final ContentItem resolved =
|
||||||
|
((ContentBundle) item).getInstance(lang);
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Resolved URL to item " + resolved);
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("I expected to get a content bundle; I got " +
|
||||||
|
item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We expected something like a Bundle, but it seems
|
||||||
|
* like we got something completely different... just
|
||||||
|
* return this crap and let other people's code deal
|
||||||
|
* with it ;-).
|
||||||
|
*
|
||||||
|
* NOTE: This should never happen :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
return item; // might be null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class contains methods for registering and resolving {@link
|
||||||
|
* ResourceHandler CMS resources} in a specific content section.</p>
|
||||||
|
*
|
||||||
|
* <p>The <tt>PageResolver</tt> includes methods for caching resource
|
||||||
|
* mappings.</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public abstract class PageResolver {
|
||||||
|
|
||||||
|
private BigDecimal m_sectionID;
|
||||||
|
|
||||||
|
// Used for caching pages
|
||||||
|
private HashMap m_pages;
|
||||||
|
|
||||||
|
|
||||||
|
public PageResolver() {
|
||||||
|
m_pages = new HashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentSectionID(BigDecimal id) {
|
||||||
|
m_sectionID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BigDecimal getContentSectionID() {
|
||||||
|
return m_sectionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the page associated with the request URL.
|
||||||
|
*
|
||||||
|
* @param url The content section-relative URL stub
|
||||||
|
* @return The resource
|
||||||
|
*/
|
||||||
|
public ResourceHandler getPage(String url) {
|
||||||
|
return (ResourceHandler) m_pages.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a page to the content section.
|
||||||
|
*
|
||||||
|
* @param page The master page
|
||||||
|
* @param url The desired URL of the page
|
||||||
|
*/
|
||||||
|
public abstract void registerPage(ResourceHandler page, String url);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a page to the content section.
|
||||||
|
*
|
||||||
|
* @param page The master page
|
||||||
|
* @param url The desired URL of the page
|
||||||
|
*/
|
||||||
|
public abstract void unregisterPage(ResourceHandler page, String url);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a page into the page resolver cache.
|
||||||
|
*
|
||||||
|
* @param url The URL of the resource to load into the cache
|
||||||
|
* @param page The resource
|
||||||
|
*/
|
||||||
|
public void loadPage(String url, ResourceHandler page) {
|
||||||
|
m_pages.put(url, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes a page from the page resolver cache.
|
||||||
|
*
|
||||||
|
* @param url The URL of the resource to remove from the cache
|
||||||
|
*/
|
||||||
|
public void releasePage(String url) {
|
||||||
|
m_pages.remove(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.Dispatcher;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for resources that can be served.
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
**/
|
||||||
|
public interface ResourceHandler extends Dispatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by the {@link com.arsdigita.dispatcher.Dispatcher}
|
||||||
|
* that initializes this page.
|
||||||
|
*/
|
||||||
|
public void init() throws ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content section context for this resource.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return A content section or null if there is none
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public ContentSection getContentSection(HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content item context for this resource.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
* @return A content item or null if there is none
|
||||||
|
* @pre ( request != null )
|
||||||
|
*/
|
||||||
|
public ContentItem getContentItem(HttpServletRequest request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.util.Assert;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.AuthorizationException;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for resources that can be served.
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class ResourceHandlerImpl implements ResourceHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by the {@link com.arsdigita.dispatcher.Dispatcher}
|
||||||
|
* that initializes this page.
|
||||||
|
*/
|
||||||
|
public void init() throws ServletException {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content section.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The current content section
|
||||||
|
*/
|
||||||
|
public ContentSection getContentSection(HttpServletRequest request) {
|
||||||
|
// resets all content sections associations
|
||||||
|
ContentSection section = CMSDispatcher.getContentSection(request);
|
||||||
|
Assert.exists(section);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the request-local content item.
|
||||||
|
*
|
||||||
|
* @param request The HTTP request
|
||||||
|
*
|
||||||
|
* @return The current content item
|
||||||
|
*/
|
||||||
|
public ContentItem getContentItem(HttpServletRequest request) {
|
||||||
|
// resets all content item associations
|
||||||
|
return CMSDispatcher.getContentItem(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkUserAccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx,
|
||||||
|
ContentItem item) {
|
||||||
|
if (!CdiUtil.createCdiUtil().findBean(PermissionChecker.class)
|
||||||
|
.isPermitted(CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED, item)) {
|
||||||
|
throw new AuthorizationException(
|
||||||
|
"cms.dispatcher.no_permission_to_access_resource");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services this resource.
|
||||||
|
*
|
||||||
|
* @param request The servlet request object
|
||||||
|
* @param response the servlet response object
|
||||||
|
* @param actx The request context
|
||||||
|
*/
|
||||||
|
public abstract void dispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,445 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.cms.CMS;
|
||||||
|
import com.arsdigita.cms.CMSConfig;
|
||||||
|
import com.arsdigita.cms.ContentItem;
|
||||||
|
import com.arsdigita.cms.ContentItemXMLRenderer;
|
||||||
|
import com.arsdigita.cms.ExtraXMLGenerator;
|
||||||
|
import com.arsdigita.cms.SecurityManager;
|
||||||
|
import com.arsdigita.cms.UserDefinedContentItem;
|
||||||
|
import com.arsdigita.cms.XMLDeliveryCache;
|
||||||
|
import com.arsdigita.cms.util.GlobalizationUtil;
|
||||||
|
import com.arsdigita.domain.DataObjectNotFoundException;
|
||||||
|
import com.arsdigita.domain.DomainObjectFactory;
|
||||||
|
import com.arsdigita.domain.DomainObjectTraversal;
|
||||||
|
import com.arsdigita.domain.SimpleDomainObjectTraversalAdapter;
|
||||||
|
import com.arsdigita.kernel.Kernel;
|
||||||
|
import com.arsdigita.kernel.Party;
|
||||||
|
import com.arsdigita.kernel.permissions.PermissionDescriptor;
|
||||||
|
import com.arsdigita.kernel.permissions.PermissionService;
|
||||||
|
import com.arsdigita.kernel.permissions.PrivilegeDescriptor;
|
||||||
|
import com.arsdigita.metadata.DynamicObjectType;
|
||||||
|
import com.arsdigita.persistence.OID;
|
||||||
|
import com.arsdigita.persistence.metadata.Property;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The default <tt>XMLGenerator</tt> implementation.</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih
|
||||||
|
* @version $Revision: #20 $ $DateTime: 2004/08/17 23:15:09 $
|
||||||
|
* @version $Id: SimpleXMLGenerator.java 2167 2011-06-19 21:12:12Z pboy $
|
||||||
|
*/
|
||||||
|
public class SimpleXMLGenerator implements XMLGenerator {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(SimpleXMLGenerator.class);
|
||||||
|
public static final String ADAPTER_CONTEXT = SimpleXMLGenerator.class.getName();
|
||||||
|
/**
|
||||||
|
* jensp 2011-10-23: Sometimes the extra XML is not needed, for example
|
||||||
|
* when embedding the XML of a content item into the XML output of another
|
||||||
|
* content item. The default value {@code true}. To change the value
|
||||||
|
* call {@link #setUseExtraXml(booelan)} after creating an instance of
|
||||||
|
* your generator.
|
||||||
|
*/
|
||||||
|
private boolean useExtraXml = true;
|
||||||
|
/**
|
||||||
|
* jensp 2012-04-18: This value is forwarded to this ExtraXMLGenerators
|
||||||
|
* by calling {@link ExtraXMLGenerator#setListMode(boolean)}. The behavior
|
||||||
|
* triggered by this value depends on the specific implementation of
|
||||||
|
* the {@code ExtraXMLGenerator}
|
||||||
|
*/
|
||||||
|
private boolean listMode = false;
|
||||||
|
/**
|
||||||
|
* Extra attributes for the cms:item element.
|
||||||
|
*/
|
||||||
|
private final Map<String, String> itemAttributes = new LinkedHashMap<String, String>();
|
||||||
|
/**
|
||||||
|
* Allows to overwrite the name and the namespace of the XML element
|
||||||
|
* used to wrap the rendered item.
|
||||||
|
*/
|
||||||
|
private String itemElemName = "cms:item";
|
||||||
|
private String itemElemNs = CMS.CMS_XML_NS;
|
||||||
|
|
||||||
|
// Register general purpose adaptor for all content items
|
||||||
|
static {
|
||||||
|
s_log.debug("Static initializer starting...");
|
||||||
|
final SimpleDomainObjectTraversalAdapter adapter = new SimpleDomainObjectTraversalAdapter();
|
||||||
|
adapter.addAssociationProperty("/object/type");
|
||||||
|
adapter.addAssociationProperty("/object/categories");
|
||||||
|
|
||||||
|
DomainObjectTraversal.registerAdapter(
|
||||||
|
ContentItem.BASE_DATA_OBJECT_TYPE,
|
||||||
|
adapter,
|
||||||
|
ADAPTER_CONTEXT);
|
||||||
|
s_log.debug("Static initializer finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleXMLGenerator() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseExtraXml(final boolean useExtraXml) {
|
||||||
|
this.useExtraXml = useExtraXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListMode(final boolean listMode) {
|
||||||
|
this.listMode = listMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItemAttribute(final String name, final String value) {
|
||||||
|
itemAttributes.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemElemName(final String itemElemName, final String itemElemNs) {
|
||||||
|
this.itemElemName = itemElemName;
|
||||||
|
this.itemElemNs = itemElemNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML to render the content panel.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @param parent The parent DOM element
|
||||||
|
* @param useContext The use context
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void generateXML(final PageState state, final Element parent, final String useContext) {
|
||||||
|
//final long start = System.nanoTime();
|
||||||
|
|
||||||
|
//ContentSection section = CMS.getContext().getContentSection();
|
||||||
|
ContentItem item = getContentItem(state);
|
||||||
|
|
||||||
|
s_log.info("Generate XML for item " + item.getOID());
|
||||||
|
|
||||||
|
Party currentParty = Kernel.getContext().getParty();
|
||||||
|
if (currentParty == null) {
|
||||||
|
currentParty = Kernel.getPublicUser();
|
||||||
|
}
|
||||||
|
// check if current user can edit the current item (nb privilege is granted on draft item, but live item
|
||||||
|
// has draft as its permission context
|
||||||
|
//
|
||||||
|
// Note that the xml that is generated is only of use if you DO NOT CACHE content pages.
|
||||||
|
// cg.
|
||||||
|
final PermissionDescriptor edit = new PermissionDescriptor(
|
||||||
|
PrivilegeDescriptor.get(SecurityManager.CMS_EDIT_ITEM), item, currentParty);
|
||||||
|
if (PermissionService.checkPermission(edit)) {
|
||||||
|
parent.addAttribute("canEdit", "true");
|
||||||
|
final Element canEditElem = parent.newChildElement("canEdit");
|
||||||
|
canEditElem.setText("true");
|
||||||
|
|
||||||
|
}
|
||||||
|
final PermissionDescriptor publish = new PermissionDescriptor(
|
||||||
|
PrivilegeDescriptor.get(SecurityManager.CMS_PUBLISH), item, currentParty);
|
||||||
|
if (PermissionService.checkPermission(publish)) {
|
||||||
|
parent.addAttribute("canPublish", "true");
|
||||||
|
}
|
||||||
|
final String className = item.getDefaultDomainClass();
|
||||||
|
|
||||||
|
// Ensure correct subtype of ContentItem is instantiated
|
||||||
|
if (!item.getClass().getName().equals(className)) {
|
||||||
|
s_log.info("Specializing item");
|
||||||
|
try {
|
||||||
|
item = (ContentItem) DomainObjectFactory.newInstance(
|
||||||
|
new OID(item.getObjectType().getQualifiedName(), item.getID()));
|
||||||
|
} catch (DataObjectNotFoundException ex) {
|
||||||
|
throw new UncheckedWrapperException(
|
||||||
|
(String) GlobalizationUtil.globalize(
|
||||||
|
"cms.dispatcher.cannot_find_domain_object").localize(),
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementing XMLGenerator directly is now deprecated
|
||||||
|
if (item instanceof XMLGenerator) {
|
||||||
|
s_log.info("Item implements XMLGenerator interface");
|
||||||
|
final XMLGenerator xitem = (XMLGenerator) item;
|
||||||
|
xitem.generateXML(state, parent, useContext);
|
||||||
|
|
||||||
|
} else if ("com.arsdigita.cms.UserDefinedContentItem".equals(className)) {
|
||||||
|
s_log.info("Item is a user defined content item");
|
||||||
|
final UserDefinedContentItem UDItem = (UserDefinedContentItem) item;
|
||||||
|
generateUDItemXML(UDItem, state, parent, useContext);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
s_log.info("Item is using DomainObjectXMLRenderer");
|
||||||
|
|
||||||
|
// This is the preferred method
|
||||||
|
//final Element content = startElement(useContext, parent);
|
||||||
|
final Element content = startElement(useContext);
|
||||||
|
s_log.debug("Item is not in cache, generating item.");
|
||||||
|
|
||||||
|
final XMLDeliveryCache xmlCache = XMLDeliveryCache.getInstance();
|
||||||
|
|
||||||
|
if (CMSConfig.getInstanceOf().getEnableXmlCache() && xmlCache.isCached(item.getOID(), useContext, listMode)) {
|
||||||
|
xmlCache.retrieveFromCache(content, item.getOID(), useContext, listMode);
|
||||||
|
} else {
|
||||||
|
final ContentItemXMLRenderer renderer = new ContentItemXMLRenderer(content);
|
||||||
|
|
||||||
|
renderer.setWrapAttributes(true);
|
||||||
|
renderer.setWrapRoot(false);
|
||||||
|
renderer.setWrapObjects(false);
|
||||||
|
renderer.setRevisitFullObject(true);
|
||||||
|
|
||||||
|
//System.out.printf("Prepared renderer in %d ms\n", (System.nanoTime() - start)
|
||||||
|
// / 1000000);
|
||||||
|
|
||||||
|
renderer.walk(item, ADAPTER_CONTEXT);
|
||||||
|
|
||||||
|
//System.out.printf("Rendered standard item xml in %d ms\n", (System.nanoTime() - start)
|
||||||
|
// / 1000000);
|
||||||
|
|
||||||
|
//parent.addContent(content);
|
||||||
|
|
||||||
|
//Only item XML Cache End
|
||||||
|
|
||||||
|
// s_log.debug("Content elem content: ");
|
||||||
|
// logElementTree(content);
|
||||||
|
// s_log.debug("Content elem content end -- ");
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2011-08-27 jensp: Introduced to remove the annoying special templates
|
||||||
|
* for MultiPartArticle, SiteProxy and others. The method called
|
||||||
|
* here was already definied but not used.
|
||||||
|
*
|
||||||
|
* 2011-10-23 jensp: It is now possible to disable the use of
|
||||||
|
* extra XML.
|
||||||
|
*/
|
||||||
|
//final long extraXMLStart = System.nanoTime();
|
||||||
|
if (useExtraXml) {
|
||||||
|
for (ExtraXMLGenerator generator : item.getExtraXMLGenerators()) {
|
||||||
|
generator.setListMode(listMode);
|
||||||
|
generator.generateXML(item, content, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only published items
|
||||||
|
//Only the XML of the item itself, no extra XML
|
||||||
|
if (CMSConfig.getInstanceOf().getEnableXmlCache() && item.isLiveVersion()) {
|
||||||
|
xmlCache.cache(item.getOID(), item, content, useContext, listMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PermissionService.checkPermission(edit)) {
|
||||||
|
final ItemResolver resolver = item.getContentSection().getItemResolver();
|
||||||
|
final Element editLinkElem = content.newChildElement("editLink");
|
||||||
|
final ContentItem draftItem = item.getDraftVersion();
|
||||||
|
editLinkElem.setText(resolver.generateItemURL(state,
|
||||||
|
draftItem,
|
||||||
|
item.getContentSection(),
|
||||||
|
draftItem.getVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.addContent(content);
|
||||||
|
|
||||||
|
//System.out.printf("Rendered item in %d ms\n\n", (System.nanoTime() - start) / 1000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current content item. This method can be overridden to
|
||||||
|
* fetch any {@link com.arsdigita.cms.ContentItem}, but by default,
|
||||||
|
* it fetches the <code>ContentItem</code> that is set in the page state
|
||||||
|
* by the dispatcher.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @return A content item
|
||||||
|
*/
|
||||||
|
protected ContentItem getContentItem(final PageState state) {
|
||||||
|
if (CMS.getContext().hasContentItem()) {
|
||||||
|
return CMS.getContext().getContentItem();
|
||||||
|
} else {
|
||||||
|
final CMSPage page = (CMSPage) state.getPage();
|
||||||
|
return page.getContentItem(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateUDItemXML(final UserDefinedContentItem UDItem,
|
||||||
|
final PageState state,
|
||||||
|
final Element parent,
|
||||||
|
final String useContext) {
|
||||||
|
|
||||||
|
final Element element = startElement(useContext, parent);
|
||||||
|
final Element additionalAttrs = UDItemElement(useContext);
|
||||||
|
|
||||||
|
element.addAttribute("type", UDItem.getContentType().getName());
|
||||||
|
element.addAttribute("id", UDItem.getID().toString());
|
||||||
|
element.addAttribute("name", UDItem.getName());
|
||||||
|
element.addAttribute("title", UDItem.getTitle());
|
||||||
|
element.addAttribute("javaClass", UDItem.getContentType().getClassName());
|
||||||
|
|
||||||
|
final DynamicObjectType dot = new DynamicObjectType(UDItem.getSpecificObjectType());
|
||||||
|
final Iterator declaredProperties =
|
||||||
|
dot.getObjectType().getDeclaredProperties();
|
||||||
|
Property currentProperty;
|
||||||
|
Object value;
|
||||||
|
while (declaredProperties.hasNext()) {
|
||||||
|
currentProperty = (Property) declaredProperties.next();
|
||||||
|
value = (Object) UDItem.get(currentProperty.getName());
|
||||||
|
if (value != null) {
|
||||||
|
element.addContent(
|
||||||
|
UDItemAttrElement(currentProperty.getName(),
|
||||||
|
value.toString()));
|
||||||
|
} else {
|
||||||
|
element.addContent(
|
||||||
|
UDItemAttrElement(currentProperty.getName(),
|
||||||
|
"none specified"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//element.addContent(additionalAttrs);
|
||||||
|
//parent.addContent(element);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element startElement(final String useContext, final Element parent) {
|
||||||
|
//Element element = new Element("cms:item", CMS.CMS_XML_NS);
|
||||||
|
//final Element element = new Element(itemElemName, itemElemNs);
|
||||||
|
final Element element = parent.newChildElement(itemElemName, itemElemNs);
|
||||||
|
if (useContext != null) {
|
||||||
|
element.addAttribute("useContext", useContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> attr : itemAttributes.entrySet()) {
|
||||||
|
element.addAttribute(attr.getKey(), attr.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element startElement(final String useContext) {
|
||||||
|
final Element element = new Element(itemElemName, itemElemNs);
|
||||||
|
|
||||||
|
if (useContext != null) {
|
||||||
|
element.addAttribute("useContext", useContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> attr : itemAttributes.entrySet()) {
|
||||||
|
element.addAttribute(attr.getKey(), attr.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element startCachedElement(final String useContext) {
|
||||||
|
final Element element = new Element(itemElemName, itemElemNs) {
|
||||||
|
@Override
|
||||||
|
public Element newChildElement(Element copyFrom) {
|
||||||
|
s_log.debug("Copy of element added to cached elem.");
|
||||||
|
return super.newChildElement(copyFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Element newChildElement(String name, Element copyFrom) {
|
||||||
|
s_log.debug("Copy of element added to cached elem.");
|
||||||
|
return super.newChildElement(name, copyFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Element addContent(final Element newContent) {
|
||||||
|
s_log.debug("Content added to cached element");
|
||||||
|
return super.addContent(newContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (useContext != null) {
|
||||||
|
element.addAttribute("useContext", useContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> attr : itemAttributes.entrySet()) {
|
||||||
|
element.addAttribute(attr.getKey(), attr.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyElement(final Element parent, final Element element) {
|
||||||
|
final Element copy = parent.newChildElement(element.getName());
|
||||||
|
final Iterator attrs = element.getAttributes().entrySet().iterator();
|
||||||
|
Map.Entry attr;
|
||||||
|
while (attrs.hasNext()) {
|
||||||
|
attr = (Map.Entry) attrs.next();
|
||||||
|
copy.addAttribute((String) attr.getKey(), (String) attr.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator childs = element.getChildren().iterator();
|
||||||
|
while (childs.hasNext()) {
|
||||||
|
copyElement(copy, (Element) childs.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.getText() != null) {
|
||||||
|
copy.setText(element.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.getCDATASection() != null) {
|
||||||
|
copy.setCDATASection(element.getCDATASection());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element UDItemElement(final String useContext) {
|
||||||
|
final Element element = new Element("cms:UDItemAttributes", CMS.CMS_XML_NS);
|
||||||
|
/*
|
||||||
|
if ( useContext != null ) {
|
||||||
|
element.addAttribute("useContext", useContext);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element UDItemAttrElement(final String name, final String value) {
|
||||||
|
final Element element = new Element("cms:UDItemAttribute", CMS.CMS_XML_NS);
|
||||||
|
element.addAttribute("UDItemAttrName", name);
|
||||||
|
element.addAttribute("UDItemAttrValue", value);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logElementTree(final Element element) {
|
||||||
|
s_log.debug("Tree of element" + element.getName() + ":\n");
|
||||||
|
s_log.debug("\n" + logElementTree(element, new StringBuilder(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String logElementTree(final Element element, final StringBuilder builder, final int depth) {
|
||||||
|
for (int i = 0; i < depth; i++) {
|
||||||
|
builder.append('\t');
|
||||||
|
}
|
||||||
|
builder.append('<').append(element.getName()).append(">\n");
|
||||||
|
|
||||||
|
for (Object childObj : element.getChildren()) {
|
||||||
|
final Element child = (Element) childObj;
|
||||||
|
logElementTree(child, builder, depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < depth; i++) {
|
||||||
|
builder.append('\t');
|
||||||
|
}
|
||||||
|
builder.append("</").append(element.getName()).append(">\n");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplementation, based on ItemTemplateResolver
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Many sites offer alternative views of the same content item depending on
|
||||||
|
* device or browser, or on user preference. For example, a site may have
|
||||||
|
* "plain" and "fancy" versions of its pages. The fancy versions would be the
|
||||||
|
* defaults, while the plain versions would be appropriate for users with
|
||||||
|
* low-bandwidth connections, older browsers, or a distaste for flashy
|
||||||
|
* appurtenances. In this the case the selection might be made based on a
|
||||||
|
* cookie.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Another common example is the "printable" version of a page. In this case a
|
||||||
|
* query variable might be more appropriate.</p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Karl Goldstein (karlg@arsdigita.com)
|
||||||
|
* @version $Id: TemplateResolver.java 1967 2009-08-29 21:05:51Z pboy $
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface TemplateResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSP template filename relative to the webapp root.
|
||||||
|
*
|
||||||
|
* @param section The ContentSection for the request
|
||||||
|
* @param item The ContentItem for the request
|
||||||
|
* @param request The current HttpServletRequest
|
||||||
|
*
|
||||||
|
* @return The path to the jsp template.
|
||||||
|
*/
|
||||||
|
public String getTemplate(ContentSection section,
|
||||||
|
ContentItem item,
|
||||||
|
HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JSP template filename relative to the webapp root for a given
|
||||||
|
* Template reference.
|
||||||
|
*
|
||||||
|
* @param template The Template to resolve the URL for.
|
||||||
|
*
|
||||||
|
* @return The path to the jsp template.
|
||||||
|
*/
|
||||||
|
public String getTemplatePath(Template template);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XSL template filename relative to the webapp root for a given
|
||||||
|
* Template reference.
|
||||||
|
*
|
||||||
|
* @param template The Template to resolve the URL for.
|
||||||
|
*
|
||||||
|
* @return The path to the xsl template.
|
||||||
|
*/
|
||||||
|
public String getTemplateXSLPath(Template template);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the TemplateContext parameter in the request
|
||||||
|
*
|
||||||
|
* @param sTemplateContext the template context to set
|
||||||
|
* @param request the request in which to set the template context
|
||||||
|
*/
|
||||||
|
public void setTemplateContext(String sTemplateContext,
|
||||||
|
HttpServletRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the template context from the request.
|
||||||
|
*
|
||||||
|
* @param request the request from which to get the template context
|
||||||
|
*
|
||||||
|
* @return the template context
|
||||||
|
*/
|
||||||
|
public String getTemplateContext(HttpServletRequest request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.cms.CMS;
|
||||||
|
import com.arsdigita.cms.ContentCenter;
|
||||||
|
import com.arsdigita.cms.ContentCenterServlet;
|
||||||
|
import com.arsdigita.dispatcher.DispatcherHelper;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
import org.librecms.assets.BinaryAsset;
|
||||||
|
import org.librecms.assets.Image;
|
||||||
|
import org.librecms.contentsection.ContentSection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class provides many utility functions for the Content Management
|
||||||
|
* System.</p>
|
||||||
|
* Specifically used by various JSP templates.
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class Utilities {
|
||||||
|
|
||||||
|
// Used for caching util lookups
|
||||||
|
private static HashMap m_cache = new HashMap();
|
||||||
|
|
||||||
|
private static Date s_lastSectionRefresh = null;
|
||||||
|
private static Map s_sectionRefreshTimes =
|
||||||
|
Collections.synchronizedMap(new HashMap());
|
||||||
|
|
||||||
|
public static final Logger LOG = Logger.getLogger(Utilities.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the location of the CMS ContentCenter package.
|
||||||
|
* @return The URL of the CMS ContentCenter package
|
||||||
|
* @deprecated use ContentCenter.getURL() instead
|
||||||
|
*/
|
||||||
|
public static String getWorkspaceURL() {
|
||||||
|
|
||||||
|
return CmsConstants.CONTENT_CENTER_URL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the location (URL) of the CMS Services package. Caches the result.
|
||||||
|
* @return The URL of the CMS Services package
|
||||||
|
* @deprecated Use Service.getURL( instead
|
||||||
|
*/
|
||||||
|
public static String getServiceURL() {
|
||||||
|
String url = (String) m_cache.get(CmsConstants.SERVICE_PACKAGE_KEY);
|
||||||
|
if ( url == null ) {
|
||||||
|
// chris.gilbert@westsussex.gov.uk
|
||||||
|
// We don't want application context in this url, especially when
|
||||||
|
// it gets cached in a static variable - if I have a
|
||||||
|
// file that is maintained by a non cms application eg
|
||||||
|
// forum, then I can end up with a url that doesn't work
|
||||||
|
// and so breaks file links everywhere
|
||||||
|
// url = getSingletonPackageURLSansContext(CMS.SERVICE_PACKAGE_KEY);
|
||||||
|
url = CmsConstants.SERVICE_URL;
|
||||||
|
m_cache.put(CmsConstants.SERVICE_PACKAGE_KEY, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to log out.
|
||||||
|
* @return The logout URL
|
||||||
|
*/
|
||||||
|
public static String getLogoutURL() {
|
||||||
|
//StringBuffer buf = new StringBuffer(getServiceURL());
|
||||||
|
StringBuilder buf = new StringBuilder(CmsConstants.SERVICE_URL );
|
||||||
|
buf.append("logout");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a URL which serves a binary asset.
|
||||||
|
*
|
||||||
|
* @param asset The binary asset
|
||||||
|
* @return the URL which will serve the specified binary asset
|
||||||
|
* @deprecated Use Service.getAssetURL(BinaryAsset asset) instead
|
||||||
|
*/
|
||||||
|
public static String getAssetURL(BinaryAsset asset) {
|
||||||
|
return getAssetURL(asset.getAssetId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constuct a URL which serves a binary asset.
|
||||||
|
*
|
||||||
|
* @param assetId The asset ID
|
||||||
|
* @return the URL which will serve the specified binary asset
|
||||||
|
* @deprecated Use Service.getAssetURL(BigDecimal assetId) instead
|
||||||
|
*/
|
||||||
|
public static String getAssetURL(long assetId) {
|
||||||
|
// StringBuffer buf = new StringBuffer(getServiceURL());
|
||||||
|
StringBuilder buf = new StringBuilder(CmsConstants.SERVICE_URL );
|
||||||
|
buf.append("stream/asset?");
|
||||||
|
buf.append(CmsConstants.ASSET_ID).append("=").append(assetId);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constuct a URL which serves an image.
|
||||||
|
*
|
||||||
|
* @param asset The image asset whose image is to be served
|
||||||
|
* @return the URL which will serve the specified image asset
|
||||||
|
* @deprecated Use Service.getImageURL(ImageAsset) instead!
|
||||||
|
*/
|
||||||
|
public static String getImageURL(Image asset) {
|
||||||
|
// StringBuffer buf = new StringBuffer(getServiceURL());
|
||||||
|
StringBuilder buf = new StringBuilder(CmsConstants.SERVICE_URL );
|
||||||
|
buf.append("stream/image/?");
|
||||||
|
buf.append(CmsConstants.IMAGE_ID).append("=").append(asset.getAssetId());
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getGlobalAssetsURL() {
|
||||||
|
return getWebappContext();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Fetch the context path of the request. This is typically "/".
|
||||||
|
*
|
||||||
|
* @return The webapp context path
|
||||||
|
*/
|
||||||
|
public static String getWebappContext() {
|
||||||
|
return DispatcherHelper.getWebappContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for the last refresh on authoring kits or content types in
|
||||||
|
* a section.
|
||||||
|
**/
|
||||||
|
public static synchronized Date
|
||||||
|
getLastSectionRefresh(ContentSection section) {
|
||||||
|
|
||||||
|
// cache by URL string instead of by section object to avoid
|
||||||
|
// holding the reference.
|
||||||
|
|
||||||
|
String sectionURL = section.getPrimaryUrl();
|
||||||
|
|
||||||
|
Date lastModified = (Date) s_sectionRefreshTimes.get(sectionURL);
|
||||||
|
if (lastModified == null) {
|
||||||
|
lastModified = new Date();
|
||||||
|
s_lastSectionRefresh = lastModified;
|
||||||
|
s_sectionRefreshTimes.put(sectionURL, lastModified);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for the last refresh on authoring kits or content types in
|
||||||
|
* any section.
|
||||||
|
**/
|
||||||
|
public static Date getLastSectionRefresh() {
|
||||||
|
|
||||||
|
// instantiate last refresh lazily to ensure that first result is
|
||||||
|
// predictable.
|
||||||
|
|
||||||
|
if (s_lastSectionRefresh == null) {
|
||||||
|
s_lastSectionRefresh = new Date();
|
||||||
|
}
|
||||||
|
return s_lastSectionRefresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the authoring UI to reload. This should be done every time an
|
||||||
|
* authoring kit or a content type are updated.
|
||||||
|
*/
|
||||||
|
public static void refreshItemUI(PageState state) {
|
||||||
|
// Drop the authoring kit UI to force it to refresh
|
||||||
|
// THE URL SHOULD NOT BE HARDCODED !
|
||||||
|
|
||||||
|
ContentSection section = CMS.getContext().getContentSection();
|
||||||
|
|
||||||
|
// OLD APPROACH: used in conjunction with CMSDispatcher. This
|
||||||
|
// shouldn't do any harm even if CMSDispatcher is not being used.
|
||||||
|
CMSDispatcher.releaseResource(section, "admin/item");
|
||||||
|
refreshAdminUI(state);
|
||||||
|
|
||||||
|
// NEW APPROACH: used in conjunction with
|
||||||
|
// ContentSectionDispatcher. cache by URL string instead of by
|
||||||
|
// section object to avoid holding the reference. This shouldn't
|
||||||
|
// do any harm even if ContentSectionDispatcher is not being used.
|
||||||
|
s_lastSectionRefresh = new Date();
|
||||||
|
s_sectionRefreshTimes.put(section.getPrimaryUrl(),
|
||||||
|
s_lastSectionRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the authoring UI to reload. This should be done every time an
|
||||||
|
* authoring kit or a content type are updated.
|
||||||
|
*/
|
||||||
|
public static void refreshAdminUI(PageState state) {
|
||||||
|
// Drop the admin UI to force it to refresh
|
||||||
|
// THE URL SHOULD NOT BE HARDCODED !
|
||||||
|
|
||||||
|
ContentSection section = CMS.getContext().getContentSection();
|
||||||
|
|
||||||
|
CMSDispatcher.releaseResource(section, "admin");
|
||||||
|
CMSDispatcher.releaseResource(section, "admin/index");
|
||||||
|
CMSDispatcher.releaseResource(section, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the "pragma: no-cache" header to the HTTP response to make sure
|
||||||
|
* the browser does not cache tha page
|
||||||
|
*
|
||||||
|
* @param response The HTTP response
|
||||||
|
* @deprecated use
|
||||||
|
* com.arsdigita.dispatcher.DispatcherHelper.cacheDisable(HttpServletResponse)
|
||||||
|
*/
|
||||||
|
public static void disableBrowserCache(HttpServletResponse response) {
|
||||||
|
response.addHeader("pragma", "no-cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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.cms.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Generates XML representing a Content Item.</p>
|
||||||
|
*
|
||||||
|
* <p>As the last step of servicing a page, the
|
||||||
|
* {@link com.arsdigita.cms.dispatcher.MasterPage} will go through the
|
||||||
|
* hierarchy of its components and ask each of them to convert themselves
|
||||||
|
* to XML. A MasterPage contains a special component that knows how to ask
|
||||||
|
* its content section for the XML generator that should be applied. The
|
||||||
|
* XML generator's <code>generateXML</code> method in turn asks the
|
||||||
|
* containing page for the content item, the one that the
|
||||||
|
* {@link com.arsdigita.cms.dispatcher.ItemResolver} found before, and
|
||||||
|
* formats it as an XML document.</p>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface XMLGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the XML to render the content panel.
|
||||||
|
*
|
||||||
|
* @param state The page state
|
||||||
|
* @param parent The parent DOM element
|
||||||
|
* @param useContext The use context
|
||||||
|
*/
|
||||||
|
public void generateXML(PageState state, Element parent, String useContext);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package com.arsdigita.cms.ui;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.BebopConfig;
|
||||||
|
import com.arsdigita.bebop.Container;
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.Page;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.page.PageTransformer;
|
||||||
|
import com.arsdigita.cms.dispatcher.Utilities;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
import com.arsdigita.xml.Element;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.security.User;
|
||||||
|
import org.libreccm.web.CcmApplication;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A <tt>CMSApplicationPage</tt> is a Bebop {@link com.arsdigita.bebop.Page}
|
||||||
|
* implementation serving as a base for any CMS pageElement served by a servlet.
|
||||||
|
*
|
||||||
|
* It stores the current {@link com.arsdigita.cms.ContentSection} and, if
|
||||||
|
* applicable, the {@link com.arsdigita.cms.ContentItem} in the pageElement
|
||||||
|
* state as request local objects. Components that are part of the
|
||||||
|
* <tt>CMSPage</tt> may access these objects by calling:
|
||||||
|
* <blockquote><code><pre>
|
||||||
|
* getContentSection(PageState state);
|
||||||
|
* </pre></code></blockquote>
|
||||||
|
*
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @author Uday Mathur (umathur@arsdigita.com)
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class CMSApplicationPage extends Page {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
CMSApplicationPage.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The global assets URL stub XML parameter name.
|
||||||
|
*/
|
||||||
|
public final static String ASSETS = "ASSETS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The XML pageElement class.
|
||||||
|
*/
|
||||||
|
public final static String PAGE_CLASS = "CMS";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of XML parameters
|
||||||
|
*/
|
||||||
|
private Map<String, String> parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private PageTransformer pageTransformer;
|
||||||
|
|
||||||
|
public CMSApplicationPage() {
|
||||||
|
super();
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSApplicationPage(final String title) {
|
||||||
|
super(title);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSApplicationPage(final String title, final Container panel) {
|
||||||
|
super(title, panel);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSApplicationPage(final Label title) {
|
||||||
|
super(title);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSApplicationPage(final Label title, final Container panel) {
|
||||||
|
super(title, panel);
|
||||||
|
buildPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the pageElement.
|
||||||
|
*/
|
||||||
|
private void buildPage() {
|
||||||
|
|
||||||
|
// Set the class attribute value (down in SimpleComponent).
|
||||||
|
setClassAttr(PAGE_CLASS);
|
||||||
|
|
||||||
|
// Global XML params.
|
||||||
|
// MP: This only works with older versions of Xalan.
|
||||||
|
parameters = new HashMap<>();
|
||||||
|
setXMLParameter(ASSETS, Utilities.getGlobalAssetsURL());
|
||||||
|
|
||||||
|
// MP: This is a hack to so that the XML params work with the newer
|
||||||
|
// version of Xalan.
|
||||||
|
setAttribute(ASSETS, Utilities.getGlobalAssetsURL());
|
||||||
|
|
||||||
|
// Make sure the error display gets rendered.
|
||||||
|
getErrorDisplay().setIdAttr("page-body");
|
||||||
|
|
||||||
|
final Class<PresentationManager> presenterClass = BebopConfig
|
||||||
|
.getConfig().getPresenterClass();
|
||||||
|
final PresentationManager presenter;
|
||||||
|
try {
|
||||||
|
presenter = presenterClass.newInstance();
|
||||||
|
} catch (InstantiationException |
|
||||||
|
IllegalAccessException ex) {
|
||||||
|
throw new RuntimeException("Failed to create PresentationManager",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presenter instanceof PageTransformer) {
|
||||||
|
pageTransformer = (PageTransformer) presenter;
|
||||||
|
} else {
|
||||||
|
pageTransformer = new PageTransformer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes and locks the pageElement. If the pageElement is already locked,
|
||||||
|
* does nothing.
|
||||||
|
*
|
||||||
|
* Client classes may overwrite this method to add context specific bits to
|
||||||
|
* the page before it is locked.
|
||||||
|
*
|
||||||
|
* This method is called by the various servlets serving the various pages
|
||||||
|
* of the CMS package, before serving and displaying the page.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @param app
|
||||||
|
*/
|
||||||
|
public synchronized void init(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response,
|
||||||
|
final CcmApplication app) {
|
||||||
|
LOGGER.debug("Initializing the page");
|
||||||
|
|
||||||
|
if (!isLocked()) {
|
||||||
|
LOGGER.debug("The page hasn't been locked; locking it now");
|
||||||
|
|
||||||
|
lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the value of the XML parameter.
|
||||||
|
*
|
||||||
|
* @param name The parameter name
|
||||||
|
*
|
||||||
|
* @return The parameter value
|
||||||
|
*
|
||||||
|
* @pre (name != null)
|
||||||
|
*/
|
||||||
|
public String getXMLParameter(final String name) {
|
||||||
|
return parameters.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an XML parameter.
|
||||||
|
*
|
||||||
|
* @param name The parameter name
|
||||||
|
* @param value The parameter value
|
||||||
|
*
|
||||||
|
* @pre (name != null)
|
||||||
|
*/
|
||||||
|
public void setXMLParameter(String name, String value) {
|
||||||
|
parameters.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrites bebop.Page#generateXMLHelper to add the name of the user
|
||||||
|
* logged in to the pageElement (displayed as part of the header).
|
||||||
|
* @param state
|
||||||
|
* @param parent
|
||||||
|
* @return pageElement for use in generateXML
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Element generateXMLHelper(final PageState state,
|
||||||
|
final Document parent) {
|
||||||
|
|
||||||
|
/* Retain elements already included. */
|
||||||
|
Element pageElement = super.generateXMLHelper(state,parent);
|
||||||
|
|
||||||
|
/* Add name of user logged in. */
|
||||||
|
// Note: There are at least 2 ways in the API to determin the user
|
||||||
|
// TODO: Check for differences, determin the best / recommended way and
|
||||||
|
// document it in the classes. Probably remove one ore the other
|
||||||
|
// way from the API if possible.
|
||||||
|
final Shiro shiro = CdiUtil.createCdiUtil().findBean(Shiro.class);
|
||||||
|
final User user = shiro.getUser();
|
||||||
|
// User user = Web.getWebContext().getUser();
|
||||||
|
if ( user != null ) {
|
||||||
|
pageElement.addAttribute("name",user.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-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.cms.ui;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Component;
|
||||||
|
import com.arsdigita.bebop.PageState;
|
||||||
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
|
import com.arsdigita.bebop.TabbedPane;
|
||||||
|
import com.arsdigita.bebop.event.RequestEvent;
|
||||||
|
import com.arsdigita.bebop.event.RequestListener;
|
||||||
|
import com.arsdigita.bebop.parameters.BigDecimalParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.BooleanParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.IntegerParameter;
|
||||||
|
import com.arsdigita.bebop.parameters.StringParameter;
|
||||||
|
import com.arsdigita.cms.*;
|
||||||
|
import com.arsdigita.cms.dispatcher.CMSPage;
|
||||||
|
import com.arsdigita.cms.util.GlobalizationUtil;
|
||||||
|
import com.arsdigita.dispatcher.RequestContext;
|
||||||
|
import com.arsdigita.domain.DataObjectNotFoundException;
|
||||||
|
import com.arsdigita.templating.PresentationManager;
|
||||||
|
import com.arsdigita.templating.Templating;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.web.Application;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
import com.arsdigita.xml.Document;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The Item Search page.</p>
|
||||||
|
*
|
||||||
|
* @author Scott Seago (scott@arsdigita.com)
|
||||||
|
* @author Sören Bernstein <quasi@quasiweb.de>
|
||||||
|
* @author Jens Pelzetter (jens@jp-digital.de)
|
||||||
|
*/
|
||||||
|
public class ItemSearchPage extends CMSPage {
|
||||||
|
|
||||||
|
private final static String XSL_CLASS = "CMS Admin";
|
||||||
|
private TabbedPane m_tabbedPane;
|
||||||
|
private ItemSearchFlatBrowsePane m_flatBrowse;
|
||||||
|
private ItemSearchBrowsePane m_browse;
|
||||||
|
private ItemSearchPopup m_search;
|
||||||
|
private ItemSearchCreateItemPane m_create;
|
||||||
|
private BigDecimalParameter m_sectionId;
|
||||||
|
private int m_lastTab;
|
||||||
|
private static final CMSConfig s_conf = CMSConfig.getInstanceOf();
|
||||||
|
private static final boolean LIMIT_TO_CONTENT_SECTION = false;
|
||||||
|
public static final String CONTENT_SECTION = "section_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new ItemSearchPage
|
||||||
|
*/
|
||||||
|
public ItemSearchPage() {
|
||||||
|
super(GlobalizationUtil.globalize("cms.ui.item_search.page_title").localize().toString(), new SimpleContainer());
|
||||||
|
|
||||||
|
setClassAttr("cms-admin");
|
||||||
|
|
||||||
|
addGlobalStateParam(new BigDecimalParameter(ItemSearch.SINGLE_TYPE_PARAM));
|
||||||
|
addGlobalStateParam(new StringParameter(ItemSearchPopup.WIDGET_PARAM));
|
||||||
|
addGlobalStateParam(new StringParameter("searchWidget"));
|
||||||
|
addGlobalStateParam(new StringParameter("publishWidget"));
|
||||||
|
addGlobalStateParam(new StringParameter("defaultCreationFolder"));
|
||||||
|
addGlobalStateParam(new IntegerParameter("lastTab"));
|
||||||
|
addGlobalStateParam(new BooleanParameter("disableCreatePane"));
|
||||||
|
addGlobalStateParam(new BooleanParameter("editAfterCreate"));
|
||||||
|
addGlobalStateParam(new StringParameter("queryField"));
|
||||||
|
m_sectionId = new BigDecimalParameter(CONTENT_SECTION);
|
||||||
|
addGlobalStateParam(m_sectionId);
|
||||||
|
|
||||||
|
m_flatBrowse = getFlatBrowsePane();
|
||||||
|
m_browse = getBrowsePane();
|
||||||
|
m_search = getSearchPane();
|
||||||
|
m_create = getCreatePane();
|
||||||
|
|
||||||
|
m_tabbedPane = createTabbedPane();
|
||||||
|
m_tabbedPane.setIdAttr("page-body");
|
||||||
|
add(m_tabbedPane);
|
||||||
|
|
||||||
|
addRequestListener(new RequestListener() {
|
||||||
|
|
||||||
|
public void pageRequested(final RequestEvent event) {
|
||||||
|
final PageState state = event.getPageState();
|
||||||
|
|
||||||
|
final String query = (String) state.getValue(new StringParameter(ItemSearchPopup.QUERY));
|
||||||
|
final Boolean disableCreatePane = (Boolean) state.getValue(new BooleanParameter("disableCreatePane"));
|
||||||
|
|
||||||
|
BigDecimal typeParam =
|
||||||
|
(BigDecimal) state.getValue(new BigDecimalParameter(ItemSearch.SINGLE_TYPE_PARAM));
|
||||||
|
if ((typeParam == null) || disableCreatePane) {
|
||||||
|
m_tabbedPane.setTabVisible(state, m_create, false);
|
||||||
|
m_create.setVisible(state, false);
|
||||||
|
} else {
|
||||||
|
m_tabbedPane.setTabVisible(state, m_create, true);
|
||||||
|
m_create.setVisible(state, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.getValue(new IntegerParameter("lastTab")) == null) {
|
||||||
|
if ((query == null) || query.isEmpty()) {
|
||||||
|
m_tabbedPane.setSelectedIndex(state, 1);
|
||||||
|
} else {
|
||||||
|
m_tabbedPane.setSelectedIndex(state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_tabbedPane.setTabVisible(state, m_create, false);
|
||||||
|
// m_create.setVisible(state, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
state.setValue(new IntegerParameter("lastTab"), m_tabbedPane.getSelectedIndex(state));
|
||||||
|
|
||||||
|
if (state.getValue(new StringParameter("defaultCreationFolder")) != null) {
|
||||||
|
m_create.setDefaultFolder((String) state.getValue(new StringParameter("defaultCreationFolder")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.getValue(new BooleanParameter("editAfterCreate")) != null) {
|
||||||
|
m_create.setEditAfterCreate((Boolean) state.getValue(new BooleanParameter("editAfterCreate")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.getValue(new StringParameter("queryField")) == null) {
|
||||||
|
//Because of Bebops silly stateful behaviour we have to do this...
|
||||||
|
m_flatBrowse.resetQueryFields();
|
||||||
|
}else {
|
||||||
|
m_flatBrowse.addQueryField((String) state.getValue(new StringParameter("queryField")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (m_lastTab != m_tabbedPane.getSelectedIndex(state)) {
|
||||||
|
// m_lastTab = m_tabbedPane.getSelectedIndex(state);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //If create pane is selected do nothing (else we don't stay in the create pane)
|
||||||
|
// if (m_tabbedPane.getCurrentPane(state) == m_create) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if ((query == null) || query.isEmpty()) {
|
||||||
|
// m_tabbedPane.setSelectedIndex(state, 1);
|
||||||
|
// } else {
|
||||||
|
// m_tabbedPane.setSelectedIndex(state, 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (m_tabbedPane.getCurrentPane(state) == m_create) {
|
||||||
|
// m_tabbedPane.setTabVisible(state, m_create, false);
|
||||||
|
// m_create.setVisible(state, false);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// m_lastTab = m_tabbedPane.getSelectedIndex(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// m_tabbedPane.addActionListener(new ActionListener() {
|
||||||
|
//
|
||||||
|
// public void actionPerformed(final ActionEvent event) {
|
||||||
|
// final PageState state = event.getPageState();
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
|
||||||
|
// m_flatBrowse.addProcessListener(new FormProcessListener() {
|
||||||
|
//
|
||||||
|
// public void process(final FormSectionEvent fse) throws FormProcessException {
|
||||||
|
// if (m_flatBrowse.getSubmit().isSelected(fse.getPageState())) {
|
||||||
|
// enableCreatePane(fse.getPageState());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
} // END constructor
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates, and then caches, the Browse pane.
|
||||||
|
*
|
||||||
|
* Overriding this method to return null will prevent this tab from
|
||||||
|
* appearing. Note: not implemented yet.
|
||||||
|
*/
|
||||||
|
protected ItemSearchBrowsePane getBrowsePane() {
|
||||||
|
if (m_browse == null) {
|
||||||
|
m_browse = new ItemSearchBrowsePane();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_browse;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ItemSearchFlatBrowsePane getFlatBrowsePane() {
|
||||||
|
if (m_flatBrowse == null) {
|
||||||
|
//m_flatBrowse = new ItemSearchFlatBrowsePane("flatBrowse");
|
||||||
|
m_flatBrowse = new ItemSearchFlatBrowsePane();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_flatBrowse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates, and then caches, the Creation pane.
|
||||||
|
* Overriding this method to return null will prevent this tab from
|
||||||
|
* appearing.
|
||||||
|
*/
|
||||||
|
protected ItemSearchPopup getSearchPane() {
|
||||||
|
if (m_search == null) {
|
||||||
|
// Always search in every content section
|
||||||
|
// m_search = new ItemSearchPopup(ContentItem.DRAFT, CMS.getConfig().limitToContentSection());
|
||||||
|
m_search = new ItemSearchPopup(ContentItem.DRAFT, LIMIT_TO_CONTENT_SECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_search;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ItemSearchCreateItemPane getCreatePane() {
|
||||||
|
if (m_create == null) {
|
||||||
|
m_create = new ItemSearchCreateItemPane(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_create;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created the TabbedPane to use for this page.
|
||||||
|
*
|
||||||
|
* Sets the class attribute for this tabbed pane. The default implementation
|
||||||
|
* uses a {@link com.arsdigita.bebop.TabbedPane} and sets the class
|
||||||
|
* attribute to "CMS Admin." This implementation also adds tasks,
|
||||||
|
* content sections, and search panes.
|
||||||
|
*
|
||||||
|
* Developers can override this method to add only the tabs they want,
|
||||||
|
* or to add additional tabs after the default CMS tabs are added.
|
||||||
|
*/
|
||||||
|
protected TabbedPane createTabbedPane() {
|
||||||
|
TabbedPane pane = new TabbedPane();
|
||||||
|
pane.setClassAttr(XSL_CLASS);
|
||||||
|
|
||||||
|
|
||||||
|
addToPane(pane, "flatBrowse", getFlatBrowsePane());
|
||||||
|
addToPane(pane, "browse", getBrowsePane());
|
||||||
|
addToPane(pane, "search", getSearchPane());
|
||||||
|
addToPane(pane, "create", getCreatePane());
|
||||||
|
|
||||||
|
if ("browse".equals(s_conf.getItemSearchDefaultTab())) {
|
||||||
|
pane.setDefaultPane(m_browse);
|
||||||
|
}
|
||||||
|
if ("search".equals(s_conf.getItemSearchDefaultTab())) {
|
||||||
|
pane.setDefaultPane(m_search);
|
||||||
|
}
|
||||||
|
|
||||||
|
//pane.setDefaultPane(m_flatBrowse);
|
||||||
|
pane.setDefaultPane(m_browse);
|
||||||
|
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified component, with the specified tab name, to the
|
||||||
|
* tabbed pane only if it is not null.
|
||||||
|
*
|
||||||
|
* @param pane The pane to which to add the tab
|
||||||
|
* @param tabName The name of the tab if it's added
|
||||||
|
* @param comp The component to add to the pane
|
||||||
|
*/
|
||||||
|
protected void addToPane(TabbedPane pane, String tabName, Component comp) {
|
||||||
|
if (comp != null) {
|
||||||
|
|
||||||
|
pane.addTab(GlobalizationUtil
|
||||||
|
.globalize("cms.ui.item_search." + tabName)
|
||||||
|
.localize().toString()
|
||||||
|
,comp);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This strange voodoo from Dan. No idea what it does.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void dispatch(final HttpServletRequest request,
|
||||||
|
final HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
new CMSExcursion() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void excurse()
|
||||||
|
throws IOException, ServletException {
|
||||||
|
ContentSection section = null;
|
||||||
|
Application app = Web.getWebContext().getApplication();
|
||||||
|
if (app instanceof ContentSection) {
|
||||||
|
section = (ContentSection) app;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
section = new ContentSection((BigDecimal) m_sectionId.transformValue(request));
|
||||||
|
} catch (DataObjectNotFoundException ex) {
|
||||||
|
throw new UncheckedWrapperException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setContentSection(section);
|
||||||
|
|
||||||
|
final Document doc = buildDocument(request, response);
|
||||||
|
final PresentationManager pm =
|
||||||
|
Templating.getPresentationManager();
|
||||||
|
|
||||||
|
pm.servePage(doc, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTabActive(final PageState state, final Component component, final boolean value) {
|
||||||
|
m_tabbedPane.setTabVisible(state, component, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTabActive(final PageState state, final int index, final boolean value) {
|
||||||
|
m_tabbedPane.setTabVisible(state, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDefaultCreationFolder(final Folder folder) {
|
||||||
|
m_create.setDefaultFolder(folder.getOID().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setEditAfterCreate(final boolean editAfterCreate) {
|
||||||
|
m_create.setEditAfterCreate(editAfterCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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.cms.ui.contentcenter;
|
||||||
|
|
||||||
|
import com.arsdigita.bebop.Label;
|
||||||
|
import com.arsdigita.bebop.SimpleContainer;
|
||||||
|
import com.arsdigita.bebop.TabbedPane;
|
||||||
|
import com.arsdigita.bebop.event.ActionEvent;
|
||||||
|
import com.arsdigita.bebop.event.ActionListener;
|
||||||
|
import com.arsdigita.bebop.parameters.BigDecimalParameter;
|
||||||
|
import com.arsdigita.cms.ui.CMSApplicationPage;
|
||||||
|
import com.arsdigita.globalization.GlobalizedMessage;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.librecms.CmsConstants;
|
||||||
|
|
||||||
|
// ////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Developer's Note:
|
||||||
|
// Replaces the (old) Dispatcher based Code cms.ui.CMSPageWorkspacePage
|
||||||
|
// Note should be removed as soon as the migration process is competed (in-
|
||||||
|
// cluding content section pages).
|
||||||
|
//
|
||||||
|
// ////////////////////////////////////////////////////////////////////////////
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The Content Center main page. </p>
|
||||||
|
*
|
||||||
|
* The page contains the general header and footer, the breadcrumb, and the
|
||||||
|
* complete content page including the tab bar, the sections/tasks page, the
|
||||||
|
* search page, and the listener to switch between the tabs.
|
||||||
|
*
|
||||||
|
* @author Jack Chung (flattop@arsdigita.com)
|
||||||
|
* @author Michael Pih (pihman@arsdigita.com)
|
||||||
|
* @author Peter Boy (pboy@barkhof.uni-bremen.de)
|
||||||
|
* @version $Id: MainPage.java pboy $
|
||||||
|
*/
|
||||||
|
public class MainPage extends CMSApplicationPage implements ActionListener {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(MainPage.class);
|
||||||
|
|
||||||
|
private final static String XSL_CLASS = "CMS Admin";
|
||||||
|
|
||||||
|
private TabbedPane m_tabbedPane;
|
||||||
|
|
||||||
|
private TasksPanel m_tasks;
|
||||||
|
private ItemSearch m_search;
|
||||||
|
private IdSearchTab m_IdSearch;
|
||||||
|
private ACSObjectSelectionModel m_typeSel;
|
||||||
|
private ACSObjectSelectionModel m_sectionSel;
|
||||||
|
|
||||||
|
public static final String CONTENT_TYPE = "type_id";
|
||||||
|
public static final String CONTENT_SECTION = "section_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new MainPage.
|
||||||
|
*
|
||||||
|
* Creates the complete page ready to be included in the page cache of
|
||||||
|
* ContentCenterServlet.
|
||||||
|
*/
|
||||||
|
public MainPage() {
|
||||||
|
|
||||||
|
super(new Label(new GlobalizedMessage("cms.ui.content_center",
|
||||||
|
CmsConstants.CMS_BUNDLE)),
|
||||||
|
new SimpleContainer());
|
||||||
|
|
||||||
|
|
||||||
|
/* Set the class attribute value (down in SimpleComponent). */
|
||||||
|
setClassAttr("cms-admin");
|
||||||
|
|
||||||
|
BigDecimalParameter typeId = new BigDecimalParameter(CONTENT_TYPE);
|
||||||
|
addGlobalStateParam(typeId);
|
||||||
|
m_typeSel = new ACSObjectSelectionModel(
|
||||||
|
ContentType.class.getName(),
|
||||||
|
ContentType.BASE_DATA_OBJECT_TYPE,
|
||||||
|
typeId
|
||||||
|
);
|
||||||
|
|
||||||
|
BigDecimalParameter sectionId = new BigDecimalParameter(CONTENT_SECTION);
|
||||||
|
addGlobalStateParam(sectionId);
|
||||||
|
m_sectionSel = new ACSObjectSelectionModel(
|
||||||
|
ContentSection.class.getName(),
|
||||||
|
ContentSection.BASE_DATA_OBJECT_TYPE,
|
||||||
|
sectionId
|
||||||
|
);
|
||||||
|
|
||||||
|
add(new WorkspaceContextBar());
|
||||||
|
add(new GlobalNavigation());
|
||||||
|
|
||||||
|
m_tasks = getTasksPane(m_typeSel, m_sectionSel);
|
||||||
|
m_search = getSearchPane();
|
||||||
|
m_IdSearch = getIdSearchPane();
|
||||||
|
|
||||||
|
m_tabbedPane = createTabbedPane();
|
||||||
|
m_tabbedPane.setIdAttr("page-body");
|
||||||
|
add(m_tabbedPane);
|
||||||
|
|
||||||
|
add(new DebugPanel());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates, and then caches, the Tasks pane. Overriding this method to
|
||||||
|
* return null will prevent this tab from appearing.
|
||||||
|
*/
|
||||||
|
protected TasksPanel getTasksPane(ACSObjectSelectionModel typeModel,
|
||||||
|
ACSObjectSelectionModel sectionModel) {
|
||||||
|
if (m_tasks == null) {
|
||||||
|
m_tasks = new TasksPanel(typeModel, sectionModel);
|
||||||
|
}
|
||||||
|
return m_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates, and then caches, the Search pane. Overriding this method to
|
||||||
|
* return null will prevent this tab from appearing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected ItemSearch getSearchPane() {
|
||||||
|
if (m_search == null) {
|
||||||
|
m_search = new ItemSearch(ContentItem.DRAFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_search;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IdSearchTab getIdSearchPane() {
|
||||||
|
if (m_IdSearch == null) {
|
||||||
|
m_IdSearch = new IdSearchTab("idsearch");
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_IdSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created the TabbedPane to use for this page. Sets the class attribute for
|
||||||
|
* this tabbed pane. The default implementation uses a
|
||||||
|
* {@link com.arsdigita.bebop.TabbedPane} and sets the class attribute to
|
||||||
|
* "CMS Admin." This implementation also adds tasks, content sections, and
|
||||||
|
* search panes.
|
||||||
|
*
|
||||||
|
* Developers can override this method to add only the tabs they want, or to
|
||||||
|
* add additional tabs after the default CMS tabs are added.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected TabbedPane createTabbedPane() {
|
||||||
|
TabbedPane tabbedPane = new TabbedPane();
|
||||||
|
tabbedPane.setClassAttr(XSL_CLASS);
|
||||||
|
Label taskLabel = new Label(GlobalizationUtil
|
||||||
|
.globalize("cms.ui.contentcenter.mainpage.taskssections"));
|
||||||
|
Label searchLabel = new Label(GlobalizationUtil
|
||||||
|
.globalize("cms.ui.contentcenter.mainpage.search"));
|
||||||
|
Label IdsearchLabel = new Label("ID Search");
|
||||||
|
|
||||||
|
addToPane(tabbedPane,
|
||||||
|
taskLabel,
|
||||||
|
getTasksPane(m_typeSel, m_sectionSel));
|
||||||
|
addToPane(tabbedPane,
|
||||||
|
// searchLabel,
|
||||||
|
new Label(GlobalizationUtil.globalize(
|
||||||
|
"cms.ui.contentcenter.mainpage.search")),
|
||||||
|
getSearchPane());
|
||||||
|
addToPane(tabbedPane,
|
||||||
|
IdsearchLabel,
|
||||||
|
getIdSearchPane());
|
||||||
|
|
||||||
|
tabbedPane.addActionListener(this);
|
||||||
|
return tabbedPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Adds the specified component, with the specified tab name, to the
|
||||||
|
// * tabbed pane only if it is not null.
|
||||||
|
// *
|
||||||
|
// * @param pane The pane to which to add the tab
|
||||||
|
// * @param tabName The name of the tab if it's added
|
||||||
|
// * @param comp The component to add to the pane
|
||||||
|
// * @deprecated refactor to use addToPane(Label, Component) instead to
|
||||||
|
// * enable localized tab strips.
|
||||||
|
// */
|
||||||
|
// protected void addToPane(TabbedPane pane, String tabName, Component comp) {
|
||||||
|
// if (comp != null) {
|
||||||
|
// pane.addTab(tabName, comp);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* Adds the specified component, with the specified Label as tab name, to
|
||||||
|
* the tabbed pane only if it is not null.
|
||||||
|
*
|
||||||
|
* @param pane The pane to which to add the tab
|
||||||
|
* @param tabName The name of the tab if it's added
|
||||||
|
* @param comp The component to add to the pane
|
||||||
|
*/
|
||||||
|
protected void addToPane(TabbedPane pane, Label tabName, Component comp) {
|
||||||
|
if (comp != null) {
|
||||||
|
pane.addTab(tabName, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a new tab is selected, reset the state of the formerly-selected
|
||||||
|
* pane.
|
||||||
|
*
|
||||||
|
* @param event The event fired by selecting a tab
|
||||||
|
*/
|
||||||
|
public void actionPerformed(ActionEvent event) {
|
||||||
|
PageState state = event.getPageState();
|
||||||
|
Component pane = m_tabbedPane.getCurrentPane(state);
|
||||||
|
|
||||||
|
if (pane == m_tasks) {
|
||||||
|
m_tasks.reset(state);
|
||||||
|
} else if (pane == m_search) {
|
||||||
|
m_search.reset(state);
|
||||||
|
} else if (pane == m_IdSearch) {
|
||||||
|
m_IdSearch.reset(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,922 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.arsdigita.cms;
|
||||||
|
|
||||||
|
import com.arsdigita.cms.dispatcher.ItemResolver;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
|
import org.libreccm.configuration.Configuration;
|
||||||
|
import org.libreccm.configuration.ConfigurationManager;
|
||||||
|
import org.libreccm.configuration.Setting;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CMSConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path for the default item template. Path is relative to the Template Root
|
||||||
|
* path.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private String defaultItemTemplatePath = "/default/item.jsp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path for the default folder template. Path is relative to the Template
|
||||||
|
* Root path.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private String defaultFolderTemplatePath = "/default/folder.jsp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path or the root folder for template folders. Path is relative to webapp
|
||||||
|
* root. Modify with care! Usually modified by developers only!
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private String templateRootPath = "/templates/ccm-cms/content-section/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item Adapters File, path to an XML resource containing adapter
|
||||||
|
* specifications. Path is relative to webapp root.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private String itemAdapters = "/WEB-INF/resources/cms-item-adapters.xml";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use streamlined content creation: upon item creation, automatically open
|
||||||
|
* authoring steps and forward to the next step
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean useStreamlinedCreation = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHTML Editor Configuration for use in CMS module, lists the config object
|
||||||
|
* name and Javascript source location for its definition.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private List<String> dhtmlEditorConfig = Arrays.asList(new String[]{
|
||||||
|
"Xinha.Config", "/assets/xinha/CCMcmsCinhaConfig.js"});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines which plugins to use, e.g.TableOperations,CSS Format:
|
||||||
|
* [string,string,string]
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private List<String> dhtmlEditorPlugins = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent undesirable functions from being made available, eg images should
|
||||||
|
* only be added through the cms methods.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private List<String> dhtmlEditorHiddenButtons = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide section admin tabs from users without administrative rights.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideAdminTabs = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide Folder Index Checkbox from folder view
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideFolderIndexCheckbox = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide launch date parameter on all forms and displays where it's used.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideLaunchDate = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Require the launch date parameter to be set by the content author.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean requireLaunchDate = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the templates tab on the item admin page.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideTemplatesTab = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the upload file link in the editing of a text asset.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideTextAssetUploadFile = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide timezone labels (if, for example, all users will be in the same
|
||||||
|
* timezone and such information would be unnecessary)
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideTimezone = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Wysiwyg editor should clear the text of MSWord tags,
|
||||||
|
* everytime the user clicks on 'Save'
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean saveTextCleansWordTags = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the search indexing not to process FileAssets, eg to avoid PDF
|
||||||
|
* slowdowns
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean disableFileAssetExtraction = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an item's workflow should be deleted, once the item has been
|
||||||
|
* (re)published.
|
||||||
|
*
|
||||||
|
* jensp 2014-11-07: Default changed from true to false. Deleting the
|
||||||
|
* assigned workflow means that the authors have to reattach a workflow
|
||||||
|
* using the Workflow tab, which is complicated (for some users too
|
||||||
|
* complicated). Also deleting the workflow means that the new convenient
|
||||||
|
* link to restart a workflow will not work.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean deleteWorkflowAfterPublication = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the number of days ahead that are covered in the 'Soon Expired'
|
||||||
|
* tab
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int soonExpiredTimespanDays = 14;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the number of months ahead that are covered in the 'Soon Expired'
|
||||||
|
* tab
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int soonExpiredTimespanMonths = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does a redirect to the unpublished item generate not found error?
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean unpublishedNotFound = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links created through browse interfaces should only be within the same
|
||||||
|
* subsite
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean linksOnlyInSameSubsite = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link available to reset lifecycle on republish. If false don't display
|
||||||
|
* the link otherwise display.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean hideResetLifecycleLink = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to include INPATH operators to contains clause in intermedia
|
||||||
|
* search
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean scoreTitleAndKeywords = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title Weight, the relative weight given to title element within cms:item
|
||||||
|
* when ranking search results (only used by interMedia)
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int titleWeight = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyword Weight, the relative weight given to the dcKeywords element
|
||||||
|
* within dublinCore element within cms:item element when ranking search
|
||||||
|
* results (only used by interMedia)
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int keywordWeight = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the item search to current content section
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean limitItemSearchToContentSection = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asset steps to skip, specify asset steps that are not relevant for
|
||||||
|
* specific content types. Each entry in the list is a : separated pair. The
|
||||||
|
* first string is the className for the type (refer to classname column in
|
||||||
|
* contenttypes table eg com.arsdigita.cms.contenttypes.MultiPartArticle
|
||||||
|
* Second string is the name of the bebop step component eg
|
||||||
|
* com.arsdigita.cms.contenttypes.ui.ImageStep
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private List<String> skipAssetSteps = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory Descriptions Content types may refer to this to decide whether
|
||||||
|
* to validate against empty descriptions
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean mandatoryDescriptions = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Finished Lifecycles. Decide whether lifecycles and their phases
|
||||||
|
* should be deleted from the system when finished.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean deleteLifecycleWhenComplete = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete Sent Workflow Notifications. Decide whether successfully sent
|
||||||
|
* notifications and messages should be deleted from the system
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean deleteWorkflowNotificationWhenSend = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide whether successfully sent notifications and messages should be
|
||||||
|
* deleted from the system
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean deleteExpiryNotificationsWhenSent = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of time (in hours) before the expiration of a content item that
|
||||||
|
* users in the Alert Recipient role are alerted via email
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int defaultNotificationTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a content item's author should be notified by the item's
|
||||||
|
* LifecycleListener; defaults to true
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean notifyAuthorOnLifecycle = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML Mapping of the content center tabs to URLs, see
|
||||||
|
* {@link ContentCenterDispatcher}
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private String contentCenterMap
|
||||||
|
= "/WEB-INF/resources/content-center-map.xml";
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private List<String> defaultItemResolverClassNames = Arrays.asList(
|
||||||
|
new String[]{
|
||||||
|
ItemResolver.class.getName()
|
||||||
|
});
|
||||||
|
|
||||||
|
// @Setting
|
||||||
|
// private List<String> defaultTemplateResolverClassNames = Arrays.asList(
|
||||||
|
// new String[]{
|
||||||
|
// DefaultTemplateResolver.class.getName(),
|
||||||
|
// TemplateResolver.class.getName()
|
||||||
|
// });
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private String itemSearchDefaultTab = "flatBrowse";
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int itemSearchFlatBrowsePanePageSize = 20;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int folderBrowseListSize = 20;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int folderAtoZShowLimit = 100;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean useOldStyleItemLifecycleItemPane = false;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean threadPublishing = true;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private String publishingFailureSender = "";
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private String publishingFailureReceiver = "";
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageBrowserThumbnailMaxWidth = 50;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageBrowserThumbnailMaxHeight = 50;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageBrowserCaptionSize = 50;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageBrowserDescriptionSize = 400;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageBrowserTitleSize = 200;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean imageCacheEnabled = true;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean imageCachePrefetchEnabled = false;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageCacheMaxSize = 100;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int imageCacheMaxAge = 300;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean attachPersonOrgaUnitsStep = true;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int personOrgaUnitsStepSortKey = 20;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private boolean enableXmlCache = false;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int xmlCacheSize = 2500;
|
||||||
|
|
||||||
|
@Setting
|
||||||
|
private int xmlCacheAge = 60 * 60 * 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max length of the description of a link (in database max length are 4000
|
||||||
|
* characters)
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int linkDescMaxLength = 400;
|
||||||
|
|
||||||
|
public static CMSConfig getConfig() {
|
||||||
|
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(ConfigurationManager.class);
|
||||||
|
return confManager.findConfiguration(CMSConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMSConfig() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultItemTemplatePath() {
|
||||||
|
return defaultItemTemplatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultItemTemplatePath(final String defaultItemTemplatePath) {
|
||||||
|
this.defaultItemTemplatePath = defaultItemTemplatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultFolderTemplatePath() {
|
||||||
|
return defaultFolderTemplatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultFolderTemplatePath(
|
||||||
|
final String defaultFolderTemplatePath) {
|
||||||
|
this.defaultFolderTemplatePath = defaultFolderTemplatePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTemplateRootPath() {
|
||||||
|
return templateRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplateRootPath(final String templateRootPath) {
|
||||||
|
this.templateRootPath = templateRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getItemAdapters() {
|
||||||
|
return itemAdapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemAdapters(final String itemAdapters) {
|
||||||
|
this.itemAdapters = itemAdapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseStreamlinedCreation() {
|
||||||
|
return useStreamlinedCreation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseStreamlinedCreation(final boolean useStreamlinedCreation) {
|
||||||
|
this.useStreamlinedCreation = useStreamlinedCreation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDhtmlEditorConfig() {
|
||||||
|
return new ArrayList<>(dhtmlEditorConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDhtmlEditorConfig(final List<String> dhtmlEditorConfig) {
|
||||||
|
this.dhtmlEditorConfig = new ArrayList<>(dhtmlEditorConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDhtmlEditorPlugins() {
|
||||||
|
return new ArrayList<>(dhtmlEditorPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDhtmlEditorPlugins(final List<String> dhtmlEditorPlugins) {
|
||||||
|
this.dhtmlEditorPlugins = new ArrayList<>(dhtmlEditorPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDhtmlEditorHiddenButtons() {
|
||||||
|
return new ArrayList<>(dhtmlEditorHiddenButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDhtmlEditorHiddenButtons(
|
||||||
|
final List<String> dhtmlEditorHiddenButtons) {
|
||||||
|
this.dhtmlEditorHiddenButtons
|
||||||
|
= new ArrayList<>(dhtmlEditorHiddenButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideAdminTabs() {
|
||||||
|
return hideAdminTabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideAdminTabs(final boolean hideAdminTabs) {
|
||||||
|
this.hideAdminTabs = hideAdminTabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideFolderIndexCheckbox() {
|
||||||
|
return hideFolderIndexCheckbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideFolderIndexCheckbox(final boolean hideFolderIndexCheckbox) {
|
||||||
|
this.hideFolderIndexCheckbox = hideFolderIndexCheckbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideLaunchDate() {
|
||||||
|
return hideLaunchDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideLaunchDate(final boolean hideLaunchDate) {
|
||||||
|
this.hideLaunchDate = hideLaunchDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequireLaunchDate() {
|
||||||
|
return requireLaunchDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequireLaunchDate(final boolean requireLaunchDate) {
|
||||||
|
this.requireLaunchDate = requireLaunchDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideTemplatesTab() {
|
||||||
|
return hideTemplatesTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideTemplatesTab(final boolean hideTemplatesTab) {
|
||||||
|
this.hideTemplatesTab = hideTemplatesTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideTextAssetUploadFile() {
|
||||||
|
return hideTextAssetUploadFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideTextAssetUploadFile(final boolean hideTextAssetUploadFile) {
|
||||||
|
this.hideTextAssetUploadFile = hideTextAssetUploadFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideTimezone() {
|
||||||
|
return hideTimezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideTimezone(final boolean hideTimezone) {
|
||||||
|
this.hideTimezone = hideTimezone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSaveTextCleansWordTags() {
|
||||||
|
return saveTextCleansWordTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSaveTextCleansWordTags(final boolean saveTextCleansWordTags) {
|
||||||
|
this.saveTextCleansWordTags = saveTextCleansWordTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisableFileAssetExtraction() {
|
||||||
|
return disableFileAssetExtraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisableFileAssetExtraction(
|
||||||
|
final boolean disableFileAssetExtraction) {
|
||||||
|
this.disableFileAssetExtraction = disableFileAssetExtraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleteWorkflowAfterPublication() {
|
||||||
|
return deleteWorkflowAfterPublication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteWorkflowAfterPublication(
|
||||||
|
boolean deleteWorkflowAfterPublication) {
|
||||||
|
this.deleteWorkflowAfterPublication = deleteWorkflowAfterPublication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSoonExpiredTimespanDays() {
|
||||||
|
return soonExpiredTimespanDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSoonExpiredTimespanDays(final int soonExpiredTimespanDays) {
|
||||||
|
this.soonExpiredTimespanDays = soonExpiredTimespanDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSoonExpiredTimespanMonths() {
|
||||||
|
return soonExpiredTimespanMonths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSoonExpiredTimespanMonths(final int soonExpiredTimespanMonths) {
|
||||||
|
this.soonExpiredTimespanMonths = soonExpiredTimespanMonths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnpublishedNotFound() {
|
||||||
|
return unpublishedNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnpublishedNotFound(final boolean unpublishedNotFound) {
|
||||||
|
this.unpublishedNotFound = unpublishedNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLinksOnlyInSameSubsite() {
|
||||||
|
return linksOnlyInSameSubsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLinksOnlyInSameSubsite(final boolean linksOnlyInSameSubsite) {
|
||||||
|
this.linksOnlyInSameSubsite = linksOnlyInSameSubsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHideResetLifecycleLink() {
|
||||||
|
return hideResetLifecycleLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHideResetLifecycleLink(final boolean hideResetLifecycleLink) {
|
||||||
|
this.hideResetLifecycleLink = hideResetLifecycleLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isScoreTitleAndKeywords() {
|
||||||
|
return scoreTitleAndKeywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScoreTitleAndKeywords(final boolean scoreTitleAndKeywords) {
|
||||||
|
this.scoreTitleAndKeywords = scoreTitleAndKeywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleWeight() {
|
||||||
|
return titleWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleWeight(final int titleWeight) {
|
||||||
|
this.titleWeight = titleWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getKeywordWeight() {
|
||||||
|
return keywordWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeywordWeight(final int keywordWeight) {
|
||||||
|
this.keywordWeight = keywordWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLimitItemSearchToContentSection() {
|
||||||
|
return limitItemSearchToContentSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimitItemSearchToContentSection(
|
||||||
|
boolean limitItemSearchToContentSection) {
|
||||||
|
this.limitItemSearchToContentSection = limitItemSearchToContentSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSkipAssetSteps() {
|
||||||
|
return new ArrayList<>(skipAssetSteps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipAssetSteps(final List<String> skipAssetSteps) {
|
||||||
|
this.skipAssetSteps = new ArrayList<>(skipAssetSteps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMandatoryDescriptions() {
|
||||||
|
return mandatoryDescriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMandatoryDescriptions(final boolean mandatoryDescriptions) {
|
||||||
|
this.mandatoryDescriptions = mandatoryDescriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleteLifecycleWhenComplete() {
|
||||||
|
return deleteLifecycleWhenComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteLifecycleWhenComplete(
|
||||||
|
boolean deleteLifecycleWhenComplete) {
|
||||||
|
this.deleteLifecycleWhenComplete = deleteLifecycleWhenComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleteWorkflowNotificationWhenSend() {
|
||||||
|
return deleteWorkflowNotificationWhenSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteWorkflowNotificationWhenSend(
|
||||||
|
boolean deleteWorkflowNotificationWhenSend) {
|
||||||
|
this.deleteWorkflowNotificationWhenSend
|
||||||
|
= deleteWorkflowNotificationWhenSend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeleteExpiryNotificationsWhenSent() {
|
||||||
|
return deleteExpiryNotificationsWhenSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleteExpiryNotificationsWhenSent(
|
||||||
|
boolean deleteExpiryNotificationsWhenSent) {
|
||||||
|
this.deleteExpiryNotificationsWhenSent
|
||||||
|
= deleteExpiryNotificationsWhenSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultNotificationTime() {
|
||||||
|
return defaultNotificationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultNotificationTime(final int defaultNotificationTime) {
|
||||||
|
this.defaultNotificationTime = defaultNotificationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNotifyAuthorOnLifecycle() {
|
||||||
|
return notifyAuthorOnLifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotifyAuthorOnLifecycle(final boolean notifyAuthorOnLifecycle) {
|
||||||
|
this.notifyAuthorOnLifecycle = notifyAuthorOnLifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentCenterMap() {
|
||||||
|
return contentCenterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentCenterMap(final String contentCenterMap) {
|
||||||
|
this.contentCenterMap = contentCenterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getDefaultItemResolverClassNames() {
|
||||||
|
return new ArrayList<>(defaultItemResolverClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultItemResolverClassNames(
|
||||||
|
final List<String> defaultItemResolverClassNames) {
|
||||||
|
this.defaultItemResolverClassNames
|
||||||
|
= new ArrayList<>(defaultItemResolverClassNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<Class<ItemResolver>> getDefaultItemResolverClasses() {
|
||||||
|
final List<Class<ItemResolver>> resolverClasses = new ArrayList<>();
|
||||||
|
for (final String className : getDefaultItemResolverClassNames()) {
|
||||||
|
try {
|
||||||
|
resolverClasses.add((Class<ItemResolver>) Class.forName(
|
||||||
|
className));
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
throw new UncheckedWrapperException(String.format(
|
||||||
|
"ItemResolver class \"%s\" not found.", className), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolverClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public List<String> getDefaultTemplateResolverClassNames() {
|
||||||
|
// return new ArrayList<>(defaultTemplateResolverClassNames);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setDefaultTemplateResolverClassNames(
|
||||||
|
// List<String> defaultTemplateResolverClassNames) {
|
||||||
|
// this.defaultTemplateResolverClassNames = new ArrayList<>(
|
||||||
|
// defaultTemplateResolverClassNames);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// public List<Class<TemplateResolver>> getDefaultTemplateResolverClasses() {
|
||||||
|
// final List<Class<TemplateResolver>> resolverClasses = new ArrayList<>();
|
||||||
|
// for (final String className : getDefaultTemplateResolverClassNames()) {
|
||||||
|
// try {
|
||||||
|
// resolverClasses.add((Class<TemplateResolver>) Class.forName(
|
||||||
|
// className));
|
||||||
|
// } catch (ClassNotFoundException ex) {
|
||||||
|
// throw new UncheckedWrapperException(String.format(
|
||||||
|
// "ItemResolver class \"%s\" not found.", className), ex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return resolverClasses;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public String getItemSearchDefaultTab() {
|
||||||
|
return itemSearchDefaultTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemSearchDefaultTab(final String itemSearchDefaultTab) {
|
||||||
|
this.itemSearchDefaultTab = itemSearchDefaultTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemSearchFlatBrowsePanePageSize() {
|
||||||
|
return itemSearchFlatBrowsePanePageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemSearchFlatBrowsePanePageSize(
|
||||||
|
int itemSearchFlatBrowsePanePageSize) {
|
||||||
|
this.itemSearchFlatBrowsePanePageSize = itemSearchFlatBrowsePanePageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFolderBrowseListSize() {
|
||||||
|
return folderBrowseListSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolderBrowseListSize(final int folderBrowseListSize) {
|
||||||
|
this.folderBrowseListSize = folderBrowseListSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFolderAtoZShowLimit() {
|
||||||
|
return folderAtoZShowLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolderAtoZShowLimit(final int folderAtoZShowLimit) {
|
||||||
|
this.folderAtoZShowLimit = folderAtoZShowLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseOldStyleItemLifecycleItemPane() {
|
||||||
|
return useOldStyleItemLifecycleItemPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseOldStyleItemLifecycleItemPane(
|
||||||
|
boolean useOldStyleItemLifecycleItemPane) {
|
||||||
|
this.useOldStyleItemLifecycleItemPane = useOldStyleItemLifecycleItemPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isThreadPublishing() {
|
||||||
|
return threadPublishing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreadPublishing(final boolean threadPublishing) {
|
||||||
|
this.threadPublishing = threadPublishing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublishingFailureSender() {
|
||||||
|
return publishingFailureSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublishingFailureSender(final String publishingFailureSender) {
|
||||||
|
this.publishingFailureSender = publishingFailureSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublishingFailureReceiver() {
|
||||||
|
return publishingFailureReceiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublishingFailureReceiver(
|
||||||
|
final String publishingFailureReceiver) {
|
||||||
|
this.publishingFailureReceiver = publishingFailureReceiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageBrowserThumbnailMaxWidth() {
|
||||||
|
return imageBrowserThumbnailMaxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBrowserThumbnailMaxWidth(
|
||||||
|
int imageBrowserThumbnailMaxWidth) {
|
||||||
|
this.imageBrowserThumbnailMaxWidth = imageBrowserThumbnailMaxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageBrowserThumbnailMaxHeight() {
|
||||||
|
return imageBrowserThumbnailMaxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBrowserThumbnailMaxHeight(
|
||||||
|
int imageBrowserThumbnailMaxHeight) {
|
||||||
|
this.imageBrowserThumbnailMaxHeight = imageBrowserThumbnailMaxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageBrowserCaptionSize() {
|
||||||
|
return imageBrowserCaptionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBrowserCaptionSize(final int imageBrowserCaptionSize) {
|
||||||
|
this.imageBrowserCaptionSize = imageBrowserCaptionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageBrowserDescriptionSize() {
|
||||||
|
return imageBrowserDescriptionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBrowserDescriptionSize(
|
||||||
|
final int imageBrowserDescriptionSize) {
|
||||||
|
this.imageBrowserDescriptionSize = imageBrowserDescriptionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageBrowserTitleSize() {
|
||||||
|
return imageBrowserTitleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBrowserTitleSize(final int imageBrowserTitleSize) {
|
||||||
|
this.imageBrowserTitleSize = imageBrowserTitleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isImageCacheEnabled() {
|
||||||
|
return imageCacheEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageCacheEnabled(final boolean imageCacheEnabled) {
|
||||||
|
this.imageCacheEnabled = imageCacheEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isImageCachePrefetchEnabled() {
|
||||||
|
return imageCachePrefetchEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageCachePrefetchEnabled(
|
||||||
|
final boolean imageCachePrefetchEnabled) {
|
||||||
|
this.imageCachePrefetchEnabled = imageCachePrefetchEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageCacheMaxSize() {
|
||||||
|
return imageCacheMaxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageCacheMaxSize(final int imageCacheMaxSize) {
|
||||||
|
this.imageCacheMaxSize = imageCacheMaxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getImageCacheMaxAge() {
|
||||||
|
return imageCacheMaxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageCacheMaxAge(final int imageCacheMaxAge) {
|
||||||
|
this.imageCacheMaxAge = imageCacheMaxAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAttachPersonOrgaUnitsStep() {
|
||||||
|
return attachPersonOrgaUnitsStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachPersonOrgaUnitsStep(
|
||||||
|
final boolean attachPersonOrgaUnitsStep) {
|
||||||
|
this.attachPersonOrgaUnitsStep = attachPersonOrgaUnitsStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPersonOrgaUnitsStepSortKey() {
|
||||||
|
return personOrgaUnitsStepSortKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPersonOrgaUnitsStepSortKey(
|
||||||
|
final int personOrgaUnitsStepSortKey) {
|
||||||
|
this.personOrgaUnitsStepSortKey = personOrgaUnitsStepSortKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableXmlCache() {
|
||||||
|
return enableXmlCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableXmlCache(final boolean enableXmlCache) {
|
||||||
|
this.enableXmlCache = enableXmlCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXmlCacheSize() {
|
||||||
|
return xmlCacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setXmlCacheSize(final int xmlCacheSize) {
|
||||||
|
this.xmlCacheSize = xmlCacheSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXmlCacheAge() {
|
||||||
|
return xmlCacheAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setXmlCacheAge(final int xmlCacheAge) {
|
||||||
|
this.xmlCacheAge = xmlCacheAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLinkDescMaxLength() {
|
||||||
|
return linkDescMaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLinkDescMaxLength(final int linkDescMaxLength) {
|
||||||
|
this.linkDescMaxLength = linkDescMaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -30,15 +30,24 @@ public class CmsConstants {
|
||||||
|
|
||||||
public static final String CMS_BUNDLE = "org.librecms.CmsResources";
|
public static final String CMS_BUNDLE = "org.librecms.CmsResources";
|
||||||
|
|
||||||
public static final String CONTENT_SECTION_APP_TYPE = "org.librecms.contentsection.ContentSection";
|
public static final String CONTENT_CENTER_URL = "/content-center/";
|
||||||
public static final String CONTENT_SECTION_SERVLET_PATH = "/templates/servlet/content-section/*";
|
|
||||||
public static final String CONTENT_SECTION_DESC_BUNDLE = "org.librecms.contentsection.ContentSectionResources";
|
|
||||||
|
|
||||||
public static final String PRIVILEGE_ADMINISTER_CATEGORIES = "administer_categories";
|
public static final String CONTENT_SECTION_APP_TYPE
|
||||||
public static final String PRIVILEGE_ADMINISTER_CONTENT_TYPES = "administer_content_types";
|
= "org.librecms.contentsection.ContentSection";
|
||||||
public static final String PRIVILEGE_ADMINISTER_LIFECYLES = "administer_lifecyles";
|
public static final String CONTENT_SECTION_SERVLET_PATH
|
||||||
|
= "/templates/servlet/content-section/*";
|
||||||
|
public static final String CONTENT_SECTION_DESC_BUNDLE
|
||||||
|
= "org.librecms.contentsection.ContentSectionResources";
|
||||||
|
|
||||||
|
public static final String PRIVILEGE_ADMINISTER_CATEGORIES
|
||||||
|
= "administer_categories";
|
||||||
|
public static final String PRIVILEGE_ADMINISTER_CONTENT_TYPES
|
||||||
|
= "administer_content_types";
|
||||||
|
public static final String PRIVILEGE_ADMINISTER_LIFECYLES
|
||||||
|
= "administer_lifecyles";
|
||||||
public static final String PRIVILEGE_ADMINISTER_ROLES = "administer_roles";
|
public static final String PRIVILEGE_ADMINISTER_ROLES = "administer_roles";
|
||||||
public static final String PRIVILEGE_ADMINISTER_WORKFLOW = "administer_workflow";
|
public static final String PRIVILEGE_ADMINISTER_WORKFLOW
|
||||||
|
= "administer_workflow";
|
||||||
public static final String PRIVILEGE_ITEMS_APPROVE = "approve_items";
|
public static final String PRIVILEGE_ITEMS_APPROVE = "approve_items";
|
||||||
public static final String PRIVILEGE_ITEMS_PUBLISH = "publish_items";
|
public static final String PRIVILEGE_ITEMS_PUBLISH = "publish_items";
|
||||||
public static final String PRIVILEGE_ITEMS_CATEGORIZE = "categorize_items";
|
public static final String PRIVILEGE_ITEMS_CATEGORIZE = "categorize_items";
|
||||||
|
|
@ -46,8 +55,20 @@ public class CmsConstants {
|
||||||
public static final String PRIVILEGE_ITEMS_DELETE = "delete_items";
|
public static final String PRIVILEGE_ITEMS_DELETE = "delete_items";
|
||||||
public static final String PRIVILEGE_ITEMS_EDIT = "edit_items";
|
public static final String PRIVILEGE_ITEMS_EDIT = "edit_items";
|
||||||
public static final String PRIVILEGE_ITEMS_PREVIEW = "preview_items";
|
public static final String PRIVILEGE_ITEMS_PREVIEW = "preview_items";
|
||||||
public static final String PRIVILEGE_ITEMS_VIEW_PUBLISHED = "view_published_items";
|
public static final String PRIVILEGE_ITEMS_VIEW_PUBLISHED
|
||||||
public static final String PRIVILEGE_APPLY_ALTERNATE_WORKFLOW = "apply_alternate_workflow";
|
= "view_published_items";
|
||||||
|
public static final String PRIVILEGE_APPLY_ALTERNATE_WORKFLOW
|
||||||
|
= "apply_alternate_workflow";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant string used as key for creating service package as a legacy
|
||||||
|
* application.
|
||||||
|
*/
|
||||||
|
public static final String SERVICE_PACKAGE_KEY = "cms-service";
|
||||||
|
public static final String SERVICE_URL = "/cms-service/";
|
||||||
|
|
||||||
|
public static final String ASSET_ID = "asset_id";
|
||||||
|
public static final String IMAGE_ID = "image_id";
|
||||||
|
|
||||||
private CmsConstants() {
|
private CmsConstants() {
|
||||||
//Nothing
|
//Nothing
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.librecms;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation providing several informations about a content type. A content
|
||||||
|
* type is provided by a class extending the {@link ContentItem} class.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public @interface ContentType {
|
||||||
|
|
||||||
|
//ToDo
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.librecms;
|
||||||
|
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for modules to describe the content types provided by a module.
|
||||||
|
* The content types - classes which extend the {@link ContentItem} class - must
|
||||||
|
* be annotated with the {@link ContentType} annotation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public @interface ContentTypes {
|
||||||
|
|
||||||
|
Class<? extends ContentItem>[] value();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 LibreCCM Foundation.
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.librecms.contentsection;
|
||||||
|
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
public class ContentItemManager {
|
||||||
|
|
||||||
|
public void move(final ContentItem item, final Category targetFolder) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void copy(final ContentItem item, final Category targetFolder) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a live version of content item or updates the live version of a
|
||||||
|
* content item if there already a live version.
|
||||||
|
*
|
||||||
|
* @param item The content item to publish.
|
||||||
|
*
|
||||||
|
* @return The published content item.
|
||||||
|
*/
|
||||||
|
public ContentItem publish(final ContentItem item) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpublishes a content item by deleting its live version if any.
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
public void unpublish(final ContentItem item) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a content item has a live version.
|
||||||
|
*
|
||||||
|
* @param item The item
|
||||||
|
* @return {@code true} if the content item has a live version,
|
||||||
|
* {@code false} if not.
|
||||||
|
*/
|
||||||
|
public boolean isLive(final ContentItem item) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the live version of the provided content item if any.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @param item
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public <T extends ContentItem> Optional<T> getLiveVersion(
|
||||||
|
final ContentItem item,
|
||||||
|
final Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends ContentItem> T getDraftVersion(final ContentItem item) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -19,13 +19,14 @@
|
||||||
package org.librecms.contentsection;
|
package org.librecms.contentsection;
|
||||||
|
|
||||||
import org.libreccm.auditing.AbstractAuditedEntityRepository;
|
import org.libreccm.auditing.AbstractAuditedEntityRepository;
|
||||||
|
import org.libreccm.categorization.Category;
|
||||||
import org.libreccm.core.CcmObject;
|
import org.libreccm.core.CcmObject;
|
||||||
import org.libreccm.core.CcmObjectRepository;
|
import org.libreccm.core.CcmObjectRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.persistence.NoResultException;
|
|
||||||
import javax.persistence.TypedQuery;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -62,6 +63,11 @@ public class ContentItemRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T extends ContentItem> T findById(final long itemId,
|
||||||
|
final Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public ContentItem findByUuid(final String uuid) {
|
public ContentItem findByUuid(final String uuid) {
|
||||||
final CcmObject result = ccmObjectRepo.findObjectByUuid(uuid);
|
final CcmObject result = ccmObjectRepo.findObjectByUuid(uuid);
|
||||||
if (result instanceof ContentItem) {
|
if (result instanceof ContentItem) {
|
||||||
|
|
@ -71,6 +77,17 @@ public class ContentItemRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ToDo: Methods for finding items by name, path, content type etc.
|
public <T extends ContentItem> T findByUuid(final String uuid,
|
||||||
|
final Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends ContentItem> List<T> findByType(final Class<T> type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ContentItem> findByFolder(final Category folder) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,150 @@
|
||||||
*/
|
*/
|
||||||
package org.librecms.contentsection;
|
package org.librecms.contentsection;
|
||||||
|
|
||||||
|
import org.libreccm.cdi.utils.CdiUtil;
|
||||||
import org.libreccm.configuration.Configuration;
|
import org.libreccm.configuration.Configuration;
|
||||||
|
import org.libreccm.configuration.ConfigurationManager;
|
||||||
import org.libreccm.configuration.Setting;
|
import org.libreccm.configuration.Setting;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global settings for content sections. Some of these settings control
|
* Global settings for content sections. Some of these settings control the
|
||||||
* the initial values for new content sections.
|
* initial values for new content sections.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ContentSectionConfig {
|
public class ContentSectionConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of workflow tasks, and the associated events for which alerts have
|
||||||
|
* to be sent. Parameter name TASK_ALERTS in the old initializer system /
|
||||||
|
* enterprise.init Specifies when to generate email alerts: by default,
|
||||||
|
* generate email alerts on enable, finish, and rollback (happens on
|
||||||
|
* rejection) changes. There are four action types for each task type:
|
||||||
|
* enable, disable, finish, and rollback. Example: (Note that the values
|
||||||
|
* below are based on the task labels, and as such are not globalized.)
|
||||||
|
* <pre>
|
||||||
|
* taskAlerts = {
|
||||||
|
* { "Authoring",
|
||||||
|
* { "enable", "finish", "rollback" }
|
||||||
|
* },
|
||||||
|
* { "Approval",
|
||||||
|
* { "enable", "finish", "rollback" }
|
||||||
|
* },
|
||||||
|
* { "Deploy",
|
||||||
|
* { "enable", "finish", "rollback" }
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* In the new Initializer system we use a specifically formatted String
|
||||||
|
* Array because we have no List parameter. Format: - A string for each task
|
||||||
|
* to handle, possible values: Authoring, Approval, Deploy - Each Task
|
||||||
|
* String: [taskName]:[alert_1]:...:[alert_n] The specially formatted string
|
||||||
|
* is not handled by StringArray parameter, but forwarded untouched to the
|
||||||
|
* initializer which has the duty to process it!
|
||||||
|
*
|
||||||
|
* Currently there is no way to persist taskAlerts section specific. So all
|
||||||
|
* sections have to treated equally. Default values are provided here.
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private List<String> taskAlerts = Arrays.asList(new String[]{
|
||||||
|
"Authoring:enable:finish:rollback",
|
||||||
|
"Approval:enable:finish:rollback",
|
||||||
|
"Deploy:enable:finish:rollback"
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we send alerts about overdue tasks at all? Send alerts when a task
|
||||||
|
* is overdue (has remained in the \"enabled\" state for a long time)
|
||||||
|
* Parameter SEND_OVERDUE_ALERTS in the old initializer system, default
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private boolean sendOverdueAlerts = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time between when a task is enabled (i.e. it is made available for
|
||||||
|
* completion) and when it is considered overdue (in HOURS).
|
||||||
|
*/
|
||||||
|
// XXX Once the Duration of a Task can actually be maintained (in the UI,
|
||||||
|
// or initialization parameters), we should use the value in the DB, and
|
||||||
|
// get rid of this
|
||||||
|
// Parameter name TASK_DURATION in the old initializer system.
|
||||||
|
// Description: How long a task can remain \"enabled\" before it is
|
||||||
|
// considered overdue (in hours)
|
||||||
|
@Setting
|
||||||
|
private int taskDuration = 96;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time to wait between sending successive alerts on the same overdue
|
||||||
|
* task (in HOURS). Parameter name OVERDUE_ALERT_INTERVAL in old initializer
|
||||||
|
* system Description: Time to wait between sending overdue notifications on
|
||||||
|
* the same task (in hours)
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int alertInterval = 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of alerts to send about any one overdue task.
|
||||||
|
* Parameter name MAX_ALERTS in old initializer system. Description: The
|
||||||
|
* maximum number of alerts to send that a single task is overdue
|
||||||
|
*/
|
||||||
|
@Setting
|
||||||
|
private int maxAlerts = 5;
|
||||||
|
|
||||||
|
public static ContentSectionConfig getConfig() {
|
||||||
|
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
|
||||||
|
.findBean(ConfigurationManager.class);
|
||||||
|
return confManager.findConfiguration(ContentSectionConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentSectionConfig() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTaskAlerts() {
|
||||||
|
return new ArrayList<>(taskAlerts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskAlerts(final List<String> taskAlerts) {
|
||||||
|
this.taskAlerts = new ArrayList<>(taskAlerts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSendOverdueAlerts() {
|
||||||
|
return sendOverdueAlerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSendOverdueAlerts(final boolean sendOverdueAlerts) {
|
||||||
|
this.sendOverdueAlerts = sendOverdueAlerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTaskDuration() {
|
||||||
|
return taskDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskDuration(final int taskDuration) {
|
||||||
|
this.taskDuration = taskDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlertInterval() {
|
||||||
|
return alertInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlertInterval(final int alertInterval) {
|
||||||
|
this.alertInterval = alertInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxAlerts() {
|
||||||
|
return maxAlerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAlerts(final int maxAlerts) {
|
||||||
|
this.maxAlerts = maxAlerts;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,639 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import com.arsdigita.util.StringUtils;
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
import com.arsdigita.web.RedirectSignal;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.SAXParser;
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains functions common to all entry-point dispatcher servlets in the core.
|
||||||
|
*
|
||||||
|
* Any dispatcher that is the first in its chain to handle an HTTP request must
|
||||||
|
* also be a servlet and should extend this class.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* You do <em>not</em> need to extend this class unless your dispatcher is also
|
||||||
|
* a servlet and is mounted in web.xml. In any given ACS installation, you
|
||||||
|
* generally only have one servlet that is mounted through web.xml, and that is
|
||||||
|
* usually the <code>com.arsdigita.sitenode.SiteNodeDispatcher</code>, mapped to
|
||||||
|
* URL "/".
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When a request comes in:
|
||||||
|
*
|
||||||
|
* <ul> <li>first we try to serve a concrete file that matches the URL (for
|
||||||
|
* example, if the URL is /dir/image.gif, try to serve $docroot/dir/image.gif)
|
||||||
|
*
|
||||||
|
* <li>if the URL has no extension we assume it is a virtual directory. if there
|
||||||
|
* is no trailing slash, redirect to URL + "/".
|
||||||
|
*
|
||||||
|
* <li>if the URL has no extension and a trailing slash, it is treated as a
|
||||||
|
* directory. If the directory exists as a concrete directory on disk, and has a
|
||||||
|
* welcome file (index.*) then serve as a directory by forwarding to the
|
||||||
|
* "default" servlet.
|
||||||
|
*
|
||||||
|
* <li>if there is no concrete match for the URL on disk, we set up a
|
||||||
|
* RequestContext object that acts as a request wrapper storing metadata about
|
||||||
|
* the request; and call the <code>dispatch</code> method.
|
||||||
|
*
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
* $
|
||||||
|
*/
|
||||||
|
public abstract class BaseDispatcherServlet extends HttpServlet
|
||||||
|
implements Dispatcher, DispatcherConstants {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger(
|
||||||
|
BaseDispatcherServlet.class);
|
||||||
|
private final static int NOT_FOUND = 0;
|
||||||
|
private final static int STATIC_FILE = 1;
|
||||||
|
private final static int JSP_FILE = 2;
|
||||||
|
private final static String WEB_XML_22_PUBLIC_ID
|
||||||
|
= "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
|
||||||
|
private final static String WEB_XML_23_PUBLIC_ID
|
||||||
|
= "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
|
||||||
|
/**
|
||||||
|
* We use a Vector here instead of another collection because Vector is
|
||||||
|
* synchronised.
|
||||||
|
*/
|
||||||
|
private static Vector s_listenerList = new Vector();
|
||||||
|
/**
|
||||||
|
* list of active requests
|
||||||
|
*/
|
||||||
|
private static Vector s_activeList = new Vector();
|
||||||
|
|
||||||
|
static {
|
||||||
|
s_log.debug("Static initalizer starting...");
|
||||||
|
// Add the basic request listeners.
|
||||||
|
|
||||||
|
BaseDispatcherServlet.addRequestListener(new RequestListener() {
|
||||||
|
|
||||||
|
public void requestStarted(RequestEvent re) {
|
||||||
|
DispatcherHelper.setRequest(re.getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestFinished(RequestEvent re) {
|
||||||
|
// We could do this:
|
||||||
|
// DispatcherHelper.setRequest(null);
|
||||||
|
// but some later RequestListener might want to access
|
||||||
|
// the request or session. So we'll just let the
|
||||||
|
// DispatcherHelper hang on to one stale
|
||||||
|
// HttpServletRequest (per thread). The reference will
|
||||||
|
// be overwritten on the next request, so we keep only
|
||||||
|
// a small amount of garbage.
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
BaseDispatcherServlet.addRequestListener(new RequestListener() {
|
||||||
|
|
||||||
|
public void requestStarted(RequestEvent re) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestFinished(RequestEvent re) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
s_log.debug("Static initalizer finished.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List m_welcomeFiles = new ArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads web.xml to get the configured list of welcome files. We have to
|
||||||
|
* read web.xml ourselves because there is no public API to get this
|
||||||
|
* information from the ServletContext.
|
||||||
|
*/
|
||||||
|
public synchronized void init() throws ServletException {
|
||||||
|
super.init();
|
||||||
|
try {
|
||||||
|
File file = new File(getServletContext().getRealPath(
|
||||||
|
"/WEB-INF/web.xml"));
|
||||||
|
// all we care about is the welcome-file-list element
|
||||||
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||||
|
spf.setValidating(false);
|
||||||
|
SAXParser parser = spf.newSAXParser();
|
||||||
|
parser.parse(file, new WebXMLReader());
|
||||||
|
} catch (SAXException se) {
|
||||||
|
s_log.error("error in init", se);
|
||||||
|
} catch (ParserConfigurationException pce) {
|
||||||
|
s_log.error("error in init", pce);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
s_log.error("error in init", ioe);
|
||||||
|
}
|
||||||
|
// default to index.jsp, index.html
|
||||||
|
if (m_welcomeFiles.isEmpty()) {
|
||||||
|
m_welcomeFiles.add("index.jsp");
|
||||||
|
m_welcomeFiles.add("index.html");
|
||||||
|
}
|
||||||
|
getServletContext().setAttribute(WELCOME_FILES, m_welcomeFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a request listener to <code>this</code>.
|
||||||
|
*
|
||||||
|
* @param rl the <code>RequestListener</code> to add to the listener list
|
||||||
|
*/
|
||||||
|
public static void addRequestListener(RequestListener rl) {
|
||||||
|
s_listenerList.add(rl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A placeholder method for performing user authentication during request
|
||||||
|
* processing. Subclasses should override this method.
|
||||||
|
*
|
||||||
|
* @param req the current servlet request object
|
||||||
|
* @param req the current servlet response object
|
||||||
|
* @param req the current request context
|
||||||
|
*
|
||||||
|
* @return the updated request context (which may be the same as the context
|
||||||
|
* context parameter).
|
||||||
|
*
|
||||||
|
* @throws com.arsdigita.dispatcher.RedirectException if the dispatcher
|
||||||
|
* should redirect the
|
||||||
|
* client to the page
|
||||||
|
* contained in the
|
||||||
|
* exception
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected abstract RequestContext authenticateUser(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext ctx)
|
||||||
|
throws RedirectException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called directly by the servlet container when this servlet is invoked
|
||||||
|
* from a URL request. First tries to dispatch the URL to a concrete file on
|
||||||
|
* disk, if there is a matching file. Otherwise, sets up an initial
|
||||||
|
* RequestContext, tries to identify the user/session, parses form
|
||||||
|
* variables, and wraps the request object to handle multipart forms if
|
||||||
|
* necessary. Calls the <code>dispatch</code> method as declared in
|
||||||
|
* implementing subclasses.
|
||||||
|
*
|
||||||
|
* @param req the servlet request
|
||||||
|
* @param resp the servlet response
|
||||||
|
*
|
||||||
|
* @throws javax.servlet.ServletException re-thrown when
|
||||||
|
* <code>dispatch</code> throws an
|
||||||
|
* exception
|
||||||
|
* @throws java.io.IOException re-thrown when
|
||||||
|
* <code>dispatch</code> throws an
|
||||||
|
* IOException
|
||||||
|
*/
|
||||||
|
public void service(HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("\n*** *** *** *** *** ***\n"
|
||||||
|
+ "Servicing request for URL '" + req
|
||||||
|
.getRequestURI()
|
||||||
|
+ "'\n" + "*** *** *** *** *** ***");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean reentrant = true;
|
||||||
|
RequestContext reqCtx = DispatcherHelper.getRequestContext(req);
|
||||||
|
boolean finishedNormal = false;
|
||||||
|
|
||||||
|
// there are two types of re-entrancy we need to consider:
|
||||||
|
// * forwarded requests specified by the application,
|
||||||
|
// where the forwarded request is picked up by a subclass
|
||||||
|
// of BDS. (e.g., SND forwards /foo/bar to
|
||||||
|
// /packages/foo/www/bar.jsp)
|
||||||
|
// * a secondary request, forwarded by the CONTAINER in response
|
||||||
|
// to an exception thrown from service(), after the first request
|
||||||
|
// completes
|
||||||
|
//
|
||||||
|
// in the FIRST case, we need to guard against running
|
||||||
|
// the start/end listeners again. in the SECOND case,
|
||||||
|
// we need to treat this like a new request so that
|
||||||
|
// we open a transaction, etc. for serving the error page.
|
||||||
|
// wrap entire rest of method in try-catch block. that way if
|
||||||
|
// some method call throws ServletException or IOException,
|
||||||
|
// implicitly exiting the service method, we'll still be able
|
||||||
|
// to free up the database connection in a finally.
|
||||||
|
// STEP #1: if no extension, treat as directory;
|
||||||
|
// make sure we have a trailing slash. and redirect
|
||||||
|
//otherwise.
|
||||||
|
DispatcherHelper.setRequest(req);
|
||||||
|
|
||||||
|
if (trailingSlashRedirect(req, resp)) {
|
||||||
|
// note, this is OUTSIDE of try/catch/finally. No
|
||||||
|
// listeners of any kind are run!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP #2: try to serve concrete static file, if one exists.
|
||||||
|
// (defer serving concrete JSPs until after listeners run)
|
||||||
|
int concreteFileType = concreteFileType(req);
|
||||||
|
if (concreteFileType == STATIC_FILE) {
|
||||||
|
s_log.debug("Setting world cache headers on static file");
|
||||||
|
DispatcherHelper.cacheForWorld(resp);
|
||||||
|
DispatcherHelper.forwardRequestByName("default", req, resp,
|
||||||
|
getServletContext());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (req.getAttribute(REENTRANCE_ATTRIBUTE) == null) {
|
||||||
|
reentrant = false;
|
||||||
|
|
||||||
|
waitForPreviousRequestToFinish(req);
|
||||||
|
|
||||||
|
// need an identifier for this particular request
|
||||||
|
String requestId = Thread.currentThread().getName() + "|"
|
||||||
|
+ System.
|
||||||
|
currentTimeMillis();
|
||||||
|
req.setAttribute(REENTRANCE_ATTRIBUTE, requestId);
|
||||||
|
s_activeList.add(requestId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// first time through:
|
||||||
|
// do all actions that must be done initially on hit
|
||||||
|
StartRequestRecord srr = startRequest(req, resp);
|
||||||
|
reqCtx = srr.m_reqCtx;
|
||||||
|
req = srr.m_req;
|
||||||
|
s_log.debug("After startRequest the request is now " + req);
|
||||||
|
} catch (RedirectException re) {
|
||||||
|
final String url = re.getRedirectURL();
|
||||||
|
|
||||||
|
resp.sendRedirect(resp.encodeRedirectURL(url));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req = DispatcherHelper.maybeWrapRequest(req);
|
||||||
|
|
||||||
|
// if we're handling a secondary request for an
|
||||||
|
// error, but we haven't run the finally ... block
|
||||||
|
// on the primary request yet (this happens when
|
||||||
|
// sendError is called explicitly, as opposed to when
|
||||||
|
// the container calls sendError(500...) in response
|
||||||
|
// to an exception rethrown here) we DON'T run the
|
||||||
|
// request listeners. BUT we need to clear
|
||||||
|
// the request context.
|
||||||
|
if (req.getAttribute(ERROR_REQUEST_ATTRIBUTE) != null
|
||||||
|
|| req.getAttribute(JSP_EXCEPTION_ATTRIBUTE) != null) {
|
||||||
|
// reset URL boookeeping but don't wipe out
|
||||||
|
// whole object since it might actually be a
|
||||||
|
// KernelRequestContext with user / session info
|
||||||
|
if (reqCtx instanceof InitialRequestContext) {
|
||||||
|
((InitialRequestContext) reqCtx).
|
||||||
|
initializeURLFromRequest(req, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// finally, call dispatch
|
||||||
|
finishedNormal = false;
|
||||||
|
|
||||||
|
if (concreteFileType == JSP_FILE) {
|
||||||
|
// STEP #3: dispatch to a concrete JSP if we have a matching
|
||||||
|
// one
|
||||||
|
DispatcherHelper.forwardRequestByName("jsp", req, resp);
|
||||||
|
} else {
|
||||||
|
// STEP #4: if no concrete file exists, dispatch to
|
||||||
|
// implementing class
|
||||||
|
dispatch(req, resp, reqCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if JSP already dispatched to error page, no exception
|
||||||
|
// will be thrown. have to check for attribute manually.
|
||||||
|
if (req.getAttribute(JSP_EXCEPTION_ATTRIBUTE) == null) {
|
||||||
|
finishedNormal = true;
|
||||||
|
}
|
||||||
|
} catch (AbortRequestSignal ars) {
|
||||||
|
// treat this as a normal end of request and
|
||||||
|
// try to commit
|
||||||
|
finishedNormal = true;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
s_log.error("error in BaseDispatcherServlet", ioe);
|
||||||
|
throw ioe;
|
||||||
|
} catch (ServletException se) {
|
||||||
|
// SDM #140226, improved handling of
|
||||||
|
// ServletException.getRootCause()
|
||||||
|
Throwable t = se;
|
||||||
|
Throwable rootError;
|
||||||
|
do {
|
||||||
|
rootError = t;
|
||||||
|
t = ((ServletException) t).getRootCause();
|
||||||
|
} while (t instanceof ServletException);
|
||||||
|
if (t != null) {
|
||||||
|
rootError = t;
|
||||||
|
}
|
||||||
|
// handle this in case AbortRequestSignal got wrapped
|
||||||
|
// accidentally--e.g., inside a JSP.
|
||||||
|
if (rootError != null
|
||||||
|
&& (rootError instanceof AbortRequestSignal)) {
|
||||||
|
finishedNormal = true;
|
||||||
|
} else if (rootError != null
|
||||||
|
&& (rootError instanceof RedirectSignal)) {
|
||||||
|
s_log.debug("rethrowing RedirectSignal", rootError);
|
||||||
|
throw (RedirectSignal) rootError;
|
||||||
|
} else {
|
||||||
|
s_log.error("error in BaseDispatcherServlet", rootError);
|
||||||
|
throw new ServletException(rootError);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
s_log.error("error in BaseDispatcherServlet", re);
|
||||||
|
throw re;
|
||||||
|
} catch (Error error) {
|
||||||
|
s_log.error("error in BaseDispatcherServlet", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (!reentrant) {
|
||||||
|
// run the request listener events
|
||||||
|
fireFinishedListener(
|
||||||
|
new RequestEvent(req, resp, reqCtx, false,
|
||||||
|
finishedNormal));
|
||||||
|
// at this point, clear the attribute so
|
||||||
|
// a secondary request will work
|
||||||
|
// and remove the request from the list of currently-active
|
||||||
|
// requests
|
||||||
|
Object requestId = req.getAttribute(REENTRANCE_ATTRIBUTE);
|
||||||
|
synchronized (s_activeList) {
|
||||||
|
s_activeList.remove(requestId);
|
||||||
|
s_activeList.notifyAll();
|
||||||
|
}
|
||||||
|
req.removeAttribute(REENTRANCE_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a request when it is first handled by the servlet. This method
|
||||||
|
* runs exactly once for each request, even if the request is reentrant.
|
||||||
|
*
|
||||||
|
* @return a tuple containing the updated request context and the request
|
||||||
|
*
|
||||||
|
* @throws com.arsdigita.dispatcher.RedirectException if the dispatcher
|
||||||
|
* should redirect the
|
||||||
|
* client to the page
|
||||||
|
* contained in the
|
||||||
|
* exception
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private StartRequestRecord startRequest(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp)
|
||||||
|
throws RedirectException, IOException, ServletException {
|
||||||
|
|
||||||
|
// turn multipart request into wrapped request
|
||||||
|
// to make up for servlet 2.2 brokenness
|
||||||
|
req = DispatcherHelper.maybeWrapRequest(req);
|
||||||
|
|
||||||
|
RequestContext reqCtx = new InitialRequestContext(req,
|
||||||
|
getServletContext());
|
||||||
|
|
||||||
|
// run the request listener events
|
||||||
|
fireStartListener(new RequestEvent(req, resp, reqCtx, true));
|
||||||
|
|
||||||
|
// Authenticate user AFTER request listeners because authentication
|
||||||
|
// may need to use the database connection (opened by a listener).
|
||||||
|
// Allow subclass to update request context with user info.
|
||||||
|
reqCtx = authenticateUser(req, resp, reqCtx);
|
||||||
|
|
||||||
|
// save the request context in the request
|
||||||
|
DispatcherHelper.setRequestContext(req, reqCtx);
|
||||||
|
|
||||||
|
return new StartRequestRecord(reqCtx, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires all finished listeners. Collects and logs errors to ensure that all
|
||||||
|
* finished listeners run.
|
||||||
|
*
|
||||||
|
* @param evt the current RequestEvent to broadcast to all event listeners
|
||||||
|
*/
|
||||||
|
protected void fireFinishedListener(RequestEvent evt) {
|
||||||
|
for (int i = 0; i < s_listenerList.size(); i++) {
|
||||||
|
try {
|
||||||
|
((RequestListener) s_listenerList.get(i)).requestFinished(evt);
|
||||||
|
} catch (Exception e) {
|
||||||
|
s_log.error("Error running request finished listener "
|
||||||
|
+ s_listenerList.
|
||||||
|
get(i) + " (#" + i + ")", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires all start listeners. Does <b>not</b> collect and log errors.
|
||||||
|
* Instead, a runtime failure in a start listener will inhibit further
|
||||||
|
* servicing of the request.
|
||||||
|
*
|
||||||
|
* @param evt the current RequestEvent to broadcast to all event listeners
|
||||||
|
*/
|
||||||
|
protected void fireStartListener(RequestEvent evt) {
|
||||||
|
for (int i = 0; i < s_listenerList.size(); i++) {
|
||||||
|
((RequestListener) s_listenerList.get(i)).requestStarted(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kludge for returning a typed 2-tuple.
|
||||||
|
*/
|
||||||
|
private class StartRequestRecord {
|
||||||
|
|
||||||
|
RequestContext m_reqCtx;
|
||||||
|
HttpServletRequest m_req;
|
||||||
|
|
||||||
|
public StartRequestRecord(RequestContext rc, HttpServletRequest req) {
|
||||||
|
m_reqCtx = rc;
|
||||||
|
m_req = req;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForPreviousRequestToFinish(HttpServletRequest req) {
|
||||||
|
// handle concurrence -- serialize requests from the same
|
||||||
|
// user agent, so that you can't follow a link/redirect from
|
||||||
|
// a request until the request's transaction has committed
|
||||||
|
|
||||||
|
// get identifier from previous request, if there is any
|
||||||
|
HttpSession sess = req.getSession(false);
|
||||||
|
if (sess != null) {
|
||||||
|
Object sema = sess.getAttribute(REDIRECT_SEMAPHORE);
|
||||||
|
if (sema != null) {
|
||||||
|
while (s_activeList.indexOf(sema) != -1) {
|
||||||
|
try {
|
||||||
|
synchronized (s_activeList) {
|
||||||
|
s_activeList.wait();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sess.removeAttribute(REDIRECT_SEMAPHORE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper method: if the current request URL points to a concrete file under
|
||||||
|
* the webapp root, returns STATIC_FILE or JSP_FILE indicating the type of
|
||||||
|
* file. returns NOT_FOUND if no corresponding concrete file exists.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the concrete file is a directory, then we require that the directory
|
||||||
|
* have a welcome file like index.*; this prevents us from serving directory
|
||||||
|
* listings. For directories we return STATIC_FILE if there is a welcome
|
||||||
|
* file, otherwise return NOT_FOUND.
|
||||||
|
*
|
||||||
|
* @return STATIC_FILE if the current request points to a concrete static
|
||||||
|
* file (non-JSP) or a directory that has a welcome file. returns
|
||||||
|
* JSP_FILE if it corresponds to a dynamic JSP file. returns
|
||||||
|
* NOT_FOUND otherwise.
|
||||||
|
*/
|
||||||
|
private int concreteFileType(HttpServletRequest req)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String path = DispatcherHelper.getCurrentResourcePath(req);
|
||||||
|
|
||||||
|
ServletContext sctx = this.getServletContext();
|
||||||
|
File realFile = new File(sctx.getRealPath(path));
|
||||||
|
if (realFile.exists() && (!realFile.isDirectory() || hasWelcomeFile(
|
||||||
|
realFile))) {
|
||||||
|
// yup. Go there, bypass the site map.
|
||||||
|
// we have a concrete file so no forwarding to
|
||||||
|
// rewrite the request URL is necessary.
|
||||||
|
if (realFile.getName().endsWith(".jsp")) {
|
||||||
|
return JSP_FILE;
|
||||||
|
} else {
|
||||||
|
return STATIC_FILE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if dir is a directory and has a welcome file like index.*.
|
||||||
|
*
|
||||||
|
* @pre dir.isDirectory()
|
||||||
|
*/
|
||||||
|
private boolean hasWelcomeFile(File dir) {
|
||||||
|
if (!dir.isDirectory()) {
|
||||||
|
throw new IllegalArgumentException("dir must be a directory");
|
||||||
|
}
|
||||||
|
String[] files = dir.list();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (m_welcomeFiles.indexOf(files[i]) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean trailingSlashRedirect(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp)
|
||||||
|
throws IOException {
|
||||||
|
String path = DispatcherHelper.getCurrentResourcePath(req);
|
||||||
|
// first, see if we have an extension
|
||||||
|
if (path.lastIndexOf(".") <= path.lastIndexOf("/")) {
|
||||||
|
// maybe no extension. check if there's a trailing
|
||||||
|
// slash already.
|
||||||
|
if (!path.endsWith("/")) {
|
||||||
|
// no trailing slash
|
||||||
|
String targetURL = req.getContextPath() + path + "/";
|
||||||
|
String query = req.getQueryString();
|
||||||
|
if (query != null && query.length() > 0) {
|
||||||
|
targetURL += "?" + query;
|
||||||
|
}
|
||||||
|
resp.sendRedirect(resp.encodeRedirectURL(targetURL));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SAX content handler class to pick welcome-file-list out of web.xml
|
||||||
|
*/
|
||||||
|
private class WebXMLReader extends DefaultHandler {
|
||||||
|
|
||||||
|
StringBuffer m_buffer = new StringBuffer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputSource resolveEntity(String publicId, String systemId)
|
||||||
|
throws SAXException {
|
||||||
|
// we don't want to read the web.xml dtd
|
||||||
|
if (WEB_XML_22_PUBLIC_ID.equals(publicId)
|
||||||
|
|| WEB_XML_23_PUBLIC_ID.equals(publicId)) {
|
||||||
|
StringReader reader = new StringReader(" ");
|
||||||
|
return new InputSource(reader);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return super.resolveEntity(publicId, systemId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof SAXException) {
|
||||||
|
throw (SAXException) e;
|
||||||
|
} else {
|
||||||
|
throw new UncheckedWrapperException("Resolve Error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int len) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
m_buffer.append(ch[start + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri,
|
||||||
|
String localName,
|
||||||
|
String qname) {
|
||||||
|
if (qname.equals("welcome-file-list")) {
|
||||||
|
String[] welcomeFiles = StringUtils.split(m_buffer.toString(),
|
||||||
|
',');
|
||||||
|
for (int i = 0; i < welcomeFiles.length; i++) {
|
||||||
|
m_welcomeFiles.add(welcomeFiles[i].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_buffer = new StringBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines
|
||||||
|
* a dispatcher that is intended to be chained together with other dispatchers.
|
||||||
|
* The functional difference between ChainedDispatcher and Dispatcher is
|
||||||
|
* that a ChainedDispatcher's chainedDispatch() method can return a status
|
||||||
|
* code to indicate that it was successful or unsuccessful in finding
|
||||||
|
* a resource to dispatch to.
|
||||||
|
*
|
||||||
|
* <p>This interface is mainly used in conjunction with
|
||||||
|
* DispatcherChain, a general-purpose dispatcher that joins many
|
||||||
|
* different ChainedDispatchers together in a chain of different
|
||||||
|
* URL-to-resource mappings; if one cannot find a resource, the next
|
||||||
|
* one is tried. This is useful when an application has several
|
||||||
|
* different methods to find a resource for a URL and each method can
|
||||||
|
* be separated into a re-usable module.
|
||||||
|
*
|
||||||
|
* <p>For example, suppose an application resolves a URL to a resource
|
||||||
|
* like this:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>try to serve a file /templates/$site-node/$page.jsp
|
||||||
|
* <li>if not found, try to serve a Bebop Page mapped to $page
|
||||||
|
* <li>if not found, try to serve a file /packages/$key/www/$page.jsp
|
||||||
|
* <li>if not found, serve "not found" page
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* If we implement each stage as a separate ChainedDispatcher, then we
|
||||||
|
* can mix and match these dispatch stages in any number of
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface ChainedDispatcher {
|
||||||
|
|
||||||
|
public final static int DISPATCH_BREAK = 0;
|
||||||
|
public final static int DISPATCH_CONTINUE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch this request and return a status code if
|
||||||
|
* successful.
|
||||||
|
*
|
||||||
|
* @param request The servlet request object
|
||||||
|
* @param response the servlet response object
|
||||||
|
* @param actx The request context
|
||||||
|
* @return DISPATCH_BREAK if dispatch successful, DISPATCH_CONTINUE
|
||||||
|
* if no resource found (try next dispatcher in chain)
|
||||||
|
*/
|
||||||
|
public int chainedDispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a single dispatch
|
||||||
|
* method that allows programmers to pass extra context information
|
||||||
|
* around in a RequestContext object. Thus, when dispatcher A chains
|
||||||
|
* to dispatcher B, dispatcher B can determine what portion of the
|
||||||
|
* original request URL it needs to work with and what portion just
|
||||||
|
* made dispatcher A chain to dispatcher B. This context information
|
||||||
|
* allows a dispatcher to dynamically hand off a request to another
|
||||||
|
* dispatcher.
|
||||||
|
*
|
||||||
|
* A dispatcher is an <em>entry point</em> for a package. Each package
|
||||||
|
* defines a dispatcher, which brokers out requests within the package.
|
||||||
|
* The dispatcher for a package can dispatch requests to other packages.
|
||||||
|
* Multiple packages can share the same dispatcher <em>type</em> but
|
||||||
|
* not instance.<p>
|
||||||
|
*
|
||||||
|
* If a dispatcher object for a package also implements HttpServlet,
|
||||||
|
* then it is also an entry point for the entire web application.
|
||||||
|
*
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface Dispatcher {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches this request.
|
||||||
|
* @param request the servlet request object
|
||||||
|
* @param response the servlet response object
|
||||||
|
* @param actx the request context
|
||||||
|
* @exception java.io.IOException may be thrown by the dispatcher
|
||||||
|
* to indicate an I/O error
|
||||||
|
* @exception javax.servlet.ServletException may be thrown by the
|
||||||
|
* dispatcher to propagate a generic error to its caller
|
||||||
|
*/
|
||||||
|
public void dispatch(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic implementation
|
||||||
|
* of "try this URL-resource mapping; if nothing found try ..."
|
||||||
|
* pattern. This is useful for packages whose URL-to-resource mapping
|
||||||
|
* is a composition of many separate, reusable mappings. The goal is to
|
||||||
|
* reduce multi-branched, hard-coded if-else blocks.
|
||||||
|
*
|
||||||
|
* <p>This class makes it easier to break up dispatchers into a series
|
||||||
|
* of smaller, re-usable, but not totally self-contained classes whose
|
||||||
|
* dispatch method tries to find a resource according to its mapping,
|
||||||
|
* and serves it if it finds one. If it can't find a resource, it
|
||||||
|
* returns a failure
|
||||||
|
* status code (DISPATCHER_CONTINUE) and the DispatcherChain tries the
|
||||||
|
* next dispatcher in the sequence.
|
||||||
|
*
|
||||||
|
* <p>The dispatcher chain tries each dispatcher in the dispatcher
|
||||||
|
* chain successively in the order in which they were added to the chain.
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class DispatcherChain implements Dispatcher {
|
||||||
|
|
||||||
|
private LinkedList m_dispatcherChain = new LinkedList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches to the dispatcher chain. Tries each dispatcher
|
||||||
|
* in the sequence, in which they were added, and breaks out of
|
||||||
|
* the loop when either one dispatcher returns DISPATCH_BREAK or
|
||||||
|
* a dispatcher throws an exception.
|
||||||
|
*
|
||||||
|
* @param req the current servlet request
|
||||||
|
* @param resp the current servlet response object
|
||||||
|
* @param ctx the current <code>RequestContext</code> object
|
||||||
|
* @throws java.io.IOException re-thrown when a dispatcher in the
|
||||||
|
* chain throws an IOException.
|
||||||
|
* @throws javax.servlet.ServletException re-thrown when a dispatcher
|
||||||
|
* in the chain throws a ServletException.
|
||||||
|
*/
|
||||||
|
public void dispatch(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext ctx)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
Iterator iter = null;
|
||||||
|
synchronized(this) {
|
||||||
|
iter = m_dispatcherChain.iterator();
|
||||||
|
}
|
||||||
|
// already have a new iterator instance, so don't need
|
||||||
|
// to synchronize rest of proc.
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
ChainedDispatcher disp = (ChainedDispatcher)iter.next();
|
||||||
|
int status = disp.chainedDispatch(req, resp, ctx);
|
||||||
|
if (status == ChainedDispatcher.DISPATCH_BREAK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a dispatcher to the dispatcher chain. Dispatchers are
|
||||||
|
* executed in the order they are added to the chain, so this
|
||||||
|
* dispatcher will be executed after all the dispatchers that
|
||||||
|
* were previously added to the chain and before all the
|
||||||
|
* dispatchers that haven't yet been added to the chain.
|
||||||
|
* @param cd the dispatcher to add
|
||||||
|
*/
|
||||||
|
public synchronized void addChainedDispatcher(ChainedDispatcher cd) {
|
||||||
|
m_dispatcherChain.addLast(cd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@ import com.arsdigita.util.URLRewriter;
|
||||||
import com.arsdigita.web.ParameterMap;
|
import com.arsdigita.web.ParameterMap;
|
||||||
import com.arsdigita.web.RedirectSignal;
|
import com.arsdigita.web.RedirectSignal;
|
||||||
import com.arsdigita.web.URL;
|
import com.arsdigita.web.URL;
|
||||||
|
import com.arsdigita.web.Web;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
|
@ -50,6 +51,8 @@ import javax.servlet.jsp.PageContext;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class static helper methods for request dispatching.
|
* Class static helper methods for request dispatching.
|
||||||
* Contains various generally useful procedural abstractions.
|
* Contains various generally useful procedural abstractions.
|
||||||
|
|
@ -1155,4 +1158,42 @@ public final class DispatcherHelper implements DispatcherConstants {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the given request into a return URL parameter. Returns
|
||||||
|
* <code>URLencode(returnURL)</code> where returnURL is
|
||||||
|
* <code>returnURI?key=URLencode(val)&...</code>. The original parameter values are
|
||||||
|
* doubly-encoded so that they are decoded appropriately.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param req the request to encode
|
||||||
|
*
|
||||||
|
* @return the URL-encoded parameter
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static String encodeReturnURL(HttpServletRequest req) {
|
||||||
|
StringBuilder returnURL = new StringBuilder(100);
|
||||||
|
returnURL.append(Web.getWebContext().getRequestURL().getRequestURI());
|
||||||
|
returnURL.append('?');
|
||||||
|
|
||||||
|
// convert posted parameters to URL parameters
|
||||||
|
Enumeration params = req.getParameterNames();
|
||||||
|
boolean first = true;
|
||||||
|
while (params.hasMoreElements()) {
|
||||||
|
String key = (String) params.nextElement();
|
||||||
|
String[] vals = req.getParameterValues(key);
|
||||||
|
for (int i = 0; i < vals.length; i++) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
returnURL.append('&');
|
||||||
|
}
|
||||||
|
returnURL.append(key);
|
||||||
|
returnURL.append('=');
|
||||||
|
returnURL.append(URLEncoder.encode(vals[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return URLEncoder.encode(returnURL.toString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic dispatcher class for dispatching URLs to JSP or
|
||||||
|
* other file-based assets (images, CSS, HTML, etc.) in the file
|
||||||
|
* system in the context of a single package's URL space.
|
||||||
|
* <p>
|
||||||
|
* Given an application context that contains a SiteNode, and a
|
||||||
|
* remaining URL, tries to resolve the remaining URL into a concrete file:<br>
|
||||||
|
* $root/packages/$key/$remaining-url<br>
|
||||||
|
*
|
||||||
|
* where<br>
|
||||||
|
* $root is the ACS webapp root<br>
|
||||||
|
* $key is siteNode.getPackageKey()<br>
|
||||||
|
* $remaining-url is given by appContext.getRemainingURLPart()
|
||||||
|
*
|
||||||
|
* <p> To make the URL mapping in JSPApplicationDispatcher work, you
|
||||||
|
* must map the "jsp" servlet in web.xml to whatever your entry-point
|
||||||
|
* dispatcher is (whatever servlet is mapped to the URL pattern "/").
|
||||||
|
* Otherwise, requests for *.jsp will get picked up by the JSP
|
||||||
|
* container before any ACS dispatchers have a shot at URL mapping.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Example: if the application sample-app is mounted on the site nodes
|
||||||
|
* /sample1 and /sample2 then URLs will be dispatched as follows:
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Request URL <th>File served</tr>
|
||||||
|
* <tr><td> /sample1/page.jsp</td> <td> /packages/sample-app/www/page.jsp</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr><td> /sample2/image.gif</td> <td> /packages/sample-app/www/image.gif</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr><td> /sample2/script.jsp</td> <td> /packages/sample-app/www/script.jsp</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class JSPApplicationDispatcher extends BaseDispatcherServlet
|
||||||
|
implements Dispatcher {
|
||||||
|
|
||||||
|
private static final Logger s_log = Logger.getLogger
|
||||||
|
(JSPApplicationDispatcher.class);
|
||||||
|
|
||||||
|
private static JSPApplicationDispatcher s_instance = newInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of a JSPApplicationDispatcher.
|
||||||
|
* @return a new JSPApplicationDispatcher.
|
||||||
|
*/
|
||||||
|
public static JSPApplicationDispatcher newInstance() {
|
||||||
|
return new JSPApplicationDispatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of JSPApplicationDispatcher.
|
||||||
|
* @return a JSPApplicationDispatcher object.
|
||||||
|
* @deprecated No longer returns a singleton instance. Remains
|
||||||
|
* in place for API stability.
|
||||||
|
*/
|
||||||
|
public static JSPApplicationDispatcher getInstance() {
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No Authentication is performed here.
|
||||||
|
protected RequestContext authenticateUser(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext ctx)
|
||||||
|
throws RedirectException {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatch(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext actx)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
// Set the request context as a request attribute because a
|
||||||
|
// JSP page might need it, which doesn't get passed the
|
||||||
|
// request context as a parameter.
|
||||||
|
DispatcherHelper.setRequestContext(req, actx);
|
||||||
|
|
||||||
|
ServletContext sctx = actx.getServletContext();
|
||||||
|
String remainingURL = actx.getRemainingURLPart();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("I think the remaining URL is '" + remainingURL + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is where we forward a request from /foo1/bar.ext or
|
||||||
|
// /foo2/bar.ext to /packages/foo/www/bar.ext the concrete
|
||||||
|
// file should then get picked up by BaseDispatcherServlet.
|
||||||
|
|
||||||
|
String concreteURL =
|
||||||
|
actx.getPageBase() +
|
||||||
|
actx.getRemainingURLPart();
|
||||||
|
|
||||||
|
if (s_log.isDebugEnabled()) {
|
||||||
|
s_log.debug("Looking for a concrete resource under the web app " +
|
||||||
|
"context at '" + concreteURL + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
File concreteFile = new File(sctx.getRealPath(concreteURL));
|
||||||
|
|
||||||
|
if (concreteFile.exists()) {
|
||||||
|
s_log.debug("Resource was found; forwarding");
|
||||||
|
DispatcherHelper.setRequestContext(req, actx);
|
||||||
|
DispatcherHelper.forwardRequestByPath(concreteURL, req, resp);
|
||||||
|
} else {
|
||||||
|
s_log.debug("Resource not found");
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request event class. Passed as a
|
||||||
|
* parameter to the methods of RequestListener.
|
||||||
|
*
|
||||||
|
* @see RequestListener
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @version $Id$
|
||||||
|
* @since 4.5 */
|
||||||
|
public class RequestEvent {
|
||||||
|
|
||||||
|
|
||||||
|
private HttpServletRequest m_req;
|
||||||
|
private HttpServletResponse m_resp;
|
||||||
|
private RequestContext m_ctx;
|
||||||
|
private boolean m_start;
|
||||||
|
private boolean m_finished_normal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>RequestEvent</code> with a success/failure status
|
||||||
|
* code.
|
||||||
|
* @param req the current request
|
||||||
|
* @param resp the current response
|
||||||
|
* @param ctx the current request context
|
||||||
|
* @param start if true, indicates that this is a start-request event;
|
||||||
|
* if false, this is an end-request event.
|
||||||
|
* @param finishedNormal if true, indicates the request finished
|
||||||
|
* without error.
|
||||||
|
*/
|
||||||
|
public RequestEvent(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext ctx,
|
||||||
|
boolean start,
|
||||||
|
boolean finishedNormal) {
|
||||||
|
m_req = req;
|
||||||
|
m_resp = resp;
|
||||||
|
m_ctx = ctx;
|
||||||
|
m_start = start;
|
||||||
|
m_finished_normal = finishedNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new <code>RequestEvent</code> with no status code.
|
||||||
|
* @param req the current request
|
||||||
|
* @param resp the current response
|
||||||
|
* @param ctx the current request context
|
||||||
|
* @param start if true, indicates that this is a start-request event;
|
||||||
|
* if false, this is an end-request event.
|
||||||
|
*/
|
||||||
|
public RequestEvent(HttpServletRequest req,
|
||||||
|
HttpServletResponse resp,
|
||||||
|
RequestContext ctx,
|
||||||
|
boolean start) {
|
||||||
|
this(req, resp, ctx, start, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current request for the request event.
|
||||||
|
* @return the current request
|
||||||
|
*/
|
||||||
|
public HttpServletRequest getRequest() {
|
||||||
|
return m_req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current response for the request event.
|
||||||
|
* @return the current response
|
||||||
|
*/
|
||||||
|
public HttpServletResponse getResponse() {
|
||||||
|
return m_resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current request context for the request event.
|
||||||
|
* @return the current request context
|
||||||
|
*/
|
||||||
|
public RequestContext getRequestContext() {
|
||||||
|
return m_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event is a start-request event;
|
||||||
|
* false for an end-request event.
|
||||||
|
*
|
||||||
|
* @return true if we're starting a request, false at end.
|
||||||
|
*/
|
||||||
|
public boolean isStart() {
|
||||||
|
return m_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a status code to indicate whether the request
|
||||||
|
* finished without error.
|
||||||
|
*
|
||||||
|
* @return true if the request finished without exception.
|
||||||
|
* false if the request finished with an error, or if the request
|
||||||
|
* event is a start-request.
|
||||||
|
*/
|
||||||
|
public boolean finishedNormal() {
|
||||||
|
return m_finished_normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.dispatcher;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to register callbacks for the code to run
|
||||||
|
* when a request starts or ends.
|
||||||
|
*
|
||||||
|
* @author Bill Schneider
|
||||||
|
* @version $Id$
|
||||||
|
* @since 4.5 */
|
||||||
|
public interface RequestListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ACSServlet starts processing an incoming request.
|
||||||
|
* @param e the event
|
||||||
|
*/
|
||||||
|
public void requestStarted(RequestEvent e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when ACSServlet finishes processing an incoming request.
|
||||||
|
* @param e the event
|
||||||
|
*/
|
||||||
|
public void requestFinished(RequestEvent e);
|
||||||
|
}
|
||||||
|
|
@ -234,7 +234,9 @@ public final class WebConfig {
|
||||||
= (Class<ApplicationFileResolver>) Class
|
= (Class<ApplicationFileResolver>) Class
|
||||||
.forName(resolverClass);
|
.forName(resolverClass);
|
||||||
return clazz.newInstance();
|
return clazz.newInstance();
|
||||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
|
} catch (ClassNotFoundException |
|
||||||
|
InstantiationException |
|
||||||
|
IllegalAccessException ex) {
|
||||||
throw new UncheckedWrapperException(
|
throw new UncheckedWrapperException(
|
||||||
"Unable to retrieve ApplicationFileResolver", ex);
|
"Unable to retrieve ApplicationFileResolver", ex);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue