From 7d2d3926ff1b3d41daafdd4c2f1d41c1a8e2b53d Mon Sep 17 00:00:00 2001 From: jensp Date: Tue, 27 Sep 2016 17:55:10 +0000 Subject: [PATCH] CCM NG/ccm-cms: Created a subclass of Category for Folders. Started to add add Repository and Manager classes and to refactor all classes to use Folder instead of plain category for folders. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4328 8810af33-2d31-482b-a856-94f89814c4df --- .../com/arsdigita/cms/ui/folder/ItemPath.java | 4 +- .../contentsection/ContentItemManager.java | 112 ++++++++-- .../contentsection/ContentSection.java | 12 +- .../contentsection/ContentSectionManager.java | 6 +- .../contentsection/ContentSectionSetup.java | 6 +- .../org/librecms/contentsection/Folder.java | 120 ++++++++++ .../contentsection/FolderManager.java | 34 +++ .../contentsection/FolderRepository.java | 205 ++++++++++++++++++ .../librecms/contentsection/FolderType.java | 30 +++ .../InvalidFolderPathException.java | 67 ++++++ .../ContentItemManagerTest.java | 29 +-- .../org/libreccm/categorization/Category.java | 43 ++-- .../categorization/CategoryRepository.java | 22 +- 13 files changed, 611 insertions(+), 79 deletions(-) create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/Folder.java create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/FolderType.java create mode 100644 ccm-cms/src/main/java/org/librecms/contentsection/InvalidFolderPathException.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java index 83cbda1d3..ca3ad36bb 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/ItemPath.java @@ -24,10 +24,12 @@ import com.arsdigita.bebop.list.ListModel; import com.arsdigita.bebop.list.ListModelBuilder; import com.arsdigita.cms.ItemSelectionModel; import com.arsdigita.util.LockableImpl; + import org.libreccm.categorization.Category; import org.libreccm.cdi.utils.CdiUtil; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.Folder; /** * Produce a list of the items starting from the selected item's root down to @@ -46,7 +48,7 @@ public class ItemPath extends List { public static class ItemPathListModel implements ListModel { - private final java.util.List pathFolders; + private final java.util.List pathFolders; private int index = -1; public ItemPathListModel(final ContentItem item) { diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java index 8cf5b8b09..d986bb95a 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java @@ -42,6 +42,8 @@ import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.ObjectNotAssignedToCategoryException; import org.libreccm.configuration.ConfigurationManager; import org.libreccm.l10n.LocalizedString; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.RequiresPrivilege; import org.libreccm.workflow.Workflow; import org.libreccm.workflow.WorkflowManager; import org.librecms.CmsConstants; @@ -112,11 +114,13 @@ public class ContentItemManager { * * @return The new content item. */ + @AuthorizationRequired @Transactional(Transactional.TxType.REQUIRED) public T createContentItem( final String name, final ContentSection section, - final Category folder, + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW) + final Folder folder, final Class type) { final Optional contentType = typeRepo @@ -159,11 +163,13 @@ public class ContentItemManager { * * @return The new content item. */ + @AuthorizationRequired @Transactional(Transactional.TxType.REQUIRED) public T createContentItem( final String name, final ContentSection section, - final Category folder, + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW) + final Folder folder, final WorkflowTemplate workflowTemplate, final Class type) { @@ -226,22 +232,27 @@ public class ContentItemManager { * only moves the draft version of the item. The live version is moved after * a the item is republished. * - * @param item The item to move. - * @param targetFolder The folder to which the item is moved. + * @param item The item to move. + * @param target The folder to which the item is moved. */ + @AuthorizationRequired @Transactional(Transactional.TxType.REQUIRED) - public void move(final ContentItem item, final Category targetFolder) { + public void move( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_EDIT) + final ContentItem item, + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_EDIT) + final Folder target) { if (item == null) { throw new IllegalArgumentException("The item to move can't be null."); } - if (targetFolder == null) { + if (target == null) { throw new IllegalArgumentException( "The target folder can't be null."); } final ContentItem draftItem = getDraftVersion(item, item.getClass()); - final Optional currentFolder = getItemFolder(item); + final Optional currentFolder = getItemFolder(item); if (currentFolder.isPresent()) { try { @@ -254,7 +265,7 @@ public class ContentItemManager { categoryManager.addObjectToCategory( draftItem, - targetFolder, + target, CmsConstants.CATEGORIZATION_TYPE_FOLDER); } @@ -271,7 +282,10 @@ public class ContentItemManager { */ @Transactional(Transactional.TxType.REQUIRED) @SuppressWarnings("unchecked") - public void copy(final ContentItem item, final Category targetFolder) { + public void copy( + final ContentItem item, + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW) + final Folder targetFolder) { if (item == null) { throw new IllegalArgumentException("The item to copy can't be null."); } @@ -317,7 +331,7 @@ public class ContentItemManager { draftItem.getCategories().forEach(categorization -> categoryManager .addObjectToCategory(copy, categorization.getCategory())); - final Optional itemFolder = getItemFolder(draftItem); + final Optional itemFolder = getItemFolder(draftItem); if (itemFolder.isPresent()) { try { categoryManager.removeObjectFromCategory( @@ -490,7 +504,12 @@ public class ContentItemManager { * * @return The published content item. */ - public ContentItem publish(final ContentItem item) { + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + public ContentItem publish( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_PUBLISH) + final ContentItem item) { + if (item == null) { throw new IllegalArgumentException( "The item to publish can't be null."); @@ -512,9 +531,13 @@ public class ContentItemManager { * * @return The published content item. */ + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) @SuppressWarnings("unchecked") - public ContentItem publish(final ContentItem item, - final LifecycleDefinition lifecycleDefinition) { + public ContentItem publish( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_PUBLISH) + final ContentItem item, + final LifecycleDefinition lifecycleDefinition) { if (item == null) { throw new IllegalArgumentException( "The item to publish can't be null."); @@ -704,8 +727,11 @@ public class ContentItemManager { * * @param item */ + @AuthorizationRequired @Transactional(Transactional.TxType.REQUIRED) - public void unpublish(final ContentItem item) { + public void unpublish( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_PUBLISH) + final ContentItem item) { if (item == null) { throw new IllegalArgumentException( "The item to unpublish can't be null"); @@ -752,6 +778,7 @@ public class ContentItemManager { * @return {@code true} if the content item has a live version, * {@code false} if not. */ + @Transactional(Transactional.TxType.REQUIRED) public boolean isLive(final ContentItem item) { final TypedQuery query = entityManager.createNamedQuery( "ContentItem.hasLiveVersion", Boolean.class); @@ -772,8 +799,11 @@ public class ContentItemManager { * version is returned. If there is no live version an empty * {@link Optional} is returned. */ + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) @SuppressWarnings({"unchecked"}) public Optional getLiveVersion( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED) final ContentItem item, final Class type) { @@ -784,7 +814,7 @@ public class ContentItemManager { type.getName(), item.getClass().getName())); } - + if (isLive(item)) { final TypedQuery query = entityManager .createNamedQuery( @@ -831,9 +861,14 @@ public class ContentItemManager { * something is seriously wrong with the database) this method will * never return {@code null}. */ + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) @SuppressWarnings("unchecked") - public T getDraftVersion(final ContentItem item, - final Class type) { + public T getDraftVersion( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_PREVIEW) + final ContentItem item, + final Class type) { + if (!ContentItem.class.isAssignableFrom(type)) { throw new IllegalArgumentException(String.format( "The provided type \"%s\" does match the type of the provided " @@ -932,20 +967,40 @@ public class ContentItemManager { * * @return */ - public List getItemFolders(final ContentItem item) { + public List getItemFolders(final ContentItem item) { final List result = item.getCategories().stream(). filter(categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. equals(categorization.getType())) .collect(Collectors.toList()); - final List folders = new ArrayList<>(); + final List folders = new ArrayList<>(); if (!result.isEmpty()) { Category current = result.get(0).getCategory(); - folders.add(current); + if (current instanceof Folder) { + folders.add((Folder) current); + } else { + throw new IllegalArgumentException(String.format( + "The item %s is assigned to the category %s with the" + + "categorization type \"%s\", but the Category is not" + + "a folder. This is no supported.", + item.getUuid(), + current.getUuid(), + CmsConstants.CATEGORIZATION_TYPE_FOLDER)); + } while (current.getParentCategory() != null) { current = current.getParentCategory(); - folders.add(current); + if (current instanceof Folder) { + folders.add((Folder) current); + } else { + throw new IllegalArgumentException(String.format( + "The item %s is assigned to the category %s with the" + + "categorization type \"%s\", but the Category is not" + + "a folder. This is no supported.", + item.getUuid(), + current.getUuid(), + CmsConstants.CATEGORIZATION_TYPE_FOLDER)); + } } Collections.reverse(folders); @@ -964,14 +1019,25 @@ public class ContentItemManager { * @return An {@link Optional} containing the folder of the item if the item * is part of a folder. */ - public Optional getItemFolder(final ContentItem item) { + public Optional getItemFolder(final ContentItem item) { final List result = item.getCategories().stream(). filter(categorization -> CmsConstants.CATEGORIZATION_TYPE_FOLDER. equals(categorization.getType())) .collect(Collectors.toList()); if (result.size() > 0) { - return Optional.of(result.get(0).getCategory()); + final Category category = result.get(0).getCategory(); + if (category instanceof Folder) { + return Optional.of((Folder) category); + } else { + throw new IllegalArgumentException(String.format( + "The item %s is assigned to the category %s with the" + + "categorization type \"%s\", but the Category is not" + + "a folder. This is no supported.", + item.getUuid(), + category.getUuid(), + CmsConstants.CATEGORIZATION_TYPE_FOLDER)); + } } else { return Optional.empty(); } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java index 749f366c8..1b8e0e559 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java @@ -88,11 +88,11 @@ public class ContentSection extends CcmApplication implements Serializable { @OneToOne @JoinColumn(name = "ROOT_DOCUMENTS_FOLDER_ID") - private Category rootDocumentsFolder; + private Folder rootDocumentsFolder; @OneToOne @JoinColumn(name = "ROOT_ASSETS_FOLDER_ID") - private Category rootAssetsFolder; + private Folder rootAssetsFolder; @Column(name = "PAGE_RESOLVER_CLASS", length = 1024) private String pageResolverClass; @@ -161,19 +161,19 @@ public class ContentSection extends CcmApplication implements Serializable { this.label = label; } - public Category getRootDocumentsFolder() { + public Folder getRootDocumentsFolder() { return rootDocumentsFolder; } - protected void setRootDocumentFolder(final Category rootDocumentsFolder) { + protected void setRootDocumentFolder(final Folder rootDocumentsFolder) { this.rootDocumentsFolder = rootDocumentsFolder; } - public Category getRootAssetsFolder() { + public Folder getRootAssetsFolder() { return rootAssetsFolder; } - protected void setRootAssetsFolder(final Category rootAssetsFolder) { + protected void setRootAssetsFolder(final Folder rootAssetsFolder) { this.rootAssetsFolder = rootAssetsFolder; } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java index f81444ef8..3095a0478 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java @@ -104,15 +104,16 @@ public class ContentSectionManager { section.setPrimaryUrl(name); section.getTitle().addValue(defautLocale, name); - final Category rootFolder = new Category(); + final Folder rootFolder = new Folder(); rootFolder.setName(String.format("%s_root", name)); rootFolder.getTitle().addValue(defautLocale, rootFolder.getName()); rootFolder.setDisplayName(rootFolder.getName()); rootFolder.setUuid(UUID.randomUUID().toString()); rootFolder.setUniqueId(rootFolder.getUuid()); rootFolder.setCategoryOrder(1L); + rootFolder.setSection(section); - final Category rootAssetFolder = new Category(); + final Folder rootAssetFolder = new Folder(); rootAssetFolder.setName(String.format("%s_assets", name)); rootAssetFolder.getTitle().addValue(defautLocale, rootAssetFolder.getName()); @@ -120,6 +121,7 @@ public class ContentSectionManager { rootAssetFolder.setUuid(UUID.randomUUID().toString()); rootAssetFolder.setUniqueId(rootAssetFolder.getUuid()); rootAssetFolder.setCategoryOrder(1L); + rootAssetFolder.setSection(section); section.setRootDocumentFolder(rootFolder); section.setRootAssetsFolder(rootAssetFolder); diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java index 863be218d..cf8a08e8c 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java @@ -89,15 +89,17 @@ public class ContentSectionSetup extends AbstractCcmApplicationSetup { section.getDisplayName(), section.getLabel()); - final Category rootFolder = new Category(); + final Folder rootFolder = new Folder(); rootFolder.setUuid(UUID.randomUUID().toString()); rootFolder.setUniqueId(rootFolder.getUuid()); rootFolder.setName(String.format("%s_" + ROOT, sectionName)); + rootFolder.setSection(section); - final Category rootAssetFolder = new Category(); + final Folder rootAssetFolder = new Folder(); rootAssetFolder.setName(String.format("%s_" + ASSETS, sectionName)); rootAssetFolder.setUuid(UUID.randomUUID().toString()); rootAssetFolder.setUniqueId(rootAssetFolder.getUuid()); + rootAssetFolder.setSection(section); section.setRootDocumentFolder(rootFolder); section.setRootAssetsFolder(rootAssetFolder); diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/Folder.java b/ccm-cms/src/main/java/org/librecms/contentsection/Folder.java new file mode 100644 index 000000000..34a352b8b --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/Folder.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.contentsection; + +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +import org.libreccm.categorization.Category; +import org.libreccm.core.CcmObject; + +import java.util.Objects; +import java.util.Optional; + +import javax.persistence.Column; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; + +import static org.librecms.CmsConstants.*; + +/** + * + * @author Jens Pelzetter + */ +@Entity +@Table(name = "FOLDERS", schema = DB_SCHEMA) +@NamedQueries({ + @NamedQuery( + name = "Folder.rootFolders", + query = "SELECT f FROM Folder f " + + "WHERE f.parentCategory IS NULL " + + " AND f.type = :type"), + @NamedQuery( + name = "Folder.findByName", + query = "SELECT f FROM Folder f WHERE f.name = :name") +}) +public class Folder extends Category { + + private static final long serialVersionUID = 1L; + + @OneToOne + @JoinColumn(name = "CONTENT_SECTION_ID") + private ContentSection section; + + @Column(name = "TYPE", nullable = false) + @Enumerated(EnumType.STRING) + private FolderType type; + + public ContentSection getSection() { + return section; + } + + protected void setSection(final ContentSection section) { + this.section = section; + } + + public FolderType getType() { + return type; + } + + protected void setType(final FolderType type) { + this.type = type; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 29 * hash + Objects.hashCode(type); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (!super.equals(obj)) { + return false; + } + + if (!(obj instanceof Folder)) { + return false; + } + final Folder other = (Folder) obj; + if (!other.canEqual(this)) { + return false; + } + + return type == other.getType(); + } + + @Override + public boolean canEqual(final Object obj) { + return obj instanceof Folder; + } + + @Override + public String toString(final String data) { + return super.toString(String.format(", type = %s%s", + type, + data)); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java new file mode 100644 index 000000000..c7282e31a --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.contentsection; + +import javax.enterprise.context.RequestScoped; + +/** + * + * @author Jens Pelzetter +*/ +@RequestScoped +public class FolderManager { + + //createFolder + //deleteFolder + //moveFolder + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java new file mode 100644 index 000000000..d67bd0afd --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderRepository.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.contentsection; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.categorization.Category; +import org.libreccm.core.AbstractEntityRepository; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.RequiresPrivilege; +import org.librecms.CmsConstants; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.persistence.TypedQuery; +import javax.transaction.Transactional; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class FolderRepository extends AbstractEntityRepository { + + private static final Logger LOGGER = LogManager.getLogger( + FolderRepository.class); + + @Inject + private ContentSectionRepository sectionRepo; + + @Override + public Class getEntityClass() { + return Folder.class; + } + + @Override + public boolean isNew(final Folder folder) { + return folder.getObjectId() == 0; + } + + @Override + public void initNewEntity(final Folder folder) { + folder.setUuid(UUID.randomUUID().toString()); + } + + @Transactional(Transactional.TxType.REQUIRED) + public List getRootDocumentFolders() { + final TypedQuery query = getEntityManager().createNamedQuery( + "Folder.rootFolders", Folder.class); + query.setParameter("type", FolderType.DOCUMENTS_FOLDER); + + return query.getResultList(); + } + + public List getRootAssetFolders() { + final TypedQuery query = getEntityManager().createNamedQuery( + "Folder.rootFolders", Folder.class); + query.setParameter("type", FolderType.ASSET_FOLDER); + + return query.getResultList(); + } + + public Optional findByPath(final String path, + final FolderType type) { + if (path == null || path.isEmpty()) { + throw new IllegalArgumentException("Path can't be null or empty."); + } + + if (type == null) { + throw new IllegalArgumentException("No folder type provided."); + } + + final String[] tokens = path.split(":"); + if (tokens.length > 2) { + throw new InvalidFolderPathException( + "The provided path is invalid: More than one colon found. " + + "Valid path format: domainKey:path"); + } + + if (tokens.length < 2) { + throw new InvalidFolderPathException( + "The provided path is invalid: No content section found in path. " + + "Valid path format: contentSection:path"); + } + + final ContentSection section = sectionRepo.findByLabel(tokens[0]); + if (section == null) { + throw new InvalidFolderPathException(String.format( + "No content section identified by label \"%s\" found.", + tokens[0])); + } + + return findByPath(section, tokens[1], type); + } + + @Transactional(Transactional.TxType.REQUIRED) + public Optional findByPath(final ContentSection section, + final String path, + final FolderType type) { + if (section == null) { + throw new IllegalArgumentException("section can't be null"); + } + + if (path == null || path.isEmpty()) { + throw new IllegalArgumentException("Path can't be null or empty."); + } + + String normalizedPath = path.replace('.', '/'); + if (normalizedPath.charAt(0) == '/') { + normalizedPath = normalizedPath.substring(1); + } + + if (normalizedPath.endsWith("/")) { + normalizedPath = normalizedPath.substring(0, + normalizedPath.length()); + } + + LOGGER.debug("Trying to find folder with path \"{}\" and type {} in" + + "content section \"{}\".", + normalizedPath, + type, + section.getLabel()); + final String[] tokens = normalizedPath.split("/"); + Folder current; + switch(type) { + case ASSET_FOLDER: + current = section.getRootAssetsFolder(); + break; + case DOCUMENTS_FOLDER: + current = section.getRootDocumentsFolder(); + break; + default: + throw new IllegalArgumentException(String.format( + "Unexpected folder type %s", type)); + } + for(final String token : tokens) { + if (current.getSubCategories() == null) { + return Optional.empty(); + } + + final Optional result = current.getSubCategories() + .stream() + .filter( c -> { + LOGGER.debug("#findByPath(ContentSection, String, FolderType: c = {}", + c.toString()); + LOGGER.debug("#findByPath(ContentSection, String, FolderType: c.getName = \"{}\"", + c.getName()); + LOGGER.debug("#findByPath(ContentSection, String, FolderType: token = \"{}\"", + token); + return c.getName().equals(token); + }) + .findFirst(); + + if (result.isPresent() + && result.get() instanceof Folder) { + current = (Folder) result.get(); + } else { + return Optional.empty(); + } + } + + return Optional.of(current); + } + + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + @Override + public void save( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW) + final Folder folder) { + + super.save(folder); + } + + @AuthorizationRequired + @Transactional(Transactional.TxType.REQUIRED) + @Override + public void delete( + @RequiresPrivilege(CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW) + final Folder folder) { + + super.delete(folder); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderType.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderType.java new file mode 100644 index 000000000..10914055f --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderType.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.librecms.contentsection; + +/** + * + * @author Jens Pelzetter + */ +public enum FolderType { + + DOCUMENTS_FOLDER, + ASSET_FOLDER, + +} diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/InvalidFolderPathException.java b/ccm-cms/src/main/java/org/librecms/contentsection/InvalidFolderPathException.java new file mode 100644 index 000000000..a77c45a97 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/contentsection/InvalidFolderPathException.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 LibreCCM Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +package org.librecms.contentsection; + +/** + * + * @author Jens Pelzetter + */ +public class InvalidFolderPathException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of InvalidFolderPathException without detail message. + */ + public InvalidFolderPathException() { + super(); + } + + + /** + * Constructs an instance of InvalidFolderPathException with the specified detail message. + * + * @param msg The detail message. + */ + public InvalidFolderPathException(final String msg) { + super(msg); + } + + /** + * Constructs an instance of InvalidFolderPathException which wraps the + * specified exception. + * + * @param exception The exception to wrap. + */ + public InvalidFolderPathException(final Exception exception) { + super(exception); + } + + /** + * Constructs an instance of InvalidFolderPathException with the specified message which also wraps the + * specified exception. + * + * @param msg The detail message. + * @param exception The exception to wrap. + */ + public InvalidFolderPathException(final String msg, final Exception exception) { + super(msg, exception); + } +} diff --git a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemManagerTest.java b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemManagerTest.java index 42f810710..d9a57f849 100644 --- a/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemManagerTest.java +++ b/ccm-cms/src/test/java/org/librecms/contentsection/ContentItemManagerTest.java @@ -90,6 +90,9 @@ public class ContentItemManagerTest { @Inject private CategoryRepository categoryRepo; + @Inject + private FolderRepository folderRepo; + @Inject private Shiro shiro; @@ -247,7 +250,7 @@ public class ContentItemManagerTest { }) public void createContentItem() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); final Article article = itemManager.createContentItem("new-article", section, @@ -284,7 +287,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemTypeNotInSection() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); itemManager.createContentItem("Test", section, folder, Event.class); } @@ -298,7 +301,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemNameIsNull() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); itemManager.createContentItem(null, section, folder, Article.class); } @@ -312,7 +315,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemNameIsEmpty() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); itemManager.createContentItem("", section, folder, Article.class); } @@ -353,7 +356,7 @@ public class ContentItemManagerTest { }) public void createContentItemWithWorkflow() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); final WorkflowTemplate workflowTemplate = workflowTemplateRepo .findById(-110L); @@ -395,7 +398,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemTypeNotInSectionWithWorkflow() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); final WorkflowTemplate workflowTemplate = workflowTemplateRepo .findById(-110L); @@ -416,7 +419,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemNameIsNullWithWorkflow() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); final WorkflowTemplate workflowTemplate = workflowTemplateRepo .findById(-110L); @@ -437,7 +440,7 @@ public class ContentItemManagerTest { @ShouldThrowException(IllegalArgumentException.class) public void createItemNameIsNullWorkflowIsNull() { final ContentSection section = sectionRepo.findByLabel("info"); - final Category folder = section.getRootDocumentsFolder(); + final Folder folder = section.getRootDocumentsFolder(); itemManager.createContentItem(null, section, @@ -486,7 +489,7 @@ public class ContentItemManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - final Category targetFolder = categoryRepo.findById(-2120L); + final Folder targetFolder = folderRepo.findById(-2120L); assertThat(targetFolder, is(not(nullValue()))); itemManager.move(item.get(), targetFolder); @@ -500,7 +503,7 @@ public class ContentItemManagerTest { + "ContentItemManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void moveItemNull() { - final Category targetFolder = categoryRepo.findById(-2120L); + final Folder targetFolder = folderRepo.findById(-2120L); assertThat(targetFolder, is(not(nullValue()))); itemManager.move(null, targetFolder); @@ -544,7 +547,7 @@ public class ContentItemManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - final Category targetFolder = categoryRepo.findById(-2120L); + final Folder targetFolder = folderRepo.findById(-2120L); assertThat(targetFolder, is(not(nullValue()))); itemManager.copy(item.get(), targetFolder); @@ -574,7 +577,7 @@ public class ContentItemManagerTest { final Optional item = itemRepo.findById(-10100L); assertThat(item.isPresent(), is(true)); - final Category targetFolder = categoryRepo.findById(-2110L); + final Folder targetFolder = folderRepo.findById(-2110L); assertThat(targetFolder, is(not(nullValue()))); itemManager.copy(item.get(), targetFolder); @@ -589,7 +592,7 @@ public class ContentItemManagerTest { + "ContentItemManagerTest/data.xml") @ShouldThrowException(IllegalArgumentException.class) public void copyItemNull() { - final Category targetFolder = categoryRepo.findById(-2120L); + final Folder targetFolder = folderRepo.findById(-2120L); assertThat(targetFolder, is(not(nullValue()))); itemManager.copy(null, targetFolder); 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 7617f3410..32303896e 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/Category.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/Category.java @@ -69,37 +69,37 @@ import javax.xml.bind.annotation.XmlRootElement; @Entity @Table(name = "CATEGORIES", schema = DB_SCHEMA) @NamedQueries({ - @NamedQuery(name = "Category.topLevelCategories", - query - = "SELECT c FROM Category c WHERE c.parentCategory IS NULL"), - @NamedQuery(name = "Category.findByName", - query = "SELECT c FROM Category c WHERE c.name = :name") + @NamedQuery( + name = "Category.topLevelCategories", + query = "SELECT c FROM Category c WHERE c.parentCategory IS NULL"), + @NamedQuery( + name = "Category.findByName", + query = "SELECT c FROM Category c WHERE c.name = :name") }) @NamedEntityGraphs({ @NamedEntityGraph( name = "Category.withSubCategoriesAndObjects", attributeNodes = { @NamedAttributeNode(value = "subCategories" - //, + //, // subgraph = "subCategories" - ), - //@NamedAttributeNode(value = "objects") + ), //@NamedAttributeNode(value = "objects") } -// , -// subgraphs = { -// @NamedSubgraph( -// name = "subCategories", -// attributeNodes = { -// @NamedAttributeNode("subCategories"), -// @NamedAttributeNode("objects") -// } -// ) -// } + // , + // subgraphs = { + // @NamedSubgraph( + // name = "subCategories", + // attributeNodes = { + // @NamedAttributeNode("subCategories"), + // @NamedAttributeNode("objects") + // } + // ) + // } ) }) @DefaultEntityGraph("Category.withSubCategoriesAndObjects") @XmlRootElement(name = "category", namespace = CAT_XML_NS) -public class Category extends CcmObject implements InheritsPermissions, +public class Category extends CcmObject implements InheritsPermissions, Serializable { private static final long serialVersionUID = -7250208963391878547L; @@ -119,7 +119,7 @@ public class Category extends CcmObject implements InheritsPermissions, @Column(name = "NAME", nullable = false) @NotBlank @Pattern(regexp = "[\\w-.]*") - @XmlElement(name ="name", namespace = CAT_XML_NS) + @XmlElement(name = "name", namespace = CAT_XML_NS) private String name; /** @@ -338,8 +338,7 @@ public class Category extends CcmObject implements InheritsPermissions, public void setCategoryOrder(final long categoryOrder) { this.categoryOrder = categoryOrder; } - - + @Override public Optional getParent() { return Optional.ofNullable(getParentCategory()); diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java b/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java index d52f14fa9..d57cb76c2 100644 --- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java +++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryRepository.java @@ -75,6 +75,7 @@ public class CategoryRepository extends AbstractEntityRepository * * @return A list of all top level categories. */ + @Transactional(Transactional.TxType.REQUIRED) public List getTopLevelCategories() { final TypedQuery query = getEntityManager().createNamedQuery( "Category.topLevelCategories", Category.class); @@ -82,6 +83,7 @@ public class CategoryRepository extends AbstractEntityRepository return query.getResultList(); } + @Transactional(Transactional.TxType.REQUIRED) public Category findByPath(final String path) { if (path == null || path.isEmpty()) { throw new IllegalArgumentException("Path can't be null or empty."); @@ -110,6 +112,7 @@ public class CategoryRepository extends AbstractEntityRepository return findByPath(domain, tokens[1]); } + @Transactional(Transactional.TxType.REQUIRED) public Category findByPath(final Domain domain, final String path) { if (domain == null) { throw new IllegalArgumentException("Domain can't be null."); @@ -129,11 +132,10 @@ public class CategoryRepository extends AbstractEntityRepository normalizedPath.length()); } - LOGGER.debug(String.format( - "Trying to find category with path \"%s\" in " - + "domain \"%s\".", - normalizedPath, - domain.getDomainKey())); + LOGGER.debug("Trying to find category with path \"{}\" in " + + "domain \"{}\".", + normalizedPath, + domain.getDomainKey()); final String[] tokens = normalizedPath.split("/"); Category current = domain.getRoot(); for (final String token : tokens) { @@ -153,6 +155,7 @@ public class CategoryRepository extends AbstractEntityRepository return c.getName().equals(token); }) .findFirst(); + if (result.isPresent()) { current = result.get(); } else { @@ -164,13 +167,12 @@ public class CategoryRepository extends AbstractEntityRepository } @AuthorizationRequired - @Transactional(Transactional.TxType.REQUIRED) @Override public void save( - @RequiresPrivilege(CategorizationConstants.MANAGE_CATEGORY_PRIVILEGE) + @RequiresPrivilege(CategorizationConstants.MANAGE_CATEGORY_PRIVILEGE) final Category category) { - + super.save(category); } @@ -179,9 +181,9 @@ public class CategoryRepository extends AbstractEntityRepository @Transactional(Transactional.TxType.REQUIRED) @Override public void delete( - @RequiresPrivilege(CategorizationConstants.MANAGE_CATEGORY_PRIVILEGE) + @RequiresPrivilege(CategorizationConstants.MANAGE_CATEGORY_PRIVILEGE) final Category category) { - + super.save(category); }