From d4f2daa78a6814ea6ddc3b4e17daa9b2cee65eb1 Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 22 Feb 2017 08:41:06 +0000 Subject: [PATCH] CCM NG/ccm-cms: Sorting for the FolderBrowser. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4588 8810af33-2d31-482b-a856-94f89814c4df --- .../cms/ui/folder/FolderBrowser.java | 54 +- .../ui/folder/FolderBrowserController.java | 580 ++++++++++++------ .../ui/folder/FolderBrowserTableModel.java | 1 - .../FolderBrowserTableModelBuilder.java | 27 +- .../cms/ui/folder/FolderBrowserTableRow.java | 13 +- .../librecms/contentsection/ContentItem.java | 2 +- .../contentsection/ContentItemRepository.java | 27 +- .../h2/V7_0_0_10__add_item_auditing_data.sql | 2 +- .../V7_0_0_10__add_item_auditing_data.sql | 2 +- .../scripts/create_ccm_cms_schema.sql | 2 +- .../scripts/create_ccm_cms_schema.sql | 2 +- .../datasets/create_ccm_cms_schema.sql | 2 +- .../auditing/CcmRevisionListener.java | 20 +- .../org/libreccm/categorization/Category.java | 14 +- .../security/RoleMembershipMarshaller.java | 17 +- 15 files changed, 503 insertions(+), 262 deletions(-) diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java index 95b1ed29c..718faddbb 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowser.java @@ -58,6 +58,7 @@ import org.librecms.contentsection.ContentSectionManager; import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.dispatcher.ItemResolver; +import java.util.Date; import java.util.List; import java.util.Locale; @@ -126,13 +127,16 @@ public class FolderBrowser extends Table { nameColumn = getColumn(0); nameColumn.setCellRenderer(new NameCellRenderer()); -// nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME)); + 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)); + getColumn(4).setHeaderRenderer(new HeaderCellRenderer( + SORT_KEY_CREATION_DATE)); + + getColumn(4).setCellRenderer(new DateCellRenderer()); + getColumn(5).setCellRenderer(new DateCellRenderer()); + getColumn(5).setHeaderRenderer(new HeaderCellRenderer( + SORT_KEY_LAST_MODIFIED_DATE)); + deleteColumn = getColumn(6); deleteColumn.setCellRenderer(new ActionCellRenderer()); deleteColumn.setAlign("center"); @@ -239,6 +243,14 @@ public class FolderBrowser extends Table { 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 extends com.arsdigita.cms.ui.util.DefaultTableCellRenderer { @@ -273,7 +285,7 @@ public class FolderBrowser extends Table { final ControlLink link = new ControlLink(new Label(headerName)) { @Override - public void setControlEvent(PageState ps) { + public void setControlEvent(final PageState state) { String sortDirectionAction; // by default, everything sorts "up" unless it // is the current key and it is already pointing up @@ -283,9 +295,9 @@ public class FolderBrowser extends Table { } else { sortDirectionAction = SORT_ACTION_UP; } - ps.setControlEvent(table, - sortDirectionAction, - headerKey); + state.setControlEvent(table, + sortDirectionAction, + 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. */ diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java index 79ebb687f..0ce23171a 100644 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java @@ -20,14 +20,11 @@ package com.arsdigita.cms.ui.folder; 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.configuration.ConfigurationManager; import org.libreccm.core.CcmObject; import org.libreccm.core.CcmObjectRepository; import org.libreccm.l10n.GlobalizationHelper; -import org.libreccm.l10n.LocalizedString; import org.librecms.CmsConstants; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemL10NManager; @@ -55,24 +52,25 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Root; 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 Jens Pelzetter */ @RequestScoped public class FolderBrowserController { - private static final Logger LOGGER = LogManager.getLogger( - FolderBrowserController.class); - @Inject private EntityManager entityManager; - @Inject - private AuditReader auditReader; - @Inject private CcmObjectRepository objectRepo; @@ -90,189 +88,296 @@ public class FolderBrowserController { 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 private void init() { final KernelConfig kernelConfig = confManager.findConfiguration( - KernelConfig.class); + KernelConfig.class); defaultLocale = kernelConfig.getDefaultLocale(); } - public List findObjects(final Folder folder, final String orderBy) { - return findObjects(folder, orderBy, -1, -1); - } - - public List findObjects(final Folder folder, - final String orderBy, - final int first, - final int maxResults) { - return findObjects(folder, "%", orderBy, first, maxResults); - } - - public List findObjects(final Folder folder, - final String filterTerm, - final String orderBy) { - return findObjects(folder, filterTerm, orderBy, -1, -1); - } - - public List 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 criteriaQuery = builder - .createQuery(CcmObject.class); - final Root 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(); - } - + /** + * Count the objects (subfolders and content items) in the provided folder. + * + * @param folder The folder. + * + * @return The number of objects (subfolders and content items) in the + * provided {@code folder}. + */ public long countObjects(final Folder 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, 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 CriteriaQuery criteriaQuery = builder.createQuery( - Long.class); + CriteriaQuery criteriaQuery = builder.createQuery(Long.class); final Root from = criteriaQuery.from(CcmObject.class); - return entityManager - .createQuery( - criteriaQuery - .select(builder.count(from)) - .where(builder.or( - from.in(findSubFolders(folder, - filterTerm)), - from.in(findItemsInFolder(folder, - filterTerm))))). - getSingleResult(); - } - - @Transactional(Transactional.TxType.REQUIRED) - List getObjectRows(final Folder folder, - final String orderBy) { - final List objects = findObjects(folder, orderBy); - - return objects.stream() - .map(object -> buildRow(object)) - .collect(Collectors.toList()); - } - - @Transactional(Transactional.TxType.REQUIRED) - List getObjectRows(final Folder folder, - final String filterTerm, - final String orderBy) { - final List objects = findObjects(folder, - filterTerm, - orderBy); - - return objects.stream() - .map(object -> buildRow(object)) - .collect(Collectors.toList()); + criteriaQuery = criteriaQuery.select(builder.count(from)); + + final List subFolders = findSubFolders(folder, + filterTerm, + FolderBrowser.SORT_KEY_NAME, + FolderBrowser.SORT_ACTION_UP, + -1, + -1); + final List items = findItemsInFolder(folder, + filterTerm, + FolderBrowser.SORT_KEY_NAME, + FolderBrowser.SORT_ACTION_UP, + -1, + -1); + if (subFolders.isEmpty() && items.isEmpty()) { + return 0; + } else if (subFolders.isEmpty() && !items.isEmpty()) { + criteriaQuery = criteriaQuery.where(from.in(items)); + } else if (!subFolders.isEmpty() && items.isEmpty()) { + criteriaQuery = criteriaQuery.where(from.in(subFolders)); + } else { + criteriaQuery = criteriaQuery.where(builder.or( + from.in(subFolders), + from.in(items))); + } + + return entityManager.createQuery(criteriaQuery).getSingleResult(); } + /** + * 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) List getObjectRows(final Folder folder, final String orderBy, - final int first, - final int maxResults) { - final List objects = findObjects(folder, - orderBy, - first, - maxResults); - - return objects.stream() - .map(object -> buildRow(object)) - .collect(Collectors.toList()); + final String orderDirection) { + return getObjectRows(folder, "%", orderBy, orderDirection, -1, -1); } + /** + * 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) List getObjectRows(final Folder folder, final String filterTerm, final String orderBy, - final int first, - final int maxResults) { - final List objects = findObjects(folder, - filterTerm, - orderBy, - first, - maxResults); - - return objects.stream() - .map(object -> buildRow(object)) - .collect(Collectors.toList()); + final String orderDirection) { + return getObjectRows(folder, + filterTerm, + orderBy, + orderDirection, + -1, + -1); } - 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 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 getObjectRows(final Folder folder, + final String filterTerm, + final String orderBy, + final String orderDirection, + final int firstResult, + final int maxResults) { + final List subFolders = findSubFolders(folder, + filterTerm, + orderBy, + orderDirection, + firstResult, + maxResults); + final List 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 items = findItemsInFolder(folder, + filterTerm, + orderBy, + orderDirection, + firstItem, + maxItems); + final List itemRows = items.stream() + .map(item -> buildRow(item)) + .collect(Collectors.toList()); + + final ArrayList 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(); - if (object instanceof Folder) { - final Folder folder = (Folder) object; - row.setObjectId(folder.getObjectId()); - row.setObjectUuid(folder.getUuid()); - row.setName(folder.getName()); - row.setLanguages(Collections.emptyList()); - if (folder.getTitle().hasValue(globalizationHelper - .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 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); + row.setObjectId(folder.getObjectId()); + row.setObjectUuid(folder.getUuid()); + row.setName(folder.getName()); + row.setLanguages(Collections.emptyList()); + if (folder.getTitle().hasValue(globalizationHelper + .getNegotiatedLocale())) { + row.setTitle(folder.getTitle().getValue(globalizationHelper + .getNegotiatedLocale())); } else { - row.setObjectId(object.getObjectId()); - row.setObjectUuid(object.getUuid()); - row.setName("???"); - row.setLanguages(Collections.emptyList()); - final LocalizedString title = new LocalizedString(); - title.addValue(globalizationHelper.getNegotiatedLocale(), "???"); - row.setFolder(false); + row.setTitle(folder.getTitle().getValue(defaultLocale)); } + row.setFolder(true); 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 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) protected void deleteObject(final long objectId) { final Optional 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 - * @param filterTerm + * If {@code orderBy} has any value other than + * {@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 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 - .getCriteriaBuilder(); + .getCriteriaBuilder(); - final CriteriaQuery query = builder.createQuery( - Folder.class); - final Root from = query.from(Folder.class); + final CriteriaQuery criteria = builder.createQuery( + Folder.class); + final Root from = criteria.from(Folder.class); - return entityManager.createQuery( - query.where(builder.and( - builder.equal(from.get("parentCategory"), folder), - builder. - like(builder.lower(from.get("name")), filterTerm)))). - getResultList(); + final Order order; + if (FolderBrowser.SORT_KEY_NAME.equals(orderBy) + && FolderBrowser.SORT_ACTION_DOWN.equals(orderDirection)) { + order = builder.desc(from.get("name")); + } else { + order = builder.asc(from.get("name")); + } + final TypedQuery 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 findItemsInFolder( - final Folder folder, - final String filterTerm) { + /** + * Retrieves all items of a folder matching the provided filter term. + * + * @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 findItemsInFolder(final Folder folder, + final String filterTerm, + final String orderBy, + final String orderDirection, + final int firstResult, + final int maxResults) { final CriteriaBuilder builder = entityManager - .getCriteriaBuilder(); + .getCriteriaBuilder(); - final CriteriaQuery query = builder.createQuery( - ContentItem.class); - final Root fromItem = query.from(ContentItem.class); + final CriteriaQuery criteria = builder.createQuery( + ContentItem.class); + final Root fromItem = criteria.from(ContentItem.class); final Join join = fromItem.join( - "categories"); + "categories"); - return entityManager.createQuery(query - .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)))) - .getResultList(); + final Path orderPath; + switch (orderBy) { + case FolderBrowser.SORT_KEY_NAME: + orderPath = fromItem.get("displayName"); + break; + case FolderBrowser.SORT_KEY_CREATION_DATE: + orderPath = fromItem.get("creationDate"); + break; + case FolderBrowser.SORT_KEY_LAST_MODIFIED_DATE: + orderPath = fromItem.get("lastModified"); + 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 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(); } } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModel.java index e6e1e57d1..3e3693ad1 100644 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModel.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModel.java @@ -26,7 +26,6 @@ import org.librecms.CmsConstants; import java.util.Iterator; import java.util.List; -import javax.ws.rs.DELETE; /** * Table model for the {@link FolderBrowser}. diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModelBuilder.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModelBuilder.java index 92976b3b4..742e3072f 100644 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModelBuilder.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableModelBuilder.java @@ -33,7 +33,8 @@ import org.librecms.contentsection.Folder; import java.util.List; /** - * + * Creates the {@link TableModel} for the {@link FolderBrowser}. + * * @author Jens Pelzetter */ class FolderBrowserTableModelBuilder extends LockableImpl @@ -62,15 +63,27 @@ class FolderBrowserTableModelBuilder extends LockableImpl final FolderBrowserController controller = cdiUtil.findBean( FolderBrowserController.class); 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 int first = paginator.getFirst(state); final int pageSize = paginator.getPageSize(state); final String filterTerm; if (filter != null && !filter.trim().isEmpty()) { - filterTerm = filter.trim(); + filterTerm = String.format("%s%%", filter.trim()); } else if (atozFilter != null && !atozFilter.trim().isEmpty()) { - filterTerm = atozFilter.trim(); + filterTerm = String.format("%s%%", atozFilter.trim()); } else { filterTerm = null; } @@ -80,13 +93,15 @@ class FolderBrowserTableModelBuilder extends LockableImpl final List rows; if (filterTerm == null) { rows = controller.getObjectRows(folder, - "name", + orderBy, + orderDirection, first - 1, pageSize); } else { rows = controller.getObjectRows(folder, - filter, - "name", + filterTerm, + orderBy, + orderDirection, first - 1, pageSize); } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableRow.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableRow.java index 3cc523812..bcf58b128 100644 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableRow.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserTableRow.java @@ -18,13 +18,16 @@ */ package com.arsdigita.cms.ui.folder; - import java.util.Collections; import java.util.Date; import java.util.List; 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 Jens Pelzetter */ @@ -85,11 +88,11 @@ class FolderBrowserTableRow { public String getTypeLabelBundle() { return typeLabelBundle; } - + protected void setTypeLabelBundle(final String typeLabelBundle) { this.typeLabelBundle = typeLabelBundle; } - + public String getTypeLabelKey() { return typeLabelKey; } @@ -137,11 +140,11 @@ class FolderBrowserTableRow { protected void setDeletable(final boolean deletable) { this.deletable = deletable; } - + public boolean isFolder() { return folder; } - + protected void setFolder(final boolean folder) { this.folder = folder; } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java index 4c73fe6f1..752502399 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItem.java @@ -311,7 +311,7 @@ public class ContentItem extends CcmObject implements Serializable { * * 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 private String lastModifyingUserName; diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java index 36da3116b..75b55817e 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java @@ -18,7 +18,10 @@ */ package org.librecms.contentsection; +import org.apache.shiro.subject.Subject; + import java.util.Date; + import org.libreccm.auditing.AbstractAuditedEntityRepository; import org.libreccm.categorization.Category; import org.libreccm.core.CcmObject; @@ -32,8 +35,8 @@ import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; + import org.libreccm.security.Shiro; -import org.libreccm.security.User; import org.libreccm.workflow.Workflow; /** @@ -50,7 +53,7 @@ public class ContentItemRepository @Inject private FolderRepository folderRepo; - + @Inject private Shiro shiro; @@ -352,26 +355,20 @@ public class ContentItemRepository return Optional.empty(); } } - + @Override public void save(final ContentItem item) { final Date now = new Date(); - final Optional user = shiro.getUser(); - final String userName; - if (user.isPresent()) { - userName = user.get().getName(); - } else { - userName = "--unknown--"; - } - + final Subject subject = shiro.getSubject(); + final String userName = subject.getPrincipal().toString(); + if (isNew(item)) { item.setCreationDate(now); item.setCreationUserName(userName); - } else { - item.setLastModified(now); - item.setLastModifyingUserName(userName); } - + item.setLastModified(now); + item.setLastModifyingUserName(userName); + super.save(item); } diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_10__add_item_auditing_data.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_10__add_item_auditing_data.sql index 9c0e5944d..a58f33a37 100644 --- a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_10__add_item_auditing_data.sql +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_10__add_item_auditing_data.sql @@ -8,5 +8,5 @@ alter table CCM_CMS.CONTENT_ITEMS add column LAST_MODIFIED timestamp; alter table CCM_CMS.CONTENT_ITEMS - add column LAST_MODIFING_USER_NAME varchar(255); + add column LAST_MODIFYING_USER_NAME varchar(255); diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_10__add_item_auditing_data.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_10__add_item_auditing_data.sql index 9c0e5944d..a58f33a37 100644 --- a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_10__add_item_auditing_data.sql +++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_10__add_item_auditing_data.sql @@ -8,5 +8,5 @@ alter table CCM_CMS.CONTENT_ITEMS add column LAST_MODIFIED timestamp; alter table CCM_CMS.CONTENT_ITEMS - add column LAST_MODIFING_USER_NAME varchar(255); + add column LAST_MODIFYING_USER_NAME varchar(255); diff --git a/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql b/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql index 35e1f953c..44734c60c 100644 --- a/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql +++ b/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql @@ -274,7 +274,7 @@ create schema CCM_CORE; CREATION_USER_NAME varchar(255), ITEM_UUID varchar(255) not null, LAST_MODIFIED timestamp, - LAST_MODIFING_USER_NAME varchar(255), + LAST_MODIFYING_USER_NAME varchar(255), LAUNCH_DATE date, VERSION varchar(255), OBJECT_ID bigint not null, diff --git a/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql b/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql index d617a3845..1749b92db 100644 --- a/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql +++ b/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql @@ -274,7 +274,7 @@ create schema CCM_CORE; CREATION_USER_NAME varchar(255), ITEM_UUID varchar(255) not null, LAST_MODIFIED timestamp, - LAST_MODIFING_USER_NAME varchar(255), + LAST_MODIFYING_USER_NAME varchar(255), LAUNCH_DATE date, VERSION varchar(255), OBJECT_ID int8 not null, diff --git a/ccm-cms/src/test/resources/datasets/create_ccm_cms_schema.sql b/ccm-cms/src/test/resources/datasets/create_ccm_cms_schema.sql index a4bccdde2..50b3900c5 100644 --- a/ccm-cms/src/test/resources/datasets/create_ccm_cms_schema.sql +++ b/ccm-cms/src/test/resources/datasets/create_ccm_cms_schema.sql @@ -274,7 +274,7 @@ create schema CCM_CORE; CREATION_USER_NAME varchar(255), ITEM_UUID varchar(255) not null, LAST_MODIFIED timestamp, - LAST_MODIFING_USER_NAME varchar(255), + LAST_MODIFYING_USER_NAME varchar(255), LAUNCH_DATE date, VERSION varchar(255), OBJECT_ID bigint not null, diff --git a/ccm-core/src/main/java/org/libreccm/auditing/CcmRevisionListener.java b/ccm-core/src/main/java/org/libreccm/auditing/CcmRevisionListener.java index aec9891be..952717b5f 100644 --- a/ccm-core/src/main/java/org/libreccm/auditing/CcmRevisionListener.java +++ b/ccm-core/src/main/java/org/libreccm/auditing/CcmRevisionListener.java @@ -18,37 +18,35 @@ */ package org.libreccm.auditing; -import java.util.Optional; +import org.apache.shiro.subject.Subject; + + import org.hibernate.envers.RevisionListener; import org.libreccm.cdi.utils.CdiUtil; import org.libreccm.security.Shiro; -import org.libreccm.security.User; - /** * {@link RevisionListener} setting the user for the {@link CcmRevision} entity. - * + * * @author Jens Pelzetter */ public class CcmRevisionListener implements RevisionListener { @Override public void newRevision(final Object revisionEntity) { - + if (!(revisionEntity instanceof CcmRevision)) { throw new IllegalArgumentException(String.format( "Provided revision entity is not an instance of class \"%s\".", CcmRevision.class.getName())); } final CcmRevision revision = (CcmRevision) revisionEntity; - + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); final Shiro shiro = cdiUtil.findBean(Shiro.class); - - final Optional user = shiro.getUser(); - if (user.isPresent()) { - revision.setUserName(user.get().getName()); - } + + final Subject subject = shiro.getSubject(); + revision.setUserName(subject.getPrincipal().toString()); } } diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Category.java b/ccm-core/src/main/java/org/libreccm/categorization/Category.java index 4452732af..c15513709 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Category.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Category.java @@ -84,20 +84,8 @@ import javax.xml.bind.annotation.XmlRootElement; name = "Category.withSubCategoriesAndObjects", attributeNodes = { @NamedAttributeNode(value = "subCategories" - //, - // subgraph = "subCategories" - ), //@NamedAttributeNode(value = "objects") + ), } - // , - // subgraphs = { - // @NamedSubgraph( - // name = "subCategories", - // attributeNodes = { - // @NamedAttributeNode("subCategories"), - // @NamedAttributeNode("objects") - // } - // ) - // } ) }) @XmlRootElement(name = "category", namespace = CAT_XML_NS) diff --git a/ccm-core/src/main/java/org/libreccm/security/RoleMembershipMarshaller.java b/ccm-core/src/main/java/org/libreccm/security/RoleMembershipMarshaller.java index d691f3e5e..26d90fb70 100644 --- a/ccm-core/src/main/java/org/libreccm/security/RoleMembershipMarshaller.java +++ b/ccm-core/src/main/java/org/libreccm/security/RoleMembershipMarshaller.java @@ -47,20 +47,27 @@ public class RoleMembershipMarshaller extends AbstractMarshaller } @Override - @Transactional(Transactional.TxType.REQUIRED) - protected void insertIntoDb(RoleMembership portableObject) { +// @Transactional(Transactional.TxType.REQUIRED) + protected void insertIntoDb(final RoleMembership portableObject) { // if (portableObject.getMembershipId() == 0) { portableObject.setMembershipId(0); // portableObject.setMembershipId(portableObject.getMembershipId() * -1); - entityManager.persist(portableObject); +// entityManager.persist(portableObject); // entityManager.merge(portableObject); - entityManager.flush(); +// entityManager.flush(); + final RoleMembership roleMembership = save(portableObject); LOGGER.debug("Saved RoleMembership with id {}.", - portableObject.getMembershipId()); + roleMembership.getMembershipId()); // } else { // entityManager.merge(portableObject); // } } + + @Transactional(Transactional.TxType.REQUIRES_NEW) + protected RoleMembership save(final RoleMembership membership) { + entityManager.persist(membership); + return membership; + } }