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
= new SpecificClassParameter(
"com.arsdigita.cms.category_authoring_add_form", "com.arsdigita.cms.category_authoring_add_form",
Parameter.REQUIRED, Parameter.REQUIRED,
ItemCategoryForm.class, ItemCategoryForm.class,
SimpleComponent.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,18 +148,19 @@ 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
= new DHTMLEditorConfigParameter(
"com.arsdigita.cms.dhtml_editor_config", "com.arsdigita.cms.dhtml_editor_config",
Parameter.REQUIRED, Parameter.REQUIRED,
new DHTMLEditor.Config("Xinha.Config", new DHTMLEditor.Config("Xinha.Config",
@ -170,17 +176,19 @@ 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
= new StringArrayParameter(
"com.arsdigita.cms.dhtml_editor_hidden_buttons", "com.arsdigita.cms.dhtml_editor_hidden_buttons",
Parameter.OPTIONAL, Parameter.OPTIONAL,
null); null);
@ -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
= new StringParameter(
"com.arsdigita.cms.publish_lifecycle_listener_class", "com.arsdigita.cms.publish_lifecycle_listener_class",
Parameter.OPTIONAL, Parameter.OPTIONAL,
PublishLifecycleListener.class.getName()); 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
= new BooleanParameter(
"com.arsdigita.cms.delete_workflow_after_publication", "com.arsdigita.cms.delete_workflow_after_publication",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); 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
= new SpecificClassParameter(
"com.arsdigita.cms.category_authoring_extension", "com.arsdigita.cms.category_authoring_extension",
Parameter.REQUIRED, Parameter.REQUIRED,
ItemCategoryExtension.class, 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
= new BooleanParameter(
"com.arsdigita.cms.delete_lifecycle_when_complete", "com.arsdigita.cms.delete_lifecycle_when_complete",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); 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
= new BooleanParameter(
"com.arsdigita.cms.allow_category_create_use_context", "com.arsdigita.cms.allow_category_create_use_context",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); 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
= new BooleanParameter(
"com.arsdigita.cms.allow_content_create_in_section_listing", "com.arsdigita.cms.allow_content_create_in_section_listing",
Parameter.REQUIRED, Parameter.REQUIRED,
Boolean.FALSE); 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
= new BooleanParameter(
"com.arsdigita.cms.delete_workflow_notification_when_sent", "com.arsdigita.cms.delete_workflow_notification_when_sent",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); 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
= new BooleanParameter(
"com.arsdigita.cms.delete_expiry_notification_when_sent", "com.arsdigita.cms.delete_expiry_notification_when_sent",
Parameter.OPTIONAL, Parameter.OPTIONAL,
Boolean.FALSE); 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,12 +517,14 @@ 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
= new SpecificClassParameter(
"com.arsdigita.cms.default_item_resolver_class", "com.arsdigita.cms.default_item_resolver_class",
Parameter.REQUIRED, Parameter.REQUIRED,
MultilingualItemResolver.class, MultilingualItemResolver.class,
ItemResolver.class); ItemResolver.class);
private final Parameter m_defaultTemplateResolverClass = new SpecificClassParameter( private final Parameter m_defaultTemplateResolverClass
= new SpecificClassParameter(
"com.arsdigita.cms.default_template_resolver_class", "com.arsdigita.cms.default_template_resolver_class",
Parameter.REQUIRED, Parameter.REQUIRED,
DefaultTemplateResolver.class, DefaultTemplateResolver.class,
@ -505,7 +539,8 @@ 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
= new IntegerParameter(
"com.arsdigita.cms.item_search.flat_browse_pane.page_size", "com.arsdigita.cms.item_search.flat_browse_pane.page_size",
Parameter.REQUIRED, Parameter.REQUIRED,
20); 20);
@ -528,7 +563,8 @@ 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
= new BooleanParameter(
"com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane", "com.arsdigita.cms.lifecycle.use_old_style_item_lifecycle_item_pane",
Parameter.REQUIRED, Parameter.REQUIRED,
false); false);
@ -552,11 +588,13 @@ public final class CMSConfig extends AbstractConfig {
///////////////////////////////////////////////// /////////////////////////////////////////////////
// ImageBrowser Parameter // ImageBrowser Parameter
///////////////////////////////////////////////// /////////////////////////////////////////////////
private final Parameter m_imageBrowserThumbnailMaxWidth = new IntegerParameter( private final Parameter m_imageBrowserThumbnailMaxWidth
= new IntegerParameter(
"com.arsdigita.cms.image_browser.thumbnail_max_width", "com.arsdigita.cms.image_browser.thumbnail_max_width",
Parameter.REQUIRED, Parameter.REQUIRED,
50); 50);
private final Parameter m_imageBrowserThumbnailMaxHeight = new IntegerParameter( private final Parameter m_imageBrowserThumbnailMaxHeight
= new IntegerParameter(
"com.arsdigita.cms.image_browser.thumbnail_max_height", "com.arsdigita.cms.image_browser.thumbnail_max_height",
Parameter.REQUIRED, Parameter.REQUIRED,
50); 50);
@ -564,7 +602,8 @@ public final class CMSConfig extends AbstractConfig {
"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
= new IntegerParameter(
"com.arsdigita.cms.image_browser.description_size", "com.arsdigita.cms.image_browser.description_size",
Parameter.REQUIRED, Parameter.REQUIRED,
400); 400);
@ -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 = "&";
@ -80,11 +81,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* *
* @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
@ -92,8 +93,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
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,10 +160,8 @@ 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);
} }
@ -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;
@ -260,9 +256,10 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* @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
@ -281,11 +278,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* @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
@ -296,9 +293,10 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
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");
@ -318,14 +316,14 @@ 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 + "'"); + "'");
} }
} }
@ -335,9 +333,10 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* @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
@ -354,11 +353,11 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* @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
@ -368,9 +367,8 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
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,12 +402,13 @@ 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
@ -438,23 +437,25 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
* *
* @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
* item
* @param item <code>ContentItem</code> for which a URL should be * @param item <code>ContentItem</code> for which a URL should be
* constructed. * constructed.
* @param templateContext template context; will be ignored if <code>null</code> * @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,45 +535,46 @@ 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 if (CMSConfig.getInstanceOf().getUseLanguageExtension()) {
* the language negotiation inoperative // This will append the language to the url, which will in turn render
// the language negotiation inoperative
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("." + 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>
* @param item The <code>ContentItem</code> for which a URL should
* be generated.
* @param templateContext the context that determines which
* template should render the item when it is previewed; ignored
* if the argument given here is <code>null</code>
* @return a URL which can be used to preview the given
* <code>item</code> * <code>item</code>
* @param item The <code>ContentItem</code> for which a URL
* should be generated.
* @param templateContext the context that determines which template should
* render the item when it is previewed; ignored if
* the argument given here is <code>null</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,
@ -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,13 +637,14 @@ 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
* to, or <code>null</code> if no ID has been found in the
* <code>url</code> * <code>url</code>
*/ */
protected ContentItem getItemFromDraftURL(final String url) { protected ContentItem getItemFromDraftURL(final String url) {
@ -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,16 +725,16 @@ 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);
@ -739,18 +745,18 @@ public class MultilingualItemResolver extends AbstractItemResolver implements It
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),
false);
return getItemFromLangAndBundle(lang, item); 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,10 +859,9 @@ 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
@ -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", */
private final Parameter m_indexPageCacheLifetime = new IntegerParameter(
"com.arsdigita.navigation.index_page_cache_lifetime",
Parameter.REQUIRED, new Integer(3600)); 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
*/
private final Parameter m_generateItemURL = new BooleanParameter(
"com.arsdigita.navigation.generate_item_url",
Parameter.REQUIRED, new Boolean(true)); 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;
@ -113,53 +129,58 @@ public final class NavigationConfig extends AbstractConfig {
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]); Parameter.OPTIONAL, new String[0]);
m_topLevelDateOrderCategories = new StringArrayParameter m_topLevelDateOrderCategories = new StringArrayParameter(
("com.arsdigita.navigation.top_level_date_order_categories", "com.arsdigita.navigation.top_level_date_order_categories",
Parameter.OPTIONAL, new String[0]); Parameter.OPTIONAL, new String[0]);
m_defaultMenuCatProvider = new ClassParameter m_defaultMenuCatProvider = new ClassParameter(
("com.arsdigita.navigation.default_menu_cat_provider", "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,31 +203,34 @@ 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() {
@ -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() { public final long getCategoryMenuShowGrandChildrenMax() {
return ((Integer)get(m_categoryMenuShowGrandChildrenMax)).longValue(); return ((Integer) get(m_categoryMenuShowGrandChildrenMax)).longValue();
} }
public final long getCategoryMenuShowGrandChildrenMin() { public final long getCategoryMenuShowGrandChildrenMin() {
return ((Integer)get(m_categoryMenuShowGrandChildrenMin)).longValue(); return ((Integer) get(m_categoryMenuShowGrandChildrenMin)).longValue();
} }
public final long getCategoryMenuShowGrandChildrenLimit() { public final long getCategoryMenuShowGrandChildrenLimit() {
return ((Integer)get(m_categoryMenuShowGrandChildrenLimit)).longValue(); 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,16 +327,16 @@ 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();
} }
@ -326,7 +351,8 @@ public final class NavigationConfig extends AbstractConfig {
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(
categoryArray[0]));
s_log.debug("retrieved top level category " + topLevelCat); s_log.debug("retrieved top level category " + topLevelCat);
Set childCats = new HashSet(); Set childCats = new HashSet();
CategoryCollection children = topLevelCat.getDescendants(); CategoryCollection children = topLevelCat.getDescendants();
@ -339,15 +365,15 @@ public final class NavigationConfig extends AbstractConfig {
} 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");
} }
} }
@ -375,15 +401,15 @@ public final class NavigationConfig extends AbstractConfig {
} 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]);
} }
@ -396,7 +422,8 @@ public final class NavigationConfig extends AbstractConfig {
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) { } catch (Exception ex) {
throw new UncheckedWrapperException(ex); throw new UncheckedWrapperException(ex);
@ -405,4 +432,8 @@ public final class NavigationConfig extends AbstractConfig {
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();
@ -106,13 +116,15 @@ public class NavigationFileResolver extends DefaultApplicationFileResolver {
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,8 +270,8 @@ 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()]);
} }
} }