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 ba0b3b410..1d38d8c4e 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSection.java @@ -62,12 +62,14 @@ import static org.librecms.CmsConstants.*; @NamedQueries({ @NamedQuery( name = "ContentSection.findById", - query = "SELECT S FROM ContentSection s WHERE s.objectId = :objectId") - , + query = "SELECT s FROM ContentSection s WHERE s.objectId = :objectId"), + @NamedQuery( + name = "ContentSection.findByUuid", + query = "SELECT s FROM ContentSection s WHERE s.uuid = :uuid" + ), @NamedQuery( name = "ContentSection.findByLabel", - query = "SELECT s FROM ContentSection s WHERE s.label = :label") - , + query = "SELECT s FROM ContentSection s WHERE s.label = :label"), @NamedQuery( name = "ContentSection.findUsableContentTypes", query = "SELECT t FROM ContentType t " @@ -77,8 +79,7 @@ import static org.librecms.CmsConstants.*; + "WHERE p.grantedPrivilege = '" + TypePrivileges.USE_TYPE + "' " + "AND p.grantee in :roles) " - + "OR true = :isSysAdmin)") - , + + "OR true = :isSysAdmin)"), @NamedQuery( name = "ContentSection.countUsableContentTypes", query = "SELECT COUNT(t) FROM ContentType t " @@ -88,8 +89,7 @@ import static org.librecms.CmsConstants.*; + "WHERE p.grantedPrivilege = '" + TypePrivileges.USE_TYPE + "' " + "AND p.grantee IN :roles) " - + "OR true = :isSysAdmin)") - , + + "OR true = :isSysAdmin)"), @NamedQuery( name = "ContentSection.hasUsableContentTypes", query = "SELECT (CASE WHEN COUNT(t) > 0 THEN true ELSE false END)" @@ -100,8 +100,7 @@ import static org.librecms.CmsConstants.*; + "WHERE p.grantedPrivilege = '" + TypePrivileges.USE_TYPE + "' " + "AND p.grantee IN :roles) " - + "OR true = :isSysAdmin)") - , + + "OR true = :isSysAdmin)"), @NamedQuery( name = "ContentSection.findPermissions", query = "SELECT p FROM Permission p " @@ -117,19 +116,26 @@ import static org.librecms.CmsConstants.*; // creator = ContentSectionCreator.class, // servlet = ContentSectionServlet.class, // instanceForm = ApplicationInstanceForm.class) -public class ContentSection - extends CcmApplication +public class ContentSection + extends CcmApplication implements Serializable, Exportable { private static final long serialVersionUID = -671718122153931727L; protected static final String ROOT = "root"; + protected static final String ASSETS = "assets"; + protected static final String ALERT_RECIPIENT = "alert_recipient"; + protected static final String AUTHOR = "author"; + protected static final String EDITOR = "editor"; + protected static final String MANAGER = "manager"; + protected static final String PUBLISHER = "publisher"; + protected static final String CONTENT_READER = "content_reader"; @Column(name = "LABEL", length = 512) 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 3a241d7ab..9139cc21d 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionManager.java @@ -55,6 +55,8 @@ import org.librecms.contentsection.privileges.TypePrivileges; import org.librecms.dispatcher.ItemResolver; import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.enterprise.inject.Instance; @@ -77,6 +79,9 @@ public class ContentSectionManager { @Inject private CategoryRepository categoryRepo; + @Inject + private FolderRepository folderRepository; + @Inject private RoleRepository roleRepo; @@ -462,7 +467,7 @@ public class ContentSectionManager { try { @SuppressWarnings("unchecked") final Class itemResolverClazz - = (Class) Class. + = (Class) Class. forName(section.getItemResolverClass()); final Instance instance = itemResolvers.select( @@ -664,4 +669,41 @@ public class ContentSectionManager { typeRepo.delete(contentType.get()); } + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public void deleteContentSection(final ContentSection section) { + Objects.requireNonNull(section); + + try { + for (final ContentType type : section.getContentTypes()) { + @SuppressWarnings("unchecked") + final Class clazz + = (Class) Class.forName( + type.getContentItemClass() + ); + removeContentTypeFromSection(clazz, section); + } + } catch (ClassNotFoundException ex) { + throw new UnexpectedErrorException(ex); + } + for (final LifecycleDefinition lifecycle : section + .getLifecycleDefinitions()) { + removeLifecycleDefinitionFromContentSection( + lifecycle, section + ); + } + for (final Role role : section.getRoles()) { + removeRoleFromContentSection(section, role); + } + for (final Workflow workflow : section.getWorkflowTemplates()) { + removeWorkflowTemplateFromContentSection(workflow, section); + } + + folderRepository.delete(section.getRootAssetsFolder()); + folderRepository.delete(section.getRootDocumentsFolder()); + + sectionRepo.delete(section); + } + } diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionRepository.java index b099c70a8..6d1be4f51 100644 --- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionRepository.java +++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionRepository.java @@ -41,6 +41,22 @@ public class ContentSectionRepository private static final long serialVersionUID = 4616599498399330865L; + public Optional findByUuid(final String uuid) { + try { + return Optional.of( + getEntityManager() + .createNamedQuery( + "ContentSection.findByUuid", + ContentSection.class + ) + .setParameter("uuid", uuid) + .getSingleResult() + ); + } catch (NoResultException ex) { + return Optional.empty(); + } + } + public Optional findByLabel(final String label) { if (label == null || label.isEmpty()) { throw new IllegalArgumentException( diff --git a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsController.java b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsController.java index f46434f2e..12ff93d52 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsController.java +++ b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsController.java @@ -5,20 +5,33 @@ */ package org.librecms.ui; +import org.libreccm.api.Identifier; +import org.libreccm.api.IdentifierParser; +import org.libreccm.core.CoreConstants; import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.PermissionChecker; +import org.libreccm.security.RequiresPrivilege; +import org.libreccm.security.Shiro; +import org.librecms.contentsection.ContentSection; +import org.librecms.contentsection.ContentSectionManager; import org.librecms.contentsection.ContentSectionRepository; +import org.librecms.contentsection.Folder; import java.net.URI; import java.net.URISyntaxException; +import java.util.Objects; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.mvc.Controller; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; +import javax.transaction.Transactional; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; @@ -34,11 +47,17 @@ public class ContentSectionsController { @Inject private ContentSectionRepository sectionRepo; + @Inject + private ContentSectionManager sectionManager; + @Inject private HttpServletRequest request; @Inject - private ServletContext servletContext; + private IdentifierParser identifierParser; + + @Inject + private PermissionChecker permissionChecker; @GET @Path("/") @@ -95,13 +114,126 @@ public class ContentSectionsController { Response.status(Response.Status.NOT_FOUND).build() ); } - + @POST @Path("/new") - public String createContentSection() { - throw new WebApplicationException( - Response.status(Response.Status.NOT_FOUND).build() + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String createContentSection( + @FormParam("sectionName") final String sectionName + ) { + sectionManager.createContentSection(sectionName); + + return "redirect:/list"; + } + + @POST + @Path("/{sectionIdentifier}/rename") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String renameContentSection( + @PathParam("sectionIdentifier") final String identifierParam, + @FormParam("sectionName") final String sectionName + ) { + final ContentSection section = findContentSection(identifierParam); + + sectionManager.renameContentSection(section, sectionName); + + return "redirect:list"; + } + + @POST + @Path("/{sectionIdentifier}/delete") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String deleteContentSection( + @PathParam("sectionIdentifier") final String identifierParam, + @FormParam("confirmed") final String confirmed + ) { + if (Objects.equals(confirmed, "true")) { + final ContentSection section = findContentSection(identifierParam); + + if (!canDelete(section)) { + throw new WebApplicationException( + String.format( + "ContentSection %s is not empty and can't be deleted.", + section.getLabel() + ), + Response.Status.BAD_REQUEST + ); + } + + sectionManager.deleteContentSection(section); + } + + return "redirect:/list"; + } + + private ContentSection findContentSection(final String identifierParam) { + final Identifier identifier = identifierParser.parseIdentifier( + identifierParam ); + + final ContentSection section; + switch (identifier.getType()) { + case ID: + section = sectionRepo.findById( + Long.parseLong(identifier.getIdentifier()) + ).orElseThrow( + () -> new WebApplicationException( + String.format( + "No ContentSection identified by ID %s " + + "available.", + identifierParam + ), + Response.Status.NOT_FOUND + ) + ); + break; + case UUID: + section = sectionRepo + .findByUuid(identifier.getIdentifier()) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "No ContentSection identifed UUID %s " + + "available.", + identifierParam + ), + Response.Status.NOT_FOUND + ) + ); + break; + default: + section = sectionRepo + .findByLabel(identifier.getIdentifier()) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "No ContentSection with name %s " + + "available.", + identifierParam + ), + Response.Status.NOT_FOUND + ) + ); + break; + } + + return section; + } + + protected boolean canDelete(final ContentSection section) { + final Folder rootAssetsFolder = section.getRootAssetsFolder(); + final Folder rootDocumentsFolder = section.getRootDocumentsFolder(); + + return rootAssetsFolder.getSubFolders().isEmpty() + && rootAssetsFolder.getObjects().isEmpty() + && rootDocumentsFolder.getSubFolders().isEmpty() + && rootDocumentsFolder.getObjects().isEmpty(); } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableModel.java b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableModel.java index 6f4c44046..87e7fdaee 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableModel.java +++ b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableModel.java @@ -38,8 +38,13 @@ import javax.transaction.Transactional; @Named("ContentSectionsTableModel") public class ContentSectionsTableModel { + @Inject + private ContentSectionsController controller; + @Inject private ContentSectionRepository sectionRepo; + + @AuthorizationRequired @Transactional @@ -59,6 +64,7 @@ public class ContentSectionsTableModel { row.setSectionId(section.getObjectId()); row.setLabel(section.getLabel()); + row.setDeletable(controller.canDelete(section)); return row; } diff --git a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableRow.java b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableRow.java index cf51ac49f..4d4e5d05c 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableRow.java +++ b/ccm-cms/src/main/java/org/librecms/ui/ContentSectionsTableRow.java @@ -31,6 +31,8 @@ public class ContentSectionsTableRow implements private String label; + private boolean deletable; + public long getSectionId() { return sectionId; } @@ -47,13 +49,21 @@ public class ContentSectionsTableRow implements this.label = label; } + public boolean isDeletable() { + return deletable; + } + + public void setDeletable(boolean deletable) { + this.deletable = deletable; + } + @Override public int compareTo(final ContentSectionsTableRow other) { int result; result = Objects.compare( label, other.getLabel(), String::compareTo ); - + if (result == 0) { result = Objects.compare( sectionId, other.getSectionId(), Long::compareTo diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/list.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/list.xhtml index 2b391699b..259df38dd 100644 --- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/list.xhtml +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/list.xhtml @@ -2,7 +2,7 @@ @@ -19,11 +19,6 @@
-
- +
- + @@ -100,16 +96,82 @@ var="section"> - + diff --git a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties index 6a5f1e7dc..c4b996a89 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages.properties @@ -13,3 +13,14 @@ contentsections.new_dialog.close=Cancel contentsections.new_dialog.save=Create new content section contentsections.new_dialog.name.label=Name contentsections.new_dialog.name.help=The name of the new content section. Can only contain the letters a to z, the numbers 0-9, the hyphen and the underscore. +contentsections.list.table.actions.edit=Rename +contentsections.list.table.actions.delete=Delete +contentsections.delete_dialog.close=Cancel +contentsections.delete_dialog.confirm=Delete +contentsections.delete_dialog.title=Delete Content Section {0} +contentsections.delete_dialog.message=Are you sure to delete content section {0}? +contentsections.edit_dialog.title=Rename content section +contentsections.edit_dialog.close=Cancel +contentsections.edit_dialog.name.label=Name +contentsections.edit_dialog.save=Rename content section +contentsections.edit_dialog.name.help=The name of the content section. Can only contain the letters a to z, the numbers 0-9, the hyphen and the underscore. diff --git a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties index 13ed12ccb..fc0578195 100644 --- a/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties +++ b/ccm-cms/src/main/resources/org/librecms/CmsAdminMessages_de.properties @@ -13,3 +13,14 @@ contentsections.new_dialog.close=Abbrechen contentsections.new_dialog.save=Neue Content Section anlegen contentsections.new_dialog.name.label=Name contentsections.new_dialog.name.help=Der Name der neuen Content Section. Darf nur die Zeichen a bis z, 0-9, the Bindestrich und den Unterstrich enthalten. +contentsections.list.table.actions.edit=Umbenennen +contentsections.list.table.actions.delete=L\u00f6schen +contentsections.delete_dialog.close=Abbrechen +contentsections.delete_dialog.confirm=L\u00f6schen +contentsections.delete_dialog.title=Content Section {0} l\u00f6schen +contentsections.delete_dialog.message=Wollen Sie die Content Section {0} wirklich l\u00f6schen? +contentsections.edit_dialog.title=Content Section umbennen +contentsections.edit_dialog.close=Abbrechen +contentsections.edit_dialog.name.label=Name +contentsections.edit_dialog.save=Content Section umbenennen +contentsections.edit_dialog.name.help=Der Name der Content Section. Darf nur die Zeichen a bis z, 0-9, the Bindestrich und den Unterstrich enthalten. diff --git a/ccm-cms/src/main/scss/content-sections/_custom.scss b/ccm-cms/src/main/scss/content-sections/_custom.scss index 6a8a96b2d..edab43adc 100644 --- a/ccm-cms/src/main/scss/content-sections/_custom.scss +++ b/ccm-cms/src/main/scss/content-sections/_custom.scss @@ -37,3 +37,11 @@ $pre-scrollable-max-height: 21.25rem; // Navbar default colors have insufficient contrast $navbar-dark-color: #fff; + +table.contentsections-table { + tbody { + td.action-col { + width: 11em; + } + } +} \ No newline at end of file
#{CmsAdminMessages['contentsections.list.table.headers.label']} #{CmsAdminMessages['contentsections.list.table.headers.actions']}#{CmsAdminMessages['contentsections.list.table.headers.actions']}
- + #{section.label} - - - #{CmsAdminMessages['contentsections.list.table.actions.show_details']} - + + + + + + +