- 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
pull/2/head
jensp 2016-08-29 15:52:23 +00:00
parent 0838a6df5f
commit e6b3630786
27 changed files with 2491 additions and 347 deletions

View File

@ -29,21 +29,24 @@ import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
* <p>The <tt>ItemResolver</tt> is responsible for mapping a URL in a * <p>
* particular content section to a content item.</p> * The <tt>ItemResolver</tt> is responsible for mapping a URL in a particular
* content section to a content item.</p>
* *
* <p>As an example, here is the item resolution process for a request to * <p>
* As an example, here is the item resolution process for a request to
* <tt>http://yourserver/cms/cheese</tt>:</p> * <tt>http://yourserver/cms/cheese</tt>:</p>
* *
* <p>The item resolver would be asked to map the URL stub <tt>/cheese</tt> * <p>
* in the content section mounted at <tt>/cms</tt> to a content item. To * The item resolver would be asked to map the URL stub <tt>/cheese</tt>
* this end, the dispatcher calls the <tt>getItem</tt> method, passing in * in the content section mounted at <tt>/cms</tt> to a content item. To this
* the {@link com.arsdigita.cms.ContentSection} and the URL stub for the * end, the dispatcher calls the <tt>getItem</tt> method, passing in the
* item within the section, <tt>/cheese</tt> in our example. As a final * {@link com.arsdigita.cms.ContentSection} and the URL stub for the item within
* argument, the dispatcher passes either <tt>ContentItem.DRAFT</tt> or * the section, <tt>/cheese</tt> in our example. As a final argument, the
* <tt>ContentItem.LIVE</tt> to the <tt>ItemResolver</tt>, depending on * dispatcher passes either <tt>ContentItem.DRAFT</tt> or
* whether the returned item should be the live version (for public pages) * <tt>ContentItem.LIVE</tt> to the <tt>ItemResolver</tt>, depending on whether
* or the draft version (for previewing).</p> * the returned item should be the live version (for public pages) or the draft
* version (for previewing).</p>
* *
* @author Michael Pih (pihman@arsdigita.com) * @author Michael Pih (pihman@arsdigita.com)
* @author Stanislav Freidin (sfreidin@arsdigita.com) * @author Stanislav Freidin (sfreidin@arsdigita.com)
@ -60,7 +63,8 @@ public interface ItemResolver {
* @param context The use context * @param context The use context
* @return The content item, or null if no such item exists * @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); String context);
/** /**
@ -82,10 +86,11 @@ public interface ItemResolver {
* @return The URL of the item * @return The URL of the item
* @see #getCurrentContext * @see #getCurrentContext
*/ */
public String generateItemURL ( public String generateItemURL(PageState state,
PageState state, BigDecimal itemId, String name, Long itemId,
ContentSection section, String context String name,
); ContentSection section,
String context);
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
@ -99,9 +104,12 @@ public interface ItemResolver {
* @return The URL of the item * @return The URL of the item
* @see #getCurrentContext * @see #getCurrentContext
*/ */
public String generateItemURL ( public String generateItemURL(PageState state,
PageState state, BigDecimal itemId, String name, Long itemId,
ContentSection section, String context, String templateContext String name,
ContentSection section,
String context,
String templateContext
); );
/** /**
@ -114,9 +122,10 @@ public interface ItemResolver {
* @return The URL of the item * @return The URL of the item
* @see #getCurrentContext * @see #getCurrentContext
*/ */
public String generateItemURL ( public String generateItemURL(PageState state,
PageState state, ContentItem item, ContentSection section, String context ContentItem item,
); ContentSection section,
String context);
/** /**
* Generates a URL for a content item. * Generates a URL for a content item.
@ -129,10 +138,11 @@ public interface ItemResolver {
* @return The URL of the item * @return The URL of the item
* @see #getCurrentContext * @see #getCurrentContext
*/ */
public String generateItemURL ( public String generateItemURL(PageState state,
PageState state, ContentItem item, ContentSection section, String context, ContentItem item,
String templateContext ContentSection section,
); String context,
String templateContext);
/** /**
* Return a master page based on page state (and content section). * Return a master page based on page state (and content section).
@ -153,10 +163,10 @@ public interface ItemResolver {
* template contexts, and TemplateResolver sets the actual template contexts * template contexts, and TemplateResolver sets the actual template contexts
* in the request. * in the request.
*/ */
/** /**
* Finds the template context from the URL and returns it, if it is there. * Finds the template context from the URL and returns it, if it is there.
* Otherwise, returns null. * Otherwise, returns null.
*
* @param inUrl the URL from which to get the template context * @param inUrl the URL from which to get the template context
* @return the template context, or null if there is no template context * @return the template context, or null if there is no template context
*/ */

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
* @author <a href="mailto:quasi@quasiweb.de">Sören Bernstein</a>
* @author <a href="mailto:lutter@arsdigita.com">David Lutterkort</a>
*/
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<ContentItem> query = criteriaBuilder.
createQuery(ContentItem.class);
final Root<Categorization> root = query.from(Categorization.class);
final Root<ContentItem> itemRoot = query.from(ContentItem.class);
//final List<Predicate> predicates = new ArrayList<>();
final Predicate categoryPredicate = criteriaBuilder.equal(
root.get("Categorization.category"), folder);
final Predicate typePredicate = criteriaBuilder.equal(
itemRoot.type(), ContentItem.class);
final List<Predicate> 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 <quasi@quasiweb.de>
*
* 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("&nbsp;", 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<ContentItem> 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<ContentItem> 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<ContentItem> 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<CcmObject> 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;
}
}

View File

@ -61,36 +61,11 @@ import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn; import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.tree.TreeCellRenderer; import com.arsdigita.bebop.tree.TreeCellRenderer;
import com.arsdigita.cms.CMS; import com.arsdigita.cms.CMS;
import com.arsdigita.cms.CMSConfig; import com.arsdigita.globalization.GlobalizedMessage;
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.toolbox.ui.ActionGroup; import com.arsdigita.toolbox.ui.ActionGroup;
import com.arsdigita.util.Assert; import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.web.Web; import com.arsdigita.web.Web;
import com.arsdigita.workflow.simple.TaskException;
import com.arsdigita.workflow.simple.Workflow;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
@ -99,12 +74,26 @@ import org.apache.log4j.Logger;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; 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). * Browse folders and manipulate them with various actions (move/copy/delete).
* *
* @author <a href="mailto:lutter@arsdigita.com">David Lutterkort</a> * @author <a href="mailto:lutter@arsdigita.com">David Lutterkort</a>
* @version $Id: FolderManipulator.java 1940 2009-05-29 07:15:05Z terry $ * @version $Id$
*/ */
@SuppressWarnings("PMD.BeanMembersShouldSerialize") @SuppressWarnings("PMD.BeanMembersShouldSerialize")
public class FolderManipulator extends SimpleContainer implements public class FolderManipulator extends SimpleContainer implements
@ -114,9 +103,8 @@ public class FolderManipulator extends SimpleContainer implements
Resettable { Resettable {
//public static final String RESOURCE_BUNDLE = "com.arsdigita.cms.ui.folder.CMSFolderResources"; //public static final String RESOURCE_BUNDLE = "com.arsdigita.cms.ui.folder.CMSFolderResources";
private final GlobalisationUtil globalizationUtil = new com.arsdigita.toolbox.GlobalisationUtil( private static final Logger LOGGER = Logger.getLogger(
"com.arsdigita.cms.ui.folder.CMSFolderResources"); FolderManipulator.class);
private static final Logger LOGGER = Logger.getLogger(FolderManipulator.class);
private static final String ATOZ_FILTER_PARAM = "aToZfilter"; private static final String ATOZ_FILTER_PARAM = "aToZfilter";
private static final String ACTION_PARAM = "act"; private static final String ACTION_PARAM = "act";
private static final String FILTER_PARAM = "filter"; private static final String FILTER_PARAM = "filter";
@ -140,7 +128,8 @@ public class FolderManipulator extends SimpleContainer implements
//private final PublishDialog publishDialog = new PublishDialog(); //private final PublishDialog publishDialog = new PublishDialog();
private FilterForm filterForm; 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); private final StringParameter filterParam = new StringParameter(FILTER_PARAM);
public FolderManipulator(final FolderSelectionModel folderModel) { public FolderManipulator(final FolderSelectionModel folderModel) {
@ -154,12 +143,13 @@ public class FolderManipulator extends SimpleContainer implements
add(itemView); add(itemView);
targetSelector.addProcessListener(new TargetSelectorProcessListener()); targetSelector.addProcessListener(new TargetSelectorProcessListener());
targetSelector.addValidationListener(new TargetSelectorValidationListener()); targetSelector.addValidationListener(
targetSelector.addSubmissionListener(new TargetSelectorSubmissionListener()); new TargetSelectorValidationListener());
targetSelector.addSubmissionListener(
new TargetSelectorSubmissionListener());
add(targetSelector); add(targetSelector);
//publishDialog.addProcessListener(new PublishDialogProcessListener()); //publishDialog.addProcessListener(new PublishDialogProcessListener());
} }
@Override @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. //Return empty array instead of null.
if (result == null) { if (result == null) {
return new BigDecimal[0]; return new Long[0];
} else { } else {
return result; return result;
} }
@ -191,7 +181,7 @@ public class FolderManipulator extends SimpleContainer implements
return sourceFolderModel; return sourceFolderModel;
} }
public final Folder getTarget(final PageState state) { public final Category getTarget(final PageState state) {
return targetSelector.getTarget(state); return targetSelector.getTarget(state);
} }
@ -210,61 +200,58 @@ public class FolderManipulator extends SimpleContainer implements
// protected final boolean isUnPublish(final PageState state) { // protected final boolean isUnPublish(final PageState state) {
// return UNPUBLISH.equals(getAction(state)); // return UNPUBLISH.equals(getAction(state));
// } // }
private String getAction(final PageState state) { private String getAction(final PageState state) {
return (String) state.getValue(actionParam); return (String) state.getValue(actionParam);
} }
protected void moveItems(final Folder target, final BigDecimal[] itemIds) { protected void moveItems(final Category target,
final Long[] itemIds) {
for (Long itemId : itemIds) {
for (BigDecimal itemId : itemIds) {
try {
changeItemParent(itemId, target); 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()));
} }
}
} }
private void changeItemParent(final BigDecimal itemId, final Folder newParent) private void changeItemParent(final Long itemId, final Category newParent) {
throws DataObjectNotFoundException {
final ContentItem item = new ContentItem(itemId); //ToDo
item.setParent(newParent); throw new UnsupportedOperationException();
item.save();
// 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, protected void copyItems(final Category target,
final BigDecimal[] itemIds) { final Long[] itemIds) {
if (LOGGER.isDebugEnabled()) { //ToDo
LOGGER.debug("Copying items " + Arrays.asList(itemIds) + " to " + target); throw new UnsupportedOperationException();
}
for (BigDecimal itemId : itemIds) {
// ContentItem source = (ContentItem) DomainObjectFactory.newInstance(
// new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemId));
// Assert.exists(source, ContentItem.class);
//
// final ACSObject parent = source.getParent();
// if (parent instanceof ContentBundle) {
// source = (ContentBundle) parent;
// }
// //
// if (LOGGER.isDebugEnabled()) { // if (LOGGER.isDebugEnabled()) {
// LOGGER.debug("Copying item " + source); // LOGGER.debug("Copying items " + Arrays.asList(itemIds) + " to "
// + target);
// }
// for (BigDecimal itemId : itemIds) {
//
// 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) { // protected void publishItems(final BigDecimal[] itemIds) {
@ -291,7 +278,6 @@ public class FolderManipulator extends SimpleContainer implements
// //
// } // }
// } // }
// private void publish(final BigDecimal itemId) { // private void publish(final BigDecimal itemId) {
// //
// } // }
@ -385,27 +371,26 @@ public class FolderManipulator extends SimpleContainer implements
// //
// thread.start(); // thread.start();
// } // }
// private ContentItem retrieveSourceItem(final BigDecimal itemToCopyId) {
private ContentItem retrieveSourceItem(final BigDecimal itemToCopyId) { //
// ContentItem source = (ContentItem) DomainObjectFactory.newInstance(
ContentItem source = (ContentItem) DomainObjectFactory.newInstance( // new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemToCopyId));
new OID(ContentItem.BASE_DATA_OBJECT_TYPE, itemToCopyId)); // Assert.exists(source, ContentItem.class);
Assert.exists(source, ContentItem.class); //
// final ACSObject parent = source.getParent();
final ACSObject parent = source.getParent(); // if (parent instanceof ContentBundle) {
if (parent instanceof ContentBundle) { // source = (ContentBundle) parent;
source = (ContentBundle) parent; // }
} //
// if (LOGGER.isDebugEnabled()) {
if (LOGGER.isDebugEnabled()) { // LOGGER.debug("Copying item " + source);
LOGGER.debug("Copying item " + source); // }
} //
// return 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 * @return The form containing the folder browser and dropdown menu
*/ */
@ -438,7 +423,8 @@ public class FolderManipulator extends SimpleContainer implements
} }
@Override @Override
public void process(final FormSectionEvent event) throws FormProcessException { public void process(final FormSectionEvent event) throws
FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
itemView.setVisible(state, false); itemView.setVisible(state, false);
@ -455,15 +441,16 @@ public class FolderManipulator extends SimpleContainer implements
} }
@Override @Override
public void process(final FormSectionEvent event) throws FormProcessException { public void process(final FormSectionEvent event) throws
FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
itemView.setVisible(state, true); itemView.setVisible(state, true);
targetSelector.setVisible(state, false); targetSelector.setVisible(state, false);
final Folder folder = targetSelector.getTarget(state); final Category folder = targetSelector.getTarget(state);
final BigDecimal[] itemIds = getSources(state); final Long[] itemIds = getSources(state);
if (isCopy(state)) { if (isCopy(state)) {
copyItems(folder, itemIds); copyItems(folder, itemIds);
@ -503,7 +490,6 @@ public class FolderManipulator extends SimpleContainer implements
// } // }
// //
// } // }
private class ItemViewValidationListener implements FormValidationListener { private class ItemViewValidationListener implements FormValidationListener {
public ItemViewValidationListener() { public ItemViewValidationListener() {
@ -511,26 +497,30 @@ public class FolderManipulator extends SimpleContainer implements
} }
@Override @Override
public void validate(final FormSectionEvent event) throws FormProcessException { public void validate(final FormSectionEvent event) throws
FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
final FormData data = event.getFormData(); final FormData data = event.getFormData();
if (getSources(state).length <= 0) { 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() { public TargetSelectorValidationListener() {
//Nothing //Nothing
} }
@Override @Override
public void validate(final FormSectionEvent event) throws FormProcessException { public void validate(final FormSectionEvent event) throws
FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
@ -538,70 +528,82 @@ public class FolderManipulator extends SimpleContainer implements
throw new IllegalStateException("No source items specified"); throw new IllegalStateException("No source items specified");
} }
final Folder target = targetSelector.getTarget(state); final Category target = targetSelector.getTarget(state);
final FormData data = event.getFormData(); final FormData data = event.getFormData();
if (target == null) { if (target == null) {
data.addError(globalizationUtil.globalize( data.addError(new GlobalizedMessage(
"cms.ui.folder.need_select_target_folder")); "cms.ui.folder.need_select_target_folder",
CmsConstants.CMS_FOLDER_BUNDLE));
//If the target is null, we can skip the rest of the checks //If the target is null, we can skip the rest of the checks
return; return;
} }
if (target.equals(sourceFolderModel.getSelectedObject(state))) { 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 // check create item permission
final User user = Web.getWebContext().getUser(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final SecurityManager securityManager = CMS.getSecurityManager(state); final Shiro shiro = cdiUtil.findBean(Shiro.class);
if (!securityManager.canAccess(user, SecurityManager.NEW_ITEM, target)) { final PermissionChecker permissionChecker = cdiUtil.findBean(
data.addError(globalizationUtil.globalize("cms.ui.folder.no_permission_for_item")); 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)) { for (Long source : getSources(state)) {
try {
validateItem(source, target, state, data); 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()));
}
} }
} }
private void validateItem(final BigDecimal itemId, private void validateItem(final Long itemId,
final Folder target, final Category target,
final PageState state, final PageState state,
final FormData data) { 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 ContentItem item = itemRepo.findById(itemId);
final String name = item.getName(); final String name = item.getDisplayName();
final ItemCollection items = target.getItems(); final long count = itemRepo.countByNameInFolder(target, name);
items.addNameFilter(name); if (count > 0) {
if (items.next()) {
// there is an item in the target folder that already has this name // there is an item in the target folder that already has this name
addErrorMessage(data, "cms.ui.folder.item_already_exists", 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); addErrorMessage(data, "cms.ui.folder.item_is_live", name);
} }
final SecurityManager securityManager = CMS.getSecurityManager(state); if (!(permissionChecker.isPermitted(
final User user = Web.getWebContext().getUser(); CmsConstants.PRIVILEGE_ITEMS_DELETE, item))
if ((!securityManager.canAccess(user, SecurityManager.DELETE_ITEM, item))
&& isMove(state)) { && isMove(state)) {
addErrorMessage(data, "cms.ui.folder.no_permission_for_item", name); addErrorMessage(data, "cms.ui.folder.no_permission_for_item",
name);
} }
} }
} }
private void addErrorMessage(final FormData data, final String message, final String itemName) { private void addErrorMessage(final FormData data,
data.addError(globalizationUtil.globalize(message, new Object[]{itemName})); final String message,
final String itemName) {
data.addError(new GlobalizedMessage(message,
CmsConstants.CMS_FOLDER_BUNDLE,
new Object[]{itemName}));
} }
// @Override // @Override
@ -615,20 +617,24 @@ public class FolderManipulator extends SimpleContainer implements
// //
// } // }
// } // }
private class TargetSelectorSubmissionListener implements FormSubmissionListener { private class TargetSelectorSubmissionListener implements
FormSubmissionListener {
public TargetSelectorSubmissionListener() { public TargetSelectorSubmissionListener() {
//Nothing //Nothing
} }
@Override @Override
public void submitted(final FormSectionEvent event) throws FormProcessException { public void submitted(final FormSectionEvent event) throws
FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
if (targetSelector.isCancelled(state)) { if (targetSelector.isCancelled(state)) {
reset(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 PageState state = event.getPageState();
final Label label = (Label) event.getTarget(); final Label label = (Label) event.getTarget();
final int numberOfItems = getSources(state).length; 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)) { if (isMove(state)) {
label.setLabel(globalizationUtil.globalize(
"cms.ui.folder.move", new Object[]{numberOfItems, label.setLabel(new GlobalizedMessage(
folder.getPathNoJsp()})); "cms.ui.folder.move",
CmsConstants.CMS_FOLDER_BUNDLE,
new Object[]{numberOfItems,
categoryManager.getCategoryPath(
folder)}));
} else if (isCopy(state)) { } else if (isCopy(state)) {
label.setLabel(globalizationUtil.globalize( label.setLabel(new GlobalizedMessage(
"cms.ui.folder.copy", new Object[]{numberOfItems, "cms.ui.folder.copy",
folder.getPathNoJsp()})); 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 // Set things up the first time the selector gets visible
public void expose(final PageState state) { public void expose(final PageState state) {
final Folder folder = (Folder) sourceFolderModel.getSelectedObject(state); final Category folder = (Category) sourceFolderModel.
getSelectedObject(
state);
targetModel.clearSelection(state); targetModel.clearSelection(state);
if (folder != null) { if (folder != null) {
final ItemCollection items = folder.getPathInfo(true); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
while (items.next()) { final ContentItemManager itemManager = cdiUtil.findBean(
folderTree.expand(items.getID().toString(), state); ContentItemManager.class);
}
items.close(); //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); folderTree.clearSelection(state);
// FIXME: add a reset method to Tree and call that instead of this // FIXME: add a reset method to Tree and call that instead of this
// hack // hack
state.setValue(folderTree.getSelectionModel().getStateParameter(), null); state.setValue(folderTree.getSelectionModel().getStateParameter(),
null);
} }
public Folder getTarget(final PageState state) { public Category getTarget(final PageState state) {
return (Folder) targetModel.getSelectedObject(state); return (Category) targetModel.getSelectedObject(state);
} }
public boolean isCancelled(final PageState 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 // The form containing the browser and the drop down for selecting an
// action // action
private class ItemView extends Form implements Resettable { private class ItemView extends Form implements Resettable {
@ -797,13 +821,15 @@ public class FolderManipulator extends SimpleContainer implements
folderBrowser.setAtoZfilterParameter(atozFilterParam); folderBrowser.setAtoZfilterParameter(atozFilterParam);
folderBrowser.setFilterParameter(filterParam); folderBrowser.setFilterParameter(filterParam);
folderBrowser.setFilterForm(filterForm); folderBrowser.setFilterForm(filterForm);
paginator = new Paginator((PaginationModelBuilder) folderBrowser.getModelBuilder(), paginator = new Paginator(
ContentSection.getConfig().getFolderBrowseListSize()); (PaginationModelBuilder) folderBrowser.getModelBuilder(),
CMSConfig.getConfig().getFolderBrowseListSize());
panel.add(paginator); panel.add(paginator);
panel.add(folderBrowser); panel.add(folderBrowser);
LOGGER.debug("Adding filter form..."); LOGGER.debug("Adding filter form...");
filterForm = new FilterForm((FilterFormModelBuilder) folderBrowser.getModelBuilder()); filterForm = new FilterForm((FilterFormModelBuilder) folderBrowser.
getModelBuilder());
FolderManipulator.this.add(filterForm); FolderManipulator.this.add(filterForm);
checkboxGroup = new CheckboxGroup(sourcesParam); checkboxGroup = new CheckboxGroup(sourcesParam);
@ -814,14 +840,20 @@ public class FolderManipulator extends SimpleContainer implements
final Container container = new SimpleContainer(); final Container container = new SimpleContainer();
group.addAction(container); 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 = new SingleSelect(actionParam);
actionSelect.addOption(new Option(COPY, actionSelect.addOption(
new Label(globalizationUtil.globalize( new Option(COPY,
"cms.ui.folder.copy.action")))); new Label(new GlobalizedMessage(
actionSelect.addOption(new Option(MOVE, "cms.ui.folder.copy.action",
new Label(globalizationUtil.globalize( CmsConstants.CMS_FOLDER_BUNDLE))));
"cms.ui.folder.move.action")))); 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 //Publishing in the folder browser only works if threaded publishing is active
// if (CMSConfig.getInstanceOf().getThreadedPublishing()) { // if (CMSConfig.getInstanceOf().getThreadedPublishing()) {
// actionSelect.addOption(new Option(PUBLISH, // actionSelect.addOption(new Option(PUBLISH,
@ -832,7 +864,10 @@ public class FolderManipulator extends SimpleContainer implements
// "cms.ui.folder.unpublish.action")))); // "cms.ui.folder.unpublish.action"))));
// } // }
container.add(actionSelect); 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); container.add(submit);
// Add a new first column to the table // Add a new first column to the table
@ -872,7 +907,8 @@ public class FolderManipulator extends SimpleContainer implements
final int row, final int row,
final int column) { final int column) {
final BigDecimal n = (BigDecimal) key; final BigDecimal n = (BigDecimal) key;
Option result = new Option(sourcesParam.marshalElement(n.abs()), ""); Option result = new Option(sourcesParam.marshalElement(n.abs()),
"");
result.setGroup(checkboxGroup); result.setGroup(checkboxGroup);
return result; return result;
} }
@ -904,7 +940,8 @@ public class FolderManipulator extends SimpleContainer implements
panel = new BoxPanel(BoxPanel.HORIZONTAL); panel = new BoxPanel(BoxPanel.HORIZONTAL);
final ActionLink allLink = new ActionLink( 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() { allLink.addActionListener(new ActionListener() {
@Override @Override
@ -929,11 +966,15 @@ public class FolderManipulator extends SimpleContainer implements
// }); // });
// panel.add(link); // 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); filterField = new TextField(filterParam);
panel.add(filterField); panel.add(filterField);
panel.add(new Submit("filterFolderSubmit", 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); add(panel);
@ -944,25 +985,29 @@ public class FolderManipulator extends SimpleContainer implements
} }
@Override @Override
public void process(final FormSectionEvent event) throws FormProcessException { public void process(final FormSectionEvent event) throws
FormProcessException {
//Nothing //Nothing
} }
@Override @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); //fse.getPageState().setValue(FolderManipulator.this.m_filter, null);
//filterField.setValue(fse.getPageState(), null); //filterField.setValue(fse.getPageState(), null);
} }
@Override @Override
public void submitted(final FormSectionEvent event) throws FormProcessException { public void submitted(final FormSectionEvent event) throws
FormProcessException {
} }
@Override @Override
public boolean isVisible(PageState state) { public boolean isVisible(PageState state) {
if (super.isVisible(state) if (super.isVisible(state)
&& (modelBuilder.getFolderSize(state) && (modelBuilder.getFolderSize(state)
>= CMSConfig.getInstanceOf().getFolderAtoZShowLimit())) { >= CMSConfig.getConfig().
getFolderAtoZShowLimit())) {
return true; return true;
} else { } else {
return false; return false;
@ -996,9 +1041,10 @@ public class FolderManipulator extends SimpleContainer implements
private RequestLocal m_invalidFolders = new RequestLocal(); private RequestLocal m_invalidFolders = new RequestLocal();
/** /**
* Render the folders appropriately. The selected folder is a bold label. Invalid folders * Render the folders appropriately. The selected folder is a bold
* are plain labels. Unselected, valid folders are control links. Invalid folders are: the * label. Invalid folders are plain labels. Unselected, valid folders
* parent folder of the sources, any of the sources, and any subfolders of the sources. * are control links. Invalid folders are: the parent folder of the
* sources, any of the sources, and any subfolders of the sources.
*/ */
@Override @Override
public Component getComponent(final Tree tree, 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. // Get the list of invalid folders once per request.
ArrayList invalidFolders = (ArrayList) m_invalidFolders.get(state); ArrayList invalidFolders = (ArrayList) m_invalidFolders.get(state);
if (invalidFolders == null) { // if (invalidFolders == null) {
// The list of invalid folders has not been set for this // // The list of invalid folders has not been set for this
// request. Setting now. // // request. Setting now.
invalidFolders = new ArrayList(); // invalidFolders = new ArrayList();
//
final DataCollection collection = SessionManager.getSession().retrieve( // final DataCollection collection = SessionManager.getSession().
ContentItem.BASE_DATA_OBJECT_TYPE); // retrieve(
CompoundFilter filter = collection.getFilterFactory().or(); // ContentItem.BASE_DATA_OBJECT_TYPE);
// The sources themselves are not valid. // CompoundFilter filter = collection.getFilterFactory().or();
final BigDecimal[] sources = getSources(state); // // The sources themselves are not valid.
// final Long[] sources = getSources(state);
for (int i = 0; i < sources.length; i++) { //
invalidFolders.add(sources[i].toString()); // 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]); // final Filter temp = filter.addFilter("id = :id" + i);
} // temp.set("id" + i, sources[i]);
collection.addFilter(filter); // }
// collection.addFilter(filter);
final DataCollection folders = SessionManager.getSession().retrieve( //
Folder.BASE_DATA_OBJECT_TYPE); // final DataCollection folders = SessionManager.getSession().
folders.addEqualsFilter(Folder.IS_DELETED, Boolean.FALSE); // retrieve(
// Folder.BASE_DATA_OBJECT_TYPE);
filter = collection.getFilterFactory().or(); // folders.addEqualsFilter(Folder.IS_DELETED, Boolean.FALSE);
int count = 0; //
while (collection.next()) { // filter = collection.getFilterFactory().or();
filter.addFilter(Folder.ANCESTORS + " like :ancestors" // int count = 0;
+ count + " || '%'"); // while (collection.next()) {
filter.set("ancestors" + count, // filter.addFilter(Folder.ANCESTORS + " like :ancestors"
collection.get(ContentItem.ANCESTORS)); // + count + " || '%'");
count++; // filter.set("ancestors" + count,
} // collection.get(ContentItem.ANCESTORS));
folders.addFilter(filter); // count++;
// }
while (folders.next()) { // folders.addFilter(filter);
invalidFolders.add(folders.get(Folder.ID).toString()); //
} // 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"); // invalidFolders.add(sourceFolderModel.getSelectedKey(state).
dq.setParameter("item_list", invalidFolders); // toString());
//
while (dq.next()) { // // Save the invalid folder list
invalidFolders.add (dq.get("folder_id").toString()); // m_invalidFolders.set(state, invalidFolders);
} // }
*/
// 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);
}
final Label label = new Label(value.toString()); final Label label = new Label(value.toString());

View File

@ -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);
}
}

View File

@ -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.
*
* <p>
* This renderer can operate in two different modes: <em>active</em>
* and <em>inactive</em> 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 <code>TableActionEvent</code> whose <code>getKey()</code>
* and <code>getColumn()</code> method return the values of the <code>key</code>
* and <code>column</code> parameters that were passed into
* {@link #getComponent getComponent}.
*
* <p>
* In a nutshell, an active renderer will let the user click a link that causes
* a <code>TableActionEvent</code> 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 <a href="mailto:lutter@arsdigita.com">David Lutterkort</a>
* @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 <code>active</code> argument
* specifies whether the renderer should be active or not.
*
* @param active <code>true</code> 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 <code>true</code> 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
* <code>TableActionEvent</code>.
*
* @return <code>true</code> if the renderer is in active mode.
*/
public final boolean isActive() {
return m_active;
}
/**
* Set the renderer to active or inactive mode.
*
* @param v <code>true</code> 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
* <code>value</code>. 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;
}
}
}

View File

@ -71,7 +71,46 @@ import static org.librecms.CmsConstants.*;
name = "ContentItem.findByFolder", name = "ContentItem.findByFolder",
query = "SELECT c.categorizedObject FROM Categorization c " 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, public class ContentItem extends CcmObject implements Serializable,
InheritsPermissions { InheritsPermissions {
@ -258,7 +297,8 @@ public class ContentItem extends CcmObject implements Serializable,
@Override @Override
public Optional<CcmObject> getParent() { public Optional<CcmObject> getParent() {
final List<Categorization> result = getCategories().stream().filter( final List<Categorization> result = getCategories().stream().filter(
categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER.equals( categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER.
equals(
categorization.getType())) categorization.getType()))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@ -18,8 +18,14 @@
*/ */
package org.librecms.contentsection; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
import org.libreccm.workflow.WorkflowTemplate; import org.libreccm.workflow.WorkflowTemplate;
import org.librecms.lifecycle.LifecycleDefinition; import org.librecms.lifecycle.LifecycleDefinition;
@ -30,8 +36,31 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import org.libreccm.categorization.Categorization; 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.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 @RequestScoped
public class ContentItemManager { 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 @Inject
private ContentItemRepository contentItemRepo; private ContentItemRepository contentItemRepo;
@ -49,6 +90,12 @@ public class ContentItemManager {
@Inject @Inject
private ContentSectionManager sectionManager; private ContentSectionManager sectionManager;
@Inject
private LifecycleManager lifecycleManager;
@Inject
private WorkflowManager workflowManager;
/** /**
* Creates a new content item in the provided content section and folder * Creates a new content item in the provided content section and folder
* with the default lifecycle and workflow. * with the default lifecycle and workflow.
@ -65,6 +112,7 @@ public class ContentItemManager {
* *
* @return The new content item. * @return The new content item.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public <T extends ContentItem> T createContentItem( public <T extends ContentItem> T createContentItem(
final String name, final String name,
final ContentSection section, final ContentSection section,
@ -112,6 +160,7 @@ public class ContentItemManager {
* *
* @return The new content item. * @return The new content item.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public <T extends ContentItem> T createContentItem( public <T extends ContentItem> T createContentItem(
final String name, final String name,
final ContentSection section, final ContentSection section,
@ -119,7 +168,47 @@ public class ContentItemManager {
final WorkflowTemplate workflowTemplate, final WorkflowTemplate workflowTemplate,
final LifecycleDefinition lifecycleDefinition, final LifecycleDefinition lifecycleDefinition,
final Class<T> type) { final Class<T> type) {
throw new UnsupportedOperationException();
final Optional<ContentType> 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 item The item to move.
* @param targetFolder The folder to which the item is moved. * @param targetFolder The folder to which the item is moved.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public void move(final ContentItem item, final Category targetFolder) { public void move(final ContentItem item, final Category targetFolder) {
throw new UnsupportedOperationException(); final ContentItem draftItem = getDraftVersion(item, item.getClass());
final Optional<Category> 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 * original item an index is appended to the name of the
* item. * item.
*/ */
@SuppressWarnings("unchecked")
public void copy(final ContentItem item, final Category targetFolder) { public void copy(final ContentItem item, final Category targetFolder) {
final Optional<ContentType> 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<Category> 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<Object> source;
final List<Object> target;
try {
source = (List<Object>) readMethod.invoke(draftItem);
target = (List<Object>) 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<Object, Object> source;
final Map<Object, Object> target;
try {
source = (Map<Object, Object>) readMethod.invoke(draftItem);
target = (Map<Object, Object>) 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<Object> source;
final Set<Object> target;
try {
source = (Set<Object>) readMethod.invoke(draftItem);
target = (Set<Object>) 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(); 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 * Creates a live version of content item or updates the live version of a
* content item if there already a live version. * content item if there already a live version.
@ -156,8 +439,151 @@ public class ContentItemManager {
* *
* @return The published content item. * @return The published content item.
*/ */
@SuppressWarnings("unchecked")
public ContentItem publish(final ContentItem item) { 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<ContentItem> 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<Object> source;
final List<Object> target;
try {
source = (List<Object>) readMethod.invoke(draftItem);
target = (List<Object>) 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<Object, Object> source;
final Map<Object, Object> target;
try {
source = (Map<Object, Object>) readMethod.invoke(draftItem);
target = (Map<Object, Object>) 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<Object> source;
final Set<Object> target;
try {
source = (Set<Object>) readMethod.invoke(draftItem);
target = (Set<Object>) 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 * @param item
*/ */
public void unpublish(final ContentItem item) { @Transactional(Transactional.TxType.REQUIRED)
throw new UnsupportedOperationException(); public void unpublish(final ContentItem item
) {
final Optional<ContentItem> liveItem = getLiveVersion(
item, ContentItem.class);
if (liveItem.isPresent()) {
entityManager.remove(liveItem);
}
} }
/** /**
@ -178,7 +612,11 @@ public class ContentItemManager {
* {@code false} if not. * {@code false} if not.
*/ */
public boolean isLive(final ContentItem item) { public boolean isLive(final ContentItem item) {
throw new UnsupportedOperationException(); final TypedQuery<Boolean> query = entityManager.createNamedQuery(
"ContentItem.hasLiveVersion", Boolean.class);
query.setParameter("uuid", item.getUuid());
return query.getSingleResult();
} }
/** /**
@ -196,7 +634,16 @@ public class ContentItemManager {
public <T extends ContentItem> Optional<T> getLiveVersion( public <T extends ContentItem> Optional<T> getLiveVersion(
final ContentItem item, final ContentItem item,
final Class<T> type) { final Class<T> type) {
throw new UnsupportedOperationException();
if (isLive(item)) {
final TypedQuery<T> 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 extends ContentItem> T getDraftVersion(final ContentItem item, public <T extends ContentItem> T getDraftVersion(final ContentItem item,
final Class<T> type) { final Class<T> type) {
throw new UnsupportedOperationException(); final TypedQuery<T> 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}. * {@code /research/computer-science/artificial-intelligence/neural-nets}.
* If the parameter {@code withContentSection} is set to {@code true} the * If the parameter {@code withContentSection} is set to {@code true} the
* the path will be prefixed with the name of the content section. For * 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 * {@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 * @param withContentSection Wether to include the content section into the
* path. * path.
* *
@ -339,4 +790,26 @@ public class ContentItemManager {
return folders; 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<Category> getItemFolder(final ContentItem item) {
final List<Categorization> 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();
}
}
} }

View File

@ -26,10 +26,12 @@ import org.libreccm.core.CcmObjectRepository;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import jdk.nashorn.internal.objects.NativeArray;
/** /**
* Repository for content items. * Repository for content items.
@ -163,16 +165,51 @@ public class ContentItemRepository
* @return A list of all items in the provided folder. * @return A list of all items in the provided folder.
*/ */
public List<ContentItem> findByFolder(final Category folder) { public List<ContentItem> findByFolder(final Category folder) {
final TypedQuery<CcmObject> query = getEntityManager() final TypedQuery<ContentItem> query = getEntityManager()
.createNamedQuery("ContentItem.findByFolder", CcmObject.class); .createNamedQuery("ContentItem.findByFolder",
ContentItem.class);
query.setParameter("folder", folder); query.setParameter("folder", folder);
final List<ContentItem> result = new ArrayList<>(); return query.getResultList();
query.getResultList().stream() }
.filter(obj -> (obj instanceof ContentItem))
.forEach(obj -> result.add((ContentItem) obj));
return result; public long countItemsInFolder(final Category folder) {
final TypedQuery<Long> query = getEntityManager()
.createNamedQuery("ContentItem.countItemsInFolder", Long.class);
query.setParameter("folder", folder);
return query.getSingleResult();
}
public long countByNameInFolder(final Category folder, final String name) {
final TypedQuery<Long> query = getEntityManager().createNamedQuery(
"ContentItem.countByNameInFolder", Long.class);
query.setParameter("folder", folder);
query.setParameter("name", name);
return query.getSingleResult();
}
public List<ContentItem> filterByFolderAndName(final Category folder,
final String name) {
final TypedQuery<ContentItem> 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<Long> query = getEntityManager()
.createNamedQuery("ContentItem.countFilterByNameAndFolder",
Long.class);
query.setParameter("folder", folder);
query.setParameter("name", name);
return query.getSingleResult();
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.librecms.contentsection; package org.librecms.contentsection;
import com.arsdigita.cms.dispatcher.ItemResolver;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
@ -34,7 +35,6 @@ import org.libreccm.security.RoleRepository;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
@ -275,4 +275,16 @@ public class ContentSectionManager {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public ItemResolver getItemResolver(final ContentSection section) {
try {
final Class<ItemResolver> itemResolverClazz = (Class<ItemResolver>) Class.
forName(section.getItemResolverClass());
return itemResolverClazz.newInstance();
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException ex) {
throw new RuntimeException(ex);
}
}
} }

View File

@ -285,6 +285,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<groups>org.libreccm.tests.categories.UnitTest</groups> <groups>org.libreccm.tests.categories.UnitTest</groups>
<trimStackTrace>false</trimStackTrace>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@ -425,6 +426,9 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId> <artifactId>maven-surefire-report-plugin</artifactId>
<version>2.19.1</version> <version>2.19.1</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
@ -708,6 +712,7 @@
<additionalClasspathElements> <additionalClasspathElements>
<additionalClasspathElement>${project.build.directory}/generated-resources</additionalClasspathElement> <additionalClasspathElement>${project.build.directory}/generated-resources</additionalClasspathElement>
</additionalClasspathElements> </additionalClasspathElements>
<trimStackTrace>false</trimStackTrace>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@ -784,6 +789,7 @@
org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest org.libreccm.tests.categories.IntegrationTest
</groups> </groups>
<trimStackTrace>false</trimStackTrace>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@ -1083,6 +1089,7 @@
org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest org.libreccm.tests.categories.IntegrationTest
</groups> </groups>
<trimStackTrace>false</trimStackTrace>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@ -1169,6 +1176,7 @@
org.libreccm.tests.categories.UnitTest, org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest org.libreccm.tests.categories.IntegrationTest
</groups> </groups>
<trimStackTrace>false</trimStackTrace>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -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;
/**
* <p>A simple layout panel with top, bottom, left, right, and body
* sections.</p>
*
* @author Justin Ross &lt;jross@redhat.com&gt;
* @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);
}
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -74,11 +74,29 @@ public abstract class AbstractAuditedEntityRepository<K, T>
* @throws NotAuditedException When entities of the given class are not * @throws NotAuditedException When entities of the given class are not
* audited. * audited.
* @throws IllegalArgumentException If cls or primaryKey is null. * @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<Number> retrieveRevisionNumbersOfEntity(final T entity, public List<Number> retrieveRevisionNumbersOfEntity(final T entity,
final Long objectId) { final Long objectId) {
return auditReader.getRevisions(entity.getClass(), objectId); return auditReader.getRevisions(entity.getClass(), objectId);
} }
public CcmRevision retrieveFirstRevision(final T entity,
final Long objectId) {
final List<Number> revisions = retrieveRevisionNumbersOfEntity(
entity, objectId);
return auditReader.findRevision(CcmRevision.class, revisions.get(0));
}
public CcmRevision retrieveCurrentRevision(final T entity,
final Long objectId) {
final List<Number> revisions = retrieveRevisionNumbersOfEntity(
entity, objectId);
final Number lastRevision = revisions.get(revisions.size() - 1);
return auditReader.findRevision(CcmRevision.class, lastRevision);
}
} }

View File

@ -48,10 +48,23 @@ import javax.persistence.Table;
@Entity @Entity
@Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA) @Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA)
@NamedQueries({ @NamedQueries({
@NamedQuery(name = "Categorization.find", @NamedQuery(
name = "Categorization.find",
query = "SELECT c FROM Categorization c " query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category " + "WHERE c.category = :category "
+ "AND c.categorizedObject = :object") + "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 { public class Categorization implements Serializable {

View File

@ -25,11 +25,15 @@ import org.apache.logging.log4j.Logger;
import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository; import org.libreccm.core.CcmObjectRepository;
import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.RequiresPrivilege; import org.libreccm.security.RequiresPrivilege;
import org.libreccm.security.Shiro; import org.libreccm.security.Shiro;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
@ -65,6 +69,9 @@ public class CategoryManager {
@Inject @Inject
private Shiro shiro; private Shiro shiro;
@Inject
private PermissionChecker permissionChecker;
/** /**
* Assigns an category to an object. * Assigns an category to an object.
* *
@ -89,6 +96,35 @@ public class CategoryManager {
@RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE) @RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE)
final Category category) { 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) { if (object == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Null can't be added to a category."); "Null can't be added to a category.");
@ -104,11 +140,12 @@ public class CategoryManager {
categorization.setCategory(category); categorization.setCategory(category);
categorization.setCategoryOrder(object.getCategories().size() + 1); categorization.setCategoryOrder(object.getCategories().size() + 1);
categorization.setObjectOrder(category.getObjects().size() + 1); categorization.setObjectOrder(category.getObjects().size() + 1);
categorization.setType(type);
object.addCategory(categorization); object.addCategory(categorization);
category.addObject(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 // 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 // to a category. Therefore we bypass the this authorisation check here
// by executing CategoryRepository#save(Category) as the system user. // 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<String> 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<Long> hasIndexItemQuery = entityManager
// .createNamedQuery("Categorization.hasIndexObject", Long.class);
// hasIndexItemQuery.setParameter("category", category);
// final long indexItems = hasIndexItemQuery.getSingleResult();
// return indexItems > 0;
final TypedQuery<Boolean> 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<CcmObject> getIndexObject(final Category category) {
if (hasIndexObject(category)) {
final TypedQuery<CcmObject> query = entityManager.createNamedQuery(
"Categorization.findIndexObject", CcmObject.class);
query.setParameter("category", category);
return Optional.of(query.getSingleResult());
} else {
return Optional.empty();
}
}
} }

View File

@ -87,11 +87,11 @@ public class PermissionChecker {
return result; return result;
} else if (object instanceof InheritsPermissions) { } else if (object instanceof InheritsPermissions) {
if (((InheritsPermissions) object).getParent().isPresent()) { if (((InheritsPermissions) object).getParent().isPresent()) {
return result;
} else {
return isPermitted( return isPermitted(
privilege, privilege,
((InheritsPermissions) object).getParent().get()); ((InheritsPermissions) object).getParent().get());
} else {
return result;
} }
} else { } else {
return result; return result;
@ -141,18 +141,16 @@ public class PermissionChecker {
if (!result) { if (!result) {
if (((InheritsPermissions) object).getParent().isPresent()) { if (((InheritsPermissions) object).getParent().isPresent()) {
if (subject.isAuthenticated()) { checkPermission(
privilege,
((InheritsPermissions) object).getParent().get());
} else if (subject.isAuthenticated()) {
subject.checkPermission(generatePermissionString( subject.checkPermission(generatePermissionString(
privilege, object)); privilege, object));
} else { } else {
shiro.getPublicUser().checkPermission( shiro.getPublicUser().checkPermission(
generatePermissionString(privilege, object)); generatePermissionString(privilege, object));
} }
} else {
checkPermission(
privilege,
((InheritsPermissions) object).getParent().get());
}
} }
} else if (subject.isAuthenticated()) { } else if (subject.isAuthenticated()) {
subject.checkPermission(generatePermissionString(privilege, object)); subject.checkPermission(generatePermissionString(privilege, object));

View File

@ -47,7 +47,7 @@ import org.libreccm.security.Shiro;
import org.libreccm.tests.categories.IntegrationTest; import org.libreccm.tests.categories.IntegrationTest;
import java.io.File; import java.io.File;
import java.util.concurrent.Callable; import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@ -583,4 +583,33 @@ public class CategoryManagerTest {
}); });
} }
@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<CcmObject> index2 = categoryManager.getIndexObject(
category2);
assertThat(index2.isPresent(), is(true));
assertThat(index2.get().getDisplayName(), is(equalTo("object3")));
}
} }

View File

@ -39,6 +39,7 @@ CREATE SCHEMA ccm_core;
CATEGORY_ORDER bigint, CATEGORY_ORDER bigint,
CATEGORY_INDEX boolean, CATEGORY_INDEX boolean,
OBJECT_ORDER bigint, OBJECT_ORDER bigint,
TYPE varchar(255),
OBJECT_ID bigint, OBJECT_ID bigint,
CATEGORY_ID bigint, CATEGORY_ID bigint,
primary key (CATEGORIZATION_ID) primary key (CATEGORIZATION_ID)

View File

@ -39,6 +39,7 @@ CREATE SCHEMA ccm_core;
CATEGORY_ORDER int8, CATEGORY_ORDER int8,
CATEGORY_INDEX boolean, CATEGORY_INDEX boolean,
OBJECT_ORDER int8, OBJECT_ORDER int8,
TYPE varchar(255),
OBJECT_ID int8, OBJECT_ID int8,
CATEGORY_ID int8, CATEGORY_ID int8,
primary key (CATEGORIZATION_ID) primary key (CATEGORIZATION_ID)

View File

@ -71,4 +71,4 @@ ccm_core.categorizations:
category_id: -2100 category_id: -2100
category_order: 1 category_order: 1
object_order: 2 object_order: 2
category_index: false category_index: true

View File

@ -76,4 +76,4 @@ ccm_core.categorizations:
object_id: -3300 object_id: -3300
category_order: 1 category_order: 1
object_order: 1 object_order: 1
category_index: false category_index: true

View File

@ -109,4 +109,4 @@ ccm_core.categorizations:
object_id: -3300 object_id: -3300
category_order: 1 category_order: 1
object_order: 1 object_order: 1
category_index: false category_index: true

View File

@ -59,4 +59,4 @@ ccm_core.categorizations:
object_id: -3300 object_id: -3300
category_order: 1 category_order: 1
object_order: 1 object_order: 1
category_index: false category_index: true

View File

@ -64,4 +64,4 @@ ccm_core.categorizations:
object_id: -3300 object_id: -3300
category_order: 1 category_order: 1
object_order: 1 object_order: 1
category_index: false category_index: true

View File

@ -65,7 +65,7 @@ ccm_core.categorizations:
object_id: -3300 object_id: -3300
category_order: 1 category_order: 1
object_order: 1 object_order: 1
category_index: false category_index: true
ccm_core.parties: ccm_core.parties:
- party_id: -3000 - party_id: -3000