/* * 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.Page; import com.arsdigita.bebop.PageState; import com.arsdigita.cms.CMS; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.Folder; import com.arsdigita.cms.ui.ContentItemPage; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.web.URL; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.core.CcmObject; import org.librecms.CmsConstants; import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemVersion; import java.util.Optional; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; /** * This is the default implementation of * {@link com.arsdigita.cms.dispatcher.ItemResolver}. * * The {@link #getItem(java.lang.String, org.librecms.contentsection.Folder) } * method of the default implementation of {@link ItemResolver}, * {@link com.arsdigita.cms.dispatcher.SimpleItemResolver} runs a simple query * using the passed in information to retrieve the content item with a name that * matches the URL stub, in our example it looks for a content item with name * {@code cheese}. If no such item exists, or if there is such an item, but * without a live version, even though one has been requested, {@code getItem} * returns {@code null}. * * After the CMS Dispatcher received the content item from the * {@link ItemResolver}, it also asks it for the * {@link com.arsdigita.cms.dispatcher.MasterPage} for that item in the current * request. With the content item and the master page in hand, the dispatcher * calls {@code service} on the page. * * * AS of version 7.0.0 this class not longer part of the public API. It is left * here to keep the changes to the UI classes as minimal as possible. For new * code other methods, for example from the {@link ContentItemManager} or * the {@link ContentItemRepository} should be used. Because this class is no * longer part of the public API the will might be removed or changed in future * releases without prior warning. * * * @author Michael Pih (pihman@arsdigita.com) * @author Jens Pelzetter */ public class SimpleItemResolver extends AbstractItemResolver implements ItemResolver { private static final Logger LOGGER = LogManager.getLogger( SimpleItemResolver.class.getName()); private static final String ADMIN_PREFIX = "admin"; private static final String WORKSPACE_PREFIX = CmsConstants.CONTENT_CENTER_URL; private static MasterPage masterPage = null; public SimpleItemResolver() { } /** * Return a content item based on page state (and content section). * * @param section The current content section * @param url The section-relative URL * @param context The LIVE/DRAFT context (*not* the template use context) * * @return The content item mapped to the content section and URL, or null * if no such item exists */ @Override public ContentItem getItem(final ContentSection section, final String url, final String context) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("trying to get " + context + " item for url " + url); } final String itemUrl = stripTemplateFromURL(url); // getItem fails if called from a JSP template, because the request URL // gets replaced with the filesystem path to the JSP (when // DispatcherHelper.forwardRequestByPath is called). To fix this, we check // if the item had already been put into the request by the CMSDispatcher // (which it usually has) and return it. ContentItem reqItem = (ContentItem) DispatcherHelper.getRequest(). getAttribute("com.arsdigita.cms.dispatcher.item"); if (reqItem != null) { LOGGER.info("found item in the request, returning it"); return reqItem; } final Folder rootFolder = section.getRootDocumentsFolder(); if (rootFolder == null) { LOGGER.info("no root folder found, returning null"); return null; } return getItem(itemUrl, rootFolder); } /** * @param state the current page state * * @return the context of the current URL, such as "live" or "admin" */ @Override public String getCurrentContext(final PageState state) { 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.getPrimaryUrl(); 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 url = stripTemplateFromURL(url); // Determine if we are under the admin UI. if (url.startsWith(ADMIN_PREFIX) || url.startsWith(WORKSPACE_PREFIX)) { return ContentItemVersion.DRAFT.toString(); } else { return ContentItemVersion.LIVE.toString(); } } /** * Return the content item at the specified path, or null if no such item * exists. The path is interpreted as a series of folders; for example, * "/foo/bar/baz" will look for an item named "baz" in a folder named "bar" * in a folder named "foo" under the specified root folder. * * @param url the URL to the item * @param rootFolder The root folder where the item search will start * * @return the item on success, null if no such item exists */ public ContentItem getItem(final String url, final Folder rootFolder) { final String[] tokens = url.split("/"); Folder currentFolder = rootFolder; int i; for (i = 0; i < tokens.length; i++) { final String token = tokens[i]; final Optional folder = currentFolder.getSubFolders() .stream() .filter(subFolder -> subFolder.getDisplayName().equals(token)) .findFirst(); if (folder.isPresent()) { currentFolder = folder.get(); } else { break; } } if (i >= tokens.length - 1) { // failure LOGGER.debug("no more tokens found, returning null"); return null; } else { //Get the content item which is the last token final String name = tokens[tokens.length - 1]; final Optional item = currentFolder.getObjects() .stream() .map(categorization -> categorization.getCategorizedObject()) .filter(object -> object.getDisplayName().equals(name)) .findFirst(); if (item.isPresent() && item.get() instanceof ContentItem) { return (ContentItem) item.get(); } else { return null; } } } /** * Generate the URL for an item in the DRAFT context * * @param itemId * @param section * * @return */ private String generateDraftURL(final Long itemId, final ContentSection section) { return ContentItemPage.getItemURL( String.format("%s%s/", URL.getDispatcherPath(), section.getPrimaryUrl()), itemId, ContentItemPage.AUTHORING_TAB); } /** * Generate the URL for an item in the LIVE context with a given template * context * * @param item * @param section * @param templateContext * * @return * */ private String generateLiveURL(final ContentItem item, final ContentSection section, final String templateContext) { final String templateUrlFrag; if (templateContext == null || templateContext.isEmpty()) { templateUrlFrag = ""; } else { templateUrlFrag = String.format(TEMPLATE_CONTEXT_PREFIX + "%s/", templateContext); } final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final ContentItemManager itemManager = cdiUtil.findBean( ContentItemManager.class); return String.format("%s/%s%s", section.getPrimaryUrl(), templateUrlFrag, itemManager.getItemPath(item)); } /** * Generate the preview URL for an item in the DRAFT context. * * @param item * @param section * @param templateContext * * @return */ private String generatePreviewURL(final ContentItem item, final ContentSection section, final String templateContext) { final String templateUrlFrag; if (templateContext == null || templateContext.isEmpty()) { templateUrlFrag = ""; } else { templateUrlFrag = String.format(TEMPLATE_CONTEXT_PREFIX + "%s/", templateContext); } final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final ContentItemManager itemManager = cdiUtil.findBean( ContentItemManager.class); final StringBuilder url = new StringBuilder(); url .append(section.getPrimaryUrl()) .append("/") .append(CMSDispatcher.PREVIEW) .append("/") .append(templateUrlFrag) .append(itemManager.getItemPath(item)); return url.toString(); } /** * 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 "DRAFT" * * @return The URL of the item */ @Override public String generateItemURL(final PageState state, final Long 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 */ @Override public String generateItemURL(final PageState state, final Long itemId, final String name, final ContentSection section, final String context, final String templateContext) { final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final ContentItemRepository itemRepo = cdiUtil.findBean( ContentItemRepository.class); if (ContentItemVersion.DRAFT.toString().equals(context)) { return generateDraftURL(itemId, section); } else if (ContentItemVersion.LIVE.toString().equals(context)) { final ContentItem item = itemRepo.findById(itemId); return generateLiveURL(item, section, templateContext); } else if (CMSDispatcher.PREVIEW.equals(context)) { final ContentItem item = itemRepo.findById(itemId); return generatePreviewURL(item, section, templateContext); } else { throw new IllegalArgumentException(String.format( "Unknown context \"%s\".", 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 "DRAFT" * * @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, final ContentSection section, final String context, final String templateContext) { if (ContentItemVersion.LIVE.toString().equals(context)) { return generateLiveURL(item, section, templateContext); } else if (ContentItemVersion.DRAFT.toString().equals(context)) { return generateDraftURL(item.getObjectId(), section); } else if (CMSDispatcher.PREVIEW.equals(context)) { return generatePreviewURL(item, section, templateContext); } else { throw new IllegalArgumentException(String.format( "Unknown context \"%s\".", context)); } } /** * Return a master page based on page state (and content section). * * @param item The content item * @param request The HTTP request * * @throws javax.servlet.ServletException */ @Override public CMSPage getMasterPage(final ContentItem item, final HttpServletRequest request) throws ServletException { if (masterPage == null) { masterPage = new MasterPage(); masterPage.init(); } return masterPage; } }