From 4b55b68c2e6bbced3252e5d4ce8079af5f66a1b4 Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 20 Jan 2017 16:38:39 +0000 Subject: [PATCH] CCM NG/ccm-cms: BrowsePane migrated to NG APIs git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4527 8810af33-2d31-482b-a856-94f89814c4df --- build-site.sh | 1 + .../{BrowsePane.java.todo => BrowsePane.java} | 100 ++- .../arsdigita/cms/ui/ContentSectionPage.java | 17 +- .../com/arsdigita/cms/ui/FlatItemList.java | 535 ++++++++++++++ .../arsdigita/cms/ui/FlatItemList.java.todo | 604 ---------------- .../com/arsdigita/cms/ui/UserAddForm.java | 4 +- .../cms/ui/authoring/CreationSelector.java | 285 ++++++++ .../ui/authoring/CreationSelector.java.todo | 258 ------- .../cms/ui/authoring/NewItemForm.java | 201 ++++++ .../cms/ui/authoring/NewItemForm.java.todo | 247 ------- .../arsdigita/cms/ui/folder/FolderPath.java | 37 + .../cms/ui/folder/FolderPathListModel.java | 63 ++ .../ui/folder/FolderPathListModelBuilder.java | 51 ++ .../cms/ui/folder/FolderRequestLocal.java | 5 +- .../cms/ui/folder/FolderSelectionModel.java | 23 +- .../com/arsdigita/cms/ui/folder/ItemPath.java | 57 +- .../cms/ui/folder/ItemPathListModel.java | 66 ++ .../ui/folder/ItemPathListModelBuilder.java | 51 ++ .../permissions/CMSPermissionsConstants.java | 167 +++++ .../ui/permissions/CMSPermissionsGrant.java | 212 ++++++ .../ui/permissions/CMSPermissionsHeader.java | 99 +++ .../ui/permissions/CMSPermissionsPane.java | 501 ++++++++++++++ .../ui/permissions/CMSPermissionsTables.java | 652 ++++++++++++++++++ .../ui/permissions/CMSUserObjectStruct.java | 112 +++ .../cms/ui/permissions/CMSUserSearchForm.java | 136 ++++ .../cms/ui/permissions/ObjectAddAdmin.java | 286 ++++++++ .../ui/permissions/ObjectAddSearchAdmin.java | 118 ++++ .../ui/permissions/ObjectAdminListing.java | 259 +++++++ .../contentsection/ContentSectionSetup.java | 1 + .../contentsection/FolderManager.java | 136 ++-- .../privileges/ItemPrivileges.java | 5 + .../contenttypes/ContentTypesManager.java | 1 - .../java/com/arsdigita/bebop/MetaForm.java | 188 +++++ 33 files changed, 4187 insertions(+), 1291 deletions(-) create mode 100755 build-site.sh rename ccm-cms/src/main/java/com/arsdigita/cms/ui/{BrowsePane.java.todo => BrowsePane.java} (66%) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java delete mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java delete mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java delete mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.todo create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPath.java create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModel.java create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModelBuilder.java create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModel.java create mode 100644 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModelBuilder.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsConstants.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsGrant.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsHeader.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsPane.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsTables.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserObjectStruct.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserSearchForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddAdmin.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddSearchAdmin.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAdminListing.java create mode 100755 ccm-core/src/main/java/com/arsdigita/bebop/MetaForm.java diff --git a/build-site.sh b/build-site.sh new file mode 100755 index 000000000..a7e755ab7 --- /dev/null +++ b/build-site.sh @@ -0,0 +1 @@ +mvn clean package site site:stage -Dmaven.test.failure.ignore=true -Pwildfly-remote-h2-mem diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java similarity index 66% rename from ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java.todo rename to ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java index 357e89738..bb664104c 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java.todo +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/BrowsePane.java @@ -36,11 +36,7 @@ import com.arsdigita.cms.ui.folder.FolderTreeModelBuilder; import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.toolbox.ui.LayoutPanel; import com.arsdigita.util.Assert; -import com.arsdigita.web.Web; -import java.math.BigDecimal; - -import org.apache.log4j.Logger; import org.libreccm.categorization.Category; import org.libreccm.core.CcmObject; import org.librecms.CmsConstants; @@ -51,25 +47,23 @@ import org.librecms.CmsConstants; * the "Browse" tab. * * @author David LutterKort <dlutter@redhat.com> - * @version $Id: BrowsePane.java 1325 2006-09-22 08:11:33Z sskracic $ + * @author Jens Pelzetter */ public class BrowsePane extends LayoutPanel implements Resettable { - private static final Logger s_log = Logger.getLogger(BrowsePane.class); - - private final BaseTree m_tree; - private final SingleSelectionModel m_model; - private final FolderSelectionModel m_folderModel; // To support legacy UI code - private final FolderRequestLocal m_folder; - private final FlatItemList m_fil; + private final BaseTree tree; + private final SingleSelectionModel selectionModel; + private final FolderSelectionModel folderModel; // To support legacy UI code + private final FolderRequestLocal folderRequestLocal; + private final FlatItemList flatItemList; public BrowsePane() { /* The folder tree displayed on the left side / left column */ - m_tree = new BaseTree(new FolderTreeModelBuilder()); - m_model = m_tree.getSelectionModel(); - m_folderModel = new FolderSelectionModel(m_model); - m_folder = new FolderRequestLocal(m_folderModel); + tree = new BaseTree(new FolderTreeModelBuilder()); + selectionModel = tree.getSelectionModel(); + folderModel = new FolderSelectionModel(selectionModel); + folderRequestLocal = new FolderRequestLocal(folderModel); final SegmentedPanel left = new SegmentedPanel(); setLeft(left); @@ -77,21 +71,21 @@ public class BrowsePane extends LayoutPanel implements Resettable { final Label heading = new Label( new GlobalizedMessage("cms.ui.folder_browser", CmsConstants.CMS_FOLDER_BUNDLE)); - left.addSegment(heading, m_tree); + left.addSegment(heading, tree); - m_fil = new FlatItemList(m_folder, m_folderModel); - setBody(m_fil); + flatItemList = new FlatItemList(folderRequestLocal, folderModel); + setBody(flatItemList); - m_fil.getManipulator().getItemView().addProcessListener( + flatItemList.getManipulator().getItemView().addProcessListener( new ProcessListener()); - m_fil.getManipulator().getTargetSelector().addProcessListener( + flatItemList.getManipulator().getTargetSelector().addProcessListener( new ProcessListener()); - m_fil.getManipulator().getTargetSelector().addSubmissionListener( + flatItemList.getManipulator().getTargetSelector().addSubmissionListener( new SubmissionListener()); } @Override - public final void register(Page page) { + public final void register(final Page page) { super.register(page); page.addActionListener(new FolderListener()); @@ -99,10 +93,10 @@ public class BrowsePane extends LayoutPanel implements Resettable { } @Override - public final void reset(PageState state) { + public final void reset(final PageState state) { super.reset(state); - m_fil.reset(state); + flatItemList.reset(state); } // Private classes and methods @@ -111,19 +105,16 @@ public class BrowsePane extends LayoutPanel implements Resettable { */ private final class ProcessListener implements FormProcessListener { - /** - * - * @param e - */ - public final void process(final FormSectionEvent e) { - final PageState state = e.getPageState(); + @Override + public final void process(final FormSectionEvent event) { + final PageState state = event.getPageState(); - if (e.getSource() == m_fil.getManipulator().getItemView()) { + if (event.getSource() == flatItemList.getManipulator().getItemView()) { // Hide everything except for the flat item list - m_tree.setVisible(state, false); - } else if (e.getSource() == m_fil.getManipulator() + tree.setVisible(state, false); + } else if (event.getSource() == flatItemList.getManipulator() .getTargetSelector()) { - m_tree.setVisible(state, true); + tree.setVisible(state, true); } } @@ -131,12 +122,15 @@ public class BrowsePane extends LayoutPanel implements Resettable { private final class SubmissionListener implements FormSubmissionListener { - public final void submitted(final FormSectionEvent e) { - final PageState s = e.getPageState(); + @Override + public final void submitted(final FormSectionEvent event) { + final PageState state = event.getPageState(); - if (e.getSource() == m_fil.getManipulator().getTargetSelector()) { - if (!m_fil.getManipulator().getTargetSelector().isVisible(s)) { - m_tree.setVisible(s, true); + if (event.getSource() == flatItemList.getManipulator() + .getTargetSelector()) { + if (!flatItemList.getManipulator().getTargetSelector() + .isVisible(state)) { + tree.setVisible(state, true); } } } @@ -145,17 +139,18 @@ public class BrowsePane extends LayoutPanel implements Resettable { private final class FolderListener implements ActionListener { - public final void actionPerformed(final ActionEvent e) { - final PageState state = e.getPageState(); + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); - if (!m_model.isSelected(state)) { + if (!selectionModel.isSelected(state)) { final String folder = state.getRequest().getParameter( ContentSectionPage.SET_FOLDER); if (folder == null) { final Category root = CMS.getContext().getContentSection() .getRootDocumentsFolder(); - Long folderID = root.getObjectId(); + final Long folderID = root.getObjectId(); /* ToDo @@ -168,9 +163,9 @@ public class BrowsePane extends LayoutPanel implements Resettable { } }*/ - m_model.setSelectedKey(state, folderID); + selectionModel.setSelectedKey(state, folderID); } else { - m_model.setSelectedKey(state, folder); + selectionModel.setSelectedKey(state, folder); } } } @@ -179,21 +174,22 @@ public class BrowsePane extends LayoutPanel implements Resettable { private final class TreeListener implements ActionListener { - public final void actionPerformed(final ActionEvent e) { - final PageState state = e.getPageState(); + @Override + public final void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); if (Assert.isEnabled()) { - Assert.isTrue(m_model.isSelected(state)); + Assert.isTrue(selectionModel.isSelected(state)); } final Category root = CMS.getContext().getContentSection() .getRootDocumentsFolder(); - if (!root.equals(m_folder.getFolder(state))) { + if (!root.equals(folderRequestLocal.getFolder(state))) { // Expand the ancestor nodes of the currently // selected node. - CcmObject object = m_folder.getFolder(state); + CcmObject object = folderRequestLocal.getFolder(state); while (object != null) { if (object instanceof Category) { @@ -204,7 +200,7 @@ public class BrowsePane extends LayoutPanel implements Resettable { if (result instanceof Category) { object = result; - m_tree.expand( + tree.expand( ((Long) object.getObjectId()).toString(), state); } else { diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java index 3c16a5fdb..8bc225acc 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ContentSectionPage.java @@ -119,7 +119,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener { private TabbedPane m_tabbedPane; private FolderAdminPane m_folderPane; -//ToDo NG private BrowsePane m_browsePane; + private BrowsePane m_browsePane; private ItemSearch m_searchPane; //ToDo NG private ImagesPane m_imagesPane; private RoleAdminPane m_rolePane; @@ -146,7 +146,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener { // Initialize the individual panes m_folderPane = getFolderAdminPane(); -//ToDo NG m_browsePane = getBrowsePane(); + m_browsePane = getBrowsePane(); m_searchPane = getSearchPane(); //ToDo NG m_imagesPane = getImagesPane(); m_rolePane = getRoleAdminPane(); @@ -237,13 +237,12 @@ public class ContentSectionPage extends CMSPage implements ActionListener { * * @return */ -// ToDo NG -// protected BrowsePane getBrowsePane() { -// if (m_browsePane == null) { -// m_browsePane = new BrowsePane(); -// } -// return m_browsePane; -// } + protected BrowsePane getBrowsePane() { + if (m_browsePane == null) { + m_browsePane = new BrowsePane(); + } + return m_browsePane; + } /** * Creates, and then caches, the search pane. Overriding this method to * return null will prevent this tab from appearing. diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java new file mode 100755 index 000000000..38e1597a1 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java @@ -0,0 +1,535 @@ +/* + * 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.ActionLink; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import com.arsdigita.bebop.Resettable; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.SingleSelectionModel; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.ChangeEvent; +import com.arsdigita.bebop.event.ChangeListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.cms.CMS; + +import org.librecms.contentsection.ContentSection; + +import com.arsdigita.cms.ui.authoring.CreationSelector; +import com.arsdigita.cms.ui.authoring.NewItemForm; +import com.arsdigita.cms.ui.folder.FolderCreator; +import com.arsdigita.cms.ui.folder.FolderEditor; +import com.arsdigita.cms.ui.folder.FolderManipulator; +import com.arsdigita.cms.ui.folder.FolderPath; +import com.arsdigita.cms.ui.folder.FolderRequestLocal; +import com.arsdigita.cms.ui.folder.FolderSelectionModel; +import com.arsdigita.cms.ui.permissions.CMSPermissionsPane; +import com.arsdigita.globalization.GlobalizedMessage; + +import com.arsdigita.toolbox.ui.ActionGroup; +import com.arsdigita.ui.CcmObjectSelectionModel; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.PermissionManager; +import org.librecms.CmsConstants; +import org.librecms.contentsection.Folder; +import org.librecms.contentsection.privileges.ItemPrivileges; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Encapsulates a {@link FolderManipulator} in order to create a flat item + * listing. Also contains a new item form. + * + * @author Stanislav Freidin + * @author Jens Pelzetter + */ +public class FlatItemList extends SegmentedPanel implements FormProcessListener, + ChangeListener, + FormSubmissionListener, + Resettable, + ActionListener { + + private static final String CONTENT_TYPE_ID = "ct"; + // The folder selectors + private final FolderSelectionModel folderSelectionModel; + private final FolderRequestLocal folderRequestLocal; + private final NewItemForm newItemForm; + private final SingleSelectionModel typeSelectionModel; + private final CreationSelector creationSelector; + private final FolderManipulator folderManipulator; + private final FolderCreator folderCreator; +// private final ActionLink m_setHomeFolderAction; +// private final ActionLink m_removeHomeFolderAction; + private final ActionLink createFolderAction; + private final ActionLink togglePrivateAction; +// private final Label m_homeFolderLabel; + private final Segment browseSegment; + private final Segment newItemSegment; + private final Segment newFolderSegment; + private final Segment editFolderSegment; + private final Segment permissionsSegment; + private final CMSPermissionsPane permissionsPane; + // Folder edit/rename functionality. + private final ActionLink editFolderAction; + private final FolderEditor folderEditor; + private final Label contentLabel; + private final FolderPath folderPath; + private final Label chooseLabel; + + /** + * Construct a new item listing pane. The provided folder selection model is + * used to keep track of the currently displayed folder. + * + * @param folderRequestLocal + * @param folderSelectionModel maintains the currently displayed folder. + */ + @SuppressWarnings("unchecked") + public FlatItemList(final FolderRequestLocal folderRequestLocal, + final FolderSelectionModel folderSelectionModel) { + this.folderRequestLocal = folderRequestLocal; + this.folderSelectionModel = folderSelectionModel; + folderSelectionModel.addChangeListener(event -> reset(event + .getPageState())); + + setIdAttr("flat-item-list"); + + newItemSegment = addSegment(); + newItemSegment.setIdAttr("folder-new-item"); + + newFolderSegment = addSegment(); + newFolderSegment.setIdAttr("folder-new-folder"); + + editFolderSegment = addSegment(); + editFolderSegment.setIdAttr("folder-edit-folder"); + + browseSegment = addSegment(); + browseSegment.setIdAttr("folder-browse"); + + final ActionGroup browseActions = new ActionGroup(); + browseSegment.add(browseActions); + + // The top 'browse' segment + contentLabel = new Label(globalize("cms.ui.contents_of"), false); + browseSegment.addHeader(contentLabel); + chooseLabel = new Label(globalize("cms.ui.choose_target_folder"), + false); + browseSegment.addHeader(chooseLabel); + folderPath = new FolderPath(folderSelectionModel); + + browseSegment.addHeader(folderPath); + folderManipulator = new FolderManipulator(folderSelectionModel); + folderManipulator.getItemView().addProcessListener(this); + folderManipulator.getTargetSelector().addProcessListener(this); + folderManipulator.getTargetSelector().addSubmissionListener(this); + + browseActions.setSubject(folderManipulator); + + // The actions + createFolderAction = new ActionLink(new Label(globalize( + "cms.ui.new_folder"))); + createFolderAction.addActionListener(this); + browseActions.addAction(createFolderAction); + + editFolderAction = new ActionLink(new Label(globalize( + "cms.ui.edit_folder"))); + editFolderAction.addActionListener(this); + browseActions.addAction(editFolderAction); + +// m_setHomeFolderAction = new ActionLink(new Label(globalize( +// "cms.ui.set_home_folder"))); +// m_setHomeFolderAction.addActionListener(this); +// browseActions.addAction(m_setHomeFolderAction); +// m_homeFolderLabel = new Label(new PrintListener() { +// +// @Override +// public final void prepare(final PrintEvent event) { +// Label label = (Label) event.getTarget(); +// User user = Web.getWebContext().getUser(); +// +// Folder folder = Folder.getUserHomeFolder(user, CMS.getContext() +// .getContentSection()); +// if (folder != null) { +// String url = folder.getContentSection().getURL() +// + PageLocations.SECTION_PAGE + "?" +// + ContentSectionPage.SET_FOLDER + "=" +// + folder.getID(); +// //label.setLabel("Go to home folder: " + folder.getLabel() + ""); +// String[] parts = new String[3]; +// parts[0] = ""; +// parts[1] = url; +// parts[2] = folder.getLabel(); +// // label.setLabel(String.format("%s: %s", +// // (String) globalize("cms.ui.go_to_home_folder").localize(), +// // url, +// // folder.getLabel())); +// label.setLabel(globalize("cms.ui.go_to_home_folder", parts)); +// } else { +// //label.setLabel("No home folder selected"); +// String[] parts = new String[3]; +// parts[0] = ""; +// parts[1] = ""; +// //label.setLabel(String.format("%s", +// // (String)globalize("cms.ui.no_home_folder_selected").localize())); +// label.setLabel(globalize("cms.ui.no_home_folder_selected", +// parts)); +// } +// label.setOutputEscaping(false); +// } +// +// }); +// browseActions.addAction(m_homeFolderLabel); +// m_removeHomeFolderAction = new ActionLink(new Label(globalize( +// "cms.ui.remove_home_folder"))); +// m_removeHomeFolderAction.addActionListener(this); +// browseActions.addAction(m_removeHomeFolderAction); + + /* */ + newItemForm = new SectionNewItemForm("newItem"); + newItemForm.addProcessListener(this); + browseActions.addAction(newItemForm); + + /* permission */ + permissionsSegment = addSegment(); + permissionsSegment.setIdAttr("folder-permissions"); + + final ActionGroup permissionActions = new ActionGroup(); + permissionsSegment.add(permissionActions); + + // The permissions segment + permissionsSegment.addHeader(new Label(new GlobalizedMessage( + "cms.ui.permissions", CmsConstants.CMS_BUNDLE))); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionManager permissionManager = cdiUtil.findBean( + PermissionManager.class); + final List privileges = permissionManager + .listDefiniedPrivileges(ItemPrivileges.class); + final Map privNameMap = new HashMap<>(); + privileges.forEach(privilege -> privNameMap.put(privilege, privilege)); + + permissionsPane = new CMSPermissionsPane( + privileges.toArray(new String[]{}), + privNameMap, + (CcmObjectSelectionModel) folderSelectionModel); + permissionActions.setSubject(permissionsPane); + + // An action + togglePrivateAction = new ActionLink(new Label(new PrintListener() { + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + final Label target = (Label) event.getTarget(); + final Folder currentFolder = folderRequestLocal.getFolder(state); + // ACSObject parent = currentFolder.getParent(); + +// if (context == null) { + target.setLabel(new GlobalizedMessage( + "cms.ui.restore_default_permissions", + CmsConstants.CMS_BUNDLE)); +// } else { +// target.setLabel(GlobalizationUtil +// .globalize("cms.ui.use_custom_permissions")); +// } + } + + })); + togglePrivateAction.addActionListener(this); + permissionActions.addAction(togglePrivateAction); + + // The 'new item' segment + newItemSegment.addHeader(new Label(globalize("cms.ui.new_item"))); + typeSelectionModel = new ParameterSingleSelectionModel<>( + new LongParameter(CONTENT_TYPE_ID)); + typeSelectionModel.addChangeListener(this); + + creationSelector = new CreationSelector(typeSelectionModel, + folderSelectionModel); + newItemSegment.add(creationSelector); + //m_newItemSeg.add(new Label("
", false)); + + // The 'new folder' segment + newFolderSegment.addHeader(new Label(globalize("cms.ui.new_folder"))); + Form folderCreate = new Form("fcreat"); + folderCreator = new FolderCreator("fcreat", folderSelectionModel); + folderCreator.addSubmissionListener(this); + folderCreator.addProcessListener(this); + folderCreate.add(folderCreator); + newFolderSegment.add(folderCreate); + newFolderSegment.add(new Label("
", false)); + + editFolderSegment.addHeader(new Label(globalize("cms.ui.edit_folder"))); + folderEditor = new FolderEditor("fedit", folderSelectionModel); + folderEditor.addSubmissionListener(this); + folderEditor.addProcessListener(this); + + Form folderEditorForm = new Form("fedit_form"); + folderEditorForm.add(folderEditor); + editFolderSegment.add(folderEditorForm); + editFolderSegment.add(new Label("
", false)); + } + + @Override + public void register(final Page page) { + super.register(page); + + page.setVisibleDefault(chooseLabel, false); + page.setVisibleDefault(newItemSegment, false); + page.setVisibleDefault(newFolderSegment, false); + page.setVisibleDefault(editFolderSegment, false); + + page + .addComponentStateParam(this, typeSelectionModel.getStateParameter()); + + page.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + + if (state.isVisibleOnPage(FlatItemList.this)) { + showHideSegments(state); + } + } + + }); + } + + /** + * Show/hide segments based on access checks. + * + * @param state The page state + * + * @pre ( state != null ) + */ + private void showHideSegments(final PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + final Folder folder = folderRequestLocal.getFolder(state); + + // MP: This should be checked on the current folder instead of just + // the content section. + final boolean newItem = permissionChecker.isPermitted( + ItemPrivileges.CREATE_NEW, folder); + + if (!newItem) { + browseMode(state); + } + createFolderAction.setVisible(state, newItem); + newItemForm.setVisible(state, newItem); + + final boolean editItem = permissionChecker.isPermitted( + ItemPrivileges.EDIT, folder); + + editFolderAction.setVisible(state, editItem); + + if (permissionChecker.isPermitted(ItemPrivileges.ADMINISTER, folder)) { + permissionsSegment.setVisible(state, true); + } else { + permissionsSegment.setVisible(state, false); + } + } + + private void browseMode(final PageState state) { + browseSegment.setVisible(state, true); + permissionsSegment.setVisible(state, true); + chooseLabel.setVisible(state, false); + contentLabel.setVisible(state, true); + folderPath.setVisible(state, true); + newItemSegment.setVisible(state, false); + newFolderSegment.setVisible(state, false); + editFolderSegment.setVisible(state, false); + + typeSelectionModel.clearSelection(state); + } + + private void newItemMode(final PageState state) { + permissionsSegment.setVisible(state, false); + newItemSegment.setVisible(state, true); + } + + private void newFolderMode(final PageState state) { + permissionsSegment.setVisible(state, false); + newFolderSegment.setVisible(state, true); + } + + @Override + public void submitted(final FormSectionEvent event) + throws FormProcessException { + + final PageState state = event.getPageState(); + if (event.getSource() == folderCreator + && folderCreator.isCancelled(state)) { + browseMode(state); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.cancelled", CmsConstants.CMS_BUNDLE)); + } else if (event.getSource() == folderEditor && folderEditor + .isCancelled(state)) { + browseMode(state); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.cancelled", CmsConstants.CMS_BUNDLE)); + } else if (event.getSource() == folderManipulator.getTargetSelector()) { + // This only works if this submission listener is run + // after the target selector's one + if (!folderManipulator.getTargetSelector().isVisible(state)) { + browseMode(state); + } + } + } + + @Override + public void process(final FormSectionEvent event) { + final PageState state = event.getPageState(); + final Object source = event.getSource(); + if (source == newItemForm) { + final Long typeID = newItemForm.getTypeID(state); + typeSelectionModel.setSelectedKey(state, typeID); + newItemMode(state); + } else if (source == folderCreator || source == folderEditor) { + browseMode(state); + } else if (source == folderManipulator.getItemView()) { + // Hide everything except for the browseSeg + permissionsSegment.setVisible(state, false); + chooseLabel.setVisible(state, true); + contentLabel.setVisible(state, false); + folderPath.setVisible(state, false); + } else if (source == folderManipulator.getTargetSelector()) { + browseMode(state); + } + } + + @Override + public void stateChanged(final ChangeEvent event) { + final PageState state = event.getPageState(); + if (event.getSource().equals(typeSelectionModel)) { + if (!typeSelectionModel.isSelected(state)) { + browseMode(state); + } + } + } + + @Override + public void actionPerformed(final ActionEvent event) { + final PageState state = event.getPageState(); + final Object source = event.getSource(); + if (source == createFolderAction) { + newFolderMode(state); + } else if (source == editFolderAction) { + permissionsSegment.setVisible(state, false); + editFolderSegment.setVisible(state, true); + } else if (source == togglePrivateAction) { + togglePermissions(state); + } +// } else if (source == m_setHomeFolderAction) { +// User user = Web.getWebContext().getUser(); +// Folder folder = m_folder.getFolder(state); +// user = (User) DomainObjectFactory.newInstance(user.getOID()); +// Folder.setUserHomeFolder(user, folder); +// } else if (source == m_removeHomeFolderAction) { +// User user = Web.getWebContext().getUser(); +// ContentSection section = CMS.getContext().getContentSection(); +// UserHomeFolderMap map = UserHomeFolderMap +// .findUserHomeFolderMap(user, section); +// if (map != null) { +// map.delete(); +// } +// } + } + + private void togglePermissions(final PageState state) { + final Folder currentFolder = folderRequestLocal.getFolder(state); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + permissionChecker.checkPermission(ItemPrivileges.ADMINISTER, + currentFolder); + + permissionsPane.reset(state); + } + + @Override + public void reset(final PageState state) { + browseMode(state); + folderManipulator.reset(state); + // switching between folders used to keep showing the permission pane + // in the same perm mode (direct or inherited) regardless + // of the folder status + permissionsPane.reset(state); + } + + public final FolderManipulator getManipulator() { + return folderManipulator; + } + + public final CMSPermissionsPane getPermissionsPane() { + return permissionsPane; + } + + public void setPermissionLinkVis(PageState state) { + final Folder currentFolder = folderRequestLocal.getFolder(state); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + if (!permissionChecker.isPermitted(ItemPrivileges.ADMINISTER, + currentFolder)) { + togglePrivateAction.setVisible(state, false); + } + } + + private static class SectionNewItemForm extends NewItemForm { + + public SectionNewItemForm(final String name) { + super(name); + } + + @Override + public ContentSection getContentSection(final PageState state) { + return CMS.getContext().getContentSection(); + } + + } + + /** + * Getting the GlobalizedMessage using a CMS Class targetBundle. + * + * @param key The resource key + * + * @pre ( key != null ) + */ + private static GlobalizedMessage globalize(final String key) { + return ContentSectionPage.globalize(key); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java.todo deleted file mode 100755 index 147b504e3..000000000 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/FlatItemList.java.todo +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.arsdigita.cms.ui; - -import com.arsdigita.bebop.ActionLink; -import com.arsdigita.bebop.Form; -import com.arsdigita.bebop.FormProcessException; -import com.arsdigita.bebop.Label; -import com.arsdigita.bebop.Page; -import com.arsdigita.bebop.PageState; -import com.arsdigita.bebop.ParameterSingleSelectionModel; -import com.arsdigita.bebop.Resettable; -import com.arsdigita.bebop.SegmentedPanel; -import com.arsdigita.bebop.SingleSelectionModel; -import com.arsdigita.bebop.event.ActionEvent; -import com.arsdigita.bebop.event.ActionListener; -import com.arsdigita.bebop.event.ChangeEvent; -import com.arsdigita.bebop.event.ChangeListener; -import com.arsdigita.bebop.event.FormProcessListener; -import com.arsdigita.bebop.event.FormSectionEvent; -import com.arsdigita.bebop.event.FormSubmissionListener; -import com.arsdigita.bebop.event.PrintEvent; -import com.arsdigita.bebop.event.PrintListener; -import com.arsdigita.bebop.parameters.BigDecimalParameter; -import com.arsdigita.cms.CMS; -import org.librecms.contentsection.ContentSection; -import com.arsdigita.cms.dispatcher.Utilities; -import com.arsdigita.cms.ui.authoring.CreationSelector; -import com.arsdigita.cms.ui.authoring.NewItemForm; -import com.arsdigita.cms.ui.folder.FolderCreator; -import com.arsdigita.cms.ui.folder.FolderEditor; -import com.arsdigita.cms.ui.folder.FolderManipulator; -import com.arsdigita.cms.ui.folder.FolderRequestLocal; -import com.arsdigita.cms.ui.folder.FolderSelectionModel; -import com.arsdigita.cms.ui.folder.ItemPath; -import com.arsdigita.cms.ui.permissions.CMSPermissionsPane; -import com.arsdigita.cms.util.GlobalizationUtil; -import com.arsdigita.domain.DomainObjectFactory; -import com.arsdigita.globalization.GlobalizedMessage; -import com.arsdigita.kernel.ACSObject; -import com.arsdigita.kernel.Kernel; -import com.arsdigita.kernel.User; -import com.arsdigita.kernel.permissions.ObjectPermissionCollection; -import com.arsdigita.kernel.permissions.PermissionDescriptor; -import com.arsdigita.kernel.permissions.PermissionService; -import com.arsdigita.kernel.permissions.PrivilegeDescriptor; -import com.arsdigita.persistence.DataObject; -import com.arsdigita.persistence.DataQuery; -import com.arsdigita.persistence.SessionManager; -import com.arsdigita.toolbox.ui.ActionGroup; -import com.arsdigita.util.Assert; -import com.arsdigita.web.Web; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Encapsulates a {@link FolderManipulator} in order to create a flat item - * listing. Also contains a new item form. - * - * @author Stanislav Freidin - * @version $Id: FlatItemList.java 1538 2007-03-23 16:26:36Z apevec $ - */ -public class FlatItemList extends SegmentedPanel - implements FormProcessListener, ChangeListener, FormSubmissionListener, - Resettable, ActionListener { - - private static final String CONTENT_TYPE_ID = "ct"; - private static final String CMS_PRIVILEGES = "com.arsdigita.cms.getPrivileges"; - private static final String PRIVILEGE = "privilege"; - private static final String PRIVILEGE_NAME = "prettyName"; - // The folder selectors - private final FolderSelectionModel m_folderSel; - private FolderRequestLocal m_folder; - private final NewItemForm m_newItem; - private final SingleSelectionModel m_typeSel; - private final CreationSelector m_selector; - private final FolderManipulator m_folderManip; - private final FolderCreator m_folderCreator; - private final ActionLink m_setHomeFolderAction; - private final ActionLink m_removeHomeFolderAction; - private final ActionLink m_createFolderAction; - private final ActionLink m_togglePrivateAction; - private final Label m_homeFolderLabel; - private final Segment m_browseSeg; - private final Segment m_newItemSeg; - private final Segment m_newFolderSeg; - private final Segment m_editFolderSeg; - private final Segment m_permissionsSeg; - private final CMSPermissionsPane m_permPane; - // Folder edit/rename functionality. - private final ActionLink m_editFolderAction; - private final FolderEditor m_folderEditor; - private final Label m_contentLabel; - private final ItemPath m_itemPath; - private final Label m_chooseLabel; - - /** - * Construct a new item listing pane. The provided folder selection model - * is used to keep track of the currently displayed folder. - * - * @param folder - * @param model maintains the currently displayed folder. - */ - public FlatItemList(final FolderRequestLocal folder, - final FolderSelectionModel model) { - m_folder = folder; - m_folderSel = model; - m_folderSel.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - PageState s = e.getPageState(); - reset(s); - } - - }); - - setIdAttr("flat-item-list"); - - m_newItemSeg = addSegment(); - m_newItemSeg.setIdAttr("folder-new-item"); - - m_newFolderSeg = addSegment(); - m_newFolderSeg.setIdAttr("folder-new-folder"); - - m_editFolderSeg = addSegment(); - m_editFolderSeg.setIdAttr("folder-edit-folder"); - - m_browseSeg = addSegment(); - m_browseSeg.setIdAttr("folder-browse"); - - final ActionGroup browseActions = new ActionGroup(); - m_browseSeg.add(browseActions); - - // The top 'browse' segment - m_contentLabel = new Label(globalize("cms.ui.contents_of"), false); - m_browseSeg.addHeader(m_contentLabel); - m_chooseLabel = new Label(globalize("cms.ui.choose_target_folder"), false); - m_browseSeg.addHeader(m_chooseLabel); - m_itemPath = new ItemPath(m_folderSel); - //m_itemPath = new Label(new PrintListener() { - // public final void prepare(final PrintEvent e) { - // final Folder f = m_folder.getFolder(e.getPageState()); - // ((Label) e.getTarget()).setLabel(f.getDisplayName()); - // } - // }); - - m_browseSeg.addHeader(m_itemPath); - m_folderManip = new FolderManipulator(m_folderSel); - m_folderManip.getItemView().addProcessListener(this); - m_folderManip.getTargetSelector().addProcessListener(this); - m_folderManip.getTargetSelector().addSubmissionListener(this); - - browseActions.setSubject(m_folderManip); - - // The actions - - m_createFolderAction = new ActionLink(new Label(globalize("cms.ui.new_folder"))); - m_createFolderAction.addActionListener(this); - browseActions.addAction(m_createFolderAction); - - m_editFolderAction = new ActionLink(new Label(globalize("cms.ui.edit_folder"))); - m_editFolderAction.addActionListener(this); - browseActions.addAction(m_editFolderAction); - - m_setHomeFolderAction = new ActionLink(new Label(globalize("cms.ui.set_home_folder"))); - m_setHomeFolderAction.addActionListener(this); - browseActions.addAction(m_setHomeFolderAction); - - m_homeFolderLabel = new Label(new PrintListener() { - @Override - public final void prepare(final PrintEvent e) { - Label label = (Label) e.getTarget(); - User user = Web.getWebContext().getUser(); - - Folder folder = Folder.getUserHomeFolder(user, CMS.getContext().getContentSection()); - if (folder != null) { - String url = folder.getContentSection().getURL() + PageLocations.SECTION_PAGE + "?" - + ContentSectionPage.SET_FOLDER + "=" + folder.getID(); - //label.setLabel("Go to home folder: " + folder.getLabel() + ""); - String[] parts = new String[3]; - parts[0] = ""; - parts[1] = url; - parts[2] = folder.getLabel(); - // label.setLabel(String.format("%s: %s", - // (String) globalize("cms.ui.go_to_home_folder").localize(), - // url, - // folder.getLabel())); - label.setLabel( globalize("cms.ui.go_to_home_folder",parts) ); - } else { - //label.setLabel("No home folder selected"); - String[] parts = new String[3]; - parts[0] = ""; - parts[1] = ""; - //label.setLabel(String.format("%s", - // (String)globalize("cms.ui.no_home_folder_selected").localize())); - label.setLabel( globalize("cms.ui.no_home_folder_selected",parts) ); - } - label.setOutputEscaping(false); - } - - }); - browseActions.addAction(m_homeFolderLabel); - - m_removeHomeFolderAction = new ActionLink(new Label(globalize("cms.ui.remove_home_folder"))); - m_removeHomeFolderAction.addActionListener(this); - browseActions.addAction(m_removeHomeFolderAction); - - /* */ - m_newItem = new SectionNewItemForm("newItem"); - m_newItem.addProcessListener(this); - browseActions.addAction(m_newItem); - - /* permission */ - m_permissionsSeg = addSegment(); - m_permissionsSeg.setIdAttr("folder-permissions"); - - final ActionGroup permActions = new ActionGroup(); - m_permissionsSeg.add(permActions); - - // The permissions segment - m_permissionsSeg.addHeader(new Label(GlobalizationUtil.globalize("cms.ui.permissions"))); - - List privs = new ArrayList(); - Map privNameMap = new HashMap(); - DataQuery query = SessionManager.getSession().retrieveQuery(CMS_PRIVILEGES); - query.addFilter("scope != 'section'"); - query.addOrder("sortOrder"); - while (query.next()) { - String privilege = (String) query.get(PRIVILEGE); - String privilegeName = (String) query.get(PRIVILEGE_NAME); - privs.add(PrivilegeDescriptor.get(privilege)); - privNameMap.put(privilege, privilegeName); - } - query.close(); - - m_permPane = - new CMSPermissionsPane((PrivilegeDescriptor[]) privs.toArray(new PrivilegeDescriptor[privs.size()]), - privNameMap, - m_folderSel); - permActions.setSubject(m_permPane); - - // An action - - m_togglePrivateAction = new ActionLink(new Label(new PrintListener() { - public void prepare(PrintEvent e) { - PageState state = e.getPageState(); - Label target = (Label) e.getTarget(); - Folder currentFolder = m_folder.getFolder(state); - // ACSObject parent = currentFolder.getParent(); - DataObject context = PermissionService.getContext(currentFolder); - if (context == null) { - target.setLabel(GlobalizationUtil - .globalize("cms.ui.restore_default_permissions")); - } else { - target.setLabel(GlobalizationUtil - .globalize("cms.ui.use_custom_permissions")); - } - } - - })); - m_togglePrivateAction.addActionListener(this); - permActions.addAction(m_togglePrivateAction); - - // The 'new item' segment - m_newItemSeg.addHeader(new Label(globalize("cms.ui.new_item"))); - m_typeSel = new ParameterSingleSelectionModel(new BigDecimalParameter(CONTENT_TYPE_ID)); - m_typeSel.addChangeListener(this); - - m_selector = new CreationSelector(m_typeSel, m_folderSel); - m_newItemSeg.add(m_selector); - //m_newItemSeg.add(new Label("
", false)); - - // The 'new folder' segment - m_newFolderSeg.addHeader(new Label(globalize("cms.ui.new_folder"))); - Form folderCreate = new Form("fcreat"); - m_folderCreator = new FolderCreator("fcreat", m_folderSel); - m_folderCreator.addSubmissionListener(this); - m_folderCreator.addProcessListener(this); - folderCreate.add(m_folderCreator); - m_newFolderSeg.add(folderCreate); - m_newFolderSeg.add(new Label("
", false)); - - m_editFolderSeg.addHeader(new Label(globalize("cms.ui.edit_folder"))); - m_folderEditor = new FolderEditor("fedit", m_folderSel); - m_folderEditor.addSubmissionListener(this); - m_folderEditor.addProcessListener(this); - - Form folderEditorForm = new Form("fedit_form"); - folderEditorForm.add(m_folderEditor); - m_editFolderSeg.add(folderEditorForm); - m_editFolderSeg.add(new Label("
", false)); - } - - @Override - public void register(Page p) { - super.register(p); - - p.setVisibleDefault(m_chooseLabel, false); - p.setVisibleDefault(m_newItemSeg, false); - p.setVisibleDefault(m_newFolderSeg, false); - p.setVisibleDefault(m_editFolderSeg, false); - - p.addComponentStateParam(this, m_typeSel.getStateParameter()); - - p.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final PageState state = e.getPageState(); - - if (state.isVisibleOnPage(FlatItemList.this)) { - showHideSegments(state); - } - } - - }); - } - - /** - * Show/hide segments based on access checks. - * - * @param state The page state - * @pre ( state != null ) - */ - private void showHideSegments(PageState state) { - SecurityManager sm = Utilities.getSecurityManager(state); - Folder folder = m_folder.getFolder(state); - Assert.exists(folder); - - // MP: This should be checked on the current folder instead of just - // the content section. - boolean newItem = - sm.canAccess(state.getRequest(), - SecurityManager.NEW_ITEM, - folder); - - if (!newItem) { - browseMode(state); - } - m_createFolderAction.setVisible(state, newItem); - m_newItem.setVisible(state, newItem); - - final boolean editItem = sm.canAccess(state.getRequest(), - SecurityManager.EDIT_ITEM, - folder); - - m_editFolderAction.setVisible(state, editItem); - - User user = (User) Kernel.getContext().getParty(); - PermissionDescriptor perm = - new PermissionDescriptor(PrivilegeDescriptor.ADMIN, - folder, - user); - - if (PermissionService.checkPermission(perm)) { - m_permissionsSeg.setVisible(state, true); - } else { - m_permissionsSeg.setVisible(state, false); - } - } - - private void browseMode(PageState s) { - m_browseSeg.setVisible(s, true); - m_permissionsSeg.setVisible(s, true); - m_chooseLabel.setVisible(s, false); - m_contentLabel.setVisible(s, true); - m_itemPath.setVisible(s, true); - m_newItemSeg.setVisible(s, false); - m_newFolderSeg.setVisible(s, false); - m_editFolderSeg.setVisible(s, false); - - m_typeSel.clearSelection(s); - } - - private void newItemMode(PageState s) { - m_permissionsSeg.setVisible(s, false); - m_newItemSeg.setVisible(s, true); - } - - private void newFolderMode(PageState s) { - m_permissionsSeg.setVisible(s, false); - m_newFolderSeg.setVisible(s, true); - } - - @Override - public void submitted(FormSectionEvent e) - throws FormProcessException { - PageState s = e.getPageState(); - if (e.getSource() == m_folderCreator - && m_folderCreator.isCancelled(s)) { - browseMode(s); - throw new FormProcessException(GlobalizationUtil.globalize("cms.ui.cancelled")); - } else if (e.getSource() == m_folderEditor && m_folderEditor.isCancelled(s)) { - browseMode(s); - throw new FormProcessException(GlobalizationUtil.globalize("cms.ui.cancelled")); - } else if (e.getSource() == m_folderManip.getTargetSelector()) { - // This only works if this submission listener is run - // after the target selector's one - if (!m_folderManip.getTargetSelector().isVisible(s)) { - browseMode(s); - } - } - } - - @Override - public void process(FormSectionEvent e) { - PageState s = e.getPageState(); - final Object source = e.getSource(); - if (source == m_newItem) { - BigDecimal typeID = m_newItem.getTypeID(s); - m_typeSel.setSelectedKey(s, typeID); - newItemMode(s); - } else if (source == m_folderCreator || source == m_folderEditor) { - browseMode(s); - } else if (source == m_folderManip.getItemView()) { - // Hide everything except for the browseSeg - m_permissionsSeg.setVisible(s, false); - m_chooseLabel.setVisible(s, true); - m_contentLabel.setVisible(s, false); - m_itemPath.setVisible(s, false); - } else if (source == m_folderManip.getTargetSelector()) { - browseMode(s); - } - } - - @Override - public void stateChanged(ChangeEvent e) { - PageState s = e.getPageState(); - if (e.getSource().equals(m_typeSel)) { - if (!m_typeSel.isSelected(s)) { - browseMode(s); - } - } - } - - @Override - public void actionPerformed(ActionEvent e) { - PageState s = e.getPageState(); - Object source = e.getSource(); - if (source == m_createFolderAction) { - newFolderMode(s); - } else if (source == m_editFolderAction) { - m_permissionsSeg.setVisible(s, false); - m_editFolderSeg.setVisible(s, true); - } else if (source == m_togglePrivateAction) { - togglePermissions(s); - } else if (source == m_setHomeFolderAction) { - User user = Web.getWebContext().getUser(); - Folder folder = m_folder.getFolder(s); - user = (User) DomainObjectFactory.newInstance(user.getOID()); - Folder.setUserHomeFolder(user, folder); - } else if( source == m_removeHomeFolderAction) { - User user = Web.getWebContext().getUser(); - ContentSection section = CMS.getContext().getContentSection(); - UserHomeFolderMap map = UserHomeFolderMap.findUserHomeFolderMap(user, section); - if (map != null) { - map.delete(); - } - } - } - - private void togglePermissions(PageState state) { - Folder currentFolder = m_folder.getFolder(state); - if (!Utilities.getSecurityManager(state).canAccess(state.getRequest(), SecurityManager.STAFF_ADMIN)) { - throw new com.arsdigita.cms.dispatcher.AccessDeniedException(); - } - DataObject context = PermissionService.getContext(currentFolder); - if (context != null) { - PermissionService.clonePermissions(currentFolder); - Folder liveFolder = (Folder) currentFolder.getLiveVersion(); - if (liveFolder != null) { - PermissionService.setContext(liveFolder, currentFolder); - } - } else { - ACSObject parent = currentFolder.getParent(); - if (parent != null) { - PermissionService.setContext(currentFolder, parent); - } else { - // if the folder has no parent, it must be a root folder or template folder - // in this case, set it's context to the ContentSection - ContentSection section = currentFolder.getContentSection(); - if (section != null) { - PermissionService.setContext(currentFolder, section); - } else { - throw new IllegalStateException("Cannot set the context for a folder with " - + "no parent and no Content Section"); - } - } - - Folder liveVersion = (Folder) currentFolder.getLiveVersion(); - if (liveVersion != null) { - ACSObject liveParent = liveVersion.getParent(); - if (liveParent != null) { - PermissionService.setContext(liveVersion, liveParent); - } else { - ContentSection liveSection = liveVersion.getContentSection(); - if (liveSection != null) { - PermissionService.setContext(liveVersion, liveSection); - } else { - throw new IllegalStateException("Cannot set the context for a folder with " - + "no parent and no Content Section"); - } - } - } - - // revoke all direct permissions so folder will only have inherited permissions - ObjectPermissionCollection perms = PermissionService.getGrantedPermissions(currentFolder.getOID()); - while (perms.next()) { - if (!perms.isInherited() && !perms.getPrivilege().equals(PrivilegeDescriptor.ADMIN)) { - PermissionDescriptor desc = new PermissionDescriptor(perms.getPrivilege(), currentFolder.getOID(), - perms.getGranteeOID()); - PermissionService.revokePermission(desc); - } - } - - if (liveVersion != null) { - ObjectPermissionCollection livePerms = PermissionService.getGrantedPermissions(liveVersion.getOID()); - while (livePerms.next()) { - if (!livePerms.isInherited()) { - PermissionDescriptor desc2 = new PermissionDescriptor(livePerms.getPrivilege(), liveVersion. - getOID(), - livePerms.getGranteeOID()); - PermissionService.revokePermission(desc2); - } - } - } - - } - m_permPane.reset(state); - } - - @Override - public void reset(PageState s) { - browseMode(s); - m_folderManip.reset(s); - // switching between folders used to keep showing the permission pane - // in the same perm mode (direct or inherited) regardless - // of the folder status - m_permPane.reset(s); - } - - public final FolderManipulator getManipulator() { - return m_folderManip; - } - - public final CMSPermissionsPane getPermissionsPane() { - return m_permPane; - } - - public void setPermissionLinkVis(PageState state) { - if (!Utilities.getSecurityManager(state). - canAccess(state.getRequest(), SecurityManager.STAFF_ADMIN)) { - m_togglePrivateAction.setVisible(state, false); - } - } - - private static class SectionNewItemForm extends NewItemForm { - - public SectionNewItemForm(String name) { - super(name); - } - - @Override - public ContentSection getContentSection(PageState s) { - return CMS.getContext().getContentSection(); - } - - } - - /** - * Getting the GlobalizedMessage using a CMS Class targetBundle. - * - * @param key The resource key - * @pre ( key != null ) - */ - private static GlobalizedMessage globalize(String key) { - return ContentSectionPage.globalize(key); - } - - private static GlobalizedMessage globalize(final String key, - final Object[] args) { - return ContentSectionPage.globalize(key, args); - } - -} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/UserAddForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/UserAddForm.java index 8969a7737..269b57d7b 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/UserAddForm.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/UserAddForm.java @@ -113,11 +113,11 @@ public abstract class UserAddForm extends SimpleContainer m_form = makeForm(name); - Label title = new Label(new GlobalizedMessage("cms.ui.matches", + final Label title = new Label(new GlobalizedMessage("cms.ui.matches", CmsConstants.CMS_BUNDLE)); title.setFontWeight(Label.BOLD); - Label label = new Label(new GlobalizedMessage( + final Label label = new Label(new GlobalizedMessage( "cms.ui.there_was_no_one_matching_the_search_criteria", CmsConstants.CMS_BUNDLE)); label.setFontWeight("em"); diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java new file mode 100755 index 000000000..300f6d57d --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.MetaForm; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SingleSelectionModel; +import com.arsdigita.bebop.form.FormErrorDisplay; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.ui.ContentItemPage; +import com.arsdigita.cms.ui.folder.FolderSelectionModel; +import com.arsdigita.cms.ui.item.ItemCreateForm; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.web.URL; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentType; +import org.librecms.contentsection.ContentTypeManager; +import org.librecms.contentsection.ContentTypeRepository; +import org.librecms.contentsection.Folder; +import org.librecms.contenttypes.AuthoringKit; +import org.librecms.contenttypes.AuthoringKitInfo; +import org.librecms.contenttypes.ContentTypeInfo; +import org.librecms.contenttypes.ContentTypesManager; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; + +/** + * An invisible component which contains all the possible creation components. + * The components are loaded from the database at construction time. The + * selector uses a {@link SingleSelectionModel} in order to get the ID of the + * current content type. + * + * Important: This component is passed in the constructor to + * every authoring kit creation component (such as {@link PageCreate}). The + * creation component is supposed to follow the following pattern: + * + *
+ *   // The member variable m_parent points to the CreationSelector
+ *   SomeContentItem item = somehowCreateTheItem(state);
+ *   item.setParent(m_parent.getFolder(state));
+ *   m_parent.editItem(state, item);
+ * 
+ * + * If the creation component wishes to cancel the creation process, it should + * call + * + *
m_parent.redirectBack(state);
+ * + * The component may also call + * + *
m_parent.getContentSection(state);
+ * + * in order to get the current content section. + * + * @author unknown + * @author Jens Pelzetter + */ +public class CreationSelector extends MetaForm { + + private static final Logger LOGGER = LogManager.getLogger( + CreationSelector.class); + + private final FolderSelectionModel folderSelectionModel; + private final SingleSelectionModel typeSelectionModel; + + private static Class[] arguments = new Class[]{ + ItemSelectionModel.class, + CreationSelector.class + }; + + private Object[] values; + + private final ItemSelectionModel itemSelectionModel; + private final LongParameter itemIdParameter; + + public static final String ITEM_ID = "iid"; + + /** + * Constructs a new CreationSelector. Load all the possible + * creation components from the database and stick them in the Map. + * + * @param typeSelectionModel the {@link SingleSelectionModel} which will + * supply a BigDecimal ID of the content type to + * instantiate + * + * @param folderSelectionModel the {@link FolderSelectionModel} containing + * the folder in which new items are to be + * created + */ + public CreationSelector(final SingleSelectionModel typeSelectionModel, + final FolderSelectionModel folderSelectionModel) { + + super("pageCreate"); + + this.typeSelectionModel = typeSelectionModel; + this.folderSelectionModel = folderSelectionModel; + + itemIdParameter = new LongParameter(ITEM_ID); + itemSelectionModel = new ItemSelectionModel(itemIdParameter); + } + + /** + * + * @param state + * + * @return + */ + @Override + public Form buildForm(final PageState state) { + final Long typeId = typeSelectionModel.getSelectedKey(state); + final Component component; + final Form returnForm = new Form("pageCreate"); + final FormErrorDisplay formErrorDisplay = new FormErrorDisplay(this); + formErrorDisplay.setStateParamsAreRegistered(false); + returnForm.add(formErrorDisplay, ColumnPanel.FULL_WIDTH + | ColumnPanel.LEFT); + + if (typeId != null) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentTypeRepository typeRepo = cdiUtil.findBean( + ContentTypeRepository.class); + final ContentTypeManager typeManager = cdiUtil.findBean( + ContentTypeManager.class); + final ContentTypesManager typesManager = cdiUtil.findBean( + ContentTypesManager.class); + + final ContentType type = typeRepo.findById(typeId); + if (type == null) { + throw new UncheckedWrapperException(String.format( + "Type with id %d not found.", typeId)); + } + final ContentTypeInfo typeInfo = typesManager.getContentTypeInfo( + type); + final AuthoringKitInfo kit = typeInfo.getAuthoringKit(); + component = instantiateKitComponent(kit, type); + if (component != null) { + returnForm.add(component); + returnForm.setMethod(Form.POST); + returnForm.setEncType("multipart/form-data"); + } + } + return returnForm; + } + + /** + * Add the item_id parameter. + * + * @param page + */ + @Override + public void register(final Page page) { + super.register(page); + page.addComponentStateParam(this, itemIdParameter); + } + + /** + * Get the creation component. + * + * @param kit + * @param type + * + * @return + */ + protected Component instantiateKitComponent(final AuthoringKitInfo kit, + final ContentType type) { + final Class createClass = kit + .getCreateComponent(); + final Object[] vals; + + try { + final ItemSelectionModel itemModel = new ItemSelectionModel(type, + itemIdParameter); + vals = new Object[]{itemModel, this}; + + final Constructor constructor + = createClass + .getConstructor(arguments); + final Component component = (Component) constructor + .newInstance(vals); + return component; + } catch (IllegalAccessException + | IllegalArgumentException + | InstantiationException + | NoSuchMethodException + | SecurityException + | InvocationTargetException ex) { + LOGGER.error("\"Failed to instantiate creation component \"{}\".", + kit.getCreateComponent().getName()); + LOGGER.error(ex); + throw new UncheckedWrapperException(String.format( + "Failed to instantiate creation component \"%s\".", + kit.getCreateComponent().getName()), + ex); + } + } + + /** + * Return the currently selected folder. Creation components will place new + * items in this folder. + * + * @param state represents the current request + * + * @return the currently selected folder, in which new items should be + * placed. + */ + public final Folder getFolder(final PageState state) { + return (Folder) folderSelectionModel.getSelectedObject(state); + } + + /** + * Return the currently selected content section. New items created by + * creation components will belong to this section. This is the content + * section to which the folder returned by {@link #getFolder getFolder} + * belongs. + * + * @param state represents the current request + * + * @return the currently selected content section. + */ + public final ContentSection getContentSection(final PageState state) { + final ContentSection section = getFolder(state).getSection(); + + return section; + } + + /** + * Forward to the item editing UI. The creation component of an authoring + * kit may call this method to indicate that the creation process is + * complete. + * + * @param state the page state + * @param item the newly created item + */ + public void editItem(final PageState state, final ContentItem item) { + final ContentSection section = getContentSection(state); + + final String nodeUrl = String.join("", URL.getDispatcherPath(), + section.getPrimaryUrl(), + "/"); + final String target = ContentItemPage.getItemURL( + nodeUrl, item.getObjectId(), ContentItemPage.AUTHORING_TAB, true); + + throw new RedirectSignal(target, true); + } + + /** + * Cancel item editing and go back to where the user came from + * + * @param state the page state + */ + public void redirectBack(final PageState state) { + typeSelectionModel.clearSelection(state); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo deleted file mode 100755 index 74ea845b6..000000000 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CreationSelector.java.todo +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.arsdigita.cms.ui.authoring; - -import com.arsdigita.bebop.ColumnPanel; -import com.arsdigita.bebop.Component; -import com.arsdigita.bebop.Form; -import com.arsdigita.bebop.MetaForm; -import com.arsdigita.bebop.Page; -import com.arsdigita.bebop.PageState; -import com.arsdigita.bebop.SingleSelectionModel; -import com.arsdigita.bebop.form.FormErrorDisplay; -import com.arsdigita.bebop.parameters.BigDecimalParameter; -import com.arsdigita.cms.AuthoringKit; -import com.arsdigita.cms.ContentBundle; -import com.arsdigita.cms.ContentItem; -import com.arsdigita.cms.ContentSection; -import com.arsdigita.cms.ContentType; -import com.arsdigita.cms.Folder; -import com.arsdigita.cms.ItemSelectionModel; -import com.arsdigita.cms.ui.ContentItemPage; -import com.arsdigita.cms.ui.folder.FolderSelectionModel; -import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.util.UncheckedWrapperException; -import com.arsdigita.web.RedirectSignal; -import com.arsdigita.web.URL; -import org.apache.log4j.Logger; - -import java.lang.reflect.Constructor; -import java.math.BigDecimal; - -/** - * An invisible component which contains all the possible creation - * components. The components are loaded from the database at - * construction time. The selector uses a {@link SingleSelectionModel} - * in order to get the ID of the current content type. - * - * Important: This component is passed in the - * constructor to every authoring kit creation component (such as - * {@link PageCreate}). The creation component is supposed to follow - * the following pattern: - * - *
- *   // The member variable m_parent points to the CreationSelector
- *   SomeContentItem item = somehowCreateTheItem(state);
- *   item.setParent(m_parent.getFolder(state));
- *   m_parent.editItem(state, item);
- * 
- * - * If the creation component wishes to cancel the creation process, - * it should call - * - *
m_parent.redirectBack(state);
- * - * The component may also call - * - *
m_parent.getContentSection(state);
- * - * in order to get the current content section. - * - * @version $Id: CreationSelector.java 2185 2011-06-20 21:16:02Z pboy $ - */ -public class CreationSelector extends MetaForm { - - private static Logger s_log = Logger.getLogger(CreationSelector.class); - - private final FolderSelectionModel m_folderSel; - private final SingleSelectionModel m_typeSel; - - private static Class[] s_args = new Class[] { - ItemSelectionModel.class, - CreationSelector.class - }; - - private Object[] m_vals; - - private ItemSelectionModel m_itemSel; - private BigDecimalParameter m_itemId; - - private ItemSelectionModel m_bundleSel; - private BigDecimalParameter m_bundleId; - - public static final String ITEM_ID = "iid"; - public static final String BUNDLE_ID = "bid"; - - /** - * Constructs a new CreationSelector. Load all the - * possible creation components from the database and stick them - * in the Map. - * - * @param typeModel the {@link SingleSelectionModel} which will - * supply a BigDecimal ID of the content type to instantiate - * - * @param folderModel the {@link FolderSelectionModel} containing - * the folder in which new items are to be created - */ - public CreationSelector(final SingleSelectionModel typeModel, - final FolderSelectionModel folderModel) { - super("pageCreate"); - - m_typeSel = typeModel; - - m_folderSel = folderModel; - m_itemId = new BigDecimalParameter(ITEM_ID); - m_bundleId = new BigDecimalParameter(BUNDLE_ID); - m_bundleSel = new ItemSelectionModel(ContentBundle.class.getName(), - ContentBundle.BASE_DATA_OBJECT_TYPE, m_bundleId); - } - - - /** - * - * @param state - * @return - */ - @Override - public Form buildForm(PageState state) { - BigDecimal typeID = (BigDecimal)m_typeSel.getSelectedKey(state); - Component c = null; - Form returnForm = new Form("pageCreate"); - FormErrorDisplay fed = new FormErrorDisplay(this); - fed.setStateParamsAreRegistered(false); - returnForm.add(fed , ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); - if (typeID != null) { - try { - ContentType type = new ContentType(typeID); - AuthoringKit kit = type.getAuthoringKit(); - if(kit != null) { - c = instantiateKitComponent(kit, type); - if(c != null) { - returnForm.add(c); - returnForm.setMethod(Form.POST); - returnForm.setEncType("multipart/form-data"); - } - } - } catch (DataObjectNotFoundException e) { - // content type not found - } - } - return returnForm; - } - - /** - * Add the item_id parameter. - * - * @param p - */ - @Override - public void register(Page p) { - super.register(p); - p.addComponentStateParam(this, m_itemId); - p.addComponentStateParam(this, m_bundleId); - } - - /** - * Get the creation component. - * - * @param kit - * @param type - * @return - */ - protected Component instantiateKitComponent - (final AuthoringKit kit, final ContentType type) { - String creatorName = kit.getCreateComponent(); - Object[] vals; - - if(creatorName == null) { - return null; - } - - try { - Class createClass = Class.forName(creatorName); - ItemSelectionModel itemModel = new ItemSelectionModel(type, m_itemId); - vals = new Object[]{itemModel, this}; - Constructor constr = createClass.getConstructor(s_args); - Component c = (Component)constr.newInstance(vals); - return c; - } catch (Exception e) { - s_log.error("Instantiation failure", e); - throw new UncheckedWrapperException ( - "Failed to instantiate creation component " + - kit.getCreateComponent() + ": " + e.getMessage(), - e); - } - } - - /** - * Return the currently selected folder. Creation components will place - * new items in this folder. - * - * @param s represents the current request - * @return the currently selected folder, in which new items should be - * placed. - */ - public final Folder getFolder(PageState s) { - return (Folder) m_folderSel.getSelectedObject(s); - } - - /** - * Return the currently selected content section. New items created by - * creation components will belong to this section. This is the content - * section to which the folder returned by {@link #getFolder getFolder} - * belongs. - * - * @param s represents the current request - * @return the currently selected content section. - */ - public final ContentSection getContentSection(PageState s) { - final ContentSection section = (ContentSection) - getFolder(s).getContentSection(); - - return section; - } - - /** - * Forward to the item editing UI. The creation component of an authoring - * kit may call this method to indicate that the creation process is - * complete. - * - * @param s the page state - * @param item the newly created item - */ - public void editItem(PageState s, ContentItem item) { - ContentSection sec = getContentSection(s); - - String nodeURL = URL.getDispatcherPath() + sec.getPath() + "/"; - String target = ContentItemPage.getItemURL - (nodeURL, item.getID(), ContentItemPage.AUTHORING_TAB,true); - - throw new RedirectSignal(target, true); - } - - /** - * Cancel item editing and go back to where the user came from - * - * @param s the page state - */ - public void redirectBack(PageState state) { - m_typeSel.clearSelection(state); - } - -} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java new file mode 100755 index 000000000..070985b57 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.bebop.form.SingleSelect; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.cms.ui.ItemSearch; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.xml.Element; + +import java.math.BigDecimal; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentType; +import org.librecms.contentsection.ContentTypeRepository; +import org.librecms.contentsection.privileges.TypePrivileges; + +import java.util.List; +import java.util.TooManyListenersException; +import java.util.stream.Collectors; + +/** + * A form element which displays a select box of all content types available + * under the given content section, and forwards to the item creation UI when + * the user selects a content type to instantiate. + * + * @author Stanislav Freidin (sfreidin@arsdigtia.com) + * @author Jens Pelzetter + */ +public abstract class NewItemForm extends Form { + + public static final String TYPE_ID = "tid"; + + private final SingleSelect typeSelect; + private final Submit submit; + private final Label emptyLabel; + private final Label createLabel; + + public NewItemForm(final String name) { + this(name, BoxPanel.HORIZONTAL); + } + + /** + * Construct a new NewItemForm. It sets a vertical BoxPanel as the component + * container. + * + * @param name the name attribute of the form. + * @param orientation + */ + public NewItemForm(final String name, final int orientation) { + + super(name, new BoxPanel(BoxPanel.VERTICAL)); + setIdAttr("new_item_form"); + + final BoxPanel panel = new BoxPanel(orientation); + panel.setWidth("2%"); + panel.setBorder(0); + + // create and add an "empty" component + emptyLabel = new Label( + new GlobalizedMessage("cms.ui.authoring.no_types_registered", + CmsConstants.CMS_BUNDLE), + false); + emptyLabel.setIdAttr("empty_label"); + panel.add(emptyLabel); + + createLabel = new Label( + new GlobalizedMessage("cms.ui.authoring.create_new", + CmsConstants.CMS_BUNDLE), + false); + createLabel.setIdAttr("create_label"); + panel.add(createLabel); + + typeSelect = new SingleSelect(new BigDecimalParameter(TYPE_ID), + OptionGroup.SortMode.ALPHABETICAL_ASCENDING); + try { + typeSelect.addPrintListener(new PrintListener() { + + // Read the content section's content types and add them as options + @Override + public void prepare(final PrintEvent event) { + final OptionGroup optionGroup = (OptionGroup) event + .getTarget(); + optionGroup.clearOptions(); + final PageState state = event.getPageState(); + + // gather the content types of this section into a list + final ContentSection section = getContentSection(state); + final ContentType parentType; + final List typesCollection; + final Long singleTypeID = (Long) state.getValue( + new LongParameter(ItemSearch.SINGLE_TYPE_PARAM)); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentTypeRepository typeRepo = cdiUtil.findBean( + ContentTypeRepository.class); + final PermissionChecker permissionChecker = cdiUtil + .findBean(PermissionChecker.class); + + if (singleTypeID == null) { + parentType = null; + } else { + parentType = typeRepo.findById(singleTypeID); + } + + typesCollection = section.getContentTypes().stream() + .filter(type -> permissionChecker.isPermitted( + TypePrivileges.USE_TYPE, + type)) + .collect(Collectors.toList()); + } + + }); + } catch (TooManyListenersException ex) { + throw new UncheckedWrapperException("Too many listeners.", ex); + } + + panel.add(typeSelect); + + submit = new Submit("new", + new GlobalizedMessage("cms.ui.authoring.go", + CmsConstants.CMS_BUNDLE)); + panel.add(submit); + + add(panel); + } + + public abstract ContentSection getContentSection(PageState state); + + /** + * + * @param state + * + * @return + */ + public Long getTypeID(final PageState state) { + return (Long) typeSelect.getValue(state); + } + + /** + * + * @return + */ + public final SingleSelect getTypeSelect() { + return typeSelect; + } + + /** + * Generate XML - show/hide labels/widgets + * + * @param state + * @param parent + */ + @Override + public void generateXML(final PageState state, final Element parent) { + + if (isVisible(state)) { + final ContentSection section = getContentSection(state); + + final List types = section.getContentTypes(); + boolean isEmpty = types.isEmpty(); + + createLabel.setVisible(state, !isEmpty); + typeSelect.setVisible(state, !isEmpty); + submit.setVisible(state, !isEmpty); + emptyLabel.setVisible(state, isEmpty); + + super.generateXML(state, parent); + } + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.todo deleted file mode 100755 index 17f626f2b..000000000 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.todo +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.arsdigita.cms.ui.authoring; - -import com.arsdigita.bebop.BoxPanel; -import com.arsdigita.bebop.Form; -import com.arsdigita.bebop.Label; -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.bebop.form.SingleSelect; -import com.arsdigita.bebop.form.Submit; -import com.arsdigita.bebop.parameters.BigDecimalParameter; -import com.arsdigita.cms.ui.ItemSearch; -import com.arsdigita.globalization.GlobalizedMessage; -import com.arsdigita.ui.admin.GlobalizationUtil; -import com.arsdigita.util.UncheckedWrapperException; -import com.arsdigita.xml.Element; - -import java.math.BigDecimal; - -import org.apache.log4j.Logger; -import org.libreccm.security.Party; -import org.librecms.CmsConstants; -import org.librecms.contentsection.ContentSection; -import org.librecms.contentsection.ContentType; - -import java.awt.image.Kernel; -import java.util.List; - -/** - * A form element which displays a select box of all content types available - * under the given content section, and forwards to the item creation UI when - * the user selects a content type to instantiate. - * - * @author Stanislav Freidin (sfreidin@arsdigtia.com) - * @version $Revision: #12 $ $DateTime: 2004/08/17 23:15:09 $ - * @version $Id: NewItemForm.java 2161 2011-02-02 00:16:13Z pboy $ - */ -public abstract class NewItemForm extends Form { - - /** - * Internal logger instance to faciliate debugging. Enable logging output by - * editing /WEB-INF/conf/log4j.properties int hte runtime environment and - * set com.arsdigita.cms.ui.authoring.NewItemForm=DEBUG by uncommenting or - * adding the line. - */ - private static final Logger s_log = Logger.getLogger(NewItemForm.class); - - private final SingleSelect m_typeWidget; - private final Submit m_submit; - private final Label m_emptyLabel; - private final Label m_createLabel; - public static final String TYPE_ID = "tid"; - - public NewItemForm(String name) { - this(name, BoxPanel.HORIZONTAL); - } - - /** - * Construct a new NewItemForm. It sets a vertical BoxPanel as the component - * container. - * - * @param name the name attribute of the form. - */ - public NewItemForm(String name, int orientation) { - - super(name, new BoxPanel(BoxPanel.VERTICAL)); - setIdAttr("new_item_form"); - - //BoxPanel panel = new BoxPanel(BoxPanel.HORIZONTAL); - BoxPanel panel = new BoxPanel(orientation); - panel.setWidth("2%"); - panel.setBorder(0); - - // create and add an "empty" component - m_emptyLabel = new Label(new GlobalizedMessage( - "cms.ui.authoring.no_types_registered", CmsConstants.CMS_BUNDLE), - false); - m_emptyLabel.setIdAttr("empty_label"); - panel.add(m_emptyLabel); - - m_createLabel = new Label(new GlobalizedMessage( - "cms.ui.authoring.create_new", CmsConstants.CMS_BUNDLE), - false); - m_createLabel.setIdAttr("create_label"); - panel.add(m_createLabel); - - m_typeWidget = new SingleSelect(new BigDecimalParameter(TYPE_ID), - OptionGroup.SortMode.ALPHABETICAL_ASCENDING); - try { - m_typeWidget.addPrintListener(new PrintListener() { - - // Read the content section's content types and add them as options - @Override - public void prepare(PrintEvent e) { - OptionGroup o = (OptionGroup) e.getTarget(); - o.clearOptions(); - PageState state = e.getPageState(); - - // gather the content types of this section into a list - ContentSection section = getContentSection(state); - ContentType parentType = null; - List typesCollection = null; - BigDecimal singleTypeID = (BigDecimal) state.getValue( - new BigDecimalParameter( - ItemSearch.SINGLE_TYPE_PARAM)); - - if (singleTypeID != null) { - try { - parentType = new ContentType(singleTypeID); - } catch (DataObjectNotFoundException ex) { - parentType = null; - } - } - - if (parentType == null) { - typesCollection = section.getCreatableContentTypes(); - } else { - typesCollection = section.getDescendantsOfContentType( - parentType); - } - - typesCollection.addOrder(ContentType.LABEL); - - if (!typesCollection.isEmpty()) { - // Add content types - while (typesCollection.next()) { - boolean list = true; - ContentType type = typesCollection.getContentType(); - if (PermissionService - .getDirectGrantedPermissions(type.getOID()) - .size() > 0) { - // chris gilbert - allow restriction of some types - // to certain users/groups. No interface to do - // this, but group could be created and permission - // granted in a content type loader - // - // can't permission filter the collection because - // most types will have no permissions granted. - // This approach involves a small overhead getting - // the count of granted permissions for each type - // (mitigated by only checking DIRECT permissions) - - Party party = Kernel.getContext().getParty(); - if (party == null) { - party = Kernel.getPublicUser(); - } - PermissionDescriptor create - = new PermissionDescriptor( - PrivilegeDescriptor - .get(SecurityManager.CMS_NEW_ITEM), - type, - party); - list = PermissionService.checkPermission(create); - - } - if (list) { - // o.addOption(new Option(type.getID().toString(), type.getName())); - o.addOption(new Option(type.getID().toString(), - new Label(type.getLabel()))); - } - - } - typesCollection.reset(); - } - } - - }); - } catch (java.util.TooManyListenersException e) { - throw new UncheckedWrapperException("Too many listeners: " + e - .getMessage(), e); - } - - panel.add(m_typeWidget); - - m_submit = new Submit("new", GlobalizationUtil.globalize( - "cms.ui.authoring.go")); - panel.add(m_submit); - - add(panel); - } - - public abstract ContentSection getContentSection(PageState state); - - /** - * - * @param state - * - * @return - */ - public BigDecimal getTypeID(PageState state) { - return (BigDecimal) m_typeWidget.getValue(state); - } - - /** - * - * @return - */ - public final SingleSelect getTypeSelect() { - return m_typeWidget; - } - - /** - * Generate XML - show/hide labels/widgets - * - * @param state - * @param parent - */ - @Override - public void generateXML(PageState state, Element parent) { - - if (isVisible(state)) { - ContentSection section = getContentSection(state); - - ContentTypeCollection c = section.getCreatableContentTypes(); - boolean isEmpty = c.isEmpty(); - c.close(); - - m_createLabel.setVisible(state, !isEmpty); - m_typeWidget.setVisible(state, !isEmpty); - m_submit.setVisible(state, !isEmpty); - m_emptyLabel.setVisible(state, isEmpty); - - super.generateXML(state, parent); - } - } - -} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPath.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPath.java new file mode 100644 index 000000000..af12367c0 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPath.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.List; + +/** + * + * @author Jens Pelzetter + */ +public class FolderPath extends List { + + public FolderPath(final FolderSelectionModel folderSelectionModel) { + super(new FolderPathListModelBuilder(folderSelectionModel)); + setAttribute("type", "item-path"); + setSelectionModel(folderSelectionModel); + } + + + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModel.java new file mode 100644 index 000000000..b0b888d2b --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModel.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.list.ListModel; + +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.Folder; +import org.librecms.contentsection.FolderManager; + +import java.util.Iterator; + +/** + * + * @author Jens Pelzetter + */ +class FolderPathListModel implements ListModel { + + private final Iterator pathFolders; + private Folder currentFolder; + + public FolderPathListModel(final Folder folder) { + pathFolders = CdiUtil.createCdiUtil().findBean(FolderManager.class) + .getParentFolders(folder).iterator(); + } + + @Override + public boolean next() { + if (pathFolders.hasNext()) { + currentFolder = pathFolders.next(); + return true; + } else { + return false; + } + } + + @Override + public Object getElement() { + return currentFolder.getName(); + } + + @Override + public String getKey() { + return Long.toString(currentFolder.getObjectId()); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModelBuilder.java new file mode 100644 index 000000000..6f3e4332b --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderPathListModelBuilder.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.List; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.list.ListModel; +import com.arsdigita.bebop.list.ListModelBuilder; +import com.arsdigita.util.LockableImpl; + +import org.librecms.contentsection.Folder; + +/** + * + * @author Jens Pelzetter + */ +public class FolderPathListModelBuilder extends LockableImpl + implements ListModelBuilder { + + private final FolderSelectionModel folderSelectionModel; + + public FolderPathListModelBuilder( + final FolderSelectionModel folderSelectionModel) { + + this.folderSelectionModel = folderSelectionModel; + } + + @Override + public ListModel makeModel(final List list, final PageState state) { + + return new FolderPathListModel(folderSelectionModel + .getSelectedObject(state)); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderRequestLocal.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderRequestLocal.java index f82279f1c..2db3c44f4 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderRequestLocal.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderRequestLocal.java @@ -26,6 +26,7 @@ import org.apache.log4j.Logger; import org.libreccm.categorization.Category; import org.libreccm.categorization.CategoryRepository; import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.Folder; public class FolderRequestLocal extends CcmObjectRequestLocal { @@ -55,8 +56,8 @@ public class FolderRequestLocal extends CcmObjectRequestLocal { } } - public final Category getFolder(final PageState state) { - return (Category) get(state); + public final Folder getFolder(final PageState state) { + return (Folder) get(state); } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderSelectionModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderSelectionModel.java index 804195576..2380dfc12 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderSelectionModel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderSelectionModel.java @@ -26,6 +26,7 @@ import com.arsdigita.ui.CcmObjectSelectionModel; import org.apache.log4j.Category; import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.Folder; /** @@ -34,15 +35,15 @@ import org.librecms.contentsection.ContentSection; * com.arsdigita.cms.Folder}. * * @author David Lutterkort - * @version $Id$ + * @author Jens Pelzetter */ -public class FolderSelectionModel extends CcmObjectSelectionModel { +public class FolderSelectionModel extends CcmObjectSelectionModel { public FolderSelectionModel(String name) { super(Category.class.getName(), name); } - public FolderSelectionModel(final SingleSelectionModel model) { + public FolderSelectionModel(final SingleSelectionModel model) { super(Category.class.getName(), model); } @@ -60,10 +61,11 @@ public class FolderSelectionModel extends CcmObjectSelectionModel { /** * Clear the selection by resetting it to the root folder id. * - * @param s represents the curent request. + * @param state represents the cuerent request. */ - public void clearSelection(PageState s) { - setSelectedKey(s, getRootFolderID(s)); + @Override + public void clearSelection(final PageState state) { + setSelectedKey(state, getRootFolderID(state)); } /** @@ -72,13 +74,13 @@ public class FolderSelectionModel extends CcmObjectSelectionModel { * is to be used outside a content section, this method has to be overriden * appropriately. * - * @param s represents the current request + * @param state represents the current request * @return the ID of the root folder * * @pre s != null * @post return != null */ - protected Long getRootFolderID(PageState s) { + protected Long getRootFolderID(final PageState state) { ContentSection sec = CMS.getContext().getContentSection(); return sec.getRootDocumentsFolder().getObjectId(); } @@ -86,8 +88,11 @@ public class FolderSelectionModel extends CcmObjectSelectionModel { /** * Return true, since this selection model will always have a folder * selected in it + * @param state + * @return */ - public boolean isSelected(PageState s) { + @Override + public boolean isSelected(final PageState state) { return true; } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java index ca3ad36bb..f3034b8e0 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java @@ -19,70 +19,21 @@ package com.arsdigita.cms.ui.folder; import com.arsdigita.bebop.List; -import com.arsdigita.bebop.PageState; -import com.arsdigita.bebop.list.ListModel; -import com.arsdigita.bebop.list.ListModelBuilder; import com.arsdigita.cms.ItemSelectionModel; -import com.arsdigita.util.LockableImpl; - -import org.libreccm.categorization.Category; -import org.libreccm.cdi.utils.CdiUtil; -import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentItemManager; -import org.librecms.contentsection.Folder; /** * Produce a list of the items starting from the selected item's root down to * the item itself. * - * @author Jens Pelzetter * @author David Lutterkort + * @author Jens Pelzetter */ public class ItemPath extends List { - public ItemPath(ItemSelectionModel folderSel) { - super(new ItemPathListModelBuilder(folderSel)); + public ItemPath(final ItemSelectionModel itemSelectionModel) { + super(new ItemPathListModelBuilder(itemSelectionModel)); setAttribute("type", "item-path"); - setSelectionModel(folderSel); + setSelectionModel(itemSelectionModel); } - public static class ItemPathListModel implements ListModel { - - private final java.util.List pathFolders; - private int index = -1; - - public ItemPathListModel(final ContentItem item) { - pathFolders = CdiUtil.createCdiUtil().findBean(ContentItemManager.class).getItemFolders(item); - } - - @Override - public boolean next() { - index++; - return index < pathFolders.size(); - } - - public Object getElement() { - return pathFolders.get(index).getName(); - } - - public String getKey() { - return Long.toString(pathFolders.get(index).getObjectId()); - } - } - - public static class ItemPathListModelBuilder extends LockableImpl - implements ListModelBuilder { - - ItemSelectionModel m_itemSel; - - public ItemPathListModelBuilder(ItemSelectionModel itemSel) { - m_itemSel = itemSel; - } - - public com.arsdigita.bebop.list.ListModel makeModel(List l, - final PageState s) { - return new ItemPathListModel((ContentItem) m_itemSel. - getSelectedObject(s)); - } - } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModel.java new file mode 100644 index 000000000..3dd0c2458 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModel.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.list.ListModel; + +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.Folder; + +import java.util.Iterator; + +/** + * Model for {@link ItemPath}. This was originally a inner class. + * + * @author David Lutterkort + * @author Jens Pelzetter + */ +class ItemPathListModel implements ListModel { + + private final Iterator pathFolders; + private Folder currentFolder; + + public ItemPathListModel(final ContentItem item) { + pathFolders = CdiUtil.createCdiUtil().findBean( + ContentItemManager.class).getItemFolders(item).iterator(); + } + + @Override + public boolean next() { + if (pathFolders.hasNext()) { + currentFolder = pathFolders.next(); + return true; + } else { + return false; + } + } + + @Override + public Object getElement() { + return currentFolder.getName(); + } + + @Override + public String getKey() { + return Long.toString(currentFolder.getObjectId()); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModelBuilder.java new file mode 100644 index 000000000..4bf4aa2c4 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPathListModelBuilder.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.List; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.list.ListModelBuilder; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.util.LockableImpl; + +/** + * ModelBuilder for {@link ItemPath}. This was originally an inner class. + * + * @author David Lutterkort + * @author Jens Pelzetter + */ +class ItemPathListModelBuilder extends LockableImpl implements ListModelBuilder { + + private final ItemSelectionModel itemSelectionModel; + + public ItemPathListModelBuilder( + final ItemSelectionModel itemSelectionModel) { + + this.itemSelectionModel = itemSelectionModel; + } + + @Override + public com.arsdigita.bebop.list.ListModel makeModel( + final List list, final PageState state) { + + return new ItemPathListModel(itemSelectionModel.getSelectedObject( + state)); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsConstants.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsConstants.java new file mode 100755 index 000000000..f8f0c07a6 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsConstants.java @@ -0,0 +1,167 @@ +/* + * 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.permissions; + +import com.arsdigita.globalization.GlobalizedMessage; + +import org.librecms.contentsection.privileges.ItemPrivileges; + +/** + * This interface is used to centralise constants and labels used in the + * Permissions UI package. + * + * @author Stefan Deusch (stefan@arsdigita.com) + * @author Jens Pelzetter + */ +class CMSPermissionsConstants { + + private CMSPermissionsConstants() { + //Nothing + } + + /** + * These are our five default privileges. + */ + public static final String[] DEFAULT_PRIVILEGES = new String[]{ + ItemPrivileges.VIEW_PUBLISHED, + ItemPrivileges.EDIT, + ItemPrivileges.CREATE_NEW, + ItemPrivileges.DELETE,}; + + public static final String BUNDLE_NAME + = "com.arsdigita.ui.permissions.PermissionsResources"; + + public static final GlobalizedMessage SEARCH_LABEL = new GlobalizedMessage( + "permissions.userSearchForm.label", BUNDLE_NAME); + + public static final GlobalizedMessage SEARCH_BUTTON = new GlobalizedMessage( + "permissions.button.search", BUNDLE_NAME); + + public static final GlobalizedMessage SAVE_BUTTON = new GlobalizedMessage( + "permissions.button.save", BUNDLE_NAME); + + public static final GlobalizedMessage NO_RESULTS = new GlobalizedMessage( + "permissions.userSearchForm.noResults", BUNDLE_NAME); + + // Direct / Indirect permissions + public static final GlobalizedMessage PERM_TABLE_DIRECT_HEADING + = new GlobalizedMessage( + "permissions.directPermissions.heading", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_DIRECT_EXPLANATION + = new GlobalizedMessage( + "permissions.directPermissions.explanation", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_INDIRECT_HEADING + = new GlobalizedMessage( + "permissions.indirectPermissions.heading", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_INDIRECT_EXPLANATION + = new GlobalizedMessage( + "permissions.indirectPermissions.explanation", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_INDIRECT_CONTEXT + = new GlobalizedMessage( + "permissions.indirectPermissions.context", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_GRANTEE + = new GlobalizedMessage( + "permissions.table.grantee", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_ACTIONS + = new GlobalizedMessage( + "permissions.table.actions", BUNDLE_NAME); + + public static final GlobalizedMessage REMOVE_ALL_CONFIRM + = new GlobalizedMessage( + "permissions.table.actions.removeAll", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_INHERITED + = new GlobalizedMessage( + "permissions.table.inherited", BUNDLE_NAME); + + public static final GlobalizedMessage PERM_TABLE_NO_PARENT_CONTEXT + = new GlobalizedMessage( + "permissions.table.parent.context.null", BUNDLE_NAME); + + // Permissions header + public static final GlobalizedMessage PAGE_TITLE = new GlobalizedMessage( + "permissions.one.title", BUNDLE_NAME); + + public static final GlobalizedMessage MAIN_SITE = new GlobalizedMessage( + "permissions.main.site", BUNDLE_NAME); + + public static final GlobalizedMessage PERSONAL_SITE = new GlobalizedMessage( + "permissions.personal.site", BUNDLE_NAME); + + public static final GlobalizedMessage PERMISSIONS_INDEX + = new GlobalizedMessage( + "permissions.index.title", BUNDLE_NAME); + + public static final GlobalizedMessage PERMISSIONS_INDEX_NAVBAR + = new GlobalizedMessage( + "permissions.index.navbarItem", BUNDLE_NAME); + + // Permissions grant form + public static final GlobalizedMessage PAGE_GRANT_TITLE + = new GlobalizedMessage( + "permissions.one.grant.title", BUNDLE_NAME); + + public static final GlobalizedMessage PAGE_GRANT_LEFT + = new GlobalizedMessage( + "permissions.one.grant.explanation.left", BUNDLE_NAME); + + public static final GlobalizedMessage PAGE_GRANT_RIGHT + = new GlobalizedMessage( + "permissions.one.grant.explanation.right", BUNDLE_NAME); + + // Access denied page + public static final GlobalizedMessage PAGE_DENIED_TITLE + = new GlobalizedMessage( + "permissions.denied.title", BUNDLE_NAME); + + // Index page + public static final GlobalizedMessage PAGE_OBJECT_INDEX + = new GlobalizedMessage( + "permissions.index.adminObjects", BUNDLE_NAME); + + public static final GlobalizedMessage PAGE_OBJECT_PANEL_TITLE + = new GlobalizedMessage( + "permissions.index.panelTitle", BUNDLE_NAME); + + public static final GlobalizedMessage PAGE_OBJECT_NONE + = new GlobalizedMessage( + "permissions.index.noAdminObjects", BUNDLE_NAME); + + // Flats for permission types + public static final int DIRECT = 0; + public static final int INHERITED = 1; + + // Form constants + public static final String OBJECT_ID = "po_id"; + public static final String DIRECT_PERMISSIONS = "direct"; + public static final String INDIRECT_PERMISSIONS = "indirect"; + public static final String SEARCH_QUERY = "query"; + public static final String PRIV_SET = "privs_set"; + + // shared query + public static final String RETRIEVE_USERS + = "com.arsdigita.kernel.RetrieveUsers"; + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsGrant.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsGrant.java new file mode 100755 index 000000000..476d43d55 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsGrant.java @@ -0,0 +1,212 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.Text; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.CheckboxGroup; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.StringParameter; + +import com.arsdigita.util.StringUtils; +import com.arsdigita.util.UncheckedWrapperException; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionManager; +import org.libreccm.security.Role; +import org.libreccm.security.RoleRepository; + +import java.util.List; +import java.util.TooManyListenersException; + +import static com.arsdigita.cms.ui.permissions.CMSPermissionsConstants.*; + +/** + * Permissions Grant container for permissions assignment. Widgets are currently + * organised on a bebop SegmentedPanel. + * + * @author Stefan Deusch (sdeusch@arsdigita.com) + * @author Jens Pelzetter + */ +class CMSPermissionsGrant { + + private final static String PARTIES_CBG = "parties_cbg"; + private final static String PRIVILEGES_CBG = "privs_cbg"; + + // data keys + private static final String USER_ID = "userID"; + private static final String SCREEN_NAME = "screenName"; + private static final String FIRST_NAME = "firstName"; + private static final String LAST_NAME = "lastName"; + + private final CMSPermissionsPane parent; + private final SegmentedPanel grantPanel; + private CheckboxGroup partiesCheckboxGroup; + private CheckboxGroup privilegesCheckboxGroup; + private Form form; + private Submit saveSubmit; + + /** + * Creates a PermissionsGrant object that will be contained with another + * component. This is currently used inside the permissions pane. + * + * @param parent the enclosing container + */ + public CMSPermissionsGrant(final CMSPermissionsPane parent) { + this.parent = parent; + makeForm(); + grantPanel = new SegmentedPanel(); + grantPanel.addSegment(new Label(PAGE_GRANT_TITLE), form); + } + + /** + * Builds the form used to grant pivileges to users and groups. + */ + private void makeForm() { + form = new Form("GrantPrivileges", new BoxPanel()); + form.setMethod(Form.POST); + form.addSubmissionListener(new GrantFormSubmissionListener()); + form.add(new Label(PAGE_GRANT_LEFT)); + partiesCheckboxGroup = new CheckboxGroup(PARTIES_CBG); + try { + partiesCheckboxGroup.addPrintListener(new UserSearchPrintListener()); + } catch (TooManyListenersException e) { + throw new UncheckedWrapperException("TooManyListeners: " + e + .getMessage(), e); + } + form.add(partiesCheckboxGroup); + + form.add(new Label(PAGE_GRANT_RIGHT)); + privilegesCheckboxGroup = new CheckboxGroup(PRIVILEGES_CBG); + try { + privilegesCheckboxGroup.addPrintListener( + new PrivilegePrintListener()); + } catch (TooManyListenersException e) { + throw new UncheckedWrapperException("TooManyListeners: " + e + .getMessage(), e); + } + form.add(privilegesCheckboxGroup); + + saveSubmit = new Submit("save", SAVE_BUTTON); + form.add(saveSubmit); + } + + /** + * Returns the SegmentedPanel with the permissions grant Form + * + * @return the SegmentedPanel with the permissions grant form + */ + public SegmentedPanel getPanel() { + return grantPanel; + } + + private class GrantFormSubmissionListener + implements FormSubmissionListener { + + @Override + public void submitted(FormSectionEvent event) throws + FormProcessException { + final PageState state = event.getPageState(); + final FormData data = event.getFormData(); + final String[] gids = (String[]) data.get(PARTIES_CBG); + final String[] privs = (String[]) data.get(PRIVILEGES_CBG); + if (privs != null && gids != null) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionManager permissionManager = cdiUtil.findBean( + PermissionManager.class); + + final Long oID = parent.getObject(state).getObjectId(); + for (String gid : gids) { + final Long gID = Long.parseLong(gid); + final CMSUserObjectStruct userObjectStruct + = new CMSUserObjectStruct(gID, + oID); + for (String priv : privs) { + permissionManager.grantPrivilege( + priv, + userObjectStruct.getRole(), + userObjectStruct.getObject()); + } + } + } + parent.showAdmin(state); + } + + } + + private class UserSearchPrintListener implements PrintListener { + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + final OptionGroup target = (OptionGroup) event.getTarget(); + + // get query string + final String search = StringUtils.stripWhiteSpace((String) state. + getValue(new StringParameter(SEARCH_QUERY))); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final RoleRepository roleRepo = cdiUtil.findBean( + RoleRepository.class); + + final List roles = roleRepo.searchByName(search); + + roles.forEach(role -> target.addOption(new Option( + Long.toString(role.getRoleId()), + new Text(role.getName())))); + } + + } + + private class PrivilegePrintListener implements PrintListener { + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + final OptionGroup target = (OptionGroup) event.getTarget(); + + // get privileges from page state + final Object[] privileges = (Object[]) state.getValue( + new ArrayParameter( + PRIV_SET)); + + // print ceckbox group with privileges + for (Object privilege : privileges) { + target.addOption(new Option((String) privilege, + new Text(parent.getPrivilegeName( + (String) privilege)))); + } + } + + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsHeader.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsHeader.java new file mode 100755 index 000000000..d481f4e67 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsHeader.java @@ -0,0 +1,99 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.DimensionalNavbar; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Link; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; + +import org.libreccm.core.CcmObject; + +import static com.arsdigita.cms.ui.permissions.CMSPermissionsConstants.*; + +/** + * + * Component that Renders the Header of the Permissions Admin pages + * + * @author sdeusch@arsdigita.com + * @author Jens Pelzetter + */ +class CMSPermissionsHeader extends BoxPanel { + + private final CMSPermissionsPane parent; + private final Label title; + + /** + * Constructor + */ + CMSPermissionsHeader(final CMSPermissionsPane parent) { + this.parent = parent; + title = new Label(); + title.addPrintListener(new PrintListener() { + + @Override + public void prepare(final PrintEvent event) { + final Label target = (Label) event.getTarget(); + target.setLabel(PAGE_TITLE); + } + + }); + title.setClassAttr("heading"); + add(title); + + // Used to render the object name in the navbar + final Label objectName = new Label(); + objectName.addPrintListener(new PrintListener() { + + public void prepare(final PrintEvent event) { + final Label target = (Label) event.getTarget(); + target.setLabel(getObjectName(event)); + } + + }); + + final DimensionalNavbar navbar = new DimensionalNavbar(); + navbar.add(new Link(new Label(PERSONAL_SITE), "/pvt/home")); + navbar.add(new Link(new Label(MAIN_SITE), "/")); + navbar.add(new Link(new Label(PERMISSIONS_INDEX), "/permissions/")); + navbar.add(objectName); + navbar.setClassAttr("permNavBar"); + add(navbar); + } + + private String getObjectName(final PrintEvent event) { + final PageState state = event.getPageState(); + final CcmObject object = parent.getObject(state); + final String objectName = String.format("%s (ID %d)", + object.getDisplayName(), + object.getObjectId()); + return objectName; + } + + /** + * Returns the object used to render the title of the panel. + */ + Label getTitle() { + return title; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsPane.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsPane.java new file mode 100755 index 000000000..7bfb2676a --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsPane.java @@ -0,0 +1,501 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.Resettable; +import com.arsdigita.bebop.SegmentedPanel; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.Text; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.RequestEvent; +import com.arsdigita.bebop.event.RequestListener; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.ParameterModel; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.dispatcher.DispatcherHelper; +import com.arsdigita.ui.CcmObjectSelectionModel; + +import com.arsdigita.util.UncheckedWrapperException; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.Role; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.Folder; +import org.librecms.contentsection.privileges.ItemPrivileges; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * A pane used to administer the permissions of one {@link + * ACSObject}. This is a reusable component that can be embedded into a page to + * provide a generic UI. The page must have the "?po_id=" parameter to supply to + * ACSObject id of the item one is managing permissions for. + * + * @author sdeusch@arsdigita.com + * @authro Jens Pelzetter + */ +public class CMSPermissionsPane extends SimpleContainer + implements Resettable, + ActionListener, + RequestListener { + + // non-shared parameter models; leave package scope for access from its members. + private ParameterModel searchString = new StringParameter( + CMSPermissionsConstants.SEARCH_QUERY); + private ParameterModel privilegeArray = new ArrayParameter( + CMSPermissionsConstants.PRIV_SET); + + private String[] privileges; + private Map privilegeNameMap; + private CMSPermissionsTables allPermissions; + private CMSPermissionsHeader PermissionsHeader; + private SimpleContainer directPermissions; + private Form roleSearchForm; + private SimpleContainer inheritedPermissions; + private SimpleComponent contextPanel; + private SimpleContainer permissionsGrantPanel; + private SimpleContainer noResultsPanel; + private ObjectAdminListing adminListing; + private CcmObjectSelectionModel selectionModel; + + private RequestLocal userObjectInfo; + + /** + * Default constructor creates components that show the default privileges + * as defined in PermissionsConstants interface + * + * @param model + */ + public CMSPermissionsPane(final CcmObjectSelectionModel model) { + this(CMSPermissionsConstants.DEFAULT_PRIVILEGES, new HashMap<>(), model); + privilegeNameMap.put("read", "Read"); + privilegeNameMap.put("write", "Write"); + privilegeNameMap.put("create", "Create"); + privilegeNameMap.put("delete", "Delete"); + privilegeNameMap.put("admin", "Admin"); + } + + /** + * Creates a PermissionsPane with components showing the privileges that are + * passed in as argument. + * + * @param privileges + * @param privilegeNameMap + * @param selectionModel + */ + public CMSPermissionsPane( + final String[] privileges, + final Map privilegeNameMap, + final CcmObjectSelectionModel selectionModel) { + + userObjectInfo = new RequestLocal() { + + @Override + protected Object initialValue(final PageState state) { + return new CMSUserObjectStruct(state, selectionModel); + } + + }; + + this.privileges = privileges; + this.selectionModel = selectionModel; + this.privilegeNameMap = privilegeNameMap; + } + + /** + * Overwrite this method to construct your default Permissions Pane with the + * components you need. You can subclass anonymously overwriting just the + * register method. Note: the getXXX methods are lazy instantiators, i.e. + * they produce the components only if not already there. (You can even + * overwrite the getXXX components with your own implementation, e.g., if + * you want to show a List instead of a Table for the direct permissions, + * but still use a Table for the inherited permissions. + * + * @param page + */ + @Override + public void register(final Page page) { + super.register(page); + + // add permissions components to this specific implementation + // add(getPermissionsHeader()); + add(getContextPanel()); + add(getDirectPermissionsPanel()); + add(getUserSearchForm()); + add(getInheritedPermissionsPanel()); + add(getPermissionGrantPanel()); + add(getNoSearchResultPanel()); + add(getAdminListingPanel()); + + // set initial visibility of components + // p.setVisibleDefault(getPermissionsHeader(), true); + page.setVisibleDefault(getDirectPermissionsPanel(), true); + page.setVisibleDefault(getUserSearchForm(), true); + page.setVisibleDefault(getInheritedPermissionsPanel(), true); + page.setVisibleDefault(getContextPanel(), true); + page.setVisibleDefault(getPermissionGrantPanel(), false); + page.setVisibleDefault(getNoSearchResultPanel(), false); + page.setVisibleDefault(getAdminListingPanel(), false); + + // p.addActionListener(this); + // p.addRequestListener(this); + // add state parameters + page.addGlobalStateParam(searchString); + page.addGlobalStateParam(privilegeArray); + + } + + /** + * Implementation of interface bebop.Resettable. Use {@code reset} to reset + * permissions component to initial state, e.g. if you embed it into another + * container. + */ + @Override + public void reset(final PageState state) { + showAdmin(state); + } + + /** + * Utility method to get the authenticated user or group + * + * @param state + * + * @return + */ + public Role getRequestingRole(final PageState state) { + return ((CMSUserObjectStruct) userObjectInfo.get(state)).getRole(); + } + + /** + * Utility method to get the ACSObject from the page state + * + * @param state + * + * @return + */ + public CcmObject getObject(final PageState state) { + return ((CMSUserObjectStruct) userObjectInfo.get(state)).getObject(); + } + + /** + * Returns the title "Permissions on object articles", e.g. + * + * @return + */ + public Label getTitle() { + return ((CMSPermissionsHeader) getPermissionsHeader()).getTitle(); + } + + /** + * Returns a string array of privilege names as defined in the constructor + * + * @return + */ + public String[] getPrivileges() { + return Arrays.copyOf(privileges, privileges.length); + } + + /** + * Produces the direct and inherited permission tables to the privileges + * defined in the constructor. + * + * @see #getDirectPermissionsPanel(), getInheritedPermissionsPanel() + */ + private CMSPermissionsTables getPermissionsTables() { + if (allPermissions == null) { + allPermissions = new CMSPermissionsTables(privileges, this); + } + return allPermissions; + } + + /** + * Returns the bebop component with a table for the direct permission on the + * privileges defined in the constructor + * + * @return + * + * @see #getInheritedPermissionsPanel() + */ + public SimpleContainer getDirectPermissionsPanel() { + directPermissions = getPermissionsTables().getPermissions( + CMSPermissionsConstants.DIRECT); + return directPermissions; + } + + /** + * Returns the bebop component with a table for the inherited permission on + * the privileges defined in the constructor. The table is non-editable. + * + * @return + * + * @see #getDirectPermissionsPanel() + */ + public SimpleContainer getInheritedPermissionsPanel() { + inheritedPermissions = getPermissionsTables() + .getPermissions(CMSPermissionsConstants.INHERITED); + return inheritedPermissions; + } + + public SimpleContainer getAdminListingPanel() { + if (adminListing == null) { + adminListing = new ObjectAdminListing(selectionModel); + } + return adminListing; + } + + /** + * This is an outstanding item. + * + * @return + */ + public SegmentedPanel getUniversalPermissionsPanel() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a bebop form for user and group search. + * + * @return + */ + public Form getUserSearchForm() { + if (roleSearchForm == null) { + roleSearchForm = new CMSUserSearchForm(this); + } + return roleSearchForm; + } + + /** + * Returns a panel with a form with 2 checkbox groups, one for parties to + * choose, one for privileges to assign. + * + * @return + */ + public SimpleContainer getPermissionGrantPanel() { + if (permissionsGrantPanel == null) { + CMSPermissionsGrant permGrant = new CMSPermissionsGrant(this); + permissionsGrantPanel = permGrant.getPanel(); + } + return permissionsGrantPanel; + } + + /** + * Returns a bebop container with the title to this object and a navigation + * bar, specific for the UI at /permissions/. + * + * @return + */ + public SimpleContainer getPermissionsHeader() { + if (PermissionsHeader == null) { + PermissionsHeader = new CMSPermissionsHeader(this); + } + return PermissionsHeader; + } + + /** + * Returns a bebop panel indicating that the user search yielded no results. + * It is customised in the xsl stylesheet. + * + * @return + */ + public SimpleContainer getNoSearchResultPanel() { + if (noResultsPanel == null) { + final Label errorMsg = new Label(CMSPermissionsConstants.NO_RESULTS); + errorMsg.setClassAttr("errorBullet"); + final BoxPanel panel = new BoxPanel(); + panel.add(errorMsg); + panel.add(new CMSUserSearchForm(this)); + noResultsPanel = new SegmentedPanel().addSegment(new Text(" "), + panel); + } + return noResultsPanel; + } + + /** + * Returns a bebop panel with a link to the permissions administration page + * of the object's direct ancestor (parent). + * + * @return + */ + public SimpleComponent getContextPanel() { + if (contextPanel == null) { + contextPanel = getPermissionsTables().makeContextPanel(); + } + return contextPanel; + } + + ParameterModel getSearchString() { + return searchString; + } + + ParameterModel getPrivilegeParam() { + return privilegeArray; + } + + CcmObjectSelectionModel getSelectionModel() { + return selectionModel; + } + + /** + * Shows panel with no results to user search. + * + * @param state + */ + public void showNoResults(final PageState state) { + getDirectPermissionsPanel().setVisible(state, false); + getInheritedPermissionsPanel().setVisible(state, false); + getContextPanel().setVisible(state, false); + getUserSearchForm().setVisible(state, false); + getPermissionGrantPanel().setVisible(state, false); + getNoSearchResultPanel().setVisible(state, true); + } + + /** + * Show the Grant privileges panel + * + * @param state + */ + public void showGrant(final PageState state) { + getDirectPermissionsPanel().setVisible(state, false); + getInheritedPermissionsPanel().setVisible(state, false); + getContextPanel().setVisible(state, false); + getUserSearchForm().setVisible(state, false); + getNoSearchResultPanel().setVisible(state, false); + getPermissionGrantPanel().setVisible(state, true); + } + + /** + * Shows the administration page of permissions to one object. + * + * @param state + */ + public void showAdmin(final PageState state) { + final CcmObject object = getObject(state); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + + final boolean canAccess; + if (object == null) { + throw new UncheckedWrapperException( + "Unexpected null value for object."); + } else if (object instanceof ContentItem) { + canAccess = permissionChecker.isPermitted(ItemPrivileges.ADMINISTER, + object); + } else if (object instanceof Folder) { + canAccess = permissionChecker.isPermitted(ItemPrivileges.ADMINISTER, + object); + } else { + throw new UncheckedWrapperException(String.format( + "The object is of type \"%s\" which is not supported here.", + object.getClass().getName())); + } + + if (canAccess) { + showCustom(state, true); + +// showCustom(state, false); + getContextPanel().setVisible(state, true); + + } else { + // do not have permission to set permissions, so don't show them + getDirectPermissionsPanel().setVisible(state, false); + getInheritedPermissionsPanel().setVisible(state, false); + getUserSearchForm().setVisible(state, false); + getContextPanel().setVisible(state, false); + } + + getPermissionGrantPanel().setVisible(state, false); + } + + @Override + public void actionPerformed(final ActionEvent event) { + + final PageState state = event.getPageState(); + + /** + * check if viewing user has admin privilege on this Object, after + * Action Event fires everytime the component is visible. + * + */ + if (this.isVisible(state)) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + + if (!permissionChecker.isPermitted(ItemPrivileges.ADMINISTER, + getObject(state))) { + try { + DispatcherHelper.sendRedirect(state.getRequest(), + state.getResponse(), + "/permissions/denied"); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + } + } + } + + public void showCustom(final PageState state, final boolean custom) { + if (custom) { + getDirectPermissionsPanel().setVisible(state, true); + getInheritedPermissionsPanel().setVisible(state, false); + getUserSearchForm().setVisible(state, true); + getAdminListingPanel().setVisible(state, true); + } else { + getDirectPermissionsPanel().setVisible(state, false); + getInheritedPermissionsPanel().setVisible(state, true); + getUserSearchForm().setVisible(state, false); + getAdminListingPanel().setVisible(state, false); + } + } + + public String getPrivilegeName(final String privilege) { + return privilegeNameMap.get(privilege); + } + + @Override + public void pageRequested(final RequestEvent event) { + // PageState s = e.getPageState(); + // ACSObject object = getObject(s); + // if (object != null) { + // DataObject context = PermissionService.getContext(object); + // if (context != null) { + // showCustom(s, false); + // } else { + // showCustom(s, true); + // } + // } else { + // throw new IllegalStateException( (String) GlobalizationUtil.globalize("cms.ui.permissions.current_object_is_null").localize()); + // } + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsTables.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsTables.java new file mode 100755 index 000000000..1d13d82ec --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSPermissionsTables.java @@ -0,0 +1,652 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.GridPanel; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.LockableImpl; +import com.arsdigita.util.StringUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.security.Permission; +import org.libreccm.security.PermissionManager; +import org.libreccm.security.Role; +import org.librecms.CmsConstants; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static com.arsdigita.cms.ui.permissions.CMSPermissionsConstants.*; + +/** + * Class to represent direct and inherited permissions of an CcmObject. This + * class provides two SegmentPanels with the direct and the inherited + * permissions tables, respectively. The expected pageState contains a variable + * "id=123" of which the permissions are rendered. The viewing user must be + * authenticated. The permissions representations can be swapped , e.g. with a + * List, if scalability warrants. + * + * @author Stefan Deusch + * @author Jens Pelzetter + */ +class CMSPermissionsTables { + + private static Logger LOGGER = LogManager.getLogger( + CMSPermissionsTables.class); + + private CMSPermissionsPane parent; + private String[] privileges; + private final GridPanel permissionsPanel[] = new GridPanel[2]; + private final int[] tableColumns = new int[2]; + + /** + * Default constructor uses the DEFAULT_PRIVILEGES as defined in + * PermissionsConstants. + */ + CMSPermissionsTables(final CMSPermissionsPane parent) { + this(DEFAULT_PRIVILEGES, parent); + } + + /** + * Constructor that takes an array of PrivilegeDescriptors and builds the + * grantee - privilege matrix. The permissions tables contain the + * set of privileges that are passed into this constructor. + * + * @param privileges the array of PrivilegeDesrciptors with which go into + * table + * @param parent the Bebop parent container + */ + CMSPermissionsTables(final String[] privileges, + final CMSPermissionsPane parent) { + + this.parent = parent; + + // fixed table information + this.privileges = privileges; + tableColumns[DIRECT] = privileges.length + 2; + tableColumns[INHERITED] = privileges.length + 1; + + // Construct Direct Permissions Panel + permissionsPanel[DIRECT] = new GridPanel(1); + final Table directTable = new Table( + new PermissionsTableModelBuilder(DIRECT), + getHeaders(DIRECT)); + directTable.setClassAttr("dataTable"); + setCellRenderers(directTable, DIRECT); + directTable.addTableActionListener( + new DirectPermissionsTableActionListener()); + permissionsPanel[DIRECT].add(new Label(new GlobalizedMessage( + "cms.ui.permissions.these_are_the_custom_permissions" + + "_that_have_been_granted_on_this_object", + CmsConstants.CMS_BUNDLE))); + permissionsPanel[DIRECT].add(directTable); + + // Construct Inherited Permissions Panel + permissionsPanel[INHERITED] = new GridPanel(1); + final Table inheritedTable = new Table( + new PermissionsTableModelBuilder(INHERITED), + getHeaders(INHERITED)); + inheritedTable.setClassAttr("dataTable"); + setCellRenderers(inheritedTable, INHERITED); + permissionsPanel[INHERITED].add(new Label(new GlobalizedMessage( + "cms.ui.permissions.these_are_the_current_permissions_for_this_folder", + CmsConstants.CMS_BUNDLE))); + permissionsPanel[INHERITED].add(inheritedTable); + + //m_permPanel[INHERITED].addSegment(new Label(PERM_TABLE_INDIRECT_HEADING), + // boxpanel); + } + + /** + * Returns the SegmentedPanel with either the direct or the indirect + * permissions table. + * + * @param use PermissionsContants.DIRECT or PermissionsContants.INHERITED + */ + GridPanel getPermissions(int type) { + return permissionsPanel[type]; + } + + /** + * Returns the set of privileges of the permission tables as a String array. + */ + String[] getPrivileges() { + return Arrays.copyOf(privileges, privileges.length); + } + + SimpleComponent makeContextPanel() { + final SimpleContainer contextPanel = new SimpleContainer(); + final Label contextLabel1 = new Label(); + contextLabel1.addPrintListener(new PrintListener() { + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + final Label label = (Label) event.getTarget(); + final CcmObject context = getContext(state); + if (context != null) { + label.setLabel(PERM_TABLE_INDIRECT_CONTEXT); + contextLabel1.setVisible(state, true); + } else { + label.setLabel(PERM_TABLE_NO_PARENT_CONTEXT); + contextLabel1.setVisible(state, false); + } + } + + }); + + final Label contextLabel2 = new Label(); + contextLabel2.addPrintListener(new PrintListener() { + + @Override + public void prepare(final PrintEvent event) { + final PageState state = event.getPageState(); + final Label label = (Label) event.getTarget(); + final CcmObject context = getContext(state); + if (context != null) { + label.setLabel(context.getDisplayName()); + } + } + + }); + contextLabel2.setFontWeight(Label.BOLD); + + contextPanel.add(contextLabel1); + contextPanel.add(contextLabel2); + return contextPanel; + } + + CcmObject getContext(final PageState state) { + + return null; + } + + private String[] getHeaders(final int type) { + String[] headers = new String[tableColumns[type]]; + headers[0] = PERM_TABLE_GRANTEE.localize() + ""; + for (int j = 0; j < privileges.length; j++) { + headers[j + 1] = parent.getPrivilegeName(privileges[j]); + } + if (type == DIRECT) { + headers[privileges.length + 1] = PERM_TABLE_ACTIONS.localize() + + ""; + } + return headers; + } + + private void setCellRenderers(final Table table, final int type) { + int j; + if (type == DIRECT) { + for (j = 1; j < table.getColumnModel().size() - 1; j++) { + table.getColumn(j).setCellRenderer( + new PermissionToggleRenderer()); + } + table.getColumn(j).setCellRenderer(new LinkRenderer()); + } else { + for (j = 1; j < table.getColumnModel().size(); j++) { + table.getColumn(j).setCellRenderer( + new PermissionStatusRenderer()); + } + } + } + + private class DirectPermissionsTableActionListener + implements TableActionListener { + + @Override + public void cellSelected(TableActionEvent event) { + + final PageState state = event.getPageState(); + final int col = event.getColumn(); + final String rowkey = (String) event.getRowKey(); + if (rowkey == null) { + return; + } + + final Table table = (Table) event.getSource(); + final int no_cols = table.getColumnModel().size(); + final int lastCol = no_cols - 1; + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionManager permissionManager = cdiUtil.findBean( + PermissionManager.class); + + if (col > 0 && col < lastCol) { + + final PermissionStatus pmds = UserPrivilegeKey + .undescribe(rowkey); + LOGGER.debug("Do perm toggle on {} for {} of {}", + pmds.getObject().getObjectId(), + pmds.getRole().getName(), + pmds.getPrivilege()); + + if (pmds.isGranted()) { + + LOGGER.debug("Do revoke permission"); + + permissionManager.revokePrivilege(pmds.getPrivilege(), + pmds.getRole(), + pmds.getObject()); + } else { + LOGGER.debug("Do grant permission"); + + permissionManager.grantPrivilege(pmds.getPrivilege(), + pmds.getRole(), + pmds.getObject()); + } + + } else if (col == lastCol) { + // Process Remove All Link + final String[] tokens = StringUtils.split(rowkey, '.'); + final Long pID = Long.parseLong(tokens[0]); + + /* + * Remove all indicated privileges from user + * enumerated in tokens array + */ + final CcmObject obj = parent.getObject(state); + final Role role = CMSUserObjectStruct.loadRole(pID); + + LOGGER.debug("Revoke all on {} for {}.", + obj.getObjectId(), + role.getName()); + + for (final String token : tokens) { + permissionManager.revokePrivilege(token, role, obj); + } + } + } + + @Override + public void headSelected(final TableActionEvent event) { + throw new UnsupportedOperationException(); + } + + } + + private final class PermissionsTableModelBuilder + extends LockableImpl implements TableModelBuilder { + + private final int m_type; + + PermissionsTableModelBuilder(final int type) { + m_type = type; + } + + /* + * this can be optimized to run the query only + * once for both tables + */ + @Override + public TableModel makeModel(final Table table, + final PageState state) { + final CcmObject object = parent.getObject(state); + final List permissions = object.getPermissions(); + + switch (m_type) { + case DIRECT: + return new DirectPermissionsTableModel(permissions, + object); + case INHERITED: + return new DirectPermissionsTableModel(permissions, + object); + default: + return null; + } + } + + } + + private class DirectPermissionsTableModel implements TableModel { + + private final List userPrivileges = new ArrayList<>(); + + private final Iterator iterator; + private Permission currentPermission; + + public DirectPermissionsTableModel(final List permissions, + final CcmObject object) { + this.iterator = permissions.iterator(); + } + + @Override + public int getColumnCount() { + return tableColumns[DIRECT]; + } + + @Override + public Object getElementAt(final int columnIndex) { + if (columnIndex == 0) { + + // the Grantee column + return currentPermission.getGrantee().getName(); + + } else if (columnIndex == getColumnCount() - 1) { + + // the Action column + return "Remove All"; + + } else { + if (userHasPermission(columnIndex - 1)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + } + + @Override + public Object getKeyAt(final int columnIndex) { + if (columnIndex == 0) { + + // the key for the grantee + return currentPermission.getGrantee().getRoleId(); + + } else if (columnIndex == getColumnCount() - 1) { + + // key for 'Remove All' link + return makeRemoveAllKey(); + + } else { + // key for a user privilege + return (new UserPrivilegeKey( + currentPermission.getObject().getObjectId(), + currentPermission.getGrantee().getRoleId(), + privileges[columnIndex - 1], + userHasPermission(columnIndex - 1))) + .toString(); + } + } + + @Override + public boolean nextRow() { + if (iterator.hasNext()) { + currentPermission = iterator.next(); + return true; + } else { + return false; + } + } + + boolean userHasPermission(final int idx) { + return userPrivileges.contains(privileges[idx]); + } + + private String makeRemoveAllKey() { + final StringBuffer buffer = new StringBuffer(); + buffer.append(Long.toString(currentPermission.getGrantee() + .getRoleId())); + for (int i = 0; i < privileges.length; i++) { + if (userHasPermission(i)) { + buffer.append(".").append(privileges[i]); + } + } + return buffer.toString(); + } + + void addPrivilege(final String privilege) { + for (String current : privileges) { + if (current.equals(current)) { + userPrivileges.add(current); + break; + } + } + } + + protected Permission getCurrentPermission() { + return currentPermission; + } + + } + + /** + * Extension of DirectPermissionsTableModel to accomodate Inherited + * permissions table model. + */ + private final class InheritedPermissionsTableModel + extends DirectPermissionsTableModel { + + public InheritedPermissionsTableModel(final List permissions, + final CcmObject object) { + super(permissions, object); + } + + @Override + public int getColumnCount() { + return tableColumns[INHERITED]; + } + + @Override + public Object getElementAt(final int columnIndex) { + if (columnIndex == 0) { + + // the Grantee column + return getCurrentPermission().getGrantee().getName(); + + } else { + if (userHasPermission(columnIndex - 1)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + } + + @Override + public Object getKeyAt(final int columnIndex) { + if (columnIndex == 0) { + // the key for the grantee + return getCurrentPermission().getGrantee().getRoleId(); + + } + // no keys for inherited permissions + return null; + } + + } + + private final class PermissionToggleRenderer implements TableCellRenderer { + + @Override + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, + final int column) { + + final ControlLink link = new ControlLink(""); + + if (((Boolean) value)) { + link.setClassAttr("checkBoxChecked"); + } else { + link.setClassAttr("checkBoxUnchecked"); + } + + return link; + } + + } + + private final class PermissionStatusRenderer implements TableCellRenderer { + + @Override + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, + final int column) { + + final Label link = new Label(); + + if (((Boolean) value)) { + link.setClassAttr("checkBoxGreyChecked"); + } else { + link.setClassAttr("checkBoxGreyUnchecked"); + } + + return link; + } + + } + + private final class LinkRenderer implements TableCellRenderer { + + @Override + public Component getComponent(final Table table, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, + final int column) { + final ControlLink link = new ControlLink((String) value); + link.setConfirmation(REMOVE_ALL_CONFIRM); + return link; + } + + } + +} + +/** + * Utility class to encode a user privilege in the bebop table + */ +final class UserPrivilegeKey { + + private final String objectId; + private final String granteeId; + private final String privilege; + private final boolean granted; + + public UserPrivilegeKey(final Long objectId, + final Long granteeId, + final String privilege, + final boolean granted) { + this.objectId = objectId.toString(); + this.granteeId = granteeId.toString(); + this.privilege = privilege; + this.granted = granted; + } + + @Override + public String toString() { + return String.format("%s{ %s }", + super.toString(), + String.join(".", privilege, + objectId, + granteeId, + Boolean.toString(granted))); + } + + /** + * Decodes the information in a key into the helper class + * + * @see PermissionStatus + */ + static PermissionStatus undescribe(final String key) { + + final int i = key.indexOf("."); + final int j = key.indexOf(".", i + 1); + final int k = key.lastIndexOf("."); + + final String privilege = key.substring(0, i); + final Long oID = Long.parseLong(key.substring(i + 1, j)); + final Long gID = Long.parseLong(key.substring(j + 1, k)); + + boolean granted = false; + final CMSUserObjectStruct uos; + try { + granted = Boolean.parseBoolean(key.substring(k + 1, k + 2)); + uos = new CMSUserObjectStruct(gID, oID); + } catch (NumberFormatException ex) { + // cannot decode + throw new IllegalArgumentException(ex.getMessage()); + } + + return new PermissionStatus(privilege, + uos.getObject(), + uos.getRole(), + granted); + } + +} + +/** + * Structure to hold a permission and its current grant state + */ +final class PermissionStatus { + + private final boolean granted; + private final CcmObject object; + private final Role role; + private final String privilege; + + PermissionStatus(final String privilege, + final CcmObject object, + final Role role, + final boolean granted) { + + this.granted = granted; + + this.object = object; + this.role = role; + this.privilege = privilege; + } + + boolean isGranted() { + return granted; + } + + CcmObject getObject() { + return object; + } + + Role getRole() { + return role; + } + + String getPrivilege() { + return privilege; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserObjectStruct.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserObjectStruct.java new file mode 100755 index 000000000..dcc1e604d --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserObjectStruct.java @@ -0,0 +1,112 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.ui.CcmObjectSelectionModel; +import com.arsdigita.util.UncheckedWrapperException; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.core.CcmObjectRepository; +import org.libreccm.security.Role; +import org.libreccm.security.RoleRepository; + +/** + * This class is mainly instantiated from a PageState It is very context + * specific for permissions. It tries to read the object_id and load the + * corresponding ACSObject, as well as the party_id and the corresponding + * entity. + * + * @author Stefan Deusch (sdeusch@arsdigita.com) + * @author Jens Pelzetter + */ +class CMSUserObjectStruct { + + private final Role role; + private final CcmObject object; + + CMSUserObjectStruct(final PageState state, + final CcmObjectSelectionModel selectionModel) { + this(getRole(state), getObject(state, selectionModel)); + } + + CMSUserObjectStruct(final Long partyId, + final Long objectId) { + this(loadRole(partyId), loadObject(objectId)); + } + + CMSUserObjectStruct(final Role role, final CcmObject object) { + this.role = role; + this.object = object; + } + + Role getRole() { + return role; + } + + CcmObject getObject() { + return object; + } + + // Utility factory methods + static CcmObject loadObject(final Long objectId) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CcmObjectRepository objectRepo = cdiUtil.findBean( + CcmObjectRepository.class); + + final CcmObject ccmObject = objectRepo.findById(objectId); + if (ccmObject == null) { + throw new UncheckedWrapperException(String.format( + "Failed to find object with ID %d.", objectId)); + } + + return ccmObject; + } + + // use in package + static Role loadRole(final Long roleId) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final RoleRepository roleRepo = cdiUtil + .findBean(RoleRepository.class); + + final Role role = roleRepo.findById(roleId); + + if (role == null) { + throw new UncheckedWrapperException(String.format( + "Failed to find party with ID %d.", roleId)); + } + + return role; + } + + public static Role getRole(final PageState state) { +// final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); +// final Shiro shiro = cdiUtil.findBean(Shiro.class); +// +// return shiro.getUser(); + return null; + } + + public static CcmObject getObject( + final PageState state, final CcmObjectSelectionModel selectionModel) { + return selectionModel.getSelectedObject(state); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserSearchForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserSearchForm.java new file mode 100755 index 000000000..15d6e601f --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/CMSUserSearchForm.java @@ -0,0 +1,136 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +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.Submit; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.parameters.NotEmptyValidationListener; +import com.arsdigita.bebop.parameters.ParameterModel; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.util.StringUtils; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.Party; +import org.libreccm.security.Role; +import org.libreccm.security.RoleRepository; + +import java.util.List; + +import static com.arsdigita.cms.ui.permissions.CMSPermissionsConstants.*; + +/** + * User Search Form for permissions. + * + * @author Stefan Deusch (stefan@arsdigita.com) + * @author Jens Pelzetter + */ +public class CMSUserSearchForm extends Form implements FormProcessListener { + + private CMSPermissionsPane parent; + private TextField searchField; + + public CMSUserSearchForm(CMSPermissionsPane parent) { + this(DEFAULT_PRIVILEGES, parent); + } + + public CMSUserSearchForm(final String[] privileges, + final CMSPermissionsPane parent) { + super("RoleSearchUsers", new SimpleContainer()); + + this.parent = parent; + setMethod(Form.POST); + + addProcessListener(this); + + add(new Label(SEARCH_LABEL)); + add(new Label(" ", false)); + + final StringParameter searchParam = new StringParameter(SEARCH_QUERY); + searchField = new TextField(searchParam); + searchField.addValidationListener(new NotEmptyValidationListener()); + searchField.setSize(20); + add(searchField, ColumnPanel.RIGHT); + + final Submit submit = new Submit(SEARCH_BUTTON); + add(submit, ColumnPanel.LEFT); + } + + @Override + public void process(final FormSectionEvent event) throws + FormProcessException { + + final PageState state = event.getPageState(); + final FormData data = event.getFormData(); + final String search = StringUtils.stripWhiteSpace((String) data.get( + SEARCH_QUERY)); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final RoleRepository roleRepo = cdiUtil.findBean(RoleRepository.class); + + final List roles = roleRepo.searchByName(search); + + if (roles.isEmpty()) { + parent.showNoResults(state); + } else { + // put search string into Page + state.setValue(getSearchString(), data.get(SEARCH_QUERY)); + + // put privileges into Page + state.setValue(getPrivilegeModel(), getPrivileges()); + + parent.showGrant(state); + } + + } + + /** + * Hide Delegate pattern, if parent's implementation changes. + */ + private ParameterModel getSearchString() { + return parent.getSearchString(); + } + + /** + * Detto + */ + private ParameterModel getPrivilegeModel() { + return parent.getPrivilegeParam(); + } + + /** + * Detto + */ + private Object[] getPrivileges() { + return parent.getPrivileges(); + } + + public TextField getSearchWidget() { + return searchField; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddAdmin.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddAdmin.java new file mode 100755 index 000000000..438192d1e --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddAdmin.java @@ -0,0 +1,286 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.Text; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.CheckboxGroup; +import com.arsdigita.bebop.form.Hidden; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.form.Widget; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.cms.ui.CMSContainer; +import com.arsdigita.cms.ui.CMSForm; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.ui.CcmObjectSelectionModel; + +import org.libreccm.core.CcmObject; +import org.libreccm.security.User; + +import com.arsdigita.util.Assert; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.PermissionManager; +import org.libreccm.security.Role; +import org.libreccm.security.RoleRepository; +import org.libreccm.security.UserRepository; +import org.librecms.CmsConstants; +import org.librecms.contentsection.privileges.ItemPrivileges; + +import java.util.List; +import java.util.TooManyListenersException; + +/** + *

+ * This component is a form for adding object administrators + * + * @author Michael Pih (pihman@arsdigita.com) + * @author Uday Mathur (umathur@arsdigita.com) + * @author Jens Pelzetter + */ +public class ObjectAddAdmin extends SimpleContainer + implements FormProcessListener { + + private final static String SEARCH_QUERY = "searchQuery"; + private final static String USERS = "roles"; + private final static String SUBMIT = "addSubmit"; + private final static String CANCEL = "addCancel"; + + private Widget searchWidget; + private final RequestLocal queryRequestLocal; + private String labelText; + private String m_submitText; + + private final CMSContainer noMatchesContainer; + private final CMSContainer matchesContainer; + + private final Form form; + private Hidden searchQuery; + private CheckboxGroup rolesCheckboxGroup; + private Submit submit; + private Submit cancel; + + private final CcmObjectSelectionModel objectSelectionModel; + + public ObjectAddAdmin( + final CcmObjectSelectionModel objectSelectionModel, + final TextField search) { + +// super(search, "ObjectAddAdmin"); + labelText = "Check the box next to the name of the person(s) to assign."; + m_submitText = "Add Members"; + searchWidget = search; + queryRequestLocal = new RequestLocal() { + + @Override + protected Object initialValue(final PageState state) { + return makeQuery(state); + } + + }; + this.objectSelectionModel = objectSelectionModel; + + form = makeForm("ObjectAddAdmin"); + final Label title = new Label(new GlobalizedMessage("cms.ui.matches", + CmsConstants.CMS_BUNDLE)); + title.setFontWeight(Label.BOLD); + + final Label label = new Label(new GlobalizedMessage( + "cms.ui.there_was_no_one_matching_the_search_criteria", + CmsConstants.CMS_BUNDLE)); + label.setFontWeight("em"); + + noMatchesContainer = new CMSContainer(); + noMatchesContainer.add(title); + noMatchesContainer.add(label); + add(noMatchesContainer); + + matchesContainer = new CMSContainer(); + matchesContainer.add(title); + matchesContainer.add(form); + add(matchesContainer); + } + + /** + * Build the form used to add roles. + * + * @param name + * + * @return The form + */ + protected Form makeForm(final String name) { + final CMSForm form = new CMSForm(name) { + + public final boolean isCancelled(final PageState state) { + return cancel.isSelected(state); + } + + }; + + // This hidden field will store the search query. A hidden widget is + // used instead of a request local variable because the search query + // should only be updated when the search form is submitted. + searchQuery = new Hidden(SEARCH_QUERY); + form.add(searchQuery, ColumnPanel.FULL_WIDTH); + + final Text label = new Text(labelText); + form.add(label, ColumnPanel.FULL_WIDTH); + + // Add the list of roles that can be added. + rolesCheckboxGroup = new CheckboxGroup(USERS); + rolesCheckboxGroup + .addValidationListener(new NotNullValidationListener()); + try { + rolesCheckboxGroup.addPrintListener(new PrintListener() { + + @Override + public void prepare(PrintEvent event) { + final CheckboxGroup target = (CheckboxGroup) event + .getTarget(); + final PageState state = event.getPageState(); + // Ensures that the init listener gets fired before the + // print listeners. + addRoles(state, target); + } + + }); + } catch (TooManyListenersException ex) { + throw new RuntimeException(ex); + } + form.add(rolesCheckboxGroup, ColumnPanel.FULL_WIDTH); + + // Submit and Cancel buttons. + final SimpleContainer container = new SimpleContainer(); + submit = new Submit(SUBMIT, m_submitText); + container.add(submit); + cancel = new Submit(CANCEL, "Cancel"); + container.add(cancel); + form.add(container, ColumnPanel.FULL_WIDTH | ColumnPanel.CENTER); + + form.addProcessListener(this); + + return form; + } + + /** + * Fetches the form for adding users. + * + * @return The "add user" form + */ + public Form getForm() { + return form; + } + + /** + * Fetches the widget that contains the search string. + * + * @return The widget that contains the search string + */ + protected Widget getSearchWidget() { + return searchQuery; + } + + /** + * Adds roles to the option group. + * + * @param state The page state + * @param target The option group + * + * @pre ( state != null && target != null ) + */ + protected void addRoles(final PageState state, final OptionGroup target) { + + @SuppressWarnings("unchecked") + final List roles = (List) queryRequestLocal.get(state); + + roles.forEach(role -> target.addOption( + new Option(Long.toString(role.getRoleId()), role.getName()))); + } + + protected List makeQuery(final PageState state) { + Assert.isTrue(objectSelectionModel.isSelected(state)); + + final CcmObject object = (CcmObject) objectSelectionModel + .getSelectedObject(state); + final String searchQuery = (String) getSearchWidget().getValue(state); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final UserRepository userRepo = cdiUtil.findBean(UserRepository.class); + + return userRepo.filtered(searchQuery); + } + + @Override + public void process(final FormSectionEvent event) throws + FormProcessException { + final FormData data = event.getFormData(); + final PageState state = event.getPageState(); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + final PermissionManager permissionManager = cdiUtil.findBean( + PermissionManager.class); + final RoleRepository roleRepo = cdiUtil.findBean(RoleRepository.class); + + final CcmObject object = objectSelectionModel.getSelectedObject(state); + + permissionChecker.checkPermission(ItemPrivileges.ADMINISTER, object); + + final String[] roleIds = (String[]) data.get("roles"); + if (roleIds != null) { + + // Add each checked user to the object + for (final String roleId : roleIds) { + final Role role = roleRepo.findById(Long.parseLong(roleId)); + if (role == null) { + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.permissions.cannot_add_user", + CmsConstants.CMS_BUNDLE)); + } + permissionManager.grantPrivilege(ItemPrivileges.ADMINISTER, + role, + object); + } + + } else { + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.permissions.no_roles_were_selected", + CmsConstants.CMS_BUNDLE)); + } + + fireCompletionEvent(state); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddSearchAdmin.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddSearchAdmin.java new file mode 100755 index 000000000..23e769065 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAddSearchAdmin.java @@ -0,0 +1,118 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.cms.ui.UserSearchForm; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.ui.CcmObjectSelectionModel; +import com.arsdigita.xml.Element; + +import org.libreccm.core.CcmObject; +import org.librecms.CmsConstants; + +/** + *

+ * This panel allows a staff administrator to search for users and add them to a + * staff role for the content section.

+ * + * @author Michael Pih (pihman@arsdigita.com) + * @author Jens Pelzetter + */ +public class ObjectAddSearchAdmin extends SimpleContainer { + + private final CcmObjectSelectionModel objectSelectionModel; + + private final UserSearchForm searchForm; + private final ObjectAddAdmin addPanel; + private final ActionLink returnLink; + + public ObjectAddSearchAdmin( + final CcmObjectSelectionModel objectSelectionModel) { + + super(); + + this.objectSelectionModel = objectSelectionModel; + + searchForm = new UserSearchForm("ObjectAdminSearch"); + add(searchForm); + + addPanel = getObjectAddAdmin(objectSelectionModel, searchForm); + add(addPanel); + + addPanel.addCompletionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + fireCompletionEvent(event.getPageState()); + } + + }); + + returnLink = new ActionLink(new GlobalizedMessage( + "cms.ui.permissions.return_to_object_info", CmsConstants.CMS_BUNDLE)); + returnLink.setClassAttr("actionLink"); + returnLink.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + fireCompletionEvent(event.getPageState()); + } + + }); + add(returnLink); + } + + /** + * Displays the appropriate form(s). + * + * @param parent + */ + @Override + public void generateXML(final PageState state, final Element parent) { + final FormData data = searchForm.getFormData(state); + final FormData data2 = addPanel.getForm().getFormData(state); + + if (data != null && (data.isSubmission() || data2.isSubmission())) { + addPanel.setVisible(state, true); + } else { + addPanel.setVisible(state, false); + } + super.generateXML(state, parent); + } + + /** + * This returns the form for adding object administrators + * + * @param model + * @param searchForm + * @return + */ + protected ObjectAddAdmin getObjectAddAdmin( + final CcmObjectSelectionModel model, + final UserSearchForm searchForm) { + return new ObjectAddAdmin(model, searchForm.getSearchWidget()); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAdminListing.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAdminListing.java new file mode 100755 index 000000000..7de302f78 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/permissions/ObjectAdminListing.java @@ -0,0 +1,259 @@ +/* + * 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.permissions; + +import com.arsdigita.bebop.ActionLink; +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.SimpleContainer; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.Text; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.dispatcher.Utilities; +import com.arsdigita.dispatcher.AccessDeniedException; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.ui.CcmObjectSelectionModel; +import com.arsdigita.util.LockableImpl; +import com.arsdigita.util.UncheckedWrapperException; + +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.security.Party; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.PermissionManager; +import org.libreccm.security.Role; +import org.libreccm.security.RoleRepository; +import org.librecms.CmsConstants; +import org.librecms.contentsection.privileges.ItemPrivileges; + +import java.math.BigDecimal; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +public class ObjectAdminListing extends SimpleContainer { + + private final Table adminTable; + private ActionLink addUserLink; + private final CcmObjectSelectionModel objectSelectionModel; + private ObjectAddSearchAdmin objectAddSearchAdmin; + + public ObjectAdminListing( + final CcmObjectSelectionModel objectSelectionModel) { + + super("cms:roleAdmin", CMS.CMS_XML_NS); + + this.objectSelectionModel = objectSelectionModel; + + adminTable = new Table(getTableModelBuilder(objectSelectionModel), + new String[]{"Member", "Action"}); + adminTable.setDefaultCellRenderer(new ObjectAdminTableRenderer()); + adminTable.setEmptyView(new Label( + "There are no administrators for this object")); + adminTable.setClassAttr("dataTable"); + adminTable.addTableActionListener(new ObjectAdminActionListener()); + + addUserLink = new ActionLink(new GlobalizedMessage( + "cms.ui.permissions.add_administrator", CmsConstants.CMS_BUNDLE)); + addUserLink.setClassAttr("actionLink"); + + objectAddSearchAdmin = getObjectAddSearchAdmin(objectSelectionModel); + + addUserLink.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + objectAddSearchAdmin.setVisible(event.getPageState(), true); + addUserLink.setVisible(event.getPageState(), false); + } + + }); + + objectAddSearchAdmin.addCompletionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + objectAddSearchAdmin.setVisible(event.getPageState(), false); + addUserLink.setVisible(event.getPageState(), true); + } + + }); + + add(adminTable); + add(addUserLink); + add(objectAddSearchAdmin); + } + + @Override + public void register(final Page page) { + super.register(page); + + page.setVisibleDefault(objectAddSearchAdmin, false); + } + + // This returns the add search admin form to use for this object + protected ObjectAddSearchAdmin getObjectAddSearchAdmin( + CcmObjectSelectionModel model) { + + return new ObjectAddSearchAdmin(model); + } + + private class ObjectAdminActionListener implements TableActionListener { + + @Override + public void cellSelected(final TableActionEvent event) { + if (event.getColumn() == 1) { + final PageState state = event.getPageState(); + + final CcmObject object = objectSelectionModel.getSelectedObject( + state); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + final PermissionManager permissionManager = cdiUtil.findBean( + PermissionManager.class); + final RoleRepository roleRepo = cdiUtil.findBean( + RoleRepository.class); + + permissionChecker.checkPermission( + ItemPrivileges.ADMINISTER, object); + + final String roleId = (String) event.getRowKey(); + final Role role = roleRepo.findById(Long.parseLong(roleId)); + if (role == null) { + throw new UncheckedWrapperException(String.format( + "No role with id %s found.", roleId)); + } + + permissionManager.revokePrivilege(ItemPrivileges.ADMINISTER, + role, + object); + } + } + + @Override + public void headSelected(final TableActionEvent event) { + } + + } + + protected TableModelBuilder getTableModelBuilder( + final CcmObjectSelectionModel model) { + + return new ObjectTableModelBuilder(model); + } + + private class ObjectTableModelBuilder extends LockableImpl + implements TableModelBuilder { + + private final CcmObjectSelectionModel model; + + ObjectTableModelBuilder(final CcmObjectSelectionModel model) { + this.model = model; + } + + @Override + public TableModel makeModel(final Table table, final PageState state) { + final CcmObject object = model.getSelectedObject(state); + + final List roles = object.getPermissions().stream() + .filter(permission -> ItemPrivileges.ADMINISTER.equals( + permission.getGrantedPrivilege())) + .map(permission -> permission.getGrantee()) + .collect(Collectors.toList()); + + return new ObjectAdminTableModel(roles); + } + + } + + private class ObjectAdminTableModel implements TableModel { + + private final Iterator roles; + private Role currentRole; + + public ObjectAdminTableModel(final List roles) { + this.roles = roles.iterator(); + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public boolean nextRow() { + if (roles.hasNext()) { + currentRole = roles.next(); + return true; + } else { + return false; + } + } + + @Override + public Object getElementAt(final int column) { + return currentRole; + } + + @Override + public Object getKeyAt(final int column) { + return currentRole.getRoleId(); + } + + } + + private class ObjectAdminTableRenderer implements TableCellRenderer { + + @Override + public Component getComponent(final Table list, + final PageState state, + final Object value, + final boolean isSelected, + final Object key, + final int row, + final int column) { + + final Role role = (Role) value; + + switch (column) { + case 0: + return new Text(role.getName()); + case 1: + return new ControlLink(new Text("remove")); + default: + throw new IllegalArgumentException("Column index " + column + + " out of bounds 0..1"); + } + } + + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java index b826c5494..dcb425c2e 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java @@ -169,6 +169,7 @@ public class ContentSectionSetup extends AbstractCcmApplicationSetup { grantPermissions(manager, rootFolder, + ItemPrivileges.ADMINISTER, ItemPrivileges.CATEGORIZE, ItemPrivileges.CREATE_NEW, ItemPrivileges.EDIT, diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java index 2ab4d480b..fb18b55d8 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java @@ -20,6 +20,7 @@ package org.librecms.contentsection; import com.arsdigita.kernel.KernelConfig; +import org.libreccm.categorization.Categorization; import org.libreccm.categorization.CategoryManager; import org.libreccm.configuration.ConfigurationManager; @@ -111,7 +112,7 @@ public class FolderManager { * and the content section to which the folder belongs are the same as for * the provided parent folder. * - * @param name The name of the new folder. + * @param name The name of the new folder. * @param parent The folder in which the new folder is generated. * * @return The new folder. @@ -120,16 +121,16 @@ public class FolderManager { public Folder createFolder(final String name, final Folder parent) { if (parent == null) { throw new IllegalArgumentException( - "Can't create a folder without a parent folder."); + "Can't create a folder without a parent folder."); } if (name == null || name.trim().isEmpty()) { throw new IllegalArgumentException( - "Can't create a folder with an empty name"); + "Can't create a folder with an empty name"); } final KernelConfig kernelConfig = confManager.findConfiguration( - KernelConfig.class); + KernelConfig.class); final Folder folder = new Folder(); folder.setName(name); @@ -147,7 +148,7 @@ public class FolderManager { public FolderIsDeletable folderIsDeletable(final Folder folder) { if (folder == null) { throw new IllegalArgumentException( - "Can't check if null is deletable."); + "Can't check if null is deletable."); } if (!folder.getSubCategories().isEmpty()) { @@ -183,20 +184,20 @@ public class FolderManager { break; case HAS_SUBCATEGORIES: throw new IllegalArgumentException(String.format( - "Can't delete folder \"%s\" because the folder is not empty", - getFolderPath(folder, true))); + "Can't delete folder \"%s\" because the folder is not empty", + getFolderPath(folder, true))); case IS_NOT_EMPTY: throw new IllegalArgumentException(String.format( - "Can't delete folder \"%s\" because the folder is not empty.", - getFolderPath(folder))); + "Can't delete folder \"%s\" because the folder is not empty.", + getFolderPath(folder))); case IS_ROOT_FOLDER: throw new IllegalArgumentException( - "The folder to delete is a root folder can can't be deleted."); + "The folder to delete is a root folder can can't be deleted."); default: throw new IllegalArgumentException(String.format( - "Unexpected return value from #folderIsDeletable: " - + "\"%s\".", - status.toString())); + "Unexpected return value from #folderIsDeletable: " + + "\"%s\".", + status.toString())); } } @@ -213,13 +214,13 @@ public class FolderManager { public void moveFolder(final Folder folder, final Folder target) { if (folder - == null) { + == null) { throw new IllegalArgumentException("Can't move folder null"); } if (target == null) { throw new IllegalArgumentException( - "Can't move a folder to folder null"); + "Can't move a folder to folder null"); } final FolderIsMovable status = folderIsMovable(folder, target); @@ -228,18 +229,18 @@ public class FolderManager { final Folder source = folder.getParentFolder(); categoryManager.removeSubCategoryFromCategory(folder, source); final boolean sameName = target.getSubCategories() - .stream() - .anyMatch(subCategory -> folder.getName().equals( - subCategory - .getName())); + .stream() + .anyMatch(subCategory -> folder.getName().equals( + subCategory + .getName())); if (sameName) { final String name = String.format("%s_1", folder.getName()); folder.setName(name); folder.setDisplayName(name); final KernelConfig kernelConfig = confManager. - findConfiguration( - KernelConfig.class); + findConfiguration( + KernelConfig.class); folder.getTitle().addValue(kernelConfig.getDefaultLocale(), name); } @@ -248,36 +249,36 @@ public class FolderManager { } case IS_ROOT_FOLDER: throw new IllegalArgumentException(String.format( - "The folder \"%s\" to move is a root folder can can't " - + "be moved.", - getFolderPath(folder))); + "The folder \"%s\" to move is a root folder can can't " + + "be moved.", + getFolderPath(folder))); case SAME_FOLDER: throw new IllegalArgumentException( - "The folder to move and the target folder are the same " - + "folder."); + "The folder to move and the target folder are the same " + + "folder."); case DIFFERENT_SECTIONS: throw new IllegalArgumentException(String.format( - "Folders can't be moved between content section. The " - + "folder \"%s\" to move belongs to section " - + "\"%s\", the target folder \"%s\" belongs to " - + "section \"%s\".", - getFolderPath(folder), - folder.getSection().getDisplayName(), - getFolderPath(target), - target.getSection().getDisplayName())); + "Folders can't be moved between content section. The " + + "folder \"%s\" to move belongs to section " + + "\"%s\", the target folder \"%s\" belongs to " + + "section \"%s\".", + getFolderPath(folder), + folder.getSection().getDisplayName(), + getFolderPath(target), + target.getSection().getDisplayName())); case DIFFERENT_TYPES: throw new IllegalArgumentException( - "The folder to move is a \"%s\"," - + "but the target folder is a \"%s\" folder."); + "The folder to move is a \"%s\"," + + "but the target folder is a \"%s\" folder."); case HAS_LIVE_ITEMS: throw new IllegalArgumentException(String.format( - "Can't move folder \"%s\" because some items in the " - + "folder or its sub folder are live.", - getFolderPath(folder, true))); + "Can't move folder \"%s\" because some items in the " + + "folder or its sub folder are live.", + getFolderPath(folder, true))); default: throw new IllegalArgumentException(String.format( - "Unexpected return value from #folderIsMovable: %s", - status.toString())); + "Unexpected return value from #folderIsMovable: %s", + status.toString())); } // if (folder.getParentFolder() @@ -345,7 +346,7 @@ public class FolderManager { if (target == null) { throw new IllegalArgumentException( - "Can't check if a server can be moved to null."); + "Can't check if a server can be moved to null."); } if (folder.getParentFolder() == null) { @@ -378,19 +379,19 @@ public class FolderManager { * @param folder The folder to check for live items. * * @return {@code true} if there any live items in the folder or its sub - * folders, {@code false} if not. + * folders, {@code false} if not. */ private boolean liveItemsInFolder(final Folder folder) { final boolean liveItemsInFolder = folder.getObjects() - .stream() - .map(categorization -> categorization.getCategorizedObject()) - .filter(object -> object instanceof ContentItem) - .map(object -> (ContentItem) object) - .anyMatch(item -> itemManager.isLive(item)); + .stream() + .map(categorization -> categorization.getCategorizedObject()) + .filter(object -> object instanceof ContentItem) + .map(object -> (ContentItem) object) + .anyMatch(item -> itemManager.isLive(item)); final boolean liveItemsInSubFolders = folder.getSubFolders() - .stream() - .anyMatch(subFolder -> liveItemsInFolder(subFolder)); + .stream() + .anyMatch(subFolder -> liveItemsInFolder(subFolder)); return liveItemsInFolder || liveItemsInSubFolders; } @@ -401,7 +402,7 @@ public class FolderManager { * @param folder The folder. * * @return The path of the folder as a UNIX-like path, but without the - * content section as prefix. + * content section as prefix. */ public String getFolderPath(final Folder folder) { return getFolderPath(folder, false); @@ -410,12 +411,12 @@ public class FolderManager { /** * Returns the path of folder. * - * @param folder The folder. + * @param folder The folder. * @param withContentSection Whether to include the content section in the - * path. + * path. * * @return The path of the folder as a UNIX-like path, optionally with the - * content section the folder belongs to as prefix.. + * content section the folder belongs to as prefix.. */ public String getFolderPath(final Folder folder, final boolean withContentSection) { @@ -443,4 +444,31 @@ public class FolderManager { } } + /** + * Creates list with a parent folders of the provided folder. + * + * @param folder The folder. + * + * @return + */ + public List getParentFolders(final Folder folder) { + + if (folder == null) { + throw new IllegalArgumentException( + "Can't create a list of parent folder for folder null."); + } + + final List folders = new ArrayList<>(); + if (folder.getParentFolder() != null) { + Folder currentFolder = folder.getParentFolder(); + while(currentFolder != null) { + folders.add(currentFolder); + currentFolder = folder.getParentFolder(); + } + } + + Collections.reverse(folders); + return folders; + } + } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/privileges/ItemPrivileges.java b/ccm-cms/src/main/java/org/librecms/contentsection/privileges/ItemPrivileges.java index 2c42a3088..ce759b3f7 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/privileges/ItemPrivileges.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/privileges/ItemPrivileges.java @@ -29,6 +29,11 @@ import org.librecms.contentsection.ContentItem; */ public final class ItemPrivileges { + /** + * Allows the user to edit the permissions for items. + */ + public static final String ADMINISTER = "administer_items"; + /** * Allows the user to approve {@link ContentItem}s. */ diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/ContentTypesManager.java b/ccm-cms/src/main/java/org/librecms/contenttypes/ContentTypesManager.java index 3915dd250..f71c172a7 100644 --- a/ccm-cms/src/main/java/org/librecms/contenttypes/ContentTypesManager.java +++ b/ccm-cms/src/main/java/org/librecms/contenttypes/ContentTypesManager.java @@ -18,7 +18,6 @@ */ package org.librecms.contenttypes; -import com.arsdigita.util.UncheckedWrapperException; import org.libreccm.modules.CcmModule; import org.librecms.contentsection.ContentItem; diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/MetaForm.java b/ccm-core/src/main/java/com/arsdigita/bebop/MetaForm.java new file mode 100755 index 000000000..04c9c7c03 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/bebop/MetaForm.java @@ -0,0 +1,188 @@ +/* + * 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.util.Traversal; +import com.arsdigita.xml.Element; + +/** + * A form that is instantiated on a per-request basis. This class + * functions as a placeholder and decorator of the request-specific form + * in a Bebop {@link Page}. + * + *

Subclasses only need to override {@link #buildForm buildForm} to + * return the request-specific form. The meta form takes care of + * interfacing that form with the normal control flow of serving a Bebop + * Page. The meta form will fool the request-specific forms + * into thinking that they are part of a static Bebop Page. + * The properties of the meta form should be used to initialize the + * correspoding properties of the request-specific form whenever + * possible. These properties include name, + * method, and encType. + * + *

Listeners can be added directly to the meta form and are run + * whenever the corresponding listeners would be run on an ordinary + * form. The source of the FormSectionEvent will be the meta + * form. + * + * @author Stas Freidin + * @author David Lutterkort + */ + +public abstract class MetaForm extends Form { + + private RequestLocal m_dynamicForm; + + /** + * Constructs a new meta form. + * + * @param name the name of the form + */ + public MetaForm(String name) { + super(name); + m_dynamicForm = new RequestLocal() { + protected Object initialValue(PageState s) { + Form result = buildForm(s); + result.getModel().mergeModel(getModel()); + // form isn't part of the page, so it is invisible + // on the page (vacuously). We should consider it + // visible iff the static container MetaForm is visible. + result.setProcessInvisible( + MetaForm.this.getProcessInvisible() || + s.isVisibleOnPage(MetaForm.this)); + result.traverse(); + Traversal t = new Traversal() { + public void act(Component c) { + c.lock(); + } + }; + t.preorder(result); + return result; + } + }; + } + + /** + * Retrieves the form for the request represented by + * state. If the form hasn't been built + * yet, calls {@link #buildForm buildForm} to build the + * form. + * + * @param state describes the current request + * @return a custom-built form for this request. + * @pre state != null + * @post return != null + */ + protected Form getDynamicForm(PageState state) { + return (Form) m_dynamicForm.get(state); + } + + /** + * Builds the dynamic form. Subclasses should override this method to + * build the form based on the request represented by state. + * + * @param state describes the current request + * @return the form to be used for this request. + * @pre state != null + * @post return != null + */ + public abstract Form buildForm(PageState state); + + /** + * Force a rebuilding and updating of the dynamic form. Calls + * buildForm again and sets the dynamic form to the form + * returned by it. + * + * @param s describes the current request + */ + public void rebuildForm(PageState s) { + m_dynamicForm.set(s, m_dynamicForm.initialValue(s)); + } + + /** + * Returns the form data constructed by {@link #process process} for the + * request described by state. If the form for this request + * hasn't been built yet, calls {@link #buildForm buildForm}. + * + * @param state describes the current request + * @return the values extracted from the HTTP request contained + * in state, or null if the form has not + * yet been processed. + * @pre state != null + */ + public FormData getFormData(PageState state) { + return getDynamicForm(state).getFormData(state); + } + + /** + * Generates the XML representing the form and its widgets, but not + * the state information, from s. The XML generation is + * delegated to the request-specific form by calling {@link + * #generateXMLSansState generateXMLSansState} on it. + * + * @param s represents the curent request + * @return the top-level element for the form. + */ + protected Element generateXMLSansState(PageState s, Element parent) { + return getDynamicForm(s).generateXMLSansState(s, parent); + } + + /** + * Processes the request-specific form for the request represented by + * state. + * + * @param state describes the current request + * @return the form data extracted from the current request. + * @pre state != null + * @post return != null + * @see Form#process Form.process(...) + * @see FormModel#process FormModel.process(...) + */ + public FormData process(PageState state) + throws FormProcessException { + + if (state.isVisibleOnPage(this)) + return getDynamicForm(state).process(state); + return null; // XXX is this ok ? + } + + /** + * Do nothing; the dynamic form will take care of the tag. + */ + protected void addMagicTag() { + return; + } + + /** + * Not implemented because meta forms currently don't support mixing static and + * dynamic widgets. + * @throws UnsupportedOperationException + */ + public void add(Component pc, int constraints) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Not implemented. + * @throws UnsupportedOperationException + */ + public Container getPanel() { + throw new UnsupportedOperationException("Not implemented"); + } +}