diff --git a/ccm-cms/src/com/arsdigita/cms/CMSConfig.java b/ccm-cms/src/com/arsdigita/cms/CMSConfig.java index d35b985e2..09241a9af 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSConfig.java +++ b/ccm-cms/src/com/arsdigita/cms/CMSConfig.java @@ -51,8 +51,8 @@ import org.apache.log4j.Logger; /** * A record containing server-session scoped configuration properties. * - * Accessors of this class may return null. Developers should take care to trap null return values - * in their code. + * Accessors of this class may return null. Developers should take care to trap + * null return values in their code. * * @see ContentSection#getConfig() * @@ -71,7 +71,8 @@ public final class CMSConfig extends AbstractConfig { private static CMSConfig s_config; /** - * Returns the singleton configuration record for the content section environment. + * Returns the singleton configuration record for the content section + * environment. * * @return The CMSConfig record; it cannot be null */ @@ -85,36 +86,40 @@ public final class CMSConfig extends AbstractConfig { } /** - * Storage (map) for method getAssetStepsToSkip(ContentType type) to store mapping of steps that - * are deemed irrelevant for the passid in type. + * Storage (map) for method getAssetStepsToSkip(ContentType type) to store + * mapping of steps that are deemed irrelevant for the passid in type. */ private static Map s_skipAssetSteps = null; /** - * Item category add form specifies Subclass of ItemCategoryForm to use for the assign - * categories step. Used in c.ad.cms.ui.authoring.ItemCategoryStep + * Item category add form specifies Subclass of ItemCategoryForm to use for + * the assign categories step. Used in + * c.ad.cms.ui.authoring.ItemCategoryStep */ - private final Parameter m_categoryAuthoringAddForm = new SpecificClassParameter( - "com.arsdigita.cms.category_authoring_add_form", - Parameter.REQUIRED, - ItemCategoryForm.class, - SimpleComponent.class); + private final Parameter m_categoryAuthoringAddForm + = new SpecificClassParameter( + "com.arsdigita.cms.category_authoring_add_form", + Parameter.REQUIRED, + ItemCategoryForm.class, + SimpleComponent.class); /** - * Path for the default item template. Path is relative to the Template Root path. + * Path for the default item template. Path is relative to the Template Root + * path. */ private final Parameter m_defaultItemTemplatePath = new StringParameter( "com.arsdigita.cms.default_item_template_path", Parameter.REQUIRED, "/default/item.jsp"); /** - * Path for the default folder template. Path is relative to the Template Root path. + * Path for the default folder template. Path is relative to the Template + * Root path. */ private final Parameter m_defaultFolderTemplatePath = new StringParameter( "com.arsdigita.cms.default_folder_template_path", Parameter.REQUIRED, "/default/folder.jsp"); /** - * Path or the root folter for template folders. Path is relative to webapp root. Modify with - * care! Usually modified by developers only! + * Path or the root folter for template folders. Path is relative to webapp + * root. Modify with care! Usually modified by developers only! */ private final Parameter m_templateRootPath = new StringParameter( "com.arsdigita.cms.template_root_path", @@ -134,8 +139,8 @@ public final class CMSConfig extends AbstractConfig { // } // ADD: /** - * Item Adapters File, path to an XML resource containing adapter specifications. Path is - * relative to webapp root. + * Item Adapters File, path to an XML resource containing adapter + * specifications. Path is relative to webapp root. */ private final Parameter m_itemAdapters = new ResourceParameter( "com.arsdigita.cms.item_adapters", @@ -143,22 +148,23 @@ public final class CMSConfig extends AbstractConfig { "/WEB-INF/resources/cms-item-adapters.xml"); // URL resource: protocol handler removal: END /** - * Use streamlined content creation: upon item creation, automatically open authoring steps and - * forward to the next step + * Use streamlined content creation: upon item creation, automatically open + * authoring steps and forward to the next step */ private final Parameter m_useStreamlinedCreation = new BooleanParameter( "com.arsdigita.cms.use_streamlined_creation", Parameter.REQUIRED, Boolean.TRUE); /** - * DHTML Editor Configuration for use in CMS module, lists the config object name and Javascript - * source location for its definition. + * DHTML Editor Configuration for use in CMS module, lists the config object + * name and Javascript source location for its definition. */ - private final Parameter m_dhtmlEditorConfig = new DHTMLEditorConfigParameter( - "com.arsdigita.cms.dhtml_editor_config", - Parameter.REQUIRED, - new DHTMLEditor.Config("Xinha.Config", - "/assets/xinha/CCMcmsXinhaConfig.js")); + private final Parameter m_dhtmlEditorConfig + = new DHTMLEditorConfigParameter( + "com.arsdigita.cms.dhtml_editor_config", + Parameter.REQUIRED, + new DHTMLEditor.Config("Xinha.Config", + "/assets/xinha/CCMcmsXinhaConfig.js")); // previous parameter definition: // > DHTMLEditor.Config.STANDARD); < // didn't work because of broken unmarshalling (cf. similiar problem @@ -170,20 +176,22 @@ public final class CMSConfig extends AbstractConfig { // be accessable by other modules which use DHTMLeditor. // Would be bad style to configure a cms specific parameter in core. /** - * Defines which plugins to use, e.g.TableOperations,CSS Format: [string,string,string] + * Defines which plugins to use, e.g.TableOperations,CSS Format: + * [string,string,string] */ private final Parameter m_dhtmlEditorPlugins = new StringArrayParameter( "com.arsdigita.cms.dhtml_editor_plugins", Parameter.OPTIONAL, null); /** - * Prevent undesirable functions from being made available, eg images should only be added - * through the cms methods. + * Prevent undesirable functions from being made available, eg images should + * only be added through the cms methods. */ - private final Parameter m_dhtmlEditorHiddenButtons = new StringArrayParameter( - "com.arsdigita.cms.dhtml_editor_hidden_buttons", - Parameter.OPTIONAL, - null); + private final Parameter m_dhtmlEditorHiddenButtons + = new StringArrayParameter( + "com.arsdigita.cms.dhtml_editor_hidden_buttons", + Parameter.OPTIONAL, + null); /** * Hide section admin tabs from users without administrative rights. */ @@ -227,8 +235,8 @@ public final class CMSConfig extends AbstractConfig { Parameter.REQUIRED, Boolean.FALSE); /** - * Hide timezone labels (if, for example, all users will be in the same timezone and such - * information would be unnecessary) + * Hide timezone labels (if, for example, all users will be in the same + * timezone and such information would be unnecessary) */ private final Parameter m_hideTimezone = new BooleanParameter( "com.arsdigita.cms.hide_timezone", @@ -244,47 +252,54 @@ public final class CMSConfig extends AbstractConfig { /** * Specifies the name of the class to use as a PublishLifecycleListener */ - private final Parameter m_publishLifecycleListenerClass = new StringParameter( - "com.arsdigita.cms.publish_lifecycle_listener_class", - Parameter.OPTIONAL, - PublishLifecycleListener.class.getName()); + private final Parameter m_publishLifecycleListenerClass + = new StringParameter( + "com.arsdigita.cms.publish_lifecycle_listener_class", + Parameter.OPTIONAL, + PublishLifecycleListener.class.getName()); /** - * Wether the Wysiwyg editor should clear the text of MSWord tags, everytime the user clicks on - * 'Save' + * Wether the Wysiwyg editor should clear the text of MSWord tags, everytime + * the user clicks on 'Save' */ private final Parameter m_saveTextCleansWordTags = new BooleanParameter( "com.arsdigita.cms.save_text_cleans_word_tags", Parameter.OPTIONAL, Boolean.FALSE); /** - * Get the search indexing not to process FileAssets, eg to avoid PDF slowdowns + * Get the search indexing not to process FileAssets, eg to avoid PDF + * slowdowns */ private final Parameter m_disableFileAssetExtraction = new BooleanParameter( "com.arsdigita.cms.search.disableFileAssetExtraction", Parameter.REQUIRED, Boolean.FALSE); /** - * Whether an item's workflow should be deleted, once the item has been (re)published. - * - * jensp 2014-11-07: Default changed from true to false. Deleting the assigned workflow means - * that the authors have to reattach a workflow using the Workflow tab, which is complicated - * (for some users too complicated). Also deleting the workflow means that the new convenient + * Whether an item's workflow should be deleted, once the item has been + * (re)published. + * + * jensp 2014-11-07: Default changed from true to false. Deleting the + * assigned workflow means that the authors have to reattach a workflow + * using the Workflow tab, which is complicated (for some users too + * complicated). Also deleting the workflow means that the new convenient * link to restart a workflow will not work. - * + * */ - private final Parameter m_deleteWorkflowAfterPublication = new BooleanParameter( - "com.arsdigita.cms.delete_workflow_after_publication", - Parameter.REQUIRED, - Boolean.FALSE); + private final Parameter m_deleteWorkflowAfterPublication + = new BooleanParameter( + "com.arsdigita.cms.delete_workflow_after_publication", + Parameter.REQUIRED, + Boolean.FALSE); /** - * Defines the number of days ahead that are covered in the 'Soon Expired' tab + * Defines the number of days ahead that are covered in the 'Soon Expired' + * tab */ private final Parameter m_soonExpiredTimespanDays = new IntegerParameter( "com.arsdigita.cms.soon_expired_timespan_days", Parameter.REQUIRED, new Integer(14)); /** - * Defines the number of months ahead that are covered in the 'Soon Expired' tab + * Defines the number of months ahead that are covered in the 'Soon Expired' + * tab */ private final Parameter m_soonExpiredTimespanMonths = new IntegerParameter( "com.arsdigita.cms.soon_expired_timespan_months", @@ -298,47 +313,51 @@ public final class CMSConfig extends AbstractConfig { Parameter.REQUIRED, Boolean.TRUE); /** - * Links created through browse interfaces should only be within the same subsite + * Links created through browse interfaces should only be within the same + * subsite */ private final Parameter m_linksOnlyInSameSubsite = new BooleanParameter( "com.arsdigita.cms.browse_links_in_same_subsite_only", Parameter.REQUIRED, Boolean.FALSE); /** - * Item category step extension hook: Subclass of ItemCategoryExtension which adds extension - * actions for the category authoring step + * Item category step extension hook: Subclass of ItemCategoryExtension + * which adds extension actions for the category authoring step */ - private final Parameter m_categoryAuthoringExtension = new SpecificClassParameter( - "com.arsdigita.cms.category_authoring_extension", - Parameter.REQUIRED, - ItemCategoryExtension.class, - ItemCategoryExtension.class); + private final Parameter m_categoryAuthoringExtension + = new SpecificClassParameter( + "com.arsdigita.cms.category_authoring_extension", + Parameter.REQUIRED, + ItemCategoryExtension.class, + ItemCategoryExtension.class); /** - * Link available to reset lifecycle on republish. If false don't display the link otherwise - * display. + * Link available to reset lifecycle on republish. If false don't display + * the link otherwise display. */ private final Parameter m_hideResetLifecycleLink = new BooleanParameter( "com.arsdigita.cms.hide_reset_lifecycle_link", Parameter.OPTIONAL, Boolean.TRUE); /** - * Whether to include INPATH operators to contains clause in intermedia search + * Whether to include INPATH operators to contains clause in intermedia + * search */ private final Parameter m_scoreTitleAndKeywords = new BooleanParameter( "com.arsdigita.cms.search.score_title_and_keywords", Parameter.OPTIONAL, Boolean.FALSE); /** - * Title Weight, the relative weight given to title element within cms:item when ranking search - * results (only used by interMedia) + * Title Weight, the relative weight given to title element within cms:item + * when ranking search results (only used by interMedia) */ private final Parameter m_titleWeight = new IntegerParameter( "com.arsdigita.cms.search.intermedia.title_weight", Parameter.OPTIONAL, new Integer(1)); /** - * Keyword Weight, the relative weight given to the dcKeywords element within dublinCore element - * within cms:item element when ranking search results (only used by interMedia) + * Keyword Weight, the relative weight given to the dcKeywords element + * within dublinCore element within cms:item element when ranking search + * results (only used by interMedia) */ private final Parameter m_keywordWeight = new IntegerParameter( "com.arsdigita.cms.search.intermedia.keyword_weight", @@ -352,74 +371,83 @@ public final class CMSConfig extends AbstractConfig { Parameter.OPTIONAL, Boolean.TRUE); /** - * Asset steps to skip, specify asset steps that are not relevant for specific content types. - * Each entry in the list is a : separated pair. The first string is the className for the type - * (refer to classname column in contenttypes table eg - * com.arsdigita.cms.contenttypes.MultiPartArticle Second string is the name of the bebop step - * component eg com.arsdigita.cms.contenttypes.ui.ImageStep + * Asset steps to skip, specify asset steps that are not relevant for + * specific content types. Each entry in the list is a : separated pair. The + * first string is the className for the type (refer to classname column in + * contenttypes table eg com.arsdigita.cms.contenttypes.MultiPartArticle + * Second string is the name of the bebop step component eg + * com.arsdigita.cms.contenttypes.ui.ImageStep */ private final Parameter m_skipAssetSteps = new StringArrayParameter( "com.arsdigita.cms.skip_asset_steps", Parameter.OPTIONAL, null); /** - * Mandatory Descriptions Content types may refer to this to decide whether to validate against - * empty descriptions + * Mandatory Descriptions Content types may refer to this to decide whether + * to validate against empty descriptions */ private final Parameter m_mandatoryDescriptions = new BooleanParameter( "com.arsdigita.cms.mandatory_descriptions", Parameter.OPTIONAL, Boolean.FALSE); /** - * Delete Finished Lifecycles. Decide whether lifecycles and their phases should be deleted from - * the system when finished. + * Delete Finished Lifecycles. Decide whether lifecycles and their phases + * should be deleted from the system when finished. */ - private final Parameter m_deleteLifecycleWhenComplete = new BooleanParameter( - "com.arsdigita.cms.delete_lifecycle_when_complete", - Parameter.OPTIONAL, - Boolean.FALSE); + private final Parameter m_deleteLifecycleWhenComplete + = new BooleanParameter( + "com.arsdigita.cms.delete_lifecycle_when_complete", + Parameter.OPTIONAL, + Boolean.FALSE); /** - * Contacts for content items. Allows you to add a Contact authoring step to all items + * Contacts for content items. Allows you to add a Contact authoring step to + * all items */ private final Parameter m_hasContactsAuthoringStep = new BooleanParameter( "com.arsdigita.cms.has_contacts_authoring_step", Parameter.REQUIRED, Boolean.FALSE); /** - * Ordering for nodes in assign category tree. Decide whether entries should be ordered - * alphabetically or according to sort key (maintained in category admin tab in content centre) - * SortKey|Alphabetical is initialized in constructor! See below. + * Ordering for nodes in assign category tree. Decide whether entries should + * be ordered alphabetically or according to sort key (maintained in + * category admin tab in content centre) SortKey|Alphabetical is initialized + * in constructor! See below. */ private final Parameter m_categoryTreeOrdering = new EnumerationParameter( "com.arsdigita.cms.category_tree_order", Parameter.OPTIONAL, Category.SORT_KEY); /** - * Allow creation of a new Use Context in category tab of content sections. "Use Context" is the - * construct to constitute a category hierarchy implementet in core. It is superseded by the - * construct "Category Domain" in Terms (ccm-ldn-terms). Global parameter for all content - * sections. Default is false because all installation bundles use Terms. + * Allow creation of a new Use Context in category tab of content sections. + * "Use Context" is the construct to constitute a category hierarchy + * implementet in core. It is superseded by the construct "Category Domain" + * in Terms (ccm-ldn-terms). Global parameter for all content sections. + * Default is false because all installation bundles use Terms. */ - private final Parameter m_allowCategoryCreateUseContext = new BooleanParameter( - "com.arsdigita.cms.allow_category_create_use_context", - Parameter.REQUIRED, - Boolean.FALSE); + private final Parameter m_allowCategoryCreateUseContext + = new BooleanParameter( + "com.arsdigita.cms.allow_category_create_use_context", + Parameter.REQUIRED, + Boolean.FALSE); /** - * Allow content creation in Workspace (content center) section listing. Allows you to turn off - * the ability to create content in the section listing. - * - * jensp 2014-11-07: Default changed to false. This feature isn't used by most users. Also - * it has some drawbacks, for example items creating using this way are put into the root - * folder. + * Allow content creation in Workspace (content center) section listing. + * Allows you to turn off the ability to create content in the section + * listing. + * + * jensp 2014-11-07: Default changed to false. This feature isn't used by + * most users. Also it has some drawbacks, for example items creating using + * this way are put into the root folder. */ - private final Parameter m_allowContentCreateInSectionListing = new BooleanParameter( - "com.arsdigita.cms.allow_content_create_in_section_listing", - Parameter.REQUIRED, - Boolean.FALSE); + private final Parameter m_allowContentCreateInSectionListing + = new BooleanParameter( + "com.arsdigita.cms.allow_content_create_in_section_listing", + Parameter.REQUIRED, + Boolean.FALSE); /** - * Hide the legacy public site link in Workspace (content center) section listing. Legacy public - * site display is replaced by navigation based presentation (or by portlets) and should be - * hidden in the admin ui by default now. + * Hide the legacy public site link in Workspace (content center) section + * listing. Legacy public site display is replaced by navigation based + * presentation (or by portlets) and should be hidden in the admin ui by + * default now. */ private final Parameter m_hideLegacyPublicSiteLink = new BooleanParameter( "com.arsdigita.cms.hide_legacy_public_site_link", @@ -429,31 +457,34 @@ public final class CMSConfig extends AbstractConfig { // Notification related parameters // /////////////////////////////////////////// /** - * Delete Sent Workflow Notifications. Decide whether successfully sent notifications and - * messages should be deleted from the system + * Delete Sent Workflow Notifications. Decide whether successfully sent + * notifications and messages should be deleted from the system */ - private final Parameter m_deleteWorkflowNotificationsWhenSent = new BooleanParameter( - "com.arsdigita.cms.delete_workflow_notification_when_sent", - Parameter.OPTIONAL, - Boolean.FALSE); + private final Parameter m_deleteWorkflowNotificationsWhenSent + = new BooleanParameter( + "com.arsdigita.cms.delete_workflow_notification_when_sent", + Parameter.OPTIONAL, + Boolean.FALSE); /** - * Decide whether successfully sent notifications and messages should be deleted from the system + * Decide whether successfully sent notifications and messages should be + * deleted from the system */ - private final Parameter m_deleteExpiryNotificationsWhenSent = new BooleanParameter( - "com.arsdigita.cms.delete_expiry_notification_when_sent", - Parameter.OPTIONAL, - Boolean.FALSE); + private final Parameter m_deleteExpiryNotificationsWhenSent + = new BooleanParameter( + "com.arsdigita.cms.delete_expiry_notification_when_sent", + Parameter.OPTIONAL, + Boolean.FALSE); /** - * Amount of time (in hours) before the expiration of a content item that users in the Alert - * Recipient role are alerted via email + * Amount of time (in hours) before the expiration of a content item that + * users in the Alert Recipient role are alerted via email */ private final Parameter m_defaultNotificationTime = new IntegerParameter( "com.arsdigita.cms.default_notification_time", Parameter.REQUIRED, new Integer(0)); /** - * Wether a content item's author should be notified by the item's LifecycleListener; defaults - * to true + * Wether a content item's author should be notified by the item's + * LifecycleListener; defaults to true */ private final Parameter m_notifyAuthorOnLifecycle = new BooleanParameter( "com.arsdigita.cms.notify_author_on_lifecycle", @@ -463,7 +494,8 @@ public final class CMSConfig extends AbstractConfig { // Content Center (Workspace) config related parameters // //////////////////////////////////////////////////// /** - * XML Mapping of the content center tabs to URLs, see {@link ContentCenterDispatcher} + * XML Mapping of the content center tabs to URLs, see + * {@link ContentCenterDispatcher} */ private final StringParameter m_contentCenterMap = new StringParameter( "com.arsdigita.cms.loader.content_center_map", @@ -485,16 +517,18 @@ public final class CMSConfig extends AbstractConfig { // to SectionInitializer. However, it still may be useful to // keep these for the default values. // /////////////////////////////////////////// - private final Parameter m_defaultItemResolverClass = new SpecificClassParameter( - "com.arsdigita.cms.default_item_resolver_class", - Parameter.REQUIRED, - MultilingualItemResolver.class, - ItemResolver.class); - private final Parameter m_defaultTemplateResolverClass = new SpecificClassParameter( - "com.arsdigita.cms.default_template_resolver_class", - Parameter.REQUIRED, - DefaultTemplateResolver.class, - TemplateResolver.class); + private final Parameter m_defaultItemResolverClass + = new SpecificClassParameter( + "com.arsdigita.cms.default_item_resolver_class", + Parameter.REQUIRED, + MultilingualItemResolver.class, + ItemResolver.class); + private final Parameter m_defaultTemplateResolverClass + = new SpecificClassParameter( + "com.arsdigita.cms.default_template_resolver_class", + Parameter.REQUIRED, + DefaultTemplateResolver.class, + TemplateResolver.class); ///////////////////////////////////////////// // ItemSearchWidget ///////////////////////////////////////////// @@ -505,10 +539,11 @@ public final class CMSConfig extends AbstractConfig { // "com.arsdigita.cms.item_search.flat_browse_pane.enable", // Parameter.REQUIRED, // true); - private final Parameter m_itemSearchFlatBrowsePanePageSize = new IntegerParameter( - "com.arsdigita.cms.item_search.flat_browse_pane.page_size", - Parameter.REQUIRED, - 20); + private final Parameter m_itemSearchFlatBrowsePanePageSize + = new IntegerParameter( + "com.arsdigita.cms.item_search.flat_browse_pane.page_size", + Parameter.REQUIRED, + 20); ///////////////////////////////////////////// // FolderBrowse ///////////////////////////////////////////// @@ -528,10 +563,11 @@ public final class CMSConfig extends AbstractConfig { //republish and withdraw items) is used. Otherwise the new style form is //used, which is more secure against wrong clicks. ////////////////////////////////////////////// - private final Parameter m_useOldStyleItemLifecycleItemPane = new BooleanParameter( - "com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane", - Parameter.REQUIRED, - false); + private final Parameter m_useOldStyleItemLifecycleItemPane + = new BooleanParameter( + "com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane", + Parameter.REQUIRED, + false); //////////////////////////////////////////////// //Actives threaded publishing. If active, the publish process for //content items will run in a separate thread. May useful if you have @@ -552,22 +588,25 @@ public final class CMSConfig extends AbstractConfig { ///////////////////////////////////////////////// // ImageBrowser Parameter ///////////////////////////////////////////////// - private final Parameter m_imageBrowserThumbnailMaxWidth = new IntegerParameter( - "com.arsdigita.cms.image_browser.thumbnail_max_width", - Parameter.REQUIRED, - 50); - private final Parameter m_imageBrowserThumbnailMaxHeight = new IntegerParameter( - "com.arsdigita.cms.image_browser.thumbnail_max_height", - Parameter.REQUIRED, - 50); + private final Parameter m_imageBrowserThumbnailMaxWidth + = new IntegerParameter( + "com.arsdigita.cms.image_browser.thumbnail_max_width", + Parameter.REQUIRED, + 50); + private final Parameter m_imageBrowserThumbnailMaxHeight + = new IntegerParameter( + "com.arsdigita.cms.image_browser.thumbnail_max_height", + Parameter.REQUIRED, + 50); private final Parameter m_imageBrowserCaptionSize = new IntegerParameter( "com.arsdigita.cms.image_browser.caption_size", Parameter.REQUIRED, 100); - private final Parameter m_imageBrowserDescriptionSize = new IntegerParameter( - "com.arsdigita.cms.image_browser.description_size", - Parameter.REQUIRED, - 400); + private final Parameter m_imageBrowserDescriptionSize + = new IntegerParameter( + "com.arsdigita.cms.image_browser.description_size", + Parameter.REQUIRED, + 400); private final Parameter m_imageBrowserTitleSize = new IntegerParameter( "com.arsdigita.cms.image_browser.title_size", Parameter.REQUIRED, @@ -627,11 +666,18 @@ public final class CMSConfig extends AbstractConfig { 60 * 60 * 24); /** - * Max length of the description of a link (in database max length are 4000 characters) + * Max length of the description of a link (in database max length are 4000 + * characters) */ private final Parameter m_linkDescMaxLength = new IntegerParameter( "com.arsdigita.cms.link_description_max_length", Parameter.REQUIRED, 400); + /** + * Always use language extension? + */ + private final Parameter m_useLanguageExtension = new BooleanParameter( + "com.arsdigita.cms.use_language_extension", Parameter.REQUIRED, false); + // /////////////////////////////////////////// // publishToFile package related parameter // /////////////////////////////////////////// @@ -743,14 +789,17 @@ public final class CMSConfig extends AbstractConfig { register(m_enableXmlCache); register(m_xmlCacheSize); register(m_xmlCacheAge); - + register(m_linkDescMaxLength); + + register(m_useLanguageExtension); loadInfo(); } /** - * Retrieve path of the root folder for template folders. Path is relative to webapp root. + * Retrieve path of the root folder for template folders. Path is relative + * to webapp root. */ public final String getTemplateRoot() { return (String) get(m_templateRootPath); @@ -790,7 +839,8 @@ public final class CMSConfig extends AbstractConfig { /** * - * @deprecated use com.arsdigita.cms.ContentSection.getDefaultSection().getName() instead + * @deprecated use + * com.arsdigita.cms.ContentSection.getDefaultSection().getName() instead */ public final String getDefaultContentSection() { // return (String) get(m_defaultSection); @@ -901,7 +951,8 @@ public final class CMSConfig extends AbstractConfig { } /** - * Fetch the file name contaning XML Mapping of the content center tabs to URLs + * Fetch the file name contaning XML Mapping of the content center tabs to + * URLs * * @return String containig file name including path component. */ @@ -910,15 +961,16 @@ public final class CMSConfig extends AbstractConfig { } /** - * Internal class representing a DHTMLEditor configuration parameter. It creates a new - * DHMTLEditor Config object (internal class in DHTMLEditor). + * Internal class representing a DHTMLEditor configuration parameter. It + * creates a new DHMTLEditor Config object (internal class in DHTMLEditor). * - * XXX Method unmarshal is broken and currently does not work correctly. It does not process - * default values provided by using DHTMLEditor.Config.Standard (see parameter - * m_dhtmlEditorConfig above). May be a similiar problem as with ResourceParameter and default - * value, see patch provided by pbrucha. Best solution may be to remove this special parameter - * class and use a string parameter instead to directly create a DHTMLEditor.Config object. - * (pboy, 2010-09-02) + * XXX Method unmarshal is broken and currently does not work correctly. It + * does not process default values provided by using + * DHTMLEditor.Config.Standard (see parameter m_dhtmlEditorConfig above). + * May be a similiar problem as with ResourceParameter and default value, + * see patch provided by pbrucha. Best solution may be to remove this + * special parameter class and use a string parameter instead to directly + * create a DHTMLEditor.Config object. (pboy, 2010-09-02) */ private class DHTMLEditorConfigParameter extends StringParameter { @@ -973,8 +1025,9 @@ public final class CMSConfig extends AbstractConfig { } /** - * The relative weight given to the dcKeywords element within dublinCore element within cms:item - * element when ranking search results Only used by the interMedia query engine. + * The relative weight given to the dcKeywords element within dublinCore + * element within cms:item element when ranking search results Only used by + * the interMedia query engine. * */ public Integer getKeywordSearchWeight() { @@ -986,8 +1039,8 @@ public final class CMSConfig extends AbstractConfig { } /** - * The relative weight given to title element within cms:item element when ranking search - * results Only used by the interMedia query engine. + * The relative weight given to title element within cms:item element when + * ranking search results Only used by the interMedia query engine. * */ public Integer getTitleSearchWeight() { @@ -995,9 +1048,11 @@ public final class CMSConfig extends AbstractConfig { } /** - * Whether to include INPATH operators to contains clause in intermedia search + * Whether to include INPATH operators to contains clause in intermedia + * search * - * NB - if true, INDEX MUST BE CREATED WITH PATH_SECTION_GROUP - upgrade 6.5.0 - 6.5.1 + * NB - if true, INDEX MUST BE CREATED WITH PATH_SECTION_GROUP - upgrade + * 6.5.0 - 6.5.1 * * @return */ @@ -1006,12 +1061,13 @@ public final class CMSConfig extends AbstractConfig { } /** - * for the given content type, returns a collection of steps that are deemed irrelevant for the - * type. + * for the given content type, returns a collection of steps that are deemed + * irrelevant for the type. * * If no irrelevant steps, an empty set is returned. * - * Steps are the names of the bebop step components that are used by the authoring kit wizard + * Steps are the names of the bebop step components that are used by the + * authoring kit wizard * * @param type * @@ -1033,7 +1089,8 @@ public final class CMSConfig extends AbstractConfig { // 1st string is name of content type, 2nd string is name of asset step s_log.debug("parameter read - type = " + pair[0] + " - step = " + pair[1]); - Collection typeSteps = (Collection) s_skipAssetSteps.get(pair[0]); + Collection typeSteps = (Collection) s_skipAssetSteps.get( + pair[0]); if (typeSteps == null) { typeSteps = new HashSet(); s_skipAssetSteps.put(pair[0], typeSteps); @@ -1056,7 +1113,8 @@ public final class CMSConfig extends AbstractConfig { } /** - * May be used by any content type creation form to decide whether to validate description field + * May be used by any content type creation form to decide whether to + * validate description field * */ public boolean mandatoryDescriptions() { @@ -1064,32 +1122,35 @@ public final class CMSConfig extends AbstractConfig { } /** - * Used to decide whether lifecycles (and all asociated phases) should be deleted from the - * system when complete + * Used to decide whether lifecycles (and all asociated phases) should be + * deleted from the system when complete * - * (Deleting lifecycle means that you lose a bit of historical information eg when was this item - * unpublished) + * (Deleting lifecycle means that you lose a bit of historical information + * eg when was this item unpublished) */ public boolean deleteFinishedLifecycles() { return ((Boolean) get(m_deleteLifecycleWhenComplete)).booleanValue(); } /** - * Used to decide whether to delete old notification records for expiry notifications. + * Used to decide whether to delete old notification records for expiry + * notifications. * - * If true, notifications and messages are deleted if the notification is successfully sent. Any - * send failures are retained + * If true, notifications and messages are deleted if the notification is + * successfully sent. Any send failures are retained * */ public boolean deleteExpiryNotifications() { - return ((Boolean) get(m_deleteExpiryNotificationsWhenSent)).booleanValue(); + return ((Boolean) get(m_deleteExpiryNotificationsWhenSent)) + .booleanValue(); } /** - * Used to decide whether to delete old notification records for workflow notifications. + * Used to decide whether to delete old notification records for workflow + * notifications. * - * If true, notifications and messages are deleted if the notification is successfully sent. Any - * send failures are retained + * If true, notifications and messages are deleted if the notification is + * successfully sent. Any send failures are retained * */ public boolean deleteWorkflowNotifications() { @@ -1102,11 +1163,12 @@ public final class CMSConfig extends AbstractConfig { } /** - * I'am not sure for what this method is. I found it here when I tried figure out how add - * multiple parts to an ContentType, like ccm-cms-types-contact and the Multipart article do. I - * think this method should not be here because it is only needed by one specific contenttype. - * Because of this, I think that this method and the contact are violating many rules of modern - * software design. Jens Pelzetter, 2009-06-02. + * I'am not sure for what this method is. I found it here when I tried + * figure out how add multiple parts to an ContentType, like + * ccm-cms-types-contact and the Multipart article do. I think this method + * should not be here because it is only needed by one specific contenttype. + * Because of this, I think that this method and the contact are violating + * many rules of modern software design. Jens Pelzetter, 2009-06-02. * * @return */ @@ -1119,10 +1181,11 @@ public final class CMSConfig extends AbstractConfig { } /** - * Retrieve whether to allow creation of a new Use Context in category tab of content sections. - * "Use Context" is used to constitute a category hierarchy in core. It is superseded by the - * construct "Category Domain" in Terms (ccm-ldn-terms). Global parameter for all content - * sections. Default is false because all installation bundles use Terms. + * Retrieve whether to allow creation of a new Use Context in category tab + * of content sections. "Use Context" is used to constitute a category + * hierarchy in core. It is superseded by the construct "Category Domain" in + * Terms (ccm-ldn-terms). Global parameter for all content sections. Default + * is false because all installation bundles use Terms. * * @return TRUE if creation is allowed, otherwise FALSE (default) */ @@ -1137,8 +1200,8 @@ public final class CMSConfig extends AbstractConfig { } /** - * Hide the (no longer used) legacy public site link in Workspace (content center) section - * listing, true by default. + * Hide the (no longer used) legacy public site link in Workspace (content + * center) section listing, true by default. */ public final boolean getHideLegacyPublicSiteLink() { return ((Boolean) get(m_hideLegacyPublicSiteLink)).booleanValue(); @@ -1181,15 +1244,18 @@ public final class CMSConfig extends AbstractConfig { } public int getImageBrowserCaptionSize() { - return Math.min(((Integer) get(m_imageBrowserCaptionSize)).intValue(), 100); + return Math.min(((Integer) get(m_imageBrowserCaptionSize)).intValue(), + 100); } public int getImageBrowserDescriptionSize() { - return Math.min(((Integer) get(m_imageBrowserDescriptionSize)).intValue(), 400); + return Math.min(((Integer) get(m_imageBrowserDescriptionSize)) + .intValue(), 400); } public int getImageBrowserTitleSize() { - return Math.min(((Integer) get(m_imageBrowserTitleSize)).intValue(), 200); + return Math + .min(((Integer) get(m_imageBrowserTitleSize)).intValue(), 200); } public Boolean getImageCacheEnabled() { @@ -1234,9 +1300,13 @@ public final class CMSConfig extends AbstractConfig { public Integer getXmlCacheAge() { return (Integer) get(m_xmlCacheAge); } - + public Integer getLinkDescMaxLength() { return (Integer) get(m_linkDescMaxLength); } + + public Boolean getUseLanguageExtension() { + return (Boolean) get(m_useLanguageExtension); + } } diff --git a/ccm-cms/src/com/arsdigita/cms/CMSConfig_parameter.properties b/ccm-cms/src/com/arsdigita/cms/CMSConfig_parameter.properties index f067a811d..abbca957c 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSConfig_parameter.properties +++ b/ccm-cms/src/com/arsdigita/cms/CMSConfig_parameter.properties @@ -359,4 +359,9 @@ com.arsdigita.cms.xml.cache.age.format=[integer] com.arsdigita.cms.link_description_max_length.title=Maximum length of the description of a link. com.arsdigita.cms.link_description_max_length.purpose=Maximum length of the description of a link. com.arsdigita.cms.link_description_max_length.example=400 -com.arsdigita.cms.link_description_max_length.format={Integer] \ No newline at end of file +com.arsdigita.cms.link_description_max_length.format={Integer] + +com.arsdigita.cms.use_language_extension.title=Add language extensions +com.arsdigita.cms.use_language_extension.purpose=Always add language extension to content item URLs +com.arsdigita.cms.use_language_extension.example=true +com.arsdigita.cms.use_language_extension.format=[Boolean] diff --git a/ccm-cms/src/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java b/ccm-cms/src/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java index 6f301c61b..9183ca5d9 100755 --- a/ccm-cms/src/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java +++ b/ccm-cms/src/com/arsdigita/cms/dispatcher/MultilingualItemResolver.java @@ -20,6 +20,7 @@ package com.arsdigita.cms.dispatcher; import com.arsdigita.bebop.PageState; import com.arsdigita.cms.CMS; +import com.arsdigita.cms.CMSConfig; import com.arsdigita.cms.ContentBundle; import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentSection; @@ -31,10 +32,12 @@ import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.globalization.GlobalizationHelper; import com.arsdigita.persistence.OID; import com.arsdigita.util.Assert; + import org.apache.log4j.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; + import java.math.BigDecimal; import java.net.URLEncoder; import java.util.ArrayList; @@ -43,66 +46,64 @@ import java.util.Iterator; import java.util.StringTokenizer; /** - * 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 s_log = Logger.getLogger( + MultilingualItemResolver.class); + private static MasterPage s_masterP = null; private static final String ADMIN_PREFIX = "admin"; /** - * The string identifying an item's ID in the query string of a - * URL. + * 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"); } - + /** * 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 url 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) { + 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); + s_log.debug("Resolving the item in content section " + section + + " at URL '" + url + "' for context " + context); } - + Assert.exists(section, "ContentSection section"); Assert.exists(url, "String url"); Assert.exists(context, "String context"); - + Folder rootFolder = section.getRootFolder(); url = stripTemplateFromURL(url); - + // nothing to do, if root folder is null if (rootFolder == null) { s_log.debug("The root folder is null; returning no item"); @@ -110,50 +111,48 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It if (s_log.isDebugEnabled()) { s_log.debug("Using root folder " + rootFolder); } - + if (ContentItem.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"); + 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(); - + 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"); + 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); + s_log + .debug("Resolved URL '" + url + "' to item " + item); } - + return item; } } else if (ContentItem.DRAFT.equals(context)) { s_log.debug("The use context is 'draft'"); - + // For 'draft' items, 'generateUrl()' method returns // URL like this // '/acs/wcms/admin/item.jsp?item_id=10201&set_tab=1' @@ -161,333 +160,336 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It // '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.indexOf(ITEM_ID) >= 0, + "url must contain parameter " + ITEM_ID); } - + final ContentItem item = getItemFromDraftURL(url); - + if (s_log.isDebugEnabled()) { s_log.debug("Resolved URL '" + url + "' to item " + item); } - + return item; } else if (CMSDispatcher.PREVIEW.equals(context)) { s_log.debug("The use context is 'preview'"); - + final String prefix = CMSDispatcher.PREVIEW + "/"; - + if (url.startsWith(prefix)) { if (s_log.isDebugEnabled()) { - s_log.debug("The URL starts with prefix '" + - prefix + "'; removing it"); + s_log.debug("The URL starts with prefix '" + prefix + + "'; removing it"); } - + url = url.substring(prefix.length()); } - + final ContentItem item = getItemFromLiveURL(url, rootFolder); - + if (s_log.isDebugEnabled()) { s_log.debug("Resolved URL '" + url + "' to item " + item); } - + return item; } else { - throw new IllegalArgumentException - ("Invalid item resolver context " + context); + throw new IllegalArgumentException( + "Invalid item resolver context " + context); } } - + s_log.debug("No item resolved; returning null"); - + return null; } - + /** * Fetches the current context based on the page state. * * @param state the current page state + * * @return the context of the current URL, such as - * ContentItem.LIVE or ContentItem.DRAFT + * ContentItem.LIVE or ContentItem.DRAFT + * * @see ContentItem#LIVE * @see ContentItem#DRAFT */ @Override public String getCurrentContext(final PageState state) { s_log.debug("Getting the current context"); - + // XXX need to use Web.getWebContext().getRequestURL() here. String url = state.getRequest().getRequestURI(); - - final ContentSection section = - CMS.getContext().getContentSection(); - + + final ContentSection section = CMS.getContext().getContentSection(); + // If this page is associated with a content section, // transform the URL so that it is relative to the content // section site node. - if (section != null) { final String sectionURL = section.getURL(); - + if (url.startsWith(sectionURL)) { url = url.substring(sectionURL.length()); } } - + // Remove any template-specific URL components (will only work // if they're first in the URL at this point; verify). XXX but // we don't actually verify? - url = stripTemplateFromURL(url); - + // Determine if we are under the admin UI. - - if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter.getURL())) { + if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter + .getURL())) { return ContentItem.DRAFT; } else { return ContentItem.LIVE; } } - + /** * Generates a URL for a content item. * - * @param itemId The item ID - * @param name The name of the content page - * @param state The page state + * @param itemId The item ID + * @param name The name of the content page + * @param state The page state * @param section the content section to which the item belongs - * @param context the context of the URL, such as "live" or - * "admin" + * @param 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. * - * @param itemId The item ID - * @param name The name of the content page - * @param state The page state - * @param section the content section to which the item belongs - * @param context the context of the URL, such as "live" or - * "admin" - * @param templateContext the context for the URL, such as - * "public" + * @param itemId The item ID + * @param name The name of the content page + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" + * * @return The URL of the item + * * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final BigDecimal itemId, - final String name, - final ContentSection section, - final String context, - final String templateContext) { + 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 + "'"); + s_log.debug("Generating an item URL for item id " + itemId + + ", section " + section + ", and context '" + + context + + "' with name '" + name + "'"); } - - Assert.exists(itemId, "BigDecimal itemId"); + + Assert.exists(itemId, "BigDecimal itemId"); Assert.exists(context, "String context"); Assert.exists(section, "ContentSection section"); - + if (ContentItem.DRAFT.equals(context)) { // No template context here. return generateDraftURL(section, itemId); } else if (CMSDispatcher.PREVIEW.equals(context)) { ContentItem item = new ContentItem(itemId); - + return generatePreviewURL(section, item, templateContext); } else if (ContentItem.LIVE.equals(context)) { ContentItem item = new ContentItem(itemId); - + if (Assert.isEnabled()) { Assert.exists(item, "item"); Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()), - "Generating " + ContentItem.LIVE + " " + - "URL; this item must be the live version"); + "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 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 item The item + * @param state The page state + * @param section the content section to which the item belongs + * @param context the context of the URL, such as "live" or "admin" + * @param templateContext the context for the URL, such as "public" + * * @return The URL of the item + * * @see #getCurrentContext */ @Override public String generateItemURL(final PageState state, - final ContentItem item, - ContentSection section, - final String context, - final String templateContext) { + 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); + s_log.debug("Generating an item URL for item " + item + ", section " + + section + ", and context " + context); } - + Assert.exists(item, "ContentItem item"); Assert.exists(context, "String context"); - + if (section == null) { section = item.getContentSection(); } - + if (ContentItem.DRAFT.equals(context)) { if (Assert.isEnabled()) { Assert.isTrue(ContentItem.DRAFT.equals(item.getVersion()), - "Generating " + ContentItem.DRAFT + - " url: item must be draft version"); + "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 item The content item * @param request The HTTP request + * * @return The master page + * * @throws javax.servlet.ServletException */ @Override public CMSPage getMasterPage(final ContentItem item, - final HttpServletRequest request) - throws ServletException { + final HttpServletRequest request) + throws ServletException { if (s_log.isDebugEnabled()) { s_log.debug("Getting the master page for item " + item); } - + // taken from SimpleItemResolver if (s_masterP == null) { s_masterP = new MasterPage(); s_masterP.init(); } - + if (s_log.isDebugEnabled()) { s_log.debug("Returning master page " + s_masterP); } - + return s_masterP; } - + /** * Returns content item's draft version URL * - * @param section The content section to which the item belongs - * @param itemId The content item's ID + * @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) { + final BigDecimal itemId) { if (s_log.isDebugEnabled()) { - s_log.debug("Generating draft URL for item ID " + itemId + - " and section " + section); + s_log.debug("Generating draft URL for item ID " + itemId + + " and section " + section); } - + if (Assert.isEnabled()) { Assert.isTrue(section != null && itemId != null, - "get draft url: neither secion nor item " + - "can be null"); + "get draft url: neither secion nor item " + + "can be null"); } - - final String url = ContentItemPage.getItemURL - (section.getPath() + "/", itemId, ContentItemPage.AUTHORING_TAB); - + + final String url = ContentItemPage.getItemURL(section.getPath() + "/", + itemId, + ContentItemPage.AUTHORING_TAB); + if (s_log.isDebugEnabled()) { s_log.debug("Generated draft URL " + url); } - + return url; } - + /** - * Generate a 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 + * @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 * - * @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) { + final ContentItem item, + final String templateContext) { if (s_log.isDebugEnabled()) { - s_log.debug("Generating live URL for item " + item + " in " + - "section " + section); + s_log.debug("Generating live URL for item " + item + " in " + + "section " + section); } - + /* * URL = URL of section + templateContext + path to the ContentBundle * which contains the item @@ -495,19 +497,20 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It 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 @@ -518,67 +521,67 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It */ if (bundle != null && bundle instanceof ContentBundle) { s_log.debug("Found a bundle; building its file name"); - + final String fname = bundle.getPath(); - + if (s_log.isDebugEnabled()) { - s_log.debug("Appending the bundle's file name '" + - fname + "'"); + s_log.debug("Appending the bundle's file name '" + fname + "'"); } - + url.append(fname); - + } else { s_log.debug("No bundle found; using the item's path directly"); - + url.append(item.getPath()); } -/* - * This will append the language to the url, which will in turn render - * the language negotiation inoperative - final String language = item.getLanguage(); - - if (language == null) { - s_log.debug("The item has no language; omitting the " + - "language encoding"); - } else { - s_log.debug("Encoding the language of the item passed in, '" + - language + "'"); - - url.append("." + language); + + if (CMSConfig.getInstanceOf().getUseLanguageExtension()) { + // This will append the language to the url, which will in turn render + // the language negotiation inoperative + final String language = item.getLanguage(); + + if (language == null) { + s_log.debug("The item has no language; omitting the " + + "language encoding"); + } else { + s_log.debug("Encoding the language of the item passed in, '" + + language + "'"); + + url.append(".").append(language); + } } - + if (s_log.isDebugEnabled()) { s_log.debug("Generated live URL " + url.toString()); } -*/ + return url.toString(); } - + /** - * Generate a URL which can be used to preview the - * 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 - * item + * @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("/"); @@ -590,12 +593,13 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It // 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 @@ -605,111 +609,113 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It */ if (bundle != null && bundle instanceof ContentBundle) { s_log.debug("Found a bundle; using its path"); - + url.append(bundle.getPath()); } else { s_log.debug("No bundle found; using the item's path directly"); - + url.append(item.getPath()); } - + final String language = item.getLanguage(); - + if (language == null) { - s_log.debug("The item has no language; omitting the " + - "language encoding"); + s_log.debug("The item has no language; omitting the " + + "language encoding"); } else { - s_log.debug("Encoding the language of the item passed in, '" + - language + "'"); - + s_log.debug("Encoding the language of the item passed in, '" + + language + "'"); + url.append(".").append(language); } - + if (s_log.isDebugEnabled()) { s_log.debug("Generated preview URL " + url.toString()); } - + return url.toString(); } - + /** - * Retrieves 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); } - + 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"); - + 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)); - + item_id = item_id.substring(pos, Math.min(i, item_id.length() - 1)); + if (s_log.isDebugEnabled()) { s_log.debug("Looking up item using item ID " + item_id); } - - OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal(item_id)); - final ContentItem item = (ContentItem) DomainObjectFactory.newInstance - (oid); - + + OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal( + item_id)); + final ContentItem item = (ContentItem) DomainObjectFactory.newInstance( + oid); + if (s_log.isDebugEnabled()) { s_log.debug("Returning item " + item); } - + return item; } - + /** - * Returns a content item based on URL relative to the root - * folder. + * 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) { + Folder parentFolder) { if (s_log.isDebugEnabled()) { - s_log.debug("Resolving the item for live URL " + url + - " and parent folder " + parentFolder); + s_log.debug("Resolving the item for live URL " + url + + " and parent folder " + parentFolder); } if (parentFolder == null || url == null || url.equals("")) { if (s_log.isDebugEnabled()) { - s_log.debug("The url is null or parent folder was null " + - "or something else is wrong, so just return " + - "the parent folder"); + s_log.debug("The url is null or parent folder was null " + + "or something else is wrong, so just return " + + "the parent folder"); } return parentFolder; @@ -719,43 +725,43 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It int index = url.indexOf('/'); if (index >= 0) { - s_log.debug("The URL starts with a slash; paring off the first " + - "URL element and recursing"); + s_log.debug("The URL starts with a slash; paring off the first " + + "URL element and recursing"); // If we got first slash (index == 0), ignore it and go // on, sample '/foo/bar/item.html.en', in next recursion // will have deal with 'foo' folder. - String name = index > 0 ? url.substring(0, index) : ""; parentFolder = "".equals(name) ? parentFolder - : (Folder) parentFolder.getItem(URLEncoder.encode(name), true); + : (Folder) parentFolder.getItem(URLEncoder + .encode(name), true); url = index + 1 < len ? url.substring(index + 1) : ""; return getItemFromLiveURL(url, parentFolder); } else { s_log.debug("Found a file element in the URL"); - String[] nameAndLang = getNameAndLangFromURLFrag(url); + String[] 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 @@ -766,20 +772,19 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It * 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); + s_log.debug("Found some file extensions to look at; they " + + "are " + exts); } - + /* * We have found at least one extension, so we can * proceed. Now try to find out if one of the @@ -787,23 +792,21 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It * 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); } - + /* * Loop through all extensions, but discard the * first one, which is the name of the item. @@ -821,31 +824,31 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It if (ext.equals(supportedLangIt.next())) { lang = ext; if (s_log.isDebugEnabled()) { - s_log.debug("Found a match; using " + - "language " + lang); + s_log.debug("Found a match; using " + + "language " + lang); } break; } } } else { if (s_log.isDebugEnabled()) { - s_log.debug("Discarding extension " + ext + "; " + - "it is too short"); + s_log.debug("Discarding extension " + ext + "; " + + "it is too short"); } } } } else { - s_log.debug("The file has no extensions; no language was " + - "encoded"); + s_log.debug("The file has no extensions; no language was " + + "encoded"); name = url; // no extension, so we just have a name here lang = null; // no extension, so we cannot guess the language } - + if (Assert.isEnabled()) { Assert.exists(name, "String name"); Assert.exists(lang == null || lang.length() == 2); } - + if (s_log.isDebugEnabled()) { s_log.debug("File name resolved to " + name); s_log.debug("File language resolved to " + lang); @@ -855,14 +858,13 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It 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. */ @@ -872,15 +874,16 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It s_log.debug("Found content bundle " + item); } if (lang == null) { - s_log.debug("The URL has no language encoded in it; " + - "negotiating the language"); + s_log.debug("The URL has no language encoded in it; " + + "negotiating the language"); // There is no language, so we get the negotiated locale and call // this method again with a proper language - return this.getItemFromLangAndBundle(GlobalizationHelper.getNegotiatedLocale().getLanguage(), item); + 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"); + s_log.debug("The URL is encoded with a langauge; " + + "fetching the appropriate item from " + + "the bundle"); /* * So the request contains a language code as an * extension of the "name" ==>go ahead and try to @@ -888,10 +891,10 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It * the bundle does not contain an instance for the * given language. */ - - final ContentItem resolved = - ((ContentBundle) item).getInstance(lang); - + + final ContentItem resolved = ((ContentBundle) item).getInstance( + lang); + if (s_log.isDebugEnabled()) { s_log.debug("Resolved URL to item " + resolved); } @@ -899,10 +902,9 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It } } else { if (s_log.isDebugEnabled()) { - s_log.debug("I expected to get a content bundle; I got " + - item); + s_log.debug("I expected to get a content bundle; I got " + item); } - + /* * We expected something like a Bundle, but it seems * like we got something completely different... just @@ -911,9 +913,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It * * NOTE: This should never happen :-) */ - return item; // might be null } } - + } diff --git a/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java b/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java index a6c1162d9..24bb57cdb 100644 --- a/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java +++ b/ccm-core/src/com/arsdigita/globalization/GlobalizationHelper.java @@ -123,6 +123,12 @@ public class GlobalizationHelper { return selectedLocale; } + public static void setSelectedLocale(final String language) { + + final HttpServletRequest request = DispatcherHelper.getRequest(); + request.getSession().setAttribute(LANG_PARAM, language); + } + /** * Create a Locale from a browser provides language string * diff --git a/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig.java b/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig.java index dc4d7545b..dc2382c3f 100755 --- a/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig.java +++ b/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig.java @@ -15,7 +15,6 @@ * 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.navigation; import com.arsdigita.bebop.PageState; @@ -51,28 +50,40 @@ import org.apache.log4j.Logger; /** * Configuration record for the navigation app + * * @author Daniel Berrange */ public final class NavigationConfig extends AbstractConfig { + private static final Logger s_log = Logger.getLogger(NavigationConfig.class); - /** The cache lifetime for category index pages in seconds. Default 1 hour */ - private final Parameter m_indexPageCacheLifetime = new IntegerParameter - ("com.arsdigita.navigation.index_page_cache_lifetime", - Parameter.REQUIRED, new Integer(3600)); - - /** Generate full item URLs instead of going via search redirector. Default true */ - private final Parameter m_generateItemURL = new BooleanParameter - ("com.arsdigita.navigation.generate_item_url", - Parameter.REQUIRED, new Boolean(true)); - - /** The default category template. */ - private final Parameter m_defaultTemplate = new StringParameter - ("com.arsdigita.navigation.default_template", - Parameter.REQUIRED, "/templates/ccm-navigation/navigation/nav-default.jsp"); - - /** If no template for category, should it get template from parent, or - * fall back on default? Default: true */ + /** + * The cache lifetime for category index pages in seconds. Default 1 hour + */ + private final Parameter m_indexPageCacheLifetime = new IntegerParameter( + "com.arsdigita.navigation.index_page_cache_lifetime", + Parameter.REQUIRED, new Integer(3600)); + + /** + * Generate full item URLs instead of going via search redirector. Default + * true + */ + private final Parameter m_generateItemURL = new BooleanParameter( + "com.arsdigita.navigation.generate_item_url", + Parameter.REQUIRED, new Boolean(true)); + + /** + * The default category template. + */ + private final Parameter m_defaultTemplate = new StringParameter( + "com.arsdigita.navigation.default_template", + Parameter.REQUIRED, + "/templates/ccm-navigation/navigation/nav-default.jsp"); + + /** + * If no template for category, should it get template from parent, or fall + * back on default? Default: true + */ private final Parameter m_inheritTemplates; // Removed, use content-section config directly instead! // ContentSection.getConfig().getDefaultContentSection() @@ -92,19 +103,24 @@ public final class NavigationConfig extends AbstractConfig { // Quasimodo: End private final Parameter m_dateOrderCategories; private final Parameter m_topLevelDateOrderCategories; - /** Class that provides categories included in menu for any categories that - * do not have an alternative provider registered */ - private final Parameter m_defaultMenuCatProvider; - + private final Parameter m_useLanguageExtension; + /** + * Class that provides categories included in menu for any categories that + * do not have an alternative provider registered + */ + private final Parameter m_defaultMenuCatProvider; + private static Set s_fixedDateOrderCats = null; private Category m_defaultCategoryRoot = null; // maybe 2 lookups in a single request so prevent double overhead - private RequestLocal m_allDateOrderCategories = new RequestLocal () { - public Object initialValue (PageState state) { + private RequestLocal m_allDateOrderCategories = new RequestLocal() { + + public Object initialValue(PageState state) { return getCurrentDateOrderCategories(); } + }; private NavigationModel m_defaultModel = null; @@ -112,54 +128,59 @@ public final class NavigationConfig extends AbstractConfig { private TreeCatProvider m_treeCatProvider = null; public NavigationConfig() { - // not desirable default value (IMHO) but retains existing behaviour - m_inheritTemplates = new BooleanParameter - ("com.arsdigita.navigation.inherit_templates", + // not desirable default value (IMHO) but retains existing behaviour + m_inheritTemplates = new BooleanParameter( + "com.arsdigita.navigation.inherit_templates", Parameter.REQUIRED, new Boolean(true)); - m_relatedItemsContext = new StringParameter - ("com.arsdigita.navigation.related_items_context", - Parameter.REQUIRED, "subject"); - m_defaultModelClass = new StringParameter - ("com.arsdigita.navigation.default_nav_model", - Parameter.REQUIRED, ApplicationNavigationModel.class.getName()); - m_defaultCatRootPath = new StringParameter - ("com.arsdigita.navigation.default_cat_root_path", - Parameter.REQUIRED, "/navigation/"); - m_relatedItemsFactory = new ClassParameter - ("com.arsdigita.navigation.related_items_factory", - Parameter.REQUIRED, RelatedItemsQueryFactoryImpl.class); - m_traversalAdapters = new ResourceParameter - ("com.arsdigita.navigation.traversal_adapters", - Parameter.REQUIRED, - "/WEB-INF/resources/navigation-adapters.xml"); - m_categoryMenuShowNephews = new BooleanParameter - ("com.arsdigita.navigation.category_menu_show_nephews", - Parameter.OPTIONAL, new Boolean(false)); + m_relatedItemsContext = new StringParameter( + "com.arsdigita.navigation.related_items_context", + Parameter.REQUIRED, "subject"); + m_defaultModelClass = new StringParameter( + "com.arsdigita.navigation.default_nav_model", + Parameter.REQUIRED, ApplicationNavigationModel.class.getName()); + m_defaultCatRootPath = new StringParameter( + "com.arsdigita.navigation.default_cat_root_path", + Parameter.REQUIRED, "/navigation/"); + m_relatedItemsFactory = new ClassParameter( + "com.arsdigita.navigation.related_items_factory", + Parameter.REQUIRED, RelatedItemsQueryFactoryImpl.class); + m_traversalAdapters = new ResourceParameter( + "com.arsdigita.navigation.traversal_adapters", + Parameter.REQUIRED, + "/WEB-INF/resources/navigation-adapters.xml"); + m_categoryMenuShowNephews = new BooleanParameter( + "com.arsdigita.navigation.category_menu_show_nephews", + Parameter.OPTIONAL, new Boolean(false)); // Quasimodo: Begin - m_categoryMenuShowGrandChildren = new StringParameter - ("com.arsdigita.navigation.category_menu_show_grand_children", - Parameter.OPTIONAL, "false"); - m_categoryMenuShowGrandChildrenMax = new IntegerParameter - ("com.arsdigita.navigation.category_menu_show_grand_children_max", - Parameter.OPTIONAL, new Integer(0)); - m_categoryMenuShowGrandChildrenMin = new IntegerParameter - ("com.arsdigita.navigation.category_menu_show_grand_children_min", - Parameter.OPTIONAL, new Integer(1)); - m_categoryMenuShowGrandChildrenLimit = new IntegerParameter - ("com.arsdigita.navigation.category_menu_show_grand_children_limit", - Parameter.OPTIONAL, new Integer(1)); + m_categoryMenuShowGrandChildren = new StringParameter( + "com.arsdigita.navigation.category_menu_show_grand_children", + Parameter.OPTIONAL, "false"); + m_categoryMenuShowGrandChildrenMax = new IntegerParameter( + "com.arsdigita.navigation.category_menu_show_grand_children_max", + Parameter.OPTIONAL, new Integer(0)); + m_categoryMenuShowGrandChildrenMin = new IntegerParameter( + "com.arsdigita.navigation.category_menu_show_grand_children_min", + Parameter.OPTIONAL, new Integer(1)); + m_categoryMenuShowGrandChildrenLimit = new IntegerParameter( + "com.arsdigita.navigation.category_menu_show_grand_children_limit", + Parameter.OPTIONAL, new Integer(1)); // Quasimodo: End - m_dateOrderCategories = new StringArrayParameter - ("com.arsdigita.navigation.date_order_categories", - Parameter.OPTIONAL, new String[0]); - m_topLevelDateOrderCategories = new StringArrayParameter - ("com.arsdigita.navigation.top_level_date_order_categories", + m_dateOrderCategories = new StringArrayParameter( + "com.arsdigita.navigation.date_order_categories", Parameter.OPTIONAL, new String[0]); - m_defaultMenuCatProvider = new ClassParameter - ("com.arsdigita.navigation.default_menu_cat_provider", + m_topLevelDateOrderCategories = new StringArrayParameter( + "com.arsdigita.navigation.top_level_date_order_categories", + Parameter.OPTIONAL, new String[0]); + m_defaultMenuCatProvider = new ClassParameter( + "com.arsdigita.navigation.default_menu_cat_provider", Parameter.OPTIONAL, null); + m_useLanguageExtension = new BooleanParameter( + "com.arsdigita.navigation.use_language_extension", + Parameter.OPTIONAL, + false); + register(m_indexPageCacheLifetime); register(m_generateItemURL); register(m_defaultTemplate); @@ -182,35 +203,38 @@ public final class NavigationConfig extends AbstractConfig { register(m_dateOrderCategories); register(m_topLevelDateOrderCategories); register(m_defaultMenuCatProvider); + register(m_useLanguageExtension); loadInfo(); // Quasimodo: Begin // Checking Paramter - String param = new String((String)get(m_categoryMenuShowGrandChildren)); - if( param.equals("false") || param.equals("adaptive") || param.equals("true")) { + String param = new String((String) get(m_categoryMenuShowGrandChildren)); + if (param.equals("false") || param.equals("adaptive") || param.equals( + "true")) { set(m_categoryMenuShowGrandChildren, param); } else { - s_log.error("com.arsdigita.navigation.category_menu_show_grand_children: "+ - "Invalid setting " + param + ". Falling back to false."); - set(m_categoryMenuShowGrandChildren, "false"); + s_log.error( + "com.arsdigita.navigation.category_menu_show_grand_children: " + + "Invalid setting " + param + ". Falling back to false."); + set(m_categoryMenuShowGrandChildren, "false"); } // Quasimodo: End } public final long getIndexPageCacheLifetime() { - return ((Integer)get(m_indexPageCacheLifetime)).longValue(); + return ((Integer) get(m_indexPageCacheLifetime)).longValue(); } public final boolean getGenerateItemURL() { - return ((Boolean)get(m_generateItemURL)).booleanValue(); + return ((Boolean) get(m_generateItemURL)).booleanValue(); } public final String getDefaultTemplate() { - return (String)get(m_defaultTemplate); + return (String) get(m_defaultTemplate); } public final boolean inheritTemplates() { - return ((Boolean) get(m_inheritTemplates)).booleanValue(); + return ((Boolean) get(m_inheritTemplates)).booleanValue(); } // Removed, use content-section config directly instead! @@ -218,17 +242,16 @@ public final class NavigationConfig extends AbstractConfig { // public final String getDefaultContentSectionURL() { // return (String)get(m_defaultContentSectionURL); // } - public final String getRelatedItemsContext() { - return (String)get(m_relatedItemsContext); + return (String) get(m_relatedItemsContext); } public final String getDefaultCategoryRootPath() { - return (String)get(m_defaultCatRootPath); + return (String) get(m_defaultCatRootPath); } public final Class getRelatedItemsFactory() { - return (Class)get(m_relatedItemsFactory); + return (Class) get(m_relatedItemsFactory); } public final synchronized NavigationModel getDefaultModel() { @@ -236,29 +259,27 @@ public final class NavigationConfig extends AbstractConfig { return m_defaultModel; } - String defaultModelClassName = (String)get(m_defaultModelClass); + String defaultModelClassName = (String) get(m_defaultModelClass); try { Class defaultModelClass = Class.forName(defaultModelClassName); - Constructor cons = defaultModelClass.getConstructor - ( new Class[]{} ); - m_defaultModel = (NavigationModel) cons.newInstance - ( new Object[]{} ); + Constructor cons = defaultModelClass.getConstructor(new Class[]{}); + m_defaultModel = (NavigationModel) cons.newInstance(new Object[]{}); } catch (Exception ex) { - throw new UncheckedWrapperException( ex ); + throw new UncheckedWrapperException(ex); } return m_defaultModel; } public final synchronized Category getDefaultCategoryRoot() { - if (null != m_defaultCategoryRoot) { + if (null != m_defaultCategoryRoot) { return m_defaultCategoryRoot; } - String defaultCatRootPath = (String)get(m_defaultCatRootPath); + String defaultCatRootPath = (String) get(m_defaultCatRootPath); - Application app = - Application.retrieveApplicationForPath(defaultCatRootPath); + Application app = Application.retrieveApplicationForPath( + defaultCatRootPath); Assert.exists(app, Application.class); Category m_defaultCategoryRoot = Category.getRootForObject(app); @@ -268,33 +289,37 @@ public final class NavigationConfig extends AbstractConfig { } InputStream getTraversalAdapters() { - return (InputStream)get(m_traversalAdapters); + return (InputStream) get(m_traversalAdapters); } - + public final boolean getCategoryMenuShowNephews() { - return ((Boolean)get(m_categoryMenuShowNephews)).booleanValue(); - } + return ((Boolean) get(m_categoryMenuShowNephews)).booleanValue(); + } // Quasimodo: Begin public final String getCategoryMenuShowGrandChildren() { - return (String)get(m_categoryMenuShowGrandChildren); - } - public final long getCategoryMenuShowGrandChildrenMax() { - return ((Integer)get(m_categoryMenuShowGrandChildrenMax)).longValue(); - } - public final long getCategoryMenuShowGrandChildrenMin() { - return ((Integer)get(m_categoryMenuShowGrandChildrenMin)).longValue(); - } - public final long getCategoryMenuShowGrandChildrenLimit() { - return ((Integer)get(m_categoryMenuShowGrandChildrenLimit)).longValue(); - } + return (String) get(m_categoryMenuShowGrandChildren); + } + + public final long getCategoryMenuShowGrandChildrenMax() { + return ((Integer) get(m_categoryMenuShowGrandChildrenMax)).longValue(); + } + + public final long getCategoryMenuShowGrandChildrenMin() { + return ((Integer) get(m_categoryMenuShowGrandChildrenMin)).longValue(); + } + + public final long getCategoryMenuShowGrandChildrenLimit() { + return ((Integer) get(m_categoryMenuShowGrandChildrenLimit)).longValue(); + } // Quasimodo: End - + /** - * retrieve a collection of categories to order by date. Collection includes - * categories directly specified as date ordered and also all subcategories of - * categories specified as being top level date ordered categories - * in config + * retrieve a collection of categories to order by date. Collection includes + * categories directly specified as date ordered and also all subcategories + * of categories specified as being top level date ordered categories in + * config + * * @return */ public Collection getDateOrderedCategories(PageState state) { @@ -302,56 +327,57 @@ public final class NavigationConfig extends AbstractConfig { if (state == null) { categories = getCurrentDateOrderCategories(); } else { - categories = (Collection)m_allDateOrderCategories.get(state); + categories = (Collection) m_allDateOrderCategories.get(state); } return categories; } - public boolean isDateOrderedCategory (Category cat, PageState state) { + public boolean isDateOrderedCategory(Category cat, PageState state) { return getDateOrderedCategories(state).contains(cat.getID().toString()); } - private Collection getCurrentDateOrderCategories () { - if (s_fixedDateOrderCats == null) { - populateFixedDateOrderCats(); - } + private Collection getCurrentDateOrderCategories() { + if (s_fixedDateOrderCats == null) { + populateFixedDateOrderCats(); + } - String[] topLevelCats = (String[]) get(m_topLevelDateOrderCategories); - Set allCats = new HashSet(); - allCats.addAll(s_fixedDateOrderCats); - for (int i = 0; i < topLevelCats.length; i++) { - try { - String[] categoryArray = StringUtils.split(topLevelCats[i], ':'); - String order = ""; - if (categoryArray.length > 1) { - order = ":" + categoryArray[1]; - } - Category topLevelCat = new Category(new BigDecimal(categoryArray[0])); - s_log.debug("retrieved top level category " + topLevelCat); - Set childCats = new HashSet(); - CategoryCollection children = topLevelCat.getDescendants(); - while (children.next()) { - BigDecimal id = children.getCategory().getID(); - childCats.add(id.toString() + order); - } + String[] topLevelCats = (String[]) get(m_topLevelDateOrderCategories); + Set allCats = new HashSet(); + allCats.addAll(s_fixedDateOrderCats); + for (int i = 0; i < topLevelCats.length; i++) { + try { + String[] categoryArray = StringUtils.split(topLevelCats[i], ':'); + String order = ""; + if (categoryArray.length > 1) { + order = ":" + categoryArray[1]; + } + Category topLevelCat = new Category(new BigDecimal( + categoryArray[0])); + s_log.debug("retrieved top level category " + topLevelCat); + Set childCats = new HashSet(); + CategoryCollection children = topLevelCat.getDescendants(); + while (children.next()) { + BigDecimal id = children.getCategory().getID(); + childCats.add(id.toString() + order); + } - allCats.addAll(childCats); - } catch (DataObjectNotFoundException e) { - // non existent category id specified in configuration. Output warning to - // logs and continue - s_log.warn("Category with id " + topLevelCats[i] + - " specified in configuration as a top level date order category, " + - "but the category does not exist"); - } catch (NumberFormatException e) { - // non number specified in configuration. Output warning to - // logs and continue - s_log.warn("Category with id " + topLevelCats[i] + - " specified in configuration as a top level date order category, "+ - "but this is not a valid number"); - } + allCats.addAll(childCats); + } catch (DataObjectNotFoundException e) { + // non existent category id specified in configuration. Output warning to + // logs and continue + s_log.warn("Category with id " + topLevelCats[i] + + " specified in configuration as a top level date order category, " + + "but the category does not exist"); + } catch (NumberFormatException e) { + // non number specified in configuration. Output warning to + // logs and continue + s_log.warn("Category with id " + topLevelCats[i] + + " specified in configuration as a top level date order category, " + + "but this is not a valid number"); + } - } - return allCats; + } + return allCats; } @@ -360,49 +386,54 @@ public final class NavigationConfig extends AbstractConfig { // concurrently first time after server restart private synchronized void populateFixedDateOrderCats() { - String[] catArray = (String[]) get(m_dateOrderCategories); - s_fixedDateOrderCats = new HashSet(); - for (int i = 0; i < catArray.length; i++) { - try { - // don't need to do this, as including invalid or non existent - // ids will not have any adverse effects, but this gives us a chance to - // provide some warning to the users when they find that a category - // they expected to be date ordered isn't because they mistyped etc. - // This only occurs once, first time navigation page is accessed after - // server restart - String[] category = StringUtils.split(catArray[i], ':'); - Category cat = new Category(new BigDecimal(category[0])); - } catch (DataObjectNotFoundException e) { - // non existent category id specified in configuration. Output warning to - // logs and continue - s_log.warn("Category with id " + catArray[i] + - " specified in configuration as a date ordered category, but the category" + - "does not exist"); - } catch (NumberFormatException e) { - // non number specified in configuration. Output warning to - // logs and continue - s_log.warn("Category with id " + catArray[i] + - " specified in configuration as a date ordered category, "+ - "but this is not a valid number"); - } - s_fixedDateOrderCats.add(catArray[i]); - } + String[] catArray = (String[]) get(m_dateOrderCategories); + s_fixedDateOrderCats = new HashSet(); + for (int i = 0; i < catArray.length; i++) { + try { + // don't need to do this, as including invalid or non existent + // ids will not have any adverse effects, but this gives us a chance to + // provide some warning to the users when they find that a category + // they expected to be date ordered isn't because they mistyped etc. + // This only occurs once, first time navigation page is accessed after + // server restart + String[] category = StringUtils.split(catArray[i], ':'); + Category cat = new Category(new BigDecimal(category[0])); + } catch (DataObjectNotFoundException e) { + // non existent category id specified in configuration. Output warning to + // logs and continue + s_log.warn("Category with id " + catArray[i] + + " specified in configuration as a date ordered category, but the category" + + "does not exist"); + } catch (NumberFormatException e) { + // non number specified in configuration. Output warning to + // logs and continue + s_log.warn("Category with id " + catArray[i] + + " specified in configuration as a date ordered category, " + + "but this is not a valid number"); + } + s_fixedDateOrderCats.add(catArray[i]); + } } public final TreeCatProvider getDefaultMenuCatProvider() { - if (null == m_treeCatProvider) { - try { - Class providerClass = (Class) get(m_defaultMenuCatProvider); - if (providerClass == null) { - m_treeCatProvider = new Menu(); - } else { - m_treeCatProvider = (TreeCatProvider) providerClass.newInstance(); - } - } catch (Exception ex) { - throw new UncheckedWrapperException(ex); - } - } - return m_treeCatProvider; + if (null == m_treeCatProvider) { + try { + Class providerClass = (Class) get(m_defaultMenuCatProvider); + if (providerClass == null) { + m_treeCatProvider = new Menu(); + } else { + m_treeCatProvider = (TreeCatProvider) providerClass + .newInstance(); + } + } catch (Exception ex) { + throw new UncheckedWrapperException(ex); + } + } + return m_treeCatProvider; + } + + public Boolean getUseLanguageExtension() { + return (Boolean) get(m_useLanguageExtension); } } diff --git a/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig_parameter.properties b/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig_parameter.properties index fac731a61..916eb7b6b 100755 --- a/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig_parameter.properties +++ b/ccm-navigation/src/com/arsdigita/navigation/NavigationConfig_parameter.properties @@ -82,3 +82,8 @@ com.arsdigita.navigation.default_menu_cat_provider.title=Default Menu Category P com.arsdigita.navigation.default_menu_cat_provider.purpose=Class that provides categories included in menu for any categories that do not have an alternative provider registered com.arsdigita.navigation.default_menu_cat_provider.example=com.arsdigita.navigation.ui.category.TreeCatProviderImpl com.arsdigita.navigation.default_menu_cat_provider.format=[class] + +com.arsdigita.navigation.use_language_extension.title=Use language extension +com.arsdigita.navigation.use_language_extension.purpose=Use language extension to distiguish language version. +com.arsdigita.navigation.use_language_extension.example=true +com.arsdigita.navigation.use_language_extension.format=[Boolean] \ No newline at end of file diff --git a/ccm-navigation/src/com/arsdigita/navigation/NavigationFileResolver.java b/ccm-navigation/src/com/arsdigita/navigation/NavigationFileResolver.java index 24ac76a3c..b8bd30e41 100755 --- a/ccm-navigation/src/com/arsdigita/navigation/NavigationFileResolver.java +++ b/ccm-navigation/src/com/arsdigita/navigation/NavigationFileResolver.java @@ -43,16 +43,18 @@ import com.arsdigita.web.Application; import com.arsdigita.web.DefaultApplicationFileResolver; import com.arsdigita.web.Web; +import com.arsdigita.globalization.GlobalizationHelper; + /** * Manages the processing of URLs in the Navigation application. * */ public class NavigationFileResolver extends DefaultApplicationFileResolver { - private static final Logger s_log = - Logger.getLogger(NavigationFileResolver.class); - private static final String CATEGORY_PATH_ATTR = - NavigationFileResolver.class + ".categoryPath"; + private static final Logger s_log = Logger.getLogger( + NavigationFileResolver.class); + private static final String CATEGORY_PATH_ATTR + = NavigationFileResolver.class + ".categoryPath"; // path is set in a cookie, which navigation models may use if they wish public static final String PATH_COOKIE_NAME = "ad_path"; public static final char PATH_COOKIE_SEPARATOR = '|'; @@ -71,6 +73,14 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { s_log.debug("Resolving " + path); } + if (Navigation.getConfig().getUseLanguageExtension() + && path.matches("(.*)/index\\.[a-zA-Z]{2}")) { + + final String lang = path.substring(path.length() - 2); + path = path.substring(0, path.length() - "index.$$".length()); + GlobalizationHelper.setSelectedLocale(lang); + } + if (path.equals("/category.jsp")) { Navigation nav = (Navigation) Web.getWebContext().getApplication(); @@ -105,14 +115,16 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { // check that the category is in the tree of categories Category root = null; DataCollection objs = SessionManager.getSession() - .retrieve(Domain.BASE_DATA_OBJECT_TYPE); - objs.addEqualsFilter("model.ownerUseContext.categoryOwner.id", nav.getID()); + .retrieve(Domain.BASE_DATA_OBJECT_TYPE); + objs.addEqualsFilter("model.ownerUseContext.categoryOwner.id", nav + .getID()); String dispatcherContext = null; TemplateContext tc = Navigation.getContext().getTemplateContext(); if (tc != null) { dispatcherContext = tc.getContext(); } - objs.addEqualsFilter("model.ownerUseContext.useContext", dispatcherContext); + objs.addEqualsFilter("model.ownerUseContext.useContext", + dispatcherContext); DomainCollection domains = new DomainCollection(objs); if (domains.next()) { root = ((Domain) domains.getDomainObject()).getModel(); @@ -141,7 +153,8 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { cats.add(parents.getCategory()); } - Category[] catsArray = (Category[]) cats.toArray(new Category[cats.size()]); + Category[] catsArray = (Category[]) cats.toArray(new Category[cats + .size()]); sreq.setAttribute(CATEGORY_PATH_ATTR, catsArray); @@ -206,9 +219,8 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { // be used if a navigation model retains the cookie. If we link to // another application, it's navigation model may use this when // deciding whether to trust the given path. - - path.append(PATH_COOKIE_SEPARATOR + - Kernel.getContext().getResource().getID().toString()); + path.append(PATH_COOKIE_SEPARATOR + Kernel.getContext().getResource() + .getID().toString()); for (int i = 0; i < catsArray.length; i++) { Category cat = catsArray[i]; path.append(PATH_COOKIE_SEPARATOR + cat.getID().toString()); @@ -223,7 +235,6 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { } resp.addCookie(cookie); - } public static Category[] getCategoryPath(HttpServletRequest req) { @@ -240,10 +251,11 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { } /** - * + * * @param cat * @param useContext - * @return + * + * @return */ private RequestDispatcher resolveTemplate(Category cat, String useContext) { Template template = null; @@ -258,9 +270,9 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { } // If there's an explicit use context which doesn't exist, give a 404 if (!Template.DEFAULT_USE_CONTEXT.equals(useContext) && null == template) { - s_log.debug("No template found in context " + getTemplateContext() + - " for category " + cat.getID() - + " with use context " + useContext); + s_log.debug("No template found in context " + getTemplateContext() + + " for category " + cat.getID() + + " with use context " + useContext); return null; } @@ -316,12 +328,13 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { /** * - * category resolution retained as an instance method to allow it to be - * overridden. Default functionality contained in static resolveCategory + * category resolution retained as an instance method to allow it to be + * overridden. Default functionality contained in static resolveCategory * method. * * @param root * @param path + * * @return */ protected Category[] resolvePath(Category root, String path) { @@ -329,13 +342,13 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { } /** - * Match a URL with the category tree and return the requested category - * if exists. + * Match a URL with the category tree and return the requested category if + * exists. * - * Quasimodo: Originally addEqualsFilter has been used to filter the - * appropriate category directly inside the SQL query. This isn't possible - * anymore due to the localised URLs of the new localised categories - * (or at least: not found it). Therefore we do the filtering in Java now. + * Quasimodo: Originally addEqualsFilter has been used to filter the + * appropriate category directly inside the SQL query. This isn't possible + * anymore due to the localised URLs of the new localised categories (or at + * least: not found it). Therefore we do the filtering in Java now. * */ public static Category[] resolveCategory(Category root, @@ -382,4 +395,5 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver { return (Category[]) cats.toArray(new Category[cats.size()]); } + }