CCM NG/ccm-cms: Sorting for the FolderBrowser.

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4588 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2017-02-22 08:41:06 +00:00
parent 7274fff101
commit d4f2daa78a
15 changed files with 503 additions and 262 deletions

View File

@ -58,6 +58,7 @@ import org.librecms.contentsection.ContentSectionManager;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.dispatcher.ItemResolver; import org.librecms.dispatcher.ItemResolver;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -126,13 +127,16 @@ public class FolderBrowser extends Table {
nameColumn = getColumn(0); nameColumn = getColumn(0);
nameColumn.setCellRenderer(new NameCellRenderer()); nameColumn.setCellRenderer(new NameCellRenderer());
// nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME)); nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME));
getColumn(1).setCellRenderer(new LanguagesCellRenderer()); getColumn(1).setCellRenderer(new LanguagesCellRenderer());
// getColumn(2).setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_TITLE)); getColumn(4).setHeaderRenderer(new HeaderCellRenderer(
// getColumn(5).setHeaderRenderer(new HeaderCellRenderer( SORT_KEY_CREATION_DATE));
// SORT_KEY_CREATION_DATE));
// getColumn(6).setHeaderRenderer(new HeaderCellRenderer( getColumn(4).setCellRenderer(new DateCellRenderer());
// SORT_KEY_LAST_MODIFIED_DATE)); getColumn(5).setCellRenderer(new DateCellRenderer());
getColumn(5).setHeaderRenderer(new HeaderCellRenderer(
SORT_KEY_LAST_MODIFIED_DATE));
deleteColumn = getColumn(6); deleteColumn = getColumn(6);
deleteColumn.setCellRenderer(new ActionCellRenderer()); deleteColumn.setCellRenderer(new ActionCellRenderer());
deleteColumn.setAlign("center"); deleteColumn.setAlign("center");
@ -239,6 +243,14 @@ public class FolderBrowser extends Table {
return (String) state.getValue(atozFilterParameter); return (String) state.getValue(atozFilterParameter);
} }
protected String getSortType(final PageState state) {
return (String) state.getValue(sortTypeParameter);
}
protected String getSortDirection(final PageState state) {
return (String) state.getValue(sortDirectionParameter);
}
private class HeaderCellRenderer private class HeaderCellRenderer
extends com.arsdigita.cms.ui.util.DefaultTableCellRenderer { extends com.arsdigita.cms.ui.util.DefaultTableCellRenderer {
@ -273,7 +285,7 @@ public class FolderBrowser extends Table {
final ControlLink link = new ControlLink(new Label(headerName)) { final ControlLink link = new ControlLink(new Label(headerName)) {
@Override @Override
public void setControlEvent(PageState ps) { public void setControlEvent(final PageState state) {
String sortDirectionAction; String sortDirectionAction;
// by default, everything sorts "up" unless it // by default, everything sorts "up" unless it
// is the current key and it is already pointing up // is the current key and it is already pointing up
@ -283,9 +295,9 @@ public class FolderBrowser extends Table {
} else { } else {
sortDirectionAction = SORT_ACTION_UP; sortDirectionAction = SORT_ACTION_UP;
} }
ps.setControlEvent(table, state.setControlEvent(table,
sortDirectionAction, sortDirectionAction,
headerKey); headerKey);
} }
}; };
@ -431,6 +443,28 @@ public class FolderBrowser extends Table {
} }
private class DateCellRenderer implements TableCellRenderer {
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
if (value instanceof Date) {
final Date date = (Date) value;
return new Text(String.format("%1$TF %1$TT", date));
} else if (value == null) {
return new Text("");
} else {
return new Text(value.toString());
}
}
}
/** /**
* Produce delete links for items and non-empty folders. * Produce delete links for items and non-empty folders.
*/ */

View File

@ -20,14 +20,11 @@ package com.arsdigita.cms.ui.folder;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.Categorization; import org.libreccm.categorization.Categorization;
import org.libreccm.configuration.ConfigurationManager; import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository; import org.libreccm.core.CcmObjectRepository;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString;
import org.librecms.CmsConstants; import org.librecms.CmsConstants;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemL10NManager; import org.librecms.contentsection.ContentItemL10NManager;
@ -55,24 +52,25 @@ import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join; import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import org.hibernate.envers.AuditReader;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
/** /**
* The {@code FolderBrowserController} wraps all database operations (queries)
* required by the {@link FolderBrowser}, the
* {@link FolderBrowserTableModelBuilder} and the
* {@link FolderBrowserTableModel}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
public class FolderBrowserController { public class FolderBrowserController {
private static final Logger LOGGER = LogManager.getLogger(
FolderBrowserController.class);
@Inject @Inject
private EntityManager entityManager; private EntityManager entityManager;
@Inject
private AuditReader auditReader;
@Inject @Inject
private CcmObjectRepository objectRepo; private CcmObjectRepository objectRepo;
@ -90,189 +88,296 @@ public class FolderBrowserController {
private Locale defaultLocale; private Locale defaultLocale;
/**
* Initialisation method called by the CDI-Container after an instance of
* this class has be created by the container. Sets the
* {@link #defaultLocale} property using the the value from the
* {@link KernelConfig}.
*/
@PostConstruct @PostConstruct
private void init() { private void init() {
final KernelConfig kernelConfig = confManager.findConfiguration( final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class); KernelConfig.class);
defaultLocale = kernelConfig.getDefaultLocale(); defaultLocale = kernelConfig.getDefaultLocale();
} }
public List<CcmObject> findObjects(final Folder folder, final String orderBy) { /**
return findObjects(folder, orderBy, -1, -1); * Count the objects (subfolders and content items) in the provided folder.
} *
* @param folder The folder.
public List<CcmObject> findObjects(final Folder folder, *
final String orderBy, * @return The number of objects (subfolders and content items) in the
final int first, * provided {@code folder}.
final int maxResults) { */
return findObjects(folder, "%", orderBy, first, maxResults);
}
public List<CcmObject> findObjects(final Folder folder,
final String filterTerm,
final String orderBy) {
return findObjects(folder, filterTerm, orderBy, -1, -1);
}
public List<CcmObject> findObjects(final Folder folder,
final String filterTerm,
final String orderBy,
final int first,
final int maxResults) {
Objects.requireNonNull(folder);
LOGGER.debug("Trying to find objects in folder {}...",
Objects.toString(folder));
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<CcmObject> criteriaQuery = builder
.createQuery(CcmObject.class);
final Root<CcmObject> from = criteriaQuery.from(CcmObject.class);
return entityManager.createQuery(
criteriaQuery
.select(from)
.where(builder.or(
from.in(findSubFolders(folder, filterTerm)),
from.in(findItemsInFolder(folder, filterTerm))))
.orderBy(builder.asc(from.get("displayName"))))
.setFirstResult(first)
.setMaxResults(maxResults)
.getResultList();
}
public long countObjects(final Folder folder) { public long countObjects(final Folder folder) {
return countObjects(folder, "%"); return countObjects(folder, "%");
} }
/**
* Count all objects (subfolders and content items) in the provided folder
* which match the provided filter term.
*
* @param folder The folder.
* @param filterTerm The filter term.
*
* @return The number of objects (subfolders and content items) in the
* provided {@code folder} which match the provided
* {@code filterTerm}.
*/
public long countObjects(final Folder folder, public long countObjects(final Folder folder,
final String filterTerm) { final String filterTerm) {
Objects.requireNonNull(folder, "Can't count objects in Folder null.");
Objects.requireNonNull(filterTerm, "Can't filter for 'null'.");
final CriteriaBuilder builder = entityManager.getCriteriaBuilder(); final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Long> criteriaQuery = builder.createQuery( CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
Long.class);
final Root<CcmObject> from = criteriaQuery.from(CcmObject.class); final Root<CcmObject> from = criteriaQuery.from(CcmObject.class);
return entityManager criteriaQuery = criteriaQuery.select(builder.count(from));
.createQuery(
criteriaQuery final List<Folder> subFolders = findSubFolders(folder,
.select(builder.count(from)) filterTerm,
.where(builder.or( FolderBrowser.SORT_KEY_NAME,
from.in(findSubFolders(folder, FolderBrowser.SORT_ACTION_UP,
filterTerm)), -1,
from.in(findItemsInFolder(folder, -1);
filterTerm))))). final List<ContentItem> items = findItemsInFolder(folder,
getSingleResult(); filterTerm,
} FolderBrowser.SORT_KEY_NAME,
FolderBrowser.SORT_ACTION_UP,
@Transactional(Transactional.TxType.REQUIRED) -1,
List<FolderBrowserTableRow> getObjectRows(final Folder folder, -1);
final String orderBy) { if (subFolders.isEmpty() && items.isEmpty()) {
final List<CcmObject> objects = findObjects(folder, orderBy); return 0;
} else if (subFolders.isEmpty() && !items.isEmpty()) {
return objects.stream() criteriaQuery = criteriaQuery.where(from.in(items));
.map(object -> buildRow(object)) } else if (!subFolders.isEmpty() && items.isEmpty()) {
.collect(Collectors.toList()); criteriaQuery = criteriaQuery.where(from.in(subFolders));
} } else {
criteriaQuery = criteriaQuery.where(builder.or(
@Transactional(Transactional.TxType.REQUIRED) from.in(subFolders),
List<FolderBrowserTableRow> getObjectRows(final Folder folder, from.in(items)));
final String filterTerm, }
final String orderBy) {
final List<CcmObject> objects = findObjects(folder, return entityManager.createQuery(criteriaQuery).getSingleResult();
filterTerm,
orderBy);
return objects.stream()
.map(object -> buildRow(object))
.collect(Collectors.toList());
} }
/**
* Create {@link FolderBrowserTableRow} objects for all objects in the
* provided folder.
*
* @param folder The folder which contains the objects.
* @param orderBy The field used to order the objects.
* @param orderDirection The direction for ordering the objects.
*
* @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} ordered by the provided field and
* in the provided direction.
*/
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
final String orderBy, final String orderBy,
final int first, final String orderDirection) {
final int maxResults) { return getObjectRows(folder, "%", orderBy, orderDirection, -1, -1);
final List<CcmObject> objects = findObjects(folder,
orderBy,
first,
maxResults);
return objects.stream()
.map(object -> buildRow(object))
.collect(Collectors.toList());
} }
/**
* Create {@link FolderBrowserTableRow} objects for all objects in the
* provided folder which match provided filter term.
*
* @param folder The folder which contains the objects.
* @param filterTerm The filter term.
* @param orderBy The field used to order the objects.
* @param orderDirection The direction for ordering the objects.
*
* @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} which matches the provided
* {@code filterTerm}, ordered by the provided field and in the
* provided direction.
*/
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
final String filterTerm, final String filterTerm,
final String orderBy, final String orderBy,
final int first, final String orderDirection) {
final int maxResults) { return getObjectRows(folder,
final List<CcmObject> objects = findObjects(folder, filterTerm,
filterTerm, orderBy,
orderBy, orderDirection,
first, -1,
maxResults); -1);
return objects.stream()
.map(object -> buildRow(object))
.collect(Collectors.toList());
} }
private FolderBrowserTableRow buildRow(final CcmObject object) { /**
* Create {@link FolderBrowserTableRow} objects for the objects in the
* provided folder which are in the range provided by {@code firstResult}
* and {@code maxResult}
*
* @param folder The folder which contains the objects.
* @param orderBy The field used to order the objects.
* @param orderDirection The direction for ordering the objects.
* @param firstResult The index of the first object to use.
* @param maxResults The maximum number of objects to retrieve.
*
* @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} ordered by the provided field and
* in the provided direction. The list will start with the object
* with index provided as {@code firstResult} and contain at most
* {@code maxResults} items.
*/
@Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder,
final String orderBy,
final String orderDirection,
final int firstResult,
final int maxResults) {
return getObjectRows(folder,
"%",
orderBy,
orderDirection,
firstResult,
maxResults);
}
/**
* Create {@link FolderBrowserTableRow} objects for the objects in the
* provided folder which match the provided filter term and which are in the
* range provided by {@code firstResult} and {@code maxResult}
*
* @param folder The folder which contains the objects.
* @param filterTerm The filter term.
* @param orderBy The field used to order the objects.
* @param orderDirection The direction for ordering the objects.
* @param firstResult The index of the first object to use.
* @param maxResults The maximum number of objects to retrieve.
*
* @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} which matches the provided
* {@code filterTerm}, ordered by the provided field and in the
* provided direction. The list will start with the object with
* index provided as {@code firstResult} and contain at most
* {@code maxResults} items.
*/
@Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder,
final String filterTerm,
final String orderBy,
final String orderDirection,
final int firstResult,
final int maxResults) {
final List<Folder> subFolders = findSubFolders(folder,
filterTerm,
orderBy,
orderDirection,
firstResult,
maxResults);
final List<FolderBrowserTableRow> subFolderRows = subFolders.stream()
.map(subFolder -> buildRow(subFolder))
.collect(Collectors.toList());
if (subFolders.size() > maxResults) {
return subFolderRows;
} else {
final int maxItems = maxResults - subFolders.size();
final int firstItem = firstResult - subFolders.size();
final List<ContentItem> items = findItemsInFolder(folder,
filterTerm,
orderBy,
orderDirection,
firstItem,
maxItems);
final List<FolderBrowserTableRow> itemRows = items.stream()
.map(item -> buildRow(item))
.collect(Collectors.toList());
final ArrayList<FolderBrowserTableRow> rows = new ArrayList<>();
rows.addAll(subFolderRows);
rows.addAll(itemRows);
return rows;
}
}
/**
* Helper method for building a {@link FolderBrowserTableRow} from a
* {@link Folder}.
*
* @param folder The {@link Folder} to use for building the
* {@link FolderBrowserTableRow}.
*
* @return A {@link FolderBrowserTableRow} containing the data needed by the
* {@link FolderBrowser} to display the provided {@code folder}.
*/
private FolderBrowserTableRow buildRow(final Folder folder) {
final FolderBrowserTableRow row = new FolderBrowserTableRow(); final FolderBrowserTableRow row = new FolderBrowserTableRow();
if (object instanceof Folder) { row.setObjectId(folder.getObjectId());
final Folder folder = (Folder) object; row.setObjectUuid(folder.getUuid());
row.setObjectId(folder.getObjectId()); row.setName(folder.getName());
row.setObjectUuid(folder.getUuid()); row.setLanguages(Collections.emptyList());
row.setName(folder.getName()); if (folder.getTitle().hasValue(globalizationHelper
row.setLanguages(Collections.emptyList()); .getNegotiatedLocale())) {
if (folder.getTitle().hasValue(globalizationHelper row.setTitle(folder.getTitle().getValue(globalizationHelper
.getNegotiatedLocale())) { .getNegotiatedLocale()));
row.setTitle(folder.getTitle().getValue(globalizationHelper
.getNegotiatedLocale()));
} else {
row.setTitle(folder.getTitle().getValue(defaultLocale));
}
row.setFolder(true);
} else if (object instanceof ContentItem) {
final ContentItem item = (ContentItem) object;
row.setObjectId(item.getObjectId());
row.setObjectUuid(item.getItemUuid());
row.setName(item.getName().getValue(defaultLocale));
final List<Locale> languages = new ArrayList<>(itemL10NManager
.availableLanguages(item));
languages.sort((lang1, lang2) -> lang1.toString().compareTo(
lang2.toString()));
row.setLanguages(languages);
if (item.getTitle().hasValue(globalizationHelper
.getNegotiatedLocale())) {
row.setTitle(item.getTitle().getValue(globalizationHelper
.getNegotiatedLocale()));
} else {
row.setTitle(item.getTitle().getValue(defaultLocale));
}
final ContentType type = item.getContentType();
final ContentTypeInfo typeInfo = typesManager.getContentTypeInfo(
type);
row.setTypeLabelBundle(typeInfo.getLabelBundle());
row.setTypeLabelKey(typeInfo.getLabelKey());
row.setFolder(false);
} else { } else {
row.setObjectId(object.getObjectId()); row.setTitle(folder.getTitle().getValue(defaultLocale));
row.setObjectUuid(object.getUuid());
row.setName("???");
row.setLanguages(Collections.emptyList());
final LocalizedString title = new LocalizedString();
title.addValue(globalizationHelper.getNegotiatedLocale(), "???");
row.setFolder(false);
} }
row.setFolder(true);
return row; return row;
} }
/**
* Helper method for building a {@link FolderBrowserTableRow} from a
* {@link ContentItem}.
*
* @param item The {@link ContentItem} to use for building the
* {@link FolderBrowserTableRow}.
*
* @return A {@link FolderBrowserTableRow} containing the data needed by the
* {@link FolderBrowser} to display the provided {@code item}.
*/
private FolderBrowserTableRow buildRow(final ContentItem item) {
final FolderBrowserTableRow row = new FolderBrowserTableRow();
row.setObjectId(item.getObjectId());
row.setObjectUuid(item.getItemUuid());
row.setName(item.getName().getValue(defaultLocale));
final List<Locale> languages = new ArrayList<>(itemL10NManager
.availableLanguages(item));
languages.sort((lang1, lang2) -> lang1.toString().compareTo(
lang2.toString()));
row.setLanguages(languages);
if (item.getTitle().hasValue(globalizationHelper
.getNegotiatedLocale())) {
row.setTitle(item.getTitle().getValue(globalizationHelper
.getNegotiatedLocale()));
} else {
row.setTitle(item.getTitle().getValue(defaultLocale));
}
final ContentType type = item.getContentType();
final ContentTypeInfo typeInfo = typesManager.getContentTypeInfo(
type);
row.setTypeLabelBundle(typeInfo.getLabelBundle());
row.setTypeLabelKey(typeInfo.getLabelKey());
row.setCreated(item.getCreationDate());
row.setLastModified(item.getLastModified());
row.setFolder(false);
return row;
}
/**
* Called by the {@link FolderBrowser} to delete an object.
*
* @param objectId
*/
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
protected void deleteObject(final long objectId) { protected void deleteObject(final long objectId) {
final Optional<CcmObject> object = objectRepo.findById(objectId); final Optional<CcmObject> object = objectRepo.findById(objectId);
@ -283,55 +388,150 @@ public class FolderBrowserController {
} }
/** /**
* Creates a Criteria Query * Retrieves all subfolders of a folder matching the provided filter term.
* Because {@link Folder} does not have any of the fields despite the name
* which can be used to order the objects ordering is done as follows:
* *
* @param folder * If {@code orderBy} has any value other than
* @param filterTerm * {@link FolderBrowser#SORT_KEY_NAME} the subfolders are ordered in
* ascending order by their name. If {@code orderBy} is
* {@link FolderBrowser#SORT_KEY_NAME} the subfolders are ordered by their
* name in ascending and descending order depending on the value of
* {@code orderDirection}.
* *
* @return * @param folder The folder which contains the subfolders.
* @param filterTerm The filter term.
* @param orderBy Field to use for ordering. If the value is negative
* the parameter is ignored.
* @param orderDirection Direction for ordering. If the value is negative
* the parameter is ignored.
* @param firstResult Index of the first result to retrieve.
* @param maxResults Maxium number of results to retrieve.
*
*
* @return A list of the subfolders of the provided {@code folder} which
* match the provided {@code filterTerm}. The list is ordered as
* described above. The list will contain at most {@code maxResults}
* starting with the result with the index provided as
* {@code firstResult}.
*/ */
private List<Folder> findSubFolders(final Folder folder, private List<Folder> findSubFolders(final Folder folder,
final String filterTerm) { final String filterTerm,
final String orderBy,
final String orderDirection,
final int firstResult,
final int maxResults) {
final CriteriaBuilder builder = entityManager final CriteriaBuilder builder = entityManager
.getCriteriaBuilder(); .getCriteriaBuilder();
final CriteriaQuery<Folder> query = builder.createQuery( final CriteriaQuery<Folder> criteria = builder.createQuery(
Folder.class); Folder.class);
final Root<Folder> from = query.from(Folder.class); final Root<Folder> from = criteria.from(Folder.class);
return entityManager.createQuery( final Order order;
query.where(builder.and( if (FolderBrowser.SORT_KEY_NAME.equals(orderBy)
builder.equal(from.get("parentCategory"), folder), && FolderBrowser.SORT_ACTION_DOWN.equals(orderDirection)) {
builder. order = builder.desc(from.get("name"));
like(builder.lower(from.get("name")), filterTerm)))). } else {
getResultList(); order = builder.asc(from.get("name"));
}
final TypedQuery<Folder> query = entityManager.createQuery(
criteria.where(builder.and(
builder.equal(from.get("parentCategory"), folder),
builder
.like(builder.lower(from.get("name")), filterTerm)))
.orderBy(order));
if (firstResult >= 0) {
query.setFirstResult(firstResult);
}
if (maxResults >= 0) {
query.setMaxResults(maxResults);
}
return query.getResultList();
} }
private List<ContentItem> findItemsInFolder( /**
final Folder folder, * Retrieves all items of a folder matching the provided filter term.
final String filterTerm) { *
* @param folder The folder which contains the subfolders.
* @param filterTerm The filter term.
* @param orderBy Field to use for ordering. If the value is negative
* the parameter is ignored.
* @param orderDirection Direction for ordering. If the value is negative
* the parameter is ignored.
* @param firstResult Index of the first result to retrieve.
* @param maxResults Maxium number of results to retrieve.
*
*
* @return A list of the subfolders of the provided {@code folder} which
* match the provided {@code filterTerm}. The list is ordered the
* field provided as {@code orderBy} in the direction provided by
* {@code orderDirection}. The list will contain at most
* {@code maxResults} starting with the result with the index
* provided as {@code firstResult}.
*/
private List<ContentItem> findItemsInFolder(final Folder folder,
final String filterTerm,
final String orderBy,
final String orderDirection,
final int firstResult,
final int maxResults) {
final CriteriaBuilder builder = entityManager final CriteriaBuilder builder = entityManager
.getCriteriaBuilder(); .getCriteriaBuilder();
final CriteriaQuery<ContentItem> query = builder.createQuery( final CriteriaQuery<ContentItem> criteria = builder.createQuery(
ContentItem.class); ContentItem.class);
final Root<ContentItem> fromItem = query.from(ContentItem.class); final Root<ContentItem> fromItem = criteria.from(ContentItem.class);
final Join<ContentItem, Categorization> join = fromItem.join( final Join<ContentItem, Categorization> join = fromItem.join(
"categories"); "categories");
return entityManager.createQuery(query final Path<?> orderPath;
.select(fromItem) switch (orderBy) {
.where(builder.and( case FolderBrowser.SORT_KEY_NAME:
builder.equal(join.get("category"), folder), orderPath = fromItem.get("displayName");
builder.equal(join.get("type"), break;
CmsConstants.CATEGORIZATION_TYPE_FOLDER), case FolderBrowser.SORT_KEY_CREATION_DATE:
builder.equal(fromItem.get("version"), orderPath = fromItem.get("creationDate");
ContentItemVersion.DRAFT), break;
builder.like(fromItem.get("displayName"), case FolderBrowser.SORT_KEY_LAST_MODIFIED_DATE:
filterTerm)))) orderPath = fromItem.get("lastModified");
.getResultList(); break;
default:
orderPath = fromItem.get("displayName");
break;
}
final Order order;
if (FolderBrowser.SORT_ACTION_DOWN.equals(orderDirection)) {
order = builder.desc(orderPath);
} else {
order = builder.asc(orderPath);
}
final TypedQuery<ContentItem> query = entityManager.createQuery(criteria
.select(fromItem)
.where(builder.and(
builder.equal(join.get("category"), folder),
builder.equal(join.get("type"),
CmsConstants.CATEGORIZATION_TYPE_FOLDER),
builder.equal(fromItem.get("version"),
ContentItemVersion.DRAFT),
builder.like(fromItem.get("displayName"),
filterTerm)))
.orderBy(order));
if (firstResult >= 0) {
query.setFirstResult(firstResult);
}
if (maxResults >= 0) {
query.setMaxResults(maxResults);
}
return query.getResultList();
} }
} }

View File

@ -26,7 +26,6 @@ import org.librecms.CmsConstants;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import javax.ws.rs.DELETE;
/** /**
* Table model for the {@link FolderBrowser}. * Table model for the {@link FolderBrowser}.

View File

@ -33,7 +33,8 @@ import org.librecms.contentsection.Folder;
import java.util.List; import java.util.List;
/** /**
* * Creates the {@link TableModel} for the {@link FolderBrowser}.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
class FolderBrowserTableModelBuilder extends LockableImpl class FolderBrowserTableModelBuilder extends LockableImpl
@ -62,15 +63,27 @@ class FolderBrowserTableModelBuilder extends LockableImpl
final FolderBrowserController controller = cdiUtil.findBean( final FolderBrowserController controller = cdiUtil.findBean(
FolderBrowserController.class); FolderBrowserController.class);
final String filter = folderBrowser.getFilter(state); final String filter = folderBrowser.getFilter(state);
final String orderBy;
if (folderBrowser.getSortType(state) == null) {
orderBy = FolderBrowser.SORT_KEY_NAME;
} else {
orderBy = folderBrowser.getSortType(state);
}
final String orderDirection;
if (folderBrowser.getSortDirection(state) == null) {
orderDirection = FolderBrowser.SORT_ACTION_UP;
} else {
orderDirection = folderBrowser.getSortDirection(state);
}
final String atozFilter = folderBrowser.getAtoZfilter(state); final String atozFilter = folderBrowser.getAtoZfilter(state);
final int first = paginator.getFirst(state); final int first = paginator.getFirst(state);
final int pageSize = paginator.getPageSize(state); final int pageSize = paginator.getPageSize(state);
final String filterTerm; final String filterTerm;
if (filter != null && !filter.trim().isEmpty()) { if (filter != null && !filter.trim().isEmpty()) {
filterTerm = filter.trim(); filterTerm = String.format("%s%%", filter.trim());
} else if (atozFilter != null && !atozFilter.trim().isEmpty()) { } else if (atozFilter != null && !atozFilter.trim().isEmpty()) {
filterTerm = atozFilter.trim(); filterTerm = String.format("%s%%", atozFilter.trim());
} else { } else {
filterTerm = null; filterTerm = null;
} }
@ -80,13 +93,15 @@ class FolderBrowserTableModelBuilder extends LockableImpl
final List<FolderBrowserTableRow> rows; final List<FolderBrowserTableRow> rows;
if (filterTerm == null) { if (filterTerm == null) {
rows = controller.getObjectRows(folder, rows = controller.getObjectRows(folder,
"name", orderBy,
orderDirection,
first - 1, first - 1,
pageSize); pageSize);
} else { } else {
rows = controller.getObjectRows(folder, rows = controller.getObjectRows(folder,
filter, filterTerm,
"name", orderBy,
orderDirection,
first - 1, first - 1,
pageSize); pageSize);
} }

View File

@ -18,13 +18,16 @@
*/ */
package com.arsdigita.cms.ui.folder; package com.arsdigita.cms.ui.folder;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
* A simple data transfer object containing the data required by the
* {@link FolderBrowser} to display its rows. Used by the
* {@link FolderBrowserTableModelBuilder} to transfer the data from the
* {@link FolderBrowserController} to the {@link FolderBrowserTableModel}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -85,11 +88,11 @@ class FolderBrowserTableRow {
public String getTypeLabelBundle() { public String getTypeLabelBundle() {
return typeLabelBundle; return typeLabelBundle;
} }
protected void setTypeLabelBundle(final String typeLabelBundle) { protected void setTypeLabelBundle(final String typeLabelBundle) {
this.typeLabelBundle = typeLabelBundle; this.typeLabelBundle = typeLabelBundle;
} }
public String getTypeLabelKey() { public String getTypeLabelKey() {
return typeLabelKey; return typeLabelKey;
} }
@ -137,11 +140,11 @@ class FolderBrowserTableRow {
protected void setDeletable(final boolean deletable) { protected void setDeletable(final boolean deletable) {
this.deletable = deletable; this.deletable = deletable;
} }
public boolean isFolder() { public boolean isFolder() {
return folder; return folder;
} }
protected void setFolder(final boolean folder) { protected void setFolder(final boolean folder) {
this.folder = folder; this.folder = folder;
} }

View File

@ -311,7 +311,7 @@ public class ContentItem extends CcmObject implements Serializable {
* *
* Please note that there is no grantee that the user still exists. * Please note that there is no grantee that the user still exists.
*/ */
@Column(name = "LAST_MODIFING_USER_NAME") @Column(name = "LAST_MODIFYING_USER_NAME")
@NotAudited @NotAudited
private String lastModifyingUserName; private String lastModifyingUserName;

View File

@ -18,7 +18,10 @@
*/ */
package org.librecms.contentsection; package org.librecms.contentsection;
import org.apache.shiro.subject.Subject;
import java.util.Date; import java.util.Date;
import org.libreccm.auditing.AbstractAuditedEntityRepository; import org.libreccm.auditing.AbstractAuditedEntityRepository;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObject;
@ -32,8 +35,8 @@ import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import org.libreccm.security.Shiro; import org.libreccm.security.Shiro;
import org.libreccm.security.User;
import org.libreccm.workflow.Workflow; import org.libreccm.workflow.Workflow;
/** /**
@ -50,7 +53,7 @@ public class ContentItemRepository
@Inject @Inject
private FolderRepository folderRepo; private FolderRepository folderRepo;
@Inject @Inject
private Shiro shiro; private Shiro shiro;
@ -352,26 +355,20 @@ public class ContentItemRepository
return Optional.empty(); return Optional.empty();
} }
} }
@Override @Override
public void save(final ContentItem item) { public void save(final ContentItem item) {
final Date now = new Date(); final Date now = new Date();
final Optional<User> user = shiro.getUser(); final Subject subject = shiro.getSubject();
final String userName; final String userName = subject.getPrincipal().toString();
if (user.isPresent()) {
userName = user.get().getName();
} else {
userName = "--unknown--";
}
if (isNew(item)) { if (isNew(item)) {
item.setCreationDate(now); item.setCreationDate(now);
item.setCreationUserName(userName); item.setCreationUserName(userName);
} else {
item.setLastModified(now);
item.setLastModifyingUserName(userName);
} }
item.setLastModified(now);
item.setLastModifyingUserName(userName);
super.save(item); super.save(item);
} }

View File

@ -8,5 +8,5 @@ alter table CCM_CMS.CONTENT_ITEMS
add column LAST_MODIFIED timestamp; add column LAST_MODIFIED timestamp;
alter table CCM_CMS.CONTENT_ITEMS alter table CCM_CMS.CONTENT_ITEMS
add column LAST_MODIFING_USER_NAME varchar(255); add column LAST_MODIFYING_USER_NAME varchar(255);

View File

@ -8,5 +8,5 @@ alter table CCM_CMS.CONTENT_ITEMS
add column LAST_MODIFIED timestamp; add column LAST_MODIFIED timestamp;
alter table CCM_CMS.CONTENT_ITEMS alter table CCM_CMS.CONTENT_ITEMS
add column LAST_MODIFING_USER_NAME varchar(255); add column LAST_MODIFYING_USER_NAME varchar(255);

View File

@ -274,7 +274,7 @@ create schema CCM_CORE;
CREATION_USER_NAME varchar(255), CREATION_USER_NAME varchar(255),
ITEM_UUID varchar(255) not null, ITEM_UUID varchar(255) not null,
LAST_MODIFIED timestamp, LAST_MODIFIED timestamp,
LAST_MODIFING_USER_NAME varchar(255), LAST_MODIFYING_USER_NAME varchar(255),
LAUNCH_DATE date, LAUNCH_DATE date,
VERSION varchar(255), VERSION varchar(255),
OBJECT_ID bigint not null, OBJECT_ID bigint not null,

View File

@ -274,7 +274,7 @@ create schema CCM_CORE;
CREATION_USER_NAME varchar(255), CREATION_USER_NAME varchar(255),
ITEM_UUID varchar(255) not null, ITEM_UUID varchar(255) not null,
LAST_MODIFIED timestamp, LAST_MODIFIED timestamp,
LAST_MODIFING_USER_NAME varchar(255), LAST_MODIFYING_USER_NAME varchar(255),
LAUNCH_DATE date, LAUNCH_DATE date,
VERSION varchar(255), VERSION varchar(255),
OBJECT_ID int8 not null, OBJECT_ID int8 not null,

View File

@ -274,7 +274,7 @@ create schema CCM_CORE;
CREATION_USER_NAME varchar(255), CREATION_USER_NAME varchar(255),
ITEM_UUID varchar(255) not null, ITEM_UUID varchar(255) not null,
LAST_MODIFIED timestamp, LAST_MODIFIED timestamp,
LAST_MODIFING_USER_NAME varchar(255), LAST_MODIFYING_USER_NAME varchar(255),
LAUNCH_DATE date, LAUNCH_DATE date,
VERSION varchar(255), VERSION varchar(255),
OBJECT_ID bigint not null, OBJECT_ID bigint not null,

View File

@ -18,37 +18,35 @@
*/ */
package org.libreccm.auditing; package org.libreccm.auditing;
import java.util.Optional; import org.apache.shiro.subject.Subject;
import org.hibernate.envers.RevisionListener; import org.hibernate.envers.RevisionListener;
import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.security.Shiro; import org.libreccm.security.Shiro;
import org.libreccm.security.User;
/** /**
* {@link RevisionListener} setting the user for the {@link CcmRevision} entity. * {@link RevisionListener} setting the user for the {@link CcmRevision} entity.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
public class CcmRevisionListener implements RevisionListener { public class CcmRevisionListener implements RevisionListener {
@Override @Override
public void newRevision(final Object revisionEntity) { public void newRevision(final Object revisionEntity) {
if (!(revisionEntity instanceof CcmRevision)) { if (!(revisionEntity instanceof CcmRevision)) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"Provided revision entity is not an instance of class \"%s\".", "Provided revision entity is not an instance of class \"%s\".",
CcmRevision.class.getName())); CcmRevision.class.getName()));
} }
final CcmRevision revision = (CcmRevision) revisionEntity; final CcmRevision revision = (CcmRevision) revisionEntity;
final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final Shiro shiro = cdiUtil.findBean(Shiro.class); final Shiro shiro = cdiUtil.findBean(Shiro.class);
final Optional<User> user = shiro.getUser(); final Subject subject = shiro.getSubject();
if (user.isPresent()) { revision.setUserName(subject.getPrincipal().toString());
revision.setUserName(user.get().getName());
}
} }
} }

View File

@ -84,20 +84,8 @@ import javax.xml.bind.annotation.XmlRootElement;
name = "Category.withSubCategoriesAndObjects", name = "Category.withSubCategoriesAndObjects",
attributeNodes = { attributeNodes = {
@NamedAttributeNode(value = "subCategories" @NamedAttributeNode(value = "subCategories"
//, ),
// subgraph = "subCategories"
), //@NamedAttributeNode(value = "objects")
} }
// ,
// subgraphs = {
// @NamedSubgraph(
// name = "subCategories",
// attributeNodes = {
// @NamedAttributeNode("subCategories"),
// @NamedAttributeNode("objects")
// }
// )
// }
) )
}) })
@XmlRootElement(name = "category", namespace = CAT_XML_NS) @XmlRootElement(name = "category", namespace = CAT_XML_NS)

View File

@ -47,20 +47,27 @@ public class RoleMembershipMarshaller extends AbstractMarshaller<RoleMembership>
} }
@Override @Override
@Transactional(Transactional.TxType.REQUIRED) // @Transactional(Transactional.TxType.REQUIRED)
protected void insertIntoDb(RoleMembership portableObject) { protected void insertIntoDb(final RoleMembership portableObject) {
// if (portableObject.getMembershipId() == 0) { // if (portableObject.getMembershipId() == 0) {
portableObject.setMembershipId(0); portableObject.setMembershipId(0);
// portableObject.setMembershipId(portableObject.getMembershipId() * -1); // portableObject.setMembershipId(portableObject.getMembershipId() * -1);
entityManager.persist(portableObject); // entityManager.persist(portableObject);
// entityManager.merge(portableObject); // entityManager.merge(portableObject);
entityManager.flush(); // entityManager.flush();
final RoleMembership roleMembership = save(portableObject);
LOGGER.debug("Saved RoleMembership with id {}.", LOGGER.debug("Saved RoleMembership with id {}.",
portableObject.getMembershipId()); roleMembership.getMembershipId());
// } else { // } else {
// entityManager.merge(portableObject); // entityManager.merge(portableObject);
// } // }
} }
@Transactional(Transactional.TxType.REQUIRES_NEW)
protected RoleMembership save(final RoleMembership membership) {
entityManager.persist(membership);
return membership;
}
} }