From e6b36307867ecb9aff5672179139670ad3449363 Mon Sep 17 00:00:00 2001 From: jensp Date: Mon, 29 Aug 2016 15:52:23 +0000 Subject: [PATCH] CCM NG: - Additional methods and implementations of ContentItemManager (not tested yet!) - Refactored several UI classes to work with CCM NG, primarly in ccm-cms/com.arsdigita.cms.ui.folder git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4231 8810af33-2d31-482b-a856-94f89814c4df --- .../cms/dispatcher/ItemResolver.java | 82 +- .../cms/ui/folder/FolderBrowser.java | 881 ++++++++++++++++++ ...ulator.java.off => FolderManipulator.java} | 520 ++++++----- .../arsdigita/cms/ui/folder/FolderTree.java | 30 + .../cms/ui/util/DefaultTableCellRenderer.java | 152 +++ .../librecms/contentsection/ContentItem.java | 46 +- .../contentsection/ContentItemManager.java | 495 +++++++++- .../contentsection/ContentItemRepository.java | 75 +- .../contentsection/ContentSectionConfig.java | 2 +- .../contentsection/ContentSectionManager.java | 32 +- ccm-core/pom.xml | 8 + .../com/arsdigita/toolbox/ui/ActionGroup.java | 98 ++ .../arsdigita/toolbox/ui/ComponentSet.java | 99 ++ .../arsdigita/toolbox/ui/FormatStandards.java | 108 +++ .../AbstractAuditedEntityRepository.java | 26 +- .../categorization/Categorization.java | 21 +- .../categorization/CategoryManager.java | 100 +- .../libreccm/security/PermissionChecker.java | 18 +- .../categorization/CategoryManagerTest.java | 31 +- .../scripts/create_ccm_core_schema.sql | 1 + .../scripts/create_ccm_core_schema.sql | 1 + .../after-add-obj-to-category.yml | 2 +- .../after-add-subcategory.yml | 2 +- .../after-create-multiple-categories.yml | 2 +- .../after-remove-obj-from-category.yml | 2 +- .../after-remove-subcategory.yml | 2 +- .../CategoryManagerTest/data.yml | 2 +- 27 files changed, 2491 insertions(+), 347 deletions(-) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java rename ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/{FolderManipulator.java.off => FolderManipulator.java} (68%) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/util/DefaultTableCellRenderer.java create mode 100755 ccm-core/src/main/java/com/arsdigita/toolbox/ui/ActionGroup.java create mode 100755 ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentSet.java create mode 100755 ccm-core/src/main/java/com/arsdigita/toolbox/ui/FormatStandards.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemResolver.java b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemResolver.java index f0a571d1c..59b094412 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemResolver.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/dispatcher/ItemResolver.java @@ -29,21 +29,24 @@ import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; /** - *

The ItemResolver is responsible for mapping a URL in a - * particular content section to a content item.

+ *

+ * The ItemResolver is responsible for mapping a URL in a particular + * content section to a content item.

* - *

As an example, here is the item resolution process for a request to + *

+ * As an example, here is the item resolution process for a request to * http://yourserver/cms/cheese:

* - *

The item resolver would be asked to map the URL stub /cheese - * in the content section mounted at /cms to a content item. To - * this end, the dispatcher calls the getItem method, passing in - * the {@link com.arsdigita.cms.ContentSection} and the URL stub for the - * item within the section, /cheese in our example. As a final - * argument, the dispatcher passes either ContentItem.DRAFT or - * ContentItem.LIVE to the ItemResolver, depending on - * whether the returned item should be the live version (for public pages) - * or the draft version (for previewing).

+ *

+ * The item resolver would be asked to map the URL stub /cheese + * in the content section mounted at /cms to a content item. To this + * end, the dispatcher calls the getItem method, passing in the + * {@link com.arsdigita.cms.ContentSection} and the URL stub for the item within + * the section, /cheese in our example. As a final argument, the + * dispatcher passes either ContentItem.DRAFT or + * ContentItem.LIVE to the ItemResolver, depending on whether + * the returned item should be the live version (for public pages) or the draft + * version (for previewing).

* * @author Michael Pih (pihman@arsdigita.com) * @author Stanislav Freidin (sfreidin@arsdigita.com) @@ -60,7 +63,8 @@ public interface ItemResolver { * @param context The use context * @return The content item, or null if no such item exists */ - public ContentItem getItem(ContentSection section, String url, + public ContentItem getItem(ContentSection section, + String url, String context); /** @@ -82,10 +86,11 @@ public interface ItemResolver { * @return The URL of the item * @see #getCurrentContext */ - public String generateItemURL ( - PageState state, BigDecimal itemId, String name, - ContentSection section, String context - ); + public String generateItemURL(PageState state, + Long itemId, + String name, + ContentSection section, + String context); /** * Generates a URL for a content item. @@ -99,10 +104,13 @@ public interface ItemResolver { * @return The URL of the item * @see #getCurrentContext */ - public String generateItemURL ( - PageState state, BigDecimal itemId, String name, - ContentSection section, String context, String templateContext - ); + public String generateItemURL(PageState state, + Long itemId, + String name, + ContentSection section, + String context, + String templateContext + ); /** * Generates a URL for a content item. @@ -114,9 +122,10 @@ public interface ItemResolver { * @return The URL of the item * @see #getCurrentContext */ - public String generateItemURL ( - PageState state, ContentItem item, ContentSection section, String context - ); + public String generateItemURL(PageState state, + ContentItem item, + ContentSection section, + String context); /** * Generates a URL for a content item. @@ -129,21 +138,22 @@ public interface ItemResolver { * @return The URL of the item * @see #getCurrentContext */ - public String generateItemURL ( - PageState state, ContentItem item, ContentSection section, String context, - String templateContext - ); + public String generateItemURL(PageState state, + ContentItem item, + ContentSection section, + String context, + String templateContext); /** * Return a master page based on page state (and content section). * - * @param item The content item + * @param item The content item * @param request The HTTP request * @return The master page */ public CMSPage getMasterPage(ContentItem item, HttpServletRequest request) - throws ServletException; - + throws ServletException; + /* * Having to stick the following methods, getTemplateFromURL, and @@ -152,16 +162,16 @@ public interface ItemResolver { * to be cleaned up to fix this. As it is, ItemResolver parses URL's for * template contexts, and TemplateResolver sets the actual template contexts * in the request. - */ - + */ /** * Finds the template context from the URL and returns it, if it is there. * Otherwise, returns null. + * * @param inUrl the URL from which to get the template context * @return the template context, or null if there is no template context - */ - public String getTemplateFromURL(String inUrl); - + */ + public String getTemplateFromURL(String inUrl); + /** * Removes the template context from the inUrl. * diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java new file mode 100755 index 000000000..57999f3a6 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java @@ -0,0 +1,881 @@ +/* + * 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.folder; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Image; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Link; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.PaginationModelBuilder; +import com.arsdigita.bebop.Paginator; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.TableActionAdapter; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.table.AbstractTableModelBuilder; +import com.arsdigita.bebop.table.DefaultTableCellRenderer; +import com.arsdigita.bebop.table.DefaultTableColumnModel; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.bebop.table.TableHeader; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.cms.*; +import com.arsdigita.cms.dispatcher.ItemResolver; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.kernel.KernelConfig; +import com.arsdigita.toolbox.ui.FormatStandards; +import com.arsdigita.util.Assert; + +import org.apache.log4j.Logger; +import org.libreccm.auditing.CcmRevision; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.servlet.ServletException; + +import org.libreccm.categorization.Categorization; +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.core.CcmObject; +import org.libreccm.security.PermissionChecker; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionManager; + +import java.util.Date; + +/** + * Browse folders and items. If the user clicks on a folder, the folder + * selection model is updated. If the user clicks on any other item, an separate + * item selection model is updated. + * + * @author Jens Pelzetter + * @author Sören Bernstein + * @author David Lutterkort + */ +public class FolderBrowser extends Table { + + private static final Logger s_log = Logger.getLogger(FolderBrowser.class); + private static GlobalizedMessage[] s_headers = { + globalize("cms.ui.folder.name"), + globalize("cms.ui.folder.languages"), + globalize("cms.ui.folder.title"), + globalize("cms.ui.folder.additionalInfo"), + globalize("cms.ui.folder.type"), + globalize("cms.ui.folder.creation_date"), + globalize("cms.ui.folder.last_modified"), + globalize("cms.ui.folder.action"), + globalize("cms.ui.folder.index")}; + private static GlobalizedMessage[] s_noIndexHeaders = { + globalize("cms.ui.folder.name"), + globalize("cms.ui.folder.languages"), + globalize("cms.ui.folder.title"), + globalize("cms.ui.folder.additionalInfo"), + globalize("cms.ui.folder.type"), + globalize("cms.ui.folder.creation_date"), + globalize("cms.ui.folder.last_modified"), + globalize("cms.ui.folder.action")}; + private static final String SORT_ACTION_UP = "sortActionUp"; + private static final String SORT_ACTION_DOWN = "sortActionDown"; + private FolderSelectionModel m_currentFolder; + private TableActionListener m_folderChanger; + private TableActionListener m_deleter; + private TableActionListener m_indexChanger; + private TableColumn m_nameColumn; + private TableColumn m_deleteColumn; + private TableColumn m_indexColumn; + private final static String SORT_KEY_NAME = "name"; + private final static String SORT_KEY_TITLE = "title"; + private final static String SORT_KEY_LAST_MODIFIED_DATE = "lastModified"; + private final static String SORT_KEY_CREATION_DATE = "creationDate"; + private StringParameter m_sortType = new StringParameter("sortType"); + private StringParameter m_sortDirection = new StringParameter("sortDirn"); + private StringParameter m_aToZfilter = null; + private StringParameter m_filter = null; + private FolderManipulator.FilterForm m_filterForm; + private long m_folderSize; + + public FolderBrowser(FolderSelectionModel currentFolder) { + //super(new FolderTableModelBuilder(), s_headers); + super(); + m_sortType.setDefaultValue(SORT_KEY_NAME); + m_sortDirection.setDefaultValue(SORT_ACTION_UP); + + setModelBuilder(new FolderTableModelBuilder(currentFolder)); + setColumnModel(new DefaultTableColumnModel(hideIndexColumn() + ? s_noIndexHeaders + : s_headers)); + setHeader(new TableHeader(getColumnModel())); + + m_currentFolder = currentFolder; + + /* + * + * This code should be uncommented if the desired behaviour is for a + * change of folder to cause reversion to default ordering of contained + * items (by name ascending). Our feeling is that the user selected + * ordering should be retained for the duration of the folder browsing + * session. If anyone wants this alternative behaviour it should be + * brought in under the control of a config parameter. + * + * m_currentFolder.addChangeListener(new ChangeListener() { + * + * public void stateChanged(ChangeEvent e) { PageState state = + * e.getPageState(); state.setValue(m_sortType, + * m_sortType.getDefaultValue()); state.setValue(m_sortDirection, + * m_sortDirection.getDefaultValue()); + * + * }}); + */ + setClassAttr("dataTable"); + + getHeader().setDefaultRenderer( + new com.arsdigita.cms.ui.util.DefaultTableCellRenderer()); + + m_nameColumn = getColumn(0); + m_nameColumn.setCellRenderer(new NameCellRenderer()); + m_nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME)); + getColumn(1).setCellRenderer(new LanguagesCellRenderer()); + getColumn(2).setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_TITLE)); + getColumn(5).setHeaderRenderer(new HeaderCellRenderer( + SORT_KEY_CREATION_DATE)); + getColumn(6).setHeaderRenderer(new HeaderCellRenderer( + SORT_KEY_LAST_MODIFIED_DATE)); + m_deleteColumn = getColumn(7); + m_deleteColumn.setCellRenderer(new ActionCellRenderer()); + m_deleteColumn.setAlign("center"); + m_folderChanger = new FolderChanger(); + addTableActionListener(m_folderChanger); + + m_deleter = new ItemDeleter(); + addTableActionListener(m_deleter); + + setEmptyView(new Label(globalize("cms.ui.folder.no_items"))); + + Assert.exists(m_currentFolder.getStateParameter()); + } + + @Override + public void register(Page p) { + super.register(p); + + p.addComponentStateParam(this, m_currentFolder.getStateParameter()); + p.addComponentStateParam(this, m_sortType); + p.addComponentStateParam(this, m_sortDirection); + p.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + final PageState state = e.getPageState(); + + if (state.isVisibleOnPage(FolderBrowser.this)) { + showHideFolderActions(state); + } + } + + }); + } + + private void showHideFolderActions(PageState state) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + Category folder = (Category) m_currentFolder.getSelectedObject(state); + Assert.exists(folder); + + final boolean canDelete = permissionChecker.isPermitted( + CmsConstants.PRIVILEGE_ITEMS_DELETE, folder); + m_deleteColumn.setVisible(state, canDelete); + } + + @Override + public void respond(PageState state) throws ServletException { + String key = state.getControlEventName(); + String value = state.getControlEventValue(); + if (SORT_ACTION_UP.equals(key)) { + state.setValue(m_sortType, value); + state.setValue(m_sortDirection, SORT_ACTION_UP); + } else if (SORT_ACTION_DOWN.equals(key)) { + state.setValue(m_sortType, value); + state.setValue(m_sortDirection, SORT_ACTION_DOWN); + } else { + super.respond(state); + //throw new ServletException("Unknown control event: " + key); + } + } + + public FolderSelectionModel getFolderSelectionModel() { + return m_currentFolder; + } + + protected void setFilterForm(FolderManipulator.FilterForm filterForm) { + m_filterForm = filterForm; + } + + protected void setAtoZfilterParameter(StringParameter aToZfilter) { + m_aToZfilter = aToZfilter; + } + + protected void setFilterParameter(StringParameter filter) { + m_filter = filter; + } + + protected long getFolderSize() { + return m_folderSize; + } + + private class FolderTableModelBuilder + extends AbstractTableModelBuilder + implements PaginationModelBuilder, + FolderManipulator.FilterFormModelBuilder { + + private final FolderSelectionModel folderModel; + private final FolderBrowser folderBrowser; + private final ContentItemRepository itemRepo; + private final ConfigurationManager confManager; + private final ContentSectionManager sectionManager; + + public FolderTableModelBuilder(final FolderSelectionModel folderModel) { + this(folderModel, null); + } + + public FolderTableModelBuilder(final FolderSelectionModel folderModel, + final FolderBrowser folderBrowser) { + this.folderModel = folderModel; + this.folderBrowser = folderBrowser; + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + itemRepo = cdiUtil.findBean(ContentItemRepository.class); + confManager = cdiUtil.findBean(ConfigurationManager.class); + sectionManager = cdiUtil.findBean(ContentSectionManager.class); + } + + @Override + public TableModel makeModel(final Table table, final PageState state) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int getTotalSize(final Paginator paginator, + final PageState state) { + final Category folder = (Category) folderModel.getSelectedObject( + state); + + + /* + SELECT c.categorizedObject FROM Categorization c " + + "WHERE c.category = :folder " + + "AND TYPE(c.categorizedObject) IN ContentItem" + + "AND (LOWER(c.categorizationObject.displayName) LIKE CONCAT(LOWER(:name), '%') " + 44+ "OR LOWER(c.categorizedObject.name.value) LIKE CONCAT(:name), '%') + */ + final CriteriaBuilder criteriaBuilder = itemRepo. + getCriteriaBuilder(); + final CriteriaQuery query = criteriaBuilder. + createQuery(ContentItem.class); + final Root root = query.from(Categorization.class); + final Root itemRoot = query.from(ContentItem.class); + //final List predicates = new ArrayList<>(); + final Predicate categoryPredicate = criteriaBuilder.equal( + root.get("Categorization.category"), folder); + final Predicate typePredicate = criteriaBuilder.equal( + itemRoot.type(), ContentItem.class); + final List filters = new ArrayList<>(); + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + final Locale defaultLocale = new Locale(kernelConfig. + getDefaultLanguage()); + if (state.getValue(m_aToZfilter) != null) { + filters.add(criteriaBuilder.like(criteriaBuilder.lower( + root.get("Categorization.categorizedObject.displayName")), + String.format("%s%%", + ((String) state. + getValue( + m_aToZfilter)). + toLowerCase( + defaultLocale)))); + filters.add(criteriaBuilder.like(criteriaBuilder.lower( + root.get("Categorization.categoriziedObject.name.value")), + String.format("%s%%", + (String) state. + getValue( + m_aToZfilter)) + .toLowerCase(defaultLocale))); + } + + if (state.getValue(m_filter) != null) { + filters.add(criteriaBuilder.like(criteriaBuilder.lower( + root.get("Categorization.categorizedObject.displayName")), + String.format("%s%%", + ((String) state. + getValue( + m_filter))))); + filters.add(criteriaBuilder.like(criteriaBuilder.lower( + root.get("Categorization.categorizedObject.name.value")), + String.format("%s%%", + ((String) state. + getValue( + m_filter))))); + } + + final Predicate filtersPredicate = criteriaBuilder.or(filters. + toArray(new Predicate[filters.size()])); + final Predicate predicates = criteriaBuilder.and(categoryPredicate, + typePredicate, + filtersPredicate); + + query.where(predicates).select(itemRoot); + + return itemRepo.executeCriteriaQuery(query.where(predicates).select( + itemRoot)).size(); + } + + @Override + public boolean isVisible(final PageState state) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long getFolderSize(final PageState state) { + return itemRepo.countItemsInFolder((Category) folderModel. + getSelectedObject(state)); + } + + } + + private class HeaderCellRenderer + extends com.arsdigita.cms.ui.util.DefaultTableCellRenderer { + + private String m_key; + + public HeaderCellRenderer(String key) { + super(true); + m_key = key; + } + + @Override + public Component getComponent(final Table table, final PageState state, + Object value, + boolean isSelected, Object key, + int row, int column) { + String headerName = (String) ((GlobalizedMessage) value).localize(); + String sortKey = (String) state.getValue(m_sortType); + final boolean isCurrentKey = sortKey.equals(m_key); + final String currentSortDirection = (String) state.getValue( + m_sortDirection); + String imageURLStub; + + if (SORT_ACTION_UP.equals(currentSortDirection)) { + imageURLStub = "gray-triangle-up.gif"; + } else { + imageURLStub = "gray-triangle-down.gif"; + } + + ControlLink cl = new ControlLink(headerName) { + + @Override + public void setControlEvent(PageState ps) { + String sortDirectionAction; + // by default, everything sorts "up" unless it + // is the current key and it is already pointing up + if (SORT_ACTION_UP.equals(currentSortDirection) + && isCurrentKey) { + sortDirectionAction = SORT_ACTION_DOWN; + } else { + sortDirectionAction = SORT_ACTION_UP; + } + ps.setControlEvent(table, + sortDirectionAction, + m_key); + } + + }; + Label l = new Label(); + l.setLabel(headerName); + l.setClassAttr("folderBrowserLink"); + l.setOutputEscaping(false); + l.setFontWeight(Label.BOLD); + + SimpleContainer container = new SimpleContainer(); + container.add(l); + if (isCurrentKey) { + Image image = new Image("/assets/" + imageURLStub); + image.setBorder("0"); + container.add(image); + } + cl.setChild(container); + return cl; + } + + } + + /** + * Produce links to view an item or control links for folders to change into + * the folder. + */ + private class NameCellRenderer extends DefaultTableCellRenderer { + + public NameCellRenderer() { + super(true); + } + + @Override + public Component getComponent(Table table, + PageState state, + Object value, + boolean isSelected, + Object key, + int row, + int column) { + + final ContentItem item = (ContentItem) value; + String name = item.getDisplayName(); +// final ContentSection section = item.getContentType(). +// getContentSection(); + final ContentSection section = CMS.getContext().getContentSection(); + final ContentSectionManager sectionManager = CdiUtil.createCdiUtil() + .findBean(ContentSectionManager.class); + final ItemResolver itemResolver = sectionManager.getItemResolver( + section); + + return new Link(name, + itemResolver.generateItemURL( + state, + item.getObjectId(), + name, + section, + item.getVersion().name())); + + } + + } + + /** + * Added by: Sören Bernstein + * + * Produce links to view an item in a specific language and show all + * existing language version and the live status in the folder browser. + */ + private class LanguagesCellRenderer extends DefaultTableCellRenderer { + + public LanguagesCellRenderer() { + super(true); + } + + @Override + public Component getComponent(Table table, + PageState state, + Object value, + boolean isSelected, + Object key, + int row, + int column) { + + final ContentItem item = (ContentItem) value; + String name = item.getDisplayName(); + + final SimpleContainer container = new SimpleContainer(); + final ContentSection section = CMS.getContext().getContentSection(); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + final ContentSectionManager sectionManager = cdiUtil.findBean( + ContentSectionManager.class); + final ItemResolver itemResolver = sectionManager.getItemResolver( + section); + + item.getName().getAvailableLocales().forEach(locale -> { + final String lang = locale.toString(); + final StringBuilder fontWeight = new StringBuilder(2); + final StringBuilder styleClasses = new StringBuilder(20); + if (itemManager.isLive(item)) { + fontWeight.append(Label.BOLD); + styleClasses.append("live "); + } + + final Label langLabel = new Label(lang); + langLabel.setFontWeight(fontWeight.toString().trim()); + langLabel.setClassAttr(styleClasses.toString().trim()); + + container.add(new Link( + langLabel, + itemResolver.generateItemURL(state, + item.getObjectId(), + name, + section, + item.getVersion().name()))); + }); + + return container; + } + + } + + /** + * Produce delete links for items and non-empty folders. + */ + private static class ActionCellRenderer implements TableCellRenderer { + + private static final Label s_noAction; + private static final ControlLink s_link; + private static final Logger logger = Logger.getLogger( + ActionCellRenderer.class); + + static { + logger.debug("Static initializer is starting..."); + s_noAction = new Label(" ", false); + s_noAction.lock(); + s_link = new ControlLink( + new Label(globalize("cms.ui.folder.delete"))); + s_link.setConfirmation( + globalize("cms.ui.folder.delete_confirmation")); + logger.debug("Static initializer finished."); + } + + @Override + public Component getComponent(Table table, PageState state, Object value, + boolean isSelected, Object key, + int row, int column) { + if (((Boolean) value)) { + return s_link; + } else { + return s_noAction; + } + } + + } + + private final class IndexToggleRenderer implements TableCellRenderer { + + @Override + public Component getComponent(Table table, + PageState state, + Object value, + boolean isSelected, + Object key, + int row, + int column) { + + if (value == null) { + return new Label(new GlobalizedMessage( + "cms.ui.folder.na", + CmsConstants.CMS_FOLDER_BUNDLE)); + } + ControlLink link = new ControlLink(""); + + if (((Boolean) value)) { + link.setClassAttr("checkBoxChecked"); + } else { + link.setClassAttr("checkBoxUnchecked"); + } + + return link; + } + + } + +// Deletes an item + private class ItemDeleter extends TableActionAdapter { + + @Override + public void cellSelected(TableActionEvent e) { + int col = e.getColumn(); + + if (m_deleteColumn != getColumn(col)) { + return; + } + + PageState s = e.getPageState(); + long itemId = Long.parseLong(e.getRowKey().toString()); + + final ContentItemRepository itemRepo = CdiUtil.createCdiUtil(). + findBean(ContentItemRepository.class); + final Optional item = itemRepo.findById(itemId); + if (item.isPresent()) { + itemRepo.delete(item.get()); + } + + ((Table) e.getSource()).clearSelection(s); + } + + } + + /** + * Table model around ItemCollection + */ + private static class FolderTableModel implements TableModel { + + private static final int NAME = 0; + private static final int LANGUAGES = 1; + private static final int TITLE = 2; + private static final int ADDITIONAL_INFO = 3; + private static final int TYPE = 4; + private static final int CREATION_DATE = 5; + private static final int LAST_MODIFIED = 6; + private static final int DELETABLE = 7; + private static final int IS_INDEX = 8; + private PageState m_state; + private FolderBrowser m_table; + private List m_itemColl; + private Category m_fol; + private Long m_folIndexID; + private final ContentItemRepository itemRepo; + private final ContentItemManager itemManager; + private final CategoryManager categoryManager; + private int index = -1; + + //old constructor before using paginator + //public FolderTableModel(Folder folder) { + //m_itemColl = folder.getItems(); + //} + public FolderTableModel(FolderBrowser table, + PageState state, + List itemColl) { + m_state = state; + m_table = table; + m_itemColl = itemColl; + + m_fol = (Category) table.getFolderSelectionModel() + .getSelectedObject(state); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + itemRepo = cdiUtil.findBean(ContentItemRepository.class); + itemManager = cdiUtil.findBean(ContentItemManager.class); + categoryManager = cdiUtil.findBean(CategoryManager.class); + + if (!hideIndexColumn()) { + final Optional indexItem = categoryManager + .getIndexObject(m_fol); + if (indexItem.isPresent()) { + m_folIndexID = indexItem.get().getObjectId(); + } else { + m_folIndexID = null; + } + } + } + + @Override + public int getColumnCount() { + return 7; + } + + @Override + public boolean nextRow() { + index++; + return index < m_itemColl.size(); + } + + @Override + public Object getElementAt(int columnIndex) { + switch (columnIndex) { + case NAME: + return m_itemColl.get(index); + case LANGUAGES: + return m_itemColl.get(index); + case TITLE: + return m_itemColl.get(index).getDisplayName(); + case ADDITIONAL_INFO: + return ""; + case TYPE: + return m_itemColl.get(index).getContentType().getLabel() + .getValue(); + case CREATION_DATE: { + final CcmRevision firstRevision = itemRepo + .retrieveFirstRevision( + m_itemColl.get(index), m_itemColl.get(index) + .getObjectId()); + if (firstRevision == null) { + return "--"; + } else { + return FormatStandards.formatDate(new Date(firstRevision + .getTimestamp())); + } + } + case LAST_MODIFIED: { + final CcmRevision currentRevision = itemRepo + .retrieveCurrentRevision( + m_itemColl.get(index), + m_itemColl.get(index).getObjectId()); + if (currentRevision == null) { + return "--"; + } else { + return FormatStandards.formatDate(new Date( + currentRevision.getTimestamp())); + } + } + case DELETABLE: + return isDeletable(); + case IS_INDEX: { + if (hideIndexColumn()) { + throw new IndexOutOfBoundsException( + "Column index " + columnIndex + + " not in table model."); + } + if (m_folIndexID == null) { + return false; + } + return m_folIndexID.compareTo( + m_itemColl.get(index).getObjectId()) == 0; + } + default: + throw new IndexOutOfBoundsException("Column index " + + columnIndex + + " not in table model."); + } + } + + public boolean isDeletable() { + if (s_log.isDebugEnabled()) { + s_log.debug("Checking to see if " + this + " is deletable"); + } + +// if (m_itemColl.isFolder()) { +// +// if (!m_itemColl.hasChildren()) { +// if (s_log.isDebugEnabled()) { +// s_log.debug( +// "The item is an empty folder; it may be deleted"); +// } +// return true; +// +// } else { +// +// if (s_log.isDebugEnabled()) { +// s_log.debug( +// "The folder is not empty; it cannot be deleted"); +// } +// return false; +// +// } +// } else + if (itemManager.isLive(m_itemColl.get(index))) { + + if (s_log.isDebugEnabled()) { + s_log.debug( + "This item has a live instance; it cannot be deleted"); + } + return false; + } + + if (s_log.isDebugEnabled()) { + s_log.debug( + "The item is not a folder and doesn't have a live instance; it may be deleted"); + } + return true; + } + + public Object getKeyAt(int columnIndex) { + // Note: Folders were marked by negative IDs + return m_itemColl.get(index).getObjectId(); + } + + } + + private class FolderChanger extends TableActionAdapter { + + @Override + public void cellSelected(TableActionEvent e) { + PageState s = e.getPageState(); + int col = e.getColumn().intValue(); + + if (m_nameColumn != getColumn(col)) { + return; + } + String key = (String) e.getRowKey(); + if (key.startsWith("-")) { // XXX dirty dirty + clearSelection(s); + getFolderSelectionModel().setSelectedKey( + s, Long.parseLong(key.substring(1))); + } + } + + } + +// private class IndexChanger extends TableActionAdapter { +// +// private FolderSelectionModel m_fol; +// +// public IndexChanger(FolderSelectionModel fol) { +// super(); +// m_fol = fol; +// } +// +// @Override +// public void cellSelected(TableActionEvent e) { +// PageState state = e.getPageState(); +// +// BigDecimal rowkey = new BigDecimal((String) e.getRowKey()); +// if (rowkey == null) { +// return; +// } +// +// try { +// ContentBundle contentItem = new ContentBundle(rowkey); +// +// Folder folder = (Folder) m_fol.getSelectedObject(state); +// +// ContentBundle currentIndexItem = (ContentBundle) folder. +// getIndexItem(); +// if (currentIndexItem == null || (currentIndexItem.getID(). +// compareTo(contentItem.getID()) +// != 0)) { +// folder.setIndexItem(contentItem); +// } else { +// folder.removeIndexItem(); +// } +// folder.save(); +// } catch (DataObjectNotFoundException donfe) { +// // Do nothing +// } +// } +// +// } + + /** + * Getting the GlobalizedMessage using a CMS Class targetBundle. + * + * @param key The resource key @pre ( key != null ) + */ + private static GlobalizedMessage globalize(String key) { + return new GlobalizedMessage(key, CmsConstants.CMS_FOLDER_BUNDLE); + + } + + private static boolean hideIndexColumn() { + return true; + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java similarity index 68% rename from ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java.off rename to ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java index 896819431..63209561f 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java @@ -61,36 +61,11 @@ import com.arsdigita.bebop.table.TableCellRenderer; import com.arsdigita.bebop.table.TableColumn; import com.arsdigita.bebop.tree.TreeCellRenderer; import com.arsdigita.cms.CMS; -import com.arsdigita.cms.CMSConfig; -import com.arsdigita.cms.ContentBundle; -import com.arsdigita.cms.ContentItem; -import com.arsdigita.cms.ContentSection; -import com.arsdigita.cms.ContentTypeLifecycleDefinition; -import com.arsdigita.cms.Folder; -import com.arsdigita.cms.ItemCollection; -import com.arsdigita.cms.SecurityManager; -import com.arsdigita.cms.lifecycle.LifecycleDefinition; -import com.arsdigita.cms.ui.lifecycle.PublishLock; -import com.arsdigita.cms.util.GlobalizationUtil; -import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.domain.DomainObjectFactory; -import com.arsdigita.kernel.ACSObject; -import com.arsdigita.kernel.Party; -import com.arsdigita.kernel.PartyCollection; -import com.arsdigita.kernel.User; -import com.arsdigita.notification.Notification; -import com.arsdigita.persistence.CompoundFilter; -import com.arsdigita.persistence.DataCollection; -import com.arsdigita.persistence.Filter; -import com.arsdigita.persistence.OID; -import com.arsdigita.persistence.SessionManager; -import com.arsdigita.toolbox.GlobalisationUtil; +import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.toolbox.ui.ActionGroup; import com.arsdigita.util.Assert; import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.web.Web; -import com.arsdigita.workflow.simple.TaskException; -import com.arsdigita.workflow.simple.Workflow; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; @@ -99,24 +74,37 @@ import org.apache.log4j.Logger; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import javax.persistence.TypedQuery; +import org.arsdigita.cms.CMSConfig; +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.Shiro; +import org.libreccm.security.User; +import org.librecms.CmsConstants; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentSectionConfig; /** * Browse folders and manipulate them with various actions (move/copy/delete). * * @author David Lutterkort - * @version $Id: FolderManipulator.java 1940 2009-05-29 07:15:05Z terry $ + * @version $Id$ */ @SuppressWarnings("PMD.BeanMembersShouldSerialize") public class FolderManipulator extends SimpleContainer implements - //FormProcessListener, - //FormValidationListener, - //FormSubmissionListener, - Resettable { + //FormProcessListener, + //FormValidationListener, + //FormSubmissionListener, + Resettable { //public static final String RESOURCE_BUNDLE = "com.arsdigita.cms.ui.folder.CMSFolderResources"; - private final GlobalisationUtil globalizationUtil = new com.arsdigita.toolbox.GlobalisationUtil( - "com.arsdigita.cms.ui.folder.CMSFolderResources"); - private static final Logger LOGGER = Logger.getLogger(FolderManipulator.class); + private static final Logger LOGGER = Logger.getLogger( + FolderManipulator.class); private static final String ATOZ_FILTER_PARAM = "aToZfilter"; private static final String ACTION_PARAM = "act"; private static final String FILTER_PARAM = "filter"; @@ -128,7 +116,7 @@ public class FolderManipulator extends SimpleContainer implements //private static final String UNPUBLISH = "UnPublish"; private final ArrayParameter sourcesParam = new ArrayParameter( - new BigDecimalParameter(SOURCES_PARAM)); + new BigDecimalParameter(SOURCES_PARAM)); private final StringParameter actionParam = new StringParameter(ACTION_PARAM); ; /** @@ -138,9 +126,10 @@ public class FolderManipulator extends SimpleContainer implements private final ItemView itemView; private final TargetSelector targetSelector = new TargetSelector(); //private final PublishDialog publishDialog = new PublishDialog(); - + private FilterForm filterForm; - private final StringParameter atozFilterParam = new StringParameter(ATOZ_FILTER_PARAM); + private final StringParameter atozFilterParam = new StringParameter( + ATOZ_FILTER_PARAM); private final StringParameter filterParam = new StringParameter(FILTER_PARAM); public FolderManipulator(final FolderSelectionModel folderModel) { @@ -154,12 +143,13 @@ public class FolderManipulator extends SimpleContainer implements add(itemView); targetSelector.addProcessListener(new TargetSelectorProcessListener()); - targetSelector.addValidationListener(new TargetSelectorValidationListener()); - targetSelector.addSubmissionListener(new TargetSelectorSubmissionListener()); + targetSelector.addValidationListener( + new TargetSelectorValidationListener()); + targetSelector.addSubmissionListener( + new TargetSelectorSubmissionListener()); add(targetSelector); //publishDialog.addProcessListener(new PublishDialogProcessListener()); - } @Override @@ -175,13 +165,13 @@ public class FolderManipulator extends SimpleContainer implements } - public final BigDecimal[] getSources(final PageState state) { + public final Long[] getSources(final PageState state) { - final BigDecimal[] result = (BigDecimal[]) state.getValue(sourcesParam); + final Long[] result = (Long[]) state.getValue(sourcesParam); //Return empty array instead of null. if (result == null) { - return new BigDecimal[0]; + return new Long[0]; } else { return result; } @@ -191,7 +181,7 @@ public class FolderManipulator extends SimpleContainer implements return sourceFolderModel; } - public final Folder getTarget(final PageState state) { + public final Category getTarget(final PageState state) { return targetSelector.getTarget(state); } @@ -210,61 +200,58 @@ public class FolderManipulator extends SimpleContainer implements // protected final boolean isUnPublish(final PageState state) { // return UNPUBLISH.equals(getAction(state)); // } - private String getAction(final PageState state) { return (String) state.getValue(actionParam); } - protected void moveItems(final Folder target, final BigDecimal[] itemIds) { + protected void moveItems(final Category target, + final Long[] itemIds) { - for (BigDecimal itemId : itemIds) { - try { - changeItemParent(itemId, target); - } catch (DataObjectNotFoundException e) { - LOGGER.warn("object not found in content move", e); - throw new IllegalStateException(String.format("Item '%s' to move not found.", - itemId.toString())); + for (Long itemId : itemIds) { + + changeItemParent(itemId, target); - } } } - private void changeItemParent(final BigDecimal itemId, final Folder newParent) - throws DataObjectNotFoundException { + private void changeItemParent(final Long itemId, final Category newParent) { - final ContentItem item = new ContentItem(itemId); - item.setParent(newParent); - item.save(); + //ToDo + throw new UnsupportedOperationException(); +// final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); +// final ContentItemRepository itemRepo = cdiUtil.findBean( +// ContentItemRepository.class); +// final ContentItemManager itemManager = cdiUtil.findBean( +// ContentItemManager.class); +// final CategoryManager categoryManager = cdiUtil.findBean( +// CategoryManager.class); +// final ContentItem item = itemRepo.findById(itemId); +// final Category itemFolder = itemManager.getItemFolders(item). +// item.addCategory(newParent); +// item.setParent(newParent); +// item.save(); } - protected void copyItems(final Folder target, - final BigDecimal[] itemIds) { + protected void copyItems(final Category target, + final Long[] itemIds) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Copying items " + Arrays.asList(itemIds) + " to " + target); - } - for (BigDecimal itemId : itemIds) { - -// ContentItem source = (ContentItem) DomainObjectFactory.newInstance( -// new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemId)); -// Assert.exists(source, ContentItem.class); + //ToDo + throw new UnsupportedOperationException(); // -// final ACSObject parent = source.getParent(); -// if (parent instanceof ContentBundle) { -// source = (ContentBundle) parent; -// } +// if (LOGGER.isDebugEnabled()) { +// LOGGER.debug("Copying items " + Arrays.asList(itemIds) + " to " +// + target); +// } +// for (BigDecimal itemId : itemIds) { // -// if (LOGGER.isDebugEnabled()) { -// LOGGER.debug("Copying item " + source); -// } - final ContentItem source = retrieveSourceItem(itemId); - - final ContentItem newItem = source.copy(target, true); - Assert.isEqual(target, newItem.getParent()); - - } +// final ContentItem source = retrieveSourceItem(itemId); +// +// final ContentItem newItem = source.copy(target, true); +// Assert.isEqual(target, newItem.getParent()); +// +// } } // protected void publishItems(final BigDecimal[] itemIds) { @@ -291,7 +278,6 @@ public class FolderManipulator extends SimpleContainer implements // // } // } - // private void publish(final BigDecimal itemId) { // // } @@ -385,27 +371,26 @@ public class FolderManipulator extends SimpleContainer implements // // thread.start(); // } - - private ContentItem retrieveSourceItem(final BigDecimal itemToCopyId) { - - ContentItem source = (ContentItem) DomainObjectFactory.newInstance( - new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemToCopyId)); - Assert.exists(source, ContentItem.class); - - final ACSObject parent = source.getParent(); - if (parent instanceof ContentBundle) { - source = (ContentBundle) parent; - } - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Copying item " + source); - } - - return source; - } - +// private ContentItem retrieveSourceItem(final BigDecimal itemToCopyId) { +// +// ContentItem source = (ContentItem) DomainObjectFactory.newInstance( +// new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemToCopyId)); +// Assert.exists(source, ContentItem.class); +// +// final ACSObject parent = source.getParent(); +// if (parent instanceof ContentBundle) { +// source = (ContentBundle) parent; +// } +// +// if (LOGGER.isDebugEnabled()) { +// LOGGER.debug("Copying item " + source); +// } +// +// return source; +// } /** - * Returns the form that contains the folder browser and the move/copy dropdown. + * Returns the form that contains the folder browser and the move/copy + * dropdown. * * @return The form containing the folder browser and dropdown menu */ @@ -438,7 +423,8 @@ public class FolderManipulator extends SimpleContainer implements } @Override - public void process(final FormSectionEvent event) throws FormProcessException { + public void process(final FormSectionEvent event) throws + FormProcessException { final PageState state = event.getPageState(); itemView.setVisible(state, false); @@ -455,15 +441,16 @@ public class FolderManipulator extends SimpleContainer implements } @Override - public void process(final FormSectionEvent event) throws FormProcessException { + public void process(final FormSectionEvent event) throws + FormProcessException { final PageState state = event.getPageState(); itemView.setVisible(state, true); targetSelector.setVisible(state, false); - final Folder folder = targetSelector.getTarget(state); - final BigDecimal[] itemIds = getSources(state); + final Category folder = targetSelector.getTarget(state); + final Long[] itemIds = getSources(state); if (isCopy(state)) { copyItems(folder, itemIds); @@ -503,7 +490,6 @@ public class FolderManipulator extends SimpleContainer implements // } // // } - private class ItemViewValidationListener implements FormValidationListener { public ItemViewValidationListener() { @@ -511,26 +497,30 @@ public class FolderManipulator extends SimpleContainer implements } @Override - public void validate(final FormSectionEvent event) throws FormProcessException { + public void validate(final FormSectionEvent event) throws + FormProcessException { final PageState state = event.getPageState(); final FormData data = event.getFormData(); if (getSources(state).length <= 0) { - data.addError(globalizationUtil.globalize("cms.ui.folder.must_select_item")); + data.addError("cms.ui.folder.must_select_item", + CmsConstants.CMS_FOLDER_BUNDLE); } } } - private class TargetSelectorValidationListener implements FormValidationListener { + private class TargetSelectorValidationListener implements + FormValidationListener { public TargetSelectorValidationListener() { //Nothing } @Override - public void validate(final FormSectionEvent event) throws FormProcessException { + public void validate(final FormSectionEvent event) throws + FormProcessException { final PageState state = event.getPageState(); @@ -538,70 +528,82 @@ public class FolderManipulator extends SimpleContainer implements throw new IllegalStateException("No source items specified"); } - final Folder target = targetSelector.getTarget(state); + final Category target = targetSelector.getTarget(state); final FormData data = event.getFormData(); if (target == null) { - data.addError(globalizationUtil.globalize( - "cms.ui.folder.need_select_target_folder")); + data.addError(new GlobalizedMessage( + "cms.ui.folder.need_select_target_folder", + CmsConstants.CMS_FOLDER_BUNDLE)); //If the target is null, we can skip the rest of the checks return; } if (target.equals(sourceFolderModel.getSelectedObject(state))) { - data.addError(globalizationUtil.globalize("cms.ui.folder.not_within_same_folder")); + data.addError(new GlobalizedMessage( + "cms.ui.folder.not_within_same_folder", + CmsConstants.CMS_FOLDER_BUNDLE)); } // check create item permission - final User user = Web.getWebContext().getUser(); - final SecurityManager securityManager = CMS.getSecurityManager(state); - if (!securityManager.canAccess(user, SecurityManager.NEW_ITEM, target)) { - data.addError(globalizationUtil.globalize("cms.ui.folder.no_permission_for_item")); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final Shiro shiro = cdiUtil.findBean(Shiro.class); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + if (!permissionChecker.isPermitted( + CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW, target)) { + data.addError("cms.ui.folder.no_permission_for_item", + CmsConstants.CMS_FOLDER_BUNDLE); } - for (BigDecimal source : getSources(state)) { - try { - validateItem(source, target, state, data); - } catch (DataObjectNotFoundException ex) { - LOGGER.warn("Object not found in validation", ex); - throw new IllegalStateException(String.format( - "There is no item with the id '%s'", source.toString())); - } + for (Long source : getSources(state)) { + + validateItem(source, target, state, data); + } } - private void validateItem(final BigDecimal itemId, - final Folder target, + private void validateItem(final Long itemId, + final Category target, final PageState state, final FormData data) { + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemRepository itemRepo = cdiUtil.findBean( + ContentItemRepository.class); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); - final ContentItem item = new ContentItem(itemId); - final String name = item.getName(); + final ContentItem item = itemRepo.findById(itemId); + final String name = item.getDisplayName(); - final ItemCollection items = target.getItems(); - items.addNameFilter(name); - if (items.next()) { + final long count = itemRepo.countByNameInFolder(target, name); + if (count > 0) { // there is an item in the target folder that already has this name addErrorMessage(data, "cms.ui.folder.item_already_exists", name); } - items.close(); - if (item.isLive() && isMove(state)) { + if (itemManager.isLive(item) && isMove(state)) { addErrorMessage(data, "cms.ui.folder.item_is_live", name); } - final SecurityManager securityManager = CMS.getSecurityManager(state); - final User user = Web.getWebContext().getUser(); - if ((!securityManager.canAccess(user, SecurityManager.DELETE_ITEM, item)) - && isMove(state)) { - addErrorMessage(data, "cms.ui.folder.no_permission_for_item", name); + if (!(permissionChecker.isPermitted( + CmsConstants.PRIVILEGE_ITEMS_DELETE, item)) + && isMove(state)) { + addErrorMessage(data, "cms.ui.folder.no_permission_for_item", + name); } } } - private void addErrorMessage(final FormData data, final String message, final String itemName) { - data.addError(globalizationUtil.globalize(message, new Object[]{itemName})); + private void addErrorMessage(final FormData data, + final String message, + final String itemName) { + data.addError(new GlobalizedMessage(message, + CmsConstants.CMS_FOLDER_BUNDLE, + new Object[]{itemName})); } // @Override @@ -615,20 +617,24 @@ public class FolderManipulator extends SimpleContainer implements // // } // } - private class TargetSelectorSubmissionListener implements FormSubmissionListener { + private class TargetSelectorSubmissionListener implements + FormSubmissionListener { public TargetSelectorSubmissionListener() { //Nothing } @Override - public void submitted(final FormSectionEvent event) throws FormProcessException { + public void submitted(final FormSectionEvent event) throws + FormProcessException { final PageState state = event.getPageState(); if (targetSelector.isCancelled(state)) { reset(state); - throw new FormProcessException(GlobalizationUtil.globalize("cms.ui.folder.cancelled")); + throw new FormProcessException(new GlobalizedMessage( + "cms.ui.folder.cancelled", + CmsConstants.CMS_FOLDER_BUNDLE)); } } @@ -671,15 +677,26 @@ public class FolderManipulator extends SimpleContainer implements final PageState state = event.getPageState(); final Label label = (Label) event.getTarget(); final int numberOfItems = getSources(state).length; - final Folder folder = (Folder) sourceFolderModel.getSelectedObject(state); + final Category folder = (Category) sourceFolderModel. + getSelectedObject(state); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CategoryManager categoryManager = cdiUtil. + findBean(CategoryManager.class); + if (isMove(state)) { - label.setLabel(globalizationUtil.globalize( - "cms.ui.folder.move", new Object[]{numberOfItems, - folder.getPathNoJsp()})); + + label.setLabel(new GlobalizedMessage( + "cms.ui.folder.move", + CmsConstants.CMS_FOLDER_BUNDLE, + new Object[]{numberOfItems, + categoryManager.getCategoryPath( + folder)})); } else if (isCopy(state)) { - label.setLabel(globalizationUtil.globalize( - "cms.ui.folder.copy", new Object[]{numberOfItems, - folder.getPathNoJsp()})); + label.setLabel(new GlobalizedMessage( + "cms.ui.folder.copy", + new Object[]{numberOfItems, + categoryManager.getCategoryPath( + folder)})); } } @@ -702,14 +719,21 @@ public class FolderManipulator extends SimpleContainer implements // Set things up the first time the selector gets visible public void expose(final PageState state) { - final Folder folder = (Folder) sourceFolderModel.getSelectedObject(state); + final Category folder = (Category) sourceFolderModel. + getSelectedObject( + state); targetModel.clearSelection(state); if (folder != null) { - final ItemCollection items = folder.getPathInfo(true); - while (items.next()) { - folderTree.expand(items.getID().toString(), state); - } - items.close(); + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final ContentItemManager itemManager = cdiUtil.findBean( + ContentItemManager.class); + + //ToDo +// final ItemCollection items = folder.getPathInfo(true); +// while (items.next()) { +// folderTree.expand(items.getID().toString(), state); +// } +// items.close(); } } @@ -718,11 +742,12 @@ public class FolderManipulator extends SimpleContainer implements folderTree.clearSelection(state); // FIXME: add a reset method to Tree and call that instead of this // hack - state.setValue(folderTree.getSelectionModel().getStateParameter(), null); + state.setValue(folderTree.getSelectionModel().getStateParameter(), + null); } - public Folder getTarget(final PageState state) { - return (Folder) targetModel.getSelectedObject(state); + public Category getTarget(final PageState state) { + return (Category) targetModel.getSelectedObject(state); } public boolean isCancelled(final PageState state) { @@ -769,7 +794,6 @@ public class FolderManipulator extends SimpleContainer implements // } // // } - // The form containing the browser and the drop down for selecting an // action private class ItemView extends Form implements Resettable { @@ -797,13 +821,15 @@ public class FolderManipulator extends SimpleContainer implements folderBrowser.setAtoZfilterParameter(atozFilterParam); folderBrowser.setFilterParameter(filterParam); folderBrowser.setFilterForm(filterForm); - paginator = new Paginator((PaginationModelBuilder) folderBrowser.getModelBuilder(), - ContentSection.getConfig().getFolderBrowseListSize()); + paginator = new Paginator( + (PaginationModelBuilder) folderBrowser.getModelBuilder(), + CMSConfig.getConfig().getFolderBrowseListSize()); panel.add(paginator); panel.add(folderBrowser); LOGGER.debug("Adding filter form..."); - filterForm = new FilterForm((FilterFormModelBuilder) folderBrowser.getModelBuilder()); + filterForm = new FilterForm((FilterFormModelBuilder) folderBrowser. + getModelBuilder()); FolderManipulator.this.add(filterForm); checkboxGroup = new CheckboxGroup(sourcesParam); @@ -814,14 +840,20 @@ public class FolderManipulator extends SimpleContainer implements final Container container = new SimpleContainer(); group.addAction(container); - container.add(new Label(globalizationUtil.globalize("cms.ui.folder.edit_selection"))); + container.add(new Label(new GlobalizedMessage( + "cms.ui.folder.edit_selection", + CmsConstants.CMS_FOLDER_BUNDLE))); actionSelect = new SingleSelect(actionParam); - actionSelect.addOption(new Option(COPY, - new Label(globalizationUtil.globalize( - "cms.ui.folder.copy.action")))); - actionSelect.addOption(new Option(MOVE, - new Label(globalizationUtil.globalize( - "cms.ui.folder.move.action")))); + actionSelect.addOption( + new Option(COPY, + new Label(new GlobalizedMessage( + "cms.ui.folder.copy.action", + CmsConstants.CMS_FOLDER_BUNDLE)))); + actionSelect.addOption( + new Option(MOVE, + new Label(new GlobalizedMessage( + "cms.ui.folder.move.action", + CmsConstants.CMS_FOLDER_BUNDLE)))); //Publishing in the folder browser only works if threaded publishing is active // if (CMSConfig.getInstanceOf().getThreadedPublishing()) { // actionSelect.addOption(new Option(PUBLISH, @@ -832,7 +864,10 @@ public class FolderManipulator extends SimpleContainer implements // "cms.ui.folder.unpublish.action")))); // } container.add(actionSelect); - submit = new Submit("Go", globalizationUtil.globalize("cms.ui.folder.go")); + submit = new Submit("Go", + new GlobalizedMessage( + "cms.ui.folder.go", + CmsConstants.CMS_FOLDER_BUNDLE)); container.add(submit); // Add a new first column to the table @@ -872,7 +907,8 @@ public class FolderManipulator extends SimpleContainer implements final int row, final int column) { final BigDecimal n = (BigDecimal) key; - Option result = new Option(sourcesParam.marshalElement(n.abs()), ""); + Option result = new Option(sourcesParam.marshalElement(n.abs()), + ""); result.setGroup(checkboxGroup); return result; } @@ -904,7 +940,8 @@ public class FolderManipulator extends SimpleContainer implements panel = new BoxPanel(BoxPanel.HORIZONTAL); final ActionLink allLink = new ActionLink( - globalizationUtil.globalize("cms.ui.folder.filter.all")); + new GlobalizedMessage("cms.ui.folder.filter.all", + CmsConstants.CMS_FOLDER_BUNDLE)); allLink.addActionListener(new ActionListener() { @Override @@ -929,11 +966,15 @@ public class FolderManipulator extends SimpleContainer implements // }); // panel.add(link); // } - panel.add(new Label(globalizationUtil.globalize("cms.ui.folder.filter"))); + panel.add(new Label(new GlobalizedMessage( + "cms.ui.folder.filter", + CmsConstants.CMS_FOLDER_BUNDLE))); filterField = new TextField(filterParam); panel.add(filterField); panel.add(new Submit("filterFolderSubmit", - globalizationUtil.globalize("cms.ui.folder.filter_do"))); + new GlobalizedMessage( + "cms.ui.folder.filter_do", + CmsConstants.CMS_FOLDER_BUNDLE))); add(panel); @@ -944,25 +985,29 @@ public class FolderManipulator extends SimpleContainer implements } @Override - public void process(final FormSectionEvent event) throws FormProcessException { + public void process(final FormSectionEvent event) throws + FormProcessException { //Nothing } @Override - public void init(final FormSectionEvent event) throws FormProcessException { + public void init(final FormSectionEvent event) throws + FormProcessException { //fse.getPageState().setValue(FolderManipulator.this.m_filter, null); //filterField.setValue(fse.getPageState(), null); } @Override - public void submitted(final FormSectionEvent event) throws FormProcessException { + public void submitted(final FormSectionEvent event) throws + FormProcessException { } @Override public boolean isVisible(PageState state) { if (super.isVisible(state) - && (modelBuilder.getFolderSize(state) - >= CMSConfig.getInstanceOf().getFolderAtoZShowLimit())) { + && (modelBuilder.getFolderSize(state) + >= CMSConfig.getConfig(). + getFolderAtoZShowLimit())) { return true; } else { return false; @@ -996,9 +1041,10 @@ public class FolderManipulator extends SimpleContainer implements private RequestLocal m_invalidFolders = new RequestLocal(); /** - * Render the folders appropriately. The selected folder is a bold label. Invalid folders - * are plain labels. Unselected, valid folders are control links. Invalid folders are: the - * parent folder of the sources, any of the sources, and any subfolders of the sources. + * Render the folders appropriately. The selected folder is a bold + * label. Invalid folders are plain labels. Unselected, valid folders + * are control links. Invalid folders are: the parent folder of the + * sources, any of the sources, and any subfolders of the sources. */ @Override public Component getComponent(final Tree tree, @@ -1012,60 +1058,52 @@ public class FolderManipulator extends SimpleContainer implements // Get the list of invalid folders once per request. ArrayList invalidFolders = (ArrayList) m_invalidFolders.get(state); - if (invalidFolders == null) { - // The list of invalid folders has not been set for this - // request. Setting now. - invalidFolders = new ArrayList(); - - final DataCollection collection = SessionManager.getSession().retrieve( - ContentItem.BASE_DATA_OBJECT_TYPE); - CompoundFilter filter = collection.getFilterFactory().or(); - // The sources themselves are not valid. - final BigDecimal[] sources = getSources(state); - - for (int i = 0; i < sources.length; i++) { - invalidFolders.add(sources[i].toString()); - - final Filter temp = filter.addFilter("id = :id" + i); - temp.set("id" + i, sources[i]); - } - collection.addFilter(filter); - - final DataCollection folders = SessionManager.getSession().retrieve( - Folder.BASE_DATA_OBJECT_TYPE); - folders.addEqualsFilter(Folder.IS_DELETED, Boolean.FALSE); - - filter = collection.getFilterFactory().or(); - int count = 0; - while (collection.next()) { - filter.addFilter(Folder.ANCESTORS + " like :ancestors" - + count + " || '%'"); - filter.set("ancestors" + count, - collection.get(ContentItem.ANCESTORS)); - count++; - } - folders.addFilter(filter); - - while (folders.next()) { - invalidFolders.add(folders.get(Folder.ID).toString()); - } - - // Get all subfolders of the sources. These are also not valid. - /* - DataQuery dq = SessionManager.getSession().retrieveQuery("com.arsdigita.cms.FoldersAndAllSubFolders"); - dq.setParameter("item_list", invalidFolders); - - while (dq.next()) { - invalidFolders.add (dq.get("folder_id").toString()); - } - */ - // The folder from which the sources are being moved/copied is - // not allowed. - invalidFolders.add(sourceFolderModel.getSelectedKey(state).toString()); - - // Save the invalid folder list - m_invalidFolders.set(state, invalidFolders); - } +// if (invalidFolders == null) { +// // The list of invalid folders has not been set for this +// // request. Setting now. +// invalidFolders = new ArrayList(); +// +// final DataCollection collection = SessionManager.getSession(). +// retrieve( +// ContentItem.BASE_DATA_OBJECT_TYPE); +// CompoundFilter filter = collection.getFilterFactory().or(); +// // The sources themselves are not valid. +// final Long[] sources = getSources(state); +// +// for (int i = 0; i < sources.length; i++) { +// invalidFolders.add(sources[i].toString()); +// +// final Filter temp = filter.addFilter("id = :id" + i); +// temp.set("id" + i, sources[i]); +// } +// collection.addFilter(filter); +// +// final DataCollection folders = SessionManager.getSession(). +// retrieve( +// Folder.BASE_DATA_OBJECT_TYPE); +// folders.addEqualsFilter(Folder.IS_DELETED, Boolean.FALSE); +// +// filter = collection.getFilterFactory().or(); +// int count = 0; +// while (collection.next()) { +// filter.addFilter(Folder.ANCESTORS + " like :ancestors" +// + count + " || '%'"); +// filter.set("ancestors" + count, +// collection.get(ContentItem.ANCESTORS)); +// count++; +// } +// folders.addFilter(filter); +// +// while (folders.next()) { +// invalidFolders.add(folders.get(Folder.ID).toString()); +// } +// +// invalidFolders.add(sourceFolderModel.getSelectedKey(state). +// toString()); +// +// // Save the invalid folder list +// m_invalidFolders.set(state, invalidFolders); +// } final Label label = new Label(value.toString()); diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java new file mode 100755 index 000000000..df6599e5f --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java @@ -0,0 +1,30 @@ +/* + * 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.folder; + +import com.arsdigita.bebop.Tree; + +public class FolderTree extends Tree { + + public FolderTree(FolderSelectionModel folderSel) { + super(new FolderTreeModelBuilder()); + setSelectionModel(folderSel); + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/util/DefaultTableCellRenderer.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/util/DefaultTableCellRenderer.java new file mode 100755 index 000000000..4e2f3ba7e --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/util/DefaultTableCellRenderer.java @@ -0,0 +1,152 @@ +/* + * 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.util; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.util.Assert; +import com.arsdigita.util.LockableImpl; +import org.librecms.CmsConstants; + +/** + * The default renderer for table cells. This renderer is used by the + * {@link com.arsdigita.bebop.Table} component for rendering the table headers + * and cells if no other renderer is specified. + * + *

+ * This renderer can operate in two different modes: active + * and inactive mode. In inactive mode, all objects are rendered by + * converting them to a string and enclosing that string in a {@link + * com.arsdigita.bebop.Label}. If the renderer is in active mode, this label is + * further enclosed in a control link. When the user clicks on this link, the + * table will fire an TableActionEvent whose getKey() + * and getColumn() method return the values of the key + * and column parameters that were passed into + * {@link #getComponent getComponent}. + * + *

+ * In a nutshell, an active renderer will let the user click a link that causes + * a TableActionEvent for the corresponding cell, while an inactive + * renderer will display the values just as strings, thus making it impossible + * for the user to cause such an event. + * + * @author David Lutterkort + * @author Michael Pih (pihman@arsdigita.com) + * @see com.arsdigita.bebop.Table + * @see com.arsdigita.bebop.event.TableActionEvent + * + * @version $Id: DefaultTableCellRenderer.java 287 2005-02-22 00:29:02Z sskracic + * $ + */ +public class DefaultTableCellRenderer extends LockableImpl + implements TableCellRenderer { + + private boolean m_active; + private ThreadLocal m_label; + private ThreadLocal m_controlLink; + + /** + * Creates a new table cell renderer. The table cell renderer is in inactive + * mode. + */ + public DefaultTableCellRenderer() { + this(false); + } + + /** + * Creates a new table cell renderer. The active argument + * specifies whether the renderer should be active or not. + * + * @param active true if the renderer should generate links + * instead of just static labels. + */ + public DefaultTableCellRenderer(boolean active) { + m_active = active; + m_label = new ThreadLocal() { + protected Object initialValue() { + return new Label(""); + } + }; + m_controlLink = new ThreadLocal() { + protected Object initialValue() { + return new ControlLink((Label) m_label.get()); + } + }; + } + + /** + * Return true if the renderer is in active mode. A rendererin + * active mode will enclose the objects it renders in links that, when + * clicked, will cause the containing table to fire a + * TableActionEvent. + * + * @return true if the renderer is in active mode. + */ + public final boolean isActive() { + return m_active; + } + + /** + * Set the renderer to active or inactive mode. + * + * @param v true if the renderer should operate in active mode. + * @pre ! isLocked() + */ + public void setActive(boolean v) { + Assert.isUnlocked(this); + m_active = v; + } + + /** + * Return the component that should be used to render the given + * value. Returns a {@link com.arsdigita.bebop.Label} if the + * renderer is active, and a {@link com.arsdigita.bebop.ControlLink} if the + * renderer is inactive. + * + * @pre table == null || table != null + */ + public Component getComponent(Table table, PageState state, Object value, + boolean isSelected, Object key, + int row, int column) { + if (!isLocked() && table != null && table.isLocked()) { + lock(); + } + + Label l = (Label) m_label.get(); + if (value == null) { + l.setLabel(new GlobalizedMessage("cms.ui.util.", + CmsConstants.CMS_BUNDLE)); + l.setOutputEscaping(false); + } else { + l.setLabel((GlobalizedMessage) value); + l.setOutputEscaping(true); + } + l.setFontWeight((isSelected && m_active) ? Label.BOLD : null); + if (m_active && !isSelected) { + return (ControlLink) m_controlLink.get(); + } else { + return l; + } + } +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java index 8d06dc080..4aae3c743 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java @@ -70,8 +70,47 @@ import static org.librecms.CmsConstants.*; @NamedQuery( name = "ContentItem.findByFolder", query = "SELECT c.categorizedObject FROM Categorization c " - + "WHERE c.category = :folder" - ) + + "WHERE c.category = :folder " + + "AND TYPE(c.categorizedObject) IN ContentItem"), + @NamedQuery( + name = "ContentItem.countItemsInFolder", + query = "SELECT COUNT(c) FROM Categorization c " + + "WHERE c.category = :folder " + + "AND TYPE(c.categorizedObject) IN ContentItem"), + @NamedQuery( + name = "ContentItem.countByNameInFolder", + query = "SELECT count(c) FROM Categorization c " + + "WHERE c.category = :folder " + + "AND c.categorizedObject.displayName = :name"), + @NamedQuery( + name = "ContentItem.filterByNameAndFolder", + query = "SELECT c.categorizedObject FROM Categorization c " + + "WHERE c.category = :folder " + + "AND TYPE(c.categorizedObject) IN (ContentItem)" + + "AND (LOWER(c.categorizedObject.displayName) LIKE CONCAT(LOWER(:name), '%') "), + @NamedQuery( + name = "ContentItem.countFilterByNameAndFolder", + query = "SELECT count(c) FROM Categorization c " + + "WHERE c.category = :folder " + + "AND TYPE(c.categorizedObject) IN (ContentItem)" + + "AND LOWER(c.categorizedObject.displayName) LIKE CONCAT(LOWER(:name), '%s') "), + @NamedQuery( + name = "ContentItem.hasLiveVersion", + query = "SELECT (CASE WHEN COUNT(i) > 0 THEN true ELSE false END) " + + "FROM ContentItem i " + + "WHERE i.uuid = :uuid " + + "AND i.version = \"LIVE\""), + @NamedQuery( + name = "ContentItem.findDraftVersion", + query = "SELECT i FROM ContentItem i " + + "WHERE i.uuid = :uuid " + + "AND i.version = \"DRAFT\""), + @NamedQuery( + name = "ContentItem.findLiveVersion", + query = "SELECT i FROM ContentItem i " + + "WHERE i.uuid = :uuid " + + "AND i.version = \"LIVE\"") + }) public class ContentItem extends CcmObject implements Serializable, InheritsPermissions { @@ -258,7 +297,8 @@ public class ContentItem extends CcmObject implements Serializable, @Override public Optional getParent() { final List result = getCategories().stream().filter( - categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER.equals( + categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. + equals( categorization.getType())) .collect(Collectors.toList()); diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java index 4591657d5..c942ac4a8 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java @@ -18,8 +18,14 @@ */ package org.librecms.contentsection; +import com.arsdigita.kernel.KernelConfig; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.util.ArrayList; import java.util.Collections; + import org.libreccm.categorization.Category; import org.libreccm.workflow.WorkflowTemplate; import org.librecms.lifecycle.LifecycleDefinition; @@ -30,8 +36,31 @@ import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; + import org.libreccm.categorization.Categorization; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.categorization.ObjectNotAssignedToCategoryException; +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.l10n.LocalizedString; +import org.libreccm.workflow.Workflow; +import org.libreccm.workflow.WorkflowManager; import org.librecms.CmsConstants; +import org.librecms.lifecycle.Lifecycle; +import org.librecms.lifecycle.LifecycleManager; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.transaction.Transactional; /** * @@ -40,6 +69,18 @@ import org.librecms.CmsConstants; @RequestScoped public class ContentItemManager { + private static final Logger LOGGER = LogManager.getLogger( + ContentItemManager.class); + + @Inject + private EntityManager entityManager; + + @Inject + private ConfigurationManager confManager; + + @Inject + private CategoryManager categoryManager; + @Inject private ContentItemRepository contentItemRepo; @@ -49,6 +90,12 @@ public class ContentItemManager { @Inject private ContentSectionManager sectionManager; + @Inject + private LifecycleManager lifecycleManager; + + @Inject + private WorkflowManager workflowManager; + /** * Creates a new content item in the provided content section and folder * with the default lifecycle and workflow. @@ -65,6 +112,7 @@ public class ContentItemManager { * * @return The new content item. */ + @Transactional(Transactional.TxType.REQUIRED) public T createContentItem( final String name, final ContentSection section, @@ -112,6 +160,7 @@ public class ContentItemManager { * * @return The new content item. */ + @Transactional(Transactional.TxType.REQUIRED) public T createContentItem( final String name, final ContentSection section, @@ -119,7 +168,47 @@ public class ContentItemManager { final WorkflowTemplate workflowTemplate, final LifecycleDefinition lifecycleDefinition, final Class type) { - throw new UnsupportedOperationException(); + + final Optional contentType = typeRepo + .findByContentSectionAndClass(section, type); + + if (!contentType.isPresent()) { + throw new IllegalArgumentException(String.format( + "ContentSection \"%s\" has no content type for \"%s\".", + section.getLabel(), + type.getName())); + } + + final Lifecycle lifecycle = lifecycleManager.createLifecycle( + lifecycleDefinition); + final Workflow workflow = workflowManager.createWorkflow( + workflowTemplate); + + final T item; + try { + item = type.newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.error("Failed to create new content item of type \"{}\" " + + "in content section \"{}\".", + type.getName(), + section.getLabel()); + throw new RuntimeException(ex); + } + + final KernelConfig kernelConfig = confManager.findConfiguration( + KernelConfig.class); + + item.setDisplayName(name); + item.getName().addValue(new Locale(kernelConfig.getDefaultLanguage()), + name); + item.setLifecycle(lifecycle); + item.setWorkflow(workflow); + + categoryManager.addObjectToCategory(item, folder); + + contentItemRepo.save(item); + + return item; } /** @@ -130,8 +219,22 @@ public class ContentItemManager { * @param item The item to move. * @param targetFolder The folder to which the item is moved. */ + @Transactional(Transactional.TxType.REQUIRED) public void move(final ContentItem item, final Category targetFolder) { - throw new UnsupportedOperationException(); + final ContentItem draftItem = getDraftVersion(item, item.getClass()); + final Optional currentFolder = getItemFolder(item); + + if (currentFolder.isPresent()) { + try { + categoryManager.removeObjectFromCategory(draftItem, + currentFolder.get()); + } catch (ObjectNotAssignedToCategoryException ex) { + throw new RuntimeException(ex); + } + } + + categoryManager.addObjectToCategory(draftItem, targetFolder); + } /** @@ -144,10 +247,190 @@ public class ContentItemManager { * original item an index is appended to the name of the * item. */ + @SuppressWarnings("unchecked") public void copy(final ContentItem item, final Category targetFolder) { + final Optional contentType = typeRepo + .findByContentSectionAndClass( + item.getContentType().getContentSection(), item.getClass()); + + if (!contentType.isPresent()) { + throw new IllegalArgumentException(String.format( + "ContentSection \"%s\" has no content type for \"%s\".", + item.getContentType().getContentSection(), + item.getClass().getName())); + } + + final ContentItem draftItem = getDraftVersion(item, item.getClass()); + + final ContentItem copy; + try { + copy = item.getClass().newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + + copy.setContentType(contentType.get()); + + final Lifecycle lifecycle = lifecycleManager.createLifecycle( + contentType.get().getDefaultLifecycle()); + final Workflow workflow = workflowManager.createWorkflow(contentType + .get().getDefaultWorkflow()); + + copy.setLifecycle(lifecycle); + copy.setWorkflow(workflow); + + draftItem.getCategories().forEach(categorization -> categoryManager + .addObjectToCategory(copy, categorization.getCategory())); + + final Optional itemFolder = getItemFolder(draftItem); + if (itemFolder.isPresent()) { + try { + categoryManager.removeObjectFromCategory( + copy, getItemFolder(draftItem).get()); + } catch (ObjectNotAssignedToCategoryException ex) { + throw new RuntimeException(ex); + } + } + + categoryManager.addObjectToCategory( + copy, + targetFolder, + CmsConstants.CATEGORIZATION_TYPE_FOLDER); + + // !!!!!!!!!!!!!!!!!!!!! + // ToDo copy Attachments + // !!!!!!!!!!!!!!!!!!!!! + // + // + final BeanInfo beanInfo; + try { + beanInfo = Introspector.getBeanInfo(item.getClass()); + } catch (IntrospectionException ex) { + throw new RuntimeException(ex); + } + + for (final PropertyDescriptor propertyDescriptor : beanInfo + .getPropertyDescriptors()) { + if (propertyIsExcluded(propertyDescriptor.getName())) { + continue; + } + + final Class propType = propertyDescriptor.getPropertyType(); + final Method readMethod = propertyDescriptor.getReadMethod(); + final Method writeMethod = propertyDescriptor.getWriteMethod(); + + if (LocalizedString.class.equals(propType)) { + final LocalizedString source; + final LocalizedString target; + try { + source = (LocalizedString) readMethod.invoke(draftItem); + target = (LocalizedString) readMethod.invoke(copy); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + source.getAvailableLocales().forEach( + locale -> target.addValue(locale, source.getValue(locale))); + } else if (propType != null + && propType.isAssignableFrom(ContentItem.class)) { + + final ContentItem linkedItem; + try { + linkedItem = (ContentItem) readMethod.invoke(draftItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + final ContentItem linkedDraftItem = getDraftVersion( + linkedItem, linkedItem.getClass()); + + try { + writeMethod.invoke(copy, linkedDraftItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } else if (propType != null + && propType.isAssignableFrom(List.class)) { + final List source; + final List target; + try { + source = (List) readMethod.invoke(draftItem); + target = (List) readMethod.invoke(copy); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + target.addAll(source); + } else if (propType != null + && propType.isAssignableFrom(Map.class)) { + final Map source; + final Map target; + + try { + source = (Map) readMethod.invoke(draftItem); + target = (Map) readMethod.invoke(copy); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + source.forEach((key, value) -> target.put(key, value)); + } else if (propType != null + && propType.isAssignableFrom(Set.class)) { + final Set source; + final Set target; + + try { + source = (Set) readMethod.invoke(draftItem); + target = (Set) readMethod.invoke(copy); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + target.addAll(source); + } else { + final Object value; + try { + value = readMethod.invoke(item); + writeMethod.invoke(copy, value); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + } + throw new UnsupportedOperationException(); } + private boolean propertyIsExcluded(final String name) { + final String[] excluded = new String[]{ + "objectId", "uuid", "lifecycle", "workflow", "categories", + "attachments" + }; + + boolean result = false; + for (final String current : excluded) { + if (current.equals(name)) { + result = true; + } + } + + return result; + } + /** * Creates a live version of content item or updates the live version of a * content item if there already a live version. @@ -156,8 +439,151 @@ public class ContentItemManager { * * @return The published content item. */ + @SuppressWarnings("unchecked") public ContentItem publish(final ContentItem item) { - throw new UnsupportedOperationException(); + final ContentItem draftItem = getDraftVersion(item, ContentItem.class); + final ContentItem liveItem; + + if (isLive(item)) { + liveItem = getLiveVersion(item, ContentItem.class).get(); + } else { + try { + liveItem = draftItem.getClass().newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + liveItem.setContentType(draftItem.getContentType()); + liveItem.setLifecycle(draftItem.getLifecycle()); + liveItem.setWorkflow(draftItem.getWorkflow()); + + draftItem.getCategories().forEach(categorization -> categoryManager + .addObjectToCategory(item, categorization.getCategory())); + + liveItem.setUuid(draftItem.getUuid()); + + // !!!!!!!!!!!!!!!!!!!!! + // ToDo copy Attachments + // !!!!!!!!!!!!!!!!!!!!! + // + // + final BeanInfo beanInfo; + try { + beanInfo = Introspector.getBeanInfo(item.getClass()); + } catch (IntrospectionException ex) { + throw new RuntimeException(ex); + } + + for (final PropertyDescriptor propertyDescriptor : beanInfo + .getPropertyDescriptors()) { + + if (propertyIsExcluded(propertyDescriptor.getName())) { + continue; + } + + final Class propType = propertyDescriptor.getPropertyType(); + final Method readMethod = propertyDescriptor.getReadMethod(); + final Method writeMethod = propertyDescriptor.getWriteMethod(); + + if (LocalizedString.class.equals(propType)) { + final LocalizedString source; + final LocalizedString target; + try { + source = (LocalizedString) readMethod.invoke(draftItem); + target = (LocalizedString) readMethod.invoke(liveItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + source.getAvailableLocales().forEach( + locale -> target.addValue(locale, source.getValue(locale))); + } else if (propType != null + && propType.isAssignableFrom(ContentItem.class)) { + final ContentItem linkedItem; + try { + linkedItem = (ContentItem) readMethod.invoke(draftItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + final ContentItem linkedDraftItem = getDraftVersion( + linkedItem, linkedItem.getClass()); + + if (isLive(linkedDraftItem)) { + try { + final Optional linkedLiveItem + = getLiveVersion( + linkedDraftItem, ContentItem.class); + writeMethod.invoke(liveItem, linkedLiveItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + } else if (propType != null + && propType.isAssignableFrom(List.class)) { + final List source; + final List target; + try { + source = (List) readMethod.invoke(draftItem); + target = (List) readMethod.invoke(liveItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + target.addAll(source); + } else if (propType != null + && propType.isAssignableFrom(Map.class)) { + final Map source; + final Map target; + + try { + source = (Map) readMethod.invoke(draftItem); + target = (Map) readMethod.invoke(liveItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + source.forEach((key, value) -> target.put(key, value)); + } else if (propType != null + && propType.isAssignableFrom(Set.class)) { + final Set source; + final Set target; + + try { + source = (Set) readMethod.invoke(draftItem); + target = (Set) readMethod.invoke(liveItem); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + + target.addAll(source); + } else { + final Object value; + try { + value = readMethod.invoke(item); + writeMethod.invoke(liveItem, value); + } catch (IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + } + + return liveItem; } /** @@ -165,8 +591,16 @@ public class ContentItemManager { * * @param item */ - public void unpublish(final ContentItem item) { - throw new UnsupportedOperationException(); + @Transactional(Transactional.TxType.REQUIRED) + public void unpublish(final ContentItem item + ) { + final Optional liveItem = getLiveVersion( + item, ContentItem.class); + + if (liveItem.isPresent()) { + entityManager.remove(liveItem); + } + } /** @@ -178,7 +612,11 @@ public class ContentItemManager { * {@code false} if not. */ public boolean isLive(final ContentItem item) { - throw new UnsupportedOperationException(); + final TypedQuery query = entityManager.createNamedQuery( + "ContentItem.hasLiveVersion", Boolean.class); + query.setParameter("uuid", item.getUuid()); + + return query.getSingleResult(); } /** @@ -196,7 +634,16 @@ public class ContentItemManager { public Optional getLiveVersion( final ContentItem item, final Class type) { - throw new UnsupportedOperationException(); + + if (isLive(item)) { + final TypedQuery query = entityManager.createNamedQuery( + "ContentItem.findLiveVersion", type); + query.setParameter("uuid", item.getUuid()); + + return Optional.of(query.getSingleResult()); + } else { + return Optional.empty(); + } } /** @@ -230,7 +677,11 @@ public class ContentItemManager { */ public T getDraftVersion(final ContentItem item, final Class type) { - throw new UnsupportedOperationException(); + final TypedQuery query = entityManager.createNamedQuery( + "ContentItem.findDraftVersion", type); + query.setParameter("uuid", item.getUuid()); + + return query.getSingleResult(); } /** @@ -262,11 +713,11 @@ public class ContentItemManager { * {@code /research/computer-science/artificial-intelligence/neural-nets}. * If the parameter {@code withContentSection} is set to {@code true} the * the path will be prefixed with the name of the content section. For - * instance if the item {@link neural-nets} is part of the content section + * instance if the item {@code neural-nets} is part of the content section * {@code info}, the path including the content section would be - * {@link info:/research/computer-science/artificial-intelligence/neural-nets}. + * {@code info:/research/computer-science/artificial-intelligence/neural-nets}. * - * @param item The item which path is generated. + * @param item The item whose path is generated. * @param withContentSection Wether to include the content section into the * path. * @@ -339,4 +790,26 @@ public class ContentItemManager { return folders; } + /** + * Gets the folder in which in item is placed (if the item is part of + * folder). + * + * @param item The item + * + * @return An {@link Optional} containing the folder of the item if the item + * is part of a folder. + */ + public Optional getItemFolder(final ContentItem item) { + final List result = item.getCategories().stream(). + filter(categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. + equals(categorization.getType())) + .collect(Collectors.toList()); + + if (result.size() > 0) { + return Optional.of(result.get(0).getCategory()); + } else { + return Optional.empty(); + } + } + } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java index 6b128d84b..418e3447a 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java @@ -26,10 +26,12 @@ import org.libreccm.core.CcmObjectRepository; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.TypedQuery; +import jdk.nashorn.internal.objects.NativeArray; /** * Repository for content items. @@ -38,7 +40,7 @@ import javax.persistence.TypedQuery; */ @RequestScoped public class ContentItemRepository - extends AbstractAuditedEntityRepository { + extends AbstractAuditedEntityRepository { @Inject private CcmObjectRepository ccmObjectRepo; @@ -64,7 +66,7 @@ public class ContentItemRepository * @param itemId The id of item to retrieve. * * @return The content item identified by the provided {@code itemId} or - * nothing if there is such content item. + * nothing if there is such content item. */ public Optional findById(final long itemId) { final CcmObject result = ccmObjectRepo.findObjectById(itemId); @@ -78,13 +80,13 @@ public class ContentItemRepository /** * Finds a content item by its ID and ensures that is a the requested type. * - * @param The type of the content item. + * @param The type of the content item. * @param itemId The id of item to retrieve. - * @param type The type of the content item. + * @param type The type of the content item. * * @return The content item identified by the provided id or an empty - * {@link Optional} if there is no such item or if it is not of the - * requested type. + * {@link Optional} if there is no such item or if it is not of the + * requested type. */ @SuppressWarnings("unchecked") public Optional findById(final long itemId, @@ -103,7 +105,7 @@ public class ContentItemRepository * @param uuid The id of item to retrieve. * * @return The content item identified by the provided {@code uuid} or - * nothing if there is such content item. + * nothing if there is such content item. */ public ContentItem findByUuid(final String uuid) { final CcmObject result = ccmObjectRepo.findObjectByUuid(uuid); @@ -118,13 +120,13 @@ public class ContentItemRepository * Finds a content item by its UUID and ensures that is a the requested * type. * - * @param The type of the content item. + * @param The type of the content item. * @param uuid The UUID of item to retrieve. * @param type The type of the content item. * * @return The content item identified by the provided UUID or an empty - * {@link Optional} if there is no such item or if it is not of the - * requested type. + * {@link Optional} if there is no such item or if it is not of the + * requested type. */ @SuppressWarnings("unchecked") public Optional findByUuid(final String uuid, @@ -141,7 +143,7 @@ public class ContentItemRepository /** * Finds all content items of a specific type. * - * @param The type of the items. + * @param The type of the items. * @param type The type of the items. * * @return A list of all content items of the requested type. @@ -149,7 +151,7 @@ public class ContentItemRepository @SuppressWarnings("unchecked") public List findByType(final Class type) { final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.findByType", ContentItem.class); + .createNamedQuery("ContentItem.findByType", ContentItem.class); query.setParameter("type", type); return (List) query.getResultList(); @@ -163,16 +165,51 @@ public class ContentItemRepository * @return A list of all items in the provided folder. */ public List findByFolder(final Category folder) { - final TypedQuery query = getEntityManager() - .createNamedQuery("ContentItem.findByFolder", CcmObject.class); + final TypedQuery query = getEntityManager() + .createNamedQuery("ContentItem.findByFolder", + ContentItem.class); query.setParameter("folder", folder); - final List result = new ArrayList<>(); - query.getResultList().stream() - .filter(obj -> (obj instanceof ContentItem)) - .forEach(obj -> result.add((ContentItem) obj)); + return query.getResultList(); + } + + public long countItemsInFolder(final Category folder) { + final TypedQuery query = getEntityManager() + .createNamedQuery("ContentItem.countItemsInFolder", Long.class); + query.setParameter("folder", folder); + + return query.getSingleResult(); + } - return result; + public long countByNameInFolder(final Category folder, final String name) { + final TypedQuery query = getEntityManager().createNamedQuery( + "ContentItem.countByNameInFolder", Long.class); + query.setParameter("folder", folder); + query.setParameter("name", name); + + return query.getSingleResult(); + } + + public List filterByFolderAndName(final Category folder, + final String name) { + final TypedQuery query = getEntityManager() + .createNamedQuery("ContentItem.filterByNameAndFolder", + ContentItem.class); + query.setParameter("folder", folder); + query.setParameter("name", name); + + return query.getResultList(); + } + + public long countFilterByFolderAndName(final Category folder, + final String name) { + final TypedQuery query = getEntityManager() + .createNamedQuery("ContentItem.countFilterByNameAndFolder", + Long.class); + query.setParameter("folder", folder); + query.setParameter("name", name); + + return query.getSingleResult(); } } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionConfig.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionConfig.java index 7c3d6aeee..dc3a22318 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionConfig.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionConfig.java @@ -116,7 +116,7 @@ public class ContentSectionConfig { public static ContentSectionConfig getConfig() { final ConfigurationManager confManager = CdiUtil.createCdiUtil() - .findBean(ConfigurationManager.class); + .findBean(ConfigurationManager.class); return confManager.findConfiguration(ContentSectionConfig.class); } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java index a120bc189..040a81b0d 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java @@ -18,6 +18,7 @@ */ package org.librecms.contentsection; +import com.arsdigita.cms.dispatcher.ItemResolver; import com.arsdigita.kernel.KernelConfig; import org.libreccm.categorization.Category; @@ -34,7 +35,6 @@ import org.libreccm.security.RoleRepository; import java.util.List; import java.util.Locale; import java.util.UUID; -import java.util.stream.Stream; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; @@ -86,13 +86,13 @@ public class ContentSectionManager { public ContentSection createContentSection(final String name) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException( - "The name of a ContentSection can't be blank."); + "The name of a ContentSection can't be blank."); } final KernelConfig kernelConfig = confManager.findConfiguration( - KernelConfig.class); + KernelConfig.class); final Locale defautLocale - = new Locale(kernelConfig.getDefaultLanguage()); + = new Locale(kernelConfig.getDefaultLanguage()); final ContentSection section = new ContentSection(); section.setLabel(name); @@ -232,12 +232,12 @@ public class ContentSectionManager { @RequiresPrivilege(CoreConstants.ADMIN_PRIVILEGE) @Transactional(Transactional.TxType.REQUIRED) public void removeRoleFromContentSection( - final ContentSection contentSection, - final Role role) { + final ContentSection contentSection, + final Role role) { if (contentSection == null) { throw new IllegalArgumentException( - "Can't remove role from ContentSection null"); + "Can't remove role from ContentSection null"); } if (role == null) { @@ -248,10 +248,10 @@ public class ContentSectionManager { sectionRepo.save(contentSection); final TypedQuery query = entityManager - .createNamedQuery("ContentSection.findPermissions", - Permission.class); + .createNamedQuery("ContentSection.findPermissions", + Permission.class); query.setParameter("section", contentSection); - query.setParameter("rootDocumentsFolder", + query.setParameter("rootDocumentsFolder", contentSection.getRootDocumentsFolder()); query.setParameter("role", role); @@ -275,4 +275,16 @@ public class ContentSectionManager { throw new UnsupportedOperationException(); } + public ItemResolver getItemResolver(final ContentSection section) { + try { + final Class itemResolverClazz = (Class) Class. + forName(section.getItemResolverClass()); + return itemResolverClazz.newInstance(); + } catch (ClassNotFoundException | + IllegalAccessException | + InstantiationException ex) { + throw new RuntimeException(ex); + } + } + } diff --git a/ccm-core/pom.xml b/ccm-core/pom.xml index ca94023cc..58654d405 100644 --- a/ccm-core/pom.xml +++ b/ccm-core/pom.xml @@ -285,6 +285,7 @@ maven-surefire-plugin org.libreccm.tests.categories.UnitTest + false @@ -425,6 +426,9 @@ org.apache.maven.plugins maven-surefire-report-plugin 2.19.1 + + false + org.jacoco @@ -708,6 +712,7 @@ ${project.build.directory}/generated-resources + false @@ -784,6 +789,7 @@ org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.IntegrationTest + false @@ -1083,6 +1089,7 @@ org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.IntegrationTest + false @@ -1169,6 +1176,7 @@ org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.IntegrationTest + false diff --git a/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ActionGroup.java b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ActionGroup.java new file mode 100755 index 000000000..70a610246 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ActionGroup.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.toolbox.ui; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.PageState; +import com.arsdigita.util.Assert; +import com.arsdigita.xml.Element; +import java.util.Iterator; +import java.util.ArrayList; +import org.apache.log4j.Logger; + +/** + *

A simple layout panel with top, bottom, left, right, and body + * sections.

+ * + * @author Justin Ross <jross@redhat.com> + * @version $Id$ + */ +public class ActionGroup extends ComponentSet { + + private static final Logger s_log = Logger.getLogger(ActionGroup.class); + + private Component m_subject; + private final ArrayList m_actions = new ArrayList(); + + public static final String ADD = "add"; + public static final String EDIT = "edit"; + public static final String DELETE = "delete"; + public static final String RETURN = "return"; + + public final void setSubject(final Component subject) { + Assert.exists(subject, "Component subject"); + Assert.isUnlocked(this); + + m_subject = subject; + add(m_subject); + } + + public final void addAction(final Component action, final String clacc) { + Assert.exists(action, "Component action"); + Assert.isUnlocked(this); + + m_actions.add(new Object[] {action, clacc}); + add(action); + } + + public final void addAction(final Component action) { + addAction(action, null); + } + + public final void generateXML(final PageState state, final Element parent) { + if (isVisible(state)) { + final Element layout = parent.newChildElement + ("bebop:actionGroup", BEBOP_XML_NS); + + final Element subject = layout.newChildElement + ("bebop:subject", BEBOP_XML_NS); + + if (m_subject != null) { + m_subject.generateXML(state, subject); + } + + for (Iterator iter = m_actions.iterator(); iter.hasNext(); ) { + final Object[] spec = (Object[]) iter.next(); + final Component component = (Component) spec[0]; + final String clacc = (String) spec[1]; + + if (component.isVisible(state)) { + final Element action = layout.newChildElement + ("bebop:action", BEBOP_XML_NS); + + if (clacc != null) { + action.addAttribute("class", clacc); + } + + component.generateXML(state, action); + } + } + } + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentSet.java b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentSet.java new file mode 100755 index 000000000..d75d96c7f --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/ComponentSet.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.toolbox.ui; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Resettable; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.util.Assert; +import com.arsdigita.xml.Element; +import java.util.ArrayList; +import java.util.Iterator; +import org.apache.log4j.Logger; + +/** + * + * @version $Id$ + */ +public class ComponentSet extends SimpleComponent + implements Resettable { + + private static final Logger s_log = Logger.getLogger(ComponentSet.class); + + private final ArrayList m_components; + + public ComponentSet() { + m_components = new ArrayList(); + } + + public void reset(final PageState state) { + s_log.debug("Resetting children"); + + final Iterator iter = children(); + + while (iter.hasNext()) { + final Object component = iter.next(); + + if (component instanceof Resettable) { + ((Resettable) component).reset(state); + } + } + } + + public final Iterator children() { + return m_components.iterator(); + } + + public final void add(final Component component) { + Assert.exists(component, "Component component"); + + synchronized (m_components) { + final int index = m_components.indexOf(component); + + if (index == -1) { + m_components.add(component); + } else { + m_components.set(index, component); + } + } + } + + public final Component get(final int index) { + return (Component) m_components.get(index); + } + + public final int indexOf(final Component component) { + return m_components.indexOf(component); + } + + public final boolean contains(final Component component) { + return m_components.contains(component); + } + + public void generateXML(final PageState state, final Element parent) { + if (isVisible(state)) { + final Iterator iter = children(); + + while (iter.hasNext()) { + ((Component) iter.next()).generateXML(state, parent); + } + } + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/toolbox/ui/FormatStandards.java b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/FormatStandards.java new file mode 100755 index 000000000..f60c0bbd5 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/toolbox/ui/FormatStandards.java @@ -0,0 +1,108 @@ +/* + * 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.toolbox.ui; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.globalization.Globalized; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.l10n.GlobalizationHelper; + +/** + * + * This class holds methods to support consistent formatting across the system. + * + * @version $Revision$ $Date$ + * @version $Id$ + */ +public class FormatStandards implements Globalized { + + private static RequestLocal s_dateFormat = new RequestLocal(); + private static RequestLocal s_dateTimeFormat = new RequestLocal(); + + private static void initialize(PageState ps) { + Locale l = CdiUtil.createCdiUtil().findBean(GlobalizationHelper.class). + getNegotiatedLocale(); + s_dateFormat.set(ps, DateFormat.getDateInstance(DATE_DISPLAY_FORMAT, l)); + s_dateTimeFormat.set(ps, DateFormat.getDateTimeInstance( + DATE_DISPLAY_FORMAT, TIME_DISPLAY_FORMAT, l)); + } + + /** + * @return A globalized DateFormat instance for formatting the date only. + * @see #getDateTimeFormat() + */ + public static DateFormat getDateFormat() { + PageState ps = PageState.getPageState(); + Object obj = s_dateFormat.get(ps); + + if (obj == null) { + initialize(ps); + obj = s_dateFormat.get(ps); + } + + return (DateFormat) obj; + } + + /** + * @return A globalized DateFormat instance for formatting the date and + * time. + * @see #getDateFormat() + */ + public static DateFormat getDateTimeFormat() { + PageState ps = PageState.getPageState(); + Object obj = s_dateTimeFormat.get(ps); + + if (obj == null) { + initialize(ps); + obj = s_dateTimeFormat.get(ps); + } + + return (DateFormat) obj; + } + + /** + * Formats a date value according to formatting standards and localization. + * In English This will show the date as "Mmm DD, YYYY" or "Jan 23, 2002." + * This method discards the clock time. + * + * @param d The date to format. + * @return A properly formatted date. + * @see #formatDateTime(Date) + */ + public static String formatDate(Date d) { + return (d == null) ? null : getDateFormat().format(d); + } + + /** + * Formats a date and time value according to formatting standards and + * localization. This method includes the date and the time. In English, it + * will appear as "Mmm DD, YYYY HH:MM AM" or "Jan 23, 2002, 5:44 PM. + * + * @param d The date to format. + * @return A properly formatted date and time. + */ + public static String formatDateTime(Date d) { + return (d == null) ? null : getDateTimeFormat().format(d); + } +} diff --git a/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java b/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java index 80ffeb184..f6bbc8303 100644 --- a/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java +++ b/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java @@ -65,20 +65,38 @@ public abstract class AbstractAuditedEntityRepository /** * Retrieves the number of revisions for a given entity. * - * @param entity The entity + * @param entity The entity * @param objectId The primary key * * @return A list of revision numbers, at which the entity was modified, * sorted in ascending order (so older revisions come first). * - * @throws NotAuditedException When entities of the given class are not - * audited. + * @throws NotAuditedException When entities of the given class are not + * audited. * @throws IllegalArgumentException If cls or primaryKey is null. - * @throws IllegalStateException If the associated entity manager is closed. + * @throws IllegalStateException If the associated entity manager is + * closed. */ public List retrieveRevisionNumbersOfEntity(final T entity, final Long objectId) { return auditReader.getRevisions(entity.getClass(), objectId); } + + public CcmRevision retrieveFirstRevision(final T entity, + final Long objectId) { + final List revisions = retrieveRevisionNumbersOfEntity( + entity, objectId); + + return auditReader.findRevision(CcmRevision.class, revisions.get(0)); + } + + public CcmRevision retrieveCurrentRevision(final T entity, + final Long objectId) { + final List revisions = retrieveRevisionNumbersOfEntity( + entity, objectId); + final Number lastRevision = revisions.get(revisions.size() - 1); + + return auditReader.findRevision(CcmRevision.class, lastRevision); + } } diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Categorization.java b/ccm-core/src/main/java/org/libreccm/categorization/Categorization.java index 66ce997d3..ad7bf1397 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Categorization.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Categorization.java @@ -48,10 +48,23 @@ import javax.persistence.Table; @Entity @Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA) @NamedQueries({ - @NamedQuery(name = "Categorization.find", - query = "SELECT c FROM Categorization c " - + "WHERE c.category = :category " - + "AND c.categorizedObject = :object") + @NamedQuery( + name = "Categorization.find", + query = "SELECT c FROM Categorization c " + + "WHERE c.category = :category " + + "AND c.categorizedObject = :object"), + @NamedQuery( + name = "Categorization.findIndexObject", + query = "SELECT c.categorizedObject FROM Categorization c " + + "WHERE c.category = :category " + + "AND c.index = TRUE"), + @NamedQuery( + name = "Categorization.hasIndexObject", + query = "SELECT (CASE WHEN COUNT(c.categorizedObject) > 0 THEN true " + + "ELSE false END) " + + "FROM Categorization c " + + "WHERE c.category = :category " + + "AND c.index = TRUE") }) public class Categorization implements Serializable { diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java b/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java index 1ecb876dd..90ed46e65 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java @@ -25,11 +25,15 @@ import org.apache.logging.log4j.Logger; import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObjectRepository; import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.PermissionChecker; import org.libreccm.security.RequiresPrivilege; import org.libreccm.security.Shiro; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.StringJoiner; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; @@ -65,6 +69,9 @@ public class CategoryManager { @Inject private Shiro shiro; + @Inject + private PermissionChecker permissionChecker; + /** * Assigns an category to an object. * @@ -89,6 +96,35 @@ public class CategoryManager { @RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE) final Category category) { + addObjectToCategory(object, category, null); + } + + /** + * Assigns an category to an object. + * + * Please note: Because the association between {@link Category} and {@code + * CcmObject} is a many-to-many association we use an association object to + * store the additional attributes of the association. The + * {@link Categorization} entity is completely managed by this class. + * + * If either {@code object} or the {@code category} parameter are + * {@code null} an {@link IllegalArgumentException} is thrown because + * passing {@code null} to this method indicates a programming error. + * + * @param object The object to assign to the category. Can never be + * {@code null}. + * @param category The category to which the object should be assigned. Can + * never be {@code null}. + * @param type Type of the categorisation. + */ + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public void addObjectToCategory( + final CcmObject object, + @RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE) + final Category category, + final String type) { + if (object == null) { throw new IllegalArgumentException( "Null can't be added to a category."); @@ -104,11 +140,12 @@ public class CategoryManager { categorization.setCategory(category); categorization.setCategoryOrder(object.getCategories().size() + 1); categorization.setObjectOrder(category.getObjects().size() + 1); + categorization.setType(type); object.addCategory(categorization); category.addObject(categorization); - // To saving a category requires the manage_category privilege which + // Saving a category requires the manage_category privilege which // may has not been granted to a user which is allowed to assign objects // to a category. Therefore we bypass the this authorisation check here // by executing CategoryRepository#save(Category) as the system user. @@ -595,4 +632,65 @@ public class CategoryManager { }); } + /** + * Returns the path of a category as string. The path of a category are the + * names of all its parent categories and the category joined together, + * separated by a slash. + * + * @param category The category whose path is generated. + * + * @return The path of the category. + */ + public String getCategoryPath(final Category category) { + final List tokens = new ArrayList<>(); + + Category current = category; + while (current.getParentCategory() != null) { + tokens.add(current.getDisplayName()); + current = current.getParentCategory(); + } + + Collections.reverse(tokens); + final StringJoiner joiner = new StringJoiner("/", "/", ""); + tokens.forEach(joiner::add); + + return joiner.toString(); + } + + public boolean hasIndexObject(final Category category) { +// final TypedQuery hasIndexItemQuery = entityManager +// .createNamedQuery("Categorization.hasIndexObject", Long.class); +// hasIndexItemQuery.setParameter("category", category); +// final long indexItems = hasIndexItemQuery.getSingleResult(); +// return indexItems > 0; + final TypedQuery query = entityManager + .createNamedQuery("Categorization.hasIndexObject", Boolean.class); + query.setParameter("category", category); + + return query.getSingleResult(); + } + + /** + * Retrieves to index object of a category. The caller is responsible for + * checking if the current user has sufficient privileges to read the index + * object! + * + * @param category The category of which the index object should be + * retrieved. + * + * @return An {@link Optional} containing the index object of the provided + * category if the category has an index object. + */ + public Optional getIndexObject(final Category category) { + if (hasIndexObject(category)) { + final TypedQuery query = entityManager.createNamedQuery( + "Categorization.findIndexObject", CcmObject.class); + query.setParameter("category", category); + + return Optional.of(query.getSingleResult()); + } else { + return Optional.empty(); + } + } + } diff --git a/ccm-core/src/main/java/org/libreccm/security/PermissionChecker.java b/ccm-core/src/main/java/org/libreccm/security/PermissionChecker.java index 35decda78..5a8a178e3 100644 --- a/ccm-core/src/main/java/org/libreccm/security/PermissionChecker.java +++ b/ccm-core/src/main/java/org/libreccm/security/PermissionChecker.java @@ -87,11 +87,11 @@ public class PermissionChecker { return result; } else if (object instanceof InheritsPermissions) { if (((InheritsPermissions) object).getParent().isPresent()) { - return result; - } else { return isPermitted( privilege, ((InheritsPermissions) object).getParent().get()); + } else { + return result; } } else { return result; @@ -141,17 +141,15 @@ public class PermissionChecker { if (!result) { if (((InheritsPermissions) object).getParent().isPresent()) { - if (subject.isAuthenticated()) { - subject.checkPermission(generatePermissionString( - privilege, object)); - } else { - shiro.getPublicUser().checkPermission( - generatePermissionString(privilege, object)); - } - } else { checkPermission( privilege, ((InheritsPermissions) object).getParent().get()); + } else if (subject.isAuthenticated()) { + subject.checkPermission(generatePermissionString( + privilege, object)); + } else { + shiro.getPublicUser().checkPermission( + generatePermissionString(privilege, object)); } } } else if (subject.isAuthenticated()) { diff --git a/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java b/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java index 2b71c35e8..c572db45b 100644 --- a/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java +++ b/ccm-core/src/test/java/org/libreccm/categorization/CategoryManagerTest.java @@ -47,7 +47,7 @@ import org.libreccm.security.Shiro; import org.libreccm.tests.categories.IntegrationTest; import java.io.File; -import java.util.concurrent.Callable; +import java.util.Optional; import javax.inject.Inject; import javax.persistence.EntityManager; @@ -582,5 +582,34 @@ public class CategoryManagerTest { categoryManager.addSubCategoryToCategory(test, categories); }); } + + @Test + @UsingDataSet( + "datasets/org/libreccm/categorization/CategoryManagerTest/data.yml") + @InSequence(6000) + public void hasIndexObject() { + final Category category1 = categoryRepo.findById(-2100L); + final Category category2 = categoryRepo.findById(-2200L); + + assertThat(categoryManager.hasIndexObject(category1), is(false)); + assertThat(categoryManager.hasIndexObject(category2), is(true)); + } + + @Test + @UsingDataSet( + "datasets/org/libreccm/categorization/CategoryManagerTest/data.yml") + @InSequence(6500) + public void getIndexObject() { + final Category category1 = categoryRepo.findById(-2100L); + final Category category2 = categoryRepo.findById(-2200L); + + assertThat(categoryManager.getIndexObject(category1).isPresent(), + is(false)); + + final Optional index2 = categoryManager.getIndexObject( + category2); + assertThat(index2.isPresent(), is(true)); + assertThat(index2.get().getDisplayName(), is(equalTo("object3"))); + } } diff --git a/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql index f67c6e54f..ab3c44404 100644 --- a/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql +++ b/ccm-core/src/test/resources-wildfly8-remote-h2-mem/scripts/create_ccm_core_schema.sql @@ -39,6 +39,7 @@ CREATE SCHEMA ccm_core; CATEGORY_ORDER bigint, CATEGORY_INDEX boolean, OBJECT_ORDER bigint, + TYPE varchar(255), OBJECT_ID bigint, CATEGORY_ID bigint, primary key (CATEGORIZATION_ID) diff --git a/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql index e149ad317..4d0978ce7 100644 --- a/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql +++ b/ccm-core/src/test/resources-wildfly8-remote-pgsql/scripts/create_ccm_core_schema.sql @@ -39,6 +39,7 @@ CREATE SCHEMA ccm_core; CATEGORY_ORDER int8, CATEGORY_INDEX boolean, OBJECT_ORDER int8, + TYPE varchar(255), OBJECT_ID int8, CATEGORY_ID int8, primary key (CATEGORIZATION_ID) diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-obj-to-category.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-obj-to-category.yml index 8b60ca8ad..d8cbadaf4 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-obj-to-category.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-obj-to-category.yml @@ -71,4 +71,4 @@ ccm_core.categorizations: category_id: -2100 category_order: 1 object_order: 2 - category_index: false \ No newline at end of file + category_index: true \ No newline at end of file diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-subcategory.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-subcategory.yml index e9c3554af..24e788e0e 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-subcategory.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-add-subcategory.yml @@ -76,4 +76,4 @@ ccm_core.categorizations: object_id: -3300 category_order: 1 object_order: 1 - category_index: false \ No newline at end of file + category_index: true \ No newline at end of file diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml index b7d6305a5..f3d3eaa0e 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-create-multiple-categories.yml @@ -109,4 +109,4 @@ ccm_core.categorizations: object_id: -3300 category_order: 1 object_order: 1 - category_index: false \ No newline at end of file + category_index: true \ No newline at end of file diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-obj-from-category.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-obj-from-category.yml index 2eadb28b1..b9212389f 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-obj-from-category.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-obj-from-category.yml @@ -59,4 +59,4 @@ ccm_core.categorizations: object_id: -3300 category_order: 1 object_order: 1 - category_index: false + category_index: true diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml index 7928e7846..84b60c823 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/after-remove-subcategory.yml @@ -64,4 +64,4 @@ ccm_core.categorizations: object_id: -3300 category_order: 1 object_order: 1 - category_index: false + category_index: true diff --git a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/data.yml b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/data.yml index 62a9a9ae7..43783a0c4 100644 --- a/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/data.yml +++ b/ccm-core/src/test/resources/datasets/org/libreccm/categorization/CategoryManagerTest/data.yml @@ -65,7 +65,7 @@ ccm_core.categorizations: object_id: -3300 category_order: 1 object_order: 1 - category_index: false + category_index: true ccm_core.parties: - party_id: -3000