diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo similarity index 56% rename from ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.off rename to ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo index a632c181a..54446684d 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java.todo @@ -23,7 +23,7 @@ import com.arsdigita.cms.CMS; import com.arsdigita.cms.ContentCenter; import com.arsdigita.util.Assert; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Logger; import org.libreccm.categorization.Category; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentSection; @@ -36,121 +36,114 @@ import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.Objects; import java.util.StringTokenizer; +import org.apache.logging.log4j.LogManager; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentItemVersion; +import org.librecms.contentsection.Folder; +import org.librecms.contentsection.FolderManager; /** - * Resolves items to URLs and URLs to items for multiple language - * variants. + * Resolves items to URLs and URLs to items for multiple language variants. * * Created Mon Jan 20 14:30:03 2003. * * @author Michael Hanisch * @version $Id: MultilingualItemResolver.java 2090 2010-04-17 08:04:14Z pboy $ */ -public class MultilingualItemResolver - extends AbstractItemResolver implements ItemResolver { - - private static final Logger s_log = Logger.getLogger - (MultilingualItemResolver.class); - +public class MultilingualItemResolver + extends AbstractItemResolver implements ItemResolver { + + private static final Logger LOGGER = LogManager.getLogger( + MultilingualItemResolver.class); + private static MasterPage s_masterP = null; private static final String ADMIN_PREFIX = "admin"; /** - * The string identifying an item's ID in the query string of a - * URL. + * The string identifying an item's ID in the query string of a URL. */ protected static final String ITEM_ID = "item_id"; - + /** - * The separator used in URL query strings; should be either "&" - * or ";". + * The separator used in URL query strings; should be either "&" or ";". */ protected static final String SEPARATOR = "&"; - + public MultilingualItemResolver() { - s_log.debug("Undergoing creation"); + LOGGER.debug("Undergoing creation"); } - + /** * Returns a content item based on section, url, and use context. * * @param section The current content section - * @param url The section-relative URL - * @param context The use context, - * e.g. ContentItem.LIVE, - * CMSDispatcher.PREVIEW or - * ContentItem.DRAFT. See {@link + * @param itemUrl The section-relative URL + * @param context The use context, e.g. ContentItem.LIVE, + * CMSDispatcher.PREVIEW or ContentItem.DRAFT. See {@link * #getCurrentContext}. * @return The content item, or null if no such item exists */ @Override public ContentItem getItem(final ContentSection section, - String url, - final String context) { - if (s_log.isDebugEnabled()) { - s_log.debug("Resolving the item in content section " + section + - " at URL '" + url + "' for context " + context); - } - + final String itemUrl, + final String context) { + LOGGER.debug("Resolving the item in content section \"{}\" at URL " + + "\"{}\" for context \"{}\"...", + section.getLabel(), + itemUrl, + context); + Assert.exists(section, "ContentSection section"); - Assert.exists(url, "String url"); + Assert.exists(itemUrl, "String url"); Assert.exists(context, "String context"); - - Category rootFolder = section.getRootDocumentsFolder(); - url = stripTemplateFromURL(url); - - // nothing to do, if root folder is null + + final Folder rootFolder = section.getRootDocumentsFolder(); + String url = stripTemplateFromURL(itemUrl); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + if (rootFolder == null) { - s_log.debug("The root folder is null; returning no item"); + // nothing to do, if root folder is null + LOGGER.debug("The root folder is null; returning no item"); } else { - if (s_log.isDebugEnabled()) { - s_log.debug("Using root folder " + rootFolder); - } - - if ("live".equals(context)) { - s_log.debug("The use context is 'live'"); - - // We allow for returning null, so the root folder may - // not be live. - //Assert.isTrue(rootFolder.isLive(), - // "live context - root folder of secion must be live"); - - // If the context is 'live', we need the live item. - - rootFolder = (Folder) rootFolder.getLiveVersion(); - - if (rootFolder == null) { - s_log.debug("The live version of the root folder is " + - "null; returning no item"); - } else { - s_log.debug("The root folder has a live version; " + - "recursing"); - - final String prefix = - section.getURL() + rootFolder.getPath(); - - if (url.startsWith(prefix)) { - if (s_log.isDebugEnabled()) { - s_log.debug("The URL starts with prefix '" + - prefix + "'; removing it"); - } - - url = url.substring(prefix.length()); - } - - final ContentItem item = getItemFromLiveURL(url, rootFolder); - - if (s_log.isDebugEnabled()) { - s_log.debug("Resolved URL '" + url + "' to item " + - item); - } - - return item; + LOGGER.debug("Using root folder {}...", + Objects.toString(rootFolder)); + + if (ContentItemVersion.LIVE.toString().equals(context)) { + LOGGER.debug("The use context is 'live'"); + + LOGGER.debug("The root folder has a live version; recursing"); + + final FolderManager folderManager = cdiUtil.findBean( + FolderManager.class); + final String prefix = String.join( + "", + section.getPrimaryUrl(), + folderManager.getFolderPath(rootFolder)); + + if (url.startsWith(prefix)) { + LOGGER. + debug("The starts with prefix \"{}\"; removing it...", + prefix); + + url = url.substring(prefix.length()); } - } else if (ContentItem.DRAFT.equals(context)) { - s_log.debug("The use context is 'draft'"); - + + final ContentItem item = getItemFromLiveURL(url, rootFolder); + + LOGGER.debug("Resolved URL \"{}\" to item {}...", + url, + Objects.toString(item)); + + return item; + + } else if (ContentItemVersion.DRAFT.toString().equals(context)) { + LOGGER.debug("The use context is 'draft'"); + // For 'draft' items, 'generateUrl()' method returns // URL like this // '/acs/wcms/admin/item.jsp?item_id=10201&set_tab=1' @@ -158,52 +151,50 @@ public class MultilingualItemResolver // 'item_id', then try to instanciate item_id value // and return FIXME: Please hack this if there is // more graceful solution. [aavetyan] - if (Assert.isEnabled()) { - Assert.isTrue - (url.indexOf(ITEM_ID) >= 0, - "url must contain parameter " + ITEM_ID); + Assert.isTrue(url.contains(ITEM_ID), + String.format("url must contain parameter %s", + ITEM_ID)); } - + final ContentItem item = getItemFromDraftURL(url); - - if (s_log.isDebugEnabled()) { - s_log.debug("Resolved URL '" + url + "' to item " + item); - } - + + LOGGER.debug("Resolved URL \"{}\" to item {}.", + url, + Objects.toString(item)); + return item; } else if (CMSDispatcher.PREVIEW.equals(context)) { - s_log.debug("The use context is 'preview'"); - + LOGGER.debug("The use context is 'preview'"); + final String prefix = CMSDispatcher.PREVIEW + "/"; - + if (url.startsWith(prefix)) { - if (s_log.isDebugEnabled()) { - s_log.debug("The URL starts with prefix '" + - prefix + "'; removing it"); - } - + LOGGER.debug( + "The URL starts with prefix \"{}\"; removing it", + prefix); + url = url.substring(prefix.length()); } - + final ContentItem item = getItemFromLiveURL(url, rootFolder); - - if (s_log.isDebugEnabled()) { - s_log.debug("Resolved URL '" + url + "' to item " + item); - } - + + LOGGER.debug("Resolved URL \"{}\" to item {}.", + url, + Objects.toString(item)); + return item; } else { - throw new IllegalArgumentException - ("Invalid item resolver context " + context); + throw new IllegalArgumentException(String.format( + "Invalid item resolver context \"%s\".", context)); } } - - s_log.debug("No item resolved; returning null"); - + + LOGGER.debug("No item resolved; returning null"); + return null; } - + /** * Fetches the current context based on the page state. * @@ -212,44 +203,43 @@ public class MultilingualItemResolver * ContentItem.LIVE or ContentItem.DRAFT * @see ContentItem#LIVE * @see ContentItem#DRAFT + * + * ToDo: Refactor to use the {@link ContentItemVersion} directly. */ @Override public String getCurrentContext(final PageState state) { - s_log.debug("Getting the current context"); - + LOGGER.debug("Getting the current context"); + // XXX need to use Web.getWebContext().getRequestURL() here. String url = state.getRequest().getRequestURI(); - - final ContentSection section = - CMS.getContext().getContentSection(); - + + final ContentSection section = CMS.getContext().getContentSection(); + // If this page is associated with a content section, // transform the URL so that it is relative to the content // section site node. - if (section != null) { - final String sectionURL = section.getURL(); - + final String sectionURL = section.getPrimaryUrl(); + if (url.startsWith(sectionURL)) { url = url.substring(sectionURL.length()); } } - + // Remove any template-specific URL components (will only work // if they're first in the URL at this point; verify). XXX but // we don't actually verify? - url = stripTemplateFromURL(url); - + // Determine if we are under the admin UI. - - if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter.getURL())) { - return ContentItem.DRAFT; + if (url.startsWith(ADMIN_PREFIX) + || url.startsWith(CmsConstants.CONTENT_CENTER_URL)) { + return ContentItemVersion.DRAFT.toString(); } else { - return ContentItem.LIVE; + return ContentItemVersion.LIVE.toString(); } } - + /** * Generates a URL for a content item. * @@ -257,20 +247,19 @@ public class MultilingualItemResolver * @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 context the context of the URL, such as "live" or "admin" * @return The URL of the item * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final BigDecimal itemId, - final String name, - final ContentSection section, - final String context) { + final BigDecimal itemId, + final String name, + final ContentSection section, + final String context) { return generateItemURL(state, itemId, name, section, context, null); } - + /** * Generates a URL for a content item. * @@ -278,131 +267,129 @@ public class MultilingualItemResolver * @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" + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" * @return The URL of the item * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final BigDecimal itemId, - final String name, - final ContentSection section, - final String context, - final String templateContext) { - if (s_log.isDebugEnabled()) { - s_log.debug("Generating an item URL for item id " + itemId + - ", section " + section + ", and context '" + - context + "' with name '" + name + "'"); + final BigDecimal itemId, + final String name, + final ContentSection section, + final String context, + final String templateContext) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generating an item URL for item id " + itemId + + ", section " + section + ", and context '" + + context + + "' with name '" + name + "'"); } - - Assert.exists(itemId, "BigDecimal itemId"); + + Assert.exists(itemId, "BigDecimal itemId"); Assert.exists(context, "String context"); Assert.exists(section, "ContentSection section"); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemRepository itemRepo = cdiUtil.findBean(ContentItemRepository.class); - if (ContentItem.DRAFT.equals(context)) { + if (ContentItemVersion.DRAFT.toString().equals(context)) { // No template context here. return generateDraftURL(section, itemId); } else if (CMSDispatcher.PREVIEW.equals(context)) { ContentItem item = new ContentItem(itemId); - + return generatePreviewURL(section, item, templateContext); } else if (ContentItem.LIVE.equals(context)) { ContentItem item = new ContentItem(itemId); - + if (Assert.isEnabled()) { Assert.exists(item, "item"); Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()), - "Generating " + ContentItem.LIVE + " " + - "URL; this item must be the live version"); + "Generating " + ContentItem.LIVE + " " + + "URL; this item must be the live version"); } - + return generateLiveURL(section, item, templateContext); } else { - throw new IllegalArgumentException - ("Unknown context '" + context + "'"); + throw new IllegalArgumentException("Unknown context '" + context + + "'"); } } - + /** * Generates a URL for a content item. * * @param item The item * @param state The page state * @param section the content section to which the item belongs - * @param context the context of the URL, such as "live" or - * "admin" + * @param context the context of the URL, such as "live" or "admin" * @return The URL of the item * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final ContentItem item, - final ContentSection section, - final String context) { + final ContentItem item, + final ContentSection section, + final String context) { return generateItemURL(state, item, section, context, null); } - + /** * Generates a URL for a content item. * * @param item The item * @param state The page state * @param section the content section to which the item belongs - * @param context the context of the URL, such as "live" or - * "admin" - * @param templateContext the context for the URL, such as - * "public" + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" * @return The URL of the item * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final ContentItem item, - ContentSection section, - final String context, - final String templateContext) { - if (s_log.isDebugEnabled()) { - s_log.debug("Generating an item URL for item " + item + - ", section " + section + ", and context " + - context); + final ContentItem item, + ContentSection section, + final String context, + final String templateContext) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generating an item URL for item " + item + + ", section " + + section + ", and context " + context); } - + Assert.exists(item, "ContentItem item"); Assert.exists(context, "String context"); - + if (section == null) { section = item.getContentSection(); } - + if (ContentItem.DRAFT.equals(context)) { if (Assert.isEnabled()) { Assert.isTrue(ContentItem.DRAFT.equals(item.getVersion()), - "Generating " + ContentItem.DRAFT + - " url: item must be draft version"); + "Generating " + ContentItem.DRAFT + + " url: item must be draft version"); } - + return generateDraftURL(section, item.getID()); } else if (CMSDispatcher.PREVIEW.equals(context)) { return generatePreviewURL(section, item, templateContext); } else if (ContentItem.LIVE.equals(context)) { if (Assert.isEnabled()) { Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()), - "Generating " + ContentItem.LIVE + - " url: item must be live version"); + "Generating " + ContentItem.LIVE + + " url: item must be live version"); } - + return generateLiveURL(section, item, templateContext); } else { throw new RuntimeException("Unknown context " + context); } } - + /** - * Returns a master page based on page state (and content - * section). + * Returns a master page based on page state (and content section). * * @param item The content item * @param request The HTTP request @@ -411,80 +398,81 @@ public class MultilingualItemResolver */ @Override public CMSPage getMasterPage(final ContentItem item, - final HttpServletRequest request) + final HttpServletRequest request) throws ServletException { - if (s_log.isDebugEnabled()) { - s_log.debug("Getting the master page for item " + item); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Getting the master page for item " + item); } - + // taken from SimpleItemResolver if (s_masterP == null) { s_masterP = new MasterPage(); s_masterP.init(); } - - if (s_log.isDebugEnabled()) { - s_log.debug("Returning master page " + s_masterP); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Returning master page " + s_masterP); } - + return s_masterP; } - + /** * Returns content item's draft version URL * - * @param section The content section to which the item belongs - * @param itemId The content item's ID + * @param section The content section to which the item belongs + * @param itemId The content item's ID * @return generated URL string */ protected String generateDraftURL(final ContentSection section, - final BigDecimal itemId) { - if (s_log.isDebugEnabled()) { - s_log.debug("Generating draft URL for item ID " + itemId + - " and section " + section); + final BigDecimal itemId) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generating draft URL for item ID " + itemId + + " and section " + section); } - + if (Assert.isEnabled()) { Assert.isTrue(section != null && itemId != null, - "get draft url: neither secion nor item " + - "can be null"); + "get draft url: neither secion nor item " + + "can be null"); } - - final String url = ContentItemPage.getItemURL - (section.getPath() + "/", itemId, ContentItemPage.AUTHORING_TAB); - - if (s_log.isDebugEnabled()) { - s_log.debug("Generated draft URL " + url); + + final String url = ContentItemPage.getItemURL(section.getPath() + "/", + itemId, + ContentItemPage.AUTHORING_TAB); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generated draft URL " + url); } - + return url; } - + /** - * Generate a language-independent URL to the - * item in the given section.

When a client - * retrieves this URL, the URL is resolved to point to a specific - * language instance of the item referenced here, i.e. this URL - * will be resolved to a language-specific URL - * internally. + * Generate a language-independent URL to the item in + * the given section.

+ * When a client retrieves this URL, the URL is resolved to point to a + * specific language instance of the item referenced here, i.e. this URL + * will be resolved to a language-specific URL internally. * * @param section the ContentSection that contains this item * @param item ContentItem for which a URL should be - * constructed. - * @param templateContext template context; will be ignored if null + * constructed. + * @param templateContext template context; will be ignored if + * null * - * @return a language-independent URL to the - * item in the given section, which will - * be presented within the given templateContext + * @return a language-independent URL to the item in + * the given section, which will be presented within the given + * templateContext */ protected String generateLiveURL(final ContentSection section, - final ContentItem item, - final String templateContext) { - if (s_log.isDebugEnabled()) { - s_log.debug("Generating live URL for item " + item + " in " + - "section " + section); + final ContentItem item, + final String templateContext) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generating live URL for item " + item + " in " + + "section " + section); } - + /* * URL = URL of section + templateContext + path to the ContentBundle * which contains the item @@ -492,19 +480,20 @@ public class MultilingualItemResolver final StringBuffer url = new StringBuffer(400); //url.append(section.getURL()); url.append(section.getPath()).append("/"); - + /* * add template context, if one is given */ // This is breaking URL's...not sure why it's here. XXX // this should work with the appropriate logic. trying again. if (!(templateContext == null || templateContext.length() == 0)) { - url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append("/"); + url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append( + "/"); } - + // Try to retrieve the bundle. final ContentItem bundle = (ContentItem) item.getParent(); - + /* * It would be nice if we had a ContentPage here, which * supports the getContentBundle() method, but unfortunately @@ -514,23 +503,22 @@ public class MultilingualItemResolver * ContentItem. */ if (bundle != null && bundle instanceof ContentBundle) { - s_log.debug("Found a bundle; building its file name"); - + LOGGER.debug("Found a bundle; building its file name"); + final String fname = bundle.getPath(); - - if (s_log.isDebugEnabled()) { - s_log.debug("Appending the bundle's file name '" + - fname + "'"); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Appending the bundle's file name '" + fname + "'"); } - + url.append(fname); - + } else { - s_log.debug("No bundle found; using the item's path directly"); - + LOGGER.debug("No bundle found; using the item's path directly"); + url.append(item.getPath()); } -/* + /* * This will append the language to the url, which will in turn render * the language negotiation inoperative final String language = item.getLanguage(); @@ -548,34 +536,32 @@ public class MultilingualItemResolver if (s_log.isDebugEnabled()) { s_log.debug("Generated live URL " + url.toString()); } -*/ + */ return url.toString(); } - + /** - * Generate a URL which can be used to preview the - * item, using the given - * templateContext.

Only a specific language - * instance can be previewed, meaning there no language - * negotiation is involved when a request is made to a URL that - * has been generated by this method. + * Generate a URL which can be used to preview the item, using + * the given templateContext.

+ * Only a specific language instance can be previewed, meaning there + * no language negotiation is involved when a request is made to a + * URL that has been generated by this method. * - * @param section The ContentSection which contains - * the item - * @param item The ContentItem for which a URL should - * be generated. - * @param templateContext the context that determines which - * template should render the item when it is previewed; ignored - * if the argument given here is null - * @return a URL which can be used to preview the given + * @param section The ContentSection which contains the * item + * @param item The ContentItem for which a URL should be + * generated. + * @param templateContext the context that determines which template should + * render the item when it is previewed; ignored if the argument given here + * is null + * @return a URL which can be used to preview the given item */ protected String generatePreviewURL(ContentSection section, - ContentItem item, - String templateContext) { + ContentItem item, + String templateContext) { Assert.exists(section, "ContentSection section"); Assert.exists(item, "ContentItem item"); - + final StringBuffer url = new StringBuffer(100); url.append(section.getPath()); url.append("/"); @@ -587,12 +573,13 @@ public class MultilingualItemResolver // This is breaking URL's...not sure why it's here. XXX // this should work with the appropriate logic. trying again. if (!(templateContext == null || templateContext.length() == 0)) { - url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append("/"); + url.append(TEMPLATE_CONTEXT_PREFIX).append(templateContext).append( + "/"); } - + // Try to retrieve the bundle. ContentItem bundle = (ContentItem) item.getParent(); - + /* It would be nice if we had a ContentPage here, which * supports the getContentBundle() method, but unfortunately * the API sucks and there is no real distinction between mere @@ -601,112 +588,111 @@ public class MultilingualItemResolver * ContentItem. */ if (bundle != null && bundle instanceof ContentBundle) { - s_log.debug("Found a bundle; using its path"); - + LOGGER.debug("Found a bundle; using its path"); + url.append(bundle.getPath()); } else { - s_log.debug("No bundle found; using the item's path directly"); - + LOGGER.debug("No bundle found; using the item's path directly"); + url.append(item.getPath()); } - + final String language = item.getLanguage(); - + if (language == null) { - s_log.debug("The item has no language; omitting the " + - "language encoding"); + LOGGER.debug("The item has no language; omitting the " + + "language encoding"); } else { - s_log.debug("Encoding the language of the item passed in, '" + - language + "'"); - + LOGGER.debug("Encoding the language of the item passed in, '" + + language + "'"); + url.append(".").append(language); } - - if (s_log.isDebugEnabled()) { - s_log.debug("Generated preview URL " + url.toString()); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Generated preview URL " + url.toString()); } - + return url.toString(); } - + /** - * Retrieves ITEM_ID parameter from URL and - * instantiates item according to this ID. + * Retrieves ITEM_ID parameter from URL and instantiates item + * according to this ID. * - * @param url URL that indicates which item should be retrieved; - * must contain the ITEM_ID parameter - * @return the ContentItem the given url - * points to, or null if no ID has been found in the - * url + * @param url URL that indicates which item should be retrieved; must + * contain the ITEM_ID parameter + * @return the ContentItem the given url points + * to, or null if no ID has been found in the url */ protected ContentItem getItemFromDraftURL(final String url) { - if (s_log.isDebugEnabled()) { - s_log.debug("Looking up the item from draft URL " + url); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Looking up the item from draft URL " + url); } - + int pos = url.indexOf(ITEM_ID); - + // XXX this is wrong: here we abort on not finding the // parameter; below we return null. if (Assert.isEnabled()) { Assert.isTrue(pos >= 0, - "Draft URL must contain parameter " + ITEM_ID); + "Draft URL must contain parameter " + ITEM_ID); } - + String item_id = url.substring(pos); // item_id == ITEM_ID=.... ? - + pos = item_id.indexOf("="); // should be exactly after the ITEM_ID string - + if (pos != ITEM_ID.length()) { // item_id seems to be something like ITEM_IDFOO= - - s_log.debug("No suitable item_id parameter found; returning null"); - + + LOGGER.debug("No suitable item_id parameter found; returning null"); + return null; // no ID found } - + pos++; // skip the "=" - + // ID is the string between the equal (at pos) and the next separator int i = item_id.indexOf(SEPARATOR); - item_id = item_id.substring(pos, Math.min(i, item_id.length() -1)); - - if (s_log.isDebugEnabled()) { - s_log.debug("Looking up item using item ID " + item_id); + item_id = item_id.substring(pos, Math.min(i, item_id.length() - 1)); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Looking up item using item ID " + item_id); } - - OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal(item_id)); - final ContentItem item = (ContentItem) DomainObjectFactory.newInstance - (oid); - - if (s_log.isDebugEnabled()) { - s_log.debug("Returning item " + item); + + OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal( + item_id)); + final ContentItem item = (ContentItem) DomainObjectFactory.newInstance( + oid); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Returning item " + item); } - + return item; } - + /** - * Returns a content item based on URL relative to the root - * folder. + * Returns a content item based on URL relative to the root folder. * - * @param url The content item url - * @param parentFolder The parent folder object, url must be relevant to it - * @return The Content Item instance, it can also be either Bundle - * or Folder objects, depending on URL and file language extension + * @param url The content item url + * @param parentFolder The parent folder object, url must be relevant to it + * @return The Content Item instance, it can also be either Bundle or Folder + * objects, depending on URL and file language extension */ protected ContentItem getItemFromLiveURL(String url, - Folder parentFolder) { - if (s_log.isDebugEnabled()) { - s_log.debug("Resolving the item for live URL " + url + - " and parent folder " + parentFolder); + Folder parentFolder) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Resolving the item for live URL " + url + + " and parent folder " + parentFolder); } if (parentFolder == null || url == null || url.equals("")) { - if (s_log.isDebugEnabled()) { - s_log.debug("The url is null or parent folder was null " + - "or something else is wrong, so just return " + - "the parent folder"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The url is null or parent folder was null " + + "or something else is wrong, so just return " + + "the parent folder"); } return parentFolder; @@ -716,43 +702,43 @@ public class MultilingualItemResolver int index = url.indexOf('/'); if (index >= 0) { - s_log.debug("The URL starts with a slash; paring off the first " + - "URL element and recursing"); + LOGGER.debug("The URL starts with a slash; paring off the first " + + "URL element and recursing"); // If we got first slash (index == 0), ignore it and go // on, sample '/foo/bar/item.html.en', in next recursion // will have deal with 'foo' folder. - String name = index > 0 ? url.substring(0, index) : ""; parentFolder = "".equals(name) ? parentFolder - : (Folder) parentFolder.getItem(URLEncoder.encode(name), true); + : (Folder) parentFolder.getItem(URLEncoder.encode( + name), true); url = index + 1 < len ? url.substring(index + 1) : ""; return getItemFromLiveURL(url, parentFolder); } else { - s_log.debug("Found a file element in the URL"); + LOGGER.debug("Found a file element in the URL"); - String[] nameAndLang = getNameAndLangFromURLFrag(url); + String[] nameAndLang = getNameAndLangFromURLFrag(url); String name = nameAndLang[0]; String lang = nameAndLang[1]; - ContentItem item = parentFolder.getItem(URLEncoder.encode(name), false); - return getItemFromLangAndBundle(lang, item); + ContentItem item = parentFolder.getItem(URLEncoder.encode(name), + false); + return getItemFromLangAndBundle(lang, item); } } /** - * Returns an array containing the the item's name and lang based - * on the URL fragment. + * Returns an array containing the the item's name and lang based on the URL + * fragment. * - * @return a two-element string array, the first element - * containing the bundle name, and the second element containing - * the lang string + * @return a two-element string array, the first element containing the + * bundle name, and the second element containing the lang string */ protected String[] getNameAndLangFromURLFrag(String url) { String name = null; String lang = null; - + /* * Try to find out if there's an extension with the language code * 1 Get a list of all "extensions", i.e. parts of the url @@ -763,20 +749,19 @@ public class MultilingualItemResolver * from a bundle * 2b if no match is found */ - final ArrayList exts = new ArrayList(5); final StringTokenizer tok = new StringTokenizer(url, "."); - + while (tok.hasMoreTokens()) { exts.add(tok.nextToken()); } - + if (exts.size() > 0) { - if (s_log.isDebugEnabled()) { - s_log.debug("Found some file extensions to look at; they " + - "are " + exts); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Found some file extensions to look at; they " + + "are " + exts); } - + /* * We have found at least one extension, so we can * proceed. Now try to find out if one of the @@ -784,23 +769,22 @@ public class MultilingualItemResolver * support 2-letter language codes!). * If so, use this as the language to look for. */ - - /* + /* * First element is the NAME of the item, not an extension! */ name = (String) exts.get(0); String ext = null; - Collection supportedLangs = - LanguageUtil.getSupportedLanguages2LA(); + Collection supportedLangs + = LanguageUtil.getSupportedLanguages2LA(); Iterator supportedLangIt = null; - + for (int i = 1; i < exts.size(); i++) { ext = (String) exts.get(i); - - if (s_log.isDebugEnabled()) { - s_log.debug("Examining extension " + ext); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Examining extension " + ext); } - + /* * Loop through all extensions, but discard the * first one, which is the name of the item. @@ -817,67 +801,67 @@ public class MultilingualItemResolver while (supportedLangIt.hasNext()) { if (ext.equals(supportedLangIt.next())) { lang = ext; - if (s_log.isDebugEnabled()) { - s_log.debug("Found a match; using " + - "language " + lang); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Found a match; using " + + "language " + lang); } break; } } } else { - if (s_log.isDebugEnabled()) { - s_log.debug("Discarding extension " + ext + "; " + - "it is too short"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Discarding extension " + ext + "; " + + "it is too short"); } } } } else { - s_log.debug("The file has no extensions; no language was " + - "encoded"); + LOGGER.debug("The file has no extensions; no language was " + + "encoded"); name = url; // no extension, so we just have a name here lang = null; // no extension, so we cannot guess the language } - + if (Assert.isEnabled()) { Assert.exists(name, "String name"); Assert.exists(lang == null || lang.length() == 2); } - - if (s_log.isDebugEnabled()) { - s_log.debug("File name resolved to " + name); - s_log.debug("File language resolved to " + lang); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("File name resolved to " + name); + LOGGER.debug("File language resolved to " + lang); } String[] returnArray = new String[2]; returnArray[0] = name; returnArray[1] = lang; return returnArray; } - - + /** - * Finds a language instance of a content item given the bundle, - * name, and lang string + * Finds a language instance of a content item given the bundle, name, and + * lang string * - * @param lang The lang string from the URL - * @param item The content bundle + * @param lang The lang string from the URL + * @param item The content bundle * * @return The negotiated lang instance for the current request. */ protected ContentItem getItemFromLangAndBundle(String lang, ContentItem item) { if (item != null && item instanceof ContentBundle) { - if (s_log.isDebugEnabled()) { - s_log.debug("Found content bundle " + item); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Found content bundle " + item); } if (lang == null) { - s_log.debug("The URL has no language encoded in it; " + - "negotiating the language"); + LOGGER.debug("The URL has no language encoded in it; " + + "negotiating the language"); // There is no language, so we get the negotiated locale and call // this method again with a proper language - return this.getItemFromLangAndBundle(GlobalizationHelper.getNegotiatedLocale().getLanguage(), item); + return this.getItemFromLangAndBundle(GlobalizationHelper. + getNegotiatedLocale().getLanguage(), item); } else { - s_log.debug("The URL is encoded with a langauge; " + - "fetching the appropriate item from " + - "the bundle"); + LOGGER.debug("The URL is encoded with a langauge; " + + "fetching the appropriate item from " + + "the bundle"); /* * So the request contains a language code as an * extension of the "name" ==>go ahead and try to @@ -885,21 +869,22 @@ public class MultilingualItemResolver * the bundle does not contain an instance for the * given language. */ - - final ContentItem resolved = - ((ContentBundle) item).getInstance(lang); - - if (s_log.isDebugEnabled()) { - s_log.debug("Resolved URL to item " + resolved); + + final ContentItem resolved + = ((ContentBundle) item).getInstance(lang); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Resolved URL to item " + resolved); } return resolved; } } else { - if (s_log.isDebugEnabled()) { - s_log.debug("I expected to get a content bundle; I got " + - item); + if (LOGGER.isDebugEnabled()) { + LOGGER. + debug("I expected to get a content bundle; I got " + + item); } - + /* * We expected something like a Bundle, but it seems * like we got something completely different... just @@ -908,9 +893,8 @@ public class MultilingualItemResolver * * NOTE: This should never happen :-) */ - return item; // might be null } } - + } 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 deleted file mode 100755 index ad268f1fb..000000000 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.off +++ /dev/null @@ -1,684 +0,0 @@ -/* - * 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.logging.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; -import org.apache.logging.log4j.LogManager; -import org.librecms.contenttypes.AuthoringKitInfo; -import org.librecms.contenttypes.ContentTypeInfo; - -/** - * 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.

- * - */ -public class AuthoringKitWizard extends LayoutPanel implements Resettable { - - /** Private Logger instance for this class */ - private static final Logger LOGGER = LogManager.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 ContentTypeInfo m_type; - private final AuthoringKitInfo 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 model - */ - public AuthoringKitWizard(final ContentTypeInfo type, - final ItemSelectionModel model) { - if (LOGGER.isDebugEnabled()) { - LOGGER.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 (LOGGER.isDebugEnabled()) { - while (it.hasNext()) { - LOGGER.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(); - LOGGER.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 - LOGGER.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()))) { - LOGGER.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 (LOGGER.isDebugEnabled()) { - LOGGER.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/AuthoringKitWizard.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo new file mode 100755 index 000000000..8f51a7557 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringKitWizard.java.todo @@ -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.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.globalization.GlobalizedMessage; +import com.arsdigita.kernel.KernelConfig; +import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.toolbox.ui.ModalPanel; +import com.arsdigita.toolbox.ui.Section; +import com.arsdigita.util.Assert; +import com.arsdigita.util.SequentialMap; +import com.arsdigita.util.UncheckedWrapperException; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Locale; +import java.util.Objects; +import java.util.ResourceBundle; +import org.apache.logging.log4j.LogManager; +import org.arsdigita.cms.CMSConfig; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.configuration.ConfigurationManager; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItem; +import org.librecms.contenttypes.AuthoringKit; +import org.librecms.contenttypes.AuthoringKitInfo; +import org.librecms.contenttypes.AuthoringStepInfo; +import org.librecms.contenttypes.ContentTypeInfo; +import org.librecms.workflow.CmsTaskType; + +/** + * 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. + * + */ +public class AuthoringKitWizard extends LayoutPanel implements Resettable { + + /** + * Private Logger instance for this class + */ + private static final Logger LOGGER = LogManager.getLogger( + AuthoringKitWizard.class); + private static Class[] arguments = new Class[]{ + ItemSelectionModel.class, + AuthoringKitWizard.class + }; + private static Class[] userDefinedArgs = new Class[]{ + ItemSelectionModel.class, + AuthoringKitWizard.class, + ContentType.class + }; + private static final java.util.List ASSETS = new ArrayList(); + private final Object[] values; + private final ContentTypeInfo typeInfo; + private final AuthoringKitInfo kitInfo; + private final ItemSelectionModel selectionModel; + private final WorkflowRequestLocal workflowRequestLocal; + private final AssignedTaskTable assignedTaskTable; + private final SequentialMap labels; + private final List list; + private String defaultKey; + private final GridPanel leftPanel; + private final ModalPanel bodyPanel; + private final SimpleContainer stepsContainer; + 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_"; + + private final static String SEC_PAGE_EDIT_DYN = "com.arsdigita.cms.ui.authoring.SecondaryPageEditDynamic"; + private final static String PAGE_EDIT_DYN = "com.arsdigita.cms.ui.authoring.PageEditDynamic"; + + /** + * Construct a new AuthoringKitWizard. Add all the steps in the authoring + * kit to the wizard. + * + * @param typeInfo The content type of the items that this wizard will + * handle + * @param selectionModel + */ + public AuthoringKitWizard(final ContentTypeInfo typeInfo, + final ItemSelectionModel selectionModel) { + LOGGER.debug("Authoring kit wizard for type {} undergoing creation...", + Objects.toString(typeInfo)); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ConfigurationManager confManager = cdiUtil.findBean( + ConfigurationManager.class); + + this.typeInfo = typeInfo; + kitInfo = typeInfo.getAuthoringKit(); + this.selectionModel = selectionModel; + values = new Object[]{selectionModel, this}; + workflowRequestLocal = new ItemWorkflowRequestLocal(); + labels = new SequentialMap(); + + leftPanel = new GridPanel(1); + setLeft(leftPanel); + + assignedTaskTable = new AssignedTaskTable(workflowRequestLocal); + + leftPanel.add(new AssignedTaskSection(workflowRequestLocal, + assignedTaskTable)); + + final Section stepSection = new Section(gz("cms.ui.authoring.steps")); + leftPanel.add(stepSection); + + list = new List(); + stepSection.setBody(list); + + list.setListData(labels); + list.setCellRenderer(new ListCellRenderer() { + + @Override + public Component getComponent(List list, + PageState state, + Object value, + String key, + int index, + boolean isSelected) { + final Label label; + if (value instanceof GlobalizedMessage) { + label = new Label((GlobalizedMessage) value); + } else { + label = new Label((String) value); + } + if (isSelected) { + label.setFontWeight(Label.BOLD); + return label; + } + return new ControlLink(label); + } + }); + + bodyPanel = new ModalPanel(); + setBody(bodyPanel); + + stepsContainer = new SimpleContainer(); + bodyPanel.add(stepsContainer); + bodyPanel.setDefault(stepsContainer); + + final java.util.List steps = kitInfo. + getAuthoringSteps(); + + if (Assert.isEnabled()) { + Assert.isTrue(!steps.isEmpty(), + String.format("The authoring kit for content type " + + "s\"%s\" has no steps.", + typeInfo.getContentItemClass().getName())); + } + + final CMSConfig cmsConfig = confManager.findConfiguration( + CMSConfig.class); + + StepComponent panel = null; + for (final AuthoringStepInfo step : steps) { + final String key = step.getComponent().getName(); + + if (defaultKey == null) { + defaultKey = key; + } + + /** + * The "label" and "description" are only here for backwards + * compatibility + */ + final ResourceBundle labelBundle = ResourceBundle.getBundle(step. + getLabelBundle()); + final ResourceBundle descBundle = ResourceBundle.getBundle(step. + getDescriptionBundle()); + final String labelKey = step.getLabelKey(); + final String label = labelBundle.getString(labelKey); + final String descriptionKey = step.getDescriptionKey(); + final String description = descBundle.getString(descriptionKey); + + final Class componentClass = step. + getComponent(); + final String compClassName = componentClass.getName(); + + if (panel != null) { + panel.setNextStepKey(step.getClass().getName()); + } + panel = new StepComponent(compClassName); + stepsContainer.add(panel); + final Component comp; + + if (compClassName.equals(SEC_PAGE_EDIT_DYN) + || compClassName.equals(PAGE_EDIT_DYN)) { + comp = instantiateUserDefinedStep(compClassName, typeInfo); + } else { + comp = instantiateStep(compClassName); + } + panel.add(comp); + // XXX should be optional + if (comp instanceof AuthoringStepComponent) { + ((AuthoringStepComponent) comp).addCompletionListener( + new StepCompletionListener()); + } + + final GlobalizedMessage gzLabel; + if (labelKey != null) { + if (step.getLabelBundle() == null) { + gzLabel = gz(labelKey); + } else { + gzLabel = new GlobalizedMessage(labelKey, + step.getLabelBundle()); + } + } else { + gzLabel = null; + } + if (gzLabel == null) { + labels.put(key, label); + } else { + labels.put(key, gzLabel); + } + } + + final Class typeClass = typeInfo. + getContentItemClass(); + + final java.util.List skipSteps = cmsConfig.getSkipAssetSteps(); + if (LOGGER.isDebugEnabled()) { + for (final String step : skipSteps) { + LOGGER.debug("skip step \"{}\"...", step); + } + } + + for (final AssetStepEntry data : ASSETS) { + + final Class baseObjectType; + try { + baseObjectType = Class.forName(data.getBaseDataObjectType()); + } catch (ClassNotFoundException ex) { + throw new UncheckedWrapperException(ex); + } + //Class step = (Class) data[1]; + Class step = data.getStep(); + LOGGER.debug("possibly adding asset step " + step.getName()); + if (!skipSteps.contains(step.getName())) { + GlobalizedMessage label = data.getLabel(); + + if (!typeClass.isAssignableFrom(baseObjectType)) { + continue; + } + + if (panel != null) { + panel.setNextStepKey(step); + } + panel = new StepComponent(step); + stepsContainer.add(panel); + + Component comp = instantiateStep(step.getName()); + if (comp instanceof AuthoringStepComponent) { + ((AuthoringStepComponent) comp).addCompletionListener( + new StepCompletionListener()); + } + panel.add(comp); + + labels.put(step, label); + } + } + + list.addChangeListener(new StepListener()); + + m_taskFinishForm = new TaskFinishForm(new TaskSelectionRequestLocal()); + bodyPanel.add(m_taskFinishForm); + + bodyPanel.connect(assignedTaskTable, 2, m_taskFinishForm); + bodyPanel.connect(m_taskFinishForm); + + m_taskFinishForm.addProcessListener(new FormProcessListener() { + + @Override + public final void process(final FormSectionEvent event) + throws FormProcessException { + final PageState state = event.getPageState(); + + assignedTaskTable.getRowSelectionModel().clearSelection(state); + } + }); + } + + private final class StepListener implements ChangeListener { + + @Override + public final void stateChanged(final ChangeEvent event) { + final PageState state = event.getPageState(); + final String key = list.getSelectedKey(state).toString(); + + final Iterator iter = stepsContainer.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 { + + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + if (ContentItemPage.isStreamlinedCreationActive(state)) { + final String key = list.getSelectedKey(state).toString(); + + final Iterator iter = stepsContainer.children(); + + while (iter.hasNext()) { + final StepComponent step = (StepComponent) iter.next(); + if (step.getStepKey().toString().equals(key)) { + Object nextStep = step.getNextStepKey(); + if (nextStep != null) { + list.getSelectionModel().setSelectedKey( + state, nextStep.toString()); + } + } + } + } + } + } + + @Override + public final void register(final Page page) { + super.register(page); + + final Iterator iter = stepsContainer.children(); + + while (iter.hasNext()) { + final StepComponent child = (StepComponent) iter.next(); + + page.setVisibleDefault(child, false); + } + + page.addActionListener(new ActionListener() { + + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + + if (state.isVisibleOnPage(AuthoringKitWizard.this)) { + final SingleSelectionModel model = list. + getSelectionModel(); + + if (!model.isSelected(state)) { + model.setSelectedKey(state, defaultKey); + } + } + } + }); + } + + public static void registerAssetStep(final String baseObjectType, + final Class step, + final GlobalizedMessage label, + final GlobalizedMessage description, + final 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 + LOGGER.debug("registering asset step - label: \"{}\"; " + + "step class: \"%s\"", + label.localize(), + step.getName()); + + for (final AssetStepEntry data : ASSETS) { + + final String thisObjectType = data.getBaseDataObjectType(); + final 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()))) { + LOGGER.debug( + "registering authoring step with same label as previously registered step"); + ASSETS.remove(data); + break; + } + } + ASSETS.add( + new AssetStepEntry(baseObjectType, step, label, description, + sortKey)); + Collections.sort(ASSETS); + } + + 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; + } + + @Override + public int compareTo(final AssetStepEntry other) { + if ((int) sortKey == (int) other.getSortKey()) { + return step.getName().compareTo(other.getStep().getName()); + } else { + return sortKey.compareTo(other.getSortKey()); + } + } + } + + /** + * @return The content type handled by this wizard + */ + public ContentTypeInfo getContentType() { + return typeInfo; + } + + public List getList() { + return list; + } + + /** + * @return The authoring kit which is represented by this wizard + */ + public AuthoringKitInfo getAuthoringKit() { + return kitInfo; + } + + /** + * @return The ItemSelectionModel used by the steps in this wizard + */ + public ItemSelectionModel getItemSelectionModel() { + return selectionModel; + } + + /** + * Instantiate the specified authoring kit step. Will throw a + * RuntimeException on failure. + * + * @param className The Java class name of the step + * @return The instance of the component. + */ + protected Component instantiateStep(final String className) { + LOGGER.debug("Instantiating kit wizard \"{}\" with arguments {}...", + className, + arguments); + + Object[] vals; + try { + // Get the creation component + final Class createClass = Class.forName(className); + final Constructor constr = createClass.getConstructor(arguments); + final Component component = (Component) constr.newInstance(values); + + return component; + } catch (ClassNotFoundException + | IllegalAccessException + | IllegalArgumentException + | InstantiationException + | InvocationTargetException + | NoSuchMethodException + | SecurityException ex) { + throw new UncheckedWrapperException(String.format( + "Failed to instantiate authoring kit component \"{}\".", + className), + ex); + } + } + + /** + * 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 originatingType + * @return + */ + protected Component instantiateUserDefinedStep( + final String className, final ContentTypeInfo originatingType) { + + Object[] vals; + try { + // Get the creation component + final Class createClass = Class.forName(className); + final Constructor constr = createClass.getConstructor( + userDefinedArgs); + final Object[] userDefinedVals = new Object[]{selectionModel, + this, + originatingType}; + final Component component = (Component) constr.newInstance( + userDefinedVals); + + return component; + } catch (ClassNotFoundException | NoSuchMethodException + | InstantiationException | IllegalAccessException + | InvocationTargetException ex) { + throw new UncheckedWrapperException(ex); + } + } + + /** + * Reset the state of this wizard + */ + @Override + public final void reset(final PageState state) { + list.setSelectedKey(state, defaultKey); + } + + private final class StepComponent extends SimpleContainer { + + private final Object key; + private Object nextKey; + + public StepComponent(final Object key) { + this.key = key; + } + + public Object getStepKey() { + return key; + } + + public Object getNextStepKey() { + return nextKey; + } + + public void setNextStepKey(final Object nextKey) { + this.nextKey = nextKey; + } + } + + private final class TaskSelectionRequestLocal extends TaskRequestLocal { + + @Override + protected final Object initialValue(final PageState state) { + final String key = assignedTaskTable + .getRowSelectionModel() + .getSelectedKey(state) + .toString(); + + return CmsTaskType.valueOf(key); + } + } + + 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/authoring/AuthoringStepComponent.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringStepComponent.java new file mode 100755 index 000000000..bb75d953a --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/AuthoringStepComponent.java @@ -0,0 +1,37 @@ +/* + * 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.authoring; + +import com.arsdigita.bebop.event.ActionListener; + +/** + * Interface which authoring step components should implement. It's + * currently an optional interface. + * + * @author Scott Seago (sseago@redhat.com) + * @version $Revision: #4 $ $DateTime: 2004/08/17 23:15:09 $ + */ +public interface AuthoringStepComponent { + + /** + * Add a completion listener to this component + */ + void addCompletionListener(ActionListener listener); + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/LanguageWidget.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/LanguageWidget.java new file mode 100755 index 000000000..f484cbc65 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/LanguageWidget.java @@ -0,0 +1,65 @@ +/* + * 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.authoring; + +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.SingleSelect; +import com.arsdigita.bebop.parameters.ParameterModel; +import com.arsdigita.bebop.parameters.StringParameter; +import org.librecms.util.LanguageUtil; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.Pair; + +import java.util.List; +import org.libreccm.cdi.utils.CdiUtil; + +/** + * Language picker for the multilingual content items. + */ +public class LanguageWidget extends SingleSelect { + + public LanguageWidget(String name) { + this(new StringParameter(name)); + } + + public LanguageWidget(ParameterModel model) { + super(model); + setupOptions(); + } + + /** + * Adds list of languages. Default version shows all supported languages for + * this CMS installation, as defined in the enterprise.init: + * com.arsdigita.cms.installer.Initializer: languages + */ + protected void setupOptions() { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final LanguageUtil languageUtil = cdiUtil.findBean(LanguageUtil.class); + final List languages = languageUtil.convertToG11N(languageUtil. + getSupportedLanguages2LA()); + for(final Pair pair : languages) { + final String langCode = (String) pair.getKey(); + final GlobalizedMessage langName = (GlobalizedMessage) pair.getValue(); + + addOption(new Option(langCode, new Label(langName))); + } + } + +} 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.todo similarity index 98% rename from ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.off rename to ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/WizardSelector.java.todo index 9754fa27d..93be56991 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.todo @@ -57,7 +57,7 @@ import java.math.BigDecimal; public class WizardSelector extends AuthoringKitSelector implements Resettable { - private ItemSelectionModel itemSelectionModel; + private final ItemSelectionModel itemSelectionModel; /** * Construct a new WizardSelector. Load all the possible authoring kits from diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo new file mode 100755 index 000000000..7a3d21aec --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo @@ -0,0 +1,203 @@ +/* + * 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.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.bebop.form.Submit; +import org.librecms.contentsection.ContentItem; +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.authoring.LanguageWidget; +import org.librecms.util.LanguageUtil; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.toolbox.ui.ActionGroup; +import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.toolbox.ui.Section; +import com.arsdigita.util.Assert; +import com.arsdigita.util.Pair; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.web.URL; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowTemplate; +import org.apache.log4j.Logger; + +import java.util.Collection; +import java.util.Iterator; +import java.util.TooManyListenersException; + +/** + * Displays the "Language instances" pane, with all the language instances in + * the Bundle. + * + * @author Alan Pevec (apevec@redhat.com) + * @author Jens Pelzetter + */ +public class ItemLanguages extends LayoutPanel { + + private final ItemSelectionModel selectionModel; + private final LanguageWidget languageWidget; + private final Submit changeSubmit; + private final Submit createSubmit; + + /** + * Constructs a new ItemLanguages. + * + * @param selectionModel the {@link ItemSelectionModel} which will supply the current + * item + */ + public ItemLanguages(final ItemSelectionModel selectionModel) { + this.selectionModel = selectionModel; + + final Section section = new Section(gz("cms.ui.item.languages")); + setBody(section); + + final ActionGroup group = new ActionGroup(); + section.setBody(group); + + group.setSubject(new ItemLanguagesTable(selectionModel)); + + final Form form = new Form("newLanguage", new BoxPanel( + BoxPanel.HORIZONTAL)); + group.addAction(form); + + form.setRedirecting(true); + languageWidget = new LanguageWidget(ContentItem.LANGUAGE) { + protected void setupOptions() { + // Don't do anything. + } + }; + + try { + languageWidget.addPrintListener(new OptionPrinter()); + } catch (TooManyListenersException tmle) { + new UncheckedWrapperException(tmle); + } + + form.add(languageWidget); + changeSubmit = new Submit("change", gz("cms.ui.item.language.change")); + form.add(changeSubmit); + createSubmit = new Submit("create", gz("cms.ui.item.language.add")); + form.add(createSubmit); + form.addProcessListener(new ProcessListener()); + } + + /** + * Offers only languages not yet present in the bundle. + */ + private class OptionPrinter implements PrintListener { + + public final void prepare(final PrintEvent e) { + final PageState state = e.getPageState(); + final OptionGroup optionGroup = (OptionGroup) e.getTarget(); + final ContentPage item = (ContentPage) selectionModel. + getSelectedItem(state); + final Collection languages = LanguageUtil.convertToG11N( + LanguageUtil.getCreatableLanguages(item)); + + for (Iterator iter = languages.iterator(); iter.hasNext();) { + final Pair pair = (Pair) iter.next(); + final String langCode = (String) pair.getKey(); + final GlobalizedMessage langName + = (GlobalizedMessage) pair.getValue(); + optionGroup.addOption(new Option(langCode, new Label(langName))); + } + } + } + + /** + * Adds a new language instance to the bundle. + */ + private class ProcessListener implements FormProcessListener { + + public final void process(final FormSectionEvent e) + throws FormProcessException { + PageState state = e.getPageState(); + String lang = (String) languageWidget.getValue(state); + ContentPage item = (ContentPage) selectionModel.getSelectedItem(state); + ContentBundle bundle = item.getContentBundle(); + String name = bundle.getName(); + + if (createSubmit.isSelected(state)) { + ContentSection section = item.getContentSection(); + + Assert.exists(section, ContentSection.class); + + ContentType type = item.getContentType(); + + item = (ContentPage) item.copy(lang); + item.setLanguage(lang); + item.setName(name); + + // Apply default workflow + WorkflowTemplate template + = ContentTypeWorkflowTemplate + .getWorkflowTemplate(section, type); + if (template != null) { + Workflow w = template.instantiateNewWorkflow(); + w.setObjectID(item.getID()); + w.start(Kernel.getContext().getUser()); + w.save(); + } + + selectionModel.setSelectedObject(state, item); + + // redirect to ContentItemPage.AUTHORING_TAB of the new instance + final String target = URL.getDispatcherPath() + ContentItemPage. + getItemURL(item, + ContentItemPage.AUTHORING_TAB); + + throw new RedirectSignal(target, true); + } else if (changeSubmit.isSelected(state)) { + String oldLang = item.getLanguage(); + item.setLanguage(lang); + // propagate language change to the Name attribute + item.setName(name); + item.save(); + + // if the item being changed is the default, update the bundle + // to keep this item as the default + if (bundle.getDefaultLanguage().equals(oldLang)) { + bundle.setDefaultLanguage(lang); + bundle.save(); + } + } + } + } + + protected static final GlobalizedMessage gz(final String key) { + return GlobalizationUtil.globalize(key); + } + + protected static final String lz(final String key) { + return (String) gz(key).localize(); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo new file mode 100755 index 000000000..0aa27f364 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo @@ -0,0 +1,255 @@ +/* + * 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.*; +import com.arsdigita.bebop.event.TableActionAdapter; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.dispatcher.MultilingualItemResolver; +import com.arsdigita.cms.ui.ContentItemPage; +import org.librecms.util.LanguageUtil; +import com.arsdigita.toolbox.ui.DataTable; +import com.arsdigita.util.LockableImpl; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.web.URL; +import java.math.BigDecimal; +import org.apache.log4j.Logger; + +/** + * Displays a list of all language instances of an item. + * + */ +public class ItemLanguagesTable extends DataTable { + + private static final Logger LOGGER = Logger.getLogger(ItemLanguagesTable.class); + private final ItemSelectionModel m_model; + private final TableColumn m_deleteColumn; + + /** + * Construct a new + * ItemHistoryTable + * + * @param model the ItemSelectionModel that supplies the current item + */ + public ItemLanguagesTable(ItemSelectionModel model) { + super(new LanguagesBuilder(model)); + m_model = model; + + addColumn("cms.ui.language.header", + ContentPage.LANGUAGE, + false, + new LanguageCellRenderer(m_model)); + addColumn("cms.ui.title", + ContentPage.TITLE); + m_deleteColumn = addColumn("cms.ui.action", new ActionCellRenderer( + m_model)); + setResourceBundle(GlobalizationUtil.getBundleName()); + addTableActionListener(new InstanceDeleter(m_model)); + } + + /** + * Builds the query for all the language instances in the current Bundle + */ + private static class LanguagesBuilder extends LockableImpl + implements DataQueryBuilder { + + ItemSelectionModel m_model; + + public LanguagesBuilder(ItemSelectionModel model) { + super(); + m_model = model; + } + + public DataQuery makeDataQuery(DataTable t, PageState s) { + ContentPage multiLingual = + (ContentPage) m_model.getSelectedObject(s); + DataQuery q = SessionManager.getSession().retrieveQuery( + "com.arsdigita.cms.getBundledItems"); + q.setParameter("bundleID", multiLingual.getContentBundle().getID()); + return q; + } + + public String getKeyColumn() { + return ContentPage.ID; + } + } + + /** + * Renders the full language name. + */ + private static class LanguageCellRenderer implements TableCellRenderer { + + private ItemSelectionModel m_model; + + public LanguageCellRenderer(ItemSelectionModel model) { + m_model = model; + } + + public Component getComponent(Table table, PageState state, Object value, + boolean isSelected, Object key, + int row, int column) { + + BigDecimal id = (BigDecimal) key; + ContentPage cp; + + try { + cp = new ContentPage(id); + } catch (DataObjectNotFoundException ex) { + // Content item was not found, return nothing + return new Label(); + } + + ContentBundle bundle = cp.getContentBundle(); + + if (bundle != null + && !(cp instanceof LanguageInvariantContentItem + && ((LanguageInvariantContentItem) cp).isLanguageInvariant())) { + + StringBuilder fontWeight = new StringBuilder(2); + StringBuilder classes = new StringBuilder(20); + + if (cp.isLive()) { + fontWeight.append(Label.BOLD); + classes.append("live "); + } + if (bundle.getPrimaryInstance().equals(cp)) { + fontWeight.append(Label.ITALIC); + classes.append("primaryInstance"); + } + + String target = ContentItemPage.getItemURL(cp, ContentItemPage.AUTHORING_TAB); + Label langLabel = new Label(LanguageUtil.getLangFull((String) value)); + + langLabel.setFontWeight(fontWeight.toString().trim()); + langLabel.setClassAttr(classes.toString().trim()); + + if (m_model.getSelectedKey(state).equals(key)) { + // Current instance: no link + return langLabel; + } else { + return new Link(langLabel, target); + } + } + + return new Label(); + } + } + + /** + * Delete language instance action link. + */ + private static class ActionCellRenderer implements TableCellRenderer { + + private static final Logger logger = + Logger.getLogger(ActionCellRenderer.class); + private static final Label s_noAction; + private static final Label s_primary; + private static final ControlLink s_link; + + static { + logger.debug("Static initializer is starting..."); + s_noAction = new Label(" ", false); + s_noAction.lock(); + s_primary = new Label(GlobalizationUtil.globalize( + "cms.ui.primary_instance"), false); + s_primary.lock(); + s_link = new ControlLink(new Label(GlobalizationUtil.globalize( + "cms.ui.delete"))); + s_link.setConfirmation(GlobalizationUtil.globalize( + "cms.ui.delete_confirmation")); + logger.debug("Static initalizer finished."); + } + private ItemSelectionModel m_model; + + public ActionCellRenderer(ItemSelectionModel model) { + m_model = model; + } + + public Component getComponent(Table table, PageState state, Object value, + boolean isSelected, Object key, + int row, int column) { + // check if primary instance + BigDecimal id = new BigDecimal(key.toString()); + OID oid = new OID(ContentPage.BASE_DATA_OBJECT_TYPE, id); + try { + ContentPage item = (ContentPage) DomainObjectFactory.newInstance(oid); + if (item.getLanguage().equals( + item.getContentBundle().getDefaultLanguage())) { + return s_primary; + } else if (item.isLive()) { + return s_noAction; + } + } catch (DataObjectNotFoundException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Could not get item with id " + id); + } + return s_noAction; + } + return s_link; + } + } + + // delete one language instance + private class InstanceDeleter extends TableActionAdapter { + + private ItemSelectionModel m_model; + + public InstanceDeleter(ItemSelectionModel model) { + m_model = model; + } + + @Override + public void cellSelected(TableActionEvent e) { + int col = e.getColumn().intValue(); + + if (m_deleteColumn != getColumn(col)) { + return; + } + + PageState s = e.getPageState(); + BigDecimal id = new BigDecimal(e.getRowKey().toString()); + + OID oid = new OID(ContentPage.BASE_DATA_OBJECT_TYPE, id); + try { + ContentPage item = + (ContentPage) DomainObjectFactory.newInstance(oid); + ContentBundle bundle = item.getContentBundle(); + bundle.removeInstance(item); + item.delete(); + + if (m_model.getSelectedKey(s).equals(id)) { + throw new RedirectSignal( + URL.there((new MultilingualItemResolver() + .generateItemURL(s, + bundle.getPrimaryInstance(), + bundle.getContentSection(), + ContentItem.DRAFT)), + null), + true); + } + } catch (com.arsdigita.domain.DataObjectNotFoundException ex) { + // Object not found is ok, it has probably been deleted already + } + ((Table) e.getSource()).clearSelection(s); + } + } +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java index ce6e3ee34..400880964 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java @@ -134,6 +134,13 @@ public class ContentItemL10NManager { return collectLanguages(item).contains(locale); } + + @Transactional(Transactional.TxType.REQUIRED) + public Set creatableLocales(final ContentItem item) { + return supportedLocales.stream() + .filter(locale -> hasLanguage(item, locale)) + .collect(Collectors.toSet()); + } /** * Adds a language to a content item. The method will retrieve all fields of