From a38485014b770aa2f4c3ecb13a1564e64d37d93d Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 16 Dec 2016 16:41:16 +0000 Subject: [PATCH] CCM NG: - Some preparation for the editor UI for PageModel - SimpleItemResolver and MultilingualItemResolver ported to new API (still disabled because of circular dependencies in UI classes) git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4486 8810af33-2d31-482b-a856-94f89814c4df Former-commit-id: aa3f76e4a17417e9f9ff513097bdf3fe02e9dc94 --- .../cms/dispatcher/AbstractItemResolver.java | 93 +-- .../MultilingualItemResolver.java.todo | 534 +++++++----------- .../dispatcher/SimpleItemResolver.java.todo | 423 ++++++++------ .../ui/authoring/AuthoringKitWizard.java.todo | 6 +- .../cms/ui/authoring/WizardSelector.java.todo | 27 +- .../librecms/contentsection/ContentItem.java | 169 +++--- .../contentsection/ContentItemRepository.java | 145 +++-- .../contentsection/FolderRepository.java | 20 +- .../org/librecms/contentsection/PathUtil.java | 59 ++ .../pagemodel/ui/ApplicationTreeNode.java | 64 +++ .../pagemodel/ui/ApplicationTypeTreeNode.java | 53 ++ .../libreccm/pagemodel/ui/PageModelPane.java | 42 ++ .../pagemodel/ui/PageModelTreeController.java | 46 ++ .../ui/PageModelTreeModelBuilder.java | 47 ++ .../pagemodel/ui/PageModelTreeNode.java | 60 ++ .../libreccm/pagemodel/ui/PageTreeModel.java | 82 +++ 16 files changed, 1182 insertions(+), 688 deletions(-) create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/PathUtil.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTreeNode.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTypeTreeNode.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelPane.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeController.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeModelBuilder.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeNode.java create mode 100644 ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageTreeModel.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/AbstractItemResolver.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/AbstractItemResolver.java index 9285aaeb3..557b313f3 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/AbstractItemResolver.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/AbstractItemResolver.java @@ -26,7 +26,6 @@ import org.librecms.contentsection.ContentSection; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; -import java.math.BigDecimal; import java.util.StringTokenizer; /** @@ -36,82 +35,45 @@ 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) - */ + @Override public abstract ContentItem getItem( ContentSection section, String url, String context); - /* (non-Javadoc) - * @see com.arsdigita.cms.dispatcher.ItemResolver#getCurrentContext( - * com.arsdigita.bebop.PageState) - */ + @Override 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) - */ + @Override public abstract String generateItemURL( PageState state, - BigDecimal itemId, + Long 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); + @Override + public abstract String generateItemURL(PageState state, + Long 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); + @Override + 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); + @Override + 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) - */ + @Override public abstract CMSPage getMasterPage(ContentItem item, HttpServletRequest request) throws ServletException; @@ -124,7 +86,8 @@ public abstract class AbstractItemResolver implements ItemResolver { * * @return the template context, or null if there is no template context */ - public String getTemplateFromURL(String inUrl) { + @Override + public String getTemplateFromURL(final String inUrl) { String tempUrl; String url; if (inUrl.startsWith("/")) { @@ -155,13 +118,15 @@ public abstract class AbstractItemResolver implements ItemResolver { * * @return inUrl with the template context removed */ - public String stripTemplateFromURL(String inUrl) { + @Override + public String stripTemplateFromURL(final 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; + + sTemplateContext.length() + 1; String sStripped = inUrl.substring(iTemplateLength, inUrl.length()); return sStripped; } else { diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo index 54446684d..4458594b6 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo @@ -20,45 +20,64 @@ package com.arsdigita.cms.dispatcher; import com.arsdigita.bebop.PageState; import com.arsdigita.cms.CMS; -import com.arsdigita.cms.ContentCenter; +import com.arsdigita.cms.ui.ContentItemPage; +import com.arsdigita.kernel.KernelConfig; import com.arsdigita.util.Assert; import org.apache.logging.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.Objects; import java.util.StringTokenizer; + import org.apache.logging.log4j.LogManager; import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.l10n.GlobalizationHelper; import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemVersion; import org.librecms.contentsection.Folder; import org.librecms.contentsection.FolderManager; +import org.librecms.util.LanguageUtil; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +import static javax.naming.ldap.SortControl.*; /** * Resolves items to URLs and URLs to items for multiple language variants. * - * Created Mon Jan 20 14:30:03 2003. + * * + * 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 Hanisch - * @version $Id: MultilingualItemResolver.java 2090 2010-04-17 08:04:14Z pboy $ + * @author Jens Pelzetter */ public class MultilingualItemResolver - extends AbstractItemResolver implements ItemResolver { + extends AbstractItemResolver implements ItemResolver { private static final Logger LOGGER = LogManager.getLogger( - MultilingualItemResolver.class); + MultilingualItemResolver.class); private static MasterPage s_masterP = null; private static final String ADMIN_PREFIX = "admin"; @@ -83,8 +102,10 @@ public class MultilingualItemResolver * @param section The current content section * @param itemUrl The section-relative URL * @param context The use context, e.g. ContentItem.LIVE, - * CMSDispatcher.PREVIEW or ContentItem.DRAFT. See {@link + * CMSDispatcher.PREVIEW or + * ContentItem.DRAFT. See {@link * #getCurrentContext}. + * * @return The content item, or null if no such item exists */ @Override @@ -92,7 +113,7 @@ public class MultilingualItemResolver final String itemUrl, final String context) { LOGGER.debug("Resolving the item in content section \"{}\" at URL " - + "\"{}\" for context \"{}\"...", + + "\"{}\" for context \"{}\"...", section.getLabel(), itemUrl, context); @@ -119,16 +140,16 @@ public class MultilingualItemResolver LOGGER.debug("The root folder has a live version; recursing"); final FolderManager folderManager = cdiUtil.findBean( - FolderManager.class); + FolderManager.class); final String prefix = String.join( - "", - section.getPrimaryUrl(), - folderManager.getFolderPath(rootFolder)); + "", + section.getPrimaryUrl(), + folderManager.getFolderPath(rootFolder)); if (url.startsWith(prefix)) { LOGGER. - debug("The starts with prefix \"{}\"; removing it...", - prefix); + debug("The starts with prefix \"{}\"; removing it...", + prefix); url = url.substring(prefix.length()); } @@ -171,8 +192,8 @@ public class MultilingualItemResolver if (url.startsWith(prefix)) { LOGGER.debug( - "The URL starts with prefix \"{}\"; removing it", - prefix); + "The URL starts with prefix \"{}\"; removing it", + prefix); url = url.substring(prefix.length()); } @@ -186,7 +207,7 @@ public class MultilingualItemResolver return item; } else { throw new IllegalArgumentException(String.format( - "Invalid item resolver context \"%s\".", context)); + "Invalid item resolver context \"%s\".", context)); } } @@ -199,11 +220,13 @@ public class MultilingualItemResolver * Fetches the current context based on the page state. * * @param state the current page state + * * @return the context of the current URL, such as - * ContentItem.LIVE or ContentItem.DRAFT + * ContentItem.LIVE or ContentItem.DRAFT + * * @see ContentItem#LIVE * @see ContentItem#DRAFT - * + * * ToDo: Refactor to use the {@link ContentItemVersion} directly. */ @Override @@ -232,8 +255,8 @@ public class MultilingualItemResolver url = stripTemplateFromURL(url); // Determine if we are under the admin UI. - if (url.startsWith(ADMIN_PREFIX) - || url.startsWith(CmsConstants.CONTENT_CENTER_URL)) { + if (url.startsWith(ADMIN_PREFIX) + || url.startsWith(CmsConstants.CONTENT_CENTER_URL)) { return ContentItemVersion.DRAFT.toString(); } else { return ContentItemVersion.LIVE.toString(); @@ -243,17 +266,19 @@ public class MultilingualItemResolver /** * 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 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 Long itemId, final String name, final ContentSection section, final String context) { @@ -263,27 +288,29 @@ public class MultilingualItemResolver /** * 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 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 Long itemId, final String name, final ContentSection section, final String context, final String templateContext) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Generating an item URL for item id " + itemId - + ", section " + section + ", and context '" - + context - + "' with name '" + name + "'"); + + ", section " + section + ", and context '" + + context + + "' with name '" + name + "'"); } Assert.exists(itemId, "BigDecimal itemId"); @@ -291,40 +318,35 @@ public class MultilingualItemResolver Assert.exists(section, "ContentSection section"); final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); - final ContentItemRepository itemRepo = cdiUtil.findBean(ContentItemRepository.class); - + final ContentItemRepository itemRepo = cdiUtil.findBean( + ContentItemRepository.class); + if (ContentItemVersion.DRAFT.toString().equals(context)) { // No template context here. return generateDraftURL(section, itemId); } else if (CMSDispatcher.PREVIEW.equals(context)) { - ContentItem item = new ContentItem(itemId); - + final ContentItem item = itemRepo.findById(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"); - } + } else if (ContentItemVersion.LIVE.toString().equals(context)) { + final ContentItem item = itemRepo.findById(itemId); 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 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 @@ -338,68 +360,61 @@ public class MultilingualItemResolver /** * 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 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 ContentSection section, final String context, final String templateContext) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Generating an item URL for item " + item - + ", section " - + section + ", and context " + context); + + ", section " + + section + ", and context " + context); } - Assert.exists(item, "ContentItem item"); - Assert.exists(context, "String context"); - + final ContentSection contentSection; if (section == null) { - section = item.getContentSection(); + contentSection = item.getContentType().getContentSection(); + } else { + contentSection = section; } - 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()); + if (ContentItemVersion.DRAFT.toString().equals(context)) { + return generateDraftURL(section, item.getObjectId()); } 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 if (ContentItemVersion.LIVE.toString().equals(context)) { + return generateLiveURL(contentSection, item, templateContext); } else { - throw new RuntimeException("Unknown context " + context); + throw new IllegalArgumentException(String.format( + "Unknown context \"%s\".", context)); } } /** * Returns a master page based on page state (and content section). * - * @param item The content item + * @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 { + throws ServletException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Getting the master page for item " + item); } @@ -421,25 +436,27 @@ public class MultilingualItemResolver * Returns content item's draft version URL * * @param section The content section to which the item belongs - * @param itemId The content item's ID + * @param itemId The content item's ID + * * @return generated URL string */ protected String generateDraftURL(final ContentSection section, - final BigDecimal itemId) { + final Long itemId) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Generating draft URL for item ID " + itemId - + " and section " + section); + + " and section " + section); } if (Assert.isEnabled()) { Assert.isTrue(section != null && itemId != null, "get draft url: neither secion nor item " - + "can be null"); + + "can be null"); } - final String url = ContentItemPage.getItemURL(section.getPath() + "/", - itemId, - ContentItemPage.AUTHORING_TAB); + final String url = ContentItemPage.getItemURL( + String.format("%s/", section.getPrimaryUrl()), + itemId, + ContentItemPage.AUTHORING_TAB); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Generated draft URL " + url); @@ -455,23 +472,23 @@ public class MultilingualItemResolver * specific language instance of the item referenced here, i.e. this URL * will be resolved to a language-specific URL internally. * - * @param section the ContentSection that contains this item - * @param item ContentItem for which a URL should be - * constructed. + * @param section the ContentSection that contains this + * item + * @param item ContentItem for which a URL should be + * constructed. * @param templateContext template context; will be ignored if - * null + * null * * @return a language-independent URL to the item in - * the given section, which will be presented within the given - * templateContext + * the given section, which will be presented within + * the given templateContext */ protected String generateLiveURL(final ContentSection section, final ContentItem item, final String templateContext) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Generating live URL for item " + item + " in " - + "section " + section); - } + LOGGER.debug("Generating live URL for item {} in section {}", + Objects.toString(item), + Objects.toString(section)); /* * URL = URL of section + templateContext + path to the ContentBundle @@ -479,7 +496,9 @@ public class MultilingualItemResolver */ final StringBuffer url = new StringBuffer(400); //url.append(section.getURL()); - url.append(section.getPath()).append("/"); + url + .append(section.getPrimaryUrl()) + .append("/"); /* * add template context, if one is given @@ -487,56 +506,17 @@ public class MultilingualItemResolver // 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( - "/"); + url + .append(TEMPLATE_CONTEXT_PREFIX) + .append(templateContext) + .append("/"); } - // Try to retrieve the bundle. - final ContentItem bundle = (ContentItem) item.getParent(); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + url.append(itemManager.getItemPath(item)); - /* - * 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) { - LOGGER.debug("Found a bundle; building its file name"); - - final String fname = bundle.getPath(); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Appending the bundle's file name '" + fname + "'"); - } - - url.append(fname); - - } else { - LOGGER.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(); } @@ -547,71 +527,44 @@ public class MultilingualItemResolver * no language negotiation is involved when a request is made to a * URL that has been generated by this method. * - * @param section The ContentSection which contains the - * item - * @param item The ContentItem for which a URL should be - * generated. + * @param section The ContentSection which contains the + * item + * @param item The ContentItem 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 null + * render the item when it is previewed; ignored if + * the argument given here is null + * * @return a URL which can be used to preview the given item */ - protected String generatePreviewURL(ContentSection section, - ContentItem item, - String templateContext) { + protected String generatePreviewURL(final ContentSection section, + final ContentItem item, + final 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("/"); + url + .append(section.getPrimaryUrl()) + .append("/") + .append(CMSDispatcher.PREVIEW) + .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( - "/"); + 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) { - LOGGER.debug("Found a bundle; using its path"); - - url.append(bundle.getPath()); - } else { - LOGGER.debug("No bundle found; using the item's path directly"); - - url.append(item.getPath()); - } - - final String language = item.getLanguage(); - - if (language == null) { - LOGGER.debug("The item has no language; omitting the " - + "language encoding"); - } else { - LOGGER.debug("Encoding the language of the item passed in, '" - + language + "'"); - - url.append(".").append(language); - } - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Generated preview URL " + url.toString()); - } + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + url.append(itemManager.getItemPath(item)); return url.toString(); } @@ -621,9 +574,11 @@ public class MultilingualItemResolver * according to this ID. * * @param url URL that indicates which item should be retrieved; must - * contain the ITEM_ID parameter + * contain the ITEM_ID parameter + * * @return the ContentItem the given url points - * to, or null if no ID has been found in the url + * to, or null if no ID has been found in the + * url */ protected ContentItem getItemFromDraftURL(final String url) { if (LOGGER.isDebugEnabled()) { @@ -661,41 +616,45 @@ public class MultilingualItemResolver LOGGER.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); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemRepository itemRepo = cdiUtil.findBean( + ContentItemRepository.class); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Returning item " + item); + final Optional item = itemRepo.findById(Long.parseLong( + item_id)); + + if (item.isPresent()) { + LOGGER.debug("Returning item {}", Objects.toString(item)); + return item.get(); + } else { + return null; } - - return item; } /** * Returns a content item based on URL relative to the root folder. * - * @param url The content item url + * @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 + * objects, depending on URL and file language extension */ - protected ContentItem getItemFromLiveURL(String url, - Folder parentFolder) { + protected ContentItem getItemFromLiveURL(final String url, + final Folder parentFolder) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Resolving the item for live URL " + url - + " and parent folder " + parentFolder); + + " and parent folder " + parentFolder); } if (parentFolder == null || url == null || url.equals("")) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("The url is null or parent folder was null " - + "or something else is wrong, so just return " - + "the parent folder"); + + "or something else is wrong, so just return " + + "null"); } - return parentFolder; + return null; } int len = url.length(); @@ -703,28 +662,28 @@ public class MultilingualItemResolver if (index >= 0) { LOGGER.debug("The URL starts with a slash; paring off the first " - + "URL element and recursing"); + + "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) : ""; + final String liveUrl = index + 1 < len ? url.substring(index + 1) + : ""; - return getItemFromLiveURL(url, parentFolder); + return getItemFromLiveURL(liveUrl, parentFolder); } else { LOGGER.debug("Found a file element in the URL"); - String[] nameAndLang = getNameAndLangFromURLFrag(url); - String name = nameAndLang[0]; - String lang = nameAndLang[1]; + final String[] nameAndLang = getNameAndLangFromURLFrag(url); + final String name = nameAndLang[0]; - ContentItem item = parentFolder.getItem(URLEncoder.encode(name), - false); - return getItemFromLangAndBundle(lang, item); + final Optional item = parentFolder.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; + } } } @@ -732,11 +691,13 @@ public class MultilingualItemResolver * Returns an array containing the the item's name and lang based on the URL * fragment. * + * @param url + * * @return a two-element string array, the first element containing the - * bundle name, and the second element containing the lang string + * bundle name, and the second element containing the lang string */ - protected String[] getNameAndLangFromURLFrag(String url) { - String name = null; + protected String[] getNameAndLangFromURLFrag(final String url) { + String name; String lang = null; /* @@ -749,7 +710,7 @@ public class MultilingualItemResolver * from a bundle * 2b if no match is found */ - final ArrayList exts = new ArrayList(5); + final List exts = new ArrayList<>(5); final StringTokenizer tok = new StringTokenizer(url, "."); while (tok.hasMoreTokens()) { @@ -757,10 +718,9 @@ public class MultilingualItemResolver } if (exts.size() > 0) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Found some file extensions to look at; they " - + "are " + exts); - } + LOGGER.debug("Found some file extensions to look at; they " + + "are {}", + exts); /* * We have found at least one extension, so we can @@ -772,18 +732,16 @@ public class MultilingualItemResolver /* * 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; + name = exts.get(0); + String ext; + final Collection supportedLangs = KernelConfig.getConfig() + .getSupportedLanguages(); + Iterator supportedLangIt; for (int i = 1; i < exts.size(); i++) { - ext = (String) exts.get(i); + ext = exts.get(i); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Examining extension " + ext); - } + LOGGER.debug("Examining extension {}", ext); /* * Loop through all extensions, but discard the @@ -801,37 +759,26 @@ public class MultilingualItemResolver while (supportedLangIt.hasNext()) { if (ext.equals(supportedLangIt.next())) { lang = ext; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Found a match; using " - + "language " + lang); - } + LOGGER.debug("Found a match; using " + + "language {}", lang); break; } } } else { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Discarding extension " + ext + "; " - + "it is too short"); - } + LOGGER.debug("Discarding extension {}; it is too short", + ext); } } } else { - LOGGER.debug("The file has no extensions; no language was " - + "encoded"); + LOGGER.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); - } + LOGGER.debug("File name resolved to {}", name); + LOGGER.debug("File language resolved to {}", lang); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("File name resolved to " + name); - LOGGER.debug("File language resolved to " + lang); - } - String[] returnArray = new String[2]; + final String[] returnArray = new String[2]; returnArray[0] = name; returnArray[1] = lang; return returnArray; @@ -839,62 +786,17 @@ public class MultilingualItemResolver /** * Finds a language instance of a content item given the bundle, name, and - * lang string + * lang string. Note: Because there not ContentBundles anymore this method + * simply returns the provided item. * * @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 (LOGGER.isDebugEnabled()) { - LOGGER.debug("Found content bundle " + item); - } - if (lang == null) { - LOGGER.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 { - LOGGER.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 (LOGGER.isDebugEnabled()) { - LOGGER.debug("Resolved URL to item " + resolved); - } - return resolved; - } - } else { - if (LOGGER.isDebugEnabled()) { - LOGGER. - 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 - } + protected ContentItem getItemFromLangAndBundle(final String lang, + final ContentItem item) { + return item; } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.todo index d9d855e5b..2ce01c379 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.todo @@ -18,56 +18,78 @@ */ 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.ContentCenter; + import com.arsdigita.cms.ui.ContentItemPage; import com.arsdigita.dispatcher.DispatcherHelper; -import org.apache.log4j.Logger; +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; -import java.math.BigDecimal; -import java.util.StringTokenizer; /** - *

This is the default implementation of - * {@link com.arsdigita.cms.dispatcher.ItemResolver}.

+ * This is the default implementation of + * {@link com.arsdigita.cms.dispatcher.ItemResolver}. * - *

The getItem method of the default implementation of - * 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 cheese. If no such item - * exists, or if there is such an item, but without a live version, even - * though one has been requested, getItem returns null.

+ * 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 - * 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 service on the page.

+ * 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) - * @version $Revision: #15 $ $DateTime: 2004/08/17 23:15:09 $ - * @version $Id: SimpleItemResolver.java 2090 2010-04-17 08:04:14Z pboy $ + * @author Jens Pelzetter */ -public class SimpleItemResolver extends AbstractItemResolver implements ItemResolver { +public class SimpleItemResolver + extends AbstractItemResolver + implements ItemResolver { - private static final Logger s_log = - Logger.getLogger(SimpleItemResolver.class.getName()); + private static final Logger LOGGER = LogManager.getLogger( + SimpleItemResolver.class.getName()); private static final String ADMIN_PREFIX = "admin"; - private static final String WORKSPACE_PREFIX = ContentCenter.getURL(); + private static final String WORKSPACE_PREFIX + = CmsConstants.CONTENT_CENTER_URL; - private static MasterPage s_masterP = null; + private static MasterPage masterPage = null; - public SimpleItemResolver() {} + public SimpleItemResolver() { + } /** * Return a content item based on page state (and content section). @@ -75,17 +97,20 @@ public class SimpleItemResolver extends AbstractItemResolver implements ItemReso * @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 + * + * @return The content item mapped to the content section and URL, or null + * if no such item exists */ - public ContentItem getItem(ContentSection section, String url, - String context) { + @Override + public ContentItem getItem(final ContentSection section, + final String url, + final String context) { - if (s_log.isDebugEnabled()) { - s_log.debug("trying to get " + context + " item for url " + url); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("trying to get " + context + " item for url " + url); } - String itemUrl = stripTemplateFromURL(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 @@ -95,58 +120,36 @@ public class SimpleItemResolver extends AbstractItemResolver implements ItemReso ContentItem reqItem = (ContentItem) DispatcherHelper.getRequest(). getAttribute("com.arsdigita.cms.dispatcher.item"); if (reqItem != null) { - s_log.info("found item in the request, returning it"); + LOGGER.info("found item in the request, returning it"); return reqItem; } - //ContentItem item = getCachedItem(section, itemUrl, context); - //if (item != null) { - // return item; - //} - - Folder rootFolder = section.getRootFolder(); - if ( rootFolder == null ) { - s_log.info("no root folder found, returning null"); + final Folder rootFolder = section.getRootDocumentsFolder(); + if (rootFolder == null) { + LOGGER.info("no root folder found, returning null"); return null; } - if ( context.equals(ContentItem.LIVE) ) { - rootFolder = (Folder) rootFolder.getLiveVersion(); - if ( rootFolder == null ) { - s_log.info("no LIVE version of root folder found, returning null"); - return null; - } - } else if ( context.equals(ContentItem.DRAFT) ) { - // Do nothing ? - } else { - throw new RuntimeException( - "getItem: Invalid item resolver context " + context); - } - - ContentItem item = getItem(itemUrl, rootFolder); - //if (item != null) { - // cacheItem(section, itemUrl, context, item); - //} - - return item; - } + return getItem(itemUrl, rootFolder); + } /** * @param state the current page state + * * @return the context of the current URL, such as "live" or "admin" */ - public String getCurrentContext(PageState state) { + @Override + public String getCurrentContext(final PageState state) { String url = state.getRequest().getRequestURI(); - ContentSection section = - CMS.getContext().getContentSection(); + 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 ) { - String sectionURL = section.getURL(); - if ( url.startsWith(sectionURL) ) { + if (section != null) { + final String sectionURL = section.getPrimaryUrl(); + if (url.startsWith(sectionURL)) { url = url.substring(sectionURL.length()); } } @@ -156,176 +159,256 @@ public class SimpleItemResolver extends AbstractItemResolver implements ItemReso url = stripTemplateFromURL(url); // Determine if we are under the admin UI. - if ( url.startsWith(ADMIN_PREFIX) || - url.startsWith(WORKSPACE_PREFIX) ) { - return ContentItem.DRAFT; + if (url.startsWith(ADMIN_PREFIX) || url.startsWith(WORKSPACE_PREFIX)) { + return ContentItemVersion.DRAFT.toString(); } else { - return ContentItem.LIVE; + 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. + * 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 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 - * @pre rootFolder != null - * @pre url != null */ - public ContentItem getItem(String url, Folder rootFolder) { + public ContentItem getItem(final String url, final Folder rootFolder) { - StringTokenizer tokenizer = new StringTokenizer(url, "/"); - String name = null; - Folder oldFolder = null; + final String[] tokens = url.split("/"); - while(rootFolder != null && tokenizer.hasMoreTokens()) { - name = tokenizer.nextToken(); - oldFolder = rootFolder; - rootFolder = (Folder)rootFolder.getItem(name, true); + 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(tokenizer.hasMoreTokens()) { + if (i >= tokens.length - 1) { // failure - s_log.debug("no more tokens found, returning null"); + LOGGER.debug("no more tokens found, returning null"); return null; } else { - // Get the content item which is the last token - if (rootFolder != null ) { - ContentItem indexItem = rootFolder.getIndexItem(); - if (indexItem != null) { - s_log.info("returning index item for folder"); - return indexItem; - } - } - if ( name == null ) { - s_log.debug("no name found"); + //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; } - return oldFolder.getItem(name, false); } } - // Generate the URL for an item in the DRAFT context - private String generateDraftURL(BigDecimal itemId, ContentSection section) { - return ContentItemPage.getItemURL(section.getFullPath() + "/", itemId, - ContentItemPage.AUTHORING_TAB); + /** + * 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) { - // Generate the URL for an item in the LIVE context with a given template context - private String generateLiveURL(ContentItem item, ContentSection section, - String templateContext) { - String templateURLFrag = - (templateContext == null || templateContext.length() == 0) ? - "" : TEMPLATE_CONTEXT_PREFIX + templateContext + "/"; - return section.getPath() + "/" + templateURLFrag + item.getPath(); + 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); - // Generate the preview URL for an item in the DRAFT context - private String generatePreviewURL(ContentItem item, ContentSection section, - String templateContext) { - String templateURLFrag = - (templateContext == null || templateContext.length() == 0) ? - "" : TEMPLATE_CONTEXT_PREFIX + templateContext + "/"; - StringBuffer url = new StringBuffer(); + final StringBuilder url = new StringBuilder(); url - .append(section.getPath()) - .append("/" + CMSDispatcher.PREVIEW) + .append(section.getPrimaryUrl()) .append("/") - .append(templateURLFrag) - .append(item.getPath()); + .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 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 - * @pre context != null */ - public String generateItemURL(PageState state, BigDecimal itemId, - String name, ContentSection section, - String context) { + @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 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 - * @pre context != null */ - public String generateItemURL(PageState state, BigDecimal itemId, - String name, ContentSection section, - String context, String templateContext) { - if ( ContentItem.DRAFT.equals(context) ) { + @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 (ContentItem.LIVE.equals(context)) { - ContentItem item = new ContentItem(itemId); + } else if (ContentItemVersion.LIVE.toString().equals(context)) { + final ContentItem item = itemRepo.findById(itemId); return generateLiveURL(item, section, templateContext); } else if (CMSDispatcher.PREVIEW.equals(context)) { - ContentItem item = new ContentItem(itemId); + final ContentItem item = itemRepo.findById(itemId); return generatePreviewURL(item, section, templateContext); } else { - throw new RuntimeException( (String) GlobalizationUtil.globalize("cms.dispatcher.unknown_context").localize() + context); + 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 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 */ - public String generateItemURL ( - PageState state, ContentItem item, ContentSection section, String context - ) { + @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 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 - ) { - if (ContentItem.LIVE.equals(context)) { + @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 (ContentItem.DRAFT.equals(context)) { - return generateDraftURL(item.getID(), section); + } 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 RuntimeException( (String) GlobalizationUtil.globalize("cms.dispatcher.unknown_context").localize() + context); + throw new IllegalArgumentException(String.format( + "Unknown context \"%s\".", context)); } } @@ -334,16 +417,20 @@ public class SimpleItemResolver extends AbstractItemResolver implements ItemReso * * @param item The content item * @param request The HTTP request + * + * @throws javax.servlet.ServletException */ - public CMSPage getMasterPage(ContentItem item, HttpServletRequest request) + @Override + public CMSPage getMasterPage(final ContentItem item, + final HttpServletRequest request) throws ServletException { - if ( s_masterP == null ) { - s_masterP = new MasterPage(); - s_masterP.init(); + if (masterPage == null) { + masterPage = new MasterPage(); + masterPage.init(); } - return s_masterP; + return masterPage; } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo index 8f51a7557..7379c930c 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo @@ -36,7 +36,9 @@ import com.arsdigita.bebop.event.FormProcessListener; import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.Label; import com.arsdigita.bebop.list.ListCellRenderer; + import org.librecms.contentsection.ContentType; + import com.arsdigita.cms.ItemSelectionModel; import com.arsdigita.cms.ui.ContentItemPage; import com.arsdigita.cms.ui.item.ItemWorkflowRequestLocal; @@ -46,13 +48,13 @@ import com.arsdigita.cms.ui.workflow.TaskFinishForm; import com.arsdigita.cms.ui.workflow.TaskRequestLocal; import com.arsdigita.cms.ui.workflow.WorkflowRequestLocal; import com.arsdigita.globalization.GlobalizedMessage; -import com.arsdigita.kernel.KernelConfig; import com.arsdigita.toolbox.ui.LayoutPanel; import com.arsdigita.toolbox.ui.ModalPanel; import com.arsdigita.toolbox.ui.Section; import com.arsdigita.util.Assert; import com.arsdigita.util.SequentialMap; import com.arsdigita.util.UncheckedWrapperException; + import org.apache.logging.log4j.Logger; import java.lang.reflect.Constructor; @@ -60,9 +62,9 @@ import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.ArrayList; import java.util.Collections; -import java.util.Locale; import java.util.Objects; import java.util.ResourceBundle; + import org.apache.logging.log4j.LogManager; import org.arsdigita.cms.CMSConfig; import org.libreccm.cdi.utils.CdiUtil; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.todo index 93be56991..71afe564a 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.todo @@ -21,26 +21,20 @@ package com.arsdigita.cms.ui.authoring; import com.arsdigita.bebop.Component; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.Resettable; -import com.arsdigita.bebop.SimpleContainer; import com.arsdigita.bebop.SingleSelectionModel; -import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.bebop.parameters.LongParameter; -import com.arsdigita.bebop.parameters.StringParameter; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentType; import com.arsdigita.cms.ItemSelectionModel; -import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.kernel.ui.ACSObjectSelectionModel; import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.xml.Element; -import oracle.jrockit.jfr.events.ContentTypeImpl; -import org.librecms.contenttypes.AuthoringKit; import org.librecms.contenttypes.AuthoringKitInfo; import org.librecms.contenttypes.ContentTypeInfo; -import java.math.BigDecimal; /** * An invisible component which contains all the possible authoring kits. The @@ -55,7 +49,7 @@ import java.math.BigDecimal; * @author unknown */ public class WizardSelector extends AuthoringKitSelector - implements Resettable { + implements Resettable { private final ItemSelectionModel itemSelectionModel; @@ -63,11 +57,11 @@ public class WizardSelector extends AuthoringKitSelector * Construct a new WizardSelector. Load all the possible authoring kits from * the database and construct wizards for them. * - * @param model the {@link ItemSelectionModel} which will supply the wizard - * with its item + * @param model the {@link ItemSelectionModel} which will supply the + * wizard with its item * * @param typeModel the {@link ACSObjectSelectionModel} which will supply - * the default content type + * the default content type * * @pre itemModel != null */ @@ -83,6 +77,7 @@ public class WizardSelector extends AuthoringKitSelector * * @param kit * @param type + * * @return */ @Override @@ -90,9 +85,10 @@ public class WizardSelector extends AuthoringKitSelector final ContentTypeInfo type) { final ItemSelectionModel itemModel = new ItemSelectionModel( - type, (LongParameter) itemSelectionModel.getStateParameter()); + type, (LongParameter) itemSelectionModel.getStateParameter()); - final AuthoringKitWizard wizard = new AuthoringKitWizard(type, itemModel); + final AuthoringKitWizard wizard + = new AuthoringKitWizard(type, itemModel); return wizard; } @@ -111,8 +107,7 @@ public class WizardSelector extends AuthoringKitSelector throw new UncheckedWrapperException("No item selected."); } - final ContentItem item = (ContentItem) itemSelectionModel - .getSelectedObject(state); + final ContentItem item = itemSelectionModel.getSelectedObject(state); final ContentType type = item.getContentType(); final String typeClass; @@ -128,7 +123,7 @@ public class WizardSelector extends AuthoringKitSelector } // Return the selected wizard - return (Component) getComponent(typeClass); + return getComponent(typeClass); } // Choose the right wizard and run it diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java index 8a1a02513..c668920cf 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java @@ -65,76 +65,83 @@ import static org.librecms.CmsConstants.*; @Table(name = "CONTENT_ITEMS", schema = DB_SCHEMA) @NamedQueries({ @NamedQuery( - name = "ContentItem.findByType", - query = "SELECT i FROM ContentItem i WHERE TYPE(i) = :type") + name = "ContentItem.findByType", + query = "SELECT i FROM ContentItem i WHERE TYPE(i) = :type") , @NamedQuery( - name = "ContentItem.findByFolder", - query = "SELECT i FROM ContentItem i " - + "JOIN i.categories c " - + "WHERE c.category = :folder " - + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + name = "ContentItem.findByFolder", + query = "SELECT i FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "'") , @NamedQuery( - name = "ContentItem.countItemsInFolder", - query = "SELECT count(i) FROM ContentItem i " - + "JOIN i.categories c " - + "WHERE c.category = :folder " - + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + name = "ContentItem.countItemsInFolder", + query = "SELECT count(i) FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "'") , @NamedQuery( - name = "ContentItem.countByNameInFolder", - query = "SELECT COUNT(i) FROM ContentItem i " - + "JOIN i.categories c " - + "WHERE c.category = :folder " - + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER - + "' " - + "AND i.displayName = :name") + name = "ContentItem.findByNameInFolder", + query = "SELECT i FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' " + + "AND i.displayName = :name") , @NamedQuery( - name = "ContentItem.filterByFolderAndName", - query = "SELECT i FROM ContentItem i " - + "JOIN i.categories c " - + "WHERE c.category = :folder " - + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER - + "' " - + "AND LOWER(i.displayName) LIKE CONCAT(LOWER(:name), '%')") + name = "ContentItem.countByNameInFolder", + query = "SELECT COUNT(i) FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' " + + "AND i.displayName = :name") , @NamedQuery( - name = "ContentItem.countFilterByFolderAndName", - query = "SELECT COUNT(i) FROM ContentItem i " - + "JOIN i.categories c " - + "WHERE c.category = :folder " - + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + name = "ContentItem.filterByFolderAndName", + query = "SELECT i FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' " - + "AND LOWER(i.displayName) LIKE CONCAT(LOWER(:name), '%')" + + "AND LOWER(i.displayName) LIKE CONCAT(LOWER(:name), '%')") + , + @NamedQuery( + name = "ContentItem.countFilterByFolderAndName", + query = "SELECT COUNT(i) FROM ContentItem i " + + "JOIN i.categories c " + + "WHERE c.category = :folder " + + "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + + "' " + + "AND LOWER(i.displayName) LIKE CONCAT(LOWER(:name), '%')" ) , @NamedQuery( - name = "ContentItem.hasLiveVersion", - query = "SELECT (CASE WHEN COUNT(i) > 0 THEN true ELSE false END) " - + "FROM ContentItem i " - + "WHERE i.itemUuid = :uuid " - + "AND i.version = org.librecms.contentsection.ContentItemVersion.LIVE") + name = "ContentItem.hasLiveVersion", + query = "SELECT (CASE WHEN COUNT(i) > 0 THEN true ELSE false END) " + + "FROM ContentItem i " + + "WHERE i.itemUuid = :uuid " + + "AND i.version = org.librecms.contentsection.ContentItemVersion.LIVE") , @NamedQuery( - name = "ContentItem.findDraftVersion", - query = "SELECT i FROM ContentItem i " - + "WHERE i.itemUuid = :uuid " - + "AND i.version = org.librecms.contentsection.ContentItemVersion.DRAFT") + name = "ContentItem.findDraftVersion", + query = "SELECT i FROM ContentItem i " + + "WHERE i.itemUuid = :uuid " + + "AND i.version = org.librecms.contentsection.ContentItemVersion.DRAFT") , @NamedQuery( - name = "ContentItem.findLiveVersion", - query = "SELECT i FROM ContentItem i " - + "WHERE i.itemUuid = :uuid " - + "AND i.version = org.librecms.contentsection.ContentItemVersion.LIVE") + name = "ContentItem.findLiveVersion", + query = "SELECT i FROM ContentItem i " + + "WHERE i.itemUuid = :uuid " + + "AND i.version = org.librecms.contentsection.ContentItemVersion.LIVE") , @NamedQuery( - name = "ContentItem.findItemWithWorkflow", - query = "SELECT i FROM ContentItem i " - + "WHERE i.workflow = :workflow" + name = "ContentItem.findItemWithWorkflow", + query = "SELECT i FROM ContentItem i " + + "WHERE i.workflow = :workflow" ) }) public class ContentItem extends CcmObject implements Serializable, @@ -153,13 +160,13 @@ public class ContentItem extends CcmObject implements Serializable, */ @Embedded @AssociationOverride( - name = "values", - joinTable = @JoinTable(name = "CONTENT_ITEM_NAMES", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "OBJECT_ID") - } - ) + name = "values", + joinTable = @JoinTable(name = "CONTENT_ITEM_NAMES", + schema = DB_SCHEMA, + joinColumns = { + @JoinColumn(name = "OBJECT_ID") + } + ) ) private LocalizedString name; @@ -176,13 +183,13 @@ public class ContentItem extends CcmObject implements Serializable, */ @Embedded @AssociationOverride( - name = "values", - joinTable = @JoinTable(name = "CONTENT_ITEM_TITLES", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "OBJECT_ID") - } - ) + name = "values", + joinTable = @JoinTable(name = "CONTENT_ITEM_TITLES", + schema = DB_SCHEMA, + joinColumns = { + @JoinColumn(name = "OBJECT_ID") + } + ) ) private LocalizedString title; @@ -191,12 +198,12 @@ public class ContentItem extends CcmObject implements Serializable, */ @Embedded @AssociationOverride( - name = "values", - joinTable = @JoinTable(name = "CONTENT_ITEM_DESCRIPTIONS", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "OBJECT_ID")} - )) + name = "values", + joinTable = @JoinTable(name = "CONTENT_ITEM_DESCRIPTIONS", + schema = DB_SCHEMA, + joinColumns = { + @JoinColumn(name = "OBJECT_ID")} + )) private LocalizedString description; /** @@ -352,10 +359,10 @@ public class ContentItem extends CcmObject implements Serializable, @Override public Optional getParent() { final List result = getCategories().stream().filter( - categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. - equals( - categorization.getType())) - .collect(Collectors.toList()); + categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. + equals( + categorization.getType())) + .collect(Collectors.toList()); if (result.isEmpty()) { return Optional.empty(); @@ -434,15 +441,15 @@ public class ContentItem extends CcmObject implements Serializable, @Override public String toString(final String data) { return super.toString(String.format(", itemUuid = %s, " - + "name = %s, " - + "contentType = { %s }, " - + "title = %s, " - + "description = %s, " - + "version = %s, " - + "launchDate = %s, " - + "lifecycle = { %s }, " - + "workflow = { %s }" - + "%s", + + "name = %s, " + + "contentType = { %s }, " + + "title = %s, " + + "description = %s, " + + "version = %s, " + + "launchDate = %s, " + + "lifecycle = { %s }, " + + "workflow = { %s }" + + "%s", itemUuid, Objects.toString(name), Objects.toString(contentType), diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java index 3961a25da..bd34406fe 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java @@ -40,11 +40,14 @@ import org.libreccm.workflow.Workflow; */ @RequestScoped public class ContentItemRepository - extends AbstractAuditedEntityRepository { + extends AbstractAuditedEntityRepository { @Inject private CcmObjectRepository ccmObjectRepo; + @Inject + private FolderRepository folderRepo; + @Override public Long getEntityId(final ContentItem item) { return item.getObjectId(); @@ -59,7 +62,7 @@ public class ContentItemRepository public boolean isNew(final ContentItem item) { return ccmObjectRepo.isNew(item); } - + @Override public void initNewEntity(final ContentItem item) { final String uuid = UUID.randomUUID().toString(); @@ -75,7 +78,7 @@ public class ContentItemRepository * @param itemId The id of item to retrieve. * * @return The content item identified by the provided {@code itemId} or - * nothing if there is such content item. + * nothing if there is such content item. */ public Optional findById(final long itemId) { final Optional result = ccmObjectRepo.findObjectById(itemId); @@ -89,13 +92,13 @@ public class ContentItemRepository /** * Finds a content item by its ID and ensures that is a the requested type. * - * @param The type of the content item. + * @param The type of the content item. * @param itemId The id of item to retrieve. - * @param type The type of the content item. + * @param type The type of the content item. * * @return The content item identified by the provided id or an empty - * {@link Optional} if there is no such item or if it is not of the - * requested type. + * {@link Optional} if there is no such item or if it is not of the + * requested type. */ @SuppressWarnings("unchecked") public Optional findById(final long itemId, @@ -114,7 +117,7 @@ public class ContentItemRepository * @param uuid The id of item to retrieve. * * @return The content item identified by the provided {@code uuid} or - * nothing if there is such content item. + * nothing if there is such content item. */ public Optional findByUuid(final String uuid) { final Optional result = ccmObjectRepo.findObjectByUuid(uuid); @@ -129,21 +132,21 @@ public class ContentItemRepository * Finds a content item by its UUID and ensures that is a the requested * type. * - * @param The type of the content item. + * @param The type of the content item. * @param uuid The UUID of item to retrieve. * @param type The type of the content item. * * @return The content item identified by the provided UUID or an empty - * {@link Optional} if there is no such item or if it is not of the - * requested type. + * {@link Optional} if there is no such item or if it is not of the + * requested type. */ @SuppressWarnings("unchecked") public Optional findByUuid(final String uuid, final Class type) { final Optional result = ccmObjectRepo.findObjectByUuid(uuid); - if (result.isPresent() - && result.get().getClass().isAssignableFrom(type)) { + if (result.isPresent() + && result.get().getClass().isAssignableFrom(type)) { return Optional.of((T) result.get()); } else { return Optional.empty(); @@ -153,7 +156,7 @@ public class ContentItemRepository /** * Finds all content items of a specific type. * - * @param The type of the items. + * @param The type of the items. * @param type The type of the items. * * @return A list of all content items of the requested type. @@ -161,7 +164,7 @@ public class ContentItemRepository @SuppressWarnings("unchecked") public List findByType(final Class type) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.findByType", ContentItem.class); + .createNamedQuery("ContentItem.findByType", ContentItem.class); query.setParameter("type", type); return (List) query.getResultList(); @@ -176,10 +179,10 @@ public class ContentItemRepository */ public List findByFolder(final Category folder) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.findByFolder", - ContentItem.class); + .createNamedQuery("ContentItem.findByFolder", + ContentItem.class); query.setParameter("folder", folder); - + return query.getResultList(); } @@ -187,26 +190,43 @@ public class ContentItemRepository * Counts the items in a folder/category. * * @param folder The folder/category + * * @return The number of content items in the category/folder. */ public long countItemsInFolder(final Category folder) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.countItemsInFolder", Long.class); + .createNamedQuery("ContentItem.countItemsInFolder", Long.class); query.setParameter("folder", folder); return query.getSingleResult(); } + public Optional findByNameInFolder(final Category folder, + final String name) { + final TypedQuery query = getEntityManager() + .createNamedQuery("ContentItem.findByNameInFolder", + ContentItem.class); + query.setParameter("folder", folder); + query.setParameter("name", name); + + try { + return Optional.of(query.getSingleResult()); + } catch (NoResultException ex) { + return Optional.empty(); + } + } + /** * Counts the number of items with a specific name in a folder/category. * * @param folder * @param name + * * @return */ public long countByNameInFolder(final Category folder, final String name) { final TypedQuery query = getEntityManager().createNamedQuery( - "ContentItem.countByNameInFolder", Long.class); + "ContentItem.countByNameInFolder", Long.class); query.setParameter("folder", folder); query.setParameter("name", name); @@ -219,14 +239,15 @@ public class ContentItemRepository * pattern. * * @param folder The folder/category whose items are filtered. - * @param name The name pattern to use. + * @param name The name pattern to use. + * * @return A list with all items in the folder matching the provided filter. */ public List filterByFolderAndName(final Category folder, final String name) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.filterByFolderAndName", - ContentItem.class); + .createNamedQuery("ContentItem.filterByFolderAndName", + ContentItem.class); query.setParameter("folder", folder); query.setParameter("name", name); @@ -238,15 +259,16 @@ public class ContentItemRepository * starts with the provided pattern. * * @param folder The folder/category to use. - * @param name The name pattern to use. + * @param name The name pattern to use. + * * @return The number of items in the folder/category which match the - * provided pattern. + * provided pattern. */ public long countFilterByFolderAndName(final Category folder, final String name) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.countFilterByFolderAndName", - Long.class); + .createNamedQuery("ContentItem.countFilterByFolderAndName", + Long.class); query.setParameter("folder", folder); query.setParameter("name", name); @@ -255,15 +277,74 @@ public class ContentItemRepository public Optional findItemWithWorkflow(final Workflow workflow) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.findItemWithWorkflow", - ContentItem.class); + .createNamedQuery("ContentItem.findItemWithWorkflow", + ContentItem.class); query.setParameter("workflow", workflow); - + try { return Optional.of(query.getSingleResult()); - } catch(NoResultException ex) { + } catch (NoResultException ex) { return Optional.empty(); } } - + + /** + * Finds a {@link ContentItem} by its path inside a {@link ContentSection}. + * + * @param path The path of the item including the content + * section, separated from the rest of the path by a + * :. + * + * @return An {@link Optional} containing the content item identified by the + * provided path or an empty {@code Optional} if there is no such + * item. + */ + public Optional findByPath(final String path) { + //The last token is the name of the item itself. Remove this part an get + //the folder containing the item using the FolderRepository. + final String normalizedPath = PathUtil.normalizePath(path); + final int lastTokenStart = normalizedPath.lastIndexOf('/'); + final String folderPath = normalizedPath.substring(0, lastTokenStart); + final String itemName = normalizedPath.substring(lastTokenStart + 1); + + final Optional folder = folderRepo.findByPath( + folderPath, FolderType.DOCUMENTS_FOLDER); + + if (folder.isPresent()) { + return findByNameInFolder(folder.get(), itemName); + } else { + return Optional.empty(); + } + } + + /** + * Finds a {@link ContentItem} by its path inside the provided + * {@link ContentSection}. + * + * @param section The section to which the item belongs. + * @param path The path of the item inside this content section. + * + * @return An {@link Optional} containing the content item identified by the + * provided path or an empty {@code Optional} if there is no such + * item. + */ + public Optional findByPath(final ContentSection section, + final String path) { + //The last token is the name of the item itself. Remove this part an get + //the folder containing the item using the FolderRepository. + final String normalizedPath = PathUtil.normalizePath(path); + final int lastTokenStart = normalizedPath.lastIndexOf('/'); + final String folderPath = normalizedPath.substring(0, lastTokenStart); + final String itemName = normalizedPath.substring(lastTokenStart + 1); + + final Optional folder = folderRepo.findByPath( + section, folderPath, FolderType.DOCUMENTS_FOLDER); + + if (folder.isPresent()) { + return findByNameInFolder(folder.get(), itemName); + } else { + return Optional.empty(); + } + } + } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java index a20d3930b..908f7e0ea 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java @@ -125,15 +125,17 @@ public class FolderRepository extends AbstractEntityRepository { throw new IllegalArgumentException("Path can't be null or empty."); } - String normalizedPath = path.replace('.', '/'); - if (normalizedPath.charAt(0) == '/') { - normalizedPath = normalizedPath.substring(1); - } - - if (normalizedPath.endsWith("/")) { - normalizedPath = normalizedPath.substring(0, - normalizedPath.length()); - } +// String normalizedPath = path.replace('.', '/'); +// if (normalizedPath.charAt(0) == '/') { +// normalizedPath = normalizedPath.substring(1); +// } +// +// if (normalizedPath.endsWith("/")) { +// normalizedPath = normalizedPath.substring(0, +// normalizedPath.length()); +// } +// + final String normalizedPath = PathUtil.normalizePath(path); LOGGER.debug("Trying to find folder with path \"{}\" and type {} in" + "content section \"{}\".", diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/PathUtil.java b/ccm-cms/src/main/java/org/librecms/contentsection/PathUtil.java new file mode 100644 index 000000000..0ced0fa93 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/PathUtil.java @@ -0,0 +1,59 @@ +/* + * 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; + +/** + * + * @author Jens Pelzetter + */ +public final class PathUtil { + + private PathUtil() { + //Nothing + } + + /** + * Normalises a path so that the path can be easily processed. This is a + * helper method used by several classes in this package. + * + * The method does the following: + *
    + *
  • Replace all "." in the path with a slash.
  • + *
  • If the first character is a slash remove the character.
  • + *
  • If the last character is a slash remove the character.
  • + *
+ * + * @param path + * @return + */ + protected static final String normalizePath(final String path) { + String normalizedPath = path.replace('.', '/'); + if (normalizedPath.charAt(0) == '/') { + normalizedPath = normalizedPath.substring(1); + } + + if (normalizedPath.endsWith("/")) { + normalizedPath = normalizedPath.substring(0, + normalizedPath.length()); + } + + return normalizedPath; + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTreeNode.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTreeNode.java new file mode 100644 index 000000000..7d4cb9372 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTreeNode.java @@ -0,0 +1,64 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.tree.TreeNode; +import com.arsdigita.kernel.KernelConfig; +import java.util.Locale; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.web.CcmApplication; + +/** + * + * @author Jens Pelzetter + */ +public class ApplicationTreeNode implements TreeNode { + + private final CcmApplication application; + + public ApplicationTreeNode(final CcmApplication application) { + this.application = application; + } + + public CcmApplication getApplication() { + return application; + } + + @Override + public Object getKey() { + return application.getPrimaryUrl(); + } + + @Override + public Object getElement() { + final GlobalizationHelper globalizationHelper = CdiUtil.createCdiUtil().findBean(GlobalizationHelper.class); + final Locale locale = globalizationHelper.getNegotiatedLocale(); + + if (application.getTitle().hasValue(locale)) { + return application.getTitle().hasValue(locale); + } else { + final Locale defaultLocale = KernelConfig.getConfig().getDefaultLocale(); + return application.getTitle().getValue(defaultLocale); + } + } + + + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTypeTreeNode.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTypeTreeNode.java new file mode 100644 index 000000000..0750429c5 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/ApplicationTypeTreeNode.java @@ -0,0 +1,53 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.tree.TreeNode; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.web.CcmApplication; + +/** + * + * @author Jens Pelzetter + */ +class ApplicationTypeTreeNode implements TreeNode { + + private final Class appType; + + protected ApplicationTypeTreeNode( + final Class appType) { + + this.appType = appType; + } + + @Override + public Object getKey() { + return appType.getName(); + } + + @Override + public Object getElement() { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PageModelTreeController controller = cdiUtil.findBean( + PageModelTreeController.class); + + return controller.getAppTypeTitle(appType); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelPane.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelPane.java new file mode 100644 index 000000000..ad8a461ca --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelPane.java @@ -0,0 +1,42 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.Tree; +import com.arsdigita.toolbox.ui.LayoutPanel; +import java.util.Map; + +/** + * + * @author Jens Pelzetter + */ +public class PageModelPane extends LayoutPanel { + + private final Tree tree; + + public PageModelPane() { + + super(); + + setClassAttr("sidebarNavPanel"); + + tree = new Tree(new PageModelTreeModelBuilder()); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeController.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeController.java new file mode 100644 index 000000000..4364f556f --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeController.java @@ -0,0 +1,46 @@ +/* + * 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.libreccm.pagemodel.ui; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import org.libreccm.web.ApplicationManager; +import org.libreccm.web.ApplicationType; +import org.libreccm.web.CcmApplication; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class PageModelTreeController { + + @Inject + private ApplicationManager applicationManager; + + public String getAppTypeTitle(final Class appType) { + + final ApplicationType applicationType = applicationManager. + getApplicationTypes().get(appType.getClass().getName()); + + return applicationManager.getApplicationTypeTitle(applicationType); + + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeModelBuilder.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeModelBuilder.java new file mode 100644 index 000000000..0e3355588 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeModelBuilder.java @@ -0,0 +1,47 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Tree; +import com.arsdigita.bebop.tree.TreeModel; +import com.arsdigita.bebop.tree.TreeModelBuilder; + +/** + * + * @author Jens Pelzetter + */ +class PageModelTreeModelBuilder implements TreeModelBuilder { + + @Override + public TreeModel makeModel(final Tree tree, final PageState state) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void lock() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean isLocked() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeNode.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeNode.java new file mode 100644 index 000000000..0aaffa4ee --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageModelTreeNode.java @@ -0,0 +1,60 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.tree.TreeNode; +import com.arsdigita.kernel.KernelConfig; +import java.util.Locale; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.pagemodel.PageModel; + +/** + * + * @author Jens Pelzetter + */ +class PageModelTreeNode implements TreeNode { + + private final PageModel pageModel; + + protected PageModelTreeNode(final PageModel pageModel) { + this.pageModel = pageModel; + } + + @Override + public Object getKey() { + return pageModel.getName(); + } + + @Override + public Object getElement() { + final GlobalizationHelper globalizationHelper = CdiUtil.createCdiUtil(). + findBean(GlobalizationHelper.class); + final Locale locale = globalizationHelper.getNegotiatedLocale(); + + if (pageModel.getTitle().hasValue(locale)) { + return pageModel.getTitle().getValue(locale); + } else { + final Locale defaultLocale = KernelConfig.getConfig(). + getDefaultLocale(); + return pageModel.getTitle().getValue(defaultLocale); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageTreeModel.java b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageTreeModel.java new file mode 100644 index 000000000..3b7558d0e --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/pagemodel/ui/PageTreeModel.java @@ -0,0 +1,82 @@ +/* + * 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.libreccm.pagemodel.ui; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.tree.TreeModel; +import com.arsdigita.bebop.tree.TreeNode; + +import java.util.Iterator; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.pagemodel.PageModelRepository; +import org.libreccm.web.CcmApplication; + +/** + * + * @author Jens Pelzetter + */ +class PageTreeModel implements TreeModel { + + @Override + public TreeNode getRoot(final PageState state) { + return new RootNode(); + } + + @Override + public boolean hasChildren(final TreeNode node, final PageState state) { + if (node instanceof RootNode) { + return true; + } else if (node instanceof ApplicationTypeTreeNode) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PageModelRepository pageModelRepo = cdiUtil.findBean( + PageModelRepository.class); + final CcmApplication application = ((ApplicationTreeNode) node) + .getApplication(); + final long count = pageModelRepo.countByApplication(application); + + return count > 0; + } else if (node instanceof PageModelTreeNode) { + return false; + } else { + throw new IllegalArgumentException(String.format( + "Unexpected node type: \"%s\".", node.getClass().getName())); + } + } + + @Override + public Iterator getChildren(final TreeNode node, final PageState state) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private class RootNode implements TreeNode { + + @Override + public Object getKey() { + return "-1"; + } + + @Override + public Object getElement() { + return "PageModels"; + } + + } + +}