diff --git a/ccm-cms/src/main/java/org/librecms/contenttypes/Article.java b/ccm-cms/src/main/java/org/librecms/contenttypes/Article.java index d4be12de4..85c20347d 100644 --- a/ccm-cms/src/main/java/org/librecms/contenttypes/Article.java +++ b/ccm-cms/src/main/java/org/librecms/contenttypes/Article.java @@ -43,7 +43,6 @@ import org.librecms.ui.contenttypes.MvcArticleCreateStep; import org.librecms.ui.contenttypes.MvcArticlePropertiesStep; import org.librecms.ui.contenttypes.MvcArticleTextBodyStep; import org.librecms.ui.contentsections.documents.MvcAuthoringKit; -import org.librecms.ui.contentsections.documents.MvcAuthoringKitStep; import javax.xml.bind.annotation.XmlRootElement; @@ -79,14 +78,8 @@ import javax.xml.bind.annotation.XmlRootElement; @MvcAuthoringKit( createStep = MvcArticleCreateStep.class, authoringSteps = { - @MvcAuthoringKitStep( - path = "basic-properties", - authoringStep = MvcArticlePropertiesStep.class - ), - @MvcAuthoringKitStep( - path = "basic-properties", - authoringStep = MvcArticleTextBodyStep.class - ) + MvcArticlePropertiesStep.class, + MvcArticleTextBodyStep.class } ) @XmlRootElement(name = "article", namespace = CMS_XML_NS) diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/AbstractMvcAuthoringStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/AbstractMvcAuthoringStep.java deleted file mode 100644 index 7e767d0fe..000000000 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/AbstractMvcAuthoringStep.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2021 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.ui.contentsections; - -import org.libreccm.l10n.GlobalizationHelper; -import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentItemManager; -import org.librecms.contentsection.ContentSection; -import org.librecms.ui.contentsections.documents.MvcAuthoringStep; - -import java.util.Map; -import java.util.Objects; - -import javax.inject.Inject; -import javax.mvc.Models; - -/** - * - * @author Jens Pelzetter - */ -public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep { - - @Inject - private ContentItemManager itemManager; - - @Inject - private GlobalizationHelper globalizationHelper; - - @Inject - private Models models; - - private ContentSection section; - - private ContentItem document; - - @Override - public ContentSection getContentSection() { - return section; - } - - @Override - public void setContentSection(final ContentSection section) { - this.section = section; - } - - @Override - public String getContentSectionLabel() { - return section.getLabel(); - } - - @Override - public String getContentSectionTitle() { - return globalizationHelper - .getValueFromLocalizedString(section.getTitle()); - } - - @Override - public ContentItem getContentItem() { - return document; - } - - @Override - public void setContentItem(final ContentItem document) { - this.document = document; - } - - @Override - public String getContentItemPath() { - return itemManager.getItemPath(document); - } - - @Override - public String getContentItemTitle() { - return globalizationHelper - .getValueFromLocalizedString(document.getTitle()); - } - - protected boolean hasParameter( - final Map parameters, - final String parameterName - ) { - Objects.requireNonNull( - parameters, - "parameters can't be null." - ); - Objects.requireNonNull( - parameterName, - "parameterName can't be null." - ); - return parameters.containsKey(parameterName) - && parameters.get(parameterName) != null - && parameters.get(parameterName).length != 0; - } - - /** - * Helper method to add a form parameter value to {@link #models}. - * - * @param parameters The form parameters. - * @param parameterName The parameter name - */ - protected void addParameterValueToModels( - final Map parameters, - final String parameterName - ) { - models.put( - Objects.requireNonNull( - parameterName, - "parameterName can't be null" - ), - Objects.requireNonNull( - parameters, - "parameters can't be null." - ).getOrDefault(parameterName, new String[]{""})[0] - ); - } - -} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java index 63d0799ab..f3353bad3 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/ContentSectionApplication.java @@ -21,14 +21,23 @@ package org.librecms.ui.contentsections; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.libreccm.ui.IsAuthenticatedFilter; +import org.librecms.ui.contentsections.documents.AuthoringStepsValidator; import org.librecms.ui.contentsections.documents.DocumentController; import org.librecms.ui.contentsections.documents.DocumentLifecyclesController; import org.librecms.ui.contentsections.documents.DocumentWorkflowController; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.mvc.Controller; import javax.ws.rs.ApplicationPath; +import javax.ws.rs.Path; import javax.ws.rs.core.Application; /** @@ -43,6 +52,13 @@ public class ContentSectionApplication extends Application { ContentSectionApplication.class ); + @Inject + private AuthoringStepsValidator stepsValidator; + + @Inject + @Any + private Instance authoringSteps; + @Override public Set> getClasses() { final Set> classes = new HashSet<>(); @@ -57,6 +73,9 @@ public class ContentSectionApplication extends Application { classes.add(ContentSectionController.class); classes.add(DocumentFolderController.class); classes.add(DocumentController.class); + + classes.addAll(getAuthoringSteps()); + classes.add(DocumentLifecyclesController.class); classes.add(DocumentWorkflowController.class); classes.add(IsAuthenticatedFilter.class); @@ -64,6 +83,13 @@ public class ContentSectionApplication extends Application { return classes; } - + private Set> getAuthoringSteps() { + return authoringSteps + .stream() + .map(MvcAuthoringSteps::getClasses) + .flatMap(Set::stream) + .filter(stepsValidator::validateAuthoringStep) + .collect(Collectors.toSet()); + } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepListEntry.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepListEntry.java index 2b5cebf8d..b252391bc 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepListEntry.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepListEntry.java @@ -41,7 +41,7 @@ public class AuthoringStepListEntry { /** * The path fragment of the authoring step. */ - private String pathFragment; + private String path; public String getLabel() { return label; @@ -59,12 +59,12 @@ public class AuthoringStepListEntry { this.description = description; } - public String getPathFragment() { - return pathFragment; + public String getPath() { + return path; } - public void setPathFragment(final String pathFragment) { - this.pathFragment = pathFragment; + public void setPath(final String path) { + this.path = path; } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepsValidator.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepsValidator.java new file mode 100644 index 000000000..c5ea47dbc --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/AuthoringStepsValidator.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.librecms.contentsection.ContentItem; + +import javax.enterprise.context.Dependent; +import javax.mvc.Controller; +import javax.ws.rs.Path; + +/** + * + * @author Jens Pelzetter + */ +@Dependent +public class AuthoringStepsValidator { + + private static final Logger LOGGER = LogManager.getLogger( + AuthoringStepsValidator.class + ); + + public boolean validateAuthoringStep(final Class stepClass) { + if (stepClass.getAnnotation(Controller.class) == null) { + LOGGER.warn( + "Class {} is part of a set of authoringsteps, but is not" + + " annotated with {}. The class will be ignored.", + stepClass.getName(), + Controller.class.getName()); + return false; + } + + final Path pathAnnotation = stepClass.getAnnotation(Path.class); + if (pathAnnotation == null) { + LOGGER.warn( + "Class {} is part of a set of authoring steps, but is not " + + "annotated with {}. the class will be ignored.", + stepClass.getName(), + Path.class.getName() + ); + return false; + } + + final String path = pathAnnotation.value(); + if (path == null + || !path.startsWith(MvcAuthoringSteps.PATH_PREFIX)) { + LOGGER.warn( + "Class {} is part of a set of authoring steps, but the value" + + "of the {} annotation of the class does not start " + + "with {}. The class will be ignored.", + stepClass.getName(), + Path.class.getName(), + MvcAuthoringSteps.PATH_PREFIX + ); + } + + if (stepClass.getAnnotation(MvcAuthoringStep.class) == null) { + LOGGER.warn( + "Class {} is part of a set of authoring steps, but is not " + + "annotated with {}. The class will be ignored.", + stepClass.getName(), + MvcAuthoringStep.class + ); + } + + return true; + } + + public boolean supportsItem( + final Class stepClass, final ContentItem item + ) { + final MvcAuthoringStep stepAnnotation = stepClass.getAnnotation( + MvcAuthoringStep.class + ); + + if (stepAnnotation == null) { + return false; + } + + return item.getClass().isAssignableFrom( + stepAnnotation.supportedDocumentType() + ); + } +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CategorizationStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CategorizationStep.java index 01c0ab60f..1d4dc99a8 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CategorizationStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CategorizationStep.java @@ -29,8 +29,6 @@ import org.libreccm.core.UnexpectedErrorException; import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.security.PermissionChecker; import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentItemManager; -import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.privileges.ItemPrivileges; import java.util.ArrayList; @@ -42,10 +40,11 @@ import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.mvc.Controller; import javax.mvc.Models; import javax.transaction.Transactional; import javax.ws.rs.FormParam; -import javax.ws.rs.POST; +import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -56,12 +55,16 @@ import javax.ws.rs.PathParam; * @author Jens Pelzetter */ @RequestScoped -@Path("/") -@AuthoringStepPathFragment(CategorizationStep.PATH_FRAGMENT) +@Path(MvcAuthoringSteps.PATH_PREFIX + "categorization") +@Controller @Named("CmsCategorizationStep") -public class CategorizationStep implements MvcAuthoringStep { - - static final String PATH_FRAGMENT = "categorization"; +@MvcAuthoringStep( + bundle = DefaultAuthoringStepConstants.BUNDLE, + descriptionKey = "authoringsteps.categorization.description", + labelKey = "authoringsteps.categorization.label", + supportedDocumentType = ContentItem.class +) +public class CategorizationStep { @Inject private CategoryManager categoryManager; @@ -72,9 +75,6 @@ public class CategorizationStep implements MvcAuthoringStep { @Inject private IdentifierParser identifierParser; - @Inject - private ContentItemManager itemManager; - @Inject private GlobalizationHelper globalizationHelper; @@ -84,130 +84,35 @@ public class CategorizationStep implements MvcAuthoringStep { @Inject private PermissionChecker permissionChecker; - /** - * The current content section. - */ - private ContentSection section; + @Inject + private MvcAuthoringStepService stepService; - /** - * The current document. - */ - private ContentItem document; + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - /** - * {@inheritDoc} - */ - @Override - public Class supportedDocumentType() { - return ContentItem.class; - } - - /** - * {@inheritDoc} - */ - @Override - public String getLabel() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.categorization.label"); - } - - /** - * {@inheritDoc} - */ - @Override - public String getDescription() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.categorization.description"); - } - - /** - * {@inheritDoc} - */ - @Override - public String getBundle() { - return DefaultAuthoringStepConstants.BUNDLE; - } - - /** - * {@inheritDoc} - */ - @Override - public ContentSection getContentSection() { - return section; - } - - /** - * {@inheritDoc} - */ - @Override - public void setContentSection(final ContentSection section) { - this.section = section; - } - - /** - * {@inheritDoc} - */ - @Override - public String getContentSectionLabel() { - return section.getLabel(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getContentSectionTitle() { - return globalizationHelper - .getValueFromLocalizedString(section.getTitle()); - } - - /** - * {@inheritDoc} - */ - @Override - public ContentItem getContentItem() { - return document; - } - - /** - * {@inheritDoc} - */ - @Override - public void setContentItem(final ContentItem document) { - this.document = document; - } - - /** - * {@inheritDoc} - */ - @Override - public String getContentItemPath() { - return itemManager.getItemPath(document); - } - - /** - * {@inheritDoc} - */ - @Override - public String getContentItemTitle() { - return globalizationHelper - .getValueFromLocalizedString(document.getTitle()); - } - - /** - * {@inheritDoc} - */ - @Override - public String showStep() { - if (permissionChecker.isPermitted(ItemPrivileges.CATEGORIZE, document)) { + if (permissionChecker.isPermitted( + ItemPrivileges.CATEGORIZE, stepService.getDocument() + )) { return "org/librecms/ui/documents/categorization.xhtml"; } else { return documentUi.showAccessDenied( - section, - document, - getLabel() + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) ); } } @@ -223,7 +128,8 @@ public class CategorizationStep implements MvcAuthoringStep { */ @Transactional(Transactional.TxType.REQUIRED) public List getCategorizationTrees() { - return section + return stepService + .getContentSection() .getDomains() .stream() .map(DomainOwnership::getDomain) @@ -234,27 +140,33 @@ public class CategorizationStep implements MvcAuthoringStep { /** * Update the categorization of the current item. * - * @param domainIdentifierParam The identifier for category system to use. - * @param assignedCategoriesParam The UUIDs of the categories assigned to - * the current content item. + * @param parameterPath The identifier for category system to use. + * @param parameters The parameters of the request. The map must contain + * a value with the key {@code assignedCategories}. + * * * @return A redirect to the categorization step. */ - @POST - @Path("/{domainIdentifier}") + @MvcAuthoringAction( + method = MvcAuthoringActionMethod.POST, + path = "/domains/" + ) + @Path("/domains/{domain}") @Transactional(Transactional.TxType.REQUIRED) public String updateCategorization( - @PathParam("domainIdentifierParam") final String domainIdentifierParam, - @FormParam("assignedCategories") + @PathParam("domain") + final String domainParam, + @FormParam("assignedCategories") final Set assignedCategoriesParam ) { final Identifier domainIdentifier = identifierParser.parseIdentifier( - domainIdentifierParam + domainParam ); final Optional domainResult; switch (domainIdentifier.getType()) { case ID: - domainResult = section + domainResult = stepService + .getContentSection() .getDomains() .stream() .map(DomainOwnership::getDomain) @@ -264,7 +176,8 @@ public class CategorizationStep implements MvcAuthoringStep { ).findAny(); break; case UUID: - domainResult = section + domainResult = stepService + .getContentSection() .getDomains() .stream() .map(DomainOwnership::getDomain) @@ -275,7 +188,8 @@ public class CategorizationStep implements MvcAuthoringStep { ).findAny(); break; default: - domainResult = section + domainResult = stepService + .getContentSection() .getDomains() .stream() .map(DomainOwnership::getDomain) @@ -287,20 +201,16 @@ public class CategorizationStep implements MvcAuthoringStep { } if (!domainResult.isPresent()) { - models.put("section", section.getLabel()); - models.put("domainIdentifier", domainIdentifierParam); + models.put("section", stepService.getContentSection().getLabel()); + models.put("domainIdentifier", domainIdentifier); return "org/librecms/ui/documents/categorization-domain-not-found.xhtml"; } - final Domain domain = domainResult.get(); - updateAssignedCategories(domain.getRoot(), assignedCategoriesParam); - - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT + updateAssignedCategories( + domainResult.get().getRoot(), assignedCategoriesParam ); + + return stepService.buildRedirectPathForStep(getClass()); } /** @@ -320,6 +230,7 @@ public class CategorizationStep implements MvcAuthoringStep { final Category category, final Set assignedCategoriesParam ) { + final ContentItem document = stepService.getDocument(); if (assignedCategoriesParam.contains(category.getUuid()) && !categoryManager.isAssignedToCategory(category, document)) { categoryManager.addObjectToCategory(document, category); @@ -416,6 +327,7 @@ public class CategorizationStep implements MvcAuthoringStep { final Category category ) { final CategorizationTreeNode node = new CategorizationTreeNode(); + final ContentItem document = stepService.getDocument(); node.setAssigned(categoryManager.isAssignedToCategory( category, document) ); diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKitStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java similarity index 62% rename from ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKitStep.java rename to ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java index 7ba210611..8381037bf 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKitStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/CmsMvcAuthoringSteps.java @@ -18,30 +18,26 @@ */ package org.librecms.ui.contentsections.documents; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.HashSet; +import java.util.Set; + +import javax.enterprise.context.ApplicationScoped; /** - * Metadata about an authoring step. * * @author Jens Pelzetter */ -@Retention(RetentionPolicy.RUNTIME) -public @interface MvcAuthoringKitStep { - - /** - * The path of authoring step. This value is added the the path for - * authoring steps. - * - * @return The path fragment for th authoring step. - */ - String path(); - - /** - * The class implementing the authoring step. - * - * @return The class implementing the authoring step. - */ - Class authoringStep(); +@ApplicationScoped +public class CmsMvcAuthoringSteps implements MvcAuthoringSteps { + @Override + public Set> getClasses() { + final Set> classes = new HashSet<>(); + classes.add(ExampleAuthoringStep.class); + + return classes; + } + + + } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ContentSectionNotFoundException.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ContentSectionNotFoundException.java new file mode 100644 index 000000000..438dfcc0d --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ContentSectionNotFoundException.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +/** + * Used by the {@link MvcAuthoringStepService} to indicate that the requested + * content section could not be found. The {@link MvcAuthoringStepService} has + * already populated {@link Models} with all necessary information. To show the + * error message the controller can simply return the string returned by + * {@link #showErrorMessage()}. + * + * @author Jens Pelzetter + */ +public class ContentSectionNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + private final String errorMessageTemplate; + + /** + * Creates a new instance of ContentSectionNotFound without + * detail message. + * + * @param errorMessageTemplate Template for the error message. + */ + ContentSectionNotFoundException(final String errorMessageTemplate) { + super(); + this.errorMessageTemplate = errorMessageTemplate; + } + + /** + * Constructs an instance of ContentSectionNotFound with the + * specified detail message. + * + * @param msg The detail message. + * @param errorMessageTemplate Template for the error message. + */ + ContentSectionNotFoundException( + final String errorMessageTemplate, final String msg + ) { + super(msg); + this.errorMessageTemplate = errorMessageTemplate; + } + + public String showErrorMessage() { + return errorMessageTemplate; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentController.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentController.java index a7454ea08..541973f1c 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentController.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentController.java @@ -75,6 +75,9 @@ import javax.ws.rs.core.MediaType; @Controller public class DocumentController { + @Inject + private AuthoringStepsValidator stepsValidator; + /** * Item manager instance for performing operations on {@link ContentItem}s. */ @@ -326,7 +329,7 @@ public class DocumentController { ) { final CreateStepResult result = findCreateStep( sectionIdentifier, - folderPath, + folderPath, documentType ); @@ -337,141 +340,140 @@ public class DocumentController { } } - /** - * Redirects to the first authoring step for the document identified by the - * provided path. - * - * @param sectionIdentifier The identifier of the current content section. - * @param documentPath The path of the document. - * - * @return A redirect to the first authoring step of the document, or the - * {@link DocumentNotFound} pseudo authoring step. - */ - @GET - @Path("/{documentPath:(.+)?}") - @AuthorizationRequired - @Transactional(Transactional.TxType.REQUIRED) - public String showEditDocument( - @PathParam("sectionIdentifier") final String sectionIdentifier, - @PathParam("documentPath") final String documentPath - ) { - final Optional sectionResult = sectionsUi - .findContentSection(sectionIdentifier); - if (!sectionResult.isPresent()) { - sectionsUi.showContentSectionNotFound(sectionIdentifier); - } - final ContentSection section = sectionResult.get(); - - final Optional itemResult = itemRepo - .findByPath(section, documentPath); - if (!itemResult.isPresent()) { - models.put("section", section.getLabel()); - models.put("documentPath", documentPath); - documentUi.showDocumentNotFound(section, documentPath); - } - final ContentItem item = itemResult.get(); - if (!itemPermissionChecker.canEditItem(item)) { - return documentUi.showAccessDenied( - section, - item, - defaultStepsMessageBundle.getMessage("edit_denied") - ); - } - - return String.format( - "redirect:/%s/documents/%s/@authoringsteps/%s", - sectionIdentifier, - documentPath, - findFirstAuthoringStep(item) - ); - } - - /** - * Redirect requests for an authoring step to the subresource of the - * authoring step. - * - * @param sectionIdentifier The identifier of the current content - * section. - * @param documentPath The path of the document to edit. - * @param authoringStepIdentifier The identifier/path fragment of the - * authoring step. - * @param request - * - * @return The authoring step subresource. - */ - @GET - @Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}") - @AuthorizationRequired - @Transactional(Transactional.TxType.REQUIRED) - public String showEditDocument( - @PathParam("sectionIdentifier") final String sectionIdentifier, - @PathParam("documentPath") final String documentPath, - @PathParam("authoringStep") final String authoringStepIdentifier, - @Context final HttpServletRequest request - ) { - final Optional sectionResult = sectionsUi - .findContentSection(sectionIdentifier); - if (!sectionResult.isPresent()) { - models.put("sectionIdentifier", sectionIdentifier); - return sectionsUi.showContentSectionNotFound(sectionIdentifier); - } - final ContentSection section = sectionResult.get(); - - final Optional itemResult = itemRepo - .findByPath(section, documentPath); - if (!itemResult.isPresent()) { - models.put("section", section.getLabel()); - models.put("documentPath", documentPath); - return documentUi.showDocumentNotFound(section, documentPath); - } - final ContentItem item = itemResult.get(); - if (!itemPermissionChecker.canEditItem(item)) { - models.put("section", section.getLabel()); - models.put("documentPath", itemManager.getItemFolder(item)); - models.put( - "step", defaultStepsMessageBundle.getMessage("edit_step") - ); - return documentUi.showAccessDenied( - section, documentPath, documentPath - ); - } - - final Instance instance = authoringSteps - .select( - new AuthoringStepPathFragmentLiteral( - authoringStepIdentifier - ) - ); - if (instance.isUnsatisfied() || instance.isAmbiguous()) { - models.put("section", section.getLabel()); - models.put("documentPath", documentPath); - models.put("authoringStep", authoringStepIdentifier); - return showAuthoringStepNotAvailable(authoringStepIdentifier); - } - final MvcAuthoringStep authoringStep = instance.get(); - - if (!authoringStep.supportedDocumentType().isAssignableFrom(item - .getClass())) { - models.put("section", section.getLabel()); - models.put("documentPath", documentPath); - models.put("documentType", item.getClass().getName()); - models.put("authoringStep", authoringStepIdentifier); - return showUnsupportedDocumentType( - authoringStepIdentifier, - item.getClass().getName() - ); - } - - models.put("authoringStep", authoringStepIdentifier); - - selectedDocumentModel.setContentItem(item); - - authoringStep.setContentSection(section); - authoringStep.setContentItem(item); - - return authoringStep.showStep(); - } - +// /** +// * Redirects to the first authoring step for the document identified by the +// * provided path. +// * +// * @param sectionIdentifier The identifier of the current content section. +// * @param documentPath The path of the document. +// * +// * @return A redirect to the first authoring step of the document, or the +// * {@link DocumentNotFound} pseudo authoring step. +// */ +// @GET +// @Path("/{documentPath:(.+)?}") +// @AuthorizationRequired +// @Transactional(Transactional.TxType.REQUIRED) +// public String showEditDocument( +// @PathParam("sectionIdentifier") final String sectionIdentifier, +// @PathParam("documentPath") final String documentPath +// ) { +// final Optional sectionResult = sectionsUi +// .findContentSection(sectionIdentifier); +// if (!sectionResult.isPresent()) { +// sectionsUi.showContentSectionNotFound(sectionIdentifier); +// } +// final ContentSection section = sectionResult.get(); +// +// final Optional itemResult = itemRepo +// .findByPath(section, documentPath); +// if (!itemResult.isPresent()) { +// models.put("section", section.getLabel()); +// models.put("documentPath", documentPath); +// documentUi.showDocumentNotFound(section, documentPath); +// } +// final ContentItem item = itemResult.get(); +// if (!itemPermissionChecker.canEditItem(item)) { +// return documentUi.showAccessDenied( +// section, +// item, +// defaultStepsMessageBundle.getMessage("edit_denied") +// ); +// } +// +// return String.format( +// "redirect:/%s/documents/%s/@authoringsteps/%s", +// sectionIdentifier, +// documentPath, +// findFirstAuthoringStep(item) +// ); +// } +// +// /** +// * Redirect requests for an authoring step to the subresource of the +// * authoring step. +// * +// * @param sectionIdentifier The identifier of the current content +// * section. +// * @param documentPath The path of the document to edit. +// * @param authoringStepIdentifier The identifier/path fragment of the +// * authoring step. +// * @param request +// * +// * @return The authoring step subresource. +// */ +// @GET +// @Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}") +// @AuthorizationRequired +// @Transactional(Transactional.TxType.REQUIRED) +// public String showEditDocument( +// @PathParam("sectionIdentifier") final String sectionIdentifier, +// @PathParam("documentPath") final String documentPath, +// @PathParam("authoringStep") final String authoringStepIdentifier, +// @Context final HttpServletRequest request +// ) { +// final Optional sectionResult = sectionsUi +// .findContentSection(sectionIdentifier); +// if (!sectionResult.isPresent()) { +// models.put("sectionIdentifier", sectionIdentifier); +// return sectionsUi.showContentSectionNotFound(sectionIdentifier); +// } +// final ContentSection section = sectionResult.get(); +// +// final Optional itemResult = itemRepo +// .findByPath(section, documentPath); +// if (!itemResult.isPresent()) { +// models.put("section", section.getLabel()); +// models.put("documentPath", documentPath); +// return documentUi.showDocumentNotFound(section, documentPath); +// } +// final ContentItem item = itemResult.get(); +// if (!itemPermissionChecker.canEditItem(item)) { +// models.put("section", section.getLabel()); +// models.put("documentPath", itemManager.getItemFolder(item)); +// models.put( +// "step", defaultStepsMessageBundle.getMessage("edit_step") +// ); +// return documentUi.showAccessDenied( +// section, documentPath, documentPath +// ); +// } +// +// final Instance instance = authoringSteps +// .select( +// new AuthoringStepPathFragmentLiteral( +// authoringStepIdentifier +// ) +// ); +// if (instance.isUnsatisfied() || instance.isAmbiguous()) { +// models.put("section", section.getLabel()); +// models.put("documentPath", documentPath); +// models.put("authoringStep", authoringStepIdentifier); +// return showAuthoringStepNotAvailable(authoringStepIdentifier); +// } +// final MvcAuthoringStep authoringStep = instance.get(); +// +// if (!authoringStep.supportedDocumentType().isAssignableFrom(item +// .getClass())) { +// models.put("section", section.getLabel()); +// models.put("documentPath", documentPath); +// models.put("documentType", item.getClass().getName()); +// models.put("authoringStep", authoringStepIdentifier); +// return showUnsupportedDocumentType( +// authoringStepIdentifier, +// item.getClass().getName() +// ); +// } +// +// models.put("authoringStep", authoringStepIdentifier); +// +// selectedDocumentModel.setContentItem(item); +// +// authoringStep.setContentSection(section); +// authoringStep.setContentItem(item); +// +// return authoringStep.showStep(); +// } @POST @Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}") @AuthorizationRequired @@ -780,21 +782,17 @@ public class DocumentController { * * @return A list of authoring steps for the provided item. */ - private List readAuthoringSteps( + private List> readAuthoringSteps( final ContentItem item ) { final MvcAuthoringKit authoringKit = item .getClass() .getAnnotation(MvcAuthoringKit.class); - final Class[] stepClasses = authoringKit - .authoringSteps(); - return Arrays - .stream(stepClasses) - .map(authoringSteps::select) - .filter(instance -> instance.isResolvable()) - .map(Instance::get) + .stream(authoringKit.authoringSteps()) + .filter(stepsValidator::validateAuthoringStep) + .filter(stepClass -> stepsValidator.supportsItem(stepClass, item)) .collect(Collectors.toList()); } @@ -804,14 +802,30 @@ public class DocumentController { * * @param item The content item. * - * @return The path fragment of the first authoring step of the item. + * @return The path of the first authoring step of the item. * */ private String findFirstAuthoringStep(final ContentItem item) { - final List steps = readAuthoringSteps(item); + final List> steps = readAuthoringSteps(item); - final MvcAuthoringStep firstStep = steps.get(0); - return firstStep.getClass().getName(); + final Class firstStep = steps.get(0); + final Path pathAnnotation = firstStep.getAnnotation(Path.class); + return pathAnnotation + .value() + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM + ), + item.getContentType().getContentSection().getLabel() + ) + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM + ), + itemManager.getItemPath(item) + ); } /** diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentNotFoundException.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentNotFoundException.java new file mode 100644 index 000000000..968130cae --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/DocumentNotFoundException.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +/** + * Used by the {@link MvcAuthoringStepService} to indicate that the requested + * document/content item could not be found. The {@link MvcAuthoringStepService} + * has already populated {@link Models} with all necessary information. To show + * the error message the controller can simply return the string returned by + * {@link #showErrorMessage()}. + * + * @author Jens Pelzetter + */ +public class DocumentNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + private final String errorMessageTemplate; + + /** + * Creates a new instance of DocumentNotFoundException without + * detail message. + * + * @param errorMessageTemplate Template for the error message. + */ + public DocumentNotFoundException(final String errorMessageTemplate) { + super(); + this.errorMessageTemplate = errorMessageTemplate; + } + + /** + * Constructs an instance of DocumentNotFoundException with the + * specified detail message. + * + * @param errorMessageTemplate + * @param msg The detail message. Template for the error + * message. + */ + public DocumentNotFoundException( + final String errorMessageTemplate, final String msg + ) { + super(msg); + this.errorMessageTemplate = errorMessageTemplate; + } + + public String showErrorMessage() { + return errorMessageTemplate; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ExampleAuthoringStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ExampleAuthoringStep.java new file mode 100644 index 000000000..1135ad5f8 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/ExampleAuthoringStep.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +import org.librecms.contentsection.ContentSection; +import org.librecms.ui.contentsections.ContentSectionModel; +import org.librecms.ui.contentsections.ContentSectionsUi; + +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.mvc.Models; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path(MvcAuthoringSteps.PATH_PREFIX + "example") +@Controller +public class ExampleAuthoringStep { + + + @Inject + private Models models; + + @Inject + private ContentSectionModel sectionModel; + + @Inject + private ContentSectionsUi sectionsUi; + + @GET + @Path("/") + public String showStep( + @PathParam("sectionIdentifier") final String sectionIdentifier, + @PathParam("documentPath") final String documentPath + ) { + models.put("sectionIdentifier", sectionIdentifier); + models.put("documentPath", documentPath); + + final Optional sectionResult = sectionsUi + .findContentSection(sectionIdentifier); + if (!sectionResult.isPresent()) { + return sectionsUi.showContentSectionNotFound(sectionIdentifier); + } + final ContentSection section = sectionResult.get(); + sectionModel.setSection(section); + + return "org/librecms/ui/contenttypes/example-authoring.xhtml"; + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringAction.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringAction.java index fffda5925..6871eb4ae 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringAction.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringAction.java @@ -56,7 +56,8 @@ public @interface MvcAuthoringAction { /** * The path fragment for invoking the action. The value of this parameter is - * added to the path of the authoring step. + * added to the path of the authoring step. Any path fragments after the + * value are provided to the method in the {@code parameterPath} parameter. * * @return The path fragment for invoking the action. */ diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKit.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKit.java index 9200ff158..da0cfee83 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKit.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringKit.java @@ -49,7 +49,7 @@ public @interface MvcAuthoringKit { * * @return The authoring steps for the annotated document type. */ - MvcAuthoringKitStep[] authoringSteps(); + Class[] authoringSteps(); /** * If set to {@code true} some authoring steps like categorization or diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStep.java index 4c3a777f4..704d17d98 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStep.java @@ -20,13 +20,17 @@ package org.librecms.ui.contentsections.documents; import org.libreccm.l10n.GlobalizationHelper; import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentSection; -import java.util.Map; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import javax.inject.Named; /** + * Metadata of an authoring step for documents (content items). + * * An authoring step for a document (content item). Implementing classes are * used as subresources by {@link DocumentController#editDocument(java.lang.String, java.lang.String, java.lang.String) * }. An implementation must be a named CDI bean (annotated with {@link Named}, @@ -37,7 +41,32 @@ import javax.inject.Named; * * @author Jens Pelzetter */ -public interface MvcAuthoringStep { +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MvcAuthoringStep { + + /** + * The name of the resource bundle providing the localized values for + * {@link #labelKey} and {@link descriptionKey}. + * + * @return The resource bundle providing the localized labelKey and + * descriptionKey. + */ + String bundle(); + + /** + * The key for the localized description of the step. + * + * @return The key for the localized description of the step. + */ + String descriptionKey(); + + /** + * The key for the localized label of the authoring step.. + * + * @return The key for the localized label of the authoring step... + */ + String labelKey(); /** * Authoring steps only support a specific type, and all subtypes. @@ -46,109 +75,4 @@ public interface MvcAuthoringStep { */ Class supportedDocumentType(); - /** - * Gets the localized label of the authoring step. The language variant to - * return should be selected using the locale returned by - * {@link GlobalizationHelper#getNegotiatedLocale()}. - * - * @return The localized label of the authoring step. - */ - String getLabel(); - - /** - * Gets the localized description of the authoring step. The language - * variant to return should be selected using the locale returned by - * {@link GlobalizationHelper#getNegotiatedLocale()}. - * - * @return The localized description of the authoring step. - */ - String getDescription(); - - /** - * Gets the name of the resource bundle providing the localized label and - * description. - * - * @return The resource bundle providing the localized label and - * description. - */ - String getBundle(); - - /** - * The current content section. - * - * @return The current content section. - */ - ContentSection getContentSection(); - - /** - * Convinient method for getting the label of the current content section. - * - * @return The label of the current content section. - */ - String getContentSectionLabel(); - - /** - * Convinient method for getting the title of the current content section. - * - * @return The title of the current content section for the current locale. - */ - String getContentSectionTitle(); - - /** - * The current content section is provided by the - * {@link DocumentController}. - * - * @param section The current content section. - */ - void setContentSection(final ContentSection section); - - /** - * The selected document/content item. - * - * @return The selected document/content item. - */ - ContentItem getContentItem(); - - /** - * Gets the path of the selected content item. - * - * @return The path of the selected content item. - */ - String getContentItemPath(); - - /** - * Gets the title of the selected content item. - * - * @return The title of the selected content item. - */ - String getContentItemTitle(); - - /** - * The current document/content item is provided by the - * {@link DocumentController}. - * - * @param document The document/content item to edit. - */ - void setContentItem(ContentItem document); - - /** - * Endpoint displaying the authoring step. This should not show the form, - * only an overview. The actual form(s) are provided by endpoints added by - * the implementation. - * - * @return The template of the edit step. - */ - String showStep(); - - /** - * Apply changes from the from of the script.Authoring steps that have - * multiple forms may choose to implement this method as any no-op method - * that simply redirects to the template view (same as {@link #showStep()}. - * - * @param formParameters The form parameters submitted. - * - * @return The template of the view to show. - */ - String applyEdits(Map formParameters); - } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStepService.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStepService.java new file mode 100644 index 000000000..87a5bda29 --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringStepService.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +import org.libreccm.l10n.GlobalizationHelper; +import org.librecms.contentsection.ContentItem; +import org.librecms.contentsection.ContentItemManager; +import org.librecms.contentsection.ContentItemRepository; +import org.librecms.contentsection.ContentSection; +import org.librecms.ui.contentsections.ContentSectionsUi; + +import java.util.Objects; +import java.util.Optional; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.ws.rs.Path; + +/** + * + * @author Jens Pelzetter + */ +@Dependent +public class MvcAuthoringStepService { + + @Inject + private DocumentUi documentUi; + + @Inject + private ContentItemManager itemManager; + + @Inject + private ContentItemRepository itemRepo; + + @Inject + private ContentSectionsUi sectionsUi; + + @Inject + private GlobalizationHelper globalizationHelper; + + private ContentSection section; + + private ContentItem document; + + private String documentPath; + + public ContentSection getContentSection() { + return section; + } + + public ContentItem getDocument() { + return document; + } + + public String getDocumentPath() { + return documentPath; + } + + /** + * Gets the label for an authoring step. + * + * @param step The authoring step class. + * + * @return The label for the authoring step. If the provided class is not + * annotated with {@link MvcAuthoringStep} the string {@code ???} is + * returned. + */ + public String getLabel(final Class step) { + return Optional + .ofNullable(step.getAnnotation(MvcAuthoringStep.class)) + .map( + annotation -> globalizationHelper.getLocalizedTextsUtil( + annotation.bundle() + ).getText(annotation.labelKey()) + ) + .orElse("???"); + } + + /** + * Gets the description for an authoring step. + * + * @param step The authoring step class. + * + * @return The label for the authoring step. If the provided class is not + * annotated with {@link MvcAuthoringStep} an empty stringis + * returned. + */ + public String getDescription(final Class step) { + return Optional + .ofNullable(step.getAnnotation(MvcAuthoringStep.class)) + .map( + annotation -> globalizationHelper.getLocalizedTextsUtil( + annotation.bundle() + ).getText(annotation.descriptionKey()) + ) + .orElse(""); + } + + /** + * Sets the properties {@link #section}, {@link #document} and + * {@link #documentPath} to content section and the document/content item + * identified by the provided parameters. + * + * @param sectionIdentifier The identifier of the content section. + * @param documentPath The identifier of the document/content item. + * + * @throws ContentSectionNotFoundException If there is no content section + * identified by + * {@code sectionIdentifier}. + * @throws DocumentNotFoundException If there is not document/content + * item with the path + * {@code documentPath} in the + * content section. + */ + public void setSectionAndDocument( + final String sectionIdentifier, final String documentPath + ) throws ContentSectionNotFoundException, DocumentNotFoundException { + section = sectionsUi + .findContentSection(sectionIdentifier) + .orElseThrow( + () -> new ContentSectionNotFoundException( + sectionsUi.showContentSectionNotFound(sectionIdentifier), + String.format( + "ContentSection %s not found.", + sectionIdentifier) + ) + ); + + document = itemRepo + .findByPath(section, documentPath) + .orElseThrow( + () -> new DocumentNotFoundException( + documentUi.showDocumentNotFound( + section, documentPath), + String.format( + "Not document for path %s in section %s.", + documentPath, + section.getLabel() + ) + ) + ); + + this.documentPath = itemManager.getItemPath(document); + } + + /** + * Builds the redirect path of the authoring step provided by the class + * {@code step}. This path is most often used to implement the redirect + * after post pattern. + * + * @param step The authoring step class. + * + * @return The redirect path. If the the provided class is not annotated + * with {@link Path} an empty string is returned. + */ + public String buildRedirectPathForStep(final Class step) { + Objects.requireNonNull(step); + return Optional + .ofNullable(step.getAnnotation(Path.class)) + .map(Path::value) + .map( + path -> path + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM + ), + section.getLabel() + ) + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM + ), + documentPath + ) + ) + .map(path -> String.format("redirect:%s", path)) + .orElse(""); + } + + /** + * Builds the redirect path of the authoring step provided by the class + * {@code step}.This path is most often used to implement the redirect after + * post pattern. + * + * @param step The authoring step class. + * @param subPath additional path fragment(s) that are appended to the path + * of the authoring step. + * + * @return The redirect path. If the the provided class is not annotated + * with {@link Path} an empty string is returned. + */ + public String buildRedirectPathForStep( + final Class step, final String subPath + ) { + Objects.requireNonNull(step); + Objects.requireNonNull(subPath); + final String subPathNormalized; + if (subPath.startsWith("/")) { + subPathNormalized = subPath.substring(1); + } else { + subPathNormalized = subPath; + } + + return Optional + .ofNullable(step.getAnnotation(Path.class)) + .map(Path::value) + .map( + path -> path + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM + ), + section.getLabel() + ) + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM + ), + documentPath + ) + ) + .map( + path -> String.format( + "redirect:%s/%s", path, subPathNormalized) + ).orElse(""); + } + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringSteps.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringSteps.java new file mode 100644 index 000000000..00202ae0c --- /dev/null +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/MvcAuthoringSteps.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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.ui.contentsections.documents; + +import java.util.Set; + +/** + * + * @author Jens Pelzetter + */ +public interface MvcAuthoringSteps { + + public static final String PATH_PREFIX + = "/{sectionIdentifier}/documents/{documentPath:(.+)?}/@"; + + public static final String SECTION_IDENTIFIER_PATH_PARAM + = "sectionIdentifier"; + + public static final String DOCUMENT_PATH_PATH_PARAM = "documentPath"; + + Set> getClasses(); + +} diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/PublishStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/PublishStep.java index 9a74a1ab9..b556aec61 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/PublishStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/PublishStep.java @@ -19,14 +19,11 @@ package org.librecms.ui.contentsections.documents; import org.libreccm.l10n.GlobalizationHelper; -import org.libreccm.security.AuthorizationRequired; import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemManager; -import org.librecms.contentsection.ContentSection; import org.librecms.lifecycle.Lifecycle; import org.librecms.lifecycle.LifecycleDefinition; import org.librecms.lifecycle.LifecycleDefinitionRepository; -import org.librecms.ui.contentsections.AbstractMvcAuthoringStep; import org.librecms.ui.contentsections.ItemPermissionChecker; import java.time.LocalDate; @@ -37,17 +34,20 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Date; -import java.util.Map; import java.util.Optional; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.mvc.Controller; import javax.mvc.Models; import javax.transaction.Transactional; +import javax.ws.rs.DefaultValue; 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; /** * Authoring step (part of the default steps) for publishing a @@ -57,22 +57,19 @@ import javax.ws.rs.Path; * @author Jens Pelzetter */ @RequestScoped -@Path("/") -@AuthoringStepPathFragment(PublishStep.PATH_FRAGMENT) +@Path(MvcAuthoringSteps.PATH_PREFIX + "publish") +@Controller @Named("CmsPublishStep") -public class PublishStep extends AbstractMvcAuthoringStep { +@MvcAuthoringStep( + bundle = DefaultAuthoringStepConstants.BUNDLE, + descriptionKey = "authoringsteps.publish.description", + labelKey = "authoringsteps.publish.label", + supportedDocumentType = ContentItem.class +) +public class PublishStep { - - private static final String SELECTED_LIFECYCLE_DEF_UUID - = "selectedLifecycleDefUuid"; - - private static final String START_DATE = "startDate"; - - private static final String START_TIME = "startTime"; - - private static final String END_DATE = "endDate"; - - private static final String END_TIME = "endTime"; + private static final String TEMPLATE + = "org/librecms/ui/documenttypes/publish.xhtml"; /** * The path fragment of the publish step. @@ -100,84 +97,54 @@ public class PublishStep extends AbstractMvcAuthoringStep { @Inject private Models models; + @Inject + private MvcAuthoringStepService stepService; - @Override - public Class supportedDocumentType() { - return ContentItem.class; - } + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - @Override - public String getLabel() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.publish.label"); - } - - @Override - public String getDescription() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.publish.description"); - } - - @Override - public String getBundle() { - return DefaultAuthoringStepConstants.BUNDLE; - } - - - - @Override - public String showStep() { - if (itemPermissionChecker.canPublishItems(getContentItem())) { + final ContentItem document = stepService.getDocument(); + if (itemPermissionChecker.canPublishItems(stepService.getDocument())) { final String lifecycleDefUuid; - if (itemManager.isLive(getContentItem())) { - lifecycleDefUuid = getContentItem() + if (itemManager.isLive(document)) { + lifecycleDefUuid = document .getLifecycle() .getDefinition() .getUuid(); } else { - lifecycleDefUuid = getContentItem() + lifecycleDefUuid = document .getContentType() .getDefaultLifecycle() .getUuid(); } models.put("lifecycleDefinitionUuid", lifecycleDefUuid); - return "org/librecms/ui/documents/publish.xhtml"; + return TEMPLATE; } else { return documentUi.showAccessDenied( - getContentSection(), - getContentItem(), + stepService.getContentSection(), + stepService.getDocument(), defaultStepsMessageBundle.getMessage( - "access_to_authoringstep_denied", new String[]{getLabel()} + "access_to_authoringstep_denied", + new String[]{stepService.getLabel(getClass())} ) ); } } - @AuthorizationRequired - @Transactional(Transactional.TxType.REQUIRED) - @Override - public String applyEdits(final Map formParameters) { - if (!formParameters.containsKey(SELECTED_LIFECYCLE_DEF_UUID) - || formParameters.get(SELECTED_LIFECYCLE_DEF_UUID) == null - || formParameters.get(SELECTED_LIFECYCLE_DEF_UUID).length == 0) { - if (!formParameters.containsKey(SELECTED_LIFECYCLE_DEF_UUID) - || formParameters.get(SELECTED_LIFECYCLE_DEF_UUID) == null - || formParameters.get(SELECTED_LIFECYCLE_DEF_UUID).length == 0) { - models.put("missingLifecycleDefinitionUuid", true); - addParameterValueToModels(formParameters, START_DATE); - addParameterValueToModels(formParameters, START_TIME); - addParameterValueToModels(formParameters, END_DATE); - addParameterValueToModels(formParameters, END_TIME); - - return "org/librecms/ui/documenttypes/publish.xhtml"; - } - } - } - - - /** * Get the label of the lifecycle assigned to the current content item. The * value is determined from the label of the definition of the lifecycle @@ -190,7 +157,7 @@ public class PublishStep extends AbstractMvcAuthoringStep { @Transactional(Transactional.TxType.REQUIRED) public String getAssignedLifecycleLabel() { return Optional - .ofNullable(getContentItem().getLifecycle()) + .ofNullable(stepService.getDocument().getLifecycle()) .map(Lifecycle::getDefinition) .map(LifecycleDefinition::getLabel) .map(globalizationHelper::getValueFromLocalizedString) @@ -210,7 +177,7 @@ public class PublishStep extends AbstractMvcAuthoringStep { @Transactional(Transactional.TxType.REQUIRED) public String getAssignedLifecycleDecription() { return Optional - .ofNullable(getContentItem().getLifecycle()) + .ofNullable(stepService.getDocument().getLifecycle()) .map(Lifecycle::getDefinition) .map(LifecycleDefinition::getDescription) .map(globalizationHelper::getValueFromLocalizedString) @@ -222,6 +189,9 @@ public class PublishStep extends AbstractMvcAuthoringStep { * {@code selectedLifecycleDefUuid} is ignored.The apply a new lifecycle the * document the unpublished first. * + * + * @param sectionIdentifier + * @param documentPath * @param selectedLifecycleDefUuid The ID of the lifecycle definition from * which the lifecycle for the item is * created. @@ -230,37 +200,55 @@ public class PublishStep extends AbstractMvcAuthoringStep { * @param endDateParam * @param endTimeParam * - * * @return A redirect the the publish step. */ - @MvcAuthoringAction( - method = MvcAuthoringActionMethod.POST, - path = "/@publish" - ) + @POST + @Path("/") @Transactional(Transactional.TxType.REQUIRED) public String publish( - final String parameterPath, - final Map parameters + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @FormParam("selectedLifecycleDefUuid") + final String selectedLifecycleDefUuid, + @FormParam("startDate") @DefaultValue("") + final String startDateParam, + @FormParam("startTime") @DefaultValue("") + final String startTimeParam, + @FormParam("endDate") @DefaultValue("") + final String endDateParam, + @FormParam("endTime") @DefaultValue("") + final String endTimeParam ) { - if (selectedLifecycleDefUuid == null) { - models.put("missingLifecycleDefinitionUuid", true); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documenttypes/publish.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - if (startDateParam == null - || startDateParam.isEmpty() - || startTimeParam == null - || startTimeParam.isEmpty()) { + + final ContentItem document = stepService.getDocument(); + + if (selectedLifecycleDefUuid.isEmpty()) { + models.put("missingLifecycleDefinitionUuid", true); + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; + } + if (startDateParam.isEmpty() || startTimeParam.isEmpty()) { models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("missingStartDateTime", true); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documents/publish.xhtml"; + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; } final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ISO_DATE @@ -274,11 +262,12 @@ public class PublishStep extends AbstractMvcAuthoringStep { } catch (DateTimeParseException ex) { models.put("invalidStartDate", true); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documents/publish.xhtml"; + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; } final LocalTime localStartTime; @@ -287,11 +276,12 @@ public class PublishStep extends AbstractMvcAuthoringStep { } catch (DateTimeParseException ex) { models.put("invalidStartTime", true); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documents/publish.xhtml"; + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; } final LocalDateTime startLocalDateTime = LocalDateTime.of( @@ -309,11 +299,12 @@ public class PublishStep extends AbstractMvcAuthoringStep { } catch (DateTimeParseException ex) { models.put("invalidEndDate", true); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documents/publish.xhtml"; + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; } final LocalTime localEndTime; @@ -322,11 +313,12 @@ public class PublishStep extends AbstractMvcAuthoringStep { } catch (DateTimeParseException ex) { models.put("invalidEndTime", true); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); - models.put("startDateTime", startDateParam); - models.put("startDateTime", startTimeParam); - models.put("endDateTime", endDateParam); - models.put("endDateTime", endTimeParam); - return "org/librecms/ui/documents/publish.xhtml"; + models.put("startDate", startDateParam); + models.put("startTime", startTimeParam); + models.put("endDate", endDateParam); + models.put("endTime", endTimeParam); + + return TEMPLATE; } final LocalDateTime endLocalDateTime = LocalDateTime.of( @@ -340,7 +332,7 @@ public class PublishStep extends AbstractMvcAuthoringStep { if (!itemPermissionChecker.canPublishItems(document)) { return documentUi.showAccessDenied( - section, + stepService.getContentSection(), document, "item.publish" ); @@ -354,13 +346,17 @@ public class PublishStep extends AbstractMvcAuthoringStep { document, definition, startDateTime, endDateTime ); } else { - itemManager.publish(document, startDateTime, endDateTime); + itemManager + .publish(document, startDateTime, endDateTime); } } else { final Optional definitionResult = lifecycleDefRepo.findByUuid(selectedLifecycleDefUuid); if (!definitionResult.isPresent()) { - models.put("contentSection", section.getLabel()); + models.put( + "contentSection", + stepService.getContentSection().getLabel() + ); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); return "org/librecms/ui/documents/lifecycle-definition-not-found.xhtml"; } @@ -370,12 +366,7 @@ public class PublishStep extends AbstractMvcAuthoringStep { ); } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + return stepService.buildRedirectPathForStep(getClass()); } /** @@ -383,13 +374,16 @@ public class PublishStep extends AbstractMvcAuthoringStep { * * @return A redirect to the publish step. */ - @POST - @Path("/@unpublish") + @MvcAuthoringAction( + method = MvcAuthoringActionMethod.POST, + path = "/@unpublish" + ) @Transactional(Transactional.TxType.REQUIRED) public String unpublish() { + final ContentItem document = stepService.getDocument(); if (!itemPermissionChecker.canPublishItems(document)) { return documentUi.showAccessDenied( - section, + stepService.getContentSection(), document, "item.unpublish" ); @@ -397,13 +391,7 @@ public class PublishStep extends AbstractMvcAuthoringStep { itemManager.unpublish(document); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + return stepService.buildRedirectPathForStep(getClass()); } - - + } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/RelatedInfoStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/RelatedInfoStep.java index bafacf7f0..5492dfc79 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/RelatedInfoStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/RelatedInfoStep.java @@ -18,10 +18,10 @@ */ package org.librecms.ui.contentsections.documents; - import org.libreccm.api.Identifier; import org.libreccm.api.IdentifierParser; import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.security.PermissionChecker; import org.librecms.assets.AssetTypeInfo; import org.librecms.assets.AssetTypesManager; import org.librecms.assets.RelatedLink; @@ -46,6 +46,7 @@ import org.librecms.contentsection.FolderRepository; import org.librecms.contentsection.FolderType; import org.librecms.contentsection.ItemAttachment; import org.librecms.contentsection.ItemAttachmentManager; +import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.ui.contentsections.AssetFolderRowModel; import org.librecms.ui.contentsections.AssetFolderTree; import org.librecms.ui.contentsections.AssetFolderTreeNode; @@ -71,11 +72,14 @@ import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.mvc.Controller; import javax.mvc.Models; import javax.transaction.Transactional; import javax.ws.rs.DefaultValue; +import javax.ws.rs.ForbiddenException; import javax.ws.rs.FormParam; import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -95,10 +99,16 @@ import javax.ws.rs.core.MediaType; * @author Jens Pelzetter */ @RequestScoped -@Path("/") -@AuthoringStepPathFragment(RelatedInfoStep.PATH_FRAGMENT) +@Path(MvcAuthoringSteps.PATH_PREFIX + "relatedinfo") +@Controller @Named("CmsRelatedInfoStep") -public class RelatedInfoStep implements MvcAuthoringStep { +@MvcAuthoringStep( + bundle = DefaultAuthoringStepConstants.BUNDLE, + descriptionKey = "authoringsteps.relatedinfo.description", + labelKey = "authoringsteps.relatedinfo.label", + supportedDocumentType = ContentItem.class +) +public class RelatedInfoStep { /** * The path fragment of the step. @@ -165,6 +175,9 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Inject private DocumentPermissions documentPermissions; + @Inject + private DocumentUi documentUi; + /** * Used to retrieve the path of folders. */ @@ -231,85 +244,40 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Inject private Models models; - /** - * The current document/content item. - */ - private ContentItem document; + @Inject + private MvcAuthoringStepService stepService; - /** - * The current content section. - */ - private ContentSection section; + @Inject + private PermissionChecker permissionChecker; - @Override - public Class supportedDocumentType() { - return ContentItem.class; - } + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - @Override - public String getLabel() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.relatedinfo.label"); - } - - @Override - public String getDescription() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.relatedinfo.description"); - } - - @Override - public String getBundle() { - return DefaultAuthoringStepConstants.BUNDLE; - } - - @Override - public ContentSection getContentSection() { - return section; - } - - @Override - public void setContentSection(final ContentSection section) { - this.section = section; - } - - @Override - public String getContentSectionLabel() { - return section.getLabel(); - } - - @Override - public String getContentSectionTitle() { - return globalizationHelper - .getValueFromLocalizedString(section.getTitle()); - } - - @Override - public ContentItem getContentItem() { - return document; - } - - @Override - public void setContentItem(final ContentItem document) { - this.document = document; - } - - @Override - public String getContentItemPath() { - return itemManager.getItemPath(document); - } - - @Override - public String getContentItemTitle() { - return globalizationHelper - .getValueFromLocalizedString(document.getTitle()); - } - - @Override - public String showStep() { - return "org/librecms/ui/documents/relatedinfo.xhtml"; + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + return "org/librecms/ui/documents/relatedinfo.xhtml"; + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** @@ -321,7 +289,8 @@ public class RelatedInfoStep implements MvcAuthoringStep { */ @Transactional(Transactional.TxType.REQUIRED) public List getAttachmentLists() { - return document + return stepService + .getDocument() .getAttachments() .stream() .filter(list -> !list.getName().startsWith(".")) @@ -332,6 +301,9 @@ public class RelatedInfoStep implements MvcAuthoringStep { /** * Gets the asset folder tree of the current content section as JSON data. * + * @param sectionIdentifier + * @param documentPath + * * @return The assets folder tree of the current content section as JSON * data. */ @@ -339,20 +311,41 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/asset-folders") @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) - public List getAssetFolderTree() { - return assetFolderTree.buildFolderTree( - section, section.getRootAssetsFolder() - ); + public List getAssetFolderTree( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentSection section = stepService.getContentSection(); + return assetFolderTree.buildFolderTree( + section, section.getRootAssetsFolder() + ); + } else { + throw new ForbiddenException(); + } } /** * Gets the assets in the folder as JSON data. * - * @param folderPath The path of the folder. - * @param firstResult The index of the firset result to show. - * @param maxResults The maximum number of results to show. - * @param filterTerm An optional filter term for filtering the assets in - * the folder by their name. + * @param folderPath The path of the folder. + * @param firstResult The index of the firset result to show. + * @param maxResults The maximum number of results to show. + * @param filterTerm An optional filter term for filtering the assets + * in the folder by their name. + * @param documentPath + * @param sectionIdentifier * * @return A list of the assets in the folder as JSON data. */ @@ -361,40 +354,67 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) public List getAssetsInFolder( - @PathParam("folderPath") final String folderPath, - @QueryParam("firstResult") @DefaultValue("0") final int firstResult, - @QueryParam("maxResults") @DefaultValue("20") final int maxResults, - @QueryParam("filterTerm") @DefaultValue("") final String filterTerm + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("folderPath") + final String folderPath, + @QueryParam("firstResult") + @DefaultValue("0") + final int firstResult, + @QueryParam("maxResults") + @DefaultValue("20") + final int maxResults, + @QueryParam("filterTerm") + @DefaultValue("") + final String filterTerm ) { - final Folder folder; - if (folderPath.isEmpty()) { - folder = section.getRootAssetsFolder(); - } else { - final Optional folderResult = folderRepo.findByPath( - section, folderPath, FolderType.ASSETS_FOLDER - ); - if (folderResult.isPresent()) { - folder = folderResult.get(); - } else { - return Collections.emptyList(); - } + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentSection section = stepService.getContentSection(); + final Folder folder; + if (folderPath.isEmpty()) { + folder = section.getRootAssetsFolder(); + } else { + final Optional folderResult = folderRepo.findByPath( + section, folderPath, FolderType.ASSETS_FOLDER + ); + if (folderResult.isPresent()) { + folder = folderResult.get(); + } else { + return Collections.emptyList(); + } + } + return folderRepo + .getAssetFolderEntries( + folder, firstResult, maxResults, filterTerm + ) + .stream() + .map(entry -> buildAssetFolderRowModel(section, entry)) + .collect(Collectors.toList()); + } else { + throw new ForbiddenException(); } - return folderRepo - .getAssetFolderEntries( - folder, firstResult, maxResults, filterTerm - ) - .stream() - .map(entry -> buildAssetFolderRowModel(section, entry)) - .collect(Collectors.toList()); } /** * Show all assets of a content section filtered by their name. * - * @param firstResult The index of the first result to show. - * @param maxResults The maximum number of results to show. - * @param searchTerm An optional search term applied to the names of the - * assets. + * @param sectionIdentifier + * @param documentPath + * @param firstResult The index of the first result to show. + * @param maxResults The maximum number of results to show. + * @param searchTerm An optional search term applied to the names of + * the assets. * * @return A list of matching assets as JSON. */ @@ -403,21 +423,48 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) public List findAssets( - @QueryParam("firstResult") @DefaultValue("0") final int firstResult, - @QueryParam("maxResults") @DefaultValue("20") final int maxResults, - @QueryParam("searchTerm") @DefaultValue("") final String searchTerm + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @QueryParam("firstResult") + @DefaultValue("0") + final int firstResult, + @QueryParam("maxResults") + @DefaultValue("20") + final int maxResults, + @QueryParam("searchTerm") + @DefaultValue("") + final String searchTerm ) { - return assetRepo.findByTitleAndContentSection(searchTerm, section) - .stream() - .map(asset -> buildAssetFolderRowModel(section, asset)) - .collect(Collectors.toList()); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); + } + final ContentSection section = stepService.getContentSection(); + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + return assetRepo.findByTitleAndContentSection(searchTerm, section) + .stream() + .map(asset -> buildAssetFolderRowModel(section, asset)) + .collect(Collectors.toList()); + } else { + throw new ForbiddenException(); + } } /** * Gets the document folder tree of the current content section as JSON * data. * + * @param sectionIdentifier + * @param documentPath + * * @return The document folder tree of the current content section as JSON * data. */ @@ -425,20 +472,41 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/document-folders") @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) - public List getDocumentFolderTree() { - return documentFolderTree.buildFolderTree( - section, section.getRootDocumentsFolder() - ); + public List getDocumentFolderTree( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentSection section = stepService.getContentSection(); + return documentFolderTree.buildFolderTree( + section, section.getRootDocumentsFolder() + ); + } else { + throw new ForbiddenException(); + } } /** * Gets the documents in the folder as JSON data. * - * @param folderPath The path of the folder. - * @param firstResult The index of the firset result to show. - * @param maxResults The maximum number of results to show. - * @param filterTerm An optional filter term for filtering the documents in - * the folder by their name. + * @param sectionIdentifier + * @param documentPath + * @param folderPath The path of the folder. + * @param firstResult The index of the firset result to show. + * @param maxResults The maximum number of results to show. + * @param filterTerm An optional filter term for filtering the + * documents in the folder by their name. * * @return A list of the documents in the folder as JSON data. */ @@ -447,44 +515,71 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) public List getDocumentsInFolder( - @PathParam("folderPath") final String folderPath, - @QueryParam("firstResult") @DefaultValue("0") final int firstResult, - @QueryParam("maxResults") @DefaultValue("20") final int maxResults, - @QueryParam("filterTerm") @DefaultValue("") final String filterTerm + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("folderPath") + final String folderPath, + @QueryParam("firstResult") + @DefaultValue("0") + final int firstResult, + @QueryParam("maxResults") + @DefaultValue("20") + final int maxResults, + @QueryParam("filterTerm") + @DefaultValue("") + final String filterTerm ) { - final Folder folder; - if (folderPath.isEmpty()) { - folder = section.getRootDocumentsFolder(); - } else { - final Optional folderResult = folderRepo.findByPath( - section, folderPath, FolderType.ASSETS_FOLDER - ); - if (folderResult.isPresent()) { - folder = folderResult.get(); - } else { - return Collections.emptyList(); - } + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); } - return folderRepo - .getDocumentFolderEntries( - folder, - firstResult, - maxResults, - filterTerm - ) - .stream() - .map(entry -> buildDocumentFolderRowModel(section, entry)) - .collect(Collectors.toList()); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentSection section = stepService.getContentSection(); + final Folder folder; + if (folderPath.isEmpty()) { + folder = section.getRootDocumentsFolder(); + } else { + final Optional folderResult = folderRepo.findByPath( + section, folderPath, FolderType.ASSETS_FOLDER + ); + if (folderResult.isPresent()) { + folder = folderResult.get(); + } else { + return Collections.emptyList(); + } + } + + return folderRepo + .getDocumentFolderEntries( + folder, + firstResult, + maxResults, + filterTerm + ) + .stream() + .map(entry -> buildDocumentFolderRowModel(section, entry)) + .collect(Collectors.toList()); + } else { + throw new ForbiddenException(); + } } /** * Show all documents of a content section filtered by their name. * - * @param firstResult The index of the first result to show. - * @param maxResults The maximum number of results to show. - * @param searchTerm An optional search term applied to the names of the - * docuemnts. + * @param sectionIdentifier + * @param documentPath + * @param firstResult The index of the first result to show. + * @param maxResults The maximum number of results to show. + * @param searchTerm An optional search term applied to the names of + * the docuemnts. * * @return A list of matching documents/content items as JSON. */ @@ -493,23 +588,51 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Produces(MediaType.APPLICATION_JSON) @Transactional(Transactional.TxType.REQUIRED) public List findDocuments( - @QueryParam("firstResult") @DefaultValue("0") final int firstResult, - @QueryParam("maxResults") @DefaultValue("20") final int maxResults, - @QueryParam("searchTerm") @DefaultValue("") final String searchTerm + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @QueryParam("firstResult") + @DefaultValue("0") + final int firstResult, + @QueryParam("maxResults") + @DefaultValue("20") + final int maxResults, + @QueryParam("searchTerm") + @DefaultValue("") + final String searchTerm ) { - return itemRepo.findByNameAndContentSection(searchTerm, section) - .stream() - .map(asset -> buildDocumentFolderRowModel(section, asset)) - .collect(Collectors.toList()); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException + | DocumentNotFoundException ex) { + throw new NotFoundException(ex.getMessage()); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentSection section = stepService.getContentSection(); + return itemRepo.findByNameAndContentSection(searchTerm, section) + .stream() + .map(asset -> buildDocumentFolderRowModel(section, asset)) + .collect(Collectors.toList()); + } else { + throw new ForbiddenException(); + } } /** * Adds a new attachment list. * - * @param name The name of the list. - * @param title The title of the list for the language returned by {@link GlobalizationHelper#getNegotiatedLocale() - * } . - * @param description The description of the list of the default locale {@link GlobalizationHelper#getNegotiatedLocale(). + * @param sectionIdentifier + * @param documentPath + * @param name The name of the list. + * @param title The title of the list for the language returned + * by {@link GlobalizationHelper#getNegotiatedLocale() + * } . + * @param description The description of the list of the default + * locale {@link GlobalizationHelper#getNegotiatedLocale(). * * @return A redirect to the list of attachment lists. */ @@ -517,32 +640,58 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/@add") @Transactional(Transactional.TxType.REQUIRED) public String addAttachmentList( - @FormParam("listName") final String name, - @FormParam("listTitle") final String title, - @FormParam("listDescription") final String description + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @FormParam("listName") + final String name, + @FormParam("listTitle") + final String title, + @FormParam("listDescription") + final String description ) { - final AttachmentList list = listManager.createAttachmentList( - document, name - ); - list.getTitle().addValue( - globalizationHelper.getNegotiatedLocale(), title - ); - list.getDescription().addValue( - globalizationHelper.getNegotiatedLocale(), description - ); - listRepo.save(list); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final ContentItem document = stepService.getDocument(); + final AttachmentList list = listManager.createAttachmentList( + document, name + ); + list.getTitle().addValue( + globalizationHelper.getNegotiatedLocale(), title + ); + list.getDescription().addValue( + globalizationHelper.getNegotiatedLocale(), description + ); + listRepo.save(list); + return stepService + .buildRedirectPathForStep( + getClass(), + String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Shows the details of an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * * @return The template for the details view. @@ -551,23 +700,48 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/@details") @Transactional(Transactional.TxType.REQUIRED) public String showAttachmentListDetails( - @PathParam("attachmentListIdentifier") final String listIdentifierParam + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - listDetailsModel.setAttachmentList(listResult.get()); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return "org/librecms/ui/documents/relatedinfo-attachmentlist-details.xhtml"; + listDetailsModel.setAttachmentList(listResult.get()); + + return "org/librecms/ui/documents/relatedinfo-attachmentlist-details.xhtml"; + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list to update. * @param name The new name of the list. * @@ -577,32 +751,55 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/@update") @Transactional(Transactional.TxType.REQUIRED) public String updateAttachmentList( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("listName") final String name + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @FormParam("listName") + final String name ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.setName(name); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.setName(name); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), + String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes an attachment list and all item attachment of the list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list to remove. * @param confirm The value of the confirm parameter. Must * contain {@code true} (as string not as @@ -614,31 +811,52 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/@remove") @Transactional(Transactional.TxType.REQUIRED) public String removeAttachmentList( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("confirm") final String confirm + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @FormParam("confirm") + final String confirm ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - if ("true".equalsIgnoreCase(confirm)) { - listManager.removeAttachmentList(listResult.get()); - } + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if ("true".equalsIgnoreCase(confirm)) { + listManager.removeAttachmentList(listResult.get()); + } + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Adds a localized title to an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the new title value. * @param value The value of the new title value. @@ -649,33 +867,57 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/title/@add") @Transactional(Transactional.TxType.REQUIRED) public String addAttachmentListTitle( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @FormParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getTitle().addValue(new Locale(localeParam), value); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getTitle().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), + String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized title value of an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the title value to update. * @param value The new title value. @@ -686,68 +928,115 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/title/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String updateAttachmentListTitle( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getTitle().addValue(new Locale(localeParam), value); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getTitle().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), + String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized title value of an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the title value to remove. * * @return A redirect to the details view of the attachment list. */ @POST - @Path("/attachmentlists/{attachmentListIdentifier}/title/@remove/{locale}") + @Path( + "/attachmentlists/{attachmentListIdentifier}/title/@remove/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String removeAttachmentListTitle( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("locale") final String localeParam + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getTitle().removeValue(new Locale(localeParam)); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getTitle().removeValue(new Locale(localeParam)); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Adds a localized description to an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the new description value. * @param value The value of the new description value. @@ -758,33 +1047,56 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/description/@add") @Transactional(Transactional.TxType.REQUIRED) public String addAttachmentListDescription( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @FormParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getDescription().addValue(new Locale(localeParam), value); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getDescription().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized description value of an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the description value to update. * @param value The new description value. @@ -796,33 +1108,56 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/description/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String updateAttachmentListDescription( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getDescription().addValue(new Locale(localeParam), value); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getDescription().addValue(new Locale(localeParam), value); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized description value of an attachment list. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list. * @param localeParam The locale of the description value to remove. * @@ -833,32 +1168,54 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/description/@remove/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String removeAttachmentListDescription( - @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("locale") final String localeParam + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("attachmentListIdentifier") + final String listIdentifierParam, + @PathParam("locale") + final String localeParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - list.getDescription().removeValue(new Locale(localeParam)); - listRepo.save(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s/attachmentslists/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT, - list.getName() - ); + final AttachmentList list = listResult.get(); + list.getDescription().removeValue(new Locale(localeParam)); + listRepo.save(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Create new attachment. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list to which the * attachment is added. * @param assetUuid The asset to use for the attachment. @@ -869,67 +1226,113 @@ public class RelatedInfoStep implements MvcAuthoringStep { @Path("/attachmentlists/{attachmentListIdentifier}/attachments") @Transactional(Transactional.TxType.REQUIRED) public String createAttachment( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("assetUuid") final String assetUuid + @FormParam("assetUuid") + final String assetUuid ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional assetResult = assetRepo.findByUuid(assetUuid); - if (!assetResult.isPresent()) { - models.put("section", section.getLabel()); - models.put("assetUuid", assetUuid); - return "org/librecms/ui/documents/asset-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final Asset asset = assetResult.get(); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - attachmentManager.attachAsset(asset, list); + final Optional assetResult = assetRepo.findByUuid(assetUuid); + if (!assetResult.isPresent()) { + models + .put("section", stepService.getContentSection().getLabel()); + models.put("assetUuid", assetUuid); + return "org/librecms/ui/documents/asset-not-found.xhtml"; + } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Asset asset = assetResult.get(); + + attachmentManager.attachAsset(asset, list); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Shows the form for creating a new internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list to which the * attachment is added. * * @return The template for the form for creating a new internal link. */ @GET - @Path("/attachmentlists/{attachmentListIdentifier}/internal-links/@create") + @Path( + "/attachmentlists/{attachmentListIdentifier}/internal-links/@create") @Transactional(Transactional.TxType.REQUIRED) public String createInternalLink( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - models.put("attachmentList", list.getName()); - return "org/librecms/ui/documents/relatedinfo-create-internallink.xhtml"; + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + models.put("attachmentList", list.getName()); + + return "org/librecms/ui/documents/relatedinfo-create-internallink.xhtml"; + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Create a new internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the list to which the * attachment is added. * @param targetItemUuid The UUID of the target item of the internal @@ -941,48 +1344,72 @@ public class RelatedInfoStep implements MvcAuthoringStep { * @return A redirect to the list of attachment lists and attachments. */ @POST - @Path("/attachmentlists/{attachmentListIdentifier}/internal-links/@create") + @Path( + "/attachmentlists/{attachmentListIdentifier}/internal-links/@create") @Transactional(Transactional.TxType.REQUIRED) public String createInternalLink( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @FormParam("targetItemUuid") final String targetItemUuid, - @FormParam("title") final String title + @FormParam("targetItemUuid") + final String targetItemUuid, + @FormParam("title") + final String title ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional itemResult = itemRepo.findByUuid( - targetItemUuid - ); - if (!itemResult.isPresent()) { - models.put("targetItemUuid", targetItemUuid); - return "org/librecms/ui/documents/target-item-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final RelatedLink relatedLink = new RelatedLink(); - relatedLink.getTitle().addValue( - globalizationHelper.getNegotiatedLocale(), title - ); - relatedLink.setTargetItem(document); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - attachmentManager.attachAsset(relatedLink, list); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Optional itemResult = itemRepo.findByUuid( + targetItemUuid + ); + if (!itemResult.isPresent()) { + models.put("targetItemUuid", targetItemUuid); + return "org/librecms/ui/documents/target-item-not-found.xhtml"; + } + + final RelatedLink relatedLink = new RelatedLink(); + relatedLink.getTitle().addValue( + globalizationHelper.getNegotiatedLocale(), title + ); + relatedLink.setTargetItem(stepService.getDocument()); + + attachmentManager.attachAsset(relatedLink, list); + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Show the details of an internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the link belongs. * @param internalLinkUuid The UUID of the link. @@ -996,44 +1423,69 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}/@details") @Transactional(Transactional.TxType.REQUIRED) public String showInternalLinkDetails( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("internalLinkUuid") final String internalLinkUuid + @PathParam("internalLinkUuid") + final String internalLinkUuid ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional linkResult = list - .getAttachments() - .stream() - .map(ItemAttachment::getAsset) - .filter(asset -> asset instanceof RelatedLink) - .map(asset -> (RelatedLink) asset) - .filter(link -> link.getUuid().equals(internalLinkUuid)) - .findAny(); - - if (!linkResult.isPresent()) { - models.put("contentItem", itemManager.getItemPath(document)); - models.put("listIdentifier", listIdentifierParam); - models.put("internalLinkUuid", internalLinkUuid); - return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final RelatedLink link = linkResult.get(); - internalLinkDetailsModel.setListIdentifier(list.getName()); - internalLinkDetailsModel.setInternalLink(link); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return "org/librecms/ui/documents/relatedinfo-internallink-details.xhtml"; + final Optional linkResult = list + .getAttachments() + .stream() + .map(ItemAttachment::getAsset) + .filter(asset -> asset instanceof RelatedLink) + .map(asset -> (RelatedLink) asset) + .filter(link -> link.getUuid().equals(internalLinkUuid)) + .findAny(); + + if (!linkResult.isPresent()) { + models.put("contentItem", stepService.getDocumentPath()); + models.put("listIdentifier", listIdentifierParam); + models.put("internalLinkUuid", internalLinkUuid); + return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + } + + final RelatedLink link = linkResult.get(); + internalLinkDetailsModel.setListIdentifier(list.getName()); + internalLinkDetailsModel.setInternalLink(link); + + return "org/librecms/ui/documents/relatedinfo-internallink-details.xhtml"; + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates the target of an internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the link belongs. * @param internalLinkUuid The UUID of the link. @@ -1047,58 +1499,81 @@ public class RelatedInfoStep implements MvcAuthoringStep { ) @Transactional(Transactional.TxType.REQUIRED) public String updateInternalLinkTarget( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("internalLinkUuid") final String internalLinkUuid, - @FormParam("targetItemUuid") final String targetItemUuid + @PathParam("internalLinkUuid") + final String internalLinkUuid, + @FormParam("targetItemUuid") + final String targetItemUuid ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional itemResult = itemRepo.findByUuid( - targetItemUuid - ); - if (!itemResult.isPresent()) { - models.put("targetItemUuid", targetItemUuid); - return "org/librecms/ui/documents/target-item-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final Optional linkResult = list - .getAttachments() - .stream() - .map(ItemAttachment::getAsset) - .filter(asset -> asset instanceof RelatedLink) - .map(asset -> (RelatedLink) asset) - .filter(link -> link.getUuid().equals(internalLinkUuid)) - .findAny(); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - if (!linkResult.isPresent()) { - models.put("contentItem", itemManager.getItemPath(document)); - models.put("listIdentifier", listIdentifierParam); - models.put("internalLinkUuid", internalLinkUuid); - return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + final Optional itemResult = itemRepo.findByUuid( + targetItemUuid + ); + if (!itemResult.isPresent()) { + models.put("targetItemUuid", targetItemUuid); + return "org/librecms/ui/documents/target-item-not-found.xhtml"; + } + + final Optional linkResult = list + .getAttachments() + .stream() + .map(ItemAttachment::getAsset) + .filter(asset -> asset instanceof RelatedLink) + .map(asset -> (RelatedLink) asset) + .filter(link -> link.getUuid().equals(internalLinkUuid)) + .findAny(); + + if (!linkResult.isPresent()) { + models.put("contentItem", stepService.getDocumentPath()); + models.put("listIdentifier", listIdentifierParam); + models.put("internalLinkUuid", internalLinkUuid); + return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + } + + final RelatedLink link = linkResult.get(); + link.setTargetItem(itemResult.get()); + assetRepo.save(link); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); } - - final RelatedLink link = linkResult.get(); - link.setTargetItem(itemResult.get()); - assetRepo.save(link); - - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); } /** * Add a localized title value to an internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the link belongs. * @param internalLinkUuid The UUID of the link. @@ -1112,52 +1587,76 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}/title/@add") @Transactional(Transactional.TxType.REQUIRED) public String addInternalLinkTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("internalLinkUuid") final String internalLinkUuid, - @FormParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam("internalLinkUuid") + final String internalLinkUuid, + @FormParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional linkResult = list - .getAttachments() - .stream() - .map(ItemAttachment::getAsset) - .filter(asset -> asset instanceof RelatedLink) - .map(asset -> (RelatedLink) asset) - .filter(link -> link.getUuid().equals(internalLinkUuid)) - .findAny(); - - if (!linkResult.isPresent()) { - models.put("contentItem", itemManager.getItemPath(document)); - models.put("listIdentifierParam", listIdentifierParam); - models.put("internalLinkUuid", internalLinkUuid); - return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final RelatedLink link = linkResult.get(); - final Locale locale = new Locale(localeParam); - link.getTitle().addValue(locale, value); - assetRepo.save(link); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Optional linkResult = list + .getAttachments() + .stream() + .map(ItemAttachment::getAsset) + .filter(asset -> asset instanceof RelatedLink) + .map(asset -> (RelatedLink) asset) + .filter(link -> link.getUuid().equals(internalLinkUuid)) + .findAny(); + + if (!linkResult.isPresent()) { + models.put("contentItem", stepService.getDocumentPath()); + models.put("listIdentifierParam", listIdentifierParam); + models.put("internalLinkUuid", internalLinkUuid); + return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + } + + final RelatedLink link = linkResult.get(); + final Locale locale = new Locale(localeParam); + link.getTitle().addValue(locale, value); + assetRepo.save(link); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized title value of an internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the link belongs. * @param internalLinkUuid The UUID of the link. @@ -1171,52 +1670,76 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/internal-links/{interalLinkUuid}/title/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String updateInternalLinkTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("internalLinkUuid") final String internalLinkUuid, - @PathParam("locale") final String localeParam, - @FormParam("value") final String value + @PathParam("internalLinkUuid") + final String internalLinkUuid, + @PathParam("locale") + final String localeParam, + @FormParam("value") + final String value ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional linkResult = list - .getAttachments() - .stream() - .map(ItemAttachment::getAsset) - .filter(asset -> asset instanceof RelatedLink) - .map(asset -> (RelatedLink) asset) - .filter(link -> link.getUuid().equals(internalLinkUuid)) - .findAny(); - - if (!linkResult.isPresent()) { - models.put("contentItem", itemManager.getItemPath(document)); - models.put("listIdentifierParam", listIdentifierParam); - models.put("internalLinkUuid", internalLinkUuid); - return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final RelatedLink link = linkResult.get(); - final Locale locale = new Locale(localeParam); - link.getTitle().addValue(locale, value); - assetRepo.save(link); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Optional linkResult = list + .getAttachments() + .stream() + .map(ItemAttachment::getAsset) + .filter(asset -> asset instanceof RelatedLink) + .map(asset -> (RelatedLink) asset) + .filter(link -> link.getUuid().equals(internalLinkUuid)) + .findAny(); + + if (!linkResult.isPresent()) { + models.put("contentItem", stepService.getDocumentPath()); + models.put("listIdentifierParam", listIdentifierParam); + models.put("internalLinkUuid", internalLinkUuid); + return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + } + + final RelatedLink link = linkResult.get(); + final Locale locale = new Locale(localeParam); + link.getTitle().addValue(locale, value); + assetRepo.save(link); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized title value from an internal link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the link belongs. * @param internalLinkUuid The UUID of the link. @@ -1230,52 +1753,75 @@ public class RelatedInfoStep implements MvcAuthoringStep { ) @Transactional(Transactional.TxType.REQUIRED) public String removeInternalLinkTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("internalLinkUuid") final String internalLinkUuid, - @PathParam("locale") final String localeParam + @PathParam("internalLinkUuid") + final String internalLinkUuid, + @PathParam("locale") + final String localeParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional linkResult = list - .getAttachments() - .stream() - .map(ItemAttachment::getAsset) - .filter(asset -> asset instanceof RelatedLink) - .map(asset -> (RelatedLink) asset) - .filter(link -> link.getUuid().equals(internalLinkUuid)) - .findAny(); - - if (!linkResult.isPresent()) { - models.put("contentItem", itemManager.getItemPath(document)); - models.put("listIdentifierParam", listIdentifierParam); - models.put("internalLinkUuid", internalLinkUuid); - return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final RelatedLink link = linkResult.get(); - final Locale locale = new Locale(localeParam); - link.getTitle().removeValue(locale); - assetRepo.save(link); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Optional linkResult = list + .getAttachments() + .stream() + .map(ItemAttachment::getAsset) + .filter(asset -> asset instanceof RelatedLink) + .map(asset -> (RelatedLink) asset) + .filter(link -> link.getUuid().equals(internalLinkUuid)) + .findAny(); + + if (!linkResult.isPresent()) { + models.put("contentItem", stepService.getDocumentPath()); + models.put("listIdentifierParam", listIdentifierParam); + models.put("internalLinkUuid", internalLinkUuid); + return "org/librecms/ui/documents/internal-link-asset-not-found.xhtml"; + } + + final RelatedLink link = linkResult.get(); + final Locale locale = new Locale(localeParam); + link.getTitle().removeValue(locale); + assetRepo.save(link); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** - * Removes an attachment from an {@link AttachmentList}. The {@link Asset} - * of the attachment will not be deleted unless it is a related link. + * Removes an attachment from an {@link AttachmentList}.The {@link Asset} of + * the attachment will not be deleted unless it is a related link. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifier of the {@link AttachmentList} * to which the attachment belongs. * @param attachmentUuid The UUID of the attachment to remove. @@ -1290,45 +1836,69 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/attachments/{attachmentUuid}/@remove") @Transactional(Transactional.TxType.REQUIRED) public String removeAttachment( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("attachmentUuid") final String attachmentUuid, - @FormParam("confirm") final String confirm + @PathParam("attachmentUuid") + final String attachmentUuid, + @FormParam("confirm") + final String confirm ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - final Optional> result = list - .getAttachments() - .stream() - .filter(attachment -> attachment.getUuid().equals(attachmentUuid)) - .findFirst(); - - if (result.isPresent() && "true".equalsIgnoreCase(confirm)) { - final Asset asset = result.get().getAsset(); - attachmentManager.unattachAsset(asset, list); - if (asset instanceof RelatedLink - && ((RelatedLink) asset).getTargetItem() != null) { - assetRepo.delete(asset); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); } - } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent() && "true".equalsIgnoreCase(confirm)) { + final Asset asset = result.get().getAsset(); + attachmentManager.unattachAsset(asset, list); + if (asset instanceof RelatedLink + && ((RelatedLink) asset).getTargetItem() != null) { + assetRepo.delete(asset); + } + } + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Move an attachment list one position up. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifer of the list to move. * * @return A redirect to list of attachment lists. @@ -1338,63 +1908,104 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/@moveUp") @Transactional(Transactional.TxType.REQUIRED) public String moveListUp( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - listManager.moveUp(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + listManager.moveUp(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Move an attachment list one position down. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifer of the list to move. * * @return A redirect to list of attachment lists. */ @POST - @Path( - "/attachmentlists/{attachmentListIdentifier}/@moveDown") + @Path("/attachmentlists/{attachmentListIdentifier}/@moveDown") @Transactional(Transactional.TxType.REQUIRED) public String moveListDown( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - final AttachmentList list = listResult.get(); - listManager.moveDown(list); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + listManager.moveDown(list); + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Move an attachment one position up. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifer to which the attachment belongs. * @param attachmentUuid The UUID of the attachment ot move. * @@ -1405,41 +2016,64 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/attachments/{attachmentUuid}/@moveUp") @Transactional(Transactional.TxType.REQUIRED) public String moveAttachmentUp( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("attachmentUuid") final String attachmentUuid + @PathParam("attachmentUuid") + final String attachmentUuid ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional> result = list - .getAttachments() - .stream() - .filter(attachment -> attachment.getUuid().equals(attachmentUuid)) - .findFirst(); - - if (result.isPresent()) { - final ItemAttachment attachment = result.get(); - final Asset asset = attachment.getAsset(); - attachmentManager.moveUp(asset, list); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent()) { + final ItemAttachment attachment = result.get(); + final Asset asset = attachment.getAsset(); + attachmentManager.moveUp(asset, list); + } + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Move an attachment one position down. * + * @param sectionIdentifier + * @param documentPath * @param listIdentifierParam The identifer to which the attachment belongs. * @param attachmentUuid The UUID of the attachment ot move. * @@ -1450,36 +2084,57 @@ public class RelatedInfoStep implements MvcAuthoringStep { "/attachmentlists/{attachmentListIdentifier}/attachments/{attachmentUuid}/@moveDown") @Transactional(Transactional.TxType.REQUIRED) public String moveAttachmentDown( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("attachmentListIdentifier") final String listIdentifierParam, - @PathParam("attachmentUuid") final String attachmentUuid + @PathParam("attachmentUuid") + final String attachmentUuid ) { - final Optional listResult = findAttachmentList( - listIdentifierParam - ); - if (!listResult.isPresent()) { - return showAttachmentListNotFound(listIdentifierParam); - } - final AttachmentList list = listResult.get(); - - final Optional> result = list - .getAttachments() - .stream() - .filter(attachment -> attachment.getUuid().equals(attachmentUuid)) - .findFirst(); - - if (result.isPresent()) { - final ItemAttachment attachment = result.get(); - final Asset asset = attachment.getAsset(); - attachmentManager.moveDown(asset, list); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - return String.format( - "redirect:/%s/@documents/%s/@authoringsteps/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (permissionChecker.isPermitted( + ItemPrivileges.EDIT, stepService.getDocument() + )) { + final Optional listResult = findAttachmentList( + listIdentifierParam + ); + if (!listResult.isPresent()) { + return showAttachmentListNotFound(listIdentifierParam); + } + final AttachmentList list = listResult.get(); + + final Optional> result = list + .getAttachments() + .stream() + .filter(attachment -> attachment.getUuid() + .equals(attachmentUuid)) + .findFirst(); + + if (result.isPresent()) { + final ItemAttachment attachment = result.get(); + final Asset asset = attachment.getAsset(); + attachmentManager.moveDown(asset, list); + } + + return stepService.buildRedirectPathForStep( + getClass(), String.format("/attachmentlists/%s", list.getName()) + ); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** @@ -1494,6 +2149,7 @@ public class RelatedInfoStep implements MvcAuthoringStep { private Optional findAttachmentList( final String attachmentListIdentifier ) { + final ContentItem document = stepService.getDocument(); final Identifier identifier = identifierParser.parseIdentifier( attachmentListIdentifier ); @@ -1530,7 +2186,7 @@ public class RelatedInfoStep implements MvcAuthoringStep { * @return The template for the "attachment list not found" page. */ private String showAttachmentListNotFound(final String listIdentifier) { - models.put("contentItem", itemManager.getItemPath(document)); + models.put("contentItem", stepService.getDocumentPath()); models.put("listIdentifier", listIdentifier); return "org/librecms/ui/documents/attachmentlist-not-found.xhtml"; } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/SelectedDocumentModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/SelectedDocumentModel.java index 4a19d8187..d07cebaf5 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/SelectedDocumentModel.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/documents/SelectedDocumentModel.java @@ -19,6 +19,7 @@ package org.librecms.ui.contentsections.documents; import org.libreccm.l10n.GlobalizationHelper; +import org.libreccm.l10n.LocalizedTextsUtil; import org.libreccm.security.PermissionChecker; import org.libreccm.security.Shiro; import org.libreccm.workflow.AssignableTask; @@ -39,9 +40,9 @@ import java.util.Objects; import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; -import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; +import javax.ws.rs.Path; /** * Model/named bean providing data about the currently selected document for @@ -60,10 +61,10 @@ public class SelectedDocumentModel { private AssignableTaskManager taskManager; /** - * All available authoring steps. + * Checks if authoring step classes have all required annotations. */ @Inject - private Instance authoringSteps; + private AuthoringStepsValidator stepsValidator; /** * Used to get information about the item. @@ -307,14 +308,14 @@ public class SelectedDocumentModel { .getClass() .getAnnotation(MvcAuthoringKit.class); - final Class[] stepClasses = authoringKit - .authoringSteps(); + final List> stepClasses = Arrays + .stream(authoringKit.authoringSteps()) + .collect(Collectors.toList()); - return Arrays - .stream(stepClasses) - .map(authoringSteps::select) - .filter(instance -> instance.isResolvable()) - .map(Instance::get) + return stepClasses + .stream() + .filter(stepsValidator::validateAuthoringStep) + .filter(stepClass -> stepsValidator.supportsItem(stepClass, item)) .map(this::buildAuthoringStepListEntry) .collect(Collectors.toList()); } @@ -328,27 +329,38 @@ public class SelectedDocumentModel { * @return An {@link AuthoringStepListEntry} for the step. */ private AuthoringStepListEntry buildAuthoringStepListEntry( - final MvcAuthoringStep step + final Class authoringStepClass ) { + final MvcAuthoringStep stepAnnotation = authoringStepClass + .getAnnotation(MvcAuthoringStep.class); + final Path pathAnnotation = authoringStepClass.getAnnotation( + Path.class + ); + final LocalizedTextsUtil textsUtil = globalizationHelper + .getLocalizedTextsUtil(stepAnnotation.bundle()); final AuthoringStepListEntry entry = new AuthoringStepListEntry(); - entry.setDescription(step.getDescription()); - entry.setLabel(step.getLabel()); - entry.setPathFragment(readAuthoringStepPathFragment(step)); + entry.setDescription(textsUtil.getText(stepAnnotation.descriptionKey())); + entry.setLabel(textsUtil.getText(stepAnnotation.labelKey())); + entry.setPath(createStepPath(pathAnnotation.value())); return entry; } - /** - * Helper method for retrieving the path fragment of an authoring step. - * - * @param step The step. - * - * @return The path fragment of the step. - */ - private String readAuthoringStepPathFragment(final MvcAuthoringStep step) { - return step - .getClass() - .getAnnotation(AuthoringStepPathFragment.class) - .value(); + private String createStepPath(final String path) { + return path + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM + ), + item.getContentType().getContentSection().getLabel() + ) + .replace( + String.format( + "{%s}", + MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM + ), + itemPath + ); } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleCreateStep.java index 56ea35cba..9e1c37efd 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleCreateStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleCreateStep.java @@ -285,7 +285,7 @@ public class MvcArticleCreateStep itemRepo.save(article); return String.format( - "redirect:/%s/documents/%s/%s/@edit/basicproperties", + "redirect:/%s/documents/%s/%s/@articleproperties", getContentSectionLabel(), getFolderPath(), name diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticlePropertiesStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticlePropertiesStep.java index 7d8cccae0..5b5602061 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticlePropertiesStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticlePropertiesStep.java @@ -18,25 +18,24 @@ */ package org.librecms.ui.contenttypes; -import org.libreccm.core.UnexpectedErrorException; import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.LocalizedString; -import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemRepository; -import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.FolderManager; import org.librecms.contenttypes.Article; import org.librecms.ui.contentsections.ItemPermissionChecker; -import org.librecms.ui.contentsections.documents.AuthoringStepPathFragment; +import org.librecms.ui.contentsections.documents.ContentSectionNotFoundException; +import org.librecms.ui.contentsections.documents.DocumentNotFoundException; import org.librecms.ui.contentsections.documents.DocumentUi; +import org.librecms.ui.contentsections.documents.MvcAuthoringStep; +import org.librecms.ui.contentsections.documents.MvcAuthoringStepService; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.ws.rs.Path; -import org.librecms.ui.contentsections.documents.MvcAuthoringStep; - import java.util.List; import java.util.Locale; import java.util.Map; @@ -44,30 +43,36 @@ import java.util.Set; import java.util.stream.Collectors; import javax.inject.Named; +import javax.mvc.Controller; +import javax.mvc.Models; import javax.transaction.Transactional; +import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PathParam; + /** * Authoring step for editing the basic properties of an a {@link Article}. * * @author Jens Pelzetter */ @RequestScoped -@Path("/") -@AuthoringStepPathFragment(MvcArticlePropertiesStep.PATH_FRAGMENT) +@Path(MvcAuthoringSteps.PATH_PREFIX + "/@basicproperties") +@Controller @Named("CmsArticlePropertiesStep") -public class MvcArticlePropertiesStep implements MvcAuthoringStep { +@MvcAuthoringStep( + bundle = ArticleStepsConstants.BUNDLE, + descriptionKey = "authoringsteps.basicproperties.description", + labelKey = "authoringsteps.basicproperties.label", + supportedDocumentType = Article.class +) +public class MvcArticlePropertiesStep { @Inject private ArticleMessageBundle articleMessageBundle; - /** - * The path fragment of the step. - */ - static final String PATH_FRAGMENT = "basicproperties"; - /** * Used for retrieving and saving the article. */ @@ -98,97 +103,38 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Inject private ItemPermissionChecker itemPermissionChecker; - /** - * The current content section. - */ - private ContentSection section; + @Inject + private Models models; - /** - * The {@link Article} to edit. - */ - private Article document; + @Inject + private MvcAuthoringStepService stepService; - @Override - public Class supportedDocumentType() { - return Article.class; - } - - @Override - public String getLabel() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.basicproperties.label"); - } - - @Override - public String getDescription() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.basicproperties.description"); - } - - @Override - public String getBundle() { - return ArticleStepsConstants.BUNDLE; - } - - @Override - public ContentSection getContentSection() { - return section; - } - - @Override - public String getContentSectionLabel() { - return section.getLabel(); - } - - @Override - public String getContentSectionTitle() { - return globalizationHelper - .getValueFromLocalizedString(section.getTitle()); - } - - @Override - public void setContentSection(final ContentSection section) { - this.section = section; - } - - @Override - public ContentItem getContentItem() { - return document; - } - - @Override - public String getContentItemPath() { - return itemManager.getItemPath(document); - } - - @Override - public String getContentItemTitle() { - return globalizationHelper - .getValueFromLocalizedString(document.getTitle()); - } - - @Override - public void setContentItem(final ContentItem document) { - if (!(document instanceof Article)) { - throw new UnexpectedErrorException("Not an article."); + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - this.document = (Article) document; - } - @Override - public String showStep() { - if (itemPermissionChecker.canEditItem(document)) { + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { return "org/librecms/ui/contenttypes/article/article-basic-properties.xhtml"; } else { return documentUi.showAccessDenied( - section, - document, + stepService.getContentSection(), + stepService.getDocument(), articleMessageBundle.getMessage("article.edit.denied") ); } - } /** @@ -197,31 +143,54 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { * @return The display name of the current article. */ public String getName() { - return document.getDisplayName(); + return stepService.getDocument().getDisplayName(); } /** * Updates the name of the current article. * - * @param name The new name of the article. + * @param sectionIdentifier + * @param documentPath + * @param name * * @return A redirect to this authoring step. */ @POST @Path("/name") @Transactional(Transactional.TxType.REQUIRED) - public String updateName(@FormParam("name") final String name) { - document.setDisplayName(name); - itemRepo.save(document); - return String.format( - "redirect:/@documents/%s/%s/%s/@edit/%s", - section.getLabel(), - folderManager.getFolderPath( - itemManager.getItemFolder(document).get() - ), - name, - PATH_FRAGMENT - ); + public String updateName( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @FormParam("name") @DefaultValue("") final String name + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } + + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + if (name.isEmpty() || name.matches("\\s*")) { + models.put("nameMissing", true); + + return showStep(sectionIdentifier, documentPath); + } + + stepService.getDocument().setDisplayName(name); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** @@ -230,7 +199,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { * @return The values of the localized title of the article. */ public Map getTitleValues() { - return document + return stepService + .getDocument() .getTitle() .getValues() .entrySet() @@ -249,7 +219,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { * @return The locales for which no localized title has been defined yet. */ public List getUnusedTitleLocales() { - final Set titleLocales = document + final Set titleLocales = stepService + .getDocument() .getTitle() .getAvailableLocales(); return globalizationHelper @@ -261,37 +232,56 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { } /** - * Adds a localized title to the article. + * Updates a localized title of the article. * - * @param localeParam The locale of the title. - * @param value The title value. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated title value. * * @return A redirect to this authoring step. */ @POST - @Path("/title/@add") + @Path("/title/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String addTitle( - @FormParam("locale") final String localeParam, + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, + @PathParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getTitle().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getTitle().addValue(locale, value); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized title of the article. * - * @param localeParam The locale to update. - * @param value The updated title value. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated title value. * * @return A redirect to this authoring step. */ @@ -299,25 +289,41 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Path("/title/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String editTitle( + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getTitle().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getTitle().removeValue(locale); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized title of the article. * - * @param localeParam The locale to remove. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to remove. * * @return A redirect to this authoring step. */ @@ -325,18 +331,33 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Path("/title/@remove/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String removeTitle( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam ) { - final Locale locale = new Locale(localeParam); - document.getTitle().removeValue(locale); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getTitle().removeValue(locale); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** @@ -346,7 +367,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { * yet. */ public List getUnusedDescriptionLocales() { - final Set descriptionLocales = document + final Set descriptionLocales = stepService + .getDocument() .getDescription() .getAvailableLocales(); return globalizationHelper @@ -363,7 +385,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { * @return The values of the localized description of the article. */ public Map getDescriptionValues() { - return document + return stepService + .getDocument() .getDescription() .getValues() .entrySet() @@ -379,8 +402,10 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { /** * Adds a localized description to the article. * - * @param localeParam The locale of the description. - * @param value The description value. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the description. + * @param value The description value. * * @return A redirect to this authoring step. */ @@ -388,26 +413,43 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Path("/title/@add") @Transactional(Transactional.TxType.REQUIRED) public String addDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @FormParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getDescription().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getDescription().addValue(locale, value); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized description of the article. * - * @param localeParam The locale to update. - * @param value The updated description value. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale to update. + * @param value The updated description value. * * @return A redirect to this authoring step. */ @@ -415,24 +457,41 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Path("/title/@edit/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String editDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getDescription().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getDescription().addValue(locale, value); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized description of the article. * + * @param sectionIdentifier + * @param documentPath * @param localeParam The locale to remove. * * @return A redirect to this authoring step. @@ -441,18 +500,33 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep { @Path("/title/@remove/{locale}") @Transactional(Transactional.TxType.REQUIRED) public String removeDescription( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam ) { - final Locale locale = new Locale(localeParam); - document.getDescription().removeValue(locale); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:%s/@documents/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + stepService.getDocument().getDescription().removeValue(locale); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } } diff --git a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleTextBodyStep.java b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleTextBodyStep.java index 24f7fa455..41c23bb1a 100644 --- a/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleTextBodyStep.java +++ b/ccm-cms/src/main/java/org/librecms/ui/contenttypes/MvcArticleTextBodyStep.java @@ -18,22 +18,21 @@ */ package org.librecms.ui.contenttypes; -import org.libreccm.core.UnexpectedErrorException; import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.LocalizedString; -import org.librecms.contentsection.ContentItem; -import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemRepository; -import org.librecms.contentsection.ContentSection; import org.librecms.contenttypes.Article; import org.librecms.ui.contentsections.ItemPermissionChecker; -import org.librecms.ui.contentsections.documents.AuthoringStepPathFragment; +import org.librecms.ui.contentsections.documents.ContentSectionNotFoundException; +import org.librecms.ui.contentsections.documents.DocumentNotFoundException; import org.librecms.ui.contentsections.documents.DocumentUi; import javax.mvc.Controller; import javax.ws.rs.Path; import org.librecms.ui.contentsections.documents.MvcAuthoringStep; +import org.librecms.ui.contentsections.documents.MvcAuthoringStepService; +import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; import java.util.List; import java.util.Locale; @@ -44,7 +43,9 @@ import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; +import javax.transaction.Transactional; import javax.ws.rs.FormParam; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PathParam; @@ -54,31 +55,26 @@ import javax.ws.rs.PathParam; * @author Jens Pelzetter */ @RequestScoped -@Path("/") -@AuthoringStepPathFragment(MvcArticleTextBodyStep.PATH_FRAGMENT) +@Path(MvcAuthoringSteps.PATH_PREFIX + "/@text") +@Controller @Named("CmsArticleTextBodyStep") -public class MvcArticleTextBodyStep implements MvcAuthoringStep { +@MvcAuthoringStep( + bundle = ArticleStepsConstants.BUNDLE, + descriptionKey = "authoringsteps.text.description", + labelKey = "authoringsteps.text.label", + supportedDocumentType = Article.class +) +public class MvcArticleTextBodyStep { @Inject private ArticleMessageBundle articleMessageBundle; - /** - * The path fragment of the step. - */ - static final String PATH_FRAGMENT = "text"; - /** * Used for retrieving and saving the article. */ @Inject private ContentItemRepository itemRepo; - /** - * Provides functions for working with content items. - */ - @Inject - private ContentItemManager itemManager; - @Inject private DocumentUi documentUi; @@ -91,93 +87,32 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep { @Inject private ItemPermissionChecker itemPermissionChecker; - /** - * The current content section. - */ - private ContentSection section; + @Inject + private MvcAuthoringStepService stepService; - /** - * The {@link Article} to edit. - */ - private Article document; - - @Override - public Class supportedDocumentType() { - return Article.class; - } - - @Override - public String getLabel() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.text.label"); - } - - @Override - public String getDescription() { - return globalizationHelper - .getLocalizedTextsUtil(getBundle()) - .getText("authoringsteps.text.description"); - } - - @Override - public String getBundle() { - return ArticleStepsConstants.BUNDLE; - } - - @Override - public ContentSection getContentSection() { - return section; - } - - @Override - public String getContentSectionLabel() { - return section.getLabel(); - } - - @Override - public String getContentSectionTitle() { - return globalizationHelper - .getValueFromLocalizedString(section.getTitle()); - } - - @Override - public void setContentSection(final ContentSection section) { - this.section = section; - } - - @Override - public ContentItem getContentItem() { - return document; - } - - @Override - public String getContentItemPath() { - return itemManager.getItemPath(document); - } - - @Override - public String getContentItemTitle() { - return globalizationHelper - .getValueFromLocalizedString(document.getTitle()); - } - - @Override - public void setContentItem(final ContentItem document) { - if (!(document instanceof Article)) { - throw new UnexpectedErrorException("Not an article."); + @GET + @Path("/") + @Transactional(Transactional.TxType.REQUIRED) + public String showStep( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath + ) { + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); } - this.document = (Article) document; - } - @Override - public String showStep() { - if (itemPermissionChecker.canEditItem(document)) { + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { return "org/librecms/ui/contenttypes/article/article-text.xhtml"; } else { return documentUi.showAccessDenied( - section, - document, + stepService.getContentSection(), + stepService.getDocument(), articleMessageBundle.getMessage("article.edit.denied") ); } @@ -189,7 +124,7 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep { * @return The localized values of the main text. */ public Map getTextValues() { - return document + return getDocument() .getText() .getValues() .entrySet() @@ -208,7 +143,7 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep { * @return The locales for which the main text has not been defined yet. */ public List getUnusedLocales() { - final Set locales = document + final Set locales = getDocument() .getText() .getAvailableLocales(); return globalizationHelper @@ -222,77 +157,135 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep { /** * Adds a localized main text. * - * @param localeParam The locale of the text. - * @param value The text. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. + * @param value The text. * * @return A redirect to this authoring step. */ @POST @Path("/@add") + @Transactional(Transactional.TxType.REQUIRED) public String addTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @FormParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getText().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:/@documents/%s/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getText().addValue(locale, value); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Updates a localized main text. * - * @param localeParam The locale of the text. - * @param value The text. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. + * @param value The text. * * @return A redirect to this authoring step. */ @POST @Path("/@edit/{locale}") + @Transactional(Transactional.TxType.REQUIRED) public String editTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam, @FormParam("value") final String value ) { - final Locale locale = new Locale(localeParam); - document.getText().addValue(locale, value); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:/@documents/%s/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getText().addValue(locale, value); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } } /** * Removes a localized main text. * - * @param localeParam The locale of the text. + * @param sectionIdentifier + * @param documentPath + * @param localeParam The locale of the text. * * @return A redirect to this authoring step. */ @POST @Path("/@remove/{locale}") + @Transactional(Transactional.TxType.REQUIRED) public String remvoeTextValue( + @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM) + final String sectionIdentifier, + @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM) + final String documentPath, @PathParam("locale") final String localeParam ) { - final Locale locale = new Locale(localeParam); - document.getText().removeValue(locale); - itemRepo.save(document); + try { + stepService.setSectionAndDocument(sectionIdentifier, documentPath); + } catch (ContentSectionNotFoundException ex) { + return ex.showErrorMessage(); + } catch (DocumentNotFoundException ex) { + return ex.showErrorMessage(); + } - return String.format( - "redirect:/@documents/%s/%s/@edit/%s", - section.getLabel(), - getContentItemPath(), - PATH_FRAGMENT - ); + if (itemPermissionChecker.canEditItem(stepService.getDocument())) { + final Locale locale = new Locale(localeParam); + getDocument().getText().removeValue(locale); + itemRepo.save(stepService.getDocument()); + + return stepService.buildRedirectPathForStep(getClass()); + } else { + return documentUi.showAccessDenied( + stepService.getContentSection(), + stepService.getDocument(), + stepService.getLabel(getClass()) + ); + } + } + + private Article getDocument() { + return (Article) stepService.getDocument(); } } diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml index 9e43c8b7e..38b53e924 100644 --- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml @@ -100,10 +100,10 @@
    -
  • +
  • + href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{step.path}"> #{step.label}
  • diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/example-authoring.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/example-authoring.xhtml new file mode 100644 index 000000000..591f6155e --- /dev/null +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/example-authoring.xhtml @@ -0,0 +1,19 @@ +]> + + + + +
    +

    Example Authoring Step

    + + #{sectionIdentifier} + #{documentPath} +
    +
    + +
    +