- 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;
/**
* <p>The <tt>ItemResolver</tt> is responsible for mapping a URL in a
* particular content section to a content item.</p>
* <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>
*
* <p>The item resolver would be asked to map the URL stub <tt>/cheese</tt>
* in the content section mounted at <tt>/cms</tt> to a content item. To
* this end, the dispatcher calls the <tt>getItem</tt> method, passing in
* the {@link com.arsdigita.cms.ContentSection} and the URL stub for the
* item within the section, <tt>/cheese</tt> in our example. As a final
* argument, the dispatcher passes either <tt>ContentItem.DRAFT</tt> or
* <tt>ContentItem.LIVE</tt> to the <tt>ItemResolver</tt>, depending on
* whether the returned item should be the live version (for public pages)
* or the draft version (for previewing).</p>
* <p>
* The item resolver would be asked to map the URL stub <tt>/cheese</tt>
* in the content section mounted at <tt>/cms</tt> to a content item. To this
* end, the dispatcher calls the <tt>getItem</tt> method, passing in the
* {@link com.arsdigita.cms.ContentSection} and the URL stub for the item within
* the section, <tt>/cheese</tt> in our example. As a final argument, the
* dispatcher passes either <tt>ContentItem.DRAFT</tt> or
* <tt>ContentItem.LIVE</tt> to the <tt>ItemResolver</tt>, depending on whether
* 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 Stanislav Freidin (sfreidin@arsdigita.com)
@ -60,7 +63,8 @@ public interface ItemResolver {
* @param context The use context
* @return The content item, or null if no such item exists
*/
public ContentItem getItem(ContentSection section, String url,
public ContentItem getItem(ContentSection section,
String url,
String context);
/**
@ -82,10 +86,11 @@ public interface ItemResolver {
* @return The URL of the item
* @see #getCurrentContext
*/
public String generateItemURL (
PageState state, BigDecimal itemId, String name,
ContentSection section, String context
);
public String generateItemURL(PageState state,
Long itemId,
String name,
ContentSection section,
String context);
/**
* Generates a URL for a content item.
@ -99,10 +104,13 @@ public interface ItemResolver {
* @return The URL of the item
* @see #getCurrentContext
*/
public String generateItemURL (
PageState state, BigDecimal itemId, String name,
ContentSection section, String context, String templateContext
);
public String generateItemURL(PageState state,
Long itemId,
String name,
ContentSection section,
String context,
String templateContext
);
/**
* Generates a URL for a content item.
@ -114,9 +122,10 @@ public interface ItemResolver {
* @return The URL of the item
* @see #getCurrentContext
*/
public String generateItemURL (
PageState state, ContentItem item, ContentSection section, String context
);
public String generateItemURL(PageState state,
ContentItem item,
ContentSection section,
String context);
/**
* Generates a URL for a content item.
@ -129,21 +138,22 @@ public interface ItemResolver {
* @return The URL of the item
* @see #getCurrentContext
*/
public String generateItemURL (
PageState state, ContentItem item, ContentSection section, String context,
String templateContext
);
public String generateItemURL(PageState state,
ContentItem item,
ContentSection section,
String context,
String templateContext);
/**
* Return a master page based on page state (and content section).
*
* @param item The content item
* @param item The content item
* @param request The HTTP request
* @return The master page
*/
public CMSPage getMasterPage(ContentItem item, HttpServletRequest request)
throws ServletException;
throws ServletException;
/*
* Having to stick the following methods, getTemplateFromURL, and
@ -152,16 +162,16 @@ public interface ItemResolver {
* to be cleaned up to fix this. As it is, ItemResolver parses URL's for
* template contexts, and TemplateResolver sets the actual template contexts
* in the request.
*/
*/
/**
* Finds the template context from the URL and returns it, if it is there.
* Otherwise, returns null.
*
* @param inUrl the URL from which to get the template context
* @return the template context, or null if there is no template context
*/
public String getTemplateFromURL(String inUrl);
*/
public String getTemplateFromURL(String inUrl);
/**
* Removes the template context from the <code>inUrl</code>.
*

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

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

@ -70,8 +70,47 @@ import static org.librecms.CmsConstants.*;
@NamedQuery(
name = "ContentItem.findByFolder",
query = "SELECT c.categorizedObject FROM Categorization c "
+ "WHERE c.category = :folder"
)
+ "WHERE c.category = :folder "
+ "AND TYPE(c.categorizedObject) IN ContentItem"),
@NamedQuery(
name = "ContentItem.countItemsInFolder",
query = "SELECT COUNT(c) FROM Categorization c "
+ "WHERE c.category = :folder "
+ "AND TYPE(c.categorizedObject) IN ContentItem"),
@NamedQuery(
name = "ContentItem.countByNameInFolder",
query = "SELECT count(c) FROM Categorization c "
+ "WHERE c.category = :folder "
+ "AND c.categorizedObject.displayName = :name"),
@NamedQuery(
name = "ContentItem.filterByNameAndFolder",
query = "SELECT c.categorizedObject FROM Categorization c "
+ "WHERE c.category = :folder "
+ "AND TYPE(c.categorizedObject) IN (ContentItem)"
+ "AND (LOWER(c.categorizedObject.displayName) LIKE CONCAT(LOWER(:name), '%') "),
@NamedQuery(
name = "ContentItem.countFilterByNameAndFolder",
query = "SELECT count(c) FROM Categorization c "
+ "WHERE c.category = :folder "
+ "AND TYPE(c.categorizedObject) IN (ContentItem)"
+ "AND LOWER(c.categorizedObject.displayName) LIKE CONCAT(LOWER(:name), '%s') "),
@NamedQuery(
name = "ContentItem.hasLiveVersion",
query = "SELECT (CASE WHEN COUNT(i) > 0 THEN true ELSE false END) "
+ "FROM ContentItem i "
+ "WHERE i.uuid = :uuid "
+ "AND i.version = \"LIVE\""),
@NamedQuery(
name = "ContentItem.findDraftVersion",
query = "SELECT i FROM ContentItem i "
+ "WHERE i.uuid = :uuid "
+ "AND i.version = \"DRAFT\""),
@NamedQuery(
name = "ContentItem.findLiveVersion",
query = "SELECT i FROM ContentItem i "
+ "WHERE i.uuid = :uuid "
+ "AND i.version = \"LIVE\"")
})
public class ContentItem extends CcmObject implements Serializable,
InheritsPermissions {
@ -258,7 +297,8 @@ public class ContentItem extends CcmObject implements Serializable,
@Override
public Optional<CcmObject> getParent() {
final List<Categorization> result = getCategories().stream().filter(
categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER.equals(
categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER.
equals(
categorization.getType()))
.collect(Collectors.toList());

View File

@ -18,8 +18,14 @@
*/
package org.librecms.contentsection;
import com.arsdigita.kernel.KernelConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collections;
import org.libreccm.categorization.Category;
import org.libreccm.workflow.WorkflowTemplate;
import org.librecms.lifecycle.LifecycleDefinition;
@ -30,8 +36,31 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.ObjectNotAssignedToCategoryException;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.l10n.LocalizedString;
import org.libreccm.workflow.Workflow;
import org.libreccm.workflow.WorkflowManager;
import org.librecms.CmsConstants;
import org.librecms.lifecycle.Lifecycle;
import org.librecms.lifecycle.LifecycleManager;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
/**
*
@ -40,6 +69,18 @@ import org.librecms.CmsConstants;
@RequestScoped
public class ContentItemManager {
private static final Logger LOGGER = LogManager.getLogger(
ContentItemManager.class);
@Inject
private EntityManager entityManager;
@Inject
private ConfigurationManager confManager;
@Inject
private CategoryManager categoryManager;
@Inject
private ContentItemRepository contentItemRepo;
@ -49,6 +90,12 @@ public class ContentItemManager {
@Inject
private ContentSectionManager sectionManager;
@Inject
private LifecycleManager lifecycleManager;
@Inject
private WorkflowManager workflowManager;
/**
* Creates a new content item in the provided content section and folder
* with the default lifecycle and workflow.
@ -65,6 +112,7 @@ public class ContentItemManager {
*
* @return The new content item.
*/
@Transactional(Transactional.TxType.REQUIRED)
public <T extends ContentItem> T createContentItem(
final String name,
final ContentSection section,
@ -112,6 +160,7 @@ public class ContentItemManager {
*
* @return The new content item.
*/
@Transactional(Transactional.TxType.REQUIRED)
public <T extends ContentItem> T createContentItem(
final String name,
final ContentSection section,
@ -119,7 +168,47 @@ public class ContentItemManager {
final WorkflowTemplate workflowTemplate,
final LifecycleDefinition lifecycleDefinition,
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 targetFolder The folder to which the item is moved.
*/
@Transactional(Transactional.TxType.REQUIRED)
public void move(final ContentItem item, final Category targetFolder) {
throw new UnsupportedOperationException();
final ContentItem draftItem = getDraftVersion(item, item.getClass());
final Optional<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
* item.
*/
@SuppressWarnings("unchecked")
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();
}
private boolean propertyIsExcluded(final String name) {
final String[] excluded = new String[]{
"objectId", "uuid", "lifecycle", "workflow", "categories",
"attachments"
};
boolean result = false;
for (final String current : excluded) {
if (current.equals(name)) {
result = true;
}
}
return result;
}
/**
* Creates a live version of content item or updates the live version of a
* content item if there already a live version.
@ -156,8 +439,151 @@ public class ContentItemManager {
*
* @return The published content item.
*/
@SuppressWarnings("unchecked")
public ContentItem publish(final ContentItem item) {
throw new UnsupportedOperationException();
final ContentItem draftItem = getDraftVersion(item, ContentItem.class);
final ContentItem liveItem;
if (isLive(item)) {
liveItem = getLiveVersion(item, ContentItem.class).get();
} else {
try {
liveItem = draftItem.getClass().newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
liveItem.setContentType(draftItem.getContentType());
liveItem.setLifecycle(draftItem.getLifecycle());
liveItem.setWorkflow(draftItem.getWorkflow());
draftItem.getCategories().forEach(categorization -> categoryManager
.addObjectToCategory(item, categorization.getCategory()));
liveItem.setUuid(draftItem.getUuid());
// !!!!!!!!!!!!!!!!!!!!!
// ToDo copy Attachments
// !!!!!!!!!!!!!!!!!!!!!
//
//
final BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(item.getClass());
} catch (IntrospectionException ex) {
throw new RuntimeException(ex);
}
for (final PropertyDescriptor propertyDescriptor : beanInfo
.getPropertyDescriptors()) {
if (propertyIsExcluded(propertyDescriptor.getName())) {
continue;
}
final Class<?> propType = propertyDescriptor.getPropertyType();
final Method readMethod = propertyDescriptor.getReadMethod();
final Method writeMethod = propertyDescriptor.getWriteMethod();
if (LocalizedString.class.equals(propType)) {
final LocalizedString source;
final LocalizedString target;
try {
source = (LocalizedString) readMethod.invoke(draftItem);
target = (LocalizedString) readMethod.invoke(liveItem);
} catch (IllegalAccessException |
IllegalArgumentException |
InvocationTargetException ex) {
throw new RuntimeException(ex);
}
source.getAvailableLocales().forEach(
locale -> target.addValue(locale, source.getValue(locale)));
} else if (propType != null
&& propType.isAssignableFrom(ContentItem.class)) {
final ContentItem linkedItem;
try {
linkedItem = (ContentItem) readMethod.invoke(draftItem);
} catch (IllegalAccessException |
IllegalArgumentException |
InvocationTargetException ex) {
throw new RuntimeException(ex);
}
final ContentItem linkedDraftItem = getDraftVersion(
linkedItem, linkedItem.getClass());
if (isLive(linkedDraftItem)) {
try {
final Optional<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
*/
public void unpublish(final ContentItem item) {
throw new UnsupportedOperationException();
@Transactional(Transactional.TxType.REQUIRED)
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.
*/
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(
final ContentItem item,
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,
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}.
* If the parameter {@code withContentSection} is set to {@code true} the
* the path will be prefixed with the name of the content section. For
* instance if the item {@link neural-nets} is part of the content section
* instance if the item {@code neural-nets} is part of the content section
* {@code info}, the path including the content section would be
* {@link info:/research/computer-science/artificial-intelligence/neural-nets}.
* {@code info:/research/computer-science/artificial-intelligence/neural-nets}.
*
* @param item The item which path is generated.
* @param item The item whose path is generated.
* @param withContentSection Wether to include the content section into the
* path.
*
@ -339,4 +790,26 @@ public class ContentItemManager {
return folders;
}
/**
* Gets the folder in which in item is placed (if the item is part of
* folder).
*
* @param item The item
*
* @return An {@link Optional} containing the folder of the item if the item
* is part of a folder.
*/
public Optional<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.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.TypedQuery;
import jdk.nashorn.internal.objects.NativeArray;
/**
* Repository for content items.
@ -38,7 +40,7 @@ import javax.persistence.TypedQuery;
*/
@RequestScoped
public class ContentItemRepository
extends AbstractAuditedEntityRepository<Long, ContentItem> {
extends AbstractAuditedEntityRepository<Long, ContentItem> {
@Inject
private CcmObjectRepository ccmObjectRepo;
@ -64,7 +66,7 @@ public class ContentItemRepository
* @param itemId The id of item to retrieve.
*
* @return The content item identified by the provided {@code itemId} or
* nothing if there is such content item.
* nothing if there is such content item.
*/
public Optional<ContentItem> findById(final long itemId) {
final CcmObject result = ccmObjectRepo.findObjectById(itemId);
@ -78,13 +80,13 @@ public class ContentItemRepository
/**
* Finds a content item by its ID and ensures that is a the requested type.
*
* @param <T> The type of the content item.
* @param <T> The type of the content item.
* @param itemId The id of item to retrieve.
* @param type The type of the content item.
* @param type The type of the content item.
*
* @return The content item identified by the provided id or an empty
* {@link Optional} if there is no such item or if it is not of the
* requested type.
* {@link Optional} if there is no such item or if it is not of the
* requested type.
*/
@SuppressWarnings("unchecked")
public <T extends ContentItem> Optional<T> findById(final long itemId,
@ -103,7 +105,7 @@ public class ContentItemRepository
* @param uuid The id of item to retrieve.
*
* @return The content item identified by the provided {@code uuid} or
* nothing if there is such content item.
* nothing if there is such content item.
*/
public ContentItem findByUuid(final String uuid) {
final CcmObject result = ccmObjectRepo.findObjectByUuid(uuid);
@ -118,13 +120,13 @@ public class ContentItemRepository
* Finds a content item by its UUID and ensures that is a the requested
* type.
*
* @param <T> The type of the content item.
* @param <T> The type of the content item.
* @param uuid The UUID of item to retrieve.
* @param type The type of the content item.
*
* @return The content item identified by the provided UUID or an empty
* {@link Optional} if there is no such item or if it is not of the
* requested type.
* {@link Optional} if there is no such item or if it is not of the
* requested type.
*/
@SuppressWarnings("unchecked")
public <T extends ContentItem> Optional<T> findByUuid(final String uuid,
@ -141,7 +143,7 @@ public class ContentItemRepository
/**
* Finds all content items of a specific type.
*
* @param <T> The type of the items.
* @param <T> The type of the items.
* @param type The type of the items.
*
* @return A list of all content items of the requested type.
@ -149,7 +151,7 @@ public class ContentItemRepository
@SuppressWarnings("unchecked")
public <T extends ContentItem> List<T> findByType(final Class<T> type) {
final TypedQuery<ContentItem> query = getEntityManager()
.createNamedQuery("ContentItem.findByType", ContentItem.class);
.createNamedQuery("ContentItem.findByType", ContentItem.class);
query.setParameter("type", type);
return (List<T>) query.getResultList();
@ -163,16 +165,51 @@ public class ContentItemRepository
* @return A list of all items in the provided folder.
*/
public List<ContentItem> findByFolder(final Category folder) {
final TypedQuery<CcmObject> query = getEntityManager()
.createNamedQuery("ContentItem.findByFolder", CcmObject.class);
final TypedQuery<ContentItem> query = getEntityManager()
.createNamedQuery("ContentItem.findByFolder",
ContentItem.class);
query.setParameter("folder", folder);
final List<ContentItem> result = new ArrayList<>();
query.getResultList().stream()
.filter(obj -> (obj instanceof ContentItem))
.forEach(obj -> result.add((ContentItem) obj));
return query.getResultList();
}
public long countItemsInFolder(final Category folder) {
final TypedQuery<Long> query = getEntityManager()
.createNamedQuery("ContentItem.countItemsInFolder", Long.class);
query.setParameter("folder", folder);
return query.getSingleResult();
}
return result;
public long countByNameInFolder(final Category folder, final String name) {
final TypedQuery<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

@ -116,7 +116,7 @@ public class ContentSectionConfig {
public static ContentSectionConfig getConfig() {
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
.findBean(ConfigurationManager.class);
.findBean(ConfigurationManager.class);
return confManager.findConfiguration(ContentSectionConfig.class);
}

View File

@ -18,6 +18,7 @@
*/
package org.librecms.contentsection;
import com.arsdigita.cms.dispatcher.ItemResolver;
import com.arsdigita.kernel.KernelConfig;
import org.libreccm.categorization.Category;
@ -34,7 +35,6 @@ import org.libreccm.security.RoleRepository;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.stream.Stream;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@ -86,13 +86,13 @@ public class ContentSectionManager {
public ContentSection createContentSection(final String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException(
"The name of a ContentSection can't be blank.");
"The name of a ContentSection can't be blank.");
}
final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class);
KernelConfig.class);
final Locale defautLocale
= new Locale(kernelConfig.getDefaultLanguage());
= new Locale(kernelConfig.getDefaultLanguage());
final ContentSection section = new ContentSection();
section.setLabel(name);
@ -232,12 +232,12 @@ public class ContentSectionManager {
@RequiresPrivilege(CoreConstants.ADMIN_PRIVILEGE)
@Transactional(Transactional.TxType.REQUIRED)
public void removeRoleFromContentSection(
final ContentSection contentSection,
final Role role) {
final ContentSection contentSection,
final Role role) {
if (contentSection == null) {
throw new IllegalArgumentException(
"Can't remove role from ContentSection null");
"Can't remove role from ContentSection null");
}
if (role == null) {
@ -248,10 +248,10 @@ public class ContentSectionManager {
sectionRepo.save(contentSection);
final TypedQuery<Permission> query = entityManager
.createNamedQuery("ContentSection.findPermissions",
Permission.class);
.createNamedQuery("ContentSection.findPermissions",
Permission.class);
query.setParameter("section", contentSection);
query.setParameter("rootDocumentsFolder",
query.setParameter("rootDocumentsFolder",
contentSection.getRootDocumentsFolder());
query.setParameter("role", role);
@ -275,4 +275,16 @@ public class ContentSectionManager {
throw new UnsupportedOperationException();
}
public ItemResolver getItemResolver(final ContentSection section) {
try {
final Class<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>
<configuration>
<groups>org.libreccm.tests.categories.UnitTest</groups>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>
@ -425,6 +426,9 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
@ -708,6 +712,7 @@
<additionalClasspathElements>
<additionalClasspathElement>${project.build.directory}/generated-resources</additionalClasspathElement>
</additionalClasspathElements>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</plugins>
@ -784,6 +789,7 @@
org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest
</groups>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</plugins>
@ -1083,6 +1089,7 @@
org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest
</groups>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</plugins>
@ -1169,6 +1176,7 @@
org.libreccm.tests.categories.UnitTest,
org.libreccm.tests.categories.IntegrationTest
</groups>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</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

@ -65,20 +65,38 @@ public abstract class AbstractAuditedEntityRepository<K, T>
/**
* Retrieves the number of revisions for a given entity.
*
* @param entity The entity
* @param entity The entity
* @param objectId The primary key
*
* @return A list of revision numbers, at which the entity was modified,
* sorted in ascending order (so older revisions come first).
*
* @throws NotAuditedException When entities of the given class are not
* audited.
* @throws NotAuditedException When entities of the given class are not
* audited.
* @throws IllegalArgumentException If cls or primaryKey is null.
* @throws IllegalStateException If the associated entity manager is closed.
* @throws IllegalStateException If the associated entity manager is
* closed.
*/
public List<Number> retrieveRevisionNumbersOfEntity(final T entity,
final Long 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
@Table(name = "CATEGORIZATIONS", schema = DB_SCHEMA)
@NamedQueries({
@NamedQuery(name = "Categorization.find",
query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.categorizedObject = :object")
@NamedQuery(
name = "Categorization.find",
query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.categorizedObject = :object"),
@NamedQuery(
name = "Categorization.findIndexObject",
query = "SELECT c.categorizedObject FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.index = TRUE"),
@NamedQuery(
name = "Categorization.hasIndexObject",
query = "SELECT (CASE WHEN COUNT(c.categorizedObject) > 0 THEN true "
+ "ELSE false END) "
+ "FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.index = TRUE")
})
public class Categorization implements Serializable {

View File

@ -25,11 +25,15 @@ import org.apache.logging.log4j.Logger;
import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.security.Shiro;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@ -65,6 +69,9 @@ public class CategoryManager {
@Inject
private Shiro shiro;
@Inject
private PermissionChecker permissionChecker;
/**
* Assigns an category to an object.
*
@ -89,6 +96,35 @@ public class CategoryManager {
@RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE)
final Category category) {
addObjectToCategory(object, category, null);
}
/**
* Assigns an category to an object.
*
* Please note: Because the association between {@link Category} and {@code
* CcmObject} is a many-to-many association we use an association object to
* store the additional attributes of the association. The
* {@link Categorization} entity is completely managed by this class.
*
* If either {@code object} or the {@code category} parameter are
* {@code null} an {@link IllegalArgumentException} is thrown because
* passing {@code null} to this method indicates a programming error.
*
* @param object The object to assign to the category. Can never be
* {@code null}.
* @param category The category to which the object should be assigned. Can
* never be {@code null}.
* @param type Type of the categorisation.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void addObjectToCategory(
final CcmObject object,
@RequiresPrivilege(MANAGE_CATEGORY_OBJECTS_PRIVILEGE)
final Category category,
final String type) {
if (object == null) {
throw new IllegalArgumentException(
"Null can't be added to a category.");
@ -104,11 +140,12 @@ public class CategoryManager {
categorization.setCategory(category);
categorization.setCategoryOrder(object.getCategories().size() + 1);
categorization.setObjectOrder(category.getObjects().size() + 1);
categorization.setType(type);
object.addCategory(categorization);
category.addObject(categorization);
// To saving a category requires the manage_category privilege which
// Saving a category requires the manage_category privilege which
// may has not been granted to a user which is allowed to assign objects
// to a category. Therefore we bypass the this authorisation check here
// by executing CategoryRepository#save(Category) as the system user.
@ -595,4 +632,65 @@ public class CategoryManager {
});
}
/**
* Returns the path of a category as string. The path of a category are the
* names of all its parent categories and the category joined together,
* separated by a slash.
*
* @param category The category whose path is generated.
*
* @return The path of the category.
*/
public String getCategoryPath(final Category category) {
final List<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;
} else if (object instanceof InheritsPermissions) {
if (((InheritsPermissions) object).getParent().isPresent()) {
return result;
} else {
return isPermitted(
privilege,
((InheritsPermissions) object).getParent().get());
} else {
return result;
}
} else {
return result;
@ -141,17 +141,15 @@ public class PermissionChecker {
if (!result) {
if (((InheritsPermissions) object).getParent().isPresent()) {
if (subject.isAuthenticated()) {
subject.checkPermission(generatePermissionString(
privilege, object));
} else {
shiro.getPublicUser().checkPermission(
generatePermissionString(privilege, object));
}
} else {
checkPermission(
privilege,
((InheritsPermissions) object).getParent().get());
} else if (subject.isAuthenticated()) {
subject.checkPermission(generatePermissionString(
privilege, object));
} else {
shiro.getPublicUser().checkPermission(
generatePermissionString(privilege, object));
}
}
} else if (subject.isAuthenticated()) {

View File

@ -47,7 +47,7 @@ import org.libreccm.security.Shiro;
import org.libreccm.tests.categories.IntegrationTest;
import java.io.File;
import java.util.concurrent.Callable;
import java.util.Optional;
import javax.inject.Inject;
import javax.persistence.EntityManager;
@ -582,5 +582,34 @@ public class CategoryManagerTest {
categoryManager.addSubCategoryToCategory(test, categories);
});
}
@Test
@UsingDataSet(
"datasets/org/libreccm/categorization/CategoryManagerTest/data.yml")
@InSequence(6000)
public void hasIndexObject() {
final Category category1 = categoryRepo.findById(-2100L);
final Category category2 = categoryRepo.findById(-2200L);
assertThat(categoryManager.hasIndexObject(category1), is(false));
assertThat(categoryManager.hasIndexObject(category2), is(true));
}
@Test
@UsingDataSet(
"datasets/org/libreccm/categorization/CategoryManagerTest/data.yml")
@InSequence(6500)
public void getIndexObject() {
final Category category1 = categoryRepo.findById(-2100L);
final Category category2 = categoryRepo.findById(-2200L);
assertThat(categoryManager.getIndexObject(category1).isPresent(),
is(false));
final Optional<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_INDEX boolean,
OBJECT_ORDER bigint,
TYPE varchar(255),
OBJECT_ID bigint,
CATEGORY_ID bigint,
primary key (CATEGORIZATION_ID)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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