From 7430901acc714f07de3dacdac79af791b5a4c4c5 Mon Sep 17 00:00:00 2001 From: jensp Date: Mon, 14 Nov 2016 19:09:40 +0000 Subject: [PATCH] CCM NG/ccm-cms: Current progress of porting the UI to CCM NG git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4440 8810af33-2d31-482b-a856-94f89814c4df --- .../arsdigita/cms/ContentSectionServlet.java | 743 ------------------ .../com/arsdigita/cms/ItemSelectionModel.java | 46 +- .../com/arsdigita/cms/dispatcher/CMSPage.java | 2 +- .../cms/dispatcher/ContentItemDispatcher.java | 5 +- .../cms/dispatcher/ContentPanel.java | 2 +- .../cms/dispatcher/ItemDispatcher.java | 2 +- .../dispatcher/SimpleItemResolver.java.off | 349 ++++++++ .../arsdigita/cms/ui/ContentItemPage.java.off | 4 - ...tor.java.off => AuthoringKitSelector.java} | 83 +- .../ui/authoring/AuthoringKitWizard.java.off | 684 ++++++++++++++++ .../cms/ui/authoring/WizardSelector.java.off | 38 +- .../ContentSectionContainer.java | 33 +- .../cms/ui/item/ItemWorkflowRequestLocal.java | 39 + .../ui/workflow/AssignedTaskController.java | 106 +++ .../cms/ui/workflow/AssignedTaskSection.java | 269 +++++++ .../cms/ui/workflow/AssignedTaskTable.java | 139 ++++ .../AssignedTaskTableModelBuilder.java | 118 +++ ccm-cms/src/main/java/org/librecms/Cms.java | 3 +- .../contentsection/ContentSectionServlet.java | 491 +++++++++++- .../contentsection/ContentSectionSetup.java | 5 +- ...7_0_0_8__update_assets_and_attachments.sql | 9 + ...7_0_0_8__update_assets_and_attachments.sql | 9 + .../org/librecms/CmsResources.properties | 16 +- .../org/librecms/CmsResources_de.properties | 16 +- .../org/librecms/CmsResources_fr.properties | 18 + 25 files changed, 2374 insertions(+), 855 deletions(-) delete mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ContentSectionServlet.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.off rename ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/{AuthoringKitSelector.java.off => AuthoringKitSelector.java} (58%) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.off create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemWorkflowRequestLocal.java create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskController.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskSection.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTable.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTableModelBuilder.java create mode 100644 ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ContentSectionServlet.java b/ccm-cms/src/main/java/com/arsdigita/cms/ContentSectionServlet.java deleted file mode 100755 index d2137b794..000000000 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ContentSectionServlet.java +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.arsdigita.cms; - -import com.arsdigita.bebop.Page; -import com.arsdigita.cms.dispatcher.CMSDispatcher; -import com.arsdigita.cms.dispatcher.CMSPage; -import com.arsdigita.cms.dispatcher.ContentItemDispatcher; -import com.arsdigita.cms.dispatcher.ItemResolver; -import com.arsdigita.cms.ui.CMSApplicationPage; -import com.arsdigita.dispatcher.AccessDeniedException; -import com.arsdigita.dispatcher.DispatcherHelper; -import com.arsdigita.dispatcher.RequestContext; -import com.arsdigita.templating.PresentationManager; -import com.arsdigita.templating.Templating; -import com.arsdigita.util.Assert; -import com.arsdigita.util.Classes; -import com.arsdigita.web.ApplicationFileResolver; -import com.arsdigita.web.BaseApplicationServlet; -import com.arsdigita.web.LoginSignal; -import com.arsdigita.web.Web; -import com.arsdigita.web.WebConfig; -import com.arsdigita.xml.Document; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.log4j.Logger; -import org.arsdigita.cms.CMSConfig; -import org.libreccm.cdi.utils.CdiUtil; -import org.libreccm.l10n.GlobalizationHelper; -import org.libreccm.security.PermissionChecker; -import org.libreccm.security.Shiro; -import org.libreccm.web.CcmApplication; -import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentItemManager; -import org.librecms.contentsection.ContentSection; -import org.librecms.contentsection.privileges.ItemPrivileges; - -/* - * NOTE: - * Repaired ItemURLCache to save multilingual items with automatic - * language negotiation. The cache now uses the remaining url part - * and the language concatinated as a hash table key. The delimiter - * is CACHE_KEY_DELIMITER. - */ - - /* - * NOTE 2: - * In a process of refactoring from legacy compatible to legacy free applications. - * TODO: - * - replace url check using RequestContext which resolves to SiteNodeRequest - * implementation (due to SiteNodeRequest used in BaseApplicationServlet). - * - Refactor content item UI bebop ApplicationPage or PageFactory instead of - * legacy infected sitenode / package dispatchers. - */ -/** - * Content Section's Application Servlet according CCM core web application - * structure { - * - * @see com.arsdigita.web.Application} implements the content section UI. - * - * It handles the UI for content items and delegates the UI for sections and - * folders to jsp templates. - * - * @author unknown - * @author Sören Bernstein - * @author Peter Boy - */ -public class ContentSectionServlet extends BaseApplicationServlet { - - /** - * Internal logger instance to faciliate debugging. Enable logging output by - * editing /WEB-INF/conf/log4j.properties int hte runtime environment and - * set com.arsdigita.cms.ContentSectionServlet=DEBUG by uncommenting or - * adding the line. - */ - private static final Logger s_log = Logger.getLogger( - ContentSectionServlet.class); - /** - * Stringarray of file name patterns for index files. - */ -// private static final String[] WELCOME_FILES = new String[]{ -// "index.jsp", "index.html" -// }; - - // Some literals - /** - * Literal for the prefix (in url) for previewing items - */ - public static final String PREVIEW = "/preview"; - /** - * Literal Template files suffix - */ - public static final String FILE_SUFFIX = ".jsp"; - /** - * Literal of URL Stub for index file name (includes leading slash) - */ - public static final String INDEX_FILE = "/index"; - public static final String XML_SUFFIX = ".xml"; - public static final String XML_MODE = "xmlMode"; - public static final String MEDIA_TYPE = "templateContext"; - private static final String CACHE_KEY_DELIMITER = "%"; - - public static final String CONTENT_ITEM - = "com.arsdigita.cms.dispatcher.item"; - public static final String CONTENT_SECTION - = "com.arsdigita.cms.dispatcher.section"; - - private final ContentItemDispatcher m_disp = new ContentItemDispatcher(); - public static Map s_itemResolverCache = Collections - .synchronizedMap(new HashMap()); -// private static Map s_itemURLCacheMap = null; - /** - * Whether to cache the content items - */ - private static final boolean s_cacheItems = true; - // NEW STUFF here used to process the pages in this servlet - /** - * URL (pathinfo) -> Page object mapping. Based on it (and the http request - * url) the doService method selects a page to display - */ - private final Map m_pages = new HashMap(); - /** - * Path to directory containg ccm-cms template (jsp) files - */ - private String m_templatePath; - - /** - * Resolver to actually use to find templates (JSP). JSP may be stored in - * file system or otherwise, depends on resolver. Resolver is retrieved from - * configuration. (probably used for other stuff as JSP's as well) - */ - private ApplicationFileResolver m_resolver; - - /** - * Init method overwrites parents init to pass in optional parameters - * {@link com.arsdigita.web.BaseServlet}. If not specified system wide - * defaults are used. - * - * @param config - * - * @throws javax.servlet.ServletException - */ - @Override - public void init(ServletConfig config) throws ServletException { - - super.init(config); - - // optional init-param named template-path from ~/WEB-INF/web.xml - // may overwrite configuration parameters - String templatePath = config.getInitParameter("template-path"); - if (templatePath == null) { - m_templatePath = CMSConfig.getConfig().getTemplateRootPath(); - } else { - m_templatePath = config.getInitParameter("template-path"); - } - - Assert.exists(m_templatePath, String.class); - Assert.isTrue(m_templatePath.startsWith("/"), - "template-path must start with '/'"); - Assert.isTrue(!m_templatePath.endsWith("/"), - "template-path must not end with '/'"); - - // optional init-param named file-resolver from ~/WEB-INF/web.xml - String resolverName = config.getInitParameter("file-resolver"); - if (resolverName == null) { - m_resolver = WebConfig.getConfig().getResolver(); - } else { - m_resolver = (ApplicationFileResolver) Classes.newInstance( - resolverName); - } - if (s_log.isDebugEnabled()) { - s_log.debug("Template path is " + m_templatePath + " with resolver " - + m_resolver. - getClass().getName()); - } - - //ToDo addPage(CmsConstants.CONTENT_SECTION_PAGE, new ContentSectionPage()); // index page at address ~/cs - //ToDo addPage(CmsConstants.CONTENT_SECTION_ITEM_PAGE, new ContentSectionPage()); - } - - /** - * Internal service method, adds one pair of Url - Page to the internal hash - * map, used as a cache. - * - * @param pathInfo url stub for a page to display - * @param page Page object to display - */ - private void addPage(final String pathInfo, final Page page) { - - Assert.exists(pathInfo, String.class); - Assert.exists(page, Page.class); - // Current Implementation requires pathInfo to start with a leading '/' - // SUN Servlet API specifies: "PathInfo *may be empty* or will start - // with a '/' character." - Assert.isTrue(pathInfo.startsWith("/"), "path starts not with '/'"); - - m_pages.put(pathInfo, page); - } - - /** - * Implementation of parent's (abstract) doService method checks HTTP - * request to determine whether to handle a content item or other stuff - * which is delegated to jsp templates. { - * - * @see com.arsdigita.web.BaseApplicationServlet#doService - * (HttpServletRequest, HttpServletResponse, Application)} - * - * @param sreq - * @param sresp - * @param app - * - * @throws javax.servlet.ServletException - * @throws java.io.IOException - */ - @Override - protected void doService(HttpServletRequest sreq, - HttpServletResponse sresp, - CcmApplication app) - throws ServletException, IOException { - - ContentSection section = (ContentSection) app; - - // //////////////////////////////////////////////////////////////////// - // Prepare OLD style dispatcher based page service - // //////////////////////////////////////////////////////////////////// - /* - * NOTE: - * Used to resolve to SiteNodeRequestContext (old style applications) - * which has been removed. - * Resolves currently to - * KernelRequestContext which will be removed as well. - */ - RequestContext ctx = DispatcherHelper.getRequestContext(); - String url = ctx.getRemainingURLPart(); // here KernelRequestContext now - if (s_log.isInfoEnabled()) { - s_log.info("Resolving URL " + url + " and trying as item first."); - } - final ItemResolver itemResolver = getItemResolver(section); - - // //////////////////////////////////////////////////////////////////// - // Prepare NEW style servlet based bebob page service - // //////////////////////////////////////////////////////////////////// - String pathInfo = sreq.getPathInfo(); - - final ContentItem item = getItem(section, pathInfo, itemResolver); - - Assert.exists(pathInfo, "String pathInfo"); - if (pathInfo.length() > 1 && pathInfo.endsWith("/")) { - /* NOTE: ServletAPI specifies, pathInfo may be empty or will - * start with a '/' character. It currently carries a - * trailing '/' if a "virtual" page, i.e. not a real jsp, but - * result of a servlet mapping. But Application requires url - * NOT to end with a trailing '/' for legacy free applications. */ - pathInfo = pathInfo.substring(0, pathInfo.length() - 1); - } - final Page page = (Page) m_pages.get(pathInfo); - - // //////////////////////////////////////////////////////////////////// - // Serve the page - // //////////////////////////////////////////////////////////////////// - /* FIRST try new style servlet based service */ - if (page != null) { - - // Check user access. - // checkUserAccess(sreq, sresp); // done in individual pages ?? - if (page instanceof CMSPage) { - // backwards compatibility fix until migration completed - final CMSPage cmsPage = (CMSPage) page; - // final RequestContext ctx = DispatcherHelper.getRequestContext(); - cmsPage.init(); - cmsPage.dispatch(sreq, sresp, ctx); - } else { - final CMSApplicationPage cmsAppPage = (CMSApplicationPage) page; - cmsAppPage.init(sreq, sresp, app); - // Serve the page. - final Document doc = cmsAppPage.buildDocument(sreq, sresp); - - PresentationManager pm = Templating.getPresentationManager(); - pm.servePage(doc, sreq, sresp); - } - - /* SECONDLY try if we have to serve an item (old style dispatcher based */ - } else if (item != null) { - - serveItem(sreq, sresp, section, item); - - /* OTHERWISE delegate to a JSP in file system */ - } else { - - /* We have to deal with a content-section, folder or another bit */ - if (s_log.isInfoEnabled()) { - s_log.info("NOT serving content item"); - } - - /* Store content section in http request to make it available - * for admin/index.jsp */ - sreq.setAttribute(CONTENT_SECTION, section); - - RequestDispatcher rd = m_resolver.resolve(m_templatePath, - sreq, sresp, app); - if (rd != null) { - if (s_log.isDebugEnabled()) { - s_log.debug("Got dispatcher " + rd); - } - sreq = DispatcherHelper.restoreOriginalRequest(sreq); - rd.forward(sreq, sresp); - } else { - if (s_log.isDebugEnabled()) { - s_log.debug("No dispatcher found for" + rd); - } - String requestUri = sreq.getRequestURI(); // same as ctx.getRemainingURLPart() - sresp.sendError(404, requestUri + " not found on this server."); - } - } - } // END doService - - /** - * - * @param sreq - * @param sresp - * @param section - * @param item - * - * @throws ServletException - * @throws IOException - */ - private void serveItem(HttpServletRequest sreq, - HttpServletResponse sresp, - ContentSection section, - ContentItem item) - throws ServletException, IOException { - - if (s_log.isInfoEnabled()) { - s_log.info("serving content item"); - } - - RequestContext ctx = DispatcherHelper.getRequestContext(); - String url = ctx.getRemainingURLPart(); - - final ItemResolver itemResolver = getItemResolver(section); - - //set the content item in the request - sreq.setAttribute(CONTENT_ITEM, item); - - //set the template context - //TemplateResolver templateResolver = m_disp.getTemplateResolver(section); - String templateURL = url; - if (!templateURL.startsWith("/")) { - templateURL = "/" + templateURL; - } - if (templateURL.startsWith(PREVIEW)) { - templateURL = templateURL.substring(PREVIEW.length()); - } - - String sTemplateContext = itemResolver.getTemplateFromURL(templateURL); - if (s_log.isDebugEnabled()) { - s_log.debug("setting template context to " + sTemplateContext); - } - //templateResolver.setTemplateContext(sTemplateContext, sreq); - - // Work out how long to cache for.... - // We take minimum(default timeout, lifecycle expiry) - //ToDo - /*Lifecycle cycle = item.getLifecycle(); - */ - int expires = DispatcherHelper.getDefaultCacheExpiry(); - /* - if (cycle != null) { - Date endDate = cycle.getEndDate(); - - if (endDate != null) { - int maxAge = (int) ((endDate.getTime() - System - .currentTimeMillis()) / 1000l); - if (maxAge < expires) { - expires = maxAge; - } - } - }*/ - //ToDo End - - // NB, this is not the same as the security check previously - // We are checking if anyone can access - ie can we allow - // this page to be publically cached - final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); - final ContentItemManager contentItemManager = cdiUtil.findBean( - ContentItemManager.class); - final PermissionChecker permissionChecker = cdiUtil.findBean( - PermissionChecker.class); - if (s_cacheItems && contentItemManager.isLive(item)) { - if (permissionChecker.isPermitted( - ItemPrivileges.VIEW_PUBLISHED, item)) { - DispatcherHelper.cacheForWorld(sresp, expires); - } else { - DispatcherHelper.cacheForUser(sresp, expires); - } - } else { - DispatcherHelper.cacheDisable(sresp); - } - - //use ContentItemDispatcher - m_disp.dispatch(sreq, sresp, ctx); - } - - /** - * Fetches the content section from the request attributes. - * - * @param request The HTTP request - * - * @return The content section - * - * @pre ( request != null ) - */ - public static ContentSection getContentSection(HttpServletRequest request) { - return (ContentSection) request.getAttribute(CONTENT_SECTION); - } - - /** - * Fetches the ItemResolver for a content section. Checks cache first. - * - * @param section The content section - * - * @return The ItemResolver associated with the content section - */ - public ItemResolver getItemResolver(ContentSection section) { - - String path = section.getPrimaryUrl(); - ItemResolver ir = (ItemResolver) s_itemResolverCache.get(path); - - if (ir == null) { - final String itemResolverClassName = section.getItemResolverClass(); - try { - ir = (ItemResolver) Class - .forName(section.getItemResolverClass()).newInstance(); - } catch (ClassNotFoundException | - IllegalAccessException | - InstantiationException ex) { - throw new RuntimeException(ex); - } - s_itemResolverCache.put(path, ir); - } - - if (s_log.isDebugEnabled()) { - s_log.debug("using ItemResolver " + ir.getClass().getName()); - } - - return ir; - } - - /** - * - * @param section - * @param url - * @param itemResolver - * - * @return - */ - public ContentItem getItem(ContentSection section, String url, - ItemResolver itemResolver) { - - if (s_log.isDebugEnabled()) { - s_log.debug("getting item at url " + url); - } - HttpServletRequest request = Web.getRequest(); - - //first sanitize the url - if (url.endsWith(XML_SUFFIX)) { - request.setAttribute(XML_MODE, Boolean.TRUE); - s_log.debug("StraightXML Requested"); - url = "/" + url.substring(0, url.length() - XML_SUFFIX.length()); - } else { - request.setAttribute(XML_MODE, Boolean.FALSE); - if (url.endsWith(FILE_SUFFIX)) { - url = "/" + url - .substring(0, url.length() - FILE_SUFFIX.length()); - } else if (url.endsWith("/")) { - url = "/" + url.substring(0, url.length() - 1); - } - } - - if (!url.startsWith("/")) { - url = "/" + url; - } - - ContentItem item; - // Check if the user has access to view public or preview pages - final PermissionChecker permissionChecker = CdiUtil.createCdiUtil() - .findBean(PermissionChecker.class); - boolean hasPermission = true; - - // If the remaining URL starts with "preview/", then try and - // preview this item. Otherwise look for the live item. - boolean preview = false; - if (url.startsWith(PREVIEW)) { - url = url.substring(PREVIEW.length()); - preview = true; - } - - if (preview) { - if (s_log.isInfoEnabled()) { - s_log.info("Trying to get item for PREVIEW"); - } - - item = itemResolver.getItem(section, url, CMSDispatcher.PREVIEW); - if (item != null) { - hasPermission = permissionChecker.isPermitted( - ItemPrivileges.PREVIEW, item); - } - } else { - if (s_log.isInfoEnabled()) { - s_log.info("Trying to get LIVE item"); - } - - //check if this item is in the cache - //we only cache live items - if (s_log.isDebugEnabled()) { - s_log.debug("Trying to get content item for URL " + url - + " from cache"); - } - - // Get the negotiated locale - String lang = CdiUtil.createCdiUtil().findBean( - GlobalizationHelper.class).getNegotiatedLocale() - .getLanguage(); - - // XXX why assign a value and afterwards null?? - // Effectively it just ignores the cache and forces a fallback to - // itemResover in any case. Maybe otherwise language selection / - // negotiation doesn't work correctly? -// item = itemURLCacheGet(section, url, lang); - item = null; - - if (item == null) { - if (s_log.isDebugEnabled()) { - s_log.debug("Did not find content item in cache, so trying " - + "to retrieve and cache..."); - } - //item not cached, so retreive it and cache it - item = itemResolver.getItem(section, url, "live"); -// itemURLCachePut(section, url, lang, item); - } else if (s_log.isDebugEnabled()) { - s_log.debug("Found content item in cache"); - } - - //ToDo -// if (s_log.isDebugEnabled() && item != null) { -// s_log.debug("Sanity check: item.getPath() is " + item.getPath()); -// } - if (item != null) { - if (s_log.isDebugEnabled()) { - s_log.debug("Content Item is not null"); - } - - hasPermission = permissionChecker.isPermitted( - ItemPrivileges.VIEW_PUBLISHED, item); - - if (hasPermission) { - } - } - } - - if (item == null && url.endsWith(INDEX_FILE)) { - - if (item == null) { - if (s_log.isInfoEnabled()) { - s_log.info("no item found"); - } - } - - // look up folder if it's an index - url = url.substring(0, url.length() - INDEX_FILE.length()); - if (s_log.isInfoEnabled()) { - s_log.info("Attempting to match folder " + url); - } - item = itemResolver.getItem(section, url, "live"); - if (item != null) { - hasPermission = permissionChecker.isPermitted( - ItemPrivileges.VIEW_PUBLISHED, item); - } - } - - if (!hasPermission) { - - // first, check if the user is logged-in - // if he isn't, give him a chance to do so... - if (!CdiUtil.createCdiUtil().findBean(Shiro.class).getSubject() - .isAuthenticated()) { - throw new LoginSignal(request); - } - - throw new AccessDeniedException(); - } - - return item; - } - - public ContentItem getItem(ContentSection section, String url) { - ItemResolver itemResolver = getItemResolver(section); - - return getItem(section, url, itemResolver); - } - - // synchronize access to the item-url cache -// private static synchronized void itemURLCachePut(ContentSection section, -// String sURL, -// String lang, -// Long itemID) { -// -// getItemURLCache(section).put(sURL + CACHE_KEY_DELIMITER + lang, itemID); -// } - /** - * Maps the content item to the URL in a cache - * - * @param section the content section in which the content item is published - * @param sURL the URL at which the content item s published - * @param lang - * @param item the content item at the URL - */ -// public static synchronized void itemURLCachePut(ContentSection section, -// String sURL, -// String lang, -// ContentItem item) { -// if (sURL == null || item == null) { -// return; -// } -// if (s_log.isDebugEnabled()) { -// s_log.debug("adding cached entry for url " + sURL + " and language " -// + lang); -// } -// -// itemURLCachePut(section, sURL, lang, item.getObjectId()); -// } - /** - * Removes the cache entry for the URL, sURL - * - * @param section the content section in which to remove the key - * @param sURL the cache entry key to remove - * @param lang - */ -// public static synchronized void itemURLCacheRemove(ContentSection section, -// String sURL, -// String lang) { -// if (s_log.isDebugEnabled()) { -// s_log.debug("removing cached entry for url " + sURL -// + "and language " + lang); -// } -// getItemURLCache(section).remove(sURL + CACHE_KEY_DELIMITER + lang); -// } - /** - * Fetches the ContentItem published at that URL from the cache. - * - * @param section the content section in which the content item is published - * @param sURL the URL for the item to fetch - * @param lang - * - * @return the ContentItem in the cache, or null - */ -// public static ContentItem itemURLCacheGet(ContentSection section, -// final String sURL, -// final String lang) { -// final Long itemID = (Long) getItemURLCache(section).get( -// sURL + CACHE_KEY_DELIMITER + lang); -// -// if (itemID == null) { -// return null; -// } else { -// -// final ContentItemRepository itemRepo = CdiUtil.createCdiUtil().findBean(ContentItemRepository.class); -// return itemRepo.findById(itemID); -// -// } -// } -// private static synchronized CacheTable getItemURLCache( -// ContentSection section) { -// Assert.exists(section, ContentSection.class); -// if (s_itemURLCacheMap == null) { -// initializeItemURLCache(); -// } -// -// if (s_itemURLCacheMap.get(section.getPath()) == null) { -// final CacheTable cache = new CacheTable( -// "ContentSectionServletItemURLCache" + section.getID().toString()); -// s_itemURLCacheMap.put(section.getPath(), cache); -// } -// -// return (CacheTable) s_itemURLCacheMap.get(section.getPath()); -// } -// -// private static synchronized void initializeItemURLCache() { -// ContentSectionCollection sections = ContentSection.getAllSections(); -// s_itemURLCacheMap = new HashMap(); -// while (sections.next()) { -// ContentSection section = sections.getContentSection(); -// String idStr = section.getID().toString(); -// String path = section.getPath(); -// CacheTable itemURLCache = new CacheTable( -// "ContentSectionServletItemURLCache" + idStr); -// s_itemURLCacheMap.put(path, itemURLCache); -// -// } -// } - /** - * Checks that the current user has permission to access the admin pages. - * - * @param request - * @param section - * - * @return - * - */ - public static boolean checkAdminAccess(HttpServletRequest request, - ContentSection section) { - return CdiUtil.createCdiUtil().findBean(PermissionChecker.class) - .isPermitted(ItemPrivileges.EDIT, - section.getRootDocumentsFolder()); - } - -} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java index 08e2f521c..5b50a0a01 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java @@ -28,7 +28,7 @@ import org.libreccm.cdi.utils.CdiUtil; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentType; import org.librecms.contentsection.ContentTypeRepository; - +import org.librecms.contenttypes.ContentTypeInfo; import java.math.BigDecimal; @@ -44,16 +44,16 @@ import java.math.BigDecimal; * model will only instantiate items that are of the specified content type, or * one of it subclasses.

* + * @author Jens Pelzetter * @author Stanislav Freidin (stas@arsdigita.com) - * @version $Revision$ $DateTime: 2004/08/17 23:15:09 $ * @see com.arsdigita.kernel.ui.ACSObjectSelectionModel * @see com.arsdigita.bebop.SingleSelectionModel */ -public class ItemSelectionModel extends CcmObjectSelectionModel { +public class ItemSelectionModel extends CcmObjectSelectionModel { - private Long m_typeId; + private Long typeId; - private static final Logger s_log = Logger.getLogger( + private static final Logger LOGGER = Logger.getLogger( ItemSelectionModel.class); /** @@ -65,7 +65,8 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { * @param parameterName The name of the state parameter which will be used * to store the item. */ - public ItemSelectionModel(ContentType type, String parameterName) { + public ItemSelectionModel(final ContentType type, + final String parameterName) { this(type, new LongParameter(parameterName)); } @@ -77,9 +78,10 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { * @param parameter The state parameter which should be used by this item * */ - public ItemSelectionModel(ContentType type, LongParameter parameter) { + public ItemSelectionModel(final ContentType type, + final LongParameter parameter) { super(type.getContentItemClass(), parameter); - m_typeId = type.getObjectId(); + typeId = type.getObjectId(); } /** @@ -91,22 +93,35 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { * {@link BigDecimal} id of the currently selected object * */ - public ItemSelectionModel(ContentType type, SingleSelectionModel model) { + public ItemSelectionModel(final ContentType type, + final SingleSelectionModel model) { super(type.getContentItemClass(), model); - m_typeId = type.getObjectId(); + typeId = type.getObjectId(); } + public ItemSelectionModel(final ContentTypeInfo type, + final SingleSelectionModel model) { + super(type.getContentItemClass().getName(), model); + typeId = null; + } + + public ItemSelectionModel(final ContentTypeInfo type, + final LongParameter parameter) { + super(type.getContentItemClass().getName(), parameter); + typeId = null; + } + /** * A convenience method that gets the currently selected object and casts it * to a ContentItem * - * @param s the current page state + * @param state the current page state * * @return the currently selected ContentItem, or null if no * item was selected. */ - public final ContentItem getSelectedItem(PageState s) { - return (ContentItem) getSelectedObject(s); + public final ContentItem getSelectedItem(final PageState state) { + return getSelectedObject(state); } /** @@ -119,8 +134,9 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { ContentType type = null; - if (m_typeId != null) { - type = CdiUtil.createCdiUtil().findBean(ContentTypeRepository.class).findById(m_typeId); + if (typeId != null) { + type = CdiUtil.createCdiUtil().findBean(ContentTypeRepository.class) + .findById(typeId); } return type; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java index 99a5c934c..451640591 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/CMSPage.java @@ -24,7 +24,6 @@ import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Page; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.page.PageTransformer; -import com.arsdigita.cms.ContentSectionServlet; import com.arsdigita.dispatcher.RequestContext; import com.arsdigita.templating.PresentationManager; import com.arsdigita.web.Web; @@ -48,6 +47,7 @@ import org.librecms.CmsConstants; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionServlet; import org.librecms.contentsection.privileges.ItemPrivileges; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentItemDispatcher.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentItemDispatcher.java index 60f884113..71c55a6f3 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentItemDispatcher.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentItemDispatcher.java @@ -18,7 +18,6 @@ */ package com.arsdigita.cms.dispatcher; -import com.arsdigita.cms.ContentSectionServlet; import com.arsdigita.dispatcher.Dispatcher; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.dispatcher.RequestContext; @@ -80,7 +79,7 @@ public class ContentItemDispatcher implements Dispatcher { throws IOException, ServletException { Boolean bXMLMode = (Boolean) request - .getAttribute(ContentSectionServlet.XML_MODE); + .getAttribute("xmlMode"); if (bXMLMode != null && bXMLMode.booleanValue()) { //if this is XML mode, then use itemXML m_itemXML.dispatch(request, response, actx); @@ -119,7 +118,7 @@ public class ContentItemDispatcher implements Dispatcher { */ public static ContentItem getContentItem(HttpServletRequest request) { return (ContentItem) request.getAttribute( - ContentSectionServlet.CONTENT_ITEM); + "com.arsdigita.cms.dispatcher.item"); } // //synchronize access to the cache diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentPanel.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentPanel.java index 7970f765a..3704fd3ed 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentPanel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ContentPanel.java @@ -21,13 +21,13 @@ package com.arsdigita.cms.dispatcher; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.SimpleComponent; import com.arsdigita.cms.CMS; -import com.arsdigita.cms.ContentSectionServlet; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.util.Assert; import com.arsdigita.xml.Element; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionServlet; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemDispatcher.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemDispatcher.java index f73affa4b..5b7959e3b 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemDispatcher.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemDispatcher.java @@ -18,7 +18,6 @@ */ package com.arsdigita.cms.dispatcher; -import com.arsdigita.cms.ContentSectionServlet; import com.arsdigita.dispatcher.ChainedDispatcher; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.dispatcher.RequestContext; @@ -39,6 +38,7 @@ import org.libreccm.security.PermissionChecker; import org.libreccm.security.Shiro; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionServlet; import org.librecms.contentsection.privileges.ItemPrivileges; /** diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.off new file mode 100755 index 000000000..d9d855e5b --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/SimpleItemResolver.java.off @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.dispatcher; + + +import com.arsdigita.bebop.PageState; +import com.arsdigita.cms.CMS; +import 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 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}.

+ * + *

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.

+ * + *

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.

+ * + * @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 $ + */ +public class SimpleItemResolver extends AbstractItemResolver implements ItemResolver { + + private static final Logger s_log = + Logger.getLogger(SimpleItemResolver.class.getName()); + + private static final String ADMIN_PREFIX = "admin"; + private static final String WORKSPACE_PREFIX = ContentCenter.getURL(); + + private static MasterPage s_masterP = null; + + public SimpleItemResolver() {} + + /** + * Return a content item based on page state (and content section). + * + * @param section The current content section + * @param url The section-relative URL + * @param context The LIVE/DRAFT context (*not* the template use context) + * @return The content item mapped to the content section and URL, or + * null if no such item exists + */ + public ContentItem getItem(ContentSection section, String url, + String context) { + + if (s_log.isDebugEnabled()) { + s_log.debug("trying to get " + context + " item for url " + url); + } + + String itemUrl = stripTemplateFromURL(url); + + // getItem fails if called from a JSP template, because the request URL + // gets replaced with the filesystem path to the JSP (when + // DispatcherHelper.forwardRequestByPath is called). To fix this, we check + // if the item had already been put into the request by the CMSDispatcher + // (which it usually has) and return it. + ContentItem reqItem = (ContentItem) DispatcherHelper.getRequest(). + getAttribute("com.arsdigita.cms.dispatcher.item"); + if (reqItem != null) { + s_log.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"); + 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; + } + + /** + * @param state the current page state + * @return the context of the current URL, such as "live" or "admin" + */ + public String getCurrentContext(PageState state) { + + String url = state.getRequest().getRequestURI(); + + 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) ) { + url = url.substring(sectionURL.length()); + } + } + + // remove any template-specific URL components + // (will only work if they're first in the URL at this point: verify + url = stripTemplateFromURL(url); + + // Determine if we are under the admin UI. + if ( url.startsWith(ADMIN_PREFIX) || + url.startsWith(WORKSPACE_PREFIX) ) { + return ContentItem.DRAFT; + } else { + return ContentItem.LIVE; + } + } + + /** + * Return the content item at the specified path, or null + * if no such item exists. The path is interpreted as a series + * of folders; for example, "/foo/bar/baz" will look for + * an item named "baz" in a folder named "bar" in a folder named "foo" + * under the specified root folder. + * + * @param url the URL to the item + * @param rootFolder The root folder where the item search will start + * @return the item on success, null if no such item exists + * @pre rootFolder != null + * @pre url != null + */ + public ContentItem getItem(String url, Folder rootFolder) { + + StringTokenizer tokenizer = new StringTokenizer(url, "/"); + String name = null; + Folder oldFolder = null; + + while(rootFolder != null && tokenizer.hasMoreTokens()) { + name = tokenizer.nextToken(); + oldFolder = rootFolder; + rootFolder = (Folder)rootFolder.getItem(name, true); + } + + if(tokenizer.hasMoreTokens()) { + // failure + s_log.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"); + 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 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(); + } + + + // 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(); + url + .append(section.getPath()) + .append("/" + CMSDispatcher.PREVIEW) + .append("/") + .append(templateURLFrag) + .append(item.getPath()); + return url.toString(); + } + + /** + * Generates a URL for a content item. + * + * @param itemId The item ID + * @param name The name of the content page + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "LIVE" or "DRAFT" + * @return The URL of the item + * @pre context != null + */ + public String generateItemURL(PageState state, BigDecimal itemId, + String name, ContentSection section, + String context) { + return generateItemURL(state, itemId, name, section, context, null); + } + + /** + * Generates a URL for a content item. + * + * @param itemId The item ID + * @param name The name of the content page + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" + * @return The URL of the item + * @pre context != null + */ + public String generateItemURL(PageState state, BigDecimal itemId, + String name, ContentSection section, + String context, String templateContext) { + if ( ContentItem.DRAFT.equals(context) ) { + return generateDraftURL(itemId, section); + } else if (ContentItem.LIVE.equals(context)) { + ContentItem item = new ContentItem(itemId); + return generateLiveURL(item, section, templateContext); + } else if (CMSDispatcher.PREVIEW.equals(context)) { + ContentItem item = new ContentItem(itemId); + return generatePreviewURL(item, section, templateContext); + } else { + throw new RuntimeException( (String) GlobalizationUtil.globalize("cms.dispatcher.unknown_context").localize() + context); + } + } + + /** + * Generates a URL for a content item. + * + * @param item The item + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "LIVE" or "DRAFT" + * @return The URL of the item + * @see #getCurrentContext + */ + public String generateItemURL ( + PageState state, ContentItem item, ContentSection section, String context + ) { + return generateItemURL(state, item, section, context, null); + } + /** + * Generates a URL for a content item. + * + * @param item The item + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" + * @return The URL of the item + * @see #getCurrentContext + */ + public String generateItemURL ( + PageState state, ContentItem item, ContentSection section, + String context, String templateContext + ) { + if (ContentItem.LIVE.equals(context)) { + return generateLiveURL(item, section, templateContext); + } else if (ContentItem.DRAFT.equals(context)) { + return generateDraftURL(item.getID(), 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); + } + } + + /** + * Return a master page based on page state (and content section). + * + * @param item The content item + * @param request The HTTP request + */ + public CMSPage getMasterPage(ContentItem item, HttpServletRequest request) + throws ServletException { + + if ( s_masterP == null ) { + s_masterP = new MasterPage(); + s_masterP.init(); + } + + return s_masterP; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.off index c05bed15a..2d39a421f 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.off @@ -48,12 +48,8 @@ import com.arsdigita.cms.ui.lifecycle.ItemLifecycleAdminPane; import com.arsdigita.cms.ui.revision.ItemRevisionAdminPane; import com.arsdigita.cms.ui.templates.ItemTemplates; import com.arsdigita.cms.ui.workflow.ItemWorkflowAdminPane; -import com.arsdigita.cms.util.GlobalizationUtil; -import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.kernel.ui.ACSObjectSelectionModel; -import com.arsdigita.persistence.OID; -import com.arsdigita.ui.DebugPanel; import com.arsdigita.util.Assert; import com.arsdigita.xml.Document; import com.arsdigita.xml.Element; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java similarity index 58% rename from ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java.off rename to ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java index 19906bd97..9ca65ca9d 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitSelector.java @@ -25,20 +25,24 @@ import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.SimpleContainer; import com.arsdigita.bebop.SingleSelectionModel; -import org.librecms.contentsection.ContentType; import com.arsdigita.cms.ItemSelectionModel; import com.arsdigita.xml.Element; import org.apache.logging.log4j.LogManager; -import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentType; +import org.librecms.contenttypes.AuthoringKitInfo; +import org.librecms.contenttypes.ContentTypeInfo; +import org.librecms.contenttypes.ContentTypesManager; import java.util.List; +import java.util.Objects; /** * Selects a component based on content type. Helper class for {@link @@ -51,9 +55,9 @@ public abstract class AuthoringKitSelector extends SimpleContainer { private static final Logger LOGGER = LogManager.getLogger( AuthoringKitSelector.class); - private Map m_comps; - private MapComponentSelectionModel m_sel; - private List m_types; + private final Map components; + private final MapComponentSelectionModel selectionModel; + private final List types; /** * Construct a new AuthoringKitSelector. Load all the possible authoring @@ -64,45 +68,52 @@ public abstract class AuthoringKitSelector extends SimpleContainer { * * @pre itemModel != null */ - public AuthoringKitSelector(final SingleSelectionModel model) { + public AuthoringKitSelector(final SingleSelectionModel model) { super(); - m_comps = new HashMap(); - m_sel = new MapComponentSelectionModel(model, m_comps); + components = new HashMap<>(); + selectionModel = new MapComponentSelectionModel<>(model, components); - m_types = ContentType.getAllContentTypes(); - if (m_types.isEmpty()) { + + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentTypesManager typesManager = cdiUtil.findBean( + ContentTypesManager.class); + types = typesManager.getAvailableContentTypes(); + + if (types.isEmpty()) { throw new RuntimeException("No Content Types were found."); } } // Overloaded add methods @Override - public void add(Component c) { + public void add(final Component component) { throw new UnsupportedOperationException(); } // Overloaded add methods @Override - public void add(Component c, int constraints) { + public void add(final Component component, final int constraints) { throw new UnsupportedOperationException(); } /** * Instantiate all the authoring kit wizards. The child class should call - * this method after it is done with initialization. + * this method after it is done with initialisation. */ protected void processKit() { - while (m_types.next()) { - ContentType type = m_types.getContentType(); - AuthoringKit kit = type.getAuthoringKit(); + for (final ContentTypeInfo type : types) { + final AuthoringKitInfo kit = type.getAuthoringKit(); if (kit != null) { - Component c = instantiateKitComponent(kit, type); - if (c != null) { - super.add(c); - m_comps.put(type.getID(), c); - LOGGER.info("Added component " + c + " for " - + type.getAssociatedObjectType()); + final Component component = instantiateKitComponent(kit, type); + if (component != null) { + super.add(component); + components.put(type.getContentItemClass().getName(), + component); + LOGGER.info("Added component {} for {}", + Objects.toString(component), + type.getContentItemClass().getName()); } } } @@ -115,36 +126,40 @@ public abstract class AuthoringKitSelector extends SimpleContainer { * * @param kit for this kit * @param type for this type + * + * @return */ protected abstract Component instantiateKitComponent( - AuthoringKit kit, ContentType type); + final AuthoringKitInfo kit, final ContentTypeInfo type); /** - * @param id The content type id + * + * @param typeClass * * @return The component the given type id */ - public Component getComponent(BigDecimal id) { - return (Component) m_comps.get(id); + public Component getComponent(final String typeClass) { + return components.get(typeClass); } /** * @return The selection model used by this wizard */ - public MapComponentSelectionModel getComponentSelectionModel() { - return m_sel; + public MapComponentSelectionModel getComponentSelectionModel() { + return selectionModel; } // Choose the right component and run it - public void generateXML(PageState state, Element parent) { + @Override + public void generateXML(final PageState state, final Element parent) { if (isVisible(state)) { - Component c = m_sel.getComponent(state); - if (c == null) { + final Component component = selectionModel.getComponent(state); + if (component == null) { throw new IllegalStateException("No component for " - + m_sel - .getSelectedKey(state)); + + selectionModel + .getSelectedKey(state)); } - c.generateXML(state, parent); + component.generateXML(state, parent); } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.off new file mode 100755 index 000000000..0424c008f --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.off @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.GridPanel; +import com.arsdigita.bebop.List; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Resettable; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.SingleSelectionModel; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.ChangeEvent; +import com.arsdigita.bebop.event.ChangeListener; +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.ContentSection; +import org.librecms.contentsection.ContentType; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.ui.ContentItemPage; +import com.arsdigita.cms.ui.item.ItemWorkflowRequestLocal; +import com.arsdigita.cms.ui.workflow.AssignedTaskSection; +import com.arsdigita.cms.ui.workflow.AssignedTaskTable; +import com.arsdigita.cms.ui.workflow.TaskFinishForm; +import com.arsdigita.cms.ui.workflow.TaskRequestLocal; +import com.arsdigita.cms.ui.workflow.WorkflowRequestLocal; +import com.arsdigita.cms.util.GlobalizationUtil; +import com.arsdigita.cms.workflow.CMSTask; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.persistence.metadata.MetadataRoot; +import com.arsdigita.persistence.metadata.ObjectType; +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.log4j.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collections; + +/** + * This class represents a single authoring kit. The wizard accepts a + * {@link ContentType} in the constructor; it then extracts + * the {@link AuthoringKit} for the content type, and creates the + * components for all the steps in the kit.

+ * + * Note that the individual authoring kit steps must provide the following + * constructor: + * + *

+ * public TheClass(ItemSelectionModel model, AuthoringKitWizard parent) { ... }
+ * 
+ * + * This constructor will be called when the component is automatically + * instantiated by the AuthoringKitWizard.

+ * + * @version $Id: AuthoringKitWizard.java 2140 2011-01-16 12:04:20Z pboy $ + */ +public class AuthoringKitWizard extends LayoutPanel implements Resettable { + + /** Private Logger instance for this class */ + private static final Logger s_log = Logger.getLogger( + AuthoringKitWizard.class); + private static Class[] s_args = new Class[]{ + ItemSelectionModel.class, + AuthoringKitWizard.class + }; + private static Class[] s_userDefinedArgs = new Class[]{ + ItemSelectionModel.class, + AuthoringKitWizard.class, + ContentType.class + }; + //private static final ArrayList s_assets = new ArrayList(); + private static final java.util.List s_assets = new + ArrayList(); + private final Object[] m_vals; + private final ContentType m_type; + private final AuthoringKit m_kit; + private final ItemSelectionModel m_sel; + private final WorkflowRequestLocal m_workflow; + private final AssignedTaskTable m_tasks; + private final SequentialMap m_labels; + private final List m_list; + private String m_defaultKey; + private final GridPanel m_left; + private final ModalPanel m_body; + private final SimpleContainer m_steps; + private final TaskFinishForm m_taskFinishForm; + /** + * The name of the state parameter that determines whether the + * wizard is in item creation mode or item editing mode. + */ + public static final String IS_EDITING = "is_edit"; + /** + * The key for the item creation step. + */ + public static final String CREATION = "_creation_"; + + /** + * Construct a new AuthoringKitWizard. Add all the steps in the + * authoring kit to the wizard. + * + * @param type The content type of the items that this wizard will + * handle + * + * @param itemModel The item selection model which will supply + * this wizard with the content item object + */ + public AuthoringKitWizard(final ContentType type, + final ItemSelectionModel model) { + if (s_log.isDebugEnabled()) { + s_log.debug("Authoring kit wizard for type " + type + " " + + "undergoing creation"); + } + + m_type = type; + m_kit = type.getAuthoringKit(); + m_sel = model; + m_vals = new Object[]{m_sel, this}; + m_workflow = new ItemWorkflowRequestLocal(); + m_labels = new SequentialMap(); + + m_left = new GridPanel(1); + setLeft(m_left); + + m_tasks = new AssignedTaskTable(m_workflow); + + m_left.add(new AssignedTaskSection(m_workflow, m_tasks)); + + final Section stepSection = new Section(gz("cms.ui.authoring.steps")); + m_left.add(stepSection); + + m_list = new List(); + stepSection.setBody(m_list); + + m_list.setListData(m_labels); + m_list.setCellRenderer(new ListCellRenderer() { + + public Component getComponent( + List list, + PageState state, + Object value, + String key, + int index, + boolean isSelected) { + Label l = null; + if (value instanceof GlobalizedMessage) { + l = new Label((GlobalizedMessage) value); + } else { + l = new Label((String) value); + } + if (isSelected) { + l.setFontWeight(Label.BOLD); + return l; + } + return new ControlLink(l); + } + }); + + m_body = new ModalPanel(); + setBody(m_body); + + m_steps = new SimpleContainer(); + m_body.add(m_steps); + m_body.setDefault(m_steps); + + final AuthoringStepCollection steps = m_kit.getSteps(); + + if (Assert.isEnabled()) { + Assert.isTrue(!steps.isEmpty(), + "The authoring kit for " + type.getID() + " " + + "(java class " + type.getClassName() + ") " + + "has no steps."); + } + + StepComponent panel = null; + while (steps.next()) { + final AuthoringStep step = steps.getAuthoringStep(); + final String key = step.getID().toString(); + + if (m_defaultKey == null) { + m_defaultKey = key; + } + + /** + * The "label" and "description" are only here for backwards + * compatibility + */ + final String label = step.getLabel(); + final String labelKey = step.getLabelKey(); + final String labelBundle = step.getLabelBundle(); + final String description = step.getDescription(); + final String descriptionKey = step.getDescription(); + final String descriptionBundle = step.getDescription(); + final String str = step.getComponent(); + + if (panel != null) { + panel.setNextStepKey(step.getID()); + } + panel = new StepComponent(step.getID()); + m_steps.add(panel); + final Component comp; + + if (str.equals("com.arsdigita.cms.ui.authoring." + + "SecondaryPageEditDynamic") + || str.equals("com.arsdigita.cms.ui.authoring." + + "PageEditDynamic")) { + comp = instantiateUserDefinedStep(str, m_type); + } else { + comp = instantiateStep(str); + } + panel.add(comp); + // XXX should be optional + if (comp instanceof AuthoringStepComponent) { + ((AuthoringStepComponent) comp).addCompletionListener( + new StepCompletionListener()); + } + + GlobalizedMessage gzLabel = null; + if (labelKey != null) { + if (labelBundle == null) { + gzLabel = gz(labelKey); + } else { + gzLabel = new GlobalizedMessage(labelKey, labelBundle); + } + } + m_labels.put(key, + gzLabel == null ? (Object) label : (Object) gzLabel); + } + + ObjectType thisType = MetadataRoot.getMetadataRoot().getObjectType(type. + getAssociatedObjectType()); + Collection skipSteps = ContentSection.getConfig().getAssetStepsToSkip( + type); + Iterator it = skipSteps.iterator(); + if (s_log.isDebugEnabled()) { + while (it.hasNext()) { + s_log.debug("skip step " + it.next()); + } + } + //Iterator assets = s_assets.iterator(); + Iterator assets = s_assets.iterator(); + while (assets.hasNext()) { + //Object[] data = (Object[]) assets.next(); + final AssetStepEntry data = assets.next(); + //String baseObjectType = (String) data[0]; + final String baseObjectType = data.getBaseDataObjectType(); + //Class step = (Class) data[1]; + Class step = data.getStep(); + s_log.debug("possibly adding asset step " + step.getName()); + if (!skipSteps.contains(step.getName())) { + //GlobalizedMessage label = (GlobalizedMessage) data[2]; + GlobalizedMessage label = data.getLabel(); + + if (!thisType.isSubtypeOf(baseObjectType)) { + continue; + } + + if (panel != null) { + panel.setNextStepKey(step); + } + panel = new StepComponent(step); + m_steps.add(panel); + + Component comp = instantiateStep(step.getName()); + if (comp instanceof AuthoringStepComponent) { + ((AuthoringStepComponent) comp).addCompletionListener( + new StepCompletionListener()); + } + panel.add(comp); + + m_labels.put(step, label); + } + } + + m_list.addChangeListener(new StepListener()); + + m_taskFinishForm = new TaskFinishForm(new TaskSelectionRequestLocal()); + m_body.add(m_taskFinishForm); + + m_body.connect(m_tasks, 2, m_taskFinishForm); + m_body.connect(m_taskFinishForm); + + m_taskFinishForm.addProcessListener(new FormProcessListener() { + + public final void process(final FormSectionEvent e) + throws FormProcessException { + final PageState state = e.getPageState(); + + m_tasks.getRowSelectionModel().clearSelection(state); + } + }); + } + + /** + * + */ + private final class StepListener implements ChangeListener { + + public final void stateChanged(final ChangeEvent e) { + final PageState state = e.getPageState(); + final String key = m_list.getSelectedKey(state).toString(); + + final Iterator iter = m_steps.children(); + + while (iter.hasNext()) { + final StepComponent step = (StepComponent) iter.next(); + + if (step.getStepKey().toString().equals(key)) { + step.setVisible(state, true); + } else { + step.setVisible(state, false); + } + } + } + } + + /** + * + */ + private final class StepCompletionListener implements ActionListener { + + public final void actionPerformed(final ActionEvent e) { + final PageState state = e.getPageState(); + if (ContentItemPage.isStreamlinedCreationActive(state)) { + final String key = m_list.getSelectedKey(state).toString(); + + final Iterator iter = m_steps.children(); + + while (iter.hasNext()) { + final StepComponent step = (StepComponent) iter.next(); + if (step.getStepKey().toString().equals(key)) { + Object nextStep = step.getNextStepKey(); + if (nextStep != null) { + m_list.getSelectionModel().setSelectedKey(state, + nextStep. + toString()); + } + } + } + } + } + } + + /** + * + * @param page + */ + @Override + public final void register(final Page page) { + super.register(page); + + final Iterator iter = m_steps.children(); + + while (iter.hasNext()) { + final StepComponent child = (StepComponent) iter.next(); + + page.setVisibleDefault(child, false); + } + + page.addActionListener(new ActionListener() { + + public final void actionPerformed(final ActionEvent e) { + final PageState state = e.getPageState(); + + if (state.isVisibleOnPage(AuthoringKitWizard.this)) { + final SingleSelectionModel model = + m_list.getSelectionModel(); + + if (!model.isSelected(state)) { + model.setSelectedKey(state, m_defaultKey); + } + } + } + }); + } + + /** + * + * @param baseObjectType + * @param step + * @param label + * @param description + * @param sortKey + */ + public static void registerAssetStep(String baseObjectType, + Class step, + GlobalizedMessage label, + GlobalizedMessage description, + int sortKey) { + // cg - allow registered steps to be overridden by registering a step with the same label + // this is a bit of a hack used specifically for creating a specialised version of image + // step. There is no straightforward way of preventing the original image step from being + // registered, but I needed the image step to use a different step class if the specialised + // image step application was loaded. Solution is to ensure initialiser in new project + // runs after original ccm-ldn-image-step initializer and override the registered step here + s_log.debug( + "registering asset step - label: " + + label.localize() + + " step class: " + + step.getName()); + + //Iterator assets = s_assets.iterator(); + Iterator assets = s_assets.iterator(); + while (assets.hasNext()) { + //Object[] data = (Object[]) assets.next(); + //String thisObjectType = (String) data[0]; + //GlobalizedMessage thisLabel = (GlobalizedMessage) data[2]; + + final AssetStepEntry data = assets.next(); + String thisObjectType = data.getBaseDataObjectType(); + GlobalizedMessage thisLabel = data.getLabel(); + + /** + * jensp 2011-11-14: The code above was only testing for the same + * label, but not for the same object type. I don't think that + * this was indented since this made it impossible to attach the + * same step to different object types. + * The orginal line was + * if (thisLabel.localize().equals(label.localize())) { + * + */ + if ((thisObjectType.equals(baseObjectType)) + && (thisLabel.localize().equals(label.localize()))) { + s_log.debug( + "registering authoring step with same label as previously registered step"); + s_assets.remove(data); + break; + } + } + s_assets.add(new AssetStepEntry(baseObjectType, step, label, description, sortKey)); + Collections.sort(s_assets); + //s_assets.add(new Object[]{baseObjectType, step, label, description}); + } + + private static class AssetStepEntry implements Comparable { + private String baseDataObjectType; + private Class step; + private GlobalizedMessage label; + private GlobalizedMessage description; + private Integer sortKey; + + public AssetStepEntry() { + super(); + } + + public AssetStepEntry(final String baseDataObjectType, + final Class step, + final GlobalizedMessage label, + final GlobalizedMessage description, + final Integer sortKey) { + this.baseDataObjectType = baseDataObjectType; + this.step = step; + this.label = label; + this.description = description; + this.sortKey = sortKey; + } + + public String getBaseDataObjectType() { + return baseDataObjectType; + } + + public void setBaseDataObjectType(final String baseDataObjectType) { + this.baseDataObjectType = baseDataObjectType; + } + + public Class getStep() { + return step; + } + + public void setStep(final Class step) { + this.step = step; + } + + public GlobalizedMessage getLabel() { + return label; + } + + public void setLabel(final GlobalizedMessage label) { + this.label = label; + } + + public GlobalizedMessage getDescription() { + return description; + } + + public void setDescription(final GlobalizedMessage description) { + this.description = description; + } + + public Integer getSortKey() { + return sortKey; + } + + public void setSortKey(final Integer sortKey) { + this.sortKey = sortKey; + } + + public int compareTo(final AssetStepEntry other) { + if (sortKey == other.getSortKey()) { + return step.getName().compareTo(other.getStep().getName()); + } else { + return sortKey.compareTo(other.getSortKey()); + } + } + } + + /** + * @return The content type handled by this wizard + */ + public ContentType getContentType() { + return m_type; + } + + /** + * + * @return + */ + public List getList() { + return m_list; + } + + /** + * @return The authoring kit which is represented by this wizard + */ + public AuthoringKit getAuthoringKit() { + return m_kit; + } + + /** + * @return The ItemSelectionModel used by the steps in this wizard + */ + public ItemSelectionModel getItemSelectionModel() { + return m_sel; + } + + /** + * Instantiate the specified authoring kit step. Will throw a + * RuntimeException on failure. + * + * @param className The Java class name of the step + */ + protected Component instantiateStep(String name) { + if (s_log.isDebugEnabled()) { + s_log.debug("Instantiating kit wizard '" + name + "' with " + + "arguments " + s_args); + } + + Object[] vals; + try { + // Get the creation component + Class createClass = Class.forName(name); + Constructor constr = createClass.getConstructor(s_args); + Component c = (Component) constr.newInstance(m_vals); + return c; + } catch (Exception e) { + Throwable cause = e.getCause(); // JDK1.4 + if (cause == null) { + cause = e; + } + throw new UncheckedWrapperException( + "Failed to instantiate authoring kit component " + m_kit. + getCreateComponent() + ": " + e.getMessage(), cause); + } + } + + /** + * Instantiate the specified authoring kit step for a user defined content type. + * Will throw a + * RuntimeException on failure. + * + * @param className The Java class name of the step + * @param description The step description, which for dynamically generated + * steps will be the object type which originally defined the step. + */ + protected Component instantiateUserDefinedStep(String name, + ContentType originatingType) { + Object[] vals; + try { + // Get the creation component + Class createClass = Class.forName(name); + Constructor constr = createClass.getConstructor(s_userDefinedArgs); + Object[] userDefinedVals = + new Object[]{m_sel, this, originatingType}; + Component c = (Component) constr.newInstance(userDefinedVals); + return c; + } catch (ClassNotFoundException cnfe) { + throw new UncheckedWrapperException(cnfe); + } catch (NoSuchMethodException nsme) { + throw new UncheckedWrapperException(nsme); + } catch (InstantiationException ie) { + throw new UncheckedWrapperException(ie); + } catch (IllegalAccessException iae) { + throw new UncheckedWrapperException(iae); + } catch (InvocationTargetException ite) { + throw new UncheckedWrapperException(ite); + } + } + +// /** +// * Tell the parent page to redirect back to its return URL +// * +// * @param state The page state +// */ +// public static void redirectBack(PageState state) { +// ((ContentItemPage)state.getPage()).redirectBack(state); +// } + /** + * Reset the state of this wizard + */ + public final void reset(PageState state) { + m_list.setSelectedKey(state, m_defaultKey); + } + + private final class StepComponent extends SimpleContainer { + + private final Object m_key; + private Object m_nextKey; + + + public StepComponent(Object key) { + m_key = key; + } + + public Object getStepKey() { + return m_key; + } + + public Object getNextStepKey() { + return m_nextKey; + } + + public void setNextStepKey(Object nextKey) { + m_nextKey = nextKey; + } + } + + private final class TaskSelectionRequestLocal extends TaskRequestLocal { + + protected final Object initialValue(final PageState state) { + final String id = m_tasks.getRowSelectionModel().getSelectedKey( + state).toString(); + + return new CMSTask(new BigDecimal(id)); + } + } + + protected final static GlobalizedMessage gz(final String key) { + return GlobalizationUtil.globalize(key); + } + + protected final static String lz(final String key) { + return (String) gz(key).localize(); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.off index 0c6b267c1..64d0db1ef 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.off @@ -25,12 +25,21 @@ 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.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; @@ -49,7 +58,7 @@ import java.math.BigDecimal; public class WizardSelector extends AuthoringKitSelector implements Resettable { - private ItemSelectionModel m_itemSel; + private ItemSelectionModel itemSelectionModel; /** * Construct a new WizardSelector. Load all the possible authoring kits @@ -63,45 +72,48 @@ public class WizardSelector extends AuthoringKitSelector * * @pre itemModel != null */ - public WizardSelector(ItemSelectionModel model, - SingleSelectionModel typeModel) { + public WizardSelector(final ItemSelectionModel model, + final SingleSelectionModel typeModel) { super(typeModel); - m_itemSel = model; + itemSelectionModel = model; super.processKit(); } /** * Get the wizard for the given kit. + * @param kit + * @param type + * @return */ - public Component instantiateKitComponent(AuthoringKit kit, - ContentType type) { + public Component instantiateKitComponent(final AuthoringKitInfo kit, + final ContentTypeInfo type) { - ItemSelectionModel itemModel = new + final ItemSelectionModel itemModel = new ItemSelectionModel(type, - (BigDecimalParameter)m_itemSel.getStateParameter()); + (LongParameter)itemSelectionModel.getStateParameter()); - AuthoringKitWizard w = new AuthoringKitWizard(type, itemModel); - return w; + final AuthoringKitWizard wizard = new AuthoringKitWizard(type, itemModel); + return wizard; } /** * @return The item selection model used by this wizard */ public ItemSelectionModel getSelectionModel() { - return m_itemSel; + return itemSelectionModel; } // Determine the current wizard private Component getCurrentWizard(PageState state) { // Get the current item and extract its content type - if(!m_itemSel.isSelected(state)) + if(!itemSelectionModel.isSelected(state)) throw new RuntimeException( (String) GlobalizationUtil.globalize( "cms.ui.authoring.missing_item_id") .localize()); ContentItem item = - (ContentItem)m_itemSel.getSelectedObject(state); + (ContentItem)itemSelectionModel.getSelectedObject(state); ContentType type = item.getContentType(); BigDecimal typeId; diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java index 3ea1ae711..ae73dea83 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java @@ -34,12 +34,13 @@ import com.arsdigita.bebop.table.TableColumnModel; import com.arsdigita.bebop.table.TableModel; import com.arsdigita.bebop.table.TableModelBuilder; import com.arsdigita.cms.ui.CMSContainer; -import com.arsdigita.ui.admin.GlobalizationUtil; +import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.util.LockableImpl; import org.libreccm.categorization.Category; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.security.PermissionChecker; +import org.librecms.CmsConstants; import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSectionRepository; import org.librecms.contentsection.privileges.ItemPrivileges; @@ -298,8 +299,8 @@ public class ContentSectionContainer extends CMSContainer { // depending on configuration of the location column! Integer colNo = 0; - Label emptyView = new Label(GlobalizationUtil - .globalize("cms.ui.contentcenter.section")); + Label emptyView = new Label(new GlobalizedMessage( + "cms.ui.contentcenter.section", CmsConstants.CMS_BUNDLE)); emptyView.setFontWeight(Label.ITALIC); setEmptyView(emptyView); @@ -309,18 +310,18 @@ public class ContentSectionContainer extends CMSContainer { TableColumnModel columnModel = getColumnModel(); // prepare column headers - Label sectionHead = new Label(GlobalizationUtil - .globalize("cms.ui.contentcenter.section")); - sectionHead.setHint(GlobalizationUtil - .globalize("cms.ui.contentcenter.section_hint")); - Label locationHead = new Label(GlobalizationUtil - .globalize("cms.ui.contentcenter.location")); - locationHead.setHint(GlobalizationUtil - .globalize("cms.ui.contentcenter.location_hint")); - Label actionHead = new Label(GlobalizationUtil - .globalize("cms.ui.contentcenter.action")); - actionHead.setHint(GlobalizationUtil - .globalize("cms.ui.contentcenter.action_hint")); + Label sectionHead = new Label(new GlobalizedMessage( + "cms.ui.contentcenter.section", CmsConstants.CMS_BUNDLE)); + sectionHead.setHint(new GlobalizedMessage( + "cms.ui.contentcenter.section_hint", CmsConstants.CMS_BUNDLE)); + Label locationHead = new Label(new GlobalizedMessage( + "cms.ui.contentcenter.location", CmsConstants.CMS_BUNDLE)); + locationHead.setHint(new GlobalizedMessage( + "cms.ui.contentcenter.location_hint", CmsConstants.CMS_BUNDLE)); + Label actionHead = new Label(new GlobalizedMessage( + "cms.ui.contentcenter.action", CmsConstants.CMS_BUNDLE)); + actionHead.setHint(new GlobalizedMessage( + "cms.ui.contentcenter.action_hint", CmsConstants.CMS_BUNDLE)); //TableColumn contentSectionColumn = new TableColumn(colNo, COLUMN_SECTION); TableColumn contentSectionColumn = new TableColumn( @@ -606,7 +607,7 @@ public class ContentSectionContainer extends CMSContainer { section.getRootDocumentsFolder())) { return new Link(section.getLabel(), - generateURL(section.getPrimaryUrl() + "/")); + generateURL(section.getPrimaryUrl())); } else { //return new Label(section.getName(), false); // return null; // Produces a NPE although it shouldn't and diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemWorkflowRequestLocal.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemWorkflowRequestLocal.java new file mode 100755 index 000000000..a7d3ad7e2 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemWorkflowRequestLocal.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.item; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.cms.CMS; + +import org.librecms.contentsection.ContentItem; + +import com.arsdigita.cms.ui.workflow.WorkflowRequestLocal; + +import org.libreccm.workflow.Workflow; + +public class ItemWorkflowRequestLocal extends WorkflowRequestLocal { + + + @Override + protected final Object initialValue(final PageState state) { + final ContentItem item = CMS.getContext().getContentItem(); + + return item.getWorkflow(); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskController.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskController.java new file mode 100644 index 000000000..25f60747f --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskController.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.table.RowData; +import com.arsdigita.kernel.KernelConfig; + +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.security.Shiro; +import org.libreccm.security.User; +import org.libreccm.workflow.UserTask; +import org.libreccm.workflow.UserTaskRepository; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowManager; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.transaction.Transactional; + +/** + * Controller for the assigned task components. + * + * @author Jens Pelzetter + */ +@RequestScoped +public class AssignedTaskController { + + @Inject + private WorkflowManager workflowManager; + + @Inject + private UserTaskRepository userTaskRepo; + + @Inject + private Shiro shiro; + + @Inject + private ConfigurationManager confManager; + + private Locale defaultLocale; + + @PostConstruct + private void init() { + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + defaultLocale = kernelConfig.getDefaultLocale(); + } + + + @Transactional(Transactional.TxType.REQUIRED) + public List> getAssignedTasks(final Workflow workflow) { + final User user = shiro.getUser(); + final List tasks = userTaskRepo.getAssignedTasks(user, + workflow); + + return tasks + .stream() + .map(task -> createRowData(task)) + .collect(Collectors.toList()); + + } + + private RowData createRowData(final UserTask task) { + + + final RowData rowData = new RowData<>(3); + + rowData.setRowKey(task.getTaskId()); + + // Change when Workflow forms provide fields to enter localised label. + rowData.setColData(0, task.getLabel().getValue(defaultLocale)); + + if (task.isLocked()) { + rowData.setColData(1, task.getLockingUser().getName()); + } else { + rowData.setColData(1,""); + } + + rowData.setColData(2, ""); + + + return rowData; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskSection.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskSection.java new file mode 100755 index 000000000..3ffe736ef --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskSection.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.toolbox.ui.ActionGroup; +import com.arsdigita.toolbox.ui.Section; +import com.arsdigita.web.Web; + +import org.libreccm.workflow.Workflow; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.apache.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.Shiro; +import org.libreccm.workflow.Task; +import org.libreccm.workflow.UserTask; +import org.libreccm.workflow.UserTaskRepository; +import org.libreccm.workflow.WorkflowConstants; +import org.libreccm.workflow.WorkflowManager; +import org.libreccm.workflow.WorkflowRepository; +import org.librecms.CmsConstants; +import org.librecms.workflow.CmsTask; +import org.librecms.workflow.CmsTaskType; + +import java.util.List; + +/** + * @author unknown + * @author Sören Bernstein + * @author Jens Pelzetter + */ +public final class AssignedTaskSection extends Section { + + private final WorkflowRequestLocal m_workflow; + private final WorkflowFacade m_facade; + + public AssignedTaskSection(final WorkflowRequestLocal workflow, + final Component subject) { + super(gz("cms.ui.workflow.task.assigned")); + + m_workflow = workflow; + m_facade = new WorkflowFacade(m_workflow); + + final ActionGroup group = new ActionGroup(); + setBody(group); + + group.setSubject(subject); + group.addAction(new RestartLink()); + //jensp 2014-06-06 Removed this two links because the funcationality they provide should + //be accessible from this place. + //group.addAction(new LockLink()); + //group.addAction(new UnlockLink()); + } + + @Override + public final boolean isVisible(final PageState state) { + return m_workflow.getWorkflow(state) != null; + } + + private class RestartLink extends ActionLink { + + RestartLink() { + super(new Label(gz("cms.ui.workflow.restart_stopped_workflow"))); + + setClassAttr("restartWorkflowLink"); + addActionListener(new Listener()); + } + + @Override + public final boolean isVisible(final PageState state) { + return m_facade.workflowState(state, WorkflowConstants.INIT) + || m_facade.workflowState(state, WorkflowConstants.STOPPED); + } + + private class Listener implements ActionListener { + + @Override + public final void actionPerformed(final ActionEvent event) { + m_facade.restartWorkflow(event.getPageState()); + } + + } + + } + + private class LockLink extends ActionLink { + + LockLink() { + super(new Label(gz("cms.ui.workflow.task.assigned.lock_all"))); + + addActionListener(new Listener()); + } + + @Override + public final boolean isVisible(final PageState state) { + return m_facade.workflowState(state, WorkflowConstants.STARTED) + && m_facade.tasksExist(state) + && !m_facade.tasksLocked(state); + } + + private class Listener implements ActionListener { + + @Override + public final void actionPerformed(final ActionEvent event) { + m_facade.lockTasks(event.getPageState()); + } + + } + + } + + private class UnlockLink extends ActionLink { + + UnlockLink() { + super(new Label(gz("cms.ui.workflow.task.assigned.unlock_all"))); + + addActionListener(new UnlockLink.Listener()); + } + + @Override + public final boolean isVisible(final PageState state) { + return m_facade.workflowState(state, WorkflowConstants.STARTED) + && m_facade.tasksExist(state) + && m_facade.tasksLocked(state); + } + + private class Listener implements ActionListener { + + @Override + public final void actionPerformed(final ActionEvent event) { + m_facade.unlockTasks(event.getPageState()); + } + + } + + } + + private class WorkflowFacade { + + private final WorkflowRequestLocal m_flow; + private final TaskListRequestLocal m_tasks; + + WorkflowFacade(final WorkflowRequestLocal flow) { + m_flow = flow; + m_tasks = new TaskListRequestLocal(); + } + + private class TaskListRequestLocal extends RequestLocal { + + @Override + protected final Object initialValue(final PageState state) { + final Workflow workflow = m_flow.getWorkflow(state); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final UserTaskRepository userTaskRepo = cdiUtil.findBean( + UserTaskRepository.class); + final Shiro shiro = cdiUtil.findBean(Shiro.class); + return userTaskRepo.findEnabledTasksForWorkflow(shiro.getUser(), + workflow); + } + + @SuppressWarnings("unchecked") + final List getTasks(final PageState state) { + return (ArrayList) get(state); + } + + } + + final void restartWorkflow(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean( + WorkflowManager.class); + final Workflow workflow = m_flow.getWorkflow(state); + workflowManager.start(workflow); + + // Lock tasks if not locked + if (!tasksLocked(state)) { + lockTasks(state); + } + } + + final void lockTasks(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean(WorkflowManager.class); + + for(final UserTask task : m_tasks.getTasks(state)) { + if (relevant(task) && !task.isLocked()) { + workflowManager.lockTask(task); + } + } + } + + final void unlockTasks(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean(WorkflowManager.class); + + for(final UserTask task : m_tasks.getTasks(state)) { + if (relevant(task) && task.isLocked()) { + workflowManager.unlockTask(task); + } + } + } + + final boolean tasksLocked(final PageState state) { + for(final UserTask task : m_tasks.getTasks(state)) { + if (relevant(task) && !task.isLocked()) { + return false; + } + } + + return true; + } + + final boolean workflowState(final PageState state, int processState) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean(WorkflowManager.class); + final Workflow workflow = m_flow.getWorkflow(state); + + return workflowManager.getState(workflow) == processState; + } + + final boolean tasksExist(final PageState state) { + return !m_tasks.getTasks(state).isEmpty(); + } + + private boolean relevant(final UserTask task) { + return true; + +// ToDo +// return task.getTaskType().getID().equals(CMSTaskType.AUTHOR) +// || task.getTaskType().getID().equals(CMSTaskType.EDIT); + } + + } + + protected final static GlobalizedMessage gz(final String key) { + return new GlobalizedMessage(key, CmsConstants.CMS_BUNDLE); + } + + protected final static String lz(final String key) { + return (String) gz(key).localize(); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTable.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTable.java new file mode 100755 index 000000000..848793dbc --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTable.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.TableActionAdapter; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.table.DefaultTableCellRenderer; +import com.arsdigita.bebop.table.TableCellRenderer; + +import org.librecms.workflow.CmsTask; + +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.web.Web; + +import java.math.BigDecimal; + +import org.apache.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.Shiro; +import org.libreccm.security.User; +import org.libreccm.workflow.UserTask; +import org.libreccm.workflow.UserTaskRepository; +import org.libreccm.workflow.WorkflowManager; +import org.librecms.CmsConstants; +import org.librecms.workflow.CmsTaskTypeRepository; + +public final class AssignedTaskTable extends Table { + + public AssignedTaskTable(final WorkflowRequestLocal workflow) { + + super(new AssignedTaskTableModelBuilder(workflow), + new String[]{lz("cms.ui.name"), "", ""}); + + // XXX The string array and setHeader(null) are a product of + // messed up Table behavior. + setEmptyView(new Label(gz("cms.ui.workflow.task.assigned.none"))); + + addTableActionListener(new LockListener()); + + getColumn(1).setCellRenderer(new CellRenderer()); + getColumn(2).setCellRenderer(new DefaultTableCellRenderer(true)); + } + + private static class LockListener extends TableActionAdapter { + + @Override + public final void cellSelected(final TableActionEvent event) { + final int column = event.getColumn(); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final UserTaskRepository userTaskRepo = cdiUtil.findBean( + UserTaskRepository.class); + final WorkflowManager workflowManager = cdiUtil.findBean(WorkflowManager.class); + final Shiro shiro = cdiUtil.findBean(Shiro.class); + + if (column == 1) { + final UserTask task = userTaskRepo.findById((Long) event + .getRowKey()); + final User currentUser = shiro.getUser(); + final User lockingUser = task.getLockingUser(); + if (task.isLocked() + && lockingUser != null + && lockingUser.equals(currentUser)) { + workflowManager.unlockTask(task); + } else { + workflowManager.lockTask(task); + } + } + } + + } + + private class CellRenderer implements TableCellRenderer { + + @Override + public final Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, + final int column) { + // SF patch [ 1587168 ] Show locking user + BoxPanel p = new BoxPanel(); + User lockingUser = (User) value; + if (lockingUser != null) { + final StringBuilder sb = new StringBuilder("Locked by
"); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final Shiro shiro = cdiUtil.findBean(Shiro.class); + if (lockingUser.equals(shiro.getUser())) { + sb.append("you"); + p.add(new ControlLink(new Label( + gz("cms.ui.workflow.task.unlock")))); + } else { + sb.append(lockingUser.getName()); + p.add(new ControlLink(new Label( + gz("cms.ui.workflow.task.takeover")))); + } + p.add(new Label(sb.toString(), false)); + } else { + p.add(new ControlLink( + new Label(gz("cms.ui.workflow.task.lock")))); + } + return p; + } + + } + + protected final static GlobalizedMessage gz(final String key) { + return new GlobalizedMessage(key, CmsConstants.CMS_BUNDLE); + } + + protected final static String lz(final String key) { + return (String) gz(key).localize(); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTableModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTableModelBuilder.java new file mode 100755 index 000000000..60425e78d --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/AssignedTaskTableModelBuilder.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the + * License at http://www.redhat.com/licenses/ccmpl.html. + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language + * governing rights and limitations under the License. + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.table.AbstractTableModelBuilder; +import com.arsdigita.bebop.table.RowData; +import com.arsdigita.bebop.table.TableModel; + + +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.Assert; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowConstants; +import org.libreccm.workflow.WorkflowManager; +import org.librecms.CmsConstants; + +import java.util.Collections; +import java.util.Iterator; + +/** + * + * + */ +class AssignedTaskTableModelBuilder extends AbstractTableModelBuilder { + + private final WorkflowRequestLocal m_workflow; + + public AssignedTaskTableModelBuilder(final WorkflowRequestLocal workflow) { + m_workflow = workflow; + } + + @Override + public TableModel makeModel(final Table table, final PageState state) { + return new Model(m_workflow.getWorkflow(state)); + } + + private static class Model implements TableModel { + + private final Iterator> m_iter; +// private CmsTask m_task; + private RowData rowData; + + Model(final Workflow workflow) { + Assert.exists(workflow, Workflow.class); + + final CdiUtil cdiUtil= CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean(WorkflowManager.class); + + if (workflowManager.getState(workflow) == WorkflowConstants.STARTED) { + final AssignedTaskController controller = cdiUtil.findBean( + AssignedTaskController.class); + m_iter = controller.getAssignedTasks(workflow).iterator(); + } else { + m_iter = Collections.emptyIterator(); + } + } + + @Override + public final int getColumnCount() { + return 3; + } + + @Override + public final boolean nextRow() { + if (m_iter.hasNext()) { + rowData = m_iter.next(); + return true; + } else { + return false; + } + } + + @Override + public final Object getKeyAt(final int column) { + return rowData.getRowKey(); + } + + @Override + public final Object getElementAt(final int column) { + switch (column) { + case 0: + return rowData.getColData(0); + case 1: + return rowData.getColData(1); + case 2: + return rowData.getColData(2); + default: + throw new IllegalArgumentException(String.format( + "Illegal column index %d. Valid column index: 0, 1, 2", + column)); + } + } + } + + protected final static GlobalizedMessage gz(final String key) { + return new GlobalizedMessage(key, CmsConstants.CMS_BUNDLE); + } + + protected final static String lz(final String key) { + return (String) gz(key).localize(); + } +} diff --git a/ccm-cms/src/main/java/org/librecms/Cms.java b/ccm-cms/src/main/java/org/librecms/Cms.java index c58cc31cf..3ac2e9384 100644 --- a/ccm-cms/src/main/java/org/librecms/Cms.java +++ b/ccm-cms/src/main/java/org/librecms/Cms.java @@ -47,7 +47,8 @@ import java.util.Properties; instanceForm = ApplicationInstanceForm.class, settingsPane = SettingsPane.class, descBundle = CmsConstants.CONTENT_SECTION_DESC_BUNDLE, - creator = ContentSectionCreator.class + creator = ContentSectionCreator.class, + servletPath = "/templates/servlet/content-section" ) } ) diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionServlet.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionServlet.java index d40e0329b..f3041abb4 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionServlet.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionServlet.java @@ -19,21 +19,50 @@ package org.librecms.contentsection; import com.arsdigita.bebop.Page; +import com.arsdigita.cms.dispatcher.CMSPage; +import com.arsdigita.cms.dispatcher.ContentItemDispatcher; +import com.arsdigita.cms.dispatcher.ItemResolver; +import com.arsdigita.cms.ui.CMSApplicationPage; +import com.arsdigita.dispatcher.AccessDeniedException; +import com.arsdigita.dispatcher.DispatcherHelper; +import com.arsdigita.dispatcher.RequestContext; +import com.arsdigita.templating.PresentationManager; +import com.arsdigita.templating.Templating; +import com.arsdigita.util.Assert; +import com.arsdigita.util.Classes; +import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.web.ApplicationFileResolver; import com.arsdigita.web.BaseApplicationServlet; +import com.arsdigita.web.LoginSignal; +import com.arsdigita.web.Web; +import com.arsdigita.web.WebConfig; +import com.arsdigita.xml.Document; + import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; + import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.Shiro; import org.libreccm.web.CcmApplication; import org.librecms.CmsConstants; +import org.librecms.contentsection.privileges.ItemPrivileges; +import org.librecms.lifecycle.Lifecycle; + +import java.util.Date; + +import javax.servlet.RequestDispatcher; /* * This servlet will maybe removed. Our current plan is to integrate the navigation @@ -77,7 +106,7 @@ public class ContentSectionServlet extends BaseApplicationServlet { private static final long serialVersionUID = 8061725145564728637L; private static final Logger LOGGER = LogManager.getLogger( - ContentSectionServlet.class); + ContentSectionServlet.class); /** * Literal for the prefix (in url) for previewing items @@ -97,18 +126,18 @@ public class ContentSectionServlet extends BaseApplicationServlet { private static final String CACHE_KEY_DELIMITER = "%"; public static final String CONTENT_ITEM - = "com.arsdigita.cms.dispatcher.item"; + = "com.arsdigita.cms.dispatcher.item"; public static final String CONTENT_SECTION - = "com.arsdigita.cms.dispatcher.section"; + = "com.arsdigita.cms.dispatcher.section"; - //ToDo: private final ContentItemDispatcher m_disp = new ContentItemDispatcher(); - public static Map s_itemResolverCache = Collections - .synchronizedMap(new HashMap()); + private final ContentItemDispatcher m_disp = new ContentItemDispatcher(); + private static Map itemResolverCache = Collections + .synchronizedMap(new HashMap<>()); private static Map s_itemURLCacheMap = null; /** * Whether to cache the content items */ - private static final boolean s_cacheItems = true; +// private static final boolean s_cacheItems = true; // NEW STUFF here used to process the pages in this servlet /** * URL (pathinfo) -> Page object mapping. Based on it (and the http request @@ -133,6 +162,7 @@ public class ContentSectionServlet extends BaseApplicationServlet { * defaults are used. * * @param config + * * @throws javax.servlet.ServletException */ @Override @@ -154,12 +184,12 @@ public class ContentSectionServlet extends BaseApplicationServlet { String resolverName = config.getInitParameter("file-resolver"); //ToDo -// if (resolverName == null) { -// m_resolver = Web.getConfig().getApplicationFileResolver(); -// } else { -// m_resolver = (ApplicationFileResolver) Classes.newInstance( -// resolverName); -// } + if (resolverName == null) { + m_resolver = WebConfig.getConfig().getResolver(); + } else { + m_resolver = (ApplicationFileResolver) Classes.newInstance( + resolverName); + } LOGGER.debug("Template path is {} with resolver {}", m_templatePath, m_resolver.getClass().getName()); @@ -176,7 +206,7 @@ public class ContentSectionServlet extends BaseApplicationServlet { * map, used as a cache. * * @param pathInfo url stub for a page to display - * @param page Page object to display + * @param page Page object to display */ private void addPage(final String pathInfo, final Page page) { m_pages.put(pathInfo, page); @@ -185,8 +215,7 @@ public class ContentSectionServlet extends BaseApplicationServlet { /** * Implementation of parent's (abstract) doService method checks HTTP * request to determine whether to handle a content item or other stuff - * which is delegated to jsp templates. - * { + * which is delegated to jsp templates. { * * @see com.arsdigita.web.BaseApplicationServlet#doService * (HttpServletRequest, HttpServletResponse, Application)} @@ -194,6 +223,7 @@ public class ContentSectionServlet extends BaseApplicationServlet { * @param request * @param response * @param app + * * @throws javax.servlet.ServletException * @throws java.io.IOException */ @@ -201,10 +231,433 @@ public class ContentSectionServlet extends BaseApplicationServlet { protected void doService(final HttpServletRequest request, final HttpServletResponse response, final CcmApplication app) - throws ServletException, IOException { + throws ServletException, IOException { + + if (!(app instanceof ContentSection)) { + throw new IllegalArgumentException( + "The provided application instance is not a content section."); + } + + final ContentSection section = (ContentSection) app; + + final RequestContext ctx = DispatcherHelper.getRequestContext(); + final String url = ctx.getRemainingURLPart(); + LOGGER.info("Resolving URL {} and trying as item first."); + final ItemResolver itemResolver = getItemResolver(section); + + String pathInfo = request.getPathInfo(); + + final ContentItem item = getItem(section, pathInfo, itemResolver); + + Assert.exists(pathInfo, "String pathInfo"); + if (pathInfo.length() > 1 && pathInfo.endsWith("/")) { + /* NOTE: ServletAPI specifies, pathInfo may be empty or will + * start with a '/' character. It currently carries a + * trailing '/' if a "virtual" page, i.e. not a real jsp, but + * result of a servlet mapping. But Application requires url + * NOT to end with a trailing '/' for legacy free applications. */ + pathInfo = pathInfo.substring(0, pathInfo.length() - 1); + } + final Page page = (Page) m_pages.get(pathInfo); + // //////////////////////////////////////////////////////////////////// + // Serve the page + // //////////////////////////////////////////////////////////////////// + /* FIRST try new style servlet based service */ + if (page != null) { + + // Check user access. + // checkUserAccess(request, response); // done in individual pages ?? + if (page instanceof CMSPage) { + // backwards compatibility fix until migration completed + final CMSPage cmsPage = (CMSPage) page; + // final RequestContext ctx = DispatcherHelper.getRequestContext(); + cmsPage.init(); + cmsPage.dispatch(request, response, ctx); + } else { + final CMSApplicationPage cmsAppPage = (CMSApplicationPage) page; + cmsAppPage.init(request, response, app); + // Serve the page. + final Document doc = cmsAppPage.buildDocument(request, response); + + final PresentationManager pm = Templating + .getPresentationManager(); + pm.servePage(doc, request, response); + } + + /* SECONDLY try if we have to serve an item (old style dispatcher based */ + } else if (item != null) { + + /* We have to serve an item here */ + String param = request.getParameter("transID"); + + serveItem(request, response, section, item); + + /* OTHERWISE delegate to a JSP in file system */ + } else { + /* We have to deal with a content-section, folder or another bit */ + if (LOGGER.isInfoEnabled()) { + LOGGER.info("NOT serving content item"); + } + + /* Store content section in http request to make it available + * for admin/index.jsp */ + request.setAttribute(CONTENT_SECTION, section); + + RequestDispatcher rd = m_resolver.resolve(m_templatePath, + request, response, app); + if (rd != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Got dispatcher " + rd); + } + rd.forward(DispatcherHelper.restoreOriginalRequest(request), + response); + } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No dispatcher found for" + rd); + } + String requestUri = request.getRequestURI(); // same as ctx.getRemainingURLPart() + response.sendError(404, requestUri + + " not found on this server."); + } + } - //ToDo - throw new UnsupportedOperationException(); } + private void serveItem(final HttpServletRequest request, + final HttpServletResponse response, + final ContentSection section, + final ContentItem item) + throws ServletException, IOException { + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("serving content item"); + } + + RequestContext ctx = DispatcherHelper.getRequestContext(); + String url = ctx.getRemainingURLPart(); + + final ItemResolver itemResolver = getItemResolver(section); + + //set the content item in the request + request.setAttribute(CONTENT_ITEM, item); + + //set the template context +// ToDo +// final TemplateResolver templateResolver = m_disp.getTemplateResolver( +// section); +// +// String templateURL = url; +// if (!templateURL.startsWith("/")) { +// templateURL = "/" + templateURL; +// } +// if (templateURL.startsWith(PREVIEW)) { +// templateURL = templateURL.substring(PREVIEW.length()); +// } +// +// final String sTemplateContext = itemResolver.getTemplateFromURL( +// templateURL); +// LOGGER.debug("setting template context to {}", sTemplateContext); +// +// templateResolver.setTemplateContext(sTemplateContext, request); +// ToDo End + + // Work out how long to cache for.... + // We take minimum(default timeout, lifecycle expiry) + Lifecycle cycle = item.getLifecycle(); + int expires = DispatcherHelper.getDefaultCacheExpiry(); + if (cycle != null) { + Date endDate = cycle.getEndDateTime(); + + if (endDate != null) { + int maxAge = (int) ((endDate.getTime() - System + .currentTimeMillis()) / 1000l); + if (maxAge < expires) { + expires = maxAge; + } + } + } + + //use ContentItemDispatcher + m_disp.dispatch(request, response, ctx); + } + + /** + * Fetches the content section from the request attributes. + * + * @param request The HTTP request + * + * @return The content section + * + * @pre ( request != null ) + */ + public static ContentSection getContentSection(HttpServletRequest request) { + return (ContentSection) request.getAttribute(CONTENT_SECTION); + } + + @SuppressWarnings("unchecked") + public ItemResolver getItemResolver(final ContentSection section) { + + final String path = section.getPrimaryUrl(); + final ItemResolver itemResolver; + if (itemResolverCache.containsKey(path)) { + itemResolver = itemResolverCache.get(path); + } else { + final String className = section.getItemResolverClass(); + final Class clazz; + try { + clazz = (Class) Class.forName(className); + itemResolver = clazz.newInstance(); + } catch (ClassNotFoundException + | IllegalAccessException + | InstantiationException ex) { + throw new UncheckedWrapperException(ex); + } + + itemResolverCache.put(path, itemResolver); + } + + LOGGER.debug("Using ItemResolver implementation \"{}\"...", + itemResolver.getClass().getName()); + + return itemResolver; + } + + public ContentItem getItem(final ContentSection section, + final String url, + final ItemResolver itemResolver) { + + LOGGER.debug("getting item at url {}", url); + final HttpServletRequest request = Web.getRequest(); + + //first sanitize the url + String itemUrl = url; + if (url.endsWith(XML_SUFFIX)) { + request.setAttribute(XML_MODE, Boolean.TRUE); + LOGGER.debug("StraightXML Requested"); + itemUrl = String.format( + "/%s", + url.substring(0, url.length() - XML_SUFFIX.length())); + itemUrl = "/" + url.substring(0, url.length() - XML_SUFFIX.length()); + } else { + request.setAttribute(XML_MODE, Boolean.FALSE); + if (url.endsWith(FILE_SUFFIX)) { + itemUrl = String.format( + "/%s", + url.substring(0, url.length() - FILE_SUFFIX.length())); + } else if (url.endsWith("/")) { + itemUrl = String.format("/%s", + url.substring(0, url.length() - 1)); + } + } + + if (!itemUrl.startsWith("/")) { + itemUrl = String.format("/%s", itemUrl); + } + + ContentItem item; + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + // Check if the user has access to view public or preview pages + boolean hasPermission = true; + + // If the remaining URL starts with "preview/", then try and + // preview this item. Otherwise look for the live item. + boolean preview = false; + if (itemUrl.startsWith(PREVIEW)) { + itemUrl = itemUrl.substring(PREVIEW.length()); + preview = true; + } + + if (preview) { + LOGGER.info("Trying to get item for PREVIEW"); + + item = itemResolver.getItem(section, + itemUrl, + ContentItemVersion.DRAFT.toString()); + if (item != null) { + hasPermission = permissionChecker.isPermitted( + ItemPrivileges.PREVIEW, item); + } + } else { + LOGGER.info("Trying to get LIVE item"); + + //check if this item is in the cache + //we only cache live items + LOGGER.debug("Trying to get content item for URL {}from cache", + itemUrl); + + // Get the negotiated locale + final GlobalizationHelper globalizationHelper = cdiUtil.findBean( + GlobalizationHelper.class); + final String lang = globalizationHelper.getNegotiatedLocale() + .getLanguage(); + + // XXX why assign a value and afterwards null?? + // Effectively it just ignores the cache and forces a fallback to + // itemResover in any case. Maybe otherwise language selection / + // negotiation doesn't work correctly? + item = null; + + if (item == null) { + LOGGER.debug("Did not find content item in cache, so trying " + + "to retrieve and cache..."); + //item not cached, so retreive it and cache it + item = itemResolver.getItem(section, + itemUrl, + ContentItemVersion.LIVE.toString()); + + if (LOGGER.isDebugEnabled() && item != null) { + LOGGER.debug("Sanity check: item.getPath() is {}", + itemManager.getItemPath(item)); + } + + if (item != null) { + LOGGER.debug("Content Item is not null"); + + hasPermission = permissionChecker.isPermitted( + ItemPrivileges.VIEW_PUBLISHED, item); + } + } + + } + + if (item == null && itemUrl.endsWith(INDEX_FILE)) { + + if (item == null) { + LOGGER.info("no item found"); + } + + // look up folder if it's an index + itemUrl = itemUrl.substring(0, itemUrl.length() - INDEX_FILE + .length()); + LOGGER.info("Attempting to match folder " + itemUrl); + + item = itemResolver.getItem(section, + itemUrl, + ContentItemVersion.LIVE.toString()); + if (item != null) { + hasPermission = permissionChecker.isPermitted( + ItemPrivileges.VIEW_PUBLISHED, item); + } + } + + if (!hasPermission) { + + // first, check if the user is logged-in + // if he isn't, give him a chance to do so... + final Shiro shiro = cdiUtil.findBean(Shiro.class); + + if (shiro.getSubject().isAuthenticated()) { + throw new LoginSignal(request); + } + + throw new AccessDeniedException(); + } + + return item; + } + + public ContentItem getItem(final ContentSection section, final String url) { + final ItemResolver itemResolver = getItemResolver(section); + + return getItem(section, url, itemResolver); + } + + // synchronize access to the item-url cache +// private static synchronized void itemURLCachePut( +// final ContentSection section, +// final String sURL, +// final String lang, +// final Long itemID) { +// +// getItemURLCache(section).put(String.format( +// "%s" + CACHE_KEY_DELIMITER + "%s", sURL, lang), itemID); +// } +// +// /** +// * Maps the content item to the URL in a cache +// * +// * @param section the content section in which the content item is published +// * @param url the URL at which the content item s published +// * @param lang +// * @param item the content item at the URL +// */ +// public static synchronized void itemURLCachePut( +// final ContentSection section, +// final String url, +// final String lang, +// final ContentItem item) { +// +// if (url == null || item == null) { +// return; +// } +// LOGGER.debug("adding cached entry for url {} and language {}", +// url, +// lang); +// +// itemURLCachePut(section, url, lang, item.getObjectId()); +// } +// +// /** +// * Removes the cache entry for the URL, sURL +// * +// * @param section the content section in which to remove the key +// * @param url the cache entry key to remove +// * @param lang +// */ +// public static synchronized void itemURLCacheRemove( +// final ContentSection section, +// final String url, +// final String lang) { +// +// LOGGER.debug("removing cached entry for url {} and language {} ", +// url, +// lang); +// +// getItemURLCache(section).remove(url + CACHE_KEY_DELIMITER + lang); +// } +// +// /** +// * Fetches the ContentItem published at that URL from the cache. +// * +// * @param section the content section in which the content item is published +// * @param url the URL for the item to fetch +// * @param lang +// * @return the ContentItem in the cache, or null +// */ +// public static ContentItem itemURLCacheGet(final ContentSection section, +// final String url, +// final String lang) { +// final Long itemID = (Long) getItemURLCache(section).get( +// url + CACHE_KEY_DELIMITER + lang); +// +// if (itemID == null) { +// return null; +// } else { +// final ContentItemRepository itemRepo = CdiUtil.createCdiUtil().findBean(ContentItemRepository.class); +// try { +// return itemRepo.findById(itemID); +// } catch(NoResultException ex) { +// return null; +// } +// } +// } +// +// private static synchronized CacheTable getItemURLCache(ContentSection section) { +// Assert.exists(section, ContentSection.class); +// if (s_itemURLCacheMap == null) { +// initializeItemURLCache(); +// } +// +// if (s_itemURLCacheMap.get(section.getPath()) == null) { +// final CacheTable cache = new CacheTable("ContentSectionServletItemURLCache" + +// section.getID().toString()); +// s_itemURLCacheMap.put(section.getPath(), cache); +// } +// +// return (CacheTable) s_itemURLCacheMap.get(section.getPath()); +// } } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java index 939911d61..b826c5494 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java @@ -20,7 +20,6 @@ package org.librecms.contentsection; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.libreccm.categorization.Category; import org.libreccm.modules.InstallEvent; import org.libreccm.security.Role; import org.libreccm.web.AbstractCcmApplicationSetup; @@ -28,8 +27,8 @@ import org.librecms.CmsConstants; import java.util.UUID; -import static org.librecms.CmsConstants.*; import static org.librecms.contentsection.ContentSection.*; + import org.librecms.contentsection.privileges.AdminPrivileges; import org.librecms.contentsection.privileges.AssetPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges; @@ -96,12 +95,14 @@ public class ContentSectionSetup extends AbstractCcmApplicationSetup { rootFolder.setUuid(UUID.randomUUID().toString()); rootFolder.setUniqueId(rootFolder.getUuid()); rootFolder.setName(String.format("%s_" + ROOT, sectionName)); + rootFolder.setType(FolderType.DOCUMENTS_FOLDER); rootFolder.setSection(section); final Folder rootAssetFolder = new Folder(); rootAssetFolder.setName(String.format("%s_" + ASSETS, sectionName)); rootAssetFolder.setUuid(UUID.randomUUID().toString()); rootAssetFolder.setUniqueId(rootAssetFolder.getUuid()); + rootAssetFolder.setType(FolderType.ASSETS_FOLDER); rootAssetFolder.setSection(section); section.setRootDocumentFolder(rootFolder); diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_8__update_assets_and_attachments.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_8__update_assets_and_attachments.sql index 3a36f52c2..5222abee8 100644 --- a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_8__update_assets_and_attachments.sql +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_8__update_assets_and_attachments.sql @@ -152,6 +152,9 @@ alter table CCM_CMS.BINARY_ASSETS_AUD drop table if exists CCM_CMS.AttachmentList_ItemAttachment_AUD; +alter table CCM_CMS.ATTACHMENTS_AUD + add column ATTACHMENT_LIST_ID bigint; + alter table CCM_CMS.AUDIO_ASSETS alter column ASSET_ID RENAME TO OBJECT_ID; @@ -246,6 +249,12 @@ alter table CCM_CMS.NOTE_TEXTS alter table CCM_CMS.SIDE_NOTE_TEXTS alter column ASSET_ID RENAME TO SIDE_NOTE_ID; +alter table CCM_CMS.NOTE_TEXTS_AUD + rename to SIDE_NOTE_TEXTS_AUD; + +alter table CCM_CMS.SIDE_NOTE_TEXTS_AUD + alter column ASSET_ID RENAME TO SIDE_NOTE_ID; + create table CCM_CMS.SIDE_NOTE_TITLES ( SIDE_NOTE_ID bigint not null, LOCALIZED_VALUE longvarchar, diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_8__update_assets_and_attachments.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_8__update_assets_and_attachments.sql index 033726bc5..eb1df07ef 100644 --- a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_8__update_assets_and_attachments.sql +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_8__update_assets_and_attachments.sql @@ -152,6 +152,9 @@ alter table CCM_CMS.BINARY_ASSETS_AUD drop table if exists CCM_CMS.AttachmentList_ItemAttachment_AUD; +alter table CCM_CMS.ATTACHMENTS_AUD + add column ATTACHMENT_LIST_ID int8; + alter table CCM_CMS.AUDIO_ASSETS rename column ASSET_ID to OBJECT_ID; @@ -244,6 +247,12 @@ alter table CCM_CMS.NOTE_TEXTS alter table CCM_CMS.SIDE_NOTE_TEXTS rename column ASSET_ID to SIDE_NOTE_ID; +alter table CCM_CMS.NOTE_TEXTS_AUD + rename to SIDE_NOTE_TEXTS_AUD; + +alter table CCM_CMS.SIDE_NOTE_TEXTS_AUD + rename column ASSET_ID TO SIDE_NOTE_ID; + create table CCM_CMS.SIDE_NOTE_TITLES ( SIDE_NOTE_ID int8 not null, LOCALIZED_VALUE text, diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources.properties index 30f792792..27f9fa354 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsResources.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsResources.properties @@ -1,4 +1,18 @@ contentsection.ui.admin.app_note=The properties for a Content Section are managed a special application: contentsection.ui.admin.link_app=Go to Content Section application -cms.ui.role.insufficient_privileges=The user is not permitted to administrate roles. \ No newline at end of file +cms.ui.role.insufficient_privileges=The user is not permitted to administrate roles. +cms.ui.change_password=Change password +cms.ui.sign_out=Sign out +cms.ui.content_center=Content Center +cms.ui.contentcenter.mainpage.taskssections=Tasks/Sections +cms.ui.contentcenter.section=Content Section +cms.ui.contentcenter.action=Action +cms.ui.workflow.task.view_locked=View your locked tasks +cms.ui.workflow.task.view_unlocked=View unlocked tasks +cms.ui.workflow.task.view_all=View all tasks +cms.ui.my_workspace=My Portal +cms.ui.admin_center=Admin Center +cms.ui.contentcenter.section_hint=All documents are organized in one or more contect sections. Each content section may have its own system of permission and its own administrators, independent from each other. Select a section where you will edit a document or create a new one. +cms.ui.contentcenter.location=Location +cms.ui.contentcenter.location_hint=In Legacy mode links to public pages. diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties index a39f49e80..53ca6579b 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsResources_de.properties @@ -1,4 +1,18 @@ contentsection.ui.admin.app_note=Die Verwaltung von Content Sections erfolgt \u00fcber eine eigene Application: contentsection.ui.admin.link_app=Zur Content Section Applikation -cms.ui.role.insufficient_privileges=Der Benutzer ist nicht privilegiert Rollen zu administrieren. \ No newline at end of file +cms.ui.role.insufficient_privileges=Der Benutzer ist nicht privilegiert Rollen zu administrieren. +cms.ui.change_password=Passwort \u00e4ndern +cms.ui.sign_out=Abmelden +cms.ui.content_center=Content Center +cms.ui.contentcenter.mainpage.taskssections=Aufgaben/Content Sections +cms.ui.contentcenter.section=Content Section +cms.ui.contentcenter.action=Aktion +cms.ui.workflow.task.view_locked=Reservierte Aufgaben anzeigen +cms.ui.workflow.task.view_unlocked=Freigegebene Aufgaben anzeigen +cms.ui.workflow.task.view_all=Alle Aufgaben anzeigen +cms.ui.my_workspace=Mein Portal +cms.ui.admin_center=Admin Center +cms.ui.contentcenter.section_hint=All documents are organized in one or more contect sections. Each content section may have its own system of permission and its own administrators, independent from each other. Select a section where you will edit a document or create a new one. +cms.ui.contentcenter.location=Ort +cms.ui.contentcenter.location_hint=In Legacy mode links to public pages. diff --git a/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties b/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties new file mode 100644 index 000000000..70bc7507f --- /dev/null +++ b/ccm-cms/src/main/resources/org/librecms/CmsResources_fr.properties @@ -0,0 +1,18 @@ + +contentsection.ui.admin.app_note=The properties for a Content Section are managed a special application: +contentsection.ui.admin.link_app=Go to Content Section application +cms.ui.role.insufficient_privileges=The user is not permitted to administrate roles. +cms.ui.change_password=Change password +cms.ui.sign_out=Sign out +cms.ui.content_center=Centre de contenu +cms.ui.contentcenter.mainpage.taskssections=Tasks/Sections +cms.ui.contentcenter.section=Content Section +cms.ui.contentcenter.action=Action +cms.ui.workflow.task.view_locked=View your locked tasks +cms.ui.workflow.task.view_unlocked=View unlocked tasks +cms.ui.workflow.task.view_all=View all tasks +cms.ui.my_workspace=Mon espace de travail +cms.ui.admin_center=Admin Center +cms.ui.contentcenter.section_hint=All documents are organized in one or more contect sections. Each content section may have its own system of permission and its own administrators, independent from each other. Select a section where you will edit a document or create a new one. +cms.ui.contentcenter.location=Location +cms.ui.contentcenter.location_hint=In Legacy mode links to public pages.