Support for language extensions in ccm-navigation

git-svn-id: https://svn.libreccm.org/ccm/trunk@4912 8810af33-2d31-482b-a856-94f89814c4df
master
jensp 2017-08-20 10:57:17 +00:00
parent c3a9839ae5
commit e8775bb21e
7 changed files with 895 additions and 763 deletions

View File

@ -51,8 +51,8 @@ import org.apache.log4j.Logger;
/** /**
* A record containing server-session scoped configuration properties. * A record containing server-session scoped configuration properties.
* *
* Accessors of this class may return null. Developers should take care to trap null return values * Accessors of this class may return null. Developers should take care to trap
* in their code. * null return values in their code.
* *
* @see ContentSection#getConfig() * @see ContentSection#getConfig()
* *
@ -71,7 +71,8 @@ public final class CMSConfig extends AbstractConfig {
private static CMSConfig s_config; 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 <code>CMSConfig</code> record; it cannot be null * @return The <code>CMSConfig</code> 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 * Storage (map) for method getAssetStepsToSkip(ContentType type) to store
* are deemed irrelevant for the passid in type. * mapping of steps that are deemed irrelevant for the passid in type.
*/ */
private static Map s_skipAssetSteps = null; private static Map s_skipAssetSteps = null;
/** /**
* Item category add form specifies Subclass of ItemCategoryForm to use for the assign * Item category add form specifies Subclass of ItemCategoryForm to use for
* categories step. Used in c.ad.cms.ui.authoring.ItemCategoryStep * the assign categories step. Used in
* c.ad.cms.ui.authoring.ItemCategoryStep
*/ */
private final Parameter m_categoryAuthoringAddForm = new SpecificClassParameter( private final Parameter m_categoryAuthoringAddForm
"com.arsdigita.cms.category_authoring_add_form", = new SpecificClassParameter(
Parameter.REQUIRED, "com.arsdigita.cms.category_authoring_add_form",
ItemCategoryForm.class, Parameter.REQUIRED,
SimpleComponent.class); 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( private final Parameter m_defaultItemTemplatePath = new StringParameter(
"com.arsdigita.cms.default_item_template_path", "com.arsdigita.cms.default_item_template_path",
Parameter.REQUIRED, Parameter.REQUIRED,
"/default/item.jsp"); "/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( private final Parameter m_defaultFolderTemplatePath = new StringParameter(
"com.arsdigita.cms.default_folder_template_path", "com.arsdigita.cms.default_folder_template_path",
Parameter.REQUIRED, Parameter.REQUIRED,
"/default/folder.jsp"); "/default/folder.jsp");
/** /**
* Path or the root folter for template folders. Path is relative to webapp root. Modify with * Path or the root folter for template folders. Path is relative to webapp
* care! Usually modified by developers only! * root. Modify with care! Usually modified by developers only!
*/ */
private final Parameter m_templateRootPath = new StringParameter( private final Parameter m_templateRootPath = new StringParameter(
"com.arsdigita.cms.template_root_path", "com.arsdigita.cms.template_root_path",
@ -134,8 +139,8 @@ public final class CMSConfig extends AbstractConfig {
// } // }
// ADD: // ADD:
/** /**
* Item Adapters File, path to an XML resource containing adapter specifications. Path is * Item Adapters File, path to an XML resource containing adapter
* relative to webapp root. * specifications. Path is relative to webapp root.
*/ */
private final Parameter m_itemAdapters = new ResourceParameter( private final Parameter m_itemAdapters = new ResourceParameter(
"com.arsdigita.cms.item_adapters", "com.arsdigita.cms.item_adapters",
@ -143,22 +148,23 @@ public final class CMSConfig extends AbstractConfig {
"/WEB-INF/resources/cms-item-adapters.xml"); "/WEB-INF/resources/cms-item-adapters.xml");
// URL resource: protocol handler removal: END // URL resource: protocol handler removal: END
/** /**
* Use streamlined content creation: upon item creation, automatically open authoring steps and * Use streamlined content creation: upon item creation, automatically open
* forward to the next step * authoring steps and forward to the next step
*/ */
private final Parameter m_useStreamlinedCreation = new BooleanParameter( private final Parameter m_useStreamlinedCreation = new BooleanParameter(
"com.arsdigita.cms.use_streamlined_creation", "com.arsdigita.cms.use_streamlined_creation",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.TRUE); Boolean.TRUE);
/** /**
* DHTML Editor Configuration for use in CMS module, lists the config object name and Javascript * DHTML Editor Configuration for use in CMS module, lists the config object
* source location for its definition. * name and Javascript source location for its definition.
*/ */
private final Parameter m_dhtmlEditorConfig = new DHTMLEditorConfigParameter( private final Parameter m_dhtmlEditorConfig
"com.arsdigita.cms.dhtml_editor_config", = new DHTMLEditorConfigParameter(
Parameter.REQUIRED, "com.arsdigita.cms.dhtml_editor_config",
new DHTMLEditor.Config("Xinha.Config", Parameter.REQUIRED,
"/assets/xinha/CCMcmsXinhaConfig.js")); new DHTMLEditor.Config("Xinha.Config",
"/assets/xinha/CCMcmsXinhaConfig.js"));
// previous parameter definition: // previous parameter definition:
// > DHTMLEditor.Config.STANDARD); < // > DHTMLEditor.Config.STANDARD); <
// didn't work because of broken unmarshalling (cf. similiar problem // 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. // be accessable by other modules which use DHTMLeditor.
// Would be bad style to configure a cms specific parameter in core. // 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( private final Parameter m_dhtmlEditorPlugins = new StringArrayParameter(
"com.arsdigita.cms.dhtml_editor_plugins", "com.arsdigita.cms.dhtml_editor_plugins",
Parameter.OPTIONAL, Parameter.OPTIONAL,
null); null);
/** /**
* Prevent undesirable functions from being made available, eg images should only be added * Prevent undesirable functions from being made available, eg images should
* through the cms methods. * only be added through the cms methods.
*/ */
private final Parameter m_dhtmlEditorHiddenButtons = new StringArrayParameter( private final Parameter m_dhtmlEditorHiddenButtons
"com.arsdigita.cms.dhtml_editor_hidden_buttons", = new StringArrayParameter(
Parameter.OPTIONAL, "com.arsdigita.cms.dhtml_editor_hidden_buttons",
null); Parameter.OPTIONAL,
null);
/** /**
* Hide section admin tabs from users without administrative rights. * Hide section admin tabs from users without administrative rights.
*/ */
@ -227,8 +235,8 @@ public final class CMSConfig extends AbstractConfig {
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Hide timezone labels (if, for example, all users will be in the same timezone and such * Hide timezone labels (if, for example, all users will be in the same
* information would be unnecessary) * timezone and such information would be unnecessary)
*/ */
private final Parameter m_hideTimezone = new BooleanParameter( private final Parameter m_hideTimezone = new BooleanParameter(
"com.arsdigita.cms.hide_timezone", "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 * Specifies the name of the class to use as a PublishLifecycleListener
*/ */
private final Parameter m_publishLifecycleListenerClass = new StringParameter( private final Parameter m_publishLifecycleListenerClass
"com.arsdigita.cms.publish_lifecycle_listener_class", = new StringParameter(
Parameter.OPTIONAL, "com.arsdigita.cms.publish_lifecycle_listener_class",
PublishLifecycleListener.class.getName()); Parameter.OPTIONAL,
PublishLifecycleListener.class.getName());
/** /**
* Wether the Wysiwyg editor should clear the text of MSWord tags, everytime the user clicks on * Wether the Wysiwyg editor should clear the text of MSWord tags, everytime
* 'Save' * the user clicks on 'Save'
*/ */
private final Parameter m_saveTextCleansWordTags = new BooleanParameter( private final Parameter m_saveTextCleansWordTags = new BooleanParameter(
"com.arsdigita.cms.save_text_cleans_word_tags", "com.arsdigita.cms.save_text_cleans_word_tags",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); 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( private final Parameter m_disableFileAssetExtraction = new BooleanParameter(
"com.arsdigita.cms.search.disableFileAssetExtraction", "com.arsdigita.cms.search.disableFileAssetExtraction",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Whether an item's workflow should be deleted, once the item has been (re)published. * 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 * jensp 2014-11-07: Default changed from true to false. Deleting the
* that the authors have to reattach a workflow using the Workflow tab, which is complicated * assigned workflow means that the authors have to reattach a workflow
* (for some users too complicated). Also deleting the workflow means that the new convenient * 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. * link to restart a workflow will not work.
* *
*/ */
private final Parameter m_deleteWorkflowAfterPublication = new BooleanParameter( private final Parameter m_deleteWorkflowAfterPublication
"com.arsdigita.cms.delete_workflow_after_publication", = new BooleanParameter(
Parameter.REQUIRED, "com.arsdigita.cms.delete_workflow_after_publication",
Boolean.FALSE); 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( private final Parameter m_soonExpiredTimespanDays = new IntegerParameter(
"com.arsdigita.cms.soon_expired_timespan_days", "com.arsdigita.cms.soon_expired_timespan_days",
Parameter.REQUIRED, Parameter.REQUIRED,
new Integer(14)); 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( private final Parameter m_soonExpiredTimespanMonths = new IntegerParameter(
"com.arsdigita.cms.soon_expired_timespan_months", "com.arsdigita.cms.soon_expired_timespan_months",
@ -298,47 +313,51 @@ public final class CMSConfig extends AbstractConfig {
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.TRUE); 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( private final Parameter m_linksOnlyInSameSubsite = new BooleanParameter(
"com.arsdigita.cms.browse_links_in_same_subsite_only", "com.arsdigita.cms.browse_links_in_same_subsite_only",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Item category step extension hook: Subclass of ItemCategoryExtension which adds extension * Item category step extension hook: Subclass of ItemCategoryExtension
* actions for the category authoring step * which adds extension actions for the category authoring step
*/ */
private final Parameter m_categoryAuthoringExtension = new SpecificClassParameter( private final Parameter m_categoryAuthoringExtension
"com.arsdigita.cms.category_authoring_extension", = new SpecificClassParameter(
Parameter.REQUIRED, "com.arsdigita.cms.category_authoring_extension",
ItemCategoryExtension.class, Parameter.REQUIRED,
ItemCategoryExtension.class); ItemCategoryExtension.class,
ItemCategoryExtension.class);
/** /**
* Link available to reset lifecycle on republish. If false don't display the link otherwise * Link available to reset lifecycle on republish. If false don't display
* display. * the link otherwise display.
*/ */
private final Parameter m_hideResetLifecycleLink = new BooleanParameter( private final Parameter m_hideResetLifecycleLink = new BooleanParameter(
"com.arsdigita.cms.hide_reset_lifecycle_link", "com.arsdigita.cms.hide_reset_lifecycle_link",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.TRUE); 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( private final Parameter m_scoreTitleAndKeywords = new BooleanParameter(
"com.arsdigita.cms.search.score_title_and_keywords", "com.arsdigita.cms.search.score_title_and_keywords",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Title Weight, the relative weight given to title element within cms:item when ranking search * Title Weight, the relative weight given to title element within cms:item
* results (only used by interMedia) * when ranking search results (only used by interMedia)
*/ */
private final Parameter m_titleWeight = new IntegerParameter( private final Parameter m_titleWeight = new IntegerParameter(
"com.arsdigita.cms.search.intermedia.title_weight", "com.arsdigita.cms.search.intermedia.title_weight",
Parameter.OPTIONAL, Parameter.OPTIONAL,
new Integer(1)); new Integer(1));
/** /**
* Keyword Weight, the relative weight given to the dcKeywords element within dublinCore element * Keyword Weight, the relative weight given to the dcKeywords element
* within cms:item element when ranking search results (only used by interMedia) * within dublinCore element within cms:item element when ranking search
* results (only used by interMedia)
*/ */
private final Parameter m_keywordWeight = new IntegerParameter( private final Parameter m_keywordWeight = new IntegerParameter(
"com.arsdigita.cms.search.intermedia.keyword_weight", "com.arsdigita.cms.search.intermedia.keyword_weight",
@ -352,74 +371,83 @@ public final class CMSConfig extends AbstractConfig {
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.TRUE); Boolean.TRUE);
/** /**
* Asset steps to skip, specify asset steps that are not relevant for specific content types. * Asset steps to skip, specify asset steps that are not relevant for
* Each entry in the list is a : separated pair. The first string is the className for the type * specific content types. Each entry in the list is a : separated pair. The
* (refer to classname column in contenttypes table eg * first string is the className for the type (refer to classname column in
* com.arsdigita.cms.contenttypes.MultiPartArticle Second string is the name of the bebop step * contenttypes table eg com.arsdigita.cms.contenttypes.MultiPartArticle
* component eg com.arsdigita.cms.contenttypes.ui.ImageStep * Second string is the name of the bebop step component eg
* com.arsdigita.cms.contenttypes.ui.ImageStep
*/ */
private final Parameter m_skipAssetSteps = new StringArrayParameter( private final Parameter m_skipAssetSteps = new StringArrayParameter(
"com.arsdigita.cms.skip_asset_steps", "com.arsdigita.cms.skip_asset_steps",
Parameter.OPTIONAL, Parameter.OPTIONAL,
null); null);
/** /**
* Mandatory Descriptions Content types may refer to this to decide whether to validate against * Mandatory Descriptions Content types may refer to this to decide whether
* empty descriptions * to validate against empty descriptions
*/ */
private final Parameter m_mandatoryDescriptions = new BooleanParameter( private final Parameter m_mandatoryDescriptions = new BooleanParameter(
"com.arsdigita.cms.mandatory_descriptions", "com.arsdigita.cms.mandatory_descriptions",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Delete Finished Lifecycles. Decide whether lifecycles and their phases should be deleted from * Delete Finished Lifecycles. Decide whether lifecycles and their phases
* the system when finished. * should be deleted from the system when finished.
*/ */
private final Parameter m_deleteLifecycleWhenComplete = new BooleanParameter( private final Parameter m_deleteLifecycleWhenComplete
"com.arsdigita.cms.delete_lifecycle_when_complete", = new BooleanParameter(
Parameter.OPTIONAL, "com.arsdigita.cms.delete_lifecycle_when_complete",
Boolean.FALSE); 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( private final Parameter m_hasContactsAuthoringStep = new BooleanParameter(
"com.arsdigita.cms.has_contacts_authoring_step", "com.arsdigita.cms.has_contacts_authoring_step",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); Boolean.FALSE);
/** /**
* Ordering for nodes in assign category tree. Decide whether entries should be ordered * Ordering for nodes in assign category tree. Decide whether entries should
* alphabetically or according to sort key (maintained in category admin tab in content centre) * be ordered alphabetically or according to sort key (maintained in
* SortKey|Alphabetical is initialized in constructor! See below. * category admin tab in content centre) SortKey|Alphabetical is initialized
* in constructor! See below.
*/ */
private final Parameter m_categoryTreeOrdering = new EnumerationParameter( private final Parameter m_categoryTreeOrdering = new EnumerationParameter(
"com.arsdigita.cms.category_tree_order", "com.arsdigita.cms.category_tree_order",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Category.SORT_KEY); Category.SORT_KEY);
/** /**
* Allow creation of a new Use Context in category tab of content sections. "Use Context" is the * Allow creation of a new Use Context in category tab of content sections.
* construct to constitute a category hierarchy implementet in core. It is superseded by the * "Use Context" is the construct to constitute a category hierarchy
* construct "Category Domain" in Terms (ccm-ldn-terms). Global parameter for all content * implementet in core. It is superseded by the construct "Category Domain"
* sections. Default is false because all installation bundles use Terms. * 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( private final Parameter m_allowCategoryCreateUseContext
"com.arsdigita.cms.allow_category_create_use_context", = new BooleanParameter(
Parameter.REQUIRED, "com.arsdigita.cms.allow_category_create_use_context",
Boolean.FALSE); Parameter.REQUIRED,
Boolean.FALSE);
/** /**
* Allow content creation in Workspace (content center) section listing. Allows you to turn off * Allow content creation in Workspace (content center) section listing.
* the ability to create content in the 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 * jensp 2014-11-07: Default changed to false. This feature isn't used by
* it has some drawbacks, for example items creating using this way are put into the root * most users. Also it has some drawbacks, for example items creating using
* folder. * this way are put into the root folder.
*/ */
private final Parameter m_allowContentCreateInSectionListing = new BooleanParameter( private final Parameter m_allowContentCreateInSectionListing
"com.arsdigita.cms.allow_content_create_in_section_listing", = new BooleanParameter(
Parameter.REQUIRED, "com.arsdigita.cms.allow_content_create_in_section_listing",
Boolean.FALSE); Parameter.REQUIRED,
Boolean.FALSE);
/** /**
* Hide the legacy public site link in Workspace (content center) section listing. Legacy public * Hide the legacy public site link in Workspace (content center) section
* site display is replaced by navigation based presentation (or by portlets) and should be * listing. Legacy public site display is replaced by navigation based
* hidden in the admin ui by default now. * presentation (or by portlets) and should be hidden in the admin ui by
* default now.
*/ */
private final Parameter m_hideLegacyPublicSiteLink = new BooleanParameter( private final Parameter m_hideLegacyPublicSiteLink = new BooleanParameter(
"com.arsdigita.cms.hide_legacy_public_site_link", "com.arsdigita.cms.hide_legacy_public_site_link",
@ -429,31 +457,34 @@ public final class CMSConfig extends AbstractConfig {
// Notification related parameters // Notification related parameters
// /////////////////////////////////////////// // ///////////////////////////////////////////
/** /**
* Delete Sent Workflow Notifications. Decide whether successfully sent notifications and * Delete Sent Workflow Notifications. Decide whether successfully sent
* messages should be deleted from the system * notifications and messages should be deleted from the system
*/ */
private final Parameter m_deleteWorkflowNotificationsWhenSent = new BooleanParameter( private final Parameter m_deleteWorkflowNotificationsWhenSent
"com.arsdigita.cms.delete_workflow_notification_when_sent", = new BooleanParameter(
Parameter.OPTIONAL, "com.arsdigita.cms.delete_workflow_notification_when_sent",
Boolean.FALSE); 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( private final Parameter m_deleteExpiryNotificationsWhenSent
"com.arsdigita.cms.delete_expiry_notification_when_sent", = new BooleanParameter(
Parameter.OPTIONAL, "com.arsdigita.cms.delete_expiry_notification_when_sent",
Boolean.FALSE); Parameter.OPTIONAL,
Boolean.FALSE);
/** /**
* Amount of time (in hours) before the expiration of a content item that users in the Alert * Amount of time (in hours) before the expiration of a content item that
* Recipient role are alerted via email * users in the Alert Recipient role are alerted via email
*/ */
private final Parameter m_defaultNotificationTime = new IntegerParameter( private final Parameter m_defaultNotificationTime = new IntegerParameter(
"com.arsdigita.cms.default_notification_time", "com.arsdigita.cms.default_notification_time",
Parameter.REQUIRED, Parameter.REQUIRED,
new Integer(0)); new Integer(0));
/** /**
* Wether a content item's author should be notified by the item's LifecycleListener; defaults * Wether a content item's author should be notified by the item's
* to true * LifecycleListener; defaults to true
*/ */
private final Parameter m_notifyAuthorOnLifecycle = new BooleanParameter( private final Parameter m_notifyAuthorOnLifecycle = new BooleanParameter(
"com.arsdigita.cms.notify_author_on_lifecycle", "com.arsdigita.cms.notify_author_on_lifecycle",
@ -463,7 +494,8 @@ public final class CMSConfig extends AbstractConfig {
// Content Center (Workspace) config related parameters // 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( private final StringParameter m_contentCenterMap = new StringParameter(
"com.arsdigita.cms.loader.content_center_map", "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 // to SectionInitializer. However, it still may be useful to
// keep these for the default values. // keep these for the default values.
// /////////////////////////////////////////// // ///////////////////////////////////////////
private final Parameter m_defaultItemResolverClass = new SpecificClassParameter( private final Parameter m_defaultItemResolverClass
"com.arsdigita.cms.default_item_resolver_class", = new SpecificClassParameter(
Parameter.REQUIRED, "com.arsdigita.cms.default_item_resolver_class",
MultilingualItemResolver.class, Parameter.REQUIRED,
ItemResolver.class); MultilingualItemResolver.class,
private final Parameter m_defaultTemplateResolverClass = new SpecificClassParameter( ItemResolver.class);
"com.arsdigita.cms.default_template_resolver_class", private final Parameter m_defaultTemplateResolverClass
Parameter.REQUIRED, = new SpecificClassParameter(
DefaultTemplateResolver.class, "com.arsdigita.cms.default_template_resolver_class",
TemplateResolver.class); Parameter.REQUIRED,
DefaultTemplateResolver.class,
TemplateResolver.class);
///////////////////////////////////////////// /////////////////////////////////////////////
// ItemSearchWidget // ItemSearchWidget
///////////////////////////////////////////// /////////////////////////////////////////////
@ -505,10 +539,11 @@ public final class CMSConfig extends AbstractConfig {
// "com.arsdigita.cms.item_search.flat_browse_pane.enable", // "com.arsdigita.cms.item_search.flat_browse_pane.enable",
// Parameter.REQUIRED, // Parameter.REQUIRED,
// true); // true);
private final Parameter m_itemSearchFlatBrowsePanePageSize = new IntegerParameter( private final Parameter m_itemSearchFlatBrowsePanePageSize
"com.arsdigita.cms.item_search.flat_browse_pane.page_size", = new IntegerParameter(
Parameter.REQUIRED, "com.arsdigita.cms.item_search.flat_browse_pane.page_size",
20); Parameter.REQUIRED,
20);
///////////////////////////////////////////// /////////////////////////////////////////////
// FolderBrowse // FolderBrowse
///////////////////////////////////////////// /////////////////////////////////////////////
@ -528,10 +563,11 @@ public final class CMSConfig extends AbstractConfig {
//republish and withdraw items) is used. Otherwise the new style form is //republish and withdraw items) is used. Otherwise the new style form is
//used, which is more secure against wrong clicks. //used, which is more secure against wrong clicks.
////////////////////////////////////////////// //////////////////////////////////////////////
private final Parameter m_useOldStyleItemLifecycleItemPane = new BooleanParameter( private final Parameter m_useOldStyleItemLifecycleItemPane
"com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane", = new BooleanParameter(
Parameter.REQUIRED, "com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane",
false); Parameter.REQUIRED,
false);
//////////////////////////////////////////////// ////////////////////////////////////////////////
//Actives threaded publishing. If active, the publish process for //Actives threaded publishing. If active, the publish process for
//content items will run in a separate thread. May useful if you have //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 // ImageBrowser Parameter
///////////////////////////////////////////////// /////////////////////////////////////////////////
private final Parameter m_imageBrowserThumbnailMaxWidth = new IntegerParameter( private final Parameter m_imageBrowserThumbnailMaxWidth
"com.arsdigita.cms.image_browser.thumbnail_max_width", = new IntegerParameter(
Parameter.REQUIRED, "com.arsdigita.cms.image_browser.thumbnail_max_width",
50); Parameter.REQUIRED,
private final Parameter m_imageBrowserThumbnailMaxHeight = new IntegerParameter( 50);
"com.arsdigita.cms.image_browser.thumbnail_max_height", private final Parameter m_imageBrowserThumbnailMaxHeight
Parameter.REQUIRED, = new IntegerParameter(
50); "com.arsdigita.cms.image_browser.thumbnail_max_height",
Parameter.REQUIRED,
50);
private final Parameter m_imageBrowserCaptionSize = new IntegerParameter( private final Parameter m_imageBrowserCaptionSize = new IntegerParameter(
"com.arsdigita.cms.image_browser.caption_size", "com.arsdigita.cms.image_browser.caption_size",
Parameter.REQUIRED, Parameter.REQUIRED,
100); 100);
private final Parameter m_imageBrowserDescriptionSize = new IntegerParameter( private final Parameter m_imageBrowserDescriptionSize
"com.arsdigita.cms.image_browser.description_size", = new IntegerParameter(
Parameter.REQUIRED, "com.arsdigita.cms.image_browser.description_size",
400); Parameter.REQUIRED,
400);
private final Parameter m_imageBrowserTitleSize = new IntegerParameter( private final Parameter m_imageBrowserTitleSize = new IntegerParameter(
"com.arsdigita.cms.image_browser.title_size", "com.arsdigita.cms.image_browser.title_size",
Parameter.REQUIRED, Parameter.REQUIRED,
@ -627,11 +666,18 @@ public final class CMSConfig extends AbstractConfig {
60 * 60 * 24); 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( private final Parameter m_linkDescMaxLength = new IntegerParameter(
"com.arsdigita.cms.link_description_max_length", Parameter.REQUIRED, 400); "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 // publishToFile package related parameter
// /////////////////////////////////////////// // ///////////////////////////////////////////
@ -746,11 +792,14 @@ public final class CMSConfig extends AbstractConfig {
register(m_linkDescMaxLength); register(m_linkDescMaxLength);
register(m_useLanguageExtension);
loadInfo(); 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() { public final String getTemplateRoot() {
return (String) get(m_templateRootPath); 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() { public final String getDefaultContentSection() {
// return (String) get(m_defaultSection); // 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. * @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 * Internal class representing a DHTMLEditor configuration parameter. It
* DHMTLEditor Config object (internal class in DHTMLEditor). * 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 * XXX Method unmarshal is broken and currently does not work correctly. It
* default values provided by using DHTMLEditor.Config.Standard (see parameter * does not process default values provided by using
* m_dhtmlEditorConfig above). May be a similiar problem as with ResourceParameter and default * DHTMLEditor.Config.Standard (see parameter m_dhtmlEditorConfig above).
* value, see patch provided by pbrucha. Best solution may be to remove this special parameter * May be a similiar problem as with ResourceParameter and default value,
* class and use a string parameter instead to directly create a DHTMLEditor.Config object. * see patch provided by pbrucha. Best solution may be to remove this
* (pboy, 2010-09-02) * 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 { 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 * The relative weight given to the dcKeywords element within dublinCore
* element when ranking search results Only used by the interMedia query engine. * element within cms:item element when ranking search results Only used by
* the interMedia query engine.
* *
*/ */
public Integer getKeywordSearchWeight() { 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 * The relative weight given to title element within cms:item element when
* results Only used by the interMedia query engine. * ranking search results Only used by the interMedia query engine.
* *
*/ */
public Integer getTitleSearchWeight() { 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 * @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 * for the given content type, returns a collection of steps that are deemed
* type. * irrelevant for the type.
* *
* If no irrelevant steps, an empty set is returned. * 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 * @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 // 1st string is name of content type, 2nd string is name of asset step
s_log.debug("parameter read - type = " + pair[0] s_log.debug("parameter read - type = " + pair[0]
+ " - step = " + pair[1]); + " - step = " + pair[1]);
Collection typeSteps = (Collection) s_skipAssetSteps.get(pair[0]); Collection typeSteps = (Collection) s_skipAssetSteps.get(
pair[0]);
if (typeSteps == null) { if (typeSteps == null) {
typeSteps = new HashSet(); typeSteps = new HashSet();
s_skipAssetSteps.put(pair[0], typeSteps); 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() { 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 * Used to decide whether lifecycles (and all asociated phases) should be
* system when complete * deleted from the system when complete
* *
* (Deleting lifecycle means that you lose a bit of historical information eg when was this item * (Deleting lifecycle means that you lose a bit of historical information
* unpublished) * eg when was this item unpublished)
*/ */
public boolean deleteFinishedLifecycles() { public boolean deleteFinishedLifecycles() {
return ((Boolean) get(m_deleteLifecycleWhenComplete)).booleanValue(); 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 * If true, notifications and messages are deleted if the notification is
* send failures are retained * successfully sent. Any send failures are retained
* *
*/ */
public boolean deleteExpiryNotifications() { 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 * If true, notifications and messages are deleted if the notification is
* send failures are retained * successfully sent. Any send failures are retained
* *
*/ */
public boolean deleteWorkflowNotifications() { 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 * I'am not sure for what this method is. I found it here when I tried
* multiple parts to an ContentType, like ccm-cms-types-contact and the Multipart article do. I * figure out how add multiple parts to an ContentType, like
* think this method should not be here because it is only needed by one specific contenttype. * ccm-cms-types-contact and the Multipart article do. I think this method
* Because of this, I think that this method and the contact are violating many rules of modern * should not be here because it is only needed by one specific contenttype.
* software design. Jens Pelzetter, 2009-06-02. * 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 * @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. * Retrieve whether to allow creation of a new Use Context in category tab
* "Use Context" is used to constitute a category hierarchy in core. It is superseded by the * of content sections. "Use Context" is used to constitute a category
* construct "Category Domain" in Terms (ccm-ldn-terms). Global parameter for all content * hierarchy in core. It is superseded by the construct "Category Domain" in
* sections. Default is false because all installation bundles use Terms. * 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) * @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 * Hide the (no longer used) legacy public site link in Workspace (content
* listing, true by default. * center) section listing, true by default.
*/ */
public final boolean getHideLegacyPublicSiteLink() { public final boolean getHideLegacyPublicSiteLink() {
return ((Boolean) get(m_hideLegacyPublicSiteLink)).booleanValue(); return ((Boolean) get(m_hideLegacyPublicSiteLink)).booleanValue();
@ -1181,15 +1244,18 @@ public final class CMSConfig extends AbstractConfig {
} }
public int getImageBrowserCaptionSize() { public int getImageBrowserCaptionSize() {
return Math.min(((Integer) get(m_imageBrowserCaptionSize)).intValue(), 100); return Math.min(((Integer) get(m_imageBrowserCaptionSize)).intValue(),
100);
} }
public int getImageBrowserDescriptionSize() { public int getImageBrowserDescriptionSize() {
return Math.min(((Integer) get(m_imageBrowserDescriptionSize)).intValue(), 400); return Math.min(((Integer) get(m_imageBrowserDescriptionSize))
.intValue(), 400);
} }
public int getImageBrowserTitleSize() { public int getImageBrowserTitleSize() {
return Math.min(((Integer) get(m_imageBrowserTitleSize)).intValue(), 200); return Math
.min(((Integer) get(m_imageBrowserTitleSize)).intValue(), 200);
} }
public Boolean getImageCacheEnabled() { public Boolean getImageCacheEnabled() {
@ -1239,4 +1305,8 @@ public final class CMSConfig extends AbstractConfig {
return (Integer) get(m_linkDescMaxLength); return (Integer) get(m_linkDescMaxLength);
} }
public Boolean getUseLanguageExtension() {
return (Boolean) get(m_useLanguageExtension);
}
} }

View File

@ -360,3 +360,8 @@ com.arsdigita.cms.link_description_max_length.title=Maximum length of the descri
com.arsdigita.cms.link_description_max_length.purpose=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.example=400
com.arsdigita.cms.link_description_max_length.format={Integer] 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]

View File

@ -20,6 +20,7 @@ package com.arsdigita.cms.dispatcher;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.cms.CMS; import com.arsdigita.cms.CMS;
import com.arsdigita.cms.CMSConfig;
import com.arsdigita.cms.ContentBundle; import com.arsdigita.cms.ContentBundle;
import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentItem;
import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.ContentSection;
@ -31,10 +32,12 @@ import com.arsdigita.domain.DomainObjectFactory;
import com.arsdigita.globalization.GlobalizationHelper; import com.arsdigita.globalization.GlobalizationHelper;
import com.arsdigita.persistence.OID; import com.arsdigita.persistence.OID;
import com.arsdigita.util.Assert; import com.arsdigita.util.Assert;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
@ -43,31 +46,29 @@ import java.util.Iterator;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* Resolves items to URLs and URLs to items for multiple language * Resolves items to URLs and URLs to items for multiple language variants.
* variants.
* *
* Created Mon Jan 20 14:30:03 2003. * Created Mon Jan 20 14:30:03 2003.
* *
* @author <a href="mailto:mhanisch@redhat.com">Michael Hanisch</a> * @author <a href="mailto:mhanisch@redhat.com">Michael Hanisch</a>
* @version $Id: MultilingualItemResolver.java 2090 2010-04-17 08:04:14Z pboy $ * @version $Id: MultilingualItemResolver.java 2090 2010-04-17 08:04:14Z pboy $
*/ */
public class MultilingualItemResolver extends AbstractItemResolver implements ItemResolver { public class MultilingualItemResolver extends AbstractItemResolver implements
ItemResolver {
private static final Logger s_log = Logger.getLogger private static final Logger s_log = Logger.getLogger(
(MultilingualItemResolver.class); MultilingualItemResolver.class);
private static MasterPage s_masterP = null; private static MasterPage s_masterP = null;
private static final String ADMIN_PREFIX = "admin"; private static final String ADMIN_PREFIX = "admin";
/** /**
* The string identifying an item's ID in the query string of a * The string identifying an item's ID in the query string of a URL.
* URL.
*/ */
protected static final String ITEM_ID = "item_id"; protected static final String ITEM_ID = "item_id";
/** /**
* The separator used in URL query strings; should be either "&" * The separator used in URL query strings; should be either "&" or ";".
* or ";".
*/ */
protected static final String SEPARATOR = "&"; protected static final String SEPARATOR = "&";
@ -79,21 +80,21 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* Returns a content item based on section, url, and use context. * Returns a content item based on section, url, and use context.
* *
* @param section The current content section * @param section The current content section
* @param url The section-relative URL * @param url The section-relative URL
* @param context The use context, * @param context The use context, e.g. <code>ContentItem.LIVE</code>,
* e.g. <code>ContentItem.LIVE</code>, * <code>CMSDispatcher.PREVIEW</code> or
* <code>CMSDispatcher.PREVIEW</code> or * <code>ContentItem.DRAFT</code>. See {@link
* <code>ContentItem.DRAFT</code>. See {@link
* #getCurrentContext}. * #getCurrentContext}.
*
* @return The content item, or null if no such item exists * @return The content item, or null if no such item exists
*/ */
@Override @Override
public ContentItem getItem(final ContentSection section, public ContentItem getItem(final ContentSection section,
String url, String url,
final String context) { final String context) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Resolving the item in content section " + section + s_log.debug("Resolving the item in content section " + section
" at URL '" + url + "' for context " + context); + " at URL '" + url + "' for context " + context);
} }
Assert.exists(section, "ContentSection section"); Assert.exists(section, "ContentSection section");
@ -118,25 +119,23 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// not be live. // not be live.
//Assert.isTrue(rootFolder.isLive(), //Assert.isTrue(rootFolder.isLive(),
// "live context - root folder of secion must be live"); // "live context - root folder of secion must be live");
// If the context is 'live', we need the live item. // If the context is 'live', we need the live item.
rootFolder = (Folder) rootFolder.getLiveVersion(); rootFolder = (Folder) rootFolder.getLiveVersion();
if (rootFolder == null) { if (rootFolder == null) {
s_log.debug("The live version of the root folder is " + s_log.debug("The live version of the root folder is "
"null; returning no item"); + "null; returning no item");
} else { } else {
s_log.debug("The root folder has a live version; " + s_log.debug("The root folder has a live version; "
"recursing"); + "recursing");
final String prefix = final String prefix = section.getURL() + rootFolder
section.getURL() + rootFolder.getPath(); .getPath();
if (url.startsWith(prefix)) { if (url.startsWith(prefix)) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("The URL starts with prefix '" + s_log.debug("The URL starts with prefix '" + prefix
prefix + "'; removing it"); + "'; removing it");
} }
url = url.substring(prefix.length()); url = url.substring(prefix.length());
@ -145,8 +144,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
final ContentItem item = getItemFromLiveURL(url, rootFolder); final ContentItem item = getItemFromLiveURL(url, rootFolder);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Resolved URL '" + url + "' to item " + s_log
item); .debug("Resolved URL '" + url + "' to item " + item);
} }
return item; return item;
@ -161,11 +160,9 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// 'item_id', then try to instanciate item_id value // 'item_id', then try to instanciate item_id value
// and return FIXME: Please hack this if there is // and return FIXME: Please hack this if there is
// more graceful solution. [aavetyan] // more graceful solution. [aavetyan]
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.isTrue Assert.isTrue(url.indexOf(ITEM_ID) >= 0,
(url.indexOf(ITEM_ID) >= 0, "url must contain parameter " + ITEM_ID);
"url must contain parameter " + ITEM_ID);
} }
final ContentItem item = getItemFromDraftURL(url); final ContentItem item = getItemFromDraftURL(url);
@ -182,8 +179,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
if (url.startsWith(prefix)) { if (url.startsWith(prefix)) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("The URL starts with prefix '" + s_log.debug("The URL starts with prefix '" + prefix
prefix + "'; removing it"); + "'; removing it");
} }
url = url.substring(prefix.length()); url = url.substring(prefix.length());
@ -197,8 +194,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
return item; return item;
} else { } else {
throw new IllegalArgumentException throw new IllegalArgumentException(
("Invalid item resolver context " + context); "Invalid item resolver context " + context);
} }
} }
@ -211,8 +208,10 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* Fetches the current context based on the page state. * Fetches the current context based on the page state.
* *
* @param state the current page state * @param state the current page state
*
* @return the context of the current URL, such as * @return the context of the current URL, such as
* <code>ContentItem.LIVE</code> or <code>ContentItem.DRAFT</code> * <code>ContentItem.LIVE</code> or <code>ContentItem.DRAFT</code>
*
* @see ContentItem#LIVE * @see ContentItem#LIVE
* @see ContentItem#DRAFT * @see ContentItem#DRAFT
*/ */
@ -223,13 +222,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// XXX need to use Web.getWebContext().getRequestURL() here. // XXX need to use Web.getWebContext().getRequestURL() here.
String url = state.getRequest().getRequestURI(); String url = state.getRequest().getRequestURI();
final ContentSection section = final ContentSection section = CMS.getContext().getContentSection();
CMS.getContext().getContentSection();
// If this page is associated with a content section, // If this page is associated with a content section,
// transform the URL so that it is relative to the content // transform the URL so that it is relative to the content
// section site node. // section site node.
if (section != null) { if (section != null) {
final String sectionURL = section.getURL(); final String sectionURL = section.getURL();
@ -241,12 +238,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// Remove any template-specific URL components (will only work // Remove any template-specific URL components (will only work
// if they're first in the URL at this point; verify). XXX but // if they're first in the URL at this point; verify). XXX but
// we don't actually verify? // we don't actually verify?
url = stripTemplateFromURL(url); url = stripTemplateFromURL(url);
// Determine if we are under the admin UI. // Determine if we are under the admin UI.
if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter
if (url.startsWith(ADMIN_PREFIX) || url.startsWith(ContentCenter.getURL())) { .getURL())) {
return ContentItem.DRAFT; return ContentItem.DRAFT;
} else { } else {
return ContentItem.LIVE; return ContentItem.LIVE;
@ -256,52 +252,54 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
* *
* @param itemId The item ID * @param itemId The item ID
* @param name The name of the content page * @param name The name of the content page
* @param state The page state * @param state The page state
* @param section the content section to which the item belongs * @param section the content section to which the item belongs
* @param context the context of the URL, such as "live" or * @param context the context of the URL, such as "live" or "admin"
* "admin" *
* @return The URL of the item * @return The URL of the item
*
* @see #getCurrentContext * @see #getCurrentContext
*/ */
@Override @Override
public String generateItemURL(final PageState state, public String generateItemURL(final PageState state,
final BigDecimal itemId, final BigDecimal itemId,
final String name, final String name,
final ContentSection section, final ContentSection section,
final String context) { final String context) {
return generateItemURL(state, itemId, name, section, context, null); return generateItemURL(state, itemId, name, section, context, null);
} }
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
* *
* @param itemId The item ID * @param itemId The item ID
* @param name The name of the content page * @param name The name of the content page
* @param state The page state * @param state The page state
* @param section the content section to which the item belongs * @param section the content section to which the item belongs
* @param context the context of the URL, such as "live" or * @param context the context of the URL, such as "live" or "admin"
* "admin" * @param templateContext the context for the URL, such as "public"
* @param templateContext the context for the URL, such as *
* "public"
* @return The URL of the item * @return The URL of the item
*
* @see #getCurrentContext * @see #getCurrentContext
*/ */
@Override @Override
public String generateItemURL(final PageState state, public String generateItemURL(final PageState state,
final BigDecimal itemId, final BigDecimal itemId,
final String name, final String name,
final ContentSection section, final ContentSection section,
final String context, final String context,
final String templateContext) { final String templateContext) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generating an item URL for item id " + itemId + s_log.debug("Generating an item URL for item id " + itemId
", section " + section + ", and context '" + + ", section " + section + ", and context '"
context + "' with name '" + name + "'"); + context
+ "' with name '" + name + "'");
} }
Assert.exists(itemId, "BigDecimal itemId"); Assert.exists(itemId, "BigDecimal itemId");
Assert.exists(context, "String context"); Assert.exists(context, "String context");
Assert.exists(section, "ContentSection section"); Assert.exists(section, "ContentSection section");
@ -318,59 +316,59 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.exists(item, "item"); Assert.exists(item, "item");
Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()), Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()),
"Generating " + ContentItem.LIVE + " " + "Generating " + ContentItem.LIVE + " "
"URL; this item must be the live version"); + "URL; this item must be the live version");
} }
return generateLiveURL(section, item, templateContext); return generateLiveURL(section, item, templateContext);
} else { } else {
throw new IllegalArgumentException throw new IllegalArgumentException("Unknown context '" + context
("Unknown context '" + context + "'"); + "'");
} }
} }
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
* *
* @param item The item * @param item The item
* @param state The page state * @param state The page state
* @param section the content section to which the item belongs * @param section the content section to which the item belongs
* @param context the context of the URL, such as "live" or * @param context the context of the URL, such as "live" or "admin"
* "admin" *
* @return The URL of the item * @return The URL of the item
*
* @see #getCurrentContext * @see #getCurrentContext
*/ */
@Override @Override
public String generateItemURL(final PageState state, public String generateItemURL(final PageState state,
final ContentItem item, final ContentItem item,
final ContentSection section, final ContentSection section,
final String context) { final String context) {
return generateItemURL(state, item, section, context, null); return generateItemURL(state, item, section, context, null);
} }
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
* *
* @param item The item * @param item The item
* @param state The page state * @param state The page state
* @param section the content section to which the item belongs * @param section the content section to which the item belongs
* @param context the context of the URL, such as "live" or * @param context the context of the URL, such as "live" or "admin"
* "admin" * @param templateContext the context for the URL, such as "public"
* @param templateContext the context for the URL, such as *
* "public"
* @return The URL of the item * @return The URL of the item
*
* @see #getCurrentContext * @see #getCurrentContext
*/ */
@Override @Override
public String generateItemURL(final PageState state, public String generateItemURL(final PageState state,
final ContentItem item, final ContentItem item,
ContentSection section, ContentSection section,
final String context, final String context,
final String templateContext) { final String templateContext) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generating an item URL for item " + item + s_log.debug("Generating an item URL for item " + item + ", section "
", section " + section + ", and context " + + section + ", and context " + context);
context);
} }
Assert.exists(item, "ContentItem item"); Assert.exists(item, "ContentItem item");
@ -383,8 +381,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
if (ContentItem.DRAFT.equals(context)) { if (ContentItem.DRAFT.equals(context)) {
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.isTrue(ContentItem.DRAFT.equals(item.getVersion()), Assert.isTrue(ContentItem.DRAFT.equals(item.getVersion()),
"Generating " + ContentItem.DRAFT + "Generating " + ContentItem.DRAFT
" url: item must be draft version"); + " url: item must be draft version");
} }
return generateDraftURL(section, item.getID()); return generateDraftURL(section, item.getID());
@ -393,8 +391,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} else if (ContentItem.LIVE.equals(context)) { } else if (ContentItem.LIVE.equals(context)) {
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()), Assert.isTrue(ContentItem.LIVE.equals(item.getVersion()),
"Generating " + ContentItem.LIVE + "Generating " + ContentItem.LIVE
" url: item must be live version"); + " url: item must be live version");
} }
return generateLiveURL(section, item, templateContext); return generateLiveURL(section, item, templateContext);
@ -404,18 +402,19 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} }
/** /**
* Returns a master page based on page state (and content * Returns a master page based on page state (and content section).
* section).
* *
* @param item The content item * @param item The content item
* @param request The HTTP request * @param request The HTTP request
*
* @return The master page * @return The master page
*
* @throws javax.servlet.ServletException * @throws javax.servlet.ServletException
*/ */
@Override @Override
public CMSPage getMasterPage(final ContentItem item, public CMSPage getMasterPage(final ContentItem item,
final HttpServletRequest request) final HttpServletRequest request)
throws ServletException { throws ServletException {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Getting the master page for item " + item); s_log.debug("Getting the master page for item " + item);
} }
@ -436,25 +435,27 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
/** /**
* Returns content item's draft version URL * Returns content item's draft version URL
* *
* @param section The content section to which the item belongs * @param section The content section to which the item belongs
* @param itemId The content item's ID * @param itemId The content item's ID
*
* @return generated URL string * @return generated URL string
*/ */
protected String generateDraftURL(final ContentSection section, protected String generateDraftURL(final ContentSection section,
final BigDecimal itemId) { final BigDecimal itemId) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generating draft URL for item ID " + itemId + s_log.debug("Generating draft URL for item ID " + itemId
" and section " + section); + " and section " + section);
} }
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.isTrue(section != null && itemId != null, Assert.isTrue(section != null && itemId != null,
"get draft url: neither secion nor item " + "get draft url: neither secion nor item "
"can be null"); + "can be null");
} }
final String url = ContentItemPage.getItemURL final String url = ContentItemPage.getItemURL(section.getPath() + "/",
(section.getPath() + "/", itemId, ContentItemPage.AUTHORING_TAB); itemId,
ContentItemPage.AUTHORING_TAB);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generated draft URL " + url); s_log.debug("Generated draft URL " + url);
@ -464,28 +465,29 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} }
/** /**
* Generate a <em>language-independent</em> URL to the * Generate a <em>language-independent</em> URL to the <code>item</code> in
* <code>item</code> in the given section.<p> When a client * the given section.<p>
* retrieves this URL, the URL is resolved to point to a specific * When a client retrieves this URL, the URL is resolved to point to a
* language instance of the item referenced here, i.e. this URL * specific language instance of the item referenced here, i.e. this URL
* will be resolved to a <em>language-specific</em> URL * will be resolved to a <em>language-specific</em> URL internally.
* internally.
* *
* @param section the <code>ContentSection</code> that contains this item * @param section the <code>ContentSection</code> that contains this
* @param item <code>ContentItem</code> for which a URL should be * item
* constructed. * @param item <code>ContentItem</code> for which a URL should be
* @param templateContext template context; will be ignored if <code>null</code> * constructed.
* @param templateContext template context; will be ignored if
* <code>null</code>
* *
* @return a <em>language-independent</em> URL to the * @return a <em>language-independent</em> URL to the <code>item</code> in
* <code>item</code> in the given <code>section</code>, which will * the given <code>section</code>, which will be presented within
* be presented within the given <code>templateContext</code> * the given <code>templateContext</code>
*/ */
protected String generateLiveURL(final ContentSection section, protected String generateLiveURL(final ContentSection section,
final ContentItem item, final ContentItem item,
final String templateContext) { final String templateContext) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generating live URL for item " + item + " in " + s_log.debug("Generating live URL for item " + item + " in "
"section " + section); + "section " + section);
} }
/* /*
@ -502,7 +504,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// This is breaking URL's...not sure why it's here. XXX // This is breaking URL's...not sure why it's here. XXX
// this should work with the appropriate logic. trying again. // this should work with the appropriate logic. trying again.
if (!(templateContext == null || templateContext.length() == 0)) { 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. // Try to retrieve the bundle.
@ -522,8 +525,7 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
final String fname = bundle.getPath(); final String fname = bundle.getPath();
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Appending the bundle's file name '" + s_log.debug("Appending the bundle's file name '" + fname + "'");
fname + "'");
} }
url.append(fname); url.append(fname);
@ -533,49 +535,50 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
url.append(item.getPath()); 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) { if (CMSConfig.getInstanceOf().getUseLanguageExtension()) {
s_log.debug("The item has no language; omitting the " + // This will append the language to the url, which will in turn render
"language encoding"); // the language negotiation inoperative
} else { final String language = item.getLanguage();
s_log.debug("Encoding the language of the item passed in, '" +
language + "'");
url.append("." + language); 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()) { if (s_log.isDebugEnabled()) {
s_log.debug("Generated live URL " + url.toString()); s_log.debug("Generated live URL " + url.toString());
} }
*/
return url.toString(); return url.toString();
} }
/** /**
* Generate a URL which can be used to preview the * Generate a URL which can be used to preview the <code>item</code>, using
* <code>item</code>, using the given * the given <code>templateContext</code>.<p>
* <code>templateContext</code>.<p> Only a specific language * Only a specific language instance can be previewed, meaning there
* instance can be previewed, meaning there <em>no</em> language * <em>no</em> language negotiation is involved when a request is made to a
* negotiation is involved when a request is made to a URL that * URL that has been generated by this method.
* has been generated by this method.
* *
* @param section The <code>ContentSection</code> which contains * @param section The <code>ContentSection</code> which contains the
* the <code>item</code> * <code>item</code>
* @param item The <code>ContentItem</code> for which a URL should * @param item The <code>ContentItem</code> for which a URL
* be generated. * should be generated.
* @param templateContext the context that determines which * @param templateContext the context that determines which template should
* template should render the item when it is previewed; ignored * render the item when it is previewed; ignored if
* if the argument given here is <code>null</code> * the argument given here is <code>null</code>
* @return a URL which can be used to preview the given *
* <code>item</code> * @return a URL which can be used to preview the given <code>item</code>
*/ */
protected String generatePreviewURL(ContentSection section, protected String generatePreviewURL(ContentSection section,
ContentItem item, ContentItem item,
String templateContext) { String templateContext) {
Assert.exists(section, "ContentSection section"); Assert.exists(section, "ContentSection section");
Assert.exists(item, "ContentItem item"); Assert.exists(item, "ContentItem item");
@ -590,7 +593,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// This is breaking URL's...not sure why it's here. XXX // This is breaking URL's...not sure why it's here. XXX
// this should work with the appropriate logic. trying again. // this should work with the appropriate logic. trying again.
if (!(templateContext == null || templateContext.length() == 0)) { 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. // Try to retrieve the bundle.
@ -616,11 +620,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
final String language = item.getLanguage(); final String language = item.getLanguage();
if (language == null) { if (language == null) {
s_log.debug("The item has no language; omitting the " + s_log.debug("The item has no language; omitting the "
"language encoding"); + "language encoding");
} else { } else {
s_log.debug("Encoding the language of the item passed in, '" + s_log.debug("Encoding the language of the item passed in, '"
language + "'"); + language + "'");
url.append(".").append(language); url.append(".").append(language);
} }
@ -633,14 +637,15 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} }
/** /**
* Retrieves <code>ITEM_ID</code> parameter from URL and * Retrieves <code>ITEM_ID</code> parameter from URL and instantiates item
* instantiates item according to this ID. * according to this ID.
* *
* @param url URL that indicates which item should be retrieved; * @param url URL that indicates which item should be retrieved; must
* must contain the <code>ITEM_ID</code> parameter * contain the <code>ITEM_ID</code> parameter
* @return the <code>ContentItem</code> the given <code>url</code> *
* points to, or <code>null</code> if no ID has been found in the * @return the <code>ContentItem</code> the given <code>url</code> points
* <code>url</code> * to, or <code>null</code> if no ID has been found in the
* <code>url</code>
*/ */
protected ContentItem getItemFromDraftURL(final String url) { protected ContentItem getItemFromDraftURL(final String url) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
@ -653,7 +658,7 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// parameter; below we return null. // parameter; below we return null.
if (Assert.isEnabled()) { if (Assert.isEnabled()) {
Assert.isTrue(pos >= 0, 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=.... ? String item_id = url.substring(pos); // item_id == ITEM_ID=.... ?
@ -672,15 +677,16 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
// ID is the string between the equal (at pos) and the next separator // ID is the string between the equal (at pos) and the next separator
int i = item_id.indexOf(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()) { if (s_log.isDebugEnabled()) {
s_log.debug("Looking up item using item ID " + item_id); s_log.debug("Looking up item using item ID " + item_id);
} }
OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal(item_id)); OID oid = new OID(ContentItem.BASE_DATA_OBJECT_TYPE, new BigDecimal(
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance item_id));
(oid); final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(
oid);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Returning item " + item); s_log.debug("Returning item " + item);
@ -690,26 +696,26 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} }
/** /**
* Returns a content item based on URL relative to the root * Returns a content item based on URL relative to the root folder.
* folder.
* *
* @param url The content item url * @param url The content item url
* @param parentFolder The parent folder object, url must be relevant to it * @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 * @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, protected ContentItem getItemFromLiveURL(String url,
Folder parentFolder) { Folder parentFolder) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Resolving the item for live URL " + url + s_log.debug("Resolving the item for live URL " + url
" and parent folder " + parentFolder); + " and parent folder " + parentFolder);
} }
if (parentFolder == null || url == null || url.equals("")) { if (parentFolder == null || url == null || url.equals("")) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("The url is null or parent folder was null " + s_log.debug("The url is null or parent folder was null "
"or something else is wrong, so just return " + + "or something else is wrong, so just return "
"the parent folder"); + "the parent folder");
} }
return parentFolder; return parentFolder;
@ -719,38 +725,38 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
int index = url.indexOf('/'); int index = url.indexOf('/');
if (index >= 0) { if (index >= 0) {
s_log.debug("The URL starts with a slash; paring off the first " + s_log.debug("The URL starts with a slash; paring off the first "
"URL element and recursing"); + "URL element and recursing");
// If we got first slash (index == 0), ignore it and go // If we got first slash (index == 0), ignore it and go
// on, sample '/foo/bar/item.html.en', in next recursion // on, sample '/foo/bar/item.html.en', in next recursion
// will have deal with 'foo' folder. // will have deal with 'foo' folder.
String name = index > 0 ? url.substring(0, index) : ""; String name = index > 0 ? url.substring(0, index) : "";
parentFolder = "".equals(name) ? parentFolder 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) : ""; url = index + 1 < len ? url.substring(index + 1) : "";
return getItemFromLiveURL(url, parentFolder); return getItemFromLiveURL(url, parentFolder);
} else { } else {
s_log.debug("Found a file element in the URL"); s_log.debug("Found a file element in the URL");
String[] nameAndLang = getNameAndLangFromURLFrag(url); String[] nameAndLang = getNameAndLangFromURLFrag(url);
String name = nameAndLang[0]; String name = nameAndLang[0];
String lang = nameAndLang[1]; String lang = nameAndLang[1];
ContentItem item = parentFolder.getItem(URLEncoder.encode(name), false); ContentItem item = parentFolder.getItem(URLEncoder.encode(name),
return getItemFromLangAndBundle(lang, item); false);
return getItemFromLangAndBundle(lang, item);
} }
} }
/** /**
* Returns an array containing the the item's name and lang based * Returns an array containing the the item's name and lang based on the URL
* on the URL fragment. * fragment.
* *
* @return a two-element string array, the first element * @return a two-element string array, the first element containing the
* containing the bundle name, and the second element containing * bundle name, and the second element containing the lang string
* the lang string
*/ */
protected String[] getNameAndLangFromURLFrag(String url) { protected String[] getNameAndLangFromURLFrag(String url) {
String name = null; String name = null;
@ -766,7 +772,6 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* from a bundle * from a bundle
* 2b if no match is found * 2b if no match is found
*/ */
final ArrayList exts = new ArrayList(5); final ArrayList exts = new ArrayList(5);
final StringTokenizer tok = new StringTokenizer(url, "."); final StringTokenizer tok = new StringTokenizer(url, ".");
@ -776,8 +781,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
if (exts.size() > 0) { if (exts.size() > 0) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Found some file extensions to look at; they " + s_log.debug("Found some file extensions to look at; they "
"are " + exts); + "are " + exts);
} }
/* /*
@ -787,14 +792,12 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* support 2-letter language codes!). * support 2-letter language codes!).
* If so, use this as the language to look for. * If so, use this as the language to look for.
*/ */
/*
/*
* First element is the NAME of the item, not an extension! * First element is the NAME of the item, not an extension!
*/ */
name = (String) exts.get(0); name = (String) exts.get(0);
String ext = null; String ext = null;
Collection supportedLangs = Collection supportedLangs = LanguageUtil.getSupportedLanguages2LA();
LanguageUtil.getSupportedLanguages2LA();
Iterator supportedLangIt = null; Iterator supportedLangIt = null;
for (int i = 1; i < exts.size(); i++) { for (int i = 1; i < exts.size(); i++) {
@ -821,22 +824,22 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
if (ext.equals(supportedLangIt.next())) { if (ext.equals(supportedLangIt.next())) {
lang = ext; lang = ext;
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Found a match; using " + s_log.debug("Found a match; using "
"language " + lang); + "language " + lang);
} }
break; break;
} }
} }
} else { } else {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Discarding extension " + ext + "; " + s_log.debug("Discarding extension " + ext + "; "
"it is too short"); + "it is too short");
} }
} }
} }
} else { } else {
s_log.debug("The file has no extensions; no language was " + s_log.debug("The file has no extensions; no language was "
"encoded"); + "encoded");
name = url; // no extension, so we just have a name here name = url; // no extension, so we just have a name here
lang = null; // no extension, so we cannot guess the language lang = null; // no extension, so we cannot guess the language
} }
@ -856,13 +859,12 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
return returnArray; return returnArray;
} }
/** /**
* Finds a language instance of a content item given the bundle, * Finds a language instance of a content item given the bundle, name, and
* name, and lang string * lang string
* *
* @param lang The lang string from the URL * @param lang The lang string from the URL
* @param item The content bundle * @param item The content bundle
* *
* @return The negotiated lang instance for the current request. * @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); s_log.debug("Found content bundle " + item);
} }
if (lang == null) { if (lang == null) {
s_log.debug("The URL has no language encoded in it; " + s_log.debug("The URL has no language encoded in it; "
"negotiating the language"); + "negotiating the language");
// There is no language, so we get the negotiated locale and call // There is no language, so we get the negotiated locale and call
// this method again with a proper language // this method again with a proper language
return this.getItemFromLangAndBundle(GlobalizationHelper.getNegotiatedLocale().getLanguage(), item); return this.getItemFromLangAndBundle(GlobalizationHelper
.getNegotiatedLocale().getLanguage(), item);
} else { } else {
s_log.debug("The URL is encoded with a langauge; " + s_log.debug("The URL is encoded with a langauge; "
"fetching the appropriate item from " + + "fetching the appropriate item from "
"the bundle"); + "the bundle");
/* /*
* So the request contains a language code as an * So the request contains a language code as an
* extension of the "name" ==>go ahead and try to * extension of the "name" ==>go ahead and try to
@ -889,8 +892,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* given language. * given language.
*/ */
final ContentItem resolved = final ContentItem resolved = ((ContentBundle) item).getInstance(
((ContentBundle) item).getInstance(lang); lang);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Resolved URL to item " + resolved); s_log.debug("Resolved URL to item " + resolved);
@ -899,8 +902,7 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
} }
} else { } else {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("I expected to get a content bundle; I got " + s_log.debug("I expected to get a content bundle; I got " + item);
item);
} }
/* /*
@ -911,7 +913,6 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* *
* NOTE: This should never happen :-) * NOTE: This should never happen :-)
*/ */
return item; // might be null return item; // might be null
} }
} }

View File

@ -123,6 +123,12 @@ public class GlobalizationHelper {
return selectedLocale; 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 * Create a Locale from a browser provides language string
* *

View File

@ -15,7 +15,6 @@
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
package com.arsdigita.navigation; package com.arsdigita.navigation;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
@ -51,28 +50,40 @@ import org.apache.log4j.Logger;
/** /**
* Configuration record for the navigation app * Configuration record for the navigation app
*
* @author Daniel Berrange * @author Daniel Berrange
*/ */
public final class NavigationConfig extends AbstractConfig { public final class NavigationConfig extends AbstractConfig {
private static final Logger s_log = Logger.getLogger(NavigationConfig.class); 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 * The cache lifetime for category index pages in seconds. Default 1 hour
("com.arsdigita.navigation.index_page_cache_lifetime", */
Parameter.REQUIRED, new Integer(3600)); 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 * Generate full item URLs instead of going via search redirector. Default
("com.arsdigita.navigation.generate_item_url", * true
Parameter.REQUIRED, new Boolean(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 * The default category template.
("com.arsdigita.navigation.default_template", */
Parameter.REQUIRED, "/templates/ccm-navigation/navigation/nav-default.jsp"); 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 */ * If no template for category, should it get template from parent, or fall
* back on default? Default: true
*/
private final Parameter m_inheritTemplates; private final Parameter m_inheritTemplates;
// Removed, use content-section config directly instead! // Removed, use content-section config directly instead!
// ContentSection.getConfig().getDefaultContentSection() // ContentSection.getConfig().getDefaultContentSection()
@ -92,8 +103,11 @@ public final class NavigationConfig extends AbstractConfig {
// Quasimodo: End // Quasimodo: End
private final Parameter m_dateOrderCategories; private final Parameter m_dateOrderCategories;
private final Parameter m_topLevelDateOrderCategories; private final Parameter m_topLevelDateOrderCategories;
/** Class that provides categories included in menu for any categories that private final Parameter m_useLanguageExtension;
* do not have an alternative provider registered */ /**
* 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_defaultMenuCatProvider;
private static Set s_fixedDateOrderCats = null; private static Set s_fixedDateOrderCats = null;
@ -101,10 +115,12 @@ public final class NavigationConfig extends AbstractConfig {
private Category m_defaultCategoryRoot = null; private Category m_defaultCategoryRoot = null;
// maybe 2 lookups in a single request so prevent double overhead // maybe 2 lookups in a single request so prevent double overhead
private RequestLocal m_allDateOrderCategories = new RequestLocal () { private RequestLocal m_allDateOrderCategories = new RequestLocal() {
public Object initialValue (PageState state) {
public Object initialValue(PageState state) {
return getCurrentDateOrderCategories(); return getCurrentDateOrderCategories();
} }
}; };
private NavigationModel m_defaultModel = null; private NavigationModel m_defaultModel = null;
@ -112,54 +128,59 @@ public final class NavigationConfig extends AbstractConfig {
private TreeCatProvider m_treeCatProvider = null; private TreeCatProvider m_treeCatProvider = null;
public NavigationConfig() { public NavigationConfig() {
// not desirable default value (IMHO) but retains existing behaviour // not desirable default value (IMHO) but retains existing behaviour
m_inheritTemplates = new BooleanParameter m_inheritTemplates = new BooleanParameter(
("com.arsdigita.navigation.inherit_templates", "com.arsdigita.navigation.inherit_templates",
Parameter.REQUIRED, new Boolean(true)); Parameter.REQUIRED, new Boolean(true));
m_relatedItemsContext = new StringParameter m_relatedItemsContext = new StringParameter(
("com.arsdigita.navigation.related_items_context", "com.arsdigita.navigation.related_items_context",
Parameter.REQUIRED, "subject"); Parameter.REQUIRED, "subject");
m_defaultModelClass = new StringParameter m_defaultModelClass = new StringParameter(
("com.arsdigita.navigation.default_nav_model", "com.arsdigita.navigation.default_nav_model",
Parameter.REQUIRED, ApplicationNavigationModel.class.getName()); Parameter.REQUIRED, ApplicationNavigationModel.class.getName());
m_defaultCatRootPath = new StringParameter m_defaultCatRootPath = new StringParameter(
("com.arsdigita.navigation.default_cat_root_path", "com.arsdigita.navigation.default_cat_root_path",
Parameter.REQUIRED, "/navigation/"); Parameter.REQUIRED, "/navigation/");
m_relatedItemsFactory = new ClassParameter m_relatedItemsFactory = new ClassParameter(
("com.arsdigita.navigation.related_items_factory", "com.arsdigita.navigation.related_items_factory",
Parameter.REQUIRED, RelatedItemsQueryFactoryImpl.class); Parameter.REQUIRED, RelatedItemsQueryFactoryImpl.class);
m_traversalAdapters = new ResourceParameter m_traversalAdapters = new ResourceParameter(
("com.arsdigita.navigation.traversal_adapters", "com.arsdigita.navigation.traversal_adapters",
Parameter.REQUIRED, Parameter.REQUIRED,
"/WEB-INF/resources/navigation-adapters.xml"); "/WEB-INF/resources/navigation-adapters.xml");
m_categoryMenuShowNephews = new BooleanParameter m_categoryMenuShowNephews = new BooleanParameter(
("com.arsdigita.navigation.category_menu_show_nephews", "com.arsdigita.navigation.category_menu_show_nephews",
Parameter.OPTIONAL, new Boolean(false)); Parameter.OPTIONAL, new Boolean(false));
// Quasimodo: Begin // Quasimodo: Begin
m_categoryMenuShowGrandChildren = new StringParameter m_categoryMenuShowGrandChildren = new StringParameter(
("com.arsdigita.navigation.category_menu_show_grand_children", "com.arsdigita.navigation.category_menu_show_grand_children",
Parameter.OPTIONAL, "false"); Parameter.OPTIONAL, "false");
m_categoryMenuShowGrandChildrenMax = new IntegerParameter m_categoryMenuShowGrandChildrenMax = new IntegerParameter(
("com.arsdigita.navigation.category_menu_show_grand_children_max", "com.arsdigita.navigation.category_menu_show_grand_children_max",
Parameter.OPTIONAL, new Integer(0)); Parameter.OPTIONAL, new Integer(0));
m_categoryMenuShowGrandChildrenMin = new IntegerParameter m_categoryMenuShowGrandChildrenMin = new IntegerParameter(
("com.arsdigita.navigation.category_menu_show_grand_children_min", "com.arsdigita.navigation.category_menu_show_grand_children_min",
Parameter.OPTIONAL, new Integer(1)); Parameter.OPTIONAL, new Integer(1));
m_categoryMenuShowGrandChildrenLimit = new IntegerParameter m_categoryMenuShowGrandChildrenLimit = new IntegerParameter(
("com.arsdigita.navigation.category_menu_show_grand_children_limit", "com.arsdigita.navigation.category_menu_show_grand_children_limit",
Parameter.OPTIONAL, new Integer(1)); Parameter.OPTIONAL, new Integer(1));
// Quasimodo: End // Quasimodo: End
m_dateOrderCategories = new StringArrayParameter m_dateOrderCategories = new StringArrayParameter(
("com.arsdigita.navigation.date_order_categories", "com.arsdigita.navigation.date_order_categories",
Parameter.OPTIONAL, new String[0]);
m_topLevelDateOrderCategories = new StringArrayParameter
("com.arsdigita.navigation.top_level_date_order_categories",
Parameter.OPTIONAL, new String[0]); Parameter.OPTIONAL, new String[0]);
m_defaultMenuCatProvider = new ClassParameter m_topLevelDateOrderCategories = new StringArrayParameter(
("com.arsdigita.navigation.default_menu_cat_provider", "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); Parameter.OPTIONAL, null);
m_useLanguageExtension = new BooleanParameter(
"com.arsdigita.navigation.use_language_extension",
Parameter.OPTIONAL,
false);
register(m_indexPageCacheLifetime); register(m_indexPageCacheLifetime);
register(m_generateItemURL); register(m_generateItemURL);
register(m_defaultTemplate); register(m_defaultTemplate);
@ -182,35 +203,38 @@ public final class NavigationConfig extends AbstractConfig {
register(m_dateOrderCategories); register(m_dateOrderCategories);
register(m_topLevelDateOrderCategories); register(m_topLevelDateOrderCategories);
register(m_defaultMenuCatProvider); register(m_defaultMenuCatProvider);
register(m_useLanguageExtension);
loadInfo(); loadInfo();
// Quasimodo: Begin // Quasimodo: Begin
// Checking Paramter // Checking Paramter
String param = new String((String)get(m_categoryMenuShowGrandChildren)); String param = new String((String) get(m_categoryMenuShowGrandChildren));
if( param.equals("false") || param.equals("adaptive") || param.equals("true")) { if (param.equals("false") || param.equals("adaptive") || param.equals(
"true")) {
set(m_categoryMenuShowGrandChildren, param); set(m_categoryMenuShowGrandChildren, param);
} else { } else {
s_log.error("com.arsdigita.navigation.category_menu_show_grand_children: "+ s_log.error(
"Invalid setting " + param + ". Falling back to false."); "com.arsdigita.navigation.category_menu_show_grand_children: "
+ "Invalid setting " + param + ". Falling back to false.");
set(m_categoryMenuShowGrandChildren, "false"); set(m_categoryMenuShowGrandChildren, "false");
} }
// Quasimodo: End // Quasimodo: End
} }
public final long getIndexPageCacheLifetime() { public final long getIndexPageCacheLifetime() {
return ((Integer)get(m_indexPageCacheLifetime)).longValue(); return ((Integer) get(m_indexPageCacheLifetime)).longValue();
} }
public final boolean getGenerateItemURL() { public final boolean getGenerateItemURL() {
return ((Boolean)get(m_generateItemURL)).booleanValue(); return ((Boolean) get(m_generateItemURL)).booleanValue();
} }
public final String getDefaultTemplate() { public final String getDefaultTemplate() {
return (String)get(m_defaultTemplate); return (String) get(m_defaultTemplate);
} }
public final boolean inheritTemplates() { public final boolean inheritTemplates() {
return ((Boolean) get(m_inheritTemplates)).booleanValue(); return ((Boolean) get(m_inheritTemplates)).booleanValue();
} }
// Removed, use content-section config directly instead! // Removed, use content-section config directly instead!
@ -218,17 +242,16 @@ public final class NavigationConfig extends AbstractConfig {
// public final String getDefaultContentSectionURL() { // public final String getDefaultContentSectionURL() {
// return (String)get(m_defaultContentSectionURL); // return (String)get(m_defaultContentSectionURL);
// } // }
public final String getRelatedItemsContext() { public final String getRelatedItemsContext() {
return (String)get(m_relatedItemsContext); return (String) get(m_relatedItemsContext);
} }
public final String getDefaultCategoryRootPath() { public final String getDefaultCategoryRootPath() {
return (String)get(m_defaultCatRootPath); return (String) get(m_defaultCatRootPath);
} }
public final Class getRelatedItemsFactory() { public final Class getRelatedItemsFactory() {
return (Class)get(m_relatedItemsFactory); return (Class) get(m_relatedItemsFactory);
} }
public final synchronized NavigationModel getDefaultModel() { public final synchronized NavigationModel getDefaultModel() {
@ -236,15 +259,13 @@ public final class NavigationConfig extends AbstractConfig {
return m_defaultModel; return m_defaultModel;
} }
String defaultModelClassName = (String)get(m_defaultModelClass); String defaultModelClassName = (String) get(m_defaultModelClass);
try { try {
Class defaultModelClass = Class.forName(defaultModelClassName); Class defaultModelClass = Class.forName(defaultModelClassName);
Constructor cons = defaultModelClass.getConstructor Constructor cons = defaultModelClass.getConstructor(new Class[]{});
( new Class[]{} ); m_defaultModel = (NavigationModel) cons.newInstance(new Object[]{});
m_defaultModel = (NavigationModel) cons.newInstance
( new Object[]{} );
} catch (Exception ex) { } catch (Exception ex) {
throw new UncheckedWrapperException( ex ); throw new UncheckedWrapperException(ex);
} }
return m_defaultModel; return m_defaultModel;
@ -255,10 +276,10 @@ public final class NavigationConfig extends AbstractConfig {
return m_defaultCategoryRoot; return m_defaultCategoryRoot;
} }
String defaultCatRootPath = (String)get(m_defaultCatRootPath); String defaultCatRootPath = (String) get(m_defaultCatRootPath);
Application app = Application app = Application.retrieveApplicationForPath(
Application.retrieveApplicationForPath(defaultCatRootPath); defaultCatRootPath);
Assert.exists(app, Application.class); Assert.exists(app, Application.class);
Category m_defaultCategoryRoot = Category.getRootForObject(app); Category m_defaultCategoryRoot = Category.getRootForObject(app);
@ -268,33 +289,37 @@ public final class NavigationConfig extends AbstractConfig {
} }
InputStream getTraversalAdapters() { InputStream getTraversalAdapters() {
return (InputStream)get(m_traversalAdapters); return (InputStream) get(m_traversalAdapters);
} }
public final boolean getCategoryMenuShowNephews() { public final boolean getCategoryMenuShowNephews() {
return ((Boolean)get(m_categoryMenuShowNephews)).booleanValue(); return ((Boolean) get(m_categoryMenuShowNephews)).booleanValue();
} }
// Quasimodo: Begin // Quasimodo: Begin
public final String getCategoryMenuShowGrandChildren() { public final String getCategoryMenuShowGrandChildren() {
return (String)get(m_categoryMenuShowGrandChildren); return (String) get(m_categoryMenuShowGrandChildren);
} }
public final long getCategoryMenuShowGrandChildrenMax() {
return ((Integer)get(m_categoryMenuShowGrandChildrenMax)).longValue(); public final long getCategoryMenuShowGrandChildrenMax() {
} return ((Integer) get(m_categoryMenuShowGrandChildrenMax)).longValue();
public final long getCategoryMenuShowGrandChildrenMin() { }
return ((Integer)get(m_categoryMenuShowGrandChildrenMin)).longValue();
} public final long getCategoryMenuShowGrandChildrenMin() {
public final long getCategoryMenuShowGrandChildrenLimit() { return ((Integer) get(m_categoryMenuShowGrandChildrenMin)).longValue();
return ((Integer)get(m_categoryMenuShowGrandChildrenLimit)).longValue(); }
}
public final long getCategoryMenuShowGrandChildrenLimit() {
return ((Integer) get(m_categoryMenuShowGrandChildrenLimit)).longValue();
}
// Quasimodo: End // Quasimodo: End
/** /**
* retrieve a collection of categories to order by date. Collection includes * retrieve a collection of categories to order by date. Collection includes
* categories directly specified as date ordered and also all subcategories of * categories directly specified as date ordered and also all subcategories
* categories specified as being top level date ordered categories * of categories specified as being top level date ordered categories in
* in config * config
*
* @return * @return
*/ */
public Collection getDateOrderedCategories(PageState state) { public Collection getDateOrderedCategories(PageState state) {
@ -302,56 +327,57 @@ public final class NavigationConfig extends AbstractConfig {
if (state == null) { if (state == null) {
categories = getCurrentDateOrderCategories(); categories = getCurrentDateOrderCategories();
} else { } else {
categories = (Collection)m_allDateOrderCategories.get(state); categories = (Collection) m_allDateOrderCategories.get(state);
} }
return categories; return categories;
} }
public boolean isDateOrderedCategory (Category cat, PageState state) { public boolean isDateOrderedCategory(Category cat, PageState state) {
return getDateOrderedCategories(state).contains(cat.getID().toString()); return getDateOrderedCategories(state).contains(cat.getID().toString());
} }
private Collection getCurrentDateOrderCategories () { private Collection getCurrentDateOrderCategories() {
if (s_fixedDateOrderCats == null) { if (s_fixedDateOrderCats == null) {
populateFixedDateOrderCats(); populateFixedDateOrderCats();
} }
String[] topLevelCats = (String[]) get(m_topLevelDateOrderCategories); String[] topLevelCats = (String[]) get(m_topLevelDateOrderCategories);
Set allCats = new HashSet(); Set allCats = new HashSet();
allCats.addAll(s_fixedDateOrderCats); allCats.addAll(s_fixedDateOrderCats);
for (int i = 0; i < topLevelCats.length; i++) { for (int i = 0; i < topLevelCats.length; i++) {
try { try {
String[] categoryArray = StringUtils.split(topLevelCats[i], ':'); String[] categoryArray = StringUtils.split(topLevelCats[i], ':');
String order = ""; String order = "";
if (categoryArray.length > 1) { if (categoryArray.length > 1) {
order = ":" + categoryArray[1]; order = ":" + categoryArray[1];
} }
Category topLevelCat = new Category(new BigDecimal(categoryArray[0])); Category topLevelCat = new Category(new BigDecimal(
s_log.debug("retrieved top level category " + topLevelCat); categoryArray[0]));
Set childCats = new HashSet(); s_log.debug("retrieved top level category " + topLevelCat);
CategoryCollection children = topLevelCat.getDescendants(); Set childCats = new HashSet();
while (children.next()) { CategoryCollection children = topLevelCat.getDescendants();
BigDecimal id = children.getCategory().getID(); while (children.next()) {
childCats.add(id.toString() + order); BigDecimal id = children.getCategory().getID();
} childCats.add(id.toString() + order);
}
allCats.addAll(childCats); allCats.addAll(childCats);
} catch (DataObjectNotFoundException e) { } catch (DataObjectNotFoundException e) {
// non existent category id specified in configuration. Output warning to // non existent category id specified in configuration. Output warning to
// logs and continue // logs and continue
s_log.warn("Category with id " + topLevelCats[i] + s_log.warn("Category with id " + topLevelCats[i]
" specified in configuration as a top level date order category, " + + " specified in configuration as a top level date order category, "
"but the category does not exist"); + "but the category does not exist");
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// non number specified in configuration. Output warning to // non number specified in configuration. Output warning to
// logs and continue // logs and continue
s_log.warn("Category with id " + topLevelCats[i] + s_log.warn("Category with id " + topLevelCats[i]
" specified in configuration as a top level date order category, "+ + " specified in configuration as a top level date order category, "
"but this is not a valid number"); + "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 // concurrently first time after server restart
private synchronized void populateFixedDateOrderCats() { private synchronized void populateFixedDateOrderCats() {
String[] catArray = (String[]) get(m_dateOrderCategories); String[] catArray = (String[]) get(m_dateOrderCategories);
s_fixedDateOrderCats = new HashSet(); s_fixedDateOrderCats = new HashSet();
for (int i = 0; i < catArray.length; i++) { for (int i = 0; i < catArray.length; i++) {
try { try {
// don't need to do this, as including invalid or non existent // 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 // 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 // provide some warning to the users when they find that a category
// they expected to be date ordered isn't because they mistyped etc. // they expected to be date ordered isn't because they mistyped etc.
// This only occurs once, first time navigation page is accessed after // This only occurs once, first time navigation page is accessed after
// server restart // server restart
String[] category = StringUtils.split(catArray[i], ':'); String[] category = StringUtils.split(catArray[i], ':');
Category cat = new Category(new BigDecimal(category[0])); Category cat = new Category(new BigDecimal(category[0]));
} catch (DataObjectNotFoundException e) { } catch (DataObjectNotFoundException e) {
// non existent category id specified in configuration. Output warning to // non existent category id specified in configuration. Output warning to
// logs and continue // logs and continue
s_log.warn("Category with id " + catArray[i] + s_log.warn("Category with id " + catArray[i]
" specified in configuration as a date ordered category, but the category" + + " specified in configuration as a date ordered category, but the category"
"does not exist"); + "does not exist");
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// non number specified in configuration. Output warning to // non number specified in configuration. Output warning to
// logs and continue // logs and continue
s_log.warn("Category with id " + catArray[i] + s_log.warn("Category with id " + catArray[i]
" specified in configuration as a date ordered category, "+ + " specified in configuration as a date ordered category, "
"but this is not a valid number"); + "but this is not a valid number");
} }
s_fixedDateOrderCats.add(catArray[i]); s_fixedDateOrderCats.add(catArray[i]);
} }
} }
public final TreeCatProvider getDefaultMenuCatProvider() { public final TreeCatProvider getDefaultMenuCatProvider() {
if (null == m_treeCatProvider) { if (null == m_treeCatProvider) {
try { try {
Class providerClass = (Class) get(m_defaultMenuCatProvider); Class providerClass = (Class) get(m_defaultMenuCatProvider);
if (providerClass == null) { if (providerClass == null) {
m_treeCatProvider = new Menu(); m_treeCatProvider = new Menu();
} else { } else {
m_treeCatProvider = (TreeCatProvider) providerClass.newInstance(); m_treeCatProvider = (TreeCatProvider) providerClass
} .newInstance();
} catch (Exception ex) { }
throw new UncheckedWrapperException(ex); } catch (Exception ex) {
} throw new UncheckedWrapperException(ex);
} }
return m_treeCatProvider; }
return m_treeCatProvider;
}
public Boolean getUseLanguageExtension() {
return (Boolean) get(m_useLanguageExtension);
} }
} }

View File

@ -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.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.example=com.arsdigita.navigation.ui.category.TreeCatProviderImpl
com.arsdigita.navigation.default_menu_cat_provider.format=[class] 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]

View File

@ -43,16 +43,18 @@ import com.arsdigita.web.Application;
import com.arsdigita.web.DefaultApplicationFileResolver; import com.arsdigita.web.DefaultApplicationFileResolver;
import com.arsdigita.web.Web; import com.arsdigita.web.Web;
import com.arsdigita.globalization.GlobalizationHelper;
/** /**
* Manages the processing of URLs in the Navigation application. * Manages the processing of URLs in the Navigation application.
* *
*/ */
public class NavigationFileResolver extends DefaultApplicationFileResolver { public class NavigationFileResolver extends DefaultApplicationFileResolver {
private static final Logger s_log = private static final Logger s_log = Logger.getLogger(
Logger.getLogger(NavigationFileResolver.class); NavigationFileResolver.class);
private static final String CATEGORY_PATH_ATTR = private static final String CATEGORY_PATH_ATTR
NavigationFileResolver.class + ".categoryPath"; = NavigationFileResolver.class + ".categoryPath";
// path is set in a cookie, which navigation models may use if they wish // 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 String PATH_COOKIE_NAME = "ad_path";
public static final char PATH_COOKIE_SEPARATOR = '|'; public static final char PATH_COOKIE_SEPARATOR = '|';
@ -71,6 +73,14 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
s_log.debug("Resolving " + path); 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")) { if (path.equals("/category.jsp")) {
Navigation nav = (Navigation) Web.getWebContext().getApplication(); 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 // check that the category is in the tree of categories
Category root = null; Category root = null;
DataCollection objs = SessionManager.getSession() DataCollection objs = SessionManager.getSession()
.retrieve(Domain.BASE_DATA_OBJECT_TYPE); .retrieve(Domain.BASE_DATA_OBJECT_TYPE);
objs.addEqualsFilter("model.ownerUseContext.categoryOwner.id", nav.getID()); objs.addEqualsFilter("model.ownerUseContext.categoryOwner.id", nav
.getID());
String dispatcherContext = null; String dispatcherContext = null;
TemplateContext tc = Navigation.getContext().getTemplateContext(); TemplateContext tc = Navigation.getContext().getTemplateContext();
if (tc != null) { if (tc != null) {
dispatcherContext = tc.getContext(); dispatcherContext = tc.getContext();
} }
objs.addEqualsFilter("model.ownerUseContext.useContext", dispatcherContext); objs.addEqualsFilter("model.ownerUseContext.useContext",
dispatcherContext);
DomainCollection domains = new DomainCollection(objs); DomainCollection domains = new DomainCollection(objs);
if (domains.next()) { if (domains.next()) {
root = ((Domain) domains.getDomainObject()).getModel(); root = ((Domain) domains.getDomainObject()).getModel();
@ -141,7 +153,8 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
cats.add(parents.getCategory()); 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, sreq.setAttribute(CATEGORY_PATH_ATTR,
catsArray); catsArray);
@ -206,9 +219,8 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
// be used if a navigation model retains the cookie. If we link to // be used if a navigation model retains the cookie. If we link to
// another application, it's navigation model may use this when // another application, it's navigation model may use this when
// deciding whether to trust the given path. // deciding whether to trust the given path.
path.append(PATH_COOKIE_SEPARATOR + Kernel.getContext().getResource()
path.append(PATH_COOKIE_SEPARATOR + .getID().toString());
Kernel.getContext().getResource().getID().toString());
for (int i = 0; i < catsArray.length; i++) { for (int i = 0; i < catsArray.length; i++) {
Category cat = catsArray[i]; Category cat = catsArray[i];
path.append(PATH_COOKIE_SEPARATOR + cat.getID().toString()); path.append(PATH_COOKIE_SEPARATOR + cat.getID().toString());
@ -223,7 +235,6 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
} }
resp.addCookie(cookie); resp.addCookie(cookie);
} }
public static Category[] getCategoryPath(HttpServletRequest req) { public static Category[] getCategoryPath(HttpServletRequest req) {
@ -243,6 +254,7 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
* *
* @param cat * @param cat
* @param useContext * @param useContext
*
* @return * @return
*/ */
private RequestDispatcher resolveTemplate(Category cat, String useContext) { private RequestDispatcher resolveTemplate(Category cat, String useContext) {
@ -258,9 +270,9 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
} }
// If there's an explicit use context which doesn't exist, give a 404 // If there's an explicit use context which doesn't exist, give a 404
if (!Template.DEFAULT_USE_CONTEXT.equals(useContext) && null == template) { if (!Template.DEFAULT_USE_CONTEXT.equals(useContext) && null == template) {
s_log.debug("No template found in context " + getTemplateContext() + s_log.debug("No template found in context " + getTemplateContext()
" for category " + cat.getID() + " for category " + cat.getID()
+ " with use context " + useContext); + " with use context " + useContext);
return null; return null;
} }
@ -322,6 +334,7 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
* *
* @param root * @param root
* @param path * @param path
*
* @return * @return
*/ */
protected Category[] resolvePath(Category root, String path) { 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 * Match a URL with the category tree and return the requested category if
* if exists. * exists.
* *
* Quasimodo: Originally addEqualsFilter has been used to filter the * Quasimodo: Originally addEqualsFilter has been used to filter the
* appropriate category directly inside the SQL query. This isn't possible * appropriate category directly inside the SQL query. This isn't possible
* anymore due to the localised URLs of the new localised categories * anymore due to the localised URLs of the new localised categories (or at
* (or at least: not found it). Therefore we do the filtering in Java now. * least: not found it). Therefore we do the filtering in Java now.
* *
*/ */
public static Category[] resolveCategory(Category root, public static Category[] resolveCategory(Category root,
@ -382,4 +395,5 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
return (Category[]) cats.toArray(new Category[cats.size()]); return (Category[]) cats.toArray(new Category[cats.size()]);
} }
} }