From f63434ae802162bd1a83b37e31ddcb1dd103867e Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 25 Nov 2016 16:32:50 +0000 Subject: [PATCH] CCM NG: Migrated several classes from ccm-core to CCM NG. No finished, none finished classes as *.todo to avoid compilation failures. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4457 8810af33-2d31-482b-a856-94f89814c4df --- .../com/arsdigita/cms/ItemSelectionModel.java | 9 +- .../java/com/arsdigita/cms/ui/CMSForm.java | 3 +- .../cms/ui/ContentItemContextBar.java.todo | 98 +++ .../cms/ui/ContentItemPage.java.todo | 466 ++++++---- .../cms/ui/SecurityPropertyEditor.java | 394 +++++++++ ...or.java.off => CreationSelector.java.todo} | 0 .../cms/ui/item/ItemLanguages.java.todo | 100 +-- .../cms/ui/item/ItemLanguagesTable.java.todo | 2 +- .../arsdigita/cms/ui/item/Summary.java.todo | 455 ++++++++++ .../ItemLifecycleAdminPane.java.todo | 210 +++++ .../revision/ItemRevisionAdminPane.java.todo | 316 +++++++ .../cms/ui/templates/ItemTemplates.java.todo | 258 ++++++ .../cms/ui/workflow/BaseWorkflowItemPane.java | 79 +- .../workflow/ItemWorkflowAdminPane.java.todo | 117 +++ .../workflow/ItemWorkflowItemPane.java.todo | 170 ++++ .../ui/workflow/ItemWorkflowSelectForm.java | 110 +++ .../workflow/ItemWorkflowSelectionModel.java | 57 ++ .../cms/ui/workflow/TaskFinishForm.java.todo | 4 +- .../cms/ui/workflow/WorkflowDeleteForm.java | 54 ++ .../WorkflowsOptionPrintListener.java | 71 ++ .../ContentItemL10NManager.java | 4 +- .../ContentItemL10NManagerTest.java | 16 +- .../com/arsdigita/bebop/PropertyEditor.java | 815 ++++++++++++++++++ .../arsdigita/bebop/PropertyEditorModel.java | 58 ++ .../bebop/PropertyEditorModelBuilder.java | 42 + .../kernel/ui/ACSObjectSelectionModel.java | 411 ++++----- .../arsdigita/toolbox/ui/ComponentAccess.java | 107 +++ 27 files changed, 3917 insertions(+), 509 deletions(-) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemContextBar.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/SecurityPropertyEditor.java rename ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/{CreationSelector.java.off => CreationSelector.java.todo} (100%) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/item/Summary.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/revision/ItemRevisionAdminPane.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/templates/ItemTemplates.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowAdminPane.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowItemPane.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectionModel.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowDeleteForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowsOptionPrintListener.java create mode 100755 ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditor.java create mode 100755 ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModel.java create mode 100755 ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModelBuilder.java create mode 100755 ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentAccess.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java index 6fd29c125..35b9a2f27 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ItemSelectionModel.java @@ -23,7 +23,6 @@ import com.arsdigita.bebop.SingleSelectionModel; import com.arsdigita.bebop.parameters.LongParameter; import com.arsdigita.ui.CcmObjectSelectionModel; -import org.apache.log4j.Logger; import org.libreccm.cdi.utils.CdiUtil; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentType; @@ -53,6 +52,14 @@ public class ItemSelectionModel extends CcmObjectSelectionModel { private Long typeId; + public ItemSelectionModel(final LongParameter parameter) { + super(parameter); + } + + public ItemSelectionModel(final String parameterName) { + super(parameterName); + } + /** * Construct a new ItemSelectionModel * diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSForm.java index 3a13c25b2..782190e37 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSForm.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CMSForm.java @@ -55,7 +55,8 @@ public class CMSForm extends Form implements Cancellable { * @param state The page state * @return true if the form is cancelled, false otherwise */ - public boolean isCancelled(PageState state) { + @Override + public boolean isCancelled(final PageState state) { return false; } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemContextBar.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemContextBar.java.todo new file mode 100755 index 000000000..690b6cf8a --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemContextBar.java.todo @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ContentSection; +import com.arsdigita.cms.ContentType; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.Template; +import com.arsdigita.cms.PageLocations; +import com.arsdigita.web.ParameterMap; +import com.arsdigita.web.URL; +import org.apache.log4j.Logger; + +import java.util.List; + +/** + *

The context bar of the content section UI.

+ * + * @author Justin Ross <jross@redhat.com> + * @version $Id: ContentItemContextBar.java 287 2005-02-22 00:29:02Z sskracic $ + */ +class ContentItemContextBar extends ContentSectionContextBar { + + private static final Logger s_log = Logger.getLogger + (ContentItemContextBar.class); + + private final ItemSelectionModel m_item; + + ContentItemContextBar(final ItemSelectionModel item) { + super(); + + m_item = item; + } + + @Override + protected final List entries(final PageState state) { + final List entries = super.entries(state); + final ContentItem item = (ContentItem) m_item.getSelectedObject(state); + final ContentSection section = CMS.getContext().getContentSection(); + boolean isTemplate = + item.getContentType().equals(ContentType.findByAssociatedObjectType(Template.BASE_DATA_OBJECT_TYPE)); + + final URL url = URL.there + (state.getRequest(), + section.getPath() + "/" + PageLocations.ITEM_PAGE, + params(item)); + + StringBuffer title = new StringBuffer(); + if (isTemplate) { + title.append(localize("cms.ui.template")); + } else { + title.append(localize("cms.ui.content_item")); + } + title.append(": ") + .append(item.getDisplayName()); + String language = item.getLanguage(); + if (language != null) { + title.append(" (") + .append(language) + .append(")"); + } + + entries.add(new Entry(title.toString(), url)); + + return entries; + } + + private static ParameterMap params(final ContentItem item) { + final ParameterMap params = new ParameterMap(); + + params.setParameter(ContentItemPage.ITEM_ID, item.getID()); + + return params; + } + + private static String localize(final String key) { + return (String) ContentSectionPage.globalize(key).localize(); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.todo index 686887a3e..4fa2d0d52 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentItemPage.java.todo @@ -24,8 +24,10 @@ import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Link; import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.ParameterSingleSelectionModel; import com.arsdigita.bebop.Resettable; import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.SingleSelectionModel; import com.arsdigita.bebop.TabbedPane; import com.arsdigita.bebop.event.ActionEvent; import com.arsdigita.bebop.event.ActionListener; @@ -33,9 +35,12 @@ import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.event.FormValidationListener; import com.arsdigita.bebop.event.PrintEvent; import com.arsdigita.bebop.event.PrintListener; -import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.LongParameter; import com.arsdigita.bebop.parameters.NotNullValidationListener; import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.PageLocations; import com.arsdigita.cms.ui.item.CustomizedPreviewLink; import com.arsdigita.cms.dispatcher.CMSDispatcher; import com.arsdigita.cms.dispatcher.CMSPage; @@ -49,31 +54,43 @@ import com.arsdigita.cms.ui.revision.ItemRevisionAdminPane; import com.arsdigita.cms.ui.templates.ItemTemplates; import com.arsdigita.cms.ui.workflow.ItemWorkflowAdminPane; import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.ui.ACSObjectSelectionModel; import com.arsdigita.util.Assert; +import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.xml.Document; import com.arsdigita.xml.Element; import java.io.IOException; -import java.math.BigDecimal; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.arsdigita.cms.CMSConfig; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentItemVersion; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentType; /** * Page for administering a content item. * * @author Michael Pih - * @author Stanislav Freidin <sfreidin@redhat.com> + * @author Stanislav Freidin * @author Jack Chung - * @author Sören Bernstein + * @author Sören Bernstein + * @author Jens Pelzetter * - * @version $Id: ContentItemPage.java 2245 2011-11-15 08:03:57Z pboy $ */ public class ContentItemPage extends CMSPage implements ActionListener { /** * Private Logger instance for debugging purpose. */ - private static final Logger s_log = Logger.getLogger(ContentItemPage.class); + private static final Logger LOGGER = LogManager.getLogger( + ContentItemPage.class); /** * The URL parameter that must be passed in in order to set the current tab. * This is a KLUDGE right now because the TabbedDialog's current tab is @@ -81,11 +98,15 @@ public class ContentItemPage extends CMSPage implements ActionListener { */ public static final String SET_TAB = "set_tab"; /** - * The name of the global state parameter that holds the item id + * The name of the global state parameter that holds the item id. */ public static final String ITEM_ID = "item_id"; /** - * The name of the global state parameter which holds the return URL + * The name of th global state parameter that holds the selected language. + */ + public static final String SELECTED_LANGUAGE = "selected_language"; + /** + * The name of the global state parameter which holds the return URL. */ public static final String RETURN_URL = "return_url"; /** @@ -96,43 +117,47 @@ public class ContentItemPage extends CMSPage implements ActionListener { public static final String STREAMLINED_CREATION = "streamlined_creation"; public static final String STREAMLINED_CREATION_ACTIVE = "active"; public static final String STREAMLINED_CREATION_INACTIVE = "active"; - private static int s_tabOrder = 0; /** * Index of the summary tab */ - public static final int SUMMARY_TAB = s_tabOrder++; + public static final int SUMMARY_TAB = 0; /** - *

The name of the state parameter which indicates the content type of - * the item the user wishes to create. or edit.

+ *

+ * The name of the state parameter which indicates the content type of the + * item the user wishes to create. or edit.

* - *

The parameter must be a BigDecimalParameter which encodes the id of - * the content type.

+ *

+ * The parameter must be a BigDecimalParameter which encodes the id of the + * content type.

*/ public static final String CONTENT_TYPE = "content_type"; - public static final int AUTHORING_TAB = s_tabOrder++; - public static final int LANGUAGE_TAB = s_tabOrder++; - public static final int WORKFLOW_TAB = s_tabOrder++; - public static final int PUBLISHING_TAB = s_tabOrder++; - public static final int HISTORY_TAB = s_tabOrder++; - public static final int TEMPLATES_TAB = s_tabOrder++; - private final TabbedPane m_tabbedPane; - private StringParameter m_returnURL; - private ItemSelectionModel m_itemModel; - private ACSObjectSelectionModel m_typeModel; - private ContentItemRequestLocal m_item; - private Summary m_summaryPane; - private ItemWorkflowAdminPane m_workflowPane; - private ItemLifecycleAdminPane m_lifecyclePane; - private WizardSelector m_wizardPane; - private ItemLanguages m_languagesPane; - private ItemRevisionAdminPane m_revisionsPane; - private ItemTemplates m_templatesPane; - private Link m_previewLink; - private GlobalNavigation m_globalNavigation; - private ContentItemContextBar m_contextBar; + public static final int AUTHORING_TAB = 1; + public static final int LANGUAGE_TAB = 2; + public static final int WORKFLOW_TAB = 3; + public static final int PUBLISHING_TAB = 4; + public static final int HISTORY_TAB = 5; + public static final int TEMPLATES_TAB = 6; + + private final TabbedPane tabbedPane; + private final StringParameter returnUrlParameter; + private final ItemSelectionModel itemModel; + private final SingleSelectionModel selectedLanguageModel; + private final ACSObjectSelectionModel typeModel; + private final ContentItemRequestLocal itemRequestLocal; + private final Summary summaryPane; + private final ItemWorkflowAdminPane workflowPane; + private final ItemLifecycleAdminPane lifecyclePane; + private final WizardSelector wizardPane; + private final ItemLanguages languagesPane; + private final ItemRevisionAdminPane revisionsPane; + private final ItemTemplates templatesPane; + private final Link m_previewLink; + private final GlobalNavigation m_globalNavigation; + private final ContentItemContextBar m_contextBar; private class ItemRequestLocal extends ContentItemRequestLocal { + @Override protected final Object initialValue(final PageState state) { return CMS.getContext().getContentItem(); } @@ -140,9 +165,11 @@ public class ContentItemPage extends CMSPage implements ActionListener { private class TitlePrinter implements PrintListener { - public final void prepare(final PrintEvent e) { - final Label label = (Label) e.getTarget(); - final ContentItem item = m_item.getContentItem(e.getPageState()); + @Override + public final void prepare(final PrintEvent event) { + final Label label = (Label) event.getTarget(); + final ContentItem item = itemRequestLocal.getContentItem(event. + getPageState()); label.setLabel(item.getDisplayName()); } @@ -154,80 +181,88 @@ public class ContentItemPage extends CMSPage implements ActionListener { public ContentItemPage() { super("", new SimpleContainer()); - m_item = new ItemRequestLocal(); + itemRequestLocal = new ItemRequestLocal(); setClassAttr("cms-admin"); setTitle(new Label(new TitlePrinter())); // Add the item id global state parameter - BigDecimalParameter itemId = new BigDecimalParameter(ITEM_ID); + final LongParameter itemId = new LongParameter(ITEM_ID); itemId.addParameterListener(new NotNullValidationListener(ITEM_ID)); addGlobalStateParam(itemId); - m_itemModel = new ItemSelectionModel(itemId); + itemModel = new ItemSelectionModel(itemId); + + // Add the selected item language as parameter + final StringParameter selectedLanguage = new StringParameter( + SELECTED_LANGUAGE); + selectedLanguage.addParameterListener(new NotNullValidationListener( + SELECTED_LANGUAGE)); + addGlobalStateParam(selectedLanguage); + selectedLanguageModel = new ParameterSingleSelectionModel<>( + selectedLanguage); // Add the content type global state parameter - BigDecimalParameter contentType = new BigDecimalParameter(CONTENT_TYPE); + final LongParameter contentType = new LongParameter(CONTENT_TYPE); addGlobalStateParam(contentType); // Add the streamlined creation global state parameter - StringParameter streamlinedCreation = new StringParameter( + final StringParameter streamlinedCreation = new StringParameter( STREAMLINED_CREATION); addGlobalStateParam(streamlinedCreation); - m_typeModel = new ACSObjectSelectionModel(ContentType.class.getName(), - ContentType.BASE_DATA_OBJECT_TYPE, - contentType); + typeModel = new ACSObjectSelectionModel(ContentType.class.getName(), + ContentType.class.getName(), + contentType); // Validate the item ID parameter (caches the validation). - getStateModel().addValidationListener(new FormValidationListener() { - public void validate(FormSectionEvent event) - throws FormProcessException { - validateItemID(event.getPageState()); - } - }); + getStateModel().addValidationListener( + event -> validateItemID(event.getPageState())); // Add the return url global state parameter - m_returnURL = new StringParameter(RETURN_URL); - addGlobalStateParam(m_returnURL); + returnUrlParameter = new StringParameter(RETURN_URL); + addGlobalStateParam(returnUrlParameter); m_globalNavigation = new GlobalNavigation(); add(m_globalNavigation); - m_contextBar = new ContentItemContextBar(m_itemModel); + m_contextBar = new ContentItemContextBar(itemModel); add(m_contextBar); // Create panels. - m_summaryPane = new Summary(m_itemModel); - m_wizardPane = new WizardSelector(m_itemModel, m_typeModel); - m_languagesPane = new ItemLanguages(m_itemModel); - m_workflowPane = new ItemWorkflowAdminPane(itemId); // Make this use m_item XXX - m_lifecyclePane = new ItemLifecycleAdminPane(m_item); - m_revisionsPane = new ItemRevisionAdminPane(m_item); - m_templatesPane = new ItemTemplates(m_itemModel); + summaryPane = new Summary(itemModel); + wizardPane = new WizardSelector(itemModel, typeModel); + languagesPane = new ItemLanguages(itemModel, selectedLanguageModel); + workflowPane = new ItemWorkflowAdminPane(itemId); // Make this use m_item XXX + lifecyclePane = new ItemLifecycleAdminPane(itemRequestLocal); + revisionsPane = new ItemRevisionAdminPane(itemRequestLocal); + templatesPane = new ItemTemplates(itemModel); // Create tabbed pane. - m_tabbedPane = new TabbedPane(); - add(m_tabbedPane); + tabbedPane = new TabbedPane(); + add(tabbedPane); - m_tabbedPane.setIdAttr("page-body"); + tabbedPane.setIdAttr("page-body"); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.summary")), m_summaryPane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.authoring")), m_wizardPane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.languages")), - m_languagesPane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.workflow")), - m_workflowPane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.lifecycles")), - m_lifecyclePane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.history")), - m_revisionsPane); - m_tabbedPane.addTab(new Label(gz("cms.ui.item.templates")), - m_templatesPane); + tabbedPane.addTab(new Label(gz("cms.ui.item.summary")), summaryPane); + tabbedPane. + addTab(new Label(gz("cms.ui.item.authoring")), wizardPane); + tabbedPane.addTab(new Label(gz("cms.ui.item.languages")), + languagesPane); + tabbedPane.addTab(new Label(gz("cms.ui.item.workflow")), + workflowPane); + tabbedPane.addTab(new Label(gz("cms.ui.item.lifecycles")), + lifecyclePane); + tabbedPane.addTab(new Label(gz("cms.ui.item.history")), + revisionsPane); + tabbedPane.addTab(new Label(gz("cms.ui.item.templates")), + templatesPane); - m_tabbedPane.addActionListener(new ActionListener() { - public final void actionPerformed(final ActionEvent e) { - final PageState state = e.getPageState(); - final Component pane = m_tabbedPane.getCurrentPane(state); + tabbedPane.addActionListener(new ActionListener() { + @Override + public final void actionPerformed(final ActionEvent event) { + + final PageState state = event.getPageState(); + final Component pane = tabbedPane.getCurrentPane(state); if (pane instanceof Resettable) { ((Resettable) pane).reset(state); @@ -237,13 +272,15 @@ public class ContentItemPage extends CMSPage implements ActionListener { // Build the preview link. m_previewLink = new Link(new Label(gz("cms.ui.preview")), - new PrintListener() { - public final void prepare(final PrintEvent e) { - final Link link = (Link) e.getTarget(); - link.setTarget(getPreviewURL(e.getPageState())); - link.setTargetFrame(Link.NEW_FRAME); - } - }); + new PrintListener() { + @Override + public final void prepare(final PrintEvent event) { + final Link link = (Link) event.getTarget(); + link.setTarget(getPreviewURL(event. + getPageState())); + link.setTargetFrame(Link.NEW_FRAME); + } + }); m_previewLink.setIdAttr("preview_link"); add(m_previewLink); @@ -251,21 +288,25 @@ public class ContentItemPage extends CMSPage implements ActionListener { // Add validation to make sure we are not attempting to edit a live item getStateModel().addValidationListener(new FormValidationListener() { - public void validate(FormSectionEvent e) throws FormProcessException { - PageState s = e.getPageState(); - FormData data = e.getFormData(); - final ContentItem item = m_item.getContentItem(s); - if (item != null && ContentItem.LIVE.equals(item.getVersion())) { - s_log.error(String.format( - "The item %d is live and cannot be edited.", item.getID())); - // data.addError(err); - throw new FormProcessException(GlobalizationUtil.globalize( - "cms.ui.live_item_not_editable")); + + @Override + public void validate(final FormSectionEvent event) + throws FormProcessException { + + PageState s = event.getPageState(); + FormData data = event.getFormData(); + final ContentItem item = itemRequestLocal.getContentItem(s); + if (item != null + && ContentItemVersion.LIVE == item.getVersion()) { + LOGGER.error(String.format( + "The item %d is live and cannot be edited.", item. + getObjectId())); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.live_item_not_editable", + CmsConstants.CMS_BUNDLE)); } } }); - - add(new DebugPanel()); } /** @@ -276,12 +317,13 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @pre state != null * @exception FormProcessException if the item_id is not valid */ - protected void validateItemID(PageState state) throws FormProcessException { - final ContentItem item = m_item.getContentItem(state); + protected void validateItemID(final PageState state) throws + FormProcessException { + final ContentItem item = itemRequestLocal.getContentItem(state); if (item == null) { - throw new FormProcessException(GlobalizationUtil.globalize( - "cms.ui.invalid_item_id")); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.invalid_item_id", CmsConstants.CMS_BUNDLE)); } } @@ -294,7 +336,7 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @return The current content section */ @Override - public ContentSection getContentSection(HttpServletRequest request) { + public ContentSection getContentSection(final HttpServletRequest request) { // Resets all content sections associations. ContentSection section = super.getContentSection(request); Assert.exists(section); @@ -310,54 +352,49 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @return The current content item, null if there is none */ @Override - public ContentItem getContentItem(PageState state) { - return (ContentItem) m_itemModel.getSelectedObject(state); + public ContentItem getContentItem(final PageState state) { + return (ContentItem) itemModel.getSelectedObject(state); } /** * Set the current tab, if necessary + * @param event */ - public void actionPerformed(ActionEvent event) { + @Override + public void actionPerformed(final ActionEvent event) { final PageState state = event.getPageState(); final String setTab = state.getRequest().getParameter(SET_TAB); // Hide the templates tab, the workflow tab, and the preview // link if the current item is a template. - final ContentItem item = m_item.getContentItem(state); - - if (item instanceof Template) { - m_tabbedPane.setTabVisible(state, m_templatesPane, false); - m_tabbedPane.setTabVisible(state, m_workflowPane, false); - m_tabbedPane.setTabVisible(state, m_languagesPane, false); - m_previewLink.setVisible(state, false); - } else { - m_tabbedPane.setTabVisible(state, m_templatesPane, !ContentSection. - getConfig().getHideTemplatesTab()); - } - - // Added by: Sören Bernstein - // If the content item is a language invariant content item, don't show - // the language pane - if (item instanceof LanguageInvariantContentItem) { - LanguageInvariantContentItem li_item = (LanguageInvariantContentItem) item; - if (li_item.isLanguageInvariant()) { - m_tabbedPane.setTabVisible(state, m_languagesPane, false); - } - } + final ContentItem item = itemRequestLocal.getContentItem(state); + // ToDo: Reable when Templates have been ported. Not clear yet if + // Templates will be ContentItems in LibreCMS... +// if (item instanceof Template) { +// tabbedPane.setTabVisible(state, templatesPane, false); +// tabbedPane.setTabVisible(state, workflowPane, false); +// tabbedPane.setTabVisible(state, languagesPane, false); +// m_previewLink.setVisible(state, false); +// } else { +// tabbedPane.setTabVisible(state, +// templatesPane, +// !ContentSectionConfig.getConfig().getHideTemplatesTab()); +// } // Set the current tab based on parameters if (setTab != null) { - Integer tab = null; + Integer tab; try { tab = Integer.valueOf(setTab); - } catch (NumberFormatException e) { + } catch (NumberFormatException ex) { // Stop processing set_tab parameter. + LOGGER.warn("Stopping processing of set_tab parameter.", ex); return; } - if (tab.intValue() < m_tabbedPane.size()) { - m_tabbedPane.setSelectedIndex(state, tab.intValue()); + if (tab < tabbedPane.size()) { + tabbedPane.setSelectedIndex(state, tab); } } } @@ -368,10 +405,11 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @param nodeURL The URL where this page is mounted * @param itemId The id of the item to display * @param tab The index of the tab to display + * @return */ - public static String getItemURL(String nodeURL, - BigDecimal itemId, - int tab) { + public static String getItemURL(final String nodeURL, + final Long itemId, + final int tab) { return getItemURL(nodeURL, itemId, tab, false); } @@ -382,33 +420,58 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @param itemId The id of the item to display * @param tab The index of the tab to display * @param streamlinedCreation Whether to activate Streamlined item authoring + * @return */ - public static String getItemURL(String nodeURL, - BigDecimal itemId, - int tab, - boolean streamlinedCreation) { - StringBuffer url = new StringBuffer(); + public static String getItemURL(final String nodeURL, + final Long itemId, + final int tab, + final boolean streamlinedCreation) { + final StringBuilder urlBuilder = new StringBuilder(); - url.append(nodeURL).append(PageLocations.ITEM_PAGE).append("?").append( - ITEM_ID).append("=").append(itemId.toString()).append("&"). - append(SET_TAB).append("=").append(tab); + urlBuilder + .append(nodeURL) + .append(PageLocations.ITEM_PAGE) + .append("?") + .append(ITEM_ID) + .append("=") + .append(itemId.toString()) + .append("&") + .append(SET_TAB) + .append("=") + .append(tab); - if (streamlinedCreation && ContentSection.getConfig(). - getUseStreamlinedCreation()) { - url.append("&").append(STREAMLINED_CREATION).append("=").append( - STREAMLINED_CREATION_ACTIVE); + if (streamlinedCreation + && CMSConfig.getConfig().isUseStreamlinedCreation()) { + + urlBuilder + .append("&") + .append(STREAMLINED_CREATION) + .append("=") + .append(STREAMLINED_CREATION_ACTIVE); } - return url.toString(); + return urlBuilder.toString(); } /** + * @param itemId + * @param tab + * @return * @deprecated Use getItemURL instead */ - public static String getRelativeItemURL(BigDecimal itemId, int tab) { - StringBuffer url = new StringBuffer(); - url.append(PageLocations.ITEM_PAGE).append("?").append(ITEM_ID).append("=").append(itemId. - toString()).append("&").append(SET_TAB).append("=").append(tab); + public static String getRelativeItemURL(final Long itemId, final int tab) { + final StringBuilder url = new StringBuilder(); + url + .append(PageLocations.ITEM_PAGE) + .append("?") + .append(ITEM_ID) + .append("=") + .append(itemId.toString()) + .append("&") + .append(SET_TAB) + .append("=") + .append(tab); + return url.toString(); } @@ -417,16 +480,18 @@ public class ContentItemPage extends CMSPage implements ActionListener { * * @param item the ContentItem object to display * @param tab The index of the tab to display + * @return */ - public static String getItemURL(ContentItem item, int tab) { - final ContentSection section = item.getContentSection(); + public static String getItemURL(final ContentItem item, + final int tab) { + final ContentSection section = item.getContentType().getContentSection(); if (section == null) { return null; } else { - final String nodeURL = section.getPath() + "/"; + final String nodeURL = section.getPrimaryUrl() + "/"; - return getItemURL(nodeURL, item.getID(), tab); + return getItemURL(nodeURL, item.getObjectId(), tab); } } @@ -435,16 +500,20 @@ public class ContentItemPage extends CMSPage implements ActionListener { * * @param itemId the id of the ContentItem object to display * @param tab The index of the tab to display + * @return */ - public static String getItemURL(long itemId, int tab) { - final ContentItem item = - (ContentItem) DomainObjectFactory.newInstance(new OID( - ContentItem.BASE_DATA_OBJECT_TYPE, itemId)); + public static String getItemURL(final long itemId, + final int tab) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemRepository itemRepo = cdiUtil.findBean( + ContentItemRepository.class); - if (item == null) { - return null; + final Optional item = itemRepo.findById(itemId); + + if (item.isPresent()) { + return getItemURL(item.get(), tab); } else { - return getItemURL(item, tab); + return null; } } @@ -454,12 +523,12 @@ public class ContentItemPage extends CMSPage implements ActionListener { * * @param state The current page state */ - public void redirectBack(PageState state) { + public void redirectBack(final PageState state) { try { - String returnURL = (String) state.getValue(m_returnURL); - state.getResponse().sendRedirect(returnURL); - } catch (IOException e) { - s_log.error("IO Error redirecting back", e); + final String returnUrl = (String) state.getValue(returnUrlParameter); + state.getResponse().sendRedirect(returnUrl); + } catch (IOException ex) { + LOGGER.error("IO Error redirecting back", ex); // do nothing } } @@ -467,13 +536,13 @@ public class ContentItemPage extends CMSPage implements ActionListener { /** * Fetch the preview URL. */ - private String getPreviewURL(PageState state) { - final ContentItem item = m_item.getContentItem(state); + private String getPreviewURL(final PageState state) { + final ContentItem item = itemRequestLocal.getContentItem(state); if (item instanceof CustomizedPreviewLink) { final String previewLink = ((CustomizedPreviewLink) item). getPreviewUrl( - state); + state); if ((previewLink == null) || previewLink.isEmpty()) { return getDefaultPreviewLink(state, item); } else { @@ -491,49 +560,66 @@ public class ContentItemPage extends CMSPage implements ActionListener { * @return */ private String getDefaultPreviewLink(final PageState state, - final ContentItem item) { + final ContentItem item) { final ContentSection section = CMS.getContext().getContentSection(); - //ContentSection section = getContentSection(state); - final ItemResolver itemResolver = section.getItemResolver(); + final ItemResolver itemResolver; + try { + final Class itemResolverClass = Class.forName(section. + getItemResolverClass()); + itemResolver = (ItemResolver) itemResolverClass.newInstance(); + } catch (ClassNotFoundException + | IllegalAccessException + | InstantiationException ex) { + throw new UncheckedWrapperException(ex); + } // Pass in the "Live" context since we need it for the preview - return itemResolver.generateItemURL(state, item, section, - CMSDispatcher.PREVIEW); + return itemResolver.generateItemURL(state, + item, + section, + CMSDispatcher.PREVIEW); } protected final static GlobalizedMessage gz(final String key) { - return GlobalizationUtil.globalize(key); + return new GlobalizedMessage(key, CmsConstants.CMS_BUNDLE); } protected final static String lz(final String key) { return (String) gz(key).localize(); } - public static boolean isStreamlinedCreationActive(PageState state) { - return ContentSection.getConfig().getUseStreamlinedCreation() - && STREAMLINED_CREATION_ACTIVE.equals(state.getRequest(). - getParameter(STREAMLINED_CREATION)); + public static boolean isStreamlinedCreationActive(final PageState state) { + return CMSConfig.getConfig().isUseStreamlinedCreation() + && STREAMLINED_CREATION_ACTIVE.equals(state.getRequest(). + getParameter(STREAMLINED_CREATION)); } protected TabbedPane getTabbedPane() { - return m_tabbedPane; + return tabbedPane; } protected WizardSelector getWizardPane() { - return m_wizardPane; + return wizardPane; } /** * Adds the content type to the output. - * + * * @param state PageState * @param parent Parent document * @return page */ - protected Element generateXMLHelper(PageState state, Document parent) { - Element page = super.generateXMLHelper(state, parent); - Element contenttype = page.newChildElement("bebop:contentType", BEBOP_XML_NS); - contenttype.setText(m_item.getContentItem(state).getContentType().getName()); + @Override + protected Element generateXMLHelper(final PageState state, + final Document parent) { + final Element page = super.generateXMLHelper(state, parent); + final Element contenttype = page.newChildElement("bebop:contentType", + BEBOP_XML_NS); + + contenttype.setText(itemRequestLocal + .getContentItem(state) + .getContentType() + .getLabel().getValue(KernelConfig.getConfig().getDefaultLocale())); return page; } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/SecurityPropertyEditor.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/SecurityPropertyEditor.java new file mode 100755 index 000000000..48b126a43 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/SecurityPropertyEditor.java @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.FormSection; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.PropertyEditor; +import com.arsdigita.bebop.PropertyEditorModel; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.cms.dispatcher.Utilities; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.toolbox.ui.ComponentAccess; +import com.arsdigita.util.Assert; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.librecms.CmsConstants; + +/** + * Extends {@link com.arsdigita.bebop.PropertyEditor} to provide access control + * features. Each link may be associated with a {@link + * com.arsdigita.toolbox.ui.ComponentAccess} object; if the current does not + * have sufficient privileges, the link will be hidden. + *

+ * The simple use pattern for this component is as follows: + * + *


+ * SecurityPropertyEditor editor = new SecurityPropertyEditor();
+ * editor.setDisplayComponent(new FooComponent());
+ * NameEditForm n = new NameEditForm();
+ * ComponentAccess ca1 = new ComponentAccess(n);
+ * ca1.addAccessCheck(WORKFLOW_ADMIN);
+ * ca1.addAccessCheck(CATEGORY_ADMIN);
+ * editor.add("name", "Edit Name", ca, n.getCancelButton());
+ * AddressEditForm a = new AddressEditForm();
+ * ComponentAccess ca2 = new ComponentAccess(a);
+ * editor.add("address", "Edit Address", ca2, a.getCancelButton());
+ * 
+ * + * @author Michael Pih (pihman@arsdigita.com) + * @author Stanislav Freidin (sfreidin@arsdigita.com) + * @author Jens Pelzetter + */ +public class SecurityPropertyEditor extends PropertyEditor { + + private final Map accessChecks; + + /** + * Construct a new, empty PropertyEditor. The {@link + * #setDisplayComponent(Component)} method must be called before this + * component is locked. + */ + public SecurityPropertyEditor() { + this(null); + } + + /** + * Construct a new, PropertyEditor with the given display + * component + * + * @param display The display component + */ + public SecurityPropertyEditor(final Component display) { + super(display); + accessChecks = new HashMap<>(); + setModelBuilder(new AccessListModelBuilder()); + } + + /** + * Add a component to the property editor. The component will be completely + * invisible; it is up to the user to call {@link #showComponent(PageState, + * String)} to display the component, and to call {@link + * #showDisplayPane(PageState)} when the component needs to be hidden. + * + * @param key The symbolic key for the component; must be unique for this + * PropertyEditor + * @param componentAccess The {@link ComponentAccess} object which contains + * the child component, along with security restrictions + */ + public void addComponent(final String key, + final ComponentAccess componentAccess) { + super.addComponent(key, componentAccess.getComponent()); + accessChecks.put(key, componentAccess); + } + + /** + * Add a component to the list of links. It is up to the component to + * correctly call showDisplayPane when it's done. + * + * @param key The symbolic key for the component; must be unique for this + * PropertyEditor + * @param label The label for the link + * @param componentAccess The component access + * @deprecated use addComponent(String,GlobalizedMessage,ComponentAccess) + * instead. + */ + public void addComponent(final String key, + final String label, + final ComponentAccess componentAccess) { + addComponent(key, componentAccess); + getLabelsMap().put(key, label); + } + + /** + * Add a component to the list of links. It is up to the component to + * correctly call showDisplayPane when it's done. + * + * @param key The symbolic key for the component; must be unique for this + * PropertyEditor + * @param label The label for the link + * @param componentAccess The component access + */ + public void addComponent(final String key, + final GlobalizedMessage label, + final ComponentAccess componentAccess) { + addComponent(key, componentAccess); + getLabelsMap().put(key, label); + } + + /** + * Specify a new {@link ComponentAccess} for a component which has already + * been added to the SecurityPropertyEditor. + * + * @param key the key under which the component was added + * @param componentAccess the ComponentAccess instance that + * will determine when the link for the specified component should be + * visible + * @pre access.getComponent() == m_forms.get(key) + */ + public void setComponentAccess(final String key, + final ComponentAccess componentAccess) { + Assert.isUnlocked(this); + final Component component = getComponent(key); + Assert.exists(component, "the specified component"); + Assert.isTrue(componentAccess.getComponent().equals(component), + "The specified component does not match the component that" + + " id already in the PropertyEditor"); + accessChecks.put(key, componentAccess); + } + + /** + * Add a form to the set of forms which could be used to edit the + * properties. + * + * @param key The symbolic key for the form; must be unique for this + * PropertyEditor + * @param label The label for the link to access the form + * @param componentAccess The form ComponentAccess + * + * @deprecated use add(String,GlobalizedMessage,ComponentAccess) + */ + public void add(final String key, + final String label, + final ComponentAccess componentAccess) { + final Component component = componentAccess.getComponent(); + if (component instanceof Form) { + final Form form = (Form) component; + accessChecks.put(key, componentAccess); + add(key, label, form); + addSecurityListener(form); + } else if (component instanceof FormSection) { + final FormSection section = (FormSection) componentAccess. + getComponent(); + accessChecks.put(key, componentAccess); + add(key, label, section); + addSecurityListener(section); + } else { + throw new IllegalArgumentException( + "The ComponentAccess object does " + + "not contain a form section."); + } + } + + /** + * Add a form to the set of forms which could be used to edit the + * properties. + * + * @param key The symbolic key for the form; must be unique for this + * PropertyEditor + * @param label The label for the link to access the form + * @param componentAccess The form ComponentAccess + */ + public void add(final String key, + final GlobalizedMessage label, + final ComponentAccess componentAccess) { + final Component component = componentAccess.getComponent(); + if (component instanceof Form) { + final Form form = (Form) component; + accessChecks.put(key, componentAccess); + add(key, label, form); + addSecurityListener(form); + } else if (component instanceof FormSection) { + final FormSection section = (FormSection) componentAccess. + getComponent(); + accessChecks.put(key, componentAccess); + add(key, label, section); + addSecurityListener(section); + } else { + throw new IllegalArgumentException( + "The ComponentAccess object does " + + "not contain a form section."); + } + } + + /** + * Add a form to the set of forms which could be used to edit the properties + * + * @param key The symbolic key for the form; must be unique for this + * PropertyEditor + * @param label The label for the link to access the form. + * @param componentAccess The form ComponentAccess + * @param cancelButton The Cancel button on the form. + * + * @deprecated use add(String,GlobalizedMessage,ComponentAccess,Submit) + * instead + */ + public void add(final String key, + final String label, + final ComponentAccess componentAccess, + final Submit cancelButton) { + add(key, label, componentAccess); + addCancelListener((FormSection) componentAccess.getComponent(), + cancelButton); + } + + /** + * Add a form to the set of forms which could be used to edit the properties + * + * @param key The symbolic key for the form; must be unique for this + * PropertyEditor + * @param label The label for the link to access the form. + * @param componentAccess The form ComponentAccess + * @param cancelButton The Cancel button on the form. + */ + public void add(final String key, + final GlobalizedMessage label, + final ComponentAccess componentAccess, + final Submit cancelButton) { + add(key, label, componentAccess); + addCancelListener((FormSection) componentAccess.getComponent(), + cancelButton); + } + + /** + * Add a submission listener to the form that will hide all components and + * show the display pane. This method should be used to add submission + * listeners to forms which are buried deep inside some component, and are + * not members of this PropertyEditor. + * + * @param form The form + */ + public void addSecurityListener(final FormSection form) { + form.addSubmissionListener(new FormSubmissionListener() { + + @Override + public void submitted(final FormSectionEvent event) throws + FormProcessException { + + final PageState state = event.getPageState(); + + // Cancel the form if the user does not pass the access checks. + final String key = (String) getList().getSelectedKey(state); + final ComponentAccess componentAccess + = (ComponentAccess) accessChecks.get(key); + + if (key == null || componentAccess == null) { + // no components currently selected and therefore + // no access checks to run for visibility + // or + // there are no access restrictions on the form + return; + } + + if (!componentAccess.canAccess()) { + showDisplayPane(state); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.insufficient_privileges", + CmsConstants.CMS_BUNDLE)); + } + } + }); + } + + /** + * Add all required listeners to the form to ensure that if the form is + * submitted successfully or cancelled, the display pane will be shown. This + * method should be used to add listeners to forms which are buried deep + * inside some component, and are not members of this + * PropertyEditor. + * + * @param form The form + * @param cancelButton the "Cancel" button on the form + */ + @Override + public void addListeners(final FormSection form, + final Submit cancelButton) { + addSecurityListener(form); + super.addListeners(form, cancelButton); + } + + /** + * Return the map of keys to access checks + * + * @return Map of keys to access check + */ + protected final Map getAccessMap() { + return accessChecks; + } + + /** + * Returns an {@link SecurityPropertyEditor.AccessListModel} during each + * request + */ + protected static class AccessListModelBuilder extends DefaultModelBuilder { + + public AccessListModelBuilder() { + super(); + } + + @Override + public PropertyEditorModel makeModel( + final PropertyEditor propertyEditor, final PageState state) { + + return new AccessListModel( + getProperties(propertyEditor), + ((SecurityPropertyEditor) propertyEditor).getAccessMap(), + state); + } + } + + /** + * Performs access checks for each property; skips the properties that the + * user is not allowed to access + */ + protected static class AccessListModel extends DefaultModel { + + private final Map accessMap; + private final PageState state; + + public AccessListModel(final Iterator iter, + final Map accessMap, + final PageState state) { + super(iter); + this.accessMap = accessMap; + this.state = state; + } + + @Override + public boolean next() { + + while (super.next()) { + final Object key = getKey(); + final ComponentAccess ca = accessMap.get(key.toString()); + + if (ca == null) { + return true; + } + + if (ca.canAccess()) { + return true; + } + + // Otherwise, skip the property + } + + return false; + } + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo similarity index 100% rename from ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.off rename to ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo index 04c0e8151..c8065ebf5 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguages.java.todo @@ -43,6 +43,7 @@ import com.arsdigita.cms.ui.authoring.LanguageWidget; import org.librecms.util.LanguageUtil; import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.kernel.KernelConfig; import com.arsdigita.toolbox.ui.ActionGroup; import com.arsdigita.toolbox.ui.LayoutPanel; import com.arsdigita.toolbox.ui.Section; @@ -60,8 +61,11 @@ import org.librecms.contentsection.ContentItemL10NManager; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.TooManyListenersException; import java.util.stream.Collectors; +import org.libreccm.configuration.ConfigurationManager; +import org.librecms.CmsConstants; /** * Displays the "Language instances" pane, with all the language instances in @@ -81,10 +85,10 @@ public class ItemLanguages extends LayoutPanel { /** * Constructs a new ItemLanguages. * - * @param selectionModel the {@link ItemSelectionModel} which will supply - * the current item + * @param selectionModel the {@link ItemSelectionModel} which will supply + * the current item * @param selectedLanguage {@link SingleSelectionModel} for the selected - * language. + * language. */ public ItemLanguages(final ItemSelectionModel selectionModel, final SingleSelectionModel selectedLanguage) { @@ -141,22 +145,22 @@ public class ItemLanguages extends LayoutPanel { final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final LanguageUtil languageUtil = cdiUtil.findBean( - LanguageUtil.class); + LanguageUtil.class); final ContentItemL10NManager l10NManager = cdiUtil.findBean( - ContentItemL10NManager.class); + ContentItemL10NManager.class); final List creatableLangs = l10NManager.creatableLocales( - item).stream() - .map(locale -> locale.toString()) - .collect(Collectors.toList()); + item).stream() + .map(locale -> locale.toString()) + .collect(Collectors.toList()); final List languages = languageUtil.convertToG11N( - creatableLangs); + creatableLangs); - for(final Pair pair : languages) { + for (final Pair pair : languages) { final String langCode = (String) pair.getKey(); final GlobalizedMessage langName - = (GlobalizedMessage) pair - .getValue(); + = (GlobalizedMessage) pair + .getValue(); optionGroup.addOption(new Option(langCode, new Label(langName))); } } @@ -170,64 +174,46 @@ public class ItemLanguages extends LayoutPanel { @Override public final void process(final FormSectionEvent event) - throws FormProcessException { - PageState state = event.getPageState(); - String lang = (String) languageWidget.getValue(state); - ContentPage item = (ContentPage) selectionModel.getSelectedItem( - state); - ContentBundle bundle = item.getContentBundle(); - String name = bundle.getName(); + throws FormProcessException { + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + + final PageState state = event.getPageState(); + final String lang = (String) languageWidget.getValue(state); + final ContentItem item = (ContentItem) selectionModel + .getSelectedItem(state); + + final ConfigurationManager confManager = cdiUtil.findBean( + ConfigurationManager.class); + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + final Locale defaultLocale = kernelConfig.getDefaultLocale(); + final String name = item.getName().getValue(defaultLocale); if (createSubmit.isSelected(state)) { - ContentSection section = item.getContentSection(); + final ContentSection section = item.getContentType(). + getContentSection(); Assert.exists(section, ContentSection.class); - ContentType type = item.getContentType(); - - item = (ContentPage) item.copy(lang); - item.setLanguage(lang); - item.setName(name); - - // Apply default workflow - WorkflowTemplate template - = ContentTypeWorkflowTemplate - .getWorkflowTemplate(section, type); - if (template != null) { - Workflow w = template.instantiateNewWorkflow(); - w.setObjectID(item.getID()); - w.start(Kernel.getContext().getUser()); - w.save(); - } - - selectionModel.setSelectedObject(state, item); + final ContentItemL10NManager l10NManager = cdiUtil.findBean( + ContentItemL10NManager.class); + l10NManager.addLanguage(item, new Locale(lang)); // redirect to ContentItemPage.AUTHORING_TAB of the new instance - final String target = URL.getDispatcherPath() + ContentItemPage. - getItemURL(item, - ContentItemPage.AUTHORING_TAB); - + final String target = String.join( + "", URL.getDispatcherPath(), + ContentItemPage.getItemURL(item, + ContentItemPage.AUTHORING_TAB)); + throw new RedirectSignal(target, true); - } else if (changeSubmit.isSelected(state)) { - String oldLang = item.getLanguage(); - item.setLanguage(lang); - // propagate language change to the Name attribute - item.setName(name); - item.save(); - - // if the item being changed is the default, update the bundle - // to keep this item as the default - if (bundle.getDefaultLanguage().equals(oldLang)) { - bundle.setDefaultLanguage(lang); - bundle.save(); - } - } + } } } protected static final GlobalizedMessage gz(final String key) { - return GlobalizationUtil.globalize(key); + return new GlobalizedMessage(key, CmsConstants.CMS_BUNDLE); } protected static final String lz(final String key) { diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo index 5c05baf98..0f618f4ac 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/ItemLanguagesTable.java.todo @@ -164,7 +164,7 @@ public class ItemLanguagesTable extends Table { final ContentItemL10NManager l10nManager = cdiUtil.findBean( ContentItemL10NManager.class); - l10nManager.removeLangauge(item, new Locale(selectedLang)); + l10nManager.removeLanguage(item, new Locale(selectedLang)); } @Override diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/Summary.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/Summary.java.todo new file mode 100755 index 000000000..85ee3b2ab --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/item/Summary.java.todo @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.item; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.ServletException; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.categorization.Category; +import com.arsdigita.categorization.CategoryCollection; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ContentPage; +import com.arsdigita.cms.ContentSection; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.SecurityManager; +import com.arsdigita.cms.dispatcher.CMSDispatcher; +import com.arsdigita.cms.lifecycle.Lifecycle; +import com.arsdigita.cms.ui.CMSContainer; +import com.arsdigita.cms.util.SecurityConstants; +import com.arsdigita.cms.workflow.CMSEngine; +import com.arsdigita.cms.workflow.CMSTask; +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.domain.DomainServiceInterfaceExposer; +import com.arsdigita.kernel.ACSObject; +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.permissions.PermissionDescriptor; +import com.arsdigita.kernel.permissions.PermissionService; +import com.arsdigita.kernel.permissions.PrivilegeDescriptor; +import com.arsdigita.persistence.Filter; +import com.arsdigita.toolbox.ui.FormatStandards; +import com.arsdigita.util.Assert; +import com.arsdigita.util.GraphSet; +import com.arsdigita.util.Graphs; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.versioning.Transaction; +import com.arsdigita.versioning.TransactionCollection; +import com.arsdigita.versioning.Versions; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.web.Web; +import com.arsdigita.web.WebContext; +import com.arsdigita.workflow.simple.Engine; +import com.arsdigita.workflow.simple.Task; +import com.arsdigita.workflow.simple.TaskCollection; +import com.arsdigita.workflow.simple.TaskComment; +import com.arsdigita.workflow.simple.Workflow; +import com.arsdigita.workflow.simple.WorkflowTemplate; +import com.arsdigita.xml.Element; +import java.io.UnsupportedEncodingException; + +/** + *

+ * This panel displays basic details about a content item such as attributes and associations.

+ * + *

+ * Container: {@link com.arsdigita.cms.ui.ContentItemPage} + * + *

+ * This panel uses an {@link com.arsdigita.cms.dispatcher.XMLGenerator} to convert content items + * into XML.

+ * + * @author Michael Pih (pihman@arsdigita.com) + * @version $Id: Summary.java 1940 2009-05-29 07:15:05Z terry $ + */ +public class Summary extends CMSContainer { + + private static final String SUMMARY = "itemAdminSummary"; + private static final String RESTART_WORKFLOW = "restartWorkflow"; + + private final ItemSelectionModel m_item; + + public Summary(ItemSelectionModel m) { + super(); + + m_item = m; + } + + /** + * Generate XML representation of an item summary. + * + * @param state The page state + * @param parent The parent DOM element + * + * @pre ( state != null ) + * @pre ( parent != null ) + */ + @Override + public void generateXML(PageState state, Element parent) { + if (isVisible(state)) { + + // Determine the item's environment + ContentItem item = getContentItem(state); + ContentSection section = getContentSection(state); + User user = Web.getWebContext().getUser(); + + // Setup xml element for item's properties + Element itemElement = new Element("cms:itemSummary", CMS.CMS_XML_NS); + + // Determine item's name / url stub + itemElement.addAttribute("name", item.getName()); + + // obviously getName() here gets the 'semantically meaningful name' + // from database using class DataType. It is not localizable! And + // it is not really 'semantically meaningful' + String objectType = item.getObjectType().getName(); + + // Quasimodo: ObjectType for summary + itemElement.addAttribute("objectType", objectType); + + // NOT USED - CUSTOMIZED SUMMARY + // Take advantage of caching in the CMS Dispatcher. + // XMLGenerator xmlGenerator = section.getXMLGenerator(); + // xmlGenerator.generateXML(state, parent, SUMMARY); + String descriptionAttribute = ""; + if (objectType.equals("NewsItem") || objectType.equals("Article")) { + descriptionAttribute = "lead"; + } else if (objectType.equals("FileStorageItem") || objectType.equals("Minutes")) { + descriptionAttribute = "description"; + } else if (objectType.equals("Job")) { + descriptionAttribute = "jobDescription"; + } else if (objectType.equals("MultiPartArticle") || objectType.equals("Agenda") + || objectType.equals("PressRelease") || objectType.equals("Service")) { + descriptionAttribute = "summary"; + } + + if (!descriptionAttribute.equals("")) { + itemElement.addAttribute("description", (String) DomainServiceInterfaceExposer.get( + item, descriptionAttribute)); + } + + try { + ContentPage page = new ContentPage(item.getID()); + itemElement.addAttribute("title", page.getTitle()); + } catch (DataObjectNotFoundException ex) { + // + } + + // subject category + Element subjectsElement = new Element("cms:subjectCategories", CMS.CMS_XML_NS); + itemElement.addContent(subjectsElement); + Category itemCategory = null; + Category subjectCategory = Category.getRootForObject(section, "subject"); + if (subjectCategory != null) { + CategoryCollection categories = item.getCategoryCollection(); + while (categories.next()) { + Category category = categories.getCategory(); + CategoryCollection parents = category.getDefaultAscendants(); + parents.addOrder(Category.DEFAULT_ANCESTORS); + if (parents.next()) { + Category parentCategory = parents.getCategory(); + if (parentCategory.equals(subjectCategory)) { + Element subjectElement = new Element("cms:subjectCategory", + CMS.CMS_XML_NS); + subjectElement.addAttribute("name", category.getName()); + subjectElement.setText(category.getPreferredQualifiedName(" -> ", + true)); + subjectsElement.addContent(subjectElement); + } + parents.close(); + } + } + } + + // URL + Element linkElement = new Element("cms:linkSummary", CMS.CMS_XML_NS); + try { + linkElement.addAttribute("url", + String.format("%s/redirect?oid=%s", + Web.getWebappContextPath(), + URLEncoder.encode(item.getDraftVersion() + .getOID() + .toString(), "utf-8"))); + } catch (UnsupportedEncodingException ex) { + throw new UncheckedWrapperException(ex); + } + + //"/redirect?oid=" + URLEncoder.encode(item.getDraftVersion().getOID().toString())); + // WORKFLOW + Element workflowElement = new Element("cms:workflowSummary", CMS.CMS_XML_NS); + Workflow workflow = Workflow.getObjectWorkflow(item); + + SecurityManager sm = CMS.getContext().getSecurityManager(); + if (canWorkflowBeExtended(user, item, workflow)) { + // control event for restarting workflow in edit mode + try { + state.setControlEvent(this, RESTART_WORKFLOW, item.getID().toString()); + workflowElement.addAttribute("restartWorkflowURL", state.stateAsURL()); + state.clearControlEvent(); + } catch (java.io.IOException ex) { + // + } + } + + if (workflow == null) { + workflowElement.addAttribute("noWorkflow", "1"); + } else { + workflowElement.addAttribute("name", workflow.getDisplayName()); + + TaskCollection tc = workflow.getTaskCollection(); + GraphSet g = new GraphSet(); + while (tc.next()) { + Task t = tc.getTask(); + final TaskCollection deps = t.getRequiredTasks(); + final StringBuffer buffer = new StringBuffer(); + while (deps.next()) { + Task dep = deps.getTask(); + g.addEdge(t, dep, null); + buffer.append(dep.getLabel() + ", "); + } + + final int len = buffer.length(); + if (len >= 2) { + buffer.setLength(len - 2); + } else { + g.addNode(t); + } + deps.close(); + } + + List taskList = new ArrayList(); + outer: + while (g.nodeCount() > 0) { + List l = Graphs.getSinkNodes(g); + for (Iterator it = l.iterator(); it.hasNext();) { + Task t = (Task) it.next(); + taskList.add(0, t); + g.removeNode(t); + continue outer; + } + // break loop if no nodes removed + break; + } + Iterator tasks = taskList.iterator(); + + while (tasks.hasNext()) { + Task task = (Task) tasks.next(); + Element taskElement = new Element("cms:task", CMS.CMS_XML_NS); + taskElement.addAttribute("name", task.getDisplayName()); + taskElement.addAttribute("state", task.getStateString()); + Iterator comments = task.getComments(); + while (comments.hasNext()) { + TaskComment comment = (TaskComment) comments.next(); + Element commentElement = new Element("cms:taskComment", CMS.CMS_XML_NS); + User author = comment.getUser(); + String authorName = "Anonymous"; + if (author != null) { + authorName = author.getDisplayName(); + } + + commentElement.addAttribute("author", authorName); + commentElement.addAttribute("comment", comment.getComment()); + commentElement.addAttribute("date", FormatStandards.formatDate(comment + .getDate())); + + taskElement.addContent(commentElement); + } + + workflowElement.addContent(taskElement); + } + } + + // REVISION HISTORY + Element transactionElement = new Element("cms:transactionSummary", CMS.CMS_XML_NS); + transactionElement.addAttribute("creationDate", FormatStandards.formatDate(item + .getCreationDate())); + transactionElement.addAttribute("lastModifiedDate", FormatStandards.formatDate(item + .getLastModifiedDate())); + + TransactionCollection transactions = Versions.getTaggedTransactions(item.getOID()); + while (transactions.next()) { + Transaction transaction = transactions.getTransaction(); + Element element = new Element("cms:transaction", CMS.CMS_XML_NS); + element.addAttribute("date", FormatStandards.formatDate(transaction.getTimestamp())); + String authorName = "Anonymous"; + User author = transaction.getUser(); + if (author != null) { + authorName = author.getDisplayName(); + } + element.addAttribute("author", authorName); + + String url = section.getItemResolver().generateItemURL(state, item, section, + CMSDispatcher.PREVIEW) + + "?transID=" + transaction.getID(); + element.addAttribute("url", url); + transactionElement.addContent(element); + } + + transactions.close(); + + // CATEGORY + Element categoryElement = new Element("cms:categorySummary", CMS.CMS_XML_NS); + + CategoryCollection categories = item.getCategoryCollection(); + while (categories.next()) { + Category category = categories.getCategory(); + Element element = new Element("cms:category", CMS.CMS_XML_NS); + element.setText(category.getPreferredQualifiedName(" -> ", true)); + categoryElement.addContent(element); + + } + categories.close(); + + // LIFECYCLE + Element lifecycleElement = new Element("cms:lifecycleSummary", CMS.CMS_XML_NS); + + Lifecycle lifecycle = item.getLifecycle(); + if (lifecycle == null) { + lifecycleElement.addAttribute("noLifecycle", "1"); + } else { + lifecycleElement.addAttribute("name", lifecycle.getLabel()); + lifecycleElement.addAttribute("startDate", FormatStandards.formatDate(lifecycle + .getStartDate())); + + java.util.Date endDate = lifecycle.getEndDate(); + if (endDate == null) { + lifecycleElement.addAttribute("endDateString", "last forever"); + } else { + lifecycleElement.addAttribute("endDateString", "expire on " + FormatStandards + .formatDate(endDate)); + lifecycleElement.addAttribute("endDate", FormatStandards.formatDate(endDate)); + } + + lifecycleElement.addAttribute("hasBegun", (new Boolean(lifecycle.hasBegun())) + .toString()); + lifecycleElement.addAttribute("hasEnded", (new Boolean(lifecycle.hasEnded())) + .toString()); + } + + parent.addContent(itemElement); + parent.addContent(categoryElement); + parent.addContent(linkElement); + parent.addContent(lifecycleElement); + parent.addContent(workflowElement); + parent.addContent(transactionElement); + } + } + + /** + * Fetch the selected content item. + * + * @param state The page state + * + * @return The selected item + * + * @pre ( state != null ) + */ + protected ContentItem getContentItem(PageState state) { + ContentItem item = (ContentItem) m_item.getSelectedObject(state); + Assert.exists(item); + return item; + } + + /** + * Fetch the current content section. + * + * @param state The page state + * + * @return The content section + * + * @pre ( state != null ) + */ + protected ContentSection getContentSection(PageState state) { + ContentSection section = CMS.getContext().getContentSection(); + return section; + } + + public void respond(PageState state) throws ServletException { + String key = state.getControlEventName(); + String value = state.getControlEventValue(); + if (RESTART_WORKFLOW.equals(key)) { + User user = Web.getWebContext().getUser(); + ContentItem item = getContentItem(state); + ContentSection section = item.getContentSection(); + Workflow w = Workflow.getObjectWorkflow(item); + + if (canWorkflowBeExtended(user, item, w)) { + WorkflowTemplate template = w.getWorkflowTemplate(); + if (template != null) { + template.extendWorkflow(w); + w.save(); + } + + // lock the next task + Engine engine = Engine.getInstance(CMSEngine.CMS_ENGINE_TYPE); + Iterator i = engine.getEnabledTasks(user, w.getID()).iterator(); + if (i.hasNext()) { + CMSTask task = (CMSTask) i.next(); + + if (!task.isLocked()) { + task.lock(user); + } + } + } + + String redirectURL = Web.getConfig().getDispatcherServletPath() + item + .getContentSection().getPath() + "/admin/item.jsp?item_id=" + item.getID() + + "&set_tab=1"; + throw new RedirectSignal(redirectURL, true); + } else { + throw new ServletException("Unknown control event: " + key); + } + } + + /* + * Checks if workflow can be extended + */ + protected boolean canWorkflowBeExtended(User user, ContentItem item, Workflow workflow) { + boolean canBeExtended = true; + + if (workflow == null) { + canBeExtended = false; + } else if (!workflow.isFinished()) { + canBeExtended = false; + } else if (workflow.getWorkflowTemplate() == null) { + canBeExtended = false; + } else { + TaskCollection templates = item.getContentSection().getWorkflowTemplates(); + Filter f = templates.addInSubqueryFilter("id", + "com.arsdigita.cms.getWorkflowTemplateUserFilter"); + f.set("userId", Web.getWebContext().getUser().getID()); + templates.addEqualsFilter(ACSObject.ID, workflow.getWorkflowTemplate().getID()); + + PrivilegeDescriptor pd = PrivilegeDescriptor.get(SecurityConstants.CMS_WORKFLOW_ADMIN); + PermissionDescriptor perm = new PermissionDescriptor(pd, item, user); + if (!(templates.next() || PermissionService.checkPermission(perm))) { + canBeExtended = false; + } + templates.close(); + + } + + return canBeExtended; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java.todo new file mode 100755 index 000000000..1aacd1861 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java.todo @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.lifecycle; + +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.categorization.Category; +import com.arsdigita.categorization.CategoryCollection; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.CMSConfig; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.lifecycle.Lifecycle; +import com.arsdigita.cms.ui.BaseItemPane; +import com.arsdigita.cms.ui.ContentItemPage; +import com.arsdigita.cms.ui.item.ContentItemRequestLocal; +import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.web.URL; + +import org.apache.log4j.Logger; + +/** + * @author Michael Pih + * @author Jack Chung + * @author Justin Ross <jross@redhat.com> + * @author Jens Pelzetter jens@jp-digital.de + * @version $Id: ItemLifecycleAdminPane.java 1942 2009-05-29 07:53:23Z terry $ + */ +public class ItemLifecycleAdminPane extends BaseItemPane { + + private static final Logger s_log = Logger.getLogger( + ItemLifecycleAdminPane.class); + private final ContentItemRequestLocal m_item; + private final LifecycleRequestLocal m_lifecycle; + private final LayoutPanel m_introPane; + private final LayoutPanel m_detailPane; + private final LayoutPanel m_selectPane; + private final LayoutPanel m_lockedPane; + private final LayoutPanel m_errorPane; + private final LayoutPanel m_cantPublishPane; + + public ItemLifecycleAdminPane(final ContentItemRequestLocal item) { + m_item = item; + m_lifecycle = new ItemLifecycleRequestLocal(); + + m_introPane = new LayoutPanel(); + add(m_introPane); + + final Label message = new Label(gz("cms.ui.item.lifecycle.intro")); + m_introPane.setBody(message); + + m_detailPane = new LayoutPanel(); + add(m_detailPane); + + final ItemLifecycleItemPane itemPane = + new ItemLifecycleItemPane(m_item, + m_lifecycle); + m_detailPane.setBody(itemPane); + + m_selectPane = new LayoutPanel(); + add(m_selectPane); + + final ItemLifecycleSelectForm selectForm = + new ItemLifecycleSelectForm(m_item); + m_selectPane.setBody(selectForm); + + m_lockedPane = new LayoutPanel(); + add(m_lockedPane); + + final Label lockedMsg = new Label(gz( + "cms.ui.item.lifecycle.publish_locked")); + m_lockedPane.setBody(lockedMsg); + final ControlLink lockedUpdateLink = new ControlLink(new Label(gz( + "cms.ui.item.lifecycle.publish_locked.update"))); + lockedUpdateLink.addActionListener(new ActionListener() { + + public void actionPerformed(final ActionEvent event) { + throw new RedirectSignal( + URL.getDispatcherPath() + + ContentItemPage.getItemURL( + item.getContentItem(event.getPageState()), + ContentItemPage.PUBLISHING_TAB), + true); + } + }); + m_lockedPane.setBottom(lockedUpdateLink); + + m_errorPane = new LayoutPanel(); + add(m_errorPane); + + final Label errorMsg = new Label(gz("cms.ui.lifecycle.publish.error")); + m_errorPane.setBody(errorMsg); + + m_cantPublishPane = new LayoutPanel(); + add(m_cantPublishPane); + + final Label cantPublish = new Label(gz("cms.ui.lifecycle.publish.not_possible_abstract_category")); + m_cantPublishPane.setBody(cantPublish); + + connect(selectForm, m_detailPane); + } + + private class ItemLifecycleRequestLocal extends LifecycleRequestLocal { + + protected final Object initialValue(final PageState state) { + final ContentItem item = m_item.getContentItem(state); + final Lifecycle lifecycle = item.getLifecycle(); + + s_log.debug("Returning lifecycle " + lifecycle); + + return lifecycle; + } + } + + public final void register(final Page page) { + super.register(page); + + page.addActionListener(new VisibilityListener()); + } + + private class VisibilityListener implements ActionListener { + + public final void actionPerformed(final ActionEvent e) { + s_log.debug("Determining which pane to show"); + + final PageState state = e.getPageState(); + + if (CMSConfig.getInstanceOf().getThreadedPublishing() + && PublishLock.getInstance().isLocked(m_item.getContentItem( + state))) { + if (PublishLock.getInstance().hasError(m_item.getContentItem( + state))) { + push(state, m_errorPane); + } else { + push(state, m_lockedPane); + state.getResponse().addIntHeader("Refresh", 5); + } + } else if(isAssignedToAbstractCategory(m_item.getContentItem(state))) { + push(state, m_cantPublishPane); + } else { + if (state.isVisibleOnPage(ItemLifecycleAdminPane.this)) { + if (m_lifecycle.getLifecycle(state) == null) { + if (hasPermission(state)) { + push(state, m_selectPane); + } else { + push(state, m_introPane); + } + } else { + push(state, m_detailPane); + } + } + } + } + } + + private boolean hasPermission(final PageState state) { + final ContentItem item = m_item.getContentItem(state); + + return CMS.getContext().getSecurityManager().canAccess( + state.getRequest(), SCHEDULE_PUBLICATION, item); + } + + /** + * Checks if the item is assigned to an abstract category. + * + * A category is abstract if not items can assigned to it. + * + * @param item + * @return {@code true} if assigned to a abstract category, {@code false} if not. + */ + private boolean isAssignedToAbstractCategory(final ContentItem item) { + + final CategoryCollection categories = item.getCategoryCollection(); + + boolean result = false; + Category category; + while(categories.next()) { + category = categories.getCategory(); + + if (category.isAbstract()) { + result = true; + break; + } + } + + categories.close(); + + return result; + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/revision/ItemRevisionAdminPane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/revision/ItemRevisionAdminPane.java.todo new file mode 100755 index 000000000..b965e1a0a --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/revision/ItemRevisionAdminPane.java.todo @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.revision; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.GridPanel; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Link; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.TableActionAdapter; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.RadioGroup; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.table.DefaultTableCellRenderer; +import com.arsdigita.bebop.table.DefaultTableColumnModel; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.bebop.table.TableColumnModel; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ContentSection; +import com.arsdigita.cms.dispatcher.CMSDispatcher; +import com.arsdigita.cms.dispatcher.ItemResolver; +import com.arsdigita.cms.ui.BaseItemPane; +import com.arsdigita.cms.ui.item.ContentItemRequestLocal; +import com.arsdigita.toolbox.ui.ActionGroup; +import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.toolbox.ui.NullComponent; +import com.arsdigita.toolbox.ui.Section; +import com.arsdigita.versioning.Transaction; +import com.arsdigita.versioning.Versions; + +import java.math.BigInteger; + +/** + * + * @version $Id: ItemRevisionAdminPane.java 287 2005-02-22 00:29:02Z sskracic $ + */ +public final class ItemRevisionAdminPane extends BaseItemPane { + + private final ContentItemRequestLocal m_item; + + private final RevisionTable m_revisions; + + private final TransactionRequestLocal m_from; + private final TransactionRequestLocal m_to; + + private final LayoutPanel m_selectPane; + private final DifferencePane m_diffPane; + + public ItemRevisionAdminPane(final ContentItemRequestLocal item) { + m_item = item; + + final RadioGroup fromSelect = new RadioGroup("from"); + final RadioGroup toSelect = new RadioGroup("to"); + + m_from = new SelectionRequestLocal(fromSelect); + m_to = new SelectionRequestLocal(toSelect); + + m_revisions = new RevisionTable(fromSelect, toSelect); + + final RevisionForm form = new RevisionForm + (fromSelect, toSelect, m_revisions); + + m_selectPane = new LayoutPanel(); + add(m_selectPane); + setDefault(m_selectPane); + + m_selectPane.setBody + (new Section(gz("cms.ui.item.revisions"), form)); + + final ActionLink returnLink = new ActionLink + (new Label(gz("cms.ui.item.revision.return"))); + + m_diffPane = new DifferencePane(m_item, m_from, m_to, returnLink); + add(m_diffPane); + + connect(form, m_diffPane); + connect(returnLink, m_selectPane); + } + + private class SelectionRequestLocal extends TransactionRequestLocal { + private final RadioGroup m_group; + + SelectionRequestLocal(final RadioGroup group) { + m_group = group; + } + + protected final Object initialValue(final PageState state) { + final Object id = m_group.getValue(state); + + if (id == null || id.toString().equals("")) { + return null; + } else { + return Transaction.retrieve(new BigInteger(id.toString())); + } + } + } + + private class RevisionForm extends Form { + RevisionForm(final RadioGroup fromSelect, + final RadioGroup toSelect, + final RevisionTable revisions) { + super("revisions", new GridPanel(1)); + + setMethod("get"); + + add(fromSelect); + add(toSelect); + + // Sets the 'to' revision to the dummy current revision + // and the 'from' revision to the dummy root revision. + fromSelect.setOptionSelected(""); + toSelect.setOptionSelected(""); + + final ActionGroup group = new ActionGroup(); + add(group); + + group.setSubject(revisions); + + group.addAction + (new Submit("diff", + gz("cms.ui.item.revision.difference.show"))); + } + } + + private class RevisionTable extends Table { + // XXX Need to fix the static l18n stuff + + private TableColumn m_from = new TableColumn + (RevisionTableModelBuilder.FROM, lz("cms.ui.item.revision.from")); + private TableColumn m_to = new TableColumn + (RevisionTableModelBuilder.TO, lz("cms.ui.item.revision.to")); + private TableColumn m_timestamp = new TableColumn + (RevisionTableModelBuilder.TIMESTAMP, lz("cms.ui.item.revision")); + private TableColumn m_user = new TableColumn + (RevisionTableModelBuilder.USER, lz("cms.ui.user")); + private TableColumn m_description = new TableColumn + (RevisionTableModelBuilder.DESCRIPTION, lz("cms.ui.description")); + private TableColumn m_preview = new TableColumn + (RevisionTableModelBuilder.PREVIEW, lz("cms.ui.item.revision.view")); + private TableColumn m_rollback = new TableColumn + (RevisionTableModelBuilder.ROLLBACK, + lz("cms.ui.item.revision.rollback")); + + public RevisionTable(final RadioGroup fromSelect, + final RadioGroup toSelect) { + super(new RevisionTableModelBuilder(m_item), + new DefaultTableColumnModel()); + + final TableColumnModel columns = getColumnModel(); + columns.add(m_from); + columns.add(m_to); + columns.add(m_timestamp); + columns.add(m_user); + columns.add(m_description); + columns.add(m_preview); + columns.add(m_rollback); + + m_from.setCellRenderer(new RadioCellRenderer(fromSelect)); + m_to.setCellRenderer(new RadioCellRenderer(toSelect)); + m_timestamp.setCellRenderer(new DefaultTableCellRenderer(false)); + m_user.setCellRenderer(new DefaultTableCellRenderer(false)); + m_description.setCellRenderer(new DefaultTableCellRenderer(false)); + m_preview.setCellRenderer(new ViewCellRenderer()); + m_rollback.setCellRenderer(new RollbackCellRenderer()); + + setEmptyView(new Label(gz("cms.ui.item.revision.none"))); + + addTableActionListener(new RollbackActionListener()); + } + + class RadioCellRenderer implements TableCellRenderer { + private final RadioGroup m_group; + + RadioCellRenderer(final RadioGroup group) { + m_group = group; + } + + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, final int column) { + if (key.toString().equals("first")) { + if (column == RevisionTableModelBuilder.FROM) { + return new NullComponent(); + } else { + final Option option = new Option("", ""); + + option.setGroup(m_group); + + return option; + } + } else if (key.toString().equals("last")) { + if (column == RevisionTableModelBuilder.FROM) { + final Option option = new Option("", ""); + + option.setGroup(m_group); + + return option; + } else { + return new NullComponent(); + } + } else { + final Option option = new Option(key.toString(), ""); + + option.setGroup(m_group); + + return option; + } + } + } + + class ViewCellRenderer implements TableCellRenderer { + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, final int column) { + if (key instanceof String) { + return new NullComponent(); + } else { + final BigInteger transID = (BigInteger) key; + final ContentItem item = m_item.getContentItem(state); + final ContentSection section = item.getContentSection(); + + final ItemResolver itemResolver = + section.getItemResolver(); + + final StringBuffer url = new StringBuffer + (itemResolver.generateItemURL + (state, item, section, CMSDispatcher.PREVIEW)); + + // Cheesy code should be fixed + + final String sep; + if (url.toString().indexOf('?') == -1) { + sep = "?"; + } else { + sep = "&"; + } + + // TODO: fix this + //url.append(sep).append + // (HistoryCollection.TRANS_ID).append("="); + url.append(sep).append("transID").append("="); + url.append(transID.toString()); + + final Link link = new Link + (new Label(gz("cms.ui.item.revision.view")), + url.toString()); + + link.setTargetFrame(lz("cms.ui.item.revision.view")); + + return link; + } + } + } + + class RollbackActionListener extends TableActionAdapter { + public final void cellSelected(final TableActionEvent e) { + final PageState state = e.getPageState(); + + if (e.getColumn().intValue() + == RevisionTableModelBuilder.ROLLBACK) { + final ContentItem item = m_item.getContentItem(state); + + Versions.rollback + (item.getOID(), + new BigInteger(e.getRowKey().toString())); + + item.applyTag(lz("cms.ui.item.revision.rolled_back")); + } + } + } + + class RollbackCellRenderer implements TableCellRenderer { + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, final int column) { + if (key instanceof String) { + return new NullComponent(); + } else { + return new ControlLink + (new Label(gz("cms.ui.item.revision.rollback"))); + } + } + } + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/templates/ItemTemplates.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/templates/ItemTemplates.java.todo new file mode 100755 index 000000000..183d8af08 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/templates/ItemTemplates.java.todo @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.templates; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.SingleSelectionModel; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.event.TableActionAdapter; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.cms.CMS; +import org.librecms.contentsection.ContentItem; + +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentType; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.Template; +import com.arsdigita.cms.TemplateCollection; +import com.arsdigita.cms.TemplateManagerFactory; +import com.arsdigita.cms.ui.SecurityPropertyEditor; +import com.arsdigita.cms.ui.workflow.WorkflowLockedComponentAccess; +import com.arsdigita.toolbox.ui.LayoutPanel; +import com.arsdigita.util.Assert; + +/** + * This component will eventually contain the full templates UI for + * content items. It is just a placeholder for now. + * + * @author Stanislav Freidin + * @author Jens Pelzetter + */ +public class ItemTemplates extends SecurityPropertyEditor { + private ItemSelectionModel m_itemModel; + + public static final String ASSIGN_TEMPLATE = "assignTemplate"; + + /** + * Construct a new ItemTemplates component + * + * @param model the ItemSelectionModel that will supply the + * current content item + */ + public ItemTemplates(ItemSelectionModel model) { + super(); + m_itemModel = model; + + ItemTemplatesListingImpl l = new ItemTemplatesListingImpl(model); + + final LayoutPanel layout = new LayoutPanel(); + setDisplayComponent(layout); + + SegmentedPanel st = new SegmentedPanel(); + layout.setBody(st); + + st.addSegment(new Label(GlobalizationUtil.globalize("cms.ui.templates.assigned_templates")), l); + + SegmentedPanel sa = new SegmentedPanel(); + Label assignLabel = new Label(GlobalizationUtil.globalize("cms.ui.templates.dummy")); + assignLabel.addPrintListener(new PrintListener() { + public void prepare(PrintEvent e) { + PageState s = e.getPageState(); + Label targetLabel = (Label)e.getTarget(); + ContentPage item = (ContentPage)m_itemModel.getSelectedItem(s); + Assert.exists(item, "item"); + targetLabel.setLabel( (String) GlobalizationUtil.globalize("cms.ui.templates.assign_a_template_to").localize() + item.getTitle()); + } + }); + sa.addSegment(assignLabel, + new AvailableTemplatesListing(l.getRowSelectionModel())); + addComponent(ASSIGN_TEMPLATE, sa); + } + + /** + * Displays a list of templates which are currently assigned to + * the current item + */ + protected class ItemTemplatesListingImpl extends ItemTemplatesListing { + + private WorkflowLockedComponentAccess m_access; + + public ItemTemplatesListingImpl(ItemSelectionModel model) { + super(model); + m_access = new WorkflowLockedComponentAccess(null, model); + } + + public void assignLinkClicked(PageState s, + ContentItem item, + String useContext) { + showComponent(s, ASSIGN_TEMPLATE); + } + + public void register(Page p) { + super.register(p); + // Hide action columns if user has no access + p.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final PageState state = e.getPageState(); + + if (state.isVisibleOnPage(ItemTemplates.this)) { + if (m_access.canAccess + (state, + CMS.getContext().getSecurityManager())) { + getRemoveColumn().setVisible(state, true); + getAssignColumn().setVisible(state, true); + } else { + getRemoveColumn().setVisible(state, false); + getAssignColumn().setVisible(state, false); + } + } + } + }); + } + } + + /** + * Displays a list of templates for the given content item in the + * given context, along with a link to select a template + */ + protected class AvailableTemplatesListing extends TemplatesListing { + + TableColumn m_assignCol; + SingleSelectionModel m_contextModel; + + /** + * Construct a new AvailableTemplatesListing + * + * @param contextModel the SingleSelectionModel that will define the + * current use context + */ + public AvailableTemplatesListing(SingleSelectionModel contextModel) { + super(); + m_contextModel = contextModel; + + // Add the "assign" column and corresponding action listener + m_assignCol = addColumn("Assign", + TemplateCollection.TEMPLATE, false, + new AssignCellRenderer()); + + addTableActionListener(new TableActionAdapter() { + public void cellSelected(TableActionEvent e) { + PageState s = e.getPageState(); + TemplatesListing l = (TemplatesListing)e.getSource(); + int i = e.getColumn().intValue(); + TableColumn c = l.getColumnModel().get(i); + + // Safe to check pointer equality since the column is + // created statically + if(c == m_assignCol) { + SectionTemplateMapping m = + (SectionTemplateMapping)getMappingModel().getSelectedObject(s); + assignTemplate(s, m.getTemplate()); + } + } + }); + } + + /** + * Get all the templates for the given context in the current section + */ + protected TemplateCollection getTemplateCollection(PageState s) { + ContentSection sec = CMS.getContext().getContentSection(); + + ContentItem item = m_itemModel.getSelectedItem(s); + Assert.exists(item, "item"); + + ContentType type = item.getContentType(); + Assert.exists(type, "content type"); + + MimeType mimeType = getMimeType(s); + TemplateCollection c = + TemplateManagerFactory.getInstance().getTemplates(sec, type); + if (mimeType != null) { + c.addEqualsFilter(TemplateCollection.TEMPLATE + "." + + Template.MIME_TYPE + "." + + MimeType.MIME_TYPE, mimeType.getMimeType()); + } + c.addEqualsFilter(TemplateCollection.USE_CONTEXT, + getUseContext(s)); + return c; + } + + // TODO: this is a 100% convoluted interdependent mess that + // really needs to be reworked + /** + * Get the currently selected use context + */ + protected String getUseContext(PageState s) { + String c = (String)m_contextModel.getSelectedKey(s); + Assert.exists(c, "use context"); + return ItemTemplatesListing.getUseContextFromKey(c); + } + + protected MimeType getMimeType(PageState s) { + String key = (String)m_contextModel.getSelectedKey(s); + return ItemTemplatesListing.getMimeTypeFromKey(key); + } + + /** + * Assign a template to the current item + */ + public void assignTemplate(PageState s, Template t) { + ContentItem item = m_itemModel.getSelectedItem(s); + Assert.exists(item, "item"); + + TemplateManagerFactory.getInstance() + .addTemplate(item, t, getUseContext(s)); + + showDisplayPane(s); + } + + /** + * Render the "assign" link + */ + protected class AssignCellRenderer implements TableCellRenderer { + + private ControlLink m_link; + + public AssignCellRenderer() { + m_link = new ControlLink + (new Label(GlobalizationUtil.globalize + ("cms.ui.templates.assign_this_template"))); + m_link.setClassAttr("assignTemplateLink"); + } + + public Component getComponent(Table table, PageState state, Object value, + boolean isSelected, Object key, + int row, int column) { + return m_link; + } + } + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/BaseWorkflowItemPane.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/BaseWorkflowItemPane.java index 3e968b315..28bb14015 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/BaseWorkflowItemPane.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/BaseWorkflowItemPane.java @@ -40,8 +40,6 @@ import org.libreccm.security.User; import com.arsdigita.toolbox.ui.ActionGroup; import com.arsdigita.toolbox.ui.PropertyList; import com.arsdigita.toolbox.ui.Section; -import com.arsdigita.util.UncheckedWrapperException; -import com.arsdigita.web.Web; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.security.Shiro; @@ -49,48 +47,45 @@ import org.libreccm.workflow.Task; import org.libreccm.workflow.TaskManager; import org.libreccm.workflow.TaskRepository; import org.libreccm.workflow.Workflow; -import org.libreccm.workflow.WorkflowManager; -import org.librecms.CmsConstants; import org.librecms.contentsection.privileges.AdminPrivileges; import org.librecms.workflow.CmsTaskType; -import java.math.BigDecimal; import java.util.List; import java.util.Locale; abstract class BaseWorkflowItemPane extends BaseItemPane { - final WorkflowRequestLocal m_workflow; - final TaskRequestLocal m_task; + final WorkflowRequestLocal workflowRequestLocal; + final TaskRequestLocal taskRequestLocal; - ActionGroup m_actionGroup; - final TaskTable m_tasks; + ActionGroup actionGroup; + final TaskTable taskTable; - final SimpleContainer m_detailPane; - final TaskItemPane m_taskPane; - final SummarySection m_summarySection; + final SimpleContainer detailPane; + final TaskItemPane taskItemPane; + final SummarySection summarySection; public BaseWorkflowItemPane(final WorkflowRequestLocal workflow, final ActionLink editLink, final ActionLink deleteLink) { - m_workflow = workflow; + workflowRequestLocal = workflow; - m_tasks = new TaskTable(); - m_task = new TaskSelectionRequestLocal(); + taskTable = new TaskTable(); + taskRequestLocal = new TaskSelectionRequestLocal(); - m_detailPane = new SimpleContainer(); + detailPane = new SimpleContainer(); // Tasks final FinishLink taskFinishLink = new FinishLink(); final ActionLink taskAddLink = new ActionLink(new Label(gz( "cms.ui.workflow.task.add"))); - final TaskAddForm taskAddForm = new TaskAddForm(m_workflow, m_tasks + final TaskAddForm taskAddForm = new TaskAddForm(workflowRequestLocal, taskTable .getRowSelectionModel()); final ActionLink taskEditLink = new ActionLink(new Label(gz( "cms.ui.workflow.task.edit"))); - final TaskEditForm taskEditForm = new TaskEditForm(m_workflow, m_task); + final TaskEditForm taskEditForm = new TaskEditForm(workflowRequestLocal, taskRequestLocal); final ActionLink taskDeleteLink = new ActionLink(new Label(gz( "cms.ui.workflow.task.delete"))); @@ -100,31 +95,31 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { "cms.ui.workflow.task.return"))); backLink.addActionListener(new ResetListener()); - m_taskPane = new TaskItemPane(m_workflow, m_task, + taskItemPane = new TaskItemPane(workflowRequestLocal, taskRequestLocal, taskFinishLink, taskEditLink, taskDeleteLink, backLink); - m_summarySection = new SummarySection(editLink, deleteLink); - m_detailPane.add(m_summarySection); - m_detailPane.add(new TaskSection(taskAddLink)); + summarySection = new SummarySection(editLink, deleteLink); + detailPane.add(summarySection); + detailPane.add(new TaskSection(taskAddLink)); - add(m_detailPane); - setDefault(m_detailPane); - add(m_taskPane); + add(detailPane); + setDefault(detailPane); + add(taskItemPane); add(taskAddForm); add(taskEditForm); add(taskDeleteForm); - connect(m_tasks, 0, m_taskPane); + connect(taskTable, 0, taskItemPane); connect(taskAddLink, taskAddForm); - connect(taskAddForm, m_taskPane); + connect(taskAddForm, taskItemPane); connect(taskEditLink, taskEditForm); connect(taskEditForm); connect(taskDeleteLink, taskDeleteForm); - connect(taskDeleteForm, m_detailPane); + connect(taskDeleteForm, detailPane); } protected class AdminVisible extends VisibilityComponent { @@ -146,7 +141,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { @Override public final boolean isVisible(final PageState state) { - final CmsTask task = m_task.getTask(state); + final CmsTask task = taskRequestLocal.getTask(state); final User lockingUser = task.getLockingUser(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final Shiro shiro = cdiUtil.findBean(Shiro.class); @@ -165,7 +160,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final TaskManager taskManager = cdiUtil.findBean(TaskManager.class); - final Task task = m_task.getTask(state); + final Task task = taskRequestLocal.getTask(state); taskManager.finish(task); } @@ -177,7 +172,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { public void reset(final PageState state) { super.reset(state); - m_tasks.getRowSelectionModel().clearSelection(state); + taskTable.getRowSelectionModel().clearSelection(state); } private class TaskDeleteForm extends BaseDeleteForm { @@ -197,10 +192,10 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { final TaskRepository taskRepo = cdiUtil.findBean( TaskRepository.class); - final Task task = m_task.getTask(state); + final Task task = taskRequestLocal.getTask(state); taskRepo.delete(task); - m_tasks.getRowSelectionModel().clearSelection(state); + taskTable.getRowSelectionModel().clearSelection(state); } } @@ -209,7 +204,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { @Override protected final Object initialValue(final PageState state) { - final String key = m_tasks.getRowSelectionModel().getSelectedKey( + final String key = taskTable.getRowSelectionModel().getSelectedKey( state).toString(); return CmsTaskType.valueOf(key); @@ -223,13 +218,13 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { final ActionLink deleteLink) { setHeading(new Label(gz("cms.ui.workflow.details"))); - m_actionGroup = new ActionGroup(); - setBody(m_actionGroup); + actionGroup = new ActionGroup(); + setBody(actionGroup); - m_actionGroup.setSubject(new Properties()); - m_actionGroup.addAction(new AdminVisible(editLink), + actionGroup.setSubject(new Properties()); + actionGroup.addAction(new AdminVisible(editLink), ActionGroup.EDIT); - m_actionGroup.addAction(new AdminVisible(deleteLink), + actionGroup.addAction(new AdminVisible(deleteLink), ActionGroup.DELETE); } @@ -239,7 +234,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { protected final List properties(final PageState state) { @SuppressWarnings("unchecked") final List props = super.properties(state); - final Workflow workflow = (Workflow) m_workflow.get(state); + final Workflow workflow = (Workflow) workflowRequestLocal.get(state); final KernelConfig kernelConfig = KernelConfig.getConfig(); final Locale defaultLocale = kernelConfig.getDefaultLocale(); @@ -269,7 +264,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { final ActionGroup group = new ActionGroup(); setBody(group); - group.setSubject(m_tasks); + group.setSubject(taskTable); group.addAction(new AdminVisible(taskAddLink), ActionGroup.ADD); } @@ -286,7 +281,7 @@ abstract class BaseWorkflowItemPane extends BaseItemPane { private class TaskTable extends Table { public TaskTable() { - super(new TaskTableModelBuilder(m_workflow), s_columns); + super(new TaskTableModelBuilder(workflowRequestLocal), s_columns); setEmptyView(new Label(gz("cms.ui.workflow.task.none"))); diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowAdminPane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowAdminPane.java.todo new file mode 100755 index 000000000..29f106e0a --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowAdminPane.java.todo @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.cms.ui.BaseItemPane; +import com.arsdigita.toolbox.ui.LayoutPanel; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.workflow.WorkflowRepository; + +/** + * Panel for applying a workflow template to a content item. By + * default this panel display a list of workflows that can be applied + * to the content item. If a workflow is applied, it displays the item + * details page. + * + * @author Uday Mathur + * @author Michael Pih + * @author Justin Ross <jross@redhat.com> + * @author Jens Pelzetter + */ +public class ItemWorkflowAdminPane extends BaseItemPane { + + private final ParameterSingleSelectionModel m_model; + private final WorkflowRequestLocal m_workflow; + + private final LayoutPanel m_detailPane; + private final LayoutPanel m_selectPane; + + public ItemWorkflowAdminPane(final LongParameter itemIdParameter) { + m_model = new ItemWorkflowSelectionModel(itemIdParameter); + m_workflow = new SelectionRequestLocal(); + + final ActionLink editLink = new ActionLink + (new Label(gz("cms.ui.workflow.edit"))); + + final WorkflowEditForm editForm = new WorkflowEditForm(m_workflow); + + final ActionLink deleteLink = new ActionLink + (new Label(gz("cms.ui.workflow.delete"))); + + final WorkflowDeleteForm deleteForm = new WorkflowDeleteForm + (m_workflow); + + m_detailPane = new LayoutPanel(); + m_detailPane.setBody(new ItemWorkflowItemPane + (m_workflow, editLink, deleteLink)); + + final ItemWorkflowSelectForm workflowSelectForm = + new ItemWorkflowSelectForm(); + + m_selectPane = new LayoutPanel(); + m_selectPane.setBody(workflowSelectForm); + + add(m_detailPane); + setDefault(m_detailPane); + add(m_selectPane); + add(editForm); + add(deleteForm); + + connect(workflowSelectForm, m_detailPane); + connect(editLink, editForm); + connect(deleteLink, deleteForm); + connect(deleteForm, m_selectPane); + } + + private class SelectionRequestLocal extends WorkflowRequestLocal { + @Override + protected final Object initialValue(final PageState state) { + final String workflowId = m_model.getSelectedKey(state).toString(); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowRepository workflowRepo = cdiUtil.findBean(WorkflowRepository.class); + return workflowRepo.findById(Long.parseLong(workflowId)); + } + } + + @Override + public final void register(final Page page) { + super.register(page); + + page.addActionListener(new ActionListener() { + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + + if (state.isVisibleOnPage(ItemWorkflowAdminPane.this) + && m_model.getSelectedKey(state) == null) { + push(state, m_selectPane); + } + } + }); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowItemPane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowItemPane.java.todo new file mode 100755 index 000000000..2f1881400 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowItemPane.java.todo @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.cms.CMS; +import org.librecms.workflow.CmsTask; +import com.arsdigita.web.Web; +import org.libreccm.workflow.Workflow; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.libreccm.workflow.TaskRepository; +import org.libreccm.workflow.WorkflowManager; +import org.libreccm.workflow.WorkflowState; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.privileges.AdminPrivileges; + +/** + * @author Justin Ross <jross@redhat.com> + * @author Jens Pelzetter + */ +final class ItemWorkflowItemPane extends BaseWorkflowItemPane { + + private final AssignedTaskTable assignedTaskTable; + + public ItemWorkflowItemPane(final WorkflowRequestLocal workflowRequestLocal, + final ActionLink editLink, + final ActionLink deleteLink) { + + super(workflowRequestLocal, editLink, deleteLink); + + actionGroup.addAction(new AdminVisible(new StartLink())); + actionGroup.addAction(new AdminVisible(new StopLink())); + + assignedTaskTable = new AssignedTaskTable(workflowRequestLocal); + detailPane.add(new AssignedTaskSection(workflowRequestLocal, + assignedTaskTable)); + + final TaskFinishForm taskFinishForm = new TaskFinishForm( + new TaskSelectionRequestLocal()); + add(taskFinishForm); + + connect(assignedTaskTable, 2, taskFinishForm); + connect(taskFinishForm); + } + + private final class TaskSelectionRequestLocal extends TaskRequestLocal { + + @Override + protected final Object initialValue(final PageState state) { + final String taskId = assignedTaskTable.getRowSelectionModel(). + getSelectedKey(state).toString(); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final TaskRepository taskRepo = cdiUtil.findBean( + TaskRepository.class); + + return (CmsTask) taskRepo.findById(Long.parseLong(taskId)); + } + + } + + private boolean hasAdmin(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + + final ContentSection section = CMS.getContext().getContentSection(); + + return permissionChecker.isPermitted( + AdminPrivileges.ADMINISTER_WORKFLOW, section); + + } + + private class StopLink extends ActionLink { + + StopLink() { + super(new Label(gz("cms.ui.item.workflow.stop"))); + + addActionListener(new Listener()); + } + + @Override + public final boolean isVisible(final PageState state) { + final Workflow workflow = workflowRequestLocal.getWorkflow(state); + + return workflow.getState() == WorkflowState.STARTED; + } + + private class Listener implements ActionListener { + + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + + if (hasAdmin(state)) { + final Workflow workflow = workflowRequestLocal.getWorkflow( + state); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean( + WorkflowManager.class); + workflowManager.stop(workflow); + } + } + + } + + } + + private class StartLink extends ActionLink { + + StartLink() { + super(new Label(gz("cms.ui.item.workflow.start"))); + + addActionListener(new Listener()); + } + + @Override + public final boolean isVisible(final PageState state) { + final Workflow workflow = workflowRequestLocal.getWorkflow(state); + + // Start link should be visible if the workflow state is stopped + // or init + return workflow.getState() == WorkflowState.STOPPED + || workflow.getState() == WorkflowState.INIT; + } + + private class Listener implements ActionListener { + + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + + if (hasAdmin(state)) { + final Workflow workflow = workflowRequestLocal.getWorkflow( + state); + + final CdiUtil cdiUtil =CdiUtil.createCdiUtil(); + final WorkflowManager workflowManager = cdiUtil.findBean( + WorkflowManager.class); + workflowManager.start(workflow); + } + } + + } + + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectForm.java new file mode 100755 index 000000000..e7b651b51 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectForm.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.form.RadioGroup; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ui.CMSForm; +import com.arsdigita.web.Web; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowTemplate; +import org.apache.logging.log4j.Logger; + +import java.math.BigDecimal; +import java.util.TooManyListenersException; +import org.apache.logging.log4j.LogManager; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.workflow.WorkflowManager; +import org.libreccm.workflow.WorkflowTemplateRepository; +import org.librecms.contentsection.ContentItem; + +/** + * This panel displays a radio group of available Workflow Templates in this + * content section that can be applied to this item. + * + * @author Uday Mathur + * @author Jens Pelzetter + */ +class ItemWorkflowSelectForm extends CMSForm { + + private static final Logger LOGGER = LogManager.getLogger( + ItemWorkflowSelectForm.class); + + private RadioGroup radioGroup; + + public ItemWorkflowSelectForm() { + super("applyWorkflow", new SimpleContainer()); + + addFormWidgets(); + addProcessListener(new ProcessListener()); + } + + protected void addFormWidgets() { + radioGroup = new RadioGroup(new BigDecimalParameter("workflowSelect")); + radioGroup.setClassAttr("vertical"); + + try { + radioGroup.addPrintListener(new WorkflowsOptionPrintListener()); + } catch (TooManyListenersException t) { + LOGGER.error("Too many listeners", t); + } + + radioGroup.addValidationListener(new NotNullValidationListener()); + add(radioGroup); + + add(new Submit("apply_wf", "Apply Workflow")); + } + + /** + * Adds a FormProcessListener to that applies a clone of the + * WorkflowTemplate to this ContentItem. In case of double-click, no change + * is made. + */ + private class ProcessListener implements FormProcessListener { + + @Override + public final void process(final FormSectionEvent event) + throws FormProcessException { + + final PageState state = event.getPageState(); + final Long flowId = (Long) radioGroup.getValue(state); + + final ContentItem item = CMS.getContext().getContentItem(); + + if (item.getWorkflow() == null) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowTemplateRepository templateRepo = cdiUtil. + findBean(WorkflowTemplateRepository.class); + final WorkflowManager workflowManager = cdiUtil.findBean( + WorkflowManager.class); + + final WorkflowTemplate template = templateRepo.findById(flowId); + workflowManager.createWorkflow(template, item); + } + } + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectionModel.java new file mode 100755 index 000000000..b1c9654ba --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/ItemWorkflowSelectionModel.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.cms.ItemSelectionModel; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemRepository; + +class ItemWorkflowSelectionModel extends ParameterSingleSelectionModel { + + private final ItemSelectionModel itemSelectionModel; + + public ItemWorkflowSelectionModel(final LongParameter itemIdParameter) { + super(itemIdParameter); + itemSelectionModel = new ItemSelectionModel(itemIdParameter); + } + + public ItemWorkflowSelectionModel( + final ItemSelectionModel itemSelectionModel) { + super(itemSelectionModel.getStateParameter()); + this.itemSelectionModel = itemSelectionModel; + } + + @Override + public Object getSelectedKey(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemRepository itemRepo = cdiUtil.findBean(ContentItemRepository.class); + final ContentItem item = itemRepo.findById((Long) super.getSelectedKey( + state)); + + return item.getWorkflow().getWorkflowId(); + } + + public ItemSelectionModel getItemSelectionModel() { + return itemSelectionModel; + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/TaskFinishForm.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/TaskFinishForm.java.todo index f9524027e..3aa639925 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/TaskFinishForm.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/TaskFinishForm.java.todo @@ -179,7 +179,6 @@ public final class TaskFinishForm extends CommentAddForm { final TaskManager taskManager = cdiUtil.findBean(TaskManager.class); final AssignableTaskManager assignableTaskManager = cdiUtil .findBean(AssignableTaskManager.class); - final Shiro shiro = cdiUtil.findBean(Shiro.class); final ConfigurationManager confManager = cdiUtil.findBean( ConfigurationManager.class); final KernelConfig kernelConfig = confManager.findConfiguration( @@ -221,7 +220,6 @@ public final class TaskFinishForm extends CommentAddForm { final TaskFinishFormController controller = cdiUtil.findBean( TaskFinishFormController.class); final Workflow workflow = task.getWorkflow(); - final User user = shiro.getUser(); final List tasks = controller.findEnabledTasks( workflow); for (final AssignableTask currentTask : tasks) { @@ -243,7 +241,7 @@ public final class TaskFinishForm extends CommentAddForm { throw new RedirectSignal( URL.there(state.getRequest(), ContentItemPage.getItemURL( - item, + item.get(), ContentItemPage.PUBLISHING_TAB)), true); } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowDeleteForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowDeleteForm.java new file mode 100755 index 000000000..ec81491b7 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowDeleteForm.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.cms.ui.BaseDeleteForm; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowRepository; + +/* + * @author Justin Ross <jross@redhat.com> + * @version $Id: WorkflowDeleteForm.java 287 2005-02-22 00:29:02Z sskracic $ + */ + +class WorkflowDeleteForm extends BaseDeleteForm { + + final WorkflowRequestLocal m_workflow; + + WorkflowDeleteForm(final WorkflowRequestLocal workflow) { + super(new Label(gz("cms.ui.workflow.delete_prompt"))); + + m_workflow = workflow; + } + + @Override + public final void process(final FormSectionEvent event) + throws FormProcessException { + + final Workflow workflow = m_workflow.getWorkflow(event.getPageState()); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final WorkflowRepository workflowRepo = cdiUtil.findBean(WorkflowRepository.class); + workflowRepo.delete(workflow); + } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowsOptionPrintListener.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowsOptionPrintListener.java new file mode 100755 index 000000000..06c146ef9 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/workflow/WorkflowsOptionPrintListener.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.workflow; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.cms.CMS; +import com.arsdigita.kernel.KernelConfig; +import java.util.List; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentSection; +import org.libreccm.workflow.Task; +import org.libreccm.workflow.WorkflowTemplate; + +/** + * Builds a list of workflow templates registered to the current content + * section. + * + * @author Uday Mathur (umathur@arsdigita.com) + * @author Jens Pelzetter + */ +public class WorkflowsOptionPrintListener implements PrintListener { + + protected List getCollection(final PageState state) { + final ContentSection section = getContentSection(state); + + return section.getWorkflowTemplates(); + } + + protected ContentSection getContentSection(final PageState state) { + return CMS.getContext().getContentSection(); + } + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + + final OptionGroup target = (OptionGroup) event.getTarget(); + target.clearOptions(); + + final List templates = getCollection(state); + + for (final WorkflowTemplate template : templates) { + target.addOption(new Option( + Long.toString(template.getWorkflowId()), + template.getName().getValue(KernelConfig + .getConfig() + .getDefaultLocale()))); + } + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java index 25e17367b..bf105e687 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemL10NManager.java @@ -308,7 +308,7 @@ public class ContentItemL10NManager { */ @AuthorizationRequired @Transactional(Transactional.TxType.REQUIRED) - public void removeLangauge( + public void removeLanguage( @RequiresPrivilege(ItemPrivileges.EDIT) final ContentItem item, final Locale locale) { @@ -330,7 +330,7 @@ public class ContentItemL10NManager { } findLocalizedStringProperties(item) - .forEach(property -> removeLanguage(item, locale, property)); + .forEach(property -> ContentItemL10NManager.this.removeLanguage(item, locale, property)); itemRepo.save(item); } diff --git a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java index 9a7e8d259..c44582951 100644 --- a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java +++ b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemL10NManagerTest.java @@ -299,7 +299,7 @@ public class ContentItemL10NManagerTest { /** * Tries to remove language from a content item by using - * {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale)}. + * {@link ContentItemL10NManager#removeLanguage(org.librecms.contentsection.ContentItem, java.util.Locale)}. */ @Test @InSequence(70) @@ -313,12 +313,12 @@ public class ContentItemL10NManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - l10nManager.removeLangauge(item.get(), Locale.FRENCH); + l10nManager.removeLanguage(item.get(), Locale.FRENCH); } /** * Verifies that - * {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale)} + * {@link ContentItemL10NManager#removeLanguage(org.librecms.contentsection.ContentItem, java.util.Locale)} * has not effect if called for not present language. */ @Test @@ -332,12 +332,12 @@ public class ContentItemL10NManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - l10nManager.removeLangauge(item.get(), Locale.GERMAN); + l10nManager.removeLanguage(item.get(), Locale.GERMAN); } /** * Verifies that - * {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale)} + * {@link ContentItemL10NManager#removeLanguage(org.librecms.contentsection.ContentItem, java.util.Locale)} * throws an {@link IllegalArgumentException} if called with {@code null} * for the item. */ @@ -352,12 +352,12 @@ public class ContentItemL10NManagerTest { public void removeLanguageItemIsNull() { final ContentItem item = null; - l10nManager.removeLangauge(item, Locale.GERMAN); + l10nManager.removeLanguage(item, Locale.GERMAN); } /** * Verifies that - * {@link ContentItemL10NManager#removeLangauge(org.librecms.contentsection.ContentItem, java.util.Locale)} + * {@link ContentItemL10NManager#removeLanguage(org.librecms.contentsection.ContentItem, java.util.Locale)} * throws an {@link IllegalArgumentException} if called with {@code null} * for the language. */ @@ -373,7 +373,7 @@ public class ContentItemL10NManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - l10nManager.removeLangauge(item.get(), null); + l10nManager.removeLanguage(item.get(), null); } diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditor.java b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditor.java new file mode 100755 index 000000000..ffe48d1ad --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditor.java @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.bebop; + +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ChangeEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.ChangeListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.list.DefaultListCellRenderer; +import com.arsdigita.bebop.list.ListModelBuilder; +import com.arsdigita.bebop.list.ListModel; +import com.arsdigita.bebop.util.GlobalizationUtil; +import com.arsdigita.util.Assert; +import com.arsdigita.util.LockableImpl; +import com.arsdigita.util.SequentialMap; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.form.FormErrorDisplay; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.globalization.GlobalizedMessage; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +/** + * Maintains a set of forms that are used when editing the + * properties of some object. The component maintains a single + * display pane and a list of forms that are selectable by links. + *

+ * By default, the component looks something like this: + *


+ * +----------------+
+ * |                |
+ * |  Display Pane  |
+ * |                |
+ * +----------------+
+ * [link to form1]
+ * [link to form2]
+ * [link to form3]
+ * 
+ * + * When the user clicks on a link, the display pane is hidden and the + * corresponding form is shown. + * + *

+ * Enter foo: [           ]
+ * Enter bar: [           ]
+ *
+ *          [Save] [Cancel]
+ * 
+ * + * When the user clicks the Save or Cancel button on the form, the form + * is hidden and the display pane (with its list of links) + * is shown once again. + *

+ * The simple usage pattern for this class is as follows: + * + *


+ * PropertyEditor editor = new PropertyEditor();
+ * editor.setDisplayComponent(new FooComponent());
+ * NameEditForm n = new NameEditForm();
+ * editor.add("name", "Edit Name", n, n.getCancelButton());
+ * AddressEditForm a = new AddressEditForm();
+ * editor.add("address", "Edit Address", a, a.getCancelButton());
+ * 
+ * + * The PropertyEditor will automatically add the right + * listeners to the forms. + *

+ * This class is used extensively in the default authoring kit steps, + * especially PageEdit and TextPageBody in CMS. + *

+ * Advanced Usage:
+ * The PropertyEditor may be used to maintain + * visibility of any components, not just forms. The + * {@link #addComponent(String, String, Component)} method + * can be used to add an arbitrary component to the editor. The + * component will be shown in the list of links, along with other components + * and/or forms. The component will be shown as usual when the user clicks + * on a link. However, you must be sure to include a call to + * {@link #showDisplayPane(PageState)} when the component needs to be hidden. + *

+ * In addition, it is possible to manually generate {@link ActionLink}s + * that will display the right components in the editor. The + * {@link #addComponent(String, Component)} method can be used to add + * a component to the PropertyEditor without automatically + * generating the link for it. The {@link #addVisibilityListener(ActionLink, String)} + * method can then be used to add an appropriate {@link ActionListener} to any + * {@link ActionLink}. For example: + * + *

// Add a form
+ * Form fooForm = new FooForm();
+ * editor.addComponent(FOO_FORM, fooForm);
+ * editor.addListeners(fooForm, fooForm.getCancelButton());
+ * // Create a link that shows the form
+ * fooLink = new ActionLink("Edit the Foo property");
+ * editor.addVisibilityListener(fooLink, FOO_FORM);
+ * + * Note that the visibility of the form will be handled automatically. There + * is no need to show or hide it manually. This approach allows + * greater flexibility in placing links on a page. The links may be + * part of the editor's display pane, but they do not have to be. + *

+ * More-advanced Usage:
+ * The PropertyEditor is backed by a + * {@link PropertyEditorModel} through a {@link PropertyEditorModelBuilder}. + * Therefore, the PropertyEditor is a model-backed component, + * as described in the Bebop tutorials. This means that the list + * of properties for the editor could be generated dynamically during + * each request. The {@link #setModelBuilder(PropertyEditorModelBuilder)} method + * can be used to set a specialized {@link PropertyEditorModelBuilder} for the + * editor. In order to write the model builder, you may choose to extend the + * protected inner classes {@link PropertyEditor.DefaultModelBuilder} and + * {@link PropertyEditor.DefaultModel}. It is also possible to write the model + * builder and the corresponding model from scratch. However, most people won't + * need to do this. + *

+ * For example, SecurityPropertyEditor uses a custom + * {@link PropertyEditorModelBuilder} in order to hide the links for properties + * which the web user is not allowed to edit. + *

+ * + * @author Stanislav Freidin + * @version $Id: PropertyEditor.java 1638 2007-09-17 11:48:34Z chrisg23 $ + */ +public class PropertyEditor extends SimpleContainer { + + private SequentialMap m_forms; + private SequentialMap m_labels; + private Component m_display; + private Container m_displayPane; + private List m_list; + private PropertyEditorModelBuilder m_builder; + private RequestLocal m_model; + private java.util.List m_additionalDisplayComponents = new ArrayList(); + + /** + * Constructs a new, empty PropertyEditor. + * The {@link #setDisplayComponent(Component)} method must be called before + * this component is locked. + */ + public PropertyEditor() { + this(null); + } + + /** + * Constructs a new PropertyEditor with the given + * display component. The pane defaults to a {@link + * com.arsdigita.bebop.SimpleContainer}. + * + * @param display the display component + */ + public PropertyEditor(Component display) { + this(display, new SimpleContainer()); + } + + /** + * Constructs a new PropertyEditor with the given + * display component and display pane. + * + * @param display the display component + * @param pane the display pane. The caller should pass in a + * freshly allocated Container. + */ + public PropertyEditor(Component display, Container pane) { + super(); + setClassAttr("propertyEditor"); + m_forms = new SequentialMap(); + m_labels = new SequentialMap(); + m_display = null; + + m_displayPane = pane; + super.add(m_displayPane); + + m_list = new List(); + m_list.setCellRenderer(new IdentityCellRenderer()); + + // Change listener: reset visibility + // Should a ComponentSelectionModel be used here instead ? It's tempting, + // but there doesn't seem to be a real need for it + m_list.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + PageState state = e.getPageState(); + + // Get the visible component + Component c = null; + if ( !m_list.isSelected(state) ) { + // Select the display pane + c = m_displayPane; + } else { + c = getComponent(getSelectedComponentKey(state)); + } + + // Iterate over the forms + for(Iterator i = m_forms.values().iterator(); i.hasNext(); ) { + Component f = (Component)i.next(); + f.setVisible(state, (f == c)); + } + + m_displayPane.setVisible(state, (m_displayPane == c)); + } + }); + + // Don't add the list yet; add it when we add the display + // component + + if(display != null) + setDisplayComponent(display); + + // Prepare the model builder + setModelBuilder(new DefaultModelBuilder()); + + m_model = new RequestLocal() { + @Override + protected Object initialValue(PageState s) { + return getModelBuilder().makeModel(PropertyEditor.this, s); + } + }; + } + + /** Set the display component visible by default, and the + * form(s) invisible by default. + */ + @Override + public void register(Page p) { + Assert.exists(m_display, "display component"); + + p.setVisibleDefault(m_displayPane, true); + + for(Iterator i = m_forms.values().iterator(); i.hasNext(); ) { + p.setVisibleDefault((Component)i.next(), false); + } + } + + /** + * Hides the form(s) and shows the display pane. + * + * @param state the page state + */ + public void showDisplayPane(PageState state) { + m_list.clearSelection(state); + } + + /** + * Shows the component that is identified by the specified key. + * + * @param state the page state + * @param key + */ + public void showComponent(PageState state, String key) { + m_list.setSelectedKey(state, key); + } + + /** + * Returns the key of the currently visible component, or null if + * the display pane is currently visible. + * @param state the page state + * + * @return the key of the currently visible component, or null if the + * display pane is visible. + */ + public String getSelectedComponentKey(PageState state) { + return (String)m_list.getSelectedKey(state); + } + + /** + * add an additional component below the list of links + * @param c + */ + public void addDisplayComponent(Component c) { + m_additionalDisplayComponents.add(c); + } + /** + * Adds the display component if it has not been added already. + * + * @param c the display component to add + */ + public void setDisplayComponent(Component c) { + if(m_display != null) { + throw new IllegalStateException("Display component has already been set"); + } + + m_displayPane.add(c); + m_displayPane.add(m_list); + Iterator it = m_additionalDisplayComponents.iterator(); + while (it.hasNext()) { + m_displayPane.add((Component)it.next()); + } + + m_display = c; + } + + /** + * Adds a component to the property editor. The component will be + * completely invisible. It is up to the user to call + * {@link #showComponent(PageState, String)} to display the component and to + * call {@link #showDisplayPane(PageState)} when the component needs to be hidden. + * + * @param key the symbolic key for the component (must be unique + * for this PropertyEditor) + * @param c the component + */ + public void addComponent(String key, Component c) { + m_forms.put(key, c); + super.add(c); + } + + /** + * Adds a component to the list of links. It is up to the component to + * correctly call {@link #showDisplayPane(PageState)} when it is done. + * + * @param key the symbolic key for the component (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param c the component + * @deprecated use addComponent(String,GlobalizedMessage,Component) instead + */ + public void addComponent(String key, String label, Component c) { + addComponent(key, c); + m_labels.put(key, label); + } + + /** + * Adds a component to the list of links. It is up to the component to + * correctly call {@link #showDisplayPane(PageState)} when it is done. + * + * @param key the symbolic key for the component (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param c the component + */ + public void addComponent(String key, GlobalizedMessage label, Component c) { + addComponent(key, c); + m_labels.put(key, label); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param form the form component + * @deprecated use add(String,GlobalizedMessage,Form) instead. + */ + public void add(String key, String label, Form form) { + addComponent(key, label, form); + addProcessListener(form); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param form the form component + */ + public void add(String key, GlobalizedMessage label, Form form) { + addComponent(key, label, form); + addProcessListener(form); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param form the form component + * @param cancelButton the Cancel button on the form + * @deprecated use add(String,GlobalizedMessage,Form,Submit) instead. + */ + public void add(String key, String label, Form form, Submit cancelButton) { + add(key, label, form); + addListeners(form, cancelButton); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param form the form component + * @param cancelButton the Cancel button on the form + */ + public void add(String key, GlobalizedMessage label, Form form, Submit cancelButton) { + add(key, label, form); + addListeners(form, cancelButton); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param formSection the form component + * + * @pre !(formSection instanceof Form) + * @deprecated use add(String,GlobalizedMessage,FormSection) instead. + */ + public void add(String key, String label, FormSection formSection) { + if (formSection instanceof Form) { + throw new IllegalArgumentException("formSection is an instance of Form"); + } + Form form = new Form("property" + key); + form.add(new FormErrorDisplay(form), ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + form.add(formSection); + form.setMethod(Form.POST); + form.setEncType("multipart/form-data"); + add(key, label , form); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param formSection the form component + * + * @pre !(formSection instanceof Form) + */ + public void add(String key, GlobalizedMessage label, FormSection formSection) { + if (formSection instanceof Form) { + throw new IllegalArgumentException("formSection is an instance of Form"); + } + Form form = new Form("property" + key); + form.add(new FormErrorDisplay(form), ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + form.add(formSection); + form.setMethod(Form.POST); + form.setEncType("multipart/form-data"); + add(key, label , form); + } + + /** + * Adds a form to the set of forms that can be used to edit the properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link + * @param formSection the form component + * @param cancelButton the Cancel button on the form + * @deprecated use add(String,GlobalizedMessage,FormSection,Submit) instead. + */ + public void add(String key, + String label, + FormSection formSection, + Submit cancelButton) { + Form form = new Form("property" + key); + form.add(new FormErrorDisplay(form), ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + form.add(formSection); + form.setMethod(Form.POST); + form.setEncType("multipart/form-data"); + add(key, label , form, cancelButton); + } + + /** + * Adds a form to the set of forms that can be used to edit the + * properties. + * + * @param key the symbolic key for the form (must be unique + * for this PropertyEditor) + * @param label the label for the link as a GlobalizedMessage + * @param formSection the form component + * @param cancelButton the Cancel button on the form + */ + public void add(String key, + GlobalizedMessage label, + FormSection formSection, + Submit cancelButton) { + Form form = new Form("property" + key); + form.add(new FormErrorDisplay(form), ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + form.add(formSection); + form.setMethod(Form.POST); + form.setEncType("multipart/form-data"); + add(key, label , form, cancelButton); + } + + /** + * Retrieves the component at the specified key. + * + * @param key the key of the component to retrieve + * @return a component that has been added to this + * PropertyEditor at the specified key, or null + * if no such component exists. + */ + public Component getComponent(String key) { + return (Component)m_forms.get(key); + } + + /** + * Adds a submission listener to the form that will hide all components + * and show the display pane. This method should be used to add + * submission listeners to forms that are buried deep inside some + * component and are not members of this PropertyEditor. + * + * @param form the form + * @param cancelButton the Cancel button on the form + */ + public void addCancelListener(FormSection form, Submit cancelButton) { + // Add a different submission listener for each form since the + // cancel button may be different + final Submit theButton = cancelButton; + + form.addSubmissionListener(new FormSubmissionListener() { + @Override + public void submitted(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + if(theButton.isSelected(state)) { + showDisplayPane(state); + throw new FormProcessException( + "Submission Cancelled", + GlobalizationUtil.globalize("bebop.cancel.msg")); + } + } + }); + } + + /** + * Adds a process listener to the form that will hide all components + * and show the display pane. This method should be used to add + * process listeners to forms that are buried deep inside some + * component and are not members of this PropertyEditor. + * + * @param form the form + */ + public void addProcessListener(FormSection form) { + form.addProcessListener(new FormProcessListener() { + @Override + public void process(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + showDisplayPane(state); + } + }); + } + + /** + * Adds all required listeners to the form to ensure that + * if the form is either submitted successfully or cancelled, + * the display pane will be shown. This method should be used + * to add listeners to forms that are buried deep inside some + * component, and are not members of this PropertyEditor. + * + * @param form the form + * @param cancelButton the Cancel button on the form + */ + public void addListeners(FormSection form, Submit cancelButton) { + addCancelListener(form, cancelButton); + addProcessListener(form); + } + + /** + * This method can be used to add an {@link ActionListener} to any + * {@link ActionLink}, causing the action link to show the specified + * component when it is clicked. For example, this method may be useful + * if the {@link ActionLink} that is supposed to show the component is + * buried somewhere deep within the UI. + * + * @param l the {@link ActionLink} + * @param key the key of the component that will be shown when the link + * is clicked, as specified in the + * {@link #addComponent(String, Component)} method + * @see #addComponent(String, Component) + */ + public void addVisibilityListener(ActionLink l, String key) { + final String t_key = key; + l.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showComponent(e.getPageState(), t_key); + } + }); + } + + /** + * Returns the {@link List} that contains all the links. + * @return the {@link List} that contains all the links. + */ + public List getList() { + return m_list; + } + + /** + * Returns the {@link PropertyEditorModelBuilder} that supplies this + * property editor with its {@link PropertyEditorModel} during each + * request. + * + * @return the {@link PropertyEditorModelBuilder} for this component. + */ + protected final PropertyEditorModelBuilder getModelBuilder() { + return m_builder; + } + + /** + * Sets the {@link PropertyEditorModelBuilder} that will supply this + * property editor with its {@link PropertyEditorModel} during each + * request. + * @param b the property editor model builder + */ + protected final void setModelBuilder(PropertyEditorModelBuilder b) { + Assert.isUnlocked(this); + m_builder = b; + m_list.setModelBuilder(new BuilderAdapter(this)); + } + + /** + * Returns the {@link PropertyEditorModel} in use during the current + * request. + * + * @param s represents the current request + * @return the {@link PropertyEditorModel} that supplies the properties + * for the current request. + */ + protected final PropertyEditorModel getModel(PageState s) { + return (PropertyEditorModel)m_model.get(s); + } + + /** + * Returns the display component. + * @return the display component. + */ + public Component getDisplayComponent() { + return m_display; + } + + /** + * Returns the display pane (component + list). + * @return the display pane (component + list). + */ + public Container getDisplayPane() { + return m_displayPane; + } + + /** + * Returns the map of labels. + * @return the map of labels. + */ + protected SequentialMap getLabelsMap() { + return m_labels; + } + + /** + * Locks this component. + */ + @Override + public void lock() { + getModelBuilder().lock(); + super.lock(); + } + + /** + * Renders the components generated by the model directly + */ + protected static class IdentityCellRenderer extends DefaultListCellRenderer { + + @Override + public Component getComponent(List list, PageState state, Object value, + String key, int index, boolean isSelected) { + return (Component)value; + } + } + + /** + * Default implementation of the {@link PropertyEditorModelBuilder}. + * Takes in a SequentialMap of key->label, and constructs a ControlLink for each + * label. + */ + protected static class DefaultModelBuilder + extends LockableImpl implements PropertyEditorModelBuilder { + + public DefaultModelBuilder() { + super(); + } + + /** + * Return an iterator of all properties of the specified property + * editor. These properties should be passed into the constructor + * of the {@link PropertyEditor.DefaultModel} + * @param p + * @return + */ + protected Iterator getProperties(PropertyEditor p) { + return p.getLabelsMap().entrySet().iterator(); + } + + /** + * Construct a {@link PropertyEditorModel} for the current + * request. + */ + @Override + public PropertyEditorModel makeModel(PropertyEditor p, PageState s) { + return new DefaultModel(getProperties(p)); + } + } + + /** + * Internal class with default implementation of the {@link PropertyEditorModel}. + * Takes in an iterator of key->label pairs, and constructs a ControlLink + * for each label. + */ + protected static class DefaultModel implements PropertyEditorModel { + + protected Iterator m_iter; + protected Map.Entry m_entry; + + public DefaultModel(Iterator iter) { + m_iter = iter; + m_entry = null; + } + + @Override + public boolean next() { + if(!m_iter.hasNext()) { + m_entry = null; + return false; + } + m_entry = (Map.Entry)m_iter.next(); + return true; + } + + /** + * Actually retrieve action link and label. Will be executed at each + * request to ensure proper localization. + * @return + */ + @Override + public Component getComponent() { + Assert.exists(m_entry); + if ( m_entry.getValue() instanceof GlobalizedMessage ) { + ControlLink l = new ControlLink(new + Label((GlobalizedMessage)m_entry.getValue())); + l.setClassAttr("actionLink"); + return l; + } else { + ControlLink l = new ControlLink(new Label((String)m_entry.getValue())); + + l.setClassAttr("actionLink"); + return l; + } + } + + @Override + public Object getKey() { + Assert.exists(m_entry); + return m_entry.getKey(); + } + } + + /** + * Adapts a {@link PropertyEditorModelBuilder} to a {@link ListModelBuilder} + */ + private static final class BuilderAdapter extends LockableImpl + implements ListModelBuilder { + + private final PropertyEditor m_parent; + + public BuilderAdapter(PropertyEditor parent) { + super(); + m_parent = parent; + } + + @Override + public ListModel makeModel(List l, PageState state) { + return new ModelAdapter(m_parent.getModel(state)); + } + } + + /** + * Adapts a {@link PropertyEditorModel} to a {@link ListModel} + */ + private static final class ModelAdapter implements ListModel { + + private final PropertyEditorModel m_model; + + public ModelAdapter(PropertyEditorModel model) { + m_model = model; + } + + @Override + public boolean next() { return m_model.next(); } + @Override + public Object getElement() { return m_model.getComponent(); } + @Override + public String getKey() { return m_model.getKey().toString(); } + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModel.java b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModel.java new file mode 100755 index 000000000..cd3aff8bf --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModel.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.bebop; + +/** + * Provides properties for the {@link PropertyEditor} during + * each request. This class is intended for advanced users only. + * + * @author Stanislav Freidin + * @version $Id: PropertyEditorModel.java 287 2005-02-22 00:29:02Z sskracic $ + * @see PropertyEditorModelBuilder + */ +public interface PropertyEditorModel { + + + /** + * Advances to the next property, if possible. + * + * @return false if there are no more properties; + * true otherwise. + */ + boolean next(); + + /** + * Returns the component that should act as a "button" for editing the + * property. Typically, this method returns a {@link ControlLink} + * of some sort. When the link is clicked, the {@link PropertyEditor} + * will display the pane for editing the property. + * + * @return a component (usually a {@link ControlLink}) that will act + * as the "button" for editing the property. + */ + Component getComponent(); + + /** + * Returns the unique key of the current property, usually + * a simple String. + * + * @return the key of the current property. + */ + Object getKey(); +} diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModelBuilder.java b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModelBuilder.java new file mode 100755 index 000000000..9ccb0d2e0 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/bebop/PropertyEditorModelBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.bebop; + +import com.arsdigita.util.Lockable; + +/** + * Generates a {@link PropertyEditorModel} for the {@link PropertyEditor} + * during each request. This class is intended for advanced users only. + * + * @author Stanislav Freidin + * @version $Id: PropertyEditorModelBuilder.java 287 2005-02-22 00:29:02Z sskracic $ + * @see PropertyEditor + */ +public interface PropertyEditorModelBuilder extends Lockable { + + + /** + * Constructs a {@link PropertyEditorModel} for the current request. + * @param p the parent {@link PropertyEditor} + * @param s represents the current request + * @return the {@link PropertyEditorModel} for the current request. + */ + PropertyEditorModel makeModel(PropertyEditor p, PageState s); + +} diff --git a/ccm-core/src/main/java/com/arsdigita/kernel/ui/ACSObjectSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/kernel/ui/ACSObjectSelectionModel.java index 9a8cdc6a5..567c435c9 100755 --- a/ccm-core/src/main/java/com/arsdigita/kernel/ui/ACSObjectSelectionModel.java +++ b/ccm-core/src/main/java/com/arsdigita/kernel/ui/ACSObjectSelectionModel.java @@ -25,6 +25,7 @@ import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.parameters.ParameterModel; import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.bebop.event.ChangeListener; +import com.arsdigita.bebop.parameters.LongParameter; import com.arsdigita.util.Assert; import com.arsdigita.util.UncheckedWrapperException; @@ -40,20 +41,26 @@ import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObjectRepository; /** - * Loads a subclass of an ACSObject from the database. - * By default, uses a BigDecimal state parameter in order to - * store and retrieve the item id. + * Loads a subclass of an ACSObject from the database. By default, uses a + * BigDecimal {@code Long} state parameter in order to store and + * retrieve the item id. + * + * + * This class has been edited to work with {@link CcmObject} instead of + * {@code ACSObject}. Most methods etc. are the the same. This should work as + * before. Also variable names etc. have been changed to match the common Java + * styles. + * * *

* The getSelectedKey(PageState state) method will return the - * BigDecimal id of the currently selected object. This method - * will return the id even if the object with this id does not - * actually exist. + * BigDecimal id of the currently selected object. This method will return the + * id even if the object with this id does not actually exist. * *

* The getSelectedObject(PageState state) method will return the - * object whose id is getSelectedKey(PageState state). If the - * object does not actually exist, the method will return null + * object whose id is getSelectedKey(PageState state). If the + * object does not actually exist, the method will return null * *

* Thus, it is possible to implement the following pattern: @@ -78,227 +85,225 @@ import org.libreccm.core.CcmObjectRepository; * } *} * - * Naturally, the ACSObjectSelectionModel could also be passed - * in as a parameter in the MyComponent constructor. In this - * case, it should be up to the parent of MyComponent to - * register the model's state parameter. + * Naturally, the ACSObjectSelectionModel could also be passed in + * as a parameter in the MyComponent constructor. In this case, it + * should be up to the parent of MyComponent to register the + * model's state parameter. *

- * Advanced Usage: The ACSObjectSelectionModel - * is actually just a wrapper for a {@link SingleSelectionModel} - * which maintains the currently selected object's ID as a - * {@link BigDecimal}. By default, a new - * {@link ParameterSingleSelectionModel} is wrapped in this way; - * however, any {@link SingleSelectionModel} may be wrapped. - * Thus, it becomes possible to use the ACSObjectSelectionModel - * even if the currently selected ID is not stored in a state parameter. + * Advanced Usage: The ACSObjectSelectionModel is actually + * just a wrapper for a {@link SingleSelectionModel} which maintains the + * currently selected object's ID as a {@link BigDecimal}. By default, a new + * {@link ParameterSingleSelectionModel} is wrapped in this way; however, any + * {@link SingleSelectionModel} may be wrapped. Thus, it becomes possible to use + * the ACSObjectSelectionModel even if the currently selected ID is + * not stored in a state parameter. *

* Persistence Details: The default constructor of * ACSObjectSelectionModel will attempt to use the - * {@link DomainObjectFactory} to automatically instantiate the correct Java + * {@link DomainObjectFactory} to automatically instantiate the correct Java * subclass of {@link ACSObject}. However, it is also possible to use an - * alternate constructor in order to force the ACSObjectSelectionModel - * to manually instantiate the objects: + * alternate constructor in order to force the + * ACSObjectSelectionModel to manually instantiate the objects: * *


- * ACSObjectSelectionModel model = 
- *     new ACSObjectSelectionModel("com.arsdigita.cms.Article", 
+ * ACSObjectSelectionModel model =
+ *     new ACSObjectSelectionModel("com.arsdigita.cms.Article",
  *                                 "com.arsdigita.cms.Article", "item_id");
  * 
* * In this case, the model will attempt to use reflection to instantiate the * correct subclass of ACSObject. In addition, the supplementary - * constructor makes it possible to create new objects in the database - * using the {@link #createACSObject(BigDecimal)} method. + * constructor makes it possible to create new objects in the database using the + * {@link #createACSObject(BigDecimal)} method. * * @see com.arsdigita.bebop.SingleSelectionModel * @see com.arsdigita.bebop.ParameterSingleSelectionModel * * @author Stanislav Freidin - * @version $Id$ + * @author Jens Pelzetter */ public class ACSObjectSelectionModel implements SingleSelectionModel { - private static final Logger s_log = - Logger.getLogger(ACSObjectSelectionModel.class); - - private RequestLocal m_loaded; - private Class m_javaClass; - private Constructor m_constructor; - private String m_objectType; - private SingleSelectionModel m_model; + private RequestLocal loaded; + private Class javaClassName; +// private Constructor constructor; + private String objectType; + private SingleSelectionModel selectionModel; /** - * Construct a new ACSObjectSelectionModel. - * This model will produce instances of ACSObject - * by automatically instantiating the correct Java subclass using - * the {@link DomainObjectFactory}. + * Construct a new ACSObjectSelectionModel. This model will + * produce instances of ACSObject by automatically + * instantiating the correct Java subclass using the + * {@link DomainObjectFactory}. * - * @param parameter The state parameter which should be used to store - * the object ID + * @param parameter The state parameter which should be used to store the + * object ID */ - public ACSObjectSelectionModel(BigDecimalParameter parameter) { + public ACSObjectSelectionModel(final LongParameter parameter) { this(null, null, parameter); } /** - * Construct a new ACSObjectSelectionModel. - * This model will produce instances of ACSObject - * by automatically instantiating the correct Java subclass using - * the {@link DomainObjectFactory}. + * Construct a new ACSObjectSelectionModel. This model will + * produce instances of ACSObject by automatically + * instantiating the correct Java subclass using the + * {@link DomainObjectFactory}. * - * @param parameterName The name of the state parameter which will - * be used to store the object ID. + * @param parameterName The name of the state parameter which will be used + * to store the object ID. */ - public ACSObjectSelectionModel(String parameterName) { - this(null, null, new BigDecimalParameter(parameterName)); + public ACSObjectSelectionModel(final String parameterName) { + this(null, null, new LongParameter(parameterName)); } /** - * Construct a new ACSObjectSelectionModel. - * This model will produce instances of ACSObject - * by automatically instantiating the correct Java subclass using - * the {@link DomainObjectFactory}. + * Construct a new ACSObjectSelectionModel. This model will + * produce instances of ACSObject by automatically + * instantiating the correct Java subclass using the + * {@link DomainObjectFactory}. * - * @param model The {@link SingleSelectionModel} which will supply - * a {@link BigDecimal} ID of the currently selected object + * @param model The {@link SingleSelectionModel} which will supply a + * {@link BigDecimal} ID of the currently selected object */ - public ACSObjectSelectionModel(SingleSelectionModel model) { + public ACSObjectSelectionModel(final SingleSelectionModel model) { this(null, null, model); } /** * Construct a new ACSObjectSelectionModel * - * @param javaClass The name of the Java class which represents - * the object. Must be a subclass of {@link ACSObject}. In - * addition, the class must have a constructor with a single - * {@link OID} parameter. - * @param objectType The name of the persistence metadata object type - * which represents the ACS object. In practice, will often be - * the same as the javaClass. - * @param parameterName The name of the state parameter which will - * be used to store the object ID. + * @param javaClass The name of the Java class which represents the object. + * Must be a subclass of {@link ACSObject}. In addition, the class must have + * a constructor with a single {@link OID} parameter. + * @param objectType The name of the persistence metadata object type which + * represents the ACS object. In practice, will often be the same as the + * javaClass. + * @param parameterName The name of the state parameter which will be used + * to store the object ID. */ - public ACSObjectSelectionModel( String javaClass, - String objectType, - String parameterName ) { - this(javaClass, objectType, new BigDecimalParameter(parameterName)); + public ACSObjectSelectionModel(final String javaClass, + final String objectType, + final String parameterName) { + this(javaClass, objectType, new LongParameter(parameterName)); } /** * Construct a new ACSObjectSelectionModel * - * @param javaClass The name of the Java class which represents - * the object. Must be a subclass of {@link ACSObject}. In - * addition, the class must have a constructor with a single - * {@link OID} parameter. - * @param objectType The name of the persistence metadata object type - * which represents the ACS object. In practice, will often be - * the same as the javaClass. - * @param parameter The state parameter which should be used to store - * the object ID + * @param javaClass The name of the Java class which represents the object. + * Must be a subclass of {@link ACSObject}. In addition, the class must have + * a constructor with a single {@link OID} parameter. + * @param objectType The name of the persistence metadata object type which + * represents the ACS object. In practice, will often be the same as the + * javaClass. + * @param parameter The state parameter which should be used to store the + * object ID */ - public ACSObjectSelectionModel( String javaClass, - String objectType, - BigDecimalParameter parameter ) { - this(javaClass, objectType, + public ACSObjectSelectionModel(final String javaClass, + final String objectType, + final LongParameter parameter) { + this(javaClass, + objectType, new ParameterSingleSelectionModel(parameter)); } /** * Construct a new ACSObjectSelectionModel * - * @param javaClass The name of the Java class which represents - * the object. Must be a subclass of {@link ACSObject}. In - * addition, the class must have a constructor with a single - * {@link OID} parameter. - * @param objectType The name of the persistence metadata object type - * which represents the ACS object. In practice, will often be - * the same as the javaClass. - * @param model The {@link SingleSelectionModel} which will supply - * a {@link BigDecimal} ID of the currently selected object + * @param javaClass The name of the Java class which represents the object. + * Must be a subclass of {@link ACSObject}. In addition, the class must have + * a constructor with a single {@link OID} parameter. + * @param objectType The name of the persistence metadata object type which + * represents the ACS object. In practice, will often be the same as the + * javaClass. + * @param model The {@link SingleSelectionModel} which will supply a + * {@link BigDecimal} ID of the currently selected object */ - public ACSObjectSelectionModel( String javaClass, - String objectType, - SingleSelectionModel model ) { - m_loaded = new RequestLocal() { - protected Object initialValue(PageState state) { - return Boolean.FALSE; - } - }; + public ACSObjectSelectionModel(final String javaClass, + final String objectType, + final SingleSelectionModel model) { + loaded = new RequestLocal() { + @Override + protected Object initialValue(final PageState state) { + return Boolean.FALSE; + } + }; if (javaClass != null) { // Cache the Class object and the constructor for further use try { - m_javaClass = Class.forName(javaClass); - m_constructor = m_javaClass.getConstructor(); - } catch (Exception e) { - throw new UncheckedWrapperException( "Problem loading class " - + javaClass, e ); + this.javaClassName = Class.forName(javaClass); +// this.constructor = javaClassName.getConstructor(); + } catch (ClassNotFoundException | SecurityException ex) { + throw new UncheckedWrapperException(String.format( + "Problem loading class %s", javaClass), + ex); } } - m_objectType = objectType; - m_model = model; + this.objectType = objectType; + this.selectionModel = model; } /** * Set the ID of the current object. The next time - * {@link #getSelectedObject(PageState)} is called, the object - * with the specified ID will be loaded from the database. + * {@link #getSelectedObject(PageState)} is called, the object with the + * specified ID will be loaded from the database. * * @param state The page state - * @param key A {@link BigDecimal} primary key for the object, - * or a String representation of a BigDecimal, such as "42". + * @param key A {@link BigDecimal} primary key for the object, or a String + * representation of a BigDecimal, such as "42". */ - public void setSelectedKey(PageState state, Object key) { + @Override + public void setSelectedKey(final PageState state, final Object key) { //BigDecimal newKey = convertToBigDecimal(key); - m_loaded.set(state, Boolean.FALSE); - m_model.setSelectedKey(state, key); + loaded.set(state, Boolean.FALSE); + selectionModel.setSelectedKey(state, key); } /** - * Return the object which was selected and loaded from the database, - * using the values supplied in the page state. May return null - * if isSelected(state) == false, or if the object was not found. + * Return the object which was selected and loaded from the database, using + * the values supplied in the page state. May return null if + * isSelected(state) == false, or if the object was not found. * * @param state The page state * @return The currently selected domain object, or null if no object is - * selected. + * selected. */ - public CcmObject getSelectedObject(PageState state) { + public CcmObject getSelectedObject(final PageState state) { Long id = convertToLong(getSelectedKey(state)); if (id == null) { return null; } - + return loadACSObject(state, id); } /** - * Load the selected object for the first time. Child classes - * may choose to override this method in order to load the object - * in nonstandard ways. The default implementation merely - * instantiates an {@link ACSObject} whose ID is the passed-in key. + * Load the selected object for the first time. Child classes may choose to + * override this method in order to load the object in nonstandard ways. The + * default implementation merely instantiates an {@link ACSObject} whose ID + * is the passed-in key. * * @param state the current page state * @param key the currently selected key; guaranteed to be non-null * @return the object identified by the specified key - * @pre key != null */ - protected CcmObject loadACSObject(PageState state, Object key) { - CcmObject item = null; - + protected CcmObject loadACSObject(final PageState state, final Object key) { // Cheesy back-and-forth conversion to ensure that // the result will be a BigDecimal, not a String or // something else. Should go away when ListModel.getKey is // changed to return an Object. - Long id = convertToLong(key); + final Long objectId = convertToLong(key); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CcmObjectRepository repository = cdiUtil.findBean( + CcmObjectRepository.class); + + return repository.findById(objectId); - return CdiUtil.createCdiUtil().findBean(CcmObjectRepository.class).findById(id); - } /** @@ -307,127 +312,121 @@ public class ACSObjectSelectionModel implements SingleSelectionModel { * @param state The page state * @param object The content item to set */ - public void setSelectedObject(PageState state, CcmObject object) { + public void setSelectedObject(final PageState state, + final CcmObject object) { CcmObject item = object; if (item == null) { - m_loaded.set(state, Boolean.FALSE); - m_model.setSelectedKey(state, null); + loaded.set(state, Boolean.FALSE); + selectionModel.setSelectedKey(state, null); } else { - m_loaded.set(state, Boolean.TRUE); - m_model.setSelectedKey(state, item.getObjectId()); + loaded.set(state, Boolean.TRUE); + selectionModel.setSelectedKey(state, item.getObjectId()); } } /** - * Determine if the attempt to load the selected object has - * been made yet. Child classes may use this method to - * perform request-local initialization. + * Determine if the attempt to load the selected object has been made yet. + * Child classes may use this method to perform request-local + * initialisation. * * @param state the page state - * @return true if the attempt to load the selected object has - * already been made, false otherwise + * @return true if the attempt to load the selected object has already been + * made, false otherwise */ - public boolean isInitialized(PageState state) { - return ((Boolean)m_loaded.get(state)).booleanValue(); + public boolean isInitialized(final PageState state) { + return ((Boolean) loaded.get(state)); } /** - * A utility function which creates a new object with the given ID. - * Uses reflection to create the instance of the class supplied - * in the constructor to this ACSObjectSelectionModel. - * If no classname was supplied in the constructor, an assertion - * failure will occur. + * A utility function which creates a new object with the given ID. Uses + * reflection to create the instance of the class supplied in the + * constructor to this ACSObjectSelectionModel. If no class + * name was supplied in the constructor, an assertion failure will occur. * - * @param id The id of the new item - this is ignored and the object - * will have a different id + * @param id The id of the new item - this is ignored and the object will + * have a different id * @return The newly created item + * @throws javax.servlet.ServletException * @post return != null * @deprecated This ignores the ID since ACSObject.setID is a no-op */ - public CcmObject createACSObject(Long id) throws ServletException { + public CcmObject createACSObject(final Long id) throws ServletException { return createACSObject(); } - /** - * A utility function which creates a new object with the given ID. - * Uses reflection to create the instance of the class supplied - * in the constructor to this ACSObjectSelectionModel. - * If no classname was supplied in the constructor, an assertion - * failure will occur. + * A utility function which creates a new object with the given ID. Uses + * reflection to create the instance of the class supplied in the + * constructor to this ACSObjectSelectionModel. If no class + * name was supplied in the constructor, an assertion failure will occur. * - * @param id The id of the new item * @return The newly created item + * @throws javax.servlet.ServletException * @post return != null */ public CcmObject createACSObject() throws ServletException { - Assert.exists(m_javaClass, Class.class); + Assert.exists(javaClassName, Class.class); try { - CcmObject item = (CcmObject)m_javaClass.newInstance(); - return item; - } catch (InstantiationException e) { - throw new ServletException(e); - } catch (IllegalAccessException e) { - throw new ServletException(e); + final CcmObject object = (CcmObject) javaClassName.newInstance(); + return object; + } catch (InstantiationException | IllegalAccessException ex) { + throw new ServletException(ex); } } - /** - * @return the Class of the content items which are produced - * by this model + * @return the Class of the content items which are produced by this model */ public Class getJavaClass() { - return m_javaClass; + return javaClassName; } /** - * @return The name of the object type of the - * content items which are produced by this model + * @return The name of the object type of the content items which are + * produced by this model */ public String getObjectType() { - return m_objectType; + return objectType; } /** - * @return the underlying {@link SingleSelectionModel} which - * maintains the ID of the selected object + * @return the underlying {@link SingleSelectionModel} which maintains the + * ID of the selected object */ public SingleSelectionModel getSingleSelectionModel() { - return m_model; + return selectionModel; } //////////////////////// // // Passthrough methods - /** * Return true if there is a selected object. * * @param state represents the state of the current request * @return true if there is a selected component. */ - public boolean isSelected(PageState state) { - return m_model.isSelected(state); + @Override + public boolean isSelected(final PageState state) { + return selectionModel.isSelected(state); } /** * Return the key that identifies the selected object. * * @param state the current page state - * @return the {@link BigDecimal} ID of the currently selected - * object, or null if no object is selected. - * @post return instanceof BigDecimal + * @return the {@link BigDecimal} {@link Long} ID of the + * currently selected object, or null if no object is selected. * */ - public Object getSelectedKey(PageState state) { - Object key = m_model.getSelectedKey(state); + @Override + public Object getSelectedKey(final PageState state) { + Object key = selectionModel.getSelectedKey(state); return key; } - /** * Clear the selection. * @@ -435,50 +434,54 @@ public class ACSObjectSelectionModel implements SingleSelectionModel { * @post ! isSelected(state) * @post ! isInitialized(state) */ - public void clearSelection(PageState state) { - m_model.clearSelection(state); - m_loaded.set(state, Boolean.FALSE); + @Override + public void clearSelection(final PageState state) { + selectionModel.clearSelection(state); + loaded.set(state, Boolean.FALSE); } /** * Add a change listener to the model. The listener's * stateChanged is called whenever the selected key changes. * - * @param l a listener to notify when the selected key changes + * @param listener a listener to notify when the selected key changes */ - public void addChangeListener(ChangeListener l) { - m_model.addChangeListener(l); + @Override + public void addChangeListener(final ChangeListener listener) { + selectionModel.addChangeListener(listener); } /** * Remove a change listener from the model. * - * @param l the listener to remove. + * @param listener the listener to remove. */ - public void removeChangeListener(ChangeListener l) { - m_model.removeChangeListener(l); + @Override + public void removeChangeListener(final ChangeListener listener) { + selectionModel.removeChangeListener(listener); } /** - * Return the state parameter which will be used to keep track - * of the currently selected key. Most likely, the parameter will - * be a {@link BigDecimalParameter}. + * Return the state parameter which will be used to keep track of the + * currently selected key. Most likely, the parameter will be a + * {@link BigDecimalParameter}. * - * @return The state parameter which should be used to keep - * track of the ID of the currently selected object, or null - * if the ID is computed in some other way + * @return The state parameter which should be used to keep track of the ID + * of the currently selected object, or null if the ID is computed in some + * other way * @see SingleSelectionModel#getStateParameter() */ + @Override public ParameterModel getStateParameter() { - return m_model.getStateParameter(); + return selectionModel.getStateParameter(); } - private static Long convertToLong(Object value) { + private static Long convertToLong(final Object value) { Long newValue = null; - if ( value instanceof Long ) { + if (value instanceof Long) { newValue = (Long) value; - } else if ( value != null ) { + } else if (value != null) { newValue = Long.parseLong(value.toString()); } diff --git a/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentAccess.java b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentAccess.java new file mode 100755 index 000000000..37767a333 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentAccess.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.toolbox.ui; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.PageState; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import org.libreccm.cdi.utils.CdiUtil; + +import org.libreccm.security.Party; +import org.libreccm.security.PermissionChecker; + +/** + *

+ * Wrapper class that registers access checks (actions) to a Bebop + * component.

+ * + * @author Michael Pih + * @author Jens Pelzetter + */ +public class ComponentAccess { + + private Component component; + private List accessCheckList; + + /** + * @param component The component + */ + public ComponentAccess(final Component component) { + accessCheckList = new ArrayList(); + this.component = component; + } + + /** + * @param component The component + * @param check An access check + */ + public ComponentAccess(final Component component, final String check) { + this(component); + accessCheckList.add(check); + } + + /** + * Add an access check to this component. + * + * @param check The access check + */ + public void addAccessCheck(final String check) { + accessCheckList.add(check); + } + + /** + * Get the access checks. + * + * @return The list of access checks + */ + public List getAccessCheckList() { + return accessCheckList; + } + + /** + * Get the component. + * + * @return The component + */ + public Component getComponent() { + return component; + } + + /** + * Do all the access checks registered to the component pass? + * + * @return true if all the access checks pass, false otherwise + */ + public boolean canAccess() { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + + final Optional canAccess = accessCheckList.stream() + .map(accessCheck -> permissionChecker.isPermitted(accessCheck)) + .reduce((result1, result2) -> result1 && result2); + + return canAccess.orElse(false); + } + +}