AuthoringSteps are now ordinary MVC controllers

pull/10/head
Jens Pelzetter 2021-04-24 20:36:47 +02:00
parent a31e242c48
commit 6c9b3748d0
24 changed files with 2811 additions and 1743 deletions

View File

@ -43,7 +43,6 @@ import org.librecms.ui.contenttypes.MvcArticleCreateStep;
import org.librecms.ui.contenttypes.MvcArticlePropertiesStep; import org.librecms.ui.contenttypes.MvcArticlePropertiesStep;
import org.librecms.ui.contenttypes.MvcArticleTextBodyStep; import org.librecms.ui.contenttypes.MvcArticleTextBodyStep;
import org.librecms.ui.contentsections.documents.MvcAuthoringKit; import org.librecms.ui.contentsections.documents.MvcAuthoringKit;
import org.librecms.ui.contentsections.documents.MvcAuthoringKitStep;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
@ -79,14 +78,8 @@ import javax.xml.bind.annotation.XmlRootElement;
@MvcAuthoringKit( @MvcAuthoringKit(
createStep = MvcArticleCreateStep.class, createStep = MvcArticleCreateStep.class,
authoringSteps = { authoringSteps = {
@MvcAuthoringKitStep( MvcArticlePropertiesStep.class,
path = "basic-properties", MvcArticleTextBodyStep.class
authoringStep = MvcArticlePropertiesStep.class
),
@MvcAuthoringKitStep(
path = "basic-properties",
authoringStep = MvcArticleTextBodyStep.class
)
} }
) )
@XmlRootElement(name = "article", namespace = CMS_XML_NS) @XmlRootElement(name = "article", namespace = CMS_XML_NS)

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
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<String, String[]> 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<String, String[]> 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]
);
}
}

View File

@ -21,14 +21,23 @@ package org.librecms.ui.contentsections;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.ui.IsAuthenticatedFilter; 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.DocumentController;
import org.librecms.ui.contentsections.documents.DocumentLifecyclesController; import org.librecms.ui.contentsections.documents.DocumentLifecyclesController;
import org.librecms.ui.contentsections.documents.DocumentWorkflowController; import org.librecms.ui.contentsections.documents.DocumentWorkflowController;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; 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.ApplicationPath;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application; import javax.ws.rs.core.Application;
/** /**
@ -43,6 +52,13 @@ public class ContentSectionApplication extends Application {
ContentSectionApplication.class ContentSectionApplication.class
); );
@Inject
private AuthoringStepsValidator stepsValidator;
@Inject
@Any
private Instance<MvcAuthoringSteps> authoringSteps;
@Override @Override
public Set<Class<?>> getClasses() { public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>(); final Set<Class<?>> classes = new HashSet<>();
@ -57,6 +73,9 @@ public class ContentSectionApplication extends Application {
classes.add(ContentSectionController.class); classes.add(ContentSectionController.class);
classes.add(DocumentFolderController.class); classes.add(DocumentFolderController.class);
classes.add(DocumentController.class); classes.add(DocumentController.class);
classes.addAll(getAuthoringSteps());
classes.add(DocumentLifecyclesController.class); classes.add(DocumentLifecyclesController.class);
classes.add(DocumentWorkflowController.class); classes.add(DocumentWorkflowController.class);
classes.add(IsAuthenticatedFilter.class); classes.add(IsAuthenticatedFilter.class);
@ -64,6 +83,13 @@ public class ContentSectionApplication extends Application {
return classes; return classes;
} }
private Set<Class<?>> getAuthoringSteps() {
return authoringSteps
.stream()
.map(MvcAuthoringSteps::getClasses)
.flatMap(Set::stream)
.filter(stepsValidator::validateAuthoringStep)
.collect(Collectors.toSet());
}
} }

View File

@ -41,7 +41,7 @@ public class AuthoringStepListEntry {
/** /**
* The path fragment of the authoring step. * The path fragment of the authoring step.
*/ */
private String pathFragment; private String path;
public String getLabel() { public String getLabel() {
return label; return label;
@ -59,12 +59,12 @@ public class AuthoringStepListEntry {
this.description = description; this.description = description;
} }
public String getPathFragment() { public String getPath() {
return pathFragment; return path;
} }
public void setPathFragment(final String pathFragment) { public void setPath(final String path) {
this.pathFragment = pathFragment; this.path = path;
} }
} }

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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()
);
}
}

View File

@ -29,8 +29,6 @@ import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.PermissionChecker; import org.libreccm.security.PermissionChecker;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import java.util.ArrayList; import java.util.ArrayList;
@ -42,10 +40,11 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.mvc.Controller;
import javax.mvc.Models; import javax.mvc.Models;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.POST; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
@ -56,12 +55,16 @@ import javax.ws.rs.PathParam;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path(MvcAuthoringSteps.PATH_PREFIX + "categorization")
@AuthoringStepPathFragment(CategorizationStep.PATH_FRAGMENT) @Controller
@Named("CmsCategorizationStep") @Named("CmsCategorizationStep")
public class CategorizationStep implements MvcAuthoringStep { @MvcAuthoringStep(
bundle = DefaultAuthoringStepConstants.BUNDLE,
static final String PATH_FRAGMENT = "categorization"; descriptionKey = "authoringsteps.categorization.description",
labelKey = "authoringsteps.categorization.label",
supportedDocumentType = ContentItem.class
)
public class CategorizationStep {
@Inject @Inject
private CategoryManager categoryManager; private CategoryManager categoryManager;
@ -72,9 +75,6 @@ public class CategorizationStep implements MvcAuthoringStep {
@Inject @Inject
private IdentifierParser identifierParser; private IdentifierParser identifierParser;
@Inject
private ContentItemManager itemManager;
@Inject @Inject
private GlobalizationHelper globalizationHelper; private GlobalizationHelper globalizationHelper;
@ -84,130 +84,35 @@ public class CategorizationStep implements MvcAuthoringStep {
@Inject @Inject
private PermissionChecker permissionChecker; private PermissionChecker permissionChecker;
/** @Inject
* The current content section. private MvcAuthoringStepService stepService;
*/
private ContentSection section;
/** @GET
* The current document. @Path("/")
*/ @Transactional(Transactional.TxType.REQUIRED)
private ContentItem document; 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();
}
/** if (permissionChecker.isPermitted(
* {@inheritDoc} ItemPrivileges.CATEGORIZE, stepService.getDocument()
*/ )) {
@Override
public Class<? extends ContentItem> 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)) {
return "org/librecms/ui/documents/categorization.xhtml"; return "org/librecms/ui/documents/categorization.xhtml";
} else { } else {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
section, stepService.getContentSection(),
document, stepService.getDocument(),
getLabel() stepService.getLabel(getClass())
); );
} }
} }
@ -223,7 +128,8 @@ public class CategorizationStep implements MvcAuthoringStep {
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<CategorizationTree> getCategorizationTrees() { public List<CategorizationTree> getCategorizationTrees() {
return section return stepService
.getContentSection()
.getDomains() .getDomains()
.stream() .stream()
.map(DomainOwnership::getDomain) .map(DomainOwnership::getDomain)
@ -234,27 +140,33 @@ public class CategorizationStep implements MvcAuthoringStep {
/** /**
* Update the categorization of the current item. * Update the categorization of the current item.
* *
* @param domainIdentifierParam The identifier for category system to use. * @param parameterPath The identifier for category system to use.
* @param assignedCategoriesParam The UUIDs of the categories assigned to * @param parameters The parameters of the request. The map must contain
* the current content item. * a value with the key {@code assignedCategories}.
*
* *
* @return A redirect to the categorization step. * @return A redirect to the categorization step.
*/ */
@POST @MvcAuthoringAction(
@Path("/{domainIdentifier}") method = MvcAuthoringActionMethod.POST,
path = "/domains/"
)
@Path("/domains/{domain}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String updateCategorization( public String updateCategorization(
@PathParam("domainIdentifierParam") final String domainIdentifierParam, @PathParam("domain")
@FormParam("assignedCategories") final String domainParam,
@FormParam("assignedCategories")
final Set<String> assignedCategoriesParam final Set<String> assignedCategoriesParam
) { ) {
final Identifier domainIdentifier = identifierParser.parseIdentifier( final Identifier domainIdentifier = identifierParser.parseIdentifier(
domainIdentifierParam domainParam
); );
final Optional<Domain> domainResult; final Optional<Domain> domainResult;
switch (domainIdentifier.getType()) { switch (domainIdentifier.getType()) {
case ID: case ID:
domainResult = section domainResult = stepService
.getContentSection()
.getDomains() .getDomains()
.stream() .stream()
.map(DomainOwnership::getDomain) .map(DomainOwnership::getDomain)
@ -264,7 +176,8 @@ public class CategorizationStep implements MvcAuthoringStep {
).findAny(); ).findAny();
break; break;
case UUID: case UUID:
domainResult = section domainResult = stepService
.getContentSection()
.getDomains() .getDomains()
.stream() .stream()
.map(DomainOwnership::getDomain) .map(DomainOwnership::getDomain)
@ -275,7 +188,8 @@ public class CategorizationStep implements MvcAuthoringStep {
).findAny(); ).findAny();
break; break;
default: default:
domainResult = section domainResult = stepService
.getContentSection()
.getDomains() .getDomains()
.stream() .stream()
.map(DomainOwnership::getDomain) .map(DomainOwnership::getDomain)
@ -287,20 +201,16 @@ public class CategorizationStep implements MvcAuthoringStep {
} }
if (!domainResult.isPresent()) { if (!domainResult.isPresent()) {
models.put("section", section.getLabel()); models.put("section", stepService.getContentSection().getLabel());
models.put("domainIdentifier", domainIdentifierParam); models.put("domainIdentifier", domainIdentifier);
return "org/librecms/ui/documents/categorization-domain-not-found.xhtml"; return "org/librecms/ui/documents/categorization-domain-not-found.xhtml";
} }
final Domain domain = domainResult.get(); updateAssignedCategories(
updateAssignedCategories(domain.getRoot(), assignedCategoriesParam); domainResult.get().getRoot(), assignedCategoriesParam
return String.format(
"redirect:/%s/@documents/%s/@authoringsteps/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT
); );
return stepService.buildRedirectPathForStep(getClass());
} }
/** /**
@ -320,6 +230,7 @@ public class CategorizationStep implements MvcAuthoringStep {
final Category category, final Category category,
final Set<String> assignedCategoriesParam final Set<String> assignedCategoriesParam
) { ) {
final ContentItem document = stepService.getDocument();
if (assignedCategoriesParam.contains(category.getUuid()) if (assignedCategoriesParam.contains(category.getUuid())
&& !categoryManager.isAssignedToCategory(category, document)) { && !categoryManager.isAssignedToCategory(category, document)) {
categoryManager.addObjectToCategory(document, category); categoryManager.addObjectToCategory(document, category);
@ -416,6 +327,7 @@ public class CategorizationStep implements MvcAuthoringStep {
final Category category final Category category
) { ) {
final CategorizationTreeNode node = new CategorizationTreeNode(); final CategorizationTreeNode node = new CategorizationTreeNode();
final ContentItem document = stepService.getDocument();
node.setAssigned(categoryManager.isAssignedToCategory( node.setAssigned(categoryManager.isAssignedToCategory(
category, document) category, document)
); );

View File

@ -18,30 +18,26 @@
*/ */
package org.librecms.ui.contentsections.documents; package org.librecms.ui.contentsections.documents;
import java.lang.annotation.Retention; import java.util.HashSet;
import java.lang.annotation.RetentionPolicy; import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
/** /**
* Metadata about an authoring step.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@Retention(RetentionPolicy.RUNTIME) @ApplicationScoped
public @interface MvcAuthoringKitStep { public class CmsMvcAuthoringSteps implements MvcAuthoringSteps {
/**
* 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<? extends MvcAuthoringStep> authoringStep();
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(ExampleAuthoringStep.class);
return classes;
}
} }

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class ContentSectionNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
private final String errorMessageTemplate;
/**
* Creates a new instance of <code>ContentSectionNotFound</code> without
* detail message.
*
* @param errorMessageTemplate Template for the error message.
*/
ContentSectionNotFoundException(final String errorMessageTemplate) {
super();
this.errorMessageTemplate = errorMessageTemplate;
}
/**
* Constructs an instance of <code>ContentSectionNotFound</code> 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;
}
}

View File

@ -75,6 +75,9 @@ import javax.ws.rs.core.MediaType;
@Controller @Controller
public class DocumentController { public class DocumentController {
@Inject
private AuthoringStepsValidator stepsValidator;
/** /**
* Item manager instance for performing operations on {@link ContentItem}s. * Item manager instance for performing operations on {@link ContentItem}s.
*/ */
@ -326,7 +329,7 @@ public class DocumentController {
) { ) {
final CreateStepResult result = findCreateStep( final CreateStepResult result = findCreateStep(
sectionIdentifier, sectionIdentifier,
folderPath, folderPath,
documentType documentType
); );
@ -337,141 +340,140 @@ public class DocumentController {
} }
} }
/** // /**
* Redirects to the first authoring step for the document identified by the // * Redirects to the first authoring step for the document identified by the
* provided path. // * provided path.
* // *
* @param sectionIdentifier The identifier of the current content section. // * @param sectionIdentifier The identifier of the current content section.
* @param documentPath The path of the document. // * @param documentPath The path of the document.
* // *
* @return A redirect to the first authoring step of the document, or the // * @return A redirect to the first authoring step of the document, or the
* {@link DocumentNotFound} pseudo authoring step. // * {@link DocumentNotFound} pseudo authoring step.
*/ // */
@GET // @GET
@Path("/{documentPath:(.+)?}") // @Path("/{documentPath:(.+)?}")
@AuthorizationRequired // @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) // @Transactional(Transactional.TxType.REQUIRED)
public String showEditDocument( // public String showEditDocument(
@PathParam("sectionIdentifier") final String sectionIdentifier, // @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath // @PathParam("documentPath") final String documentPath
) { // ) {
final Optional<ContentSection> sectionResult = sectionsUi // final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifier); // .findContentSection(sectionIdentifier);
if (!sectionResult.isPresent()) { // if (!sectionResult.isPresent()) {
sectionsUi.showContentSectionNotFound(sectionIdentifier); // sectionsUi.showContentSectionNotFound(sectionIdentifier);
} // }
final ContentSection section = sectionResult.get(); // final ContentSection section = sectionResult.get();
//
final Optional<ContentItem> itemResult = itemRepo // final Optional<ContentItem> itemResult = itemRepo
.findByPath(section, documentPath); // .findByPath(section, documentPath);
if (!itemResult.isPresent()) { // if (!itemResult.isPresent()) {
models.put("section", section.getLabel()); // models.put("section", section.getLabel());
models.put("documentPath", documentPath); // models.put("documentPath", documentPath);
documentUi.showDocumentNotFound(section, documentPath); // documentUi.showDocumentNotFound(section, documentPath);
} // }
final ContentItem item = itemResult.get(); // final ContentItem item = itemResult.get();
if (!itemPermissionChecker.canEditItem(item)) { // if (!itemPermissionChecker.canEditItem(item)) {
return documentUi.showAccessDenied( // return documentUi.showAccessDenied(
section, // section,
item, // item,
defaultStepsMessageBundle.getMessage("edit_denied") // defaultStepsMessageBundle.getMessage("edit_denied")
); // );
} // }
//
return String.format( // return String.format(
"redirect:/%s/documents/%s/@authoringsteps/%s", // "redirect:/%s/documents/%s/@authoringsteps/%s",
sectionIdentifier, // sectionIdentifier,
documentPath, // documentPath,
findFirstAuthoringStep(item) // findFirstAuthoringStep(item)
); // );
} // }
//
/** // /**
* Redirect requests for an authoring step to the subresource of the // * Redirect requests for an authoring step to the subresource of the
* authoring step. // * authoring step.
* // *
* @param sectionIdentifier The identifier of the current content // * @param sectionIdentifier The identifier of the current content
* section. // * section.
* @param documentPath The path of the document to edit. // * @param documentPath The path of the document to edit.
* @param authoringStepIdentifier The identifier/path fragment of the // * @param authoringStepIdentifier The identifier/path fragment of the
* authoring step. // * authoring step.
* @param request // * @param request
* // *
* @return The authoring step subresource. // * @return The authoring step subresource.
*/ // */
@GET // @GET
@Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}") // @Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}")
@AuthorizationRequired // @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) // @Transactional(Transactional.TxType.REQUIRED)
public String showEditDocument( // public String showEditDocument(
@PathParam("sectionIdentifier") final String sectionIdentifier, // @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, // @PathParam("documentPath") final String documentPath,
@PathParam("authoringStep") final String authoringStepIdentifier, // @PathParam("authoringStep") final String authoringStepIdentifier,
@Context final HttpServletRequest request // @Context final HttpServletRequest request
) { // ) {
final Optional<ContentSection> sectionResult = sectionsUi // final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifier); // .findContentSection(sectionIdentifier);
if (!sectionResult.isPresent()) { // if (!sectionResult.isPresent()) {
models.put("sectionIdentifier", sectionIdentifier); // models.put("sectionIdentifier", sectionIdentifier);
return sectionsUi.showContentSectionNotFound(sectionIdentifier); // return sectionsUi.showContentSectionNotFound(sectionIdentifier);
} // }
final ContentSection section = sectionResult.get(); // final ContentSection section = sectionResult.get();
//
final Optional<ContentItem> itemResult = itemRepo // final Optional<ContentItem> itemResult = itemRepo
.findByPath(section, documentPath); // .findByPath(section, documentPath);
if (!itemResult.isPresent()) { // if (!itemResult.isPresent()) {
models.put("section", section.getLabel()); // models.put("section", section.getLabel());
models.put("documentPath", documentPath); // models.put("documentPath", documentPath);
return documentUi.showDocumentNotFound(section, documentPath); // return documentUi.showDocumentNotFound(section, documentPath);
} // }
final ContentItem item = itemResult.get(); // final ContentItem item = itemResult.get();
if (!itemPermissionChecker.canEditItem(item)) { // if (!itemPermissionChecker.canEditItem(item)) {
models.put("section", section.getLabel()); // models.put("section", section.getLabel());
models.put("documentPath", itemManager.getItemFolder(item)); // models.put("documentPath", itemManager.getItemFolder(item));
models.put( // models.put(
"step", defaultStepsMessageBundle.getMessage("edit_step") // "step", defaultStepsMessageBundle.getMessage("edit_step")
); // );
return documentUi.showAccessDenied( // return documentUi.showAccessDenied(
section, documentPath, documentPath // section, documentPath, documentPath
); // );
} // }
//
final Instance<MvcAuthoringStep> instance = authoringSteps // final Instance<MvcAuthoringStep> instance = authoringSteps
.select( // .select(
new AuthoringStepPathFragmentLiteral( // new AuthoringStepPathFragmentLiteral(
authoringStepIdentifier // authoringStepIdentifier
) // )
); // );
if (instance.isUnsatisfied() || instance.isAmbiguous()) { // if (instance.isUnsatisfied() || instance.isAmbiguous()) {
models.put("section", section.getLabel()); // models.put("section", section.getLabel());
models.put("documentPath", documentPath); // models.put("documentPath", documentPath);
models.put("authoringStep", authoringStepIdentifier); // models.put("authoringStep", authoringStepIdentifier);
return showAuthoringStepNotAvailable(authoringStepIdentifier); // return showAuthoringStepNotAvailable(authoringStepIdentifier);
} // }
final MvcAuthoringStep authoringStep = instance.get(); // final MvcAuthoringStep authoringStep = instance.get();
//
if (!authoringStep.supportedDocumentType().isAssignableFrom(item // if (!authoringStep.supportedDocumentType().isAssignableFrom(item
.getClass())) { // .getClass())) {
models.put("section", section.getLabel()); // models.put("section", section.getLabel());
models.put("documentPath", documentPath); // models.put("documentPath", documentPath);
models.put("documentType", item.getClass().getName()); // models.put("documentType", item.getClass().getName());
models.put("authoringStep", authoringStepIdentifier); // models.put("authoringStep", authoringStepIdentifier);
return showUnsupportedDocumentType( // return showUnsupportedDocumentType(
authoringStepIdentifier, // authoringStepIdentifier,
item.getClass().getName() // item.getClass().getName()
); // );
} // }
//
models.put("authoringStep", authoringStepIdentifier); // models.put("authoringStep", authoringStepIdentifier);
//
selectedDocumentModel.setContentItem(item); // selectedDocumentModel.setContentItem(item);
//
authoringStep.setContentSection(section); // authoringStep.setContentSection(section);
authoringStep.setContentItem(item); // authoringStep.setContentItem(item);
//
return authoringStep.showStep(); // return authoringStep.showStep();
} // }
@POST @POST
@Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}") @Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}")
@AuthorizationRequired @AuthorizationRequired
@ -780,21 +782,17 @@ public class DocumentController {
* *
* @return A list of authoring steps for the provided item. * @return A list of authoring steps for the provided item.
*/ */
private List<MvcAuthoringStep> readAuthoringSteps( private List<Class<?>> readAuthoringSteps(
final ContentItem item final ContentItem item
) { ) {
final MvcAuthoringKit authoringKit = item final MvcAuthoringKit authoringKit = item
.getClass() .getClass()
.getAnnotation(MvcAuthoringKit.class); .getAnnotation(MvcAuthoringKit.class);
final Class<? extends MvcAuthoringStep>[] stepClasses = authoringKit
.authoringSteps();
return Arrays return Arrays
.stream(stepClasses) .stream(authoringKit.authoringSteps())
.map(authoringSteps::select) .filter(stepsValidator::validateAuthoringStep)
.filter(instance -> instance.isResolvable()) .filter(stepClass -> stepsValidator.supportsItem(stepClass, item))
.map(Instance::get)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -804,14 +802,30 @@ public class DocumentController {
* *
* @param item The content item. * @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) { private String findFirstAuthoringStep(final ContentItem item) {
final List<MvcAuthoringStep> steps = readAuthoringSteps(item); final List<Class<?>> steps = readAuthoringSteps(item);
final MvcAuthoringStep firstStep = steps.get(0); final Class<?> firstStep = steps.get(0);
return firstStep.getClass().getName(); 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)
);
} }
/** /**

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class DocumentNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
private final String errorMessageTemplate;
/**
* Creates a new instance of <code>DocumentNotFoundException</code> without
* detail message.
*
* @param errorMessageTemplate Template for the error message.
*/
public DocumentNotFoundException(final String errorMessageTemplate) {
super();
this.errorMessageTemplate = errorMessageTemplate;
}
/**
* Constructs an instance of <code>DocumentNotFoundException</code> 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;
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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<ContentSection> 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";
}
}

View File

@ -56,7 +56,8 @@ public @interface MvcAuthoringAction {
/** /**
* The path fragment for invoking the action. The value of this parameter is * 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. * @return The path fragment for invoking the action.
*/ */

View File

@ -49,7 +49,7 @@ public @interface MvcAuthoringKit {
* *
* @return The authoring steps for the annotated document type. * @return The authoring steps for the annotated document type.
*/ */
MvcAuthoringKitStep[] authoringSteps(); Class<?>[] authoringSteps();
/** /**
* If set to {@code true} some authoring steps like categorization or * If set to {@code true} some authoring steps like categorization or

View File

@ -20,13 +20,17 @@ package org.librecms.ui.contentsections.documents;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.librecms.contentsection.ContentItem; 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; import javax.inject.Named;
/** /**
* Metadata of an authoring step for documents (content items).
*
* An authoring step for a document (content item). Implementing classes are * 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) * 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}, * }. An implementation must be a named CDI bean (annotated with {@link Named},
@ -37,7 +41,32 @@ import javax.inject.Named;
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
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. * Authoring steps only support a specific type, and all subtypes.
@ -46,109 +75,4 @@ public interface MvcAuthoringStep {
*/ */
Class<? extends ContentItem> supportedDocumentType(); Class<? extends ContentItem> 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<String, String[]> formParameters);
} }

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@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("");
}
}

View File

@ -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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
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<Class<?>> getClasses();
}

View File

@ -19,14 +19,11 @@
package org.librecms.ui.contentsections.documents; package org.librecms.ui.contentsections.documents;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired;
import org.librecms.contentsection.ContentItem; import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentSection;
import org.librecms.lifecycle.Lifecycle; import org.librecms.lifecycle.Lifecycle;
import org.librecms.lifecycle.LifecycleDefinition; import org.librecms.lifecycle.LifecycleDefinition;
import org.librecms.lifecycle.LifecycleDefinitionRepository; import org.librecms.lifecycle.LifecycleDefinitionRepository;
import org.librecms.ui.contentsections.AbstractMvcAuthoringStep;
import org.librecms.ui.contentsections.ItemPermissionChecker; import org.librecms.ui.contentsections.ItemPermissionChecker;
import java.time.LocalDate; import java.time.LocalDate;
@ -37,17 +34,20 @@ import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.util.Date; import java.util.Date;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.mvc.Controller;
import javax.mvc.Models; import javax.mvc.Models;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/** /**
* Authoring step (part of the default steps) for publishing a * Authoring step (part of the default steps) for publishing a
@ -57,22 +57,19 @@ import javax.ws.rs.Path;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path(MvcAuthoringSteps.PATH_PREFIX + "publish")
@AuthoringStepPathFragment(PublishStep.PATH_FRAGMENT) @Controller
@Named("CmsPublishStep") @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 TEMPLATE
private static final String SELECTED_LIFECYCLE_DEF_UUID = "org/librecms/ui/documenttypes/publish.xhtml";
= "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";
/** /**
* The path fragment of the publish step. * The path fragment of the publish step.
@ -100,84 +97,54 @@ public class PublishStep extends AbstractMvcAuthoringStep {
@Inject @Inject
private Models models; private Models models;
@Inject
private MvcAuthoringStepService stepService;
@Override @GET
public Class<? extends ContentItem> supportedDocumentType() { @Path("/")
return ContentItem.class; @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 final ContentItem document = stepService.getDocument();
public String getLabel() { if (itemPermissionChecker.canPublishItems(stepService.getDocument())) {
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 String lifecycleDefUuid; final String lifecycleDefUuid;
if (itemManager.isLive(getContentItem())) { if (itemManager.isLive(document)) {
lifecycleDefUuid = getContentItem() lifecycleDefUuid = document
.getLifecycle() .getLifecycle()
.getDefinition() .getDefinition()
.getUuid(); .getUuid();
} else { } else {
lifecycleDefUuid = getContentItem() lifecycleDefUuid = document
.getContentType() .getContentType()
.getDefaultLifecycle() .getDefaultLifecycle()
.getUuid(); .getUuid();
} }
models.put("lifecycleDefinitionUuid", lifecycleDefUuid); models.put("lifecycleDefinitionUuid", lifecycleDefUuid);
return "org/librecms/ui/documents/publish.xhtml"; return TEMPLATE;
} else { } else {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
getContentSection(), stepService.getContentSection(),
getContentItem(), stepService.getDocument(),
defaultStepsMessageBundle.getMessage( 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<String, String[]> 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 * 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 * 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) @Transactional(Transactional.TxType.REQUIRED)
public String getAssignedLifecycleLabel() { public String getAssignedLifecycleLabel() {
return Optional return Optional
.ofNullable(getContentItem().getLifecycle()) .ofNullable(stepService.getDocument().getLifecycle())
.map(Lifecycle::getDefinition) .map(Lifecycle::getDefinition)
.map(LifecycleDefinition::getLabel) .map(LifecycleDefinition::getLabel)
.map(globalizationHelper::getValueFromLocalizedString) .map(globalizationHelper::getValueFromLocalizedString)
@ -210,7 +177,7 @@ public class PublishStep extends AbstractMvcAuthoringStep {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String getAssignedLifecycleDecription() { public String getAssignedLifecycleDecription() {
return Optional return Optional
.ofNullable(getContentItem().getLifecycle()) .ofNullable(stepService.getDocument().getLifecycle())
.map(Lifecycle::getDefinition) .map(Lifecycle::getDefinition)
.map(LifecycleDefinition::getDescription) .map(LifecycleDefinition::getDescription)
.map(globalizationHelper::getValueFromLocalizedString) .map(globalizationHelper::getValueFromLocalizedString)
@ -222,6 +189,9 @@ public class PublishStep extends AbstractMvcAuthoringStep {
* {@code selectedLifecycleDefUuid} is ignored.The apply a new lifecycle the * {@code selectedLifecycleDefUuid} is ignored.The apply a new lifecycle the
* document the unpublished first. * document the unpublished first.
* *
*
* @param sectionIdentifier
* @param documentPath
* @param selectedLifecycleDefUuid The ID of the lifecycle definition from * @param selectedLifecycleDefUuid The ID of the lifecycle definition from
* which the lifecycle for the item is * which the lifecycle for the item is
* created. * created.
@ -230,37 +200,55 @@ public class PublishStep extends AbstractMvcAuthoringStep {
* @param endDateParam * @param endDateParam
* @param endTimeParam * @param endTimeParam
* *
*
* @return A redirect the the publish step. * @return A redirect the the publish step.
*/ */
@MvcAuthoringAction( @POST
method = MvcAuthoringActionMethod.POST, @Path("/")
path = "/@publish"
)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String publish( public String publish(
final String parameterPath, @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final Map<String, String[]> parameters 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) { try {
models.put("missingLifecycleDefinitionUuid", true); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
models.put("startDateTime", startDateParam); } catch (ContentSectionNotFoundException ex) {
models.put("startDateTime", startTimeParam); return ex.showErrorMessage();
models.put("endDateTime", endDateParam); } catch (DocumentNotFoundException ex) {
models.put("endDateTime", endTimeParam); return ex.showErrorMessage();
return "org/librecms/ui/documenttypes/publish.xhtml";
} }
if (startDateParam == null
|| startDateParam.isEmpty() final ContentItem document = stepService.getDocument();
|| startTimeParam == null
|| startTimeParam.isEmpty()) { 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("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
models.put("missingStartDateTime", true); models.put("missingStartDateTime", true);
models.put("startDateTime", startDateParam); models.put("startDate", startDateParam);
models.put("startDateTime", startTimeParam); models.put("startTime", startTimeParam);
models.put("endDateTime", endDateParam); models.put("endDate", endDateParam);
models.put("endDateTime", endTimeParam); models.put("endTime", endTimeParam);
return "org/librecms/ui/documents/publish.xhtml";
return TEMPLATE;
} }
final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ISO_DATE final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ISO_DATE
@ -274,11 +262,12 @@ public class PublishStep extends AbstractMvcAuthoringStep {
} catch (DateTimeParseException ex) { } catch (DateTimeParseException ex) {
models.put("invalidStartDate", true); models.put("invalidStartDate", true);
models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
models.put("startDateTime", startDateParam); models.put("startDate", startDateParam);
models.put("startDateTime", startTimeParam); models.put("startTime", startTimeParam);
models.put("endDateTime", endDateParam); models.put("endDate", endDateParam);
models.put("endDateTime", endTimeParam); models.put("endTime", endTimeParam);
return "org/librecms/ui/documents/publish.xhtml";
return TEMPLATE;
} }
final LocalTime localStartTime; final LocalTime localStartTime;
@ -287,11 +276,12 @@ public class PublishStep extends AbstractMvcAuthoringStep {
} catch (DateTimeParseException ex) { } catch (DateTimeParseException ex) {
models.put("invalidStartTime", true); models.put("invalidStartTime", true);
models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
models.put("startDateTime", startDateParam); models.put("startDate", startDateParam);
models.put("startDateTime", startTimeParam); models.put("startTime", startTimeParam);
models.put("endDateTime", endDateParam); models.put("endDate", endDateParam);
models.put("endDateTime", endTimeParam); models.put("endTime", endTimeParam);
return "org/librecms/ui/documents/publish.xhtml";
return TEMPLATE;
} }
final LocalDateTime startLocalDateTime = LocalDateTime.of( final LocalDateTime startLocalDateTime = LocalDateTime.of(
@ -309,11 +299,12 @@ public class PublishStep extends AbstractMvcAuthoringStep {
} catch (DateTimeParseException ex) { } catch (DateTimeParseException ex) {
models.put("invalidEndDate", true); models.put("invalidEndDate", true);
models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
models.put("startDateTime", startDateParam); models.put("startDate", startDateParam);
models.put("startDateTime", startTimeParam); models.put("startTime", startTimeParam);
models.put("endDateTime", endDateParam); models.put("endDate", endDateParam);
models.put("endDateTime", endTimeParam); models.put("endTime", endTimeParam);
return "org/librecms/ui/documents/publish.xhtml";
return TEMPLATE;
} }
final LocalTime localEndTime; final LocalTime localEndTime;
@ -322,11 +313,12 @@ public class PublishStep extends AbstractMvcAuthoringStep {
} catch (DateTimeParseException ex) { } catch (DateTimeParseException ex) {
models.put("invalidEndTime", true); models.put("invalidEndTime", true);
models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
models.put("startDateTime", startDateParam); models.put("startDate", startDateParam);
models.put("startDateTime", startTimeParam); models.put("startTime", startTimeParam);
models.put("endDateTime", endDateParam); models.put("endDate", endDateParam);
models.put("endDateTime", endTimeParam); models.put("endTime", endTimeParam);
return "org/librecms/ui/documents/publish.xhtml";
return TEMPLATE;
} }
final LocalDateTime endLocalDateTime = LocalDateTime.of( final LocalDateTime endLocalDateTime = LocalDateTime.of(
@ -340,7 +332,7 @@ public class PublishStep extends AbstractMvcAuthoringStep {
if (!itemPermissionChecker.canPublishItems(document)) { if (!itemPermissionChecker.canPublishItems(document)) {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
section, stepService.getContentSection(),
document, document,
"item.publish" "item.publish"
); );
@ -354,13 +346,17 @@ public class PublishStep extends AbstractMvcAuthoringStep {
document, definition, startDateTime, endDateTime document, definition, startDateTime, endDateTime
); );
} else { } else {
itemManager.publish(document, startDateTime, endDateTime); itemManager
.publish(document, startDateTime, endDateTime);
} }
} else { } else {
final Optional<LifecycleDefinition> definitionResult final Optional<LifecycleDefinition> definitionResult
= lifecycleDefRepo.findByUuid(selectedLifecycleDefUuid); = lifecycleDefRepo.findByUuid(selectedLifecycleDefUuid);
if (!definitionResult.isPresent()) { if (!definitionResult.isPresent()) {
models.put("contentSection", section.getLabel()); models.put(
"contentSection",
stepService.getContentSection().getLabel()
);
models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid); models.put("lifecycleDefinitionUuid", selectedLifecycleDefUuid);
return "org/librecms/ui/documents/lifecycle-definition-not-found.xhtml"; return "org/librecms/ui/documents/lifecycle-definition-not-found.xhtml";
} }
@ -370,12 +366,7 @@ public class PublishStep extends AbstractMvcAuthoringStep {
); );
} }
return String.format( return stepService.buildRedirectPathForStep(getClass());
"redirect:/%s/@documents/%s/@authoringsteps/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT
);
} }
/** /**
@ -383,13 +374,16 @@ public class PublishStep extends AbstractMvcAuthoringStep {
* *
* @return A redirect to the publish step. * @return A redirect to the publish step.
*/ */
@POST @MvcAuthoringAction(
@Path("/@unpublish") method = MvcAuthoringActionMethod.POST,
path = "/@unpublish"
)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String unpublish() { public String unpublish() {
final ContentItem document = stepService.getDocument();
if (!itemPermissionChecker.canPublishItems(document)) { if (!itemPermissionChecker.canPublishItems(document)) {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
section, stepService.getContentSection(),
document, document,
"item.unpublish" "item.unpublish"
); );
@ -397,13 +391,7 @@ public class PublishStep extends AbstractMvcAuthoringStep {
itemManager.unpublish(document); itemManager.unpublish(document);
return String.format( return stepService.buildRedirectPathForStep(getClass());
"redirect:/%s/@documents/%s/@authoringsteps/%s",
section.getLabel(),
getContentItemPath(),
PATH_FRAGMENT
);
} }
} }

View File

@ -19,6 +19,7 @@
package org.librecms.ui.contentsections.documents; package org.librecms.ui.contentsections.documents;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedTextsUtil;
import org.libreccm.security.PermissionChecker; import org.libreccm.security.PermissionChecker;
import org.libreccm.security.Shiro; import org.libreccm.security.Shiro;
import org.libreccm.workflow.AssignableTask; import org.libreccm.workflow.AssignableTask;
@ -39,9 +40,9 @@ import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.ws.rs.Path;
/** /**
* Model/named bean providing data about the currently selected document for * Model/named bean providing data about the currently selected document for
@ -60,10 +61,10 @@ public class SelectedDocumentModel {
private AssignableTaskManager taskManager; private AssignableTaskManager taskManager;
/** /**
* All available authoring steps. * Checks if authoring step classes have all required annotations.
*/ */
@Inject @Inject
private Instance<MvcAuthoringStep> authoringSteps; private AuthoringStepsValidator stepsValidator;
/** /**
* Used to get information about the item. * Used to get information about the item.
@ -307,14 +308,14 @@ public class SelectedDocumentModel {
.getClass() .getClass()
.getAnnotation(MvcAuthoringKit.class); .getAnnotation(MvcAuthoringKit.class);
final Class<? extends MvcAuthoringStep>[] stepClasses = authoringKit final List<Class<?>> stepClasses = Arrays
.authoringSteps(); .stream(authoringKit.authoringSteps())
.collect(Collectors.toList());
return Arrays return stepClasses
.stream(stepClasses) .stream()
.map(authoringSteps::select) .filter(stepsValidator::validateAuthoringStep)
.filter(instance -> instance.isResolvable()) .filter(stepClass -> stepsValidator.supportsItem(stepClass, item))
.map(Instance::get)
.map(this::buildAuthoringStepListEntry) .map(this::buildAuthoringStepListEntry)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -328,27 +329,38 @@ public class SelectedDocumentModel {
* @return An {@link AuthoringStepListEntry} for the step. * @return An {@link AuthoringStepListEntry} for the step.
*/ */
private AuthoringStepListEntry buildAuthoringStepListEntry( 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(); final AuthoringStepListEntry entry = new AuthoringStepListEntry();
entry.setDescription(step.getDescription()); entry.setDescription(textsUtil.getText(stepAnnotation.descriptionKey()));
entry.setLabel(step.getLabel()); entry.setLabel(textsUtil.getText(stepAnnotation.labelKey()));
entry.setPathFragment(readAuthoringStepPathFragment(step)); entry.setPath(createStepPath(pathAnnotation.value()));
return entry; return entry;
} }
/** private String createStepPath(final String path) {
* Helper method for retrieving the path fragment of an authoring step. return path
* .replace(
* @param step The step. String.format(
* "{%s}",
* @return The path fragment of the step. MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM
*/ ),
private String readAuthoringStepPathFragment(final MvcAuthoringStep step) { item.getContentType().getContentSection().getLabel()
return step )
.getClass() .replace(
.getAnnotation(AuthoringStepPathFragment.class) String.format(
.value(); "{%s}",
MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM
),
itemPath
);
} }
} }

View File

@ -285,7 +285,7 @@ public class MvcArticleCreateStep
itemRepo.save(article); itemRepo.save(article);
return String.format( return String.format(
"redirect:/%s/documents/%s/%s/@edit/basicproperties", "redirect:/%s/documents/%s/%s/@articleproperties",
getContentSectionLabel(), getContentSectionLabel(),
getFolderPath(), getFolderPath(),
name name

View File

@ -18,25 +18,24 @@
*/ */
package org.librecms.ui.contenttypes; package org.librecms.ui.contenttypes;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager; import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.FolderManager; import org.librecms.contentsection.FolderManager;
import org.librecms.contenttypes.Article; import org.librecms.contenttypes.Article;
import org.librecms.ui.contentsections.ItemPermissionChecker; 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.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.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import org.librecms.ui.contentsections.documents.MvcAuthoringStep;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -44,30 +43,36 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Named; import javax.inject.Named;
import javax.mvc.Controller;
import javax.mvc.Models;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
/** /**
* Authoring step for editing the basic properties of an a {@link Article}. * Authoring step for editing the basic properties of an a {@link Article}.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path(MvcAuthoringSteps.PATH_PREFIX + "/@basicproperties")
@AuthoringStepPathFragment(MvcArticlePropertiesStep.PATH_FRAGMENT) @Controller
@Named("CmsArticlePropertiesStep") @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 @Inject
private ArticleMessageBundle articleMessageBundle; private ArticleMessageBundle articleMessageBundle;
/**
* The path fragment of the step.
*/
static final String PATH_FRAGMENT = "basicproperties";
/** /**
* Used for retrieving and saving the article. * Used for retrieving and saving the article.
*/ */
@ -98,97 +103,38 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Inject @Inject
private ItemPermissionChecker itemPermissionChecker; private ItemPermissionChecker itemPermissionChecker;
/** @Inject
* The current content section. private Models models;
*/
private ContentSection section;
/** @Inject
* The {@link Article} to edit. private MvcAuthoringStepService stepService;
*/
private Article document;
@Override @GET
public Class<? extends ContentItem> supportedDocumentType() { @Path("/")
return Article.class; @Transactional(Transactional.TxType.REQUIRED)
} public String showStep(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
@Override final String sectionIdentifier,
public String getLabel() { @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM)
return globalizationHelper final String documentPath
.getLocalizedTextsUtil(getBundle()) ) {
.getText("authoringsteps.basicproperties.label"); try {
} stepService.setSectionAndDocument(sectionIdentifier, documentPath);
} catch (ContentSectionNotFoundException ex) {
@Override return ex.showErrorMessage();
public String getDescription() { } catch (DocumentNotFoundException ex) {
return globalizationHelper return ex.showErrorMessage();
.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.");
} }
this.document = (Article) document;
}
@Override if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
public String showStep() {
if (itemPermissionChecker.canEditItem(document)) {
return "org/librecms/ui/contenttypes/article/article-basic-properties.xhtml"; return "org/librecms/ui/contenttypes/article/article-basic-properties.xhtml";
} else { } else {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
section, stepService.getContentSection(),
document, stepService.getDocument(),
articleMessageBundle.getMessage("article.edit.denied") articleMessageBundle.getMessage("article.edit.denied")
); );
} }
} }
/** /**
@ -197,31 +143,54 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
* @return The display name of the current article. * @return The display name of the current article.
*/ */
public String getName() { public String getName() {
return document.getDisplayName(); return stepService.getDocument().getDisplayName();
} }
/** /**
* Updates the name of the current article. * 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. * @return A redirect to this authoring step.
*/ */
@POST @POST
@Path("/name") @Path("/name")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String updateName(@FormParam("name") final String name) { public String updateName(
document.setDisplayName(name); @PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
itemRepo.save(document); final String sectionIdentifier,
return String.format( @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM)
"redirect:/@documents/%s/%s/%s/@edit/%s", final String documentPath,
section.getLabel(), @FormParam("name") @DefaultValue("") final String name
folderManager.getFolderPath( ) {
itemManager.getItemFolder(document).get() try {
), stepService.setSectionAndDocument(sectionIdentifier, documentPath);
name, } catch (ContentSectionNotFoundException ex) {
PATH_FRAGMENT 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. * @return The values of the localized title of the article.
*/ */
public Map<String, String> getTitleValues() { public Map<String, String> getTitleValues() {
return document return stepService
.getDocument()
.getTitle() .getTitle()
.getValues() .getValues()
.entrySet() .entrySet()
@ -249,7 +219,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
* @return The locales for which no localized title has been defined yet. * @return The locales for which no localized title has been defined yet.
*/ */
public List<String> getUnusedTitleLocales() { public List<String> getUnusedTitleLocales() {
final Set<Locale> titleLocales = document final Set<Locale> titleLocales = stepService
.getDocument()
.getTitle() .getTitle()
.getAvailableLocales(); .getAvailableLocales();
return globalizationHelper 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 sectionIdentifier
* @param value The title value. * @param documentPath
* @param localeParam The locale to update.
* @param value The updated title value.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@POST @POST
@Path("/title/@add") @Path("/title/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String addTitle( 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 @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getTitle().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getTitle().addValue(locale, value);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Updates a localized title of the article. * Updates a localized title of the article.
* *
* @param localeParam The locale to update. * @param sectionIdentifier
* @param value The updated title value. * @param documentPath
* @param localeParam The locale to update.
* @param value The updated title value.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@ -299,25 +289,41 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Path("/title/@edit/{locale}") @Path("/title/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String editTitle( public String editTitle(
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM)
final String documentPath,
@PathParam("locale") final String localeParam, @PathParam("locale") final String localeParam,
@FormParam("value") final String value @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getTitle().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getTitle().removeValue(locale);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Removes a localized title of the article. * 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. * @return A redirect to this authoring step.
*/ */
@ -325,18 +331,33 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Path("/title/@remove/{locale}") @Path("/title/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String removeTitle( 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 @PathParam("locale") final String localeParam
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getTitle().removeValue(locale); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getTitle().removeValue(locale);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); 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. * yet.
*/ */
public List<String> getUnusedDescriptionLocales() { public List<String> getUnusedDescriptionLocales() {
final Set<Locale> descriptionLocales = document final Set<Locale> descriptionLocales = stepService
.getDocument()
.getDescription() .getDescription()
.getAvailableLocales(); .getAvailableLocales();
return globalizationHelper return globalizationHelper
@ -363,7 +385,8 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
* @return The values of the localized description of the article. * @return The values of the localized description of the article.
*/ */
public Map<String, String> getDescriptionValues() { public Map<String, String> getDescriptionValues() {
return document return stepService
.getDocument()
.getDescription() .getDescription()
.getValues() .getValues()
.entrySet() .entrySet()
@ -379,8 +402,10 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
/** /**
* Adds a localized description to the article. * Adds a localized description to the article.
* *
* @param localeParam The locale of the description. * @param sectionIdentifier
* @param value The description value. * @param documentPath
* @param localeParam The locale of the description.
* @param value The description value.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@ -388,26 +413,43 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Path("/title/@add") @Path("/title/@add")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String addDescription( 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("locale") final String localeParam,
@FormParam("value") final String value @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getDescription().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getDescription().addValue(locale, value);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Updates a localized description of the article. * Updates a localized description of the article.
* *
* @param localeParam The locale to update. * @param sectionIdentifier
* @param value The updated description value. * @param documentPath
* @param localeParam The locale to update.
* @param value The updated description value.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@ -415,24 +457,41 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Path("/title/@edit/{locale}") @Path("/title/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String editDescription( 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, @PathParam("locale") final String localeParam,
@FormParam("value") final String value @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getDescription().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getDescription().addValue(locale, value);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Removes a localized description of the article. * Removes a localized description of the article.
* *
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale to remove. * @param localeParam The locale to remove.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
@ -441,18 +500,33 @@ public class MvcArticlePropertiesStep implements MvcAuthoringStep {
@Path("/title/@remove/{locale}") @Path("/title/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String removeDescription( 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 @PathParam("locale") final String localeParam
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getDescription().removeValue(locale); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:%s/@documents/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), stepService.getDocument().getDescription().removeValue(locale);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
} }

View File

@ -18,22 +18,21 @@
*/ */
package org.librecms.ui.contenttypes; package org.librecms.ui.contenttypes;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository; import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.contenttypes.Article; import org.librecms.contenttypes.Article;
import org.librecms.ui.contentsections.ItemPermissionChecker; 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.DocumentUi;
import javax.mvc.Controller; import javax.mvc.Controller;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import org.librecms.ui.contentsections.documents.MvcAuthoringStep; 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.List;
import java.util.Locale; import java.util.Locale;
@ -44,7 +43,9 @@ import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.transaction.Transactional;
import javax.ws.rs.FormParam; import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
@ -54,31 +55,26 @@ import javax.ws.rs.PathParam;
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
@Path("/") @Path(MvcAuthoringSteps.PATH_PREFIX + "/@text")
@AuthoringStepPathFragment(MvcArticleTextBodyStep.PATH_FRAGMENT) @Controller
@Named("CmsArticleTextBodyStep") @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 @Inject
private ArticleMessageBundle articleMessageBundle; private ArticleMessageBundle articleMessageBundle;
/**
* The path fragment of the step.
*/
static final String PATH_FRAGMENT = "text";
/** /**
* Used for retrieving and saving the article. * Used for retrieving and saving the article.
*/ */
@Inject @Inject
private ContentItemRepository itemRepo; private ContentItemRepository itemRepo;
/**
* Provides functions for working with content items.
*/
@Inject
private ContentItemManager itemManager;
@Inject @Inject
private DocumentUi documentUi; private DocumentUi documentUi;
@ -91,93 +87,32 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep {
@Inject @Inject
private ItemPermissionChecker itemPermissionChecker; private ItemPermissionChecker itemPermissionChecker;
/** @Inject
* The current content section. private MvcAuthoringStepService stepService;
*/
private ContentSection section;
/** @GET
* The {@link Article} to edit. @Path("/")
*/ @Transactional(Transactional.TxType.REQUIRED)
private Article document; public String showStep(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
@Override final String sectionIdentifier,
public Class<? extends ContentItem> supportedDocumentType() { @PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM)
return Article.class; final String documentPath
} ) {
try {
@Override stepService.setSectionAndDocument(sectionIdentifier, documentPath);
public String getLabel() { } catch (ContentSectionNotFoundException ex) {
return globalizationHelper return ex.showErrorMessage();
.getLocalizedTextsUtil(getBundle()) } catch (DocumentNotFoundException ex) {
.getText("authoringsteps.text.label"); return ex.showErrorMessage();
}
@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.");
} }
this.document = (Article) document;
}
@Override if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
public String showStep() {
if (itemPermissionChecker.canEditItem(document)) {
return "org/librecms/ui/contenttypes/article/article-text.xhtml"; return "org/librecms/ui/contenttypes/article/article-text.xhtml";
} else { } else {
return documentUi.showAccessDenied( return documentUi.showAccessDenied(
section, stepService.getContentSection(),
document, stepService.getDocument(),
articleMessageBundle.getMessage("article.edit.denied") articleMessageBundle.getMessage("article.edit.denied")
); );
} }
@ -189,7 +124,7 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep {
* @return The localized values of the main text. * @return The localized values of the main text.
*/ */
public Map<String, String> getTextValues() { public Map<String, String> getTextValues() {
return document return getDocument()
.getText() .getText()
.getValues() .getValues()
.entrySet() .entrySet()
@ -208,7 +143,7 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep {
* @return The locales for which the main text has not been defined yet. * @return The locales for which the main text has not been defined yet.
*/ */
public List<String> getUnusedLocales() { public List<String> getUnusedLocales() {
final Set<Locale> locales = document final Set<Locale> locales = getDocument()
.getText() .getText()
.getAvailableLocales(); .getAvailableLocales();
return globalizationHelper return globalizationHelper
@ -222,77 +157,135 @@ public class MvcArticleTextBodyStep implements MvcAuthoringStep {
/** /**
* Adds a localized main text. * Adds a localized main text.
* *
* @param localeParam The locale of the text. * @param sectionIdentifier
* @param value The text. * @param documentPath
* @param localeParam The locale of the text.
* @param value The text.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@POST @POST
@Path("/@add") @Path("/@add")
@Transactional(Transactional.TxType.REQUIRED)
public String addTextValue( 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("locale") final String localeParam,
@FormParam("value") final String value @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getText().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:/@documents/%s/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), getDocument().getText().addValue(locale, value);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Updates a localized main text. * Updates a localized main text.
* *
* @param localeParam The locale of the text. * @param sectionIdentifier
* @param value The text. * @param documentPath
* @param localeParam The locale of the text.
* @param value The text.
* *
* @return A redirect to this authoring step. * @return A redirect to this authoring step.
*/ */
@POST @POST
@Path("/@edit/{locale}") @Path("/@edit/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String editTextValue( 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, @PathParam("locale") final String localeParam,
@FormParam("value") final String value @FormParam("value") final String value
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getText().addValue(locale, value); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:/@documents/%s/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), getDocument().getText().addValue(locale, value);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
} }
/** /**
* Removes a localized main text. * 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. * @return A redirect to this authoring step.
*/ */
@POST @POST
@Path("/@remove/{locale}") @Path("/@remove/{locale}")
@Transactional(Transactional.TxType.REQUIRED)
public String remvoeTextValue( 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 @PathParam("locale") final String localeParam
) { ) {
final Locale locale = new Locale(localeParam); try {
document.getText().removeValue(locale); stepService.setSectionAndDocument(sectionIdentifier, documentPath);
itemRepo.save(document); } catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
return String.format( if (itemPermissionChecker.canEditItem(stepService.getDocument())) {
"redirect:/@documents/%s/%s/@edit/%s", final Locale locale = new Locale(localeParam);
section.getLabel(), getDocument().getText().removeValue(locale);
getContentItemPath(), itemRepo.save(stepService.getDocument());
PATH_FRAGMENT
); return stepService.buildRedirectPathForStep(getClass());
} else {
return documentUi.showAccessDenied(
stepService.getContentSection(),
stepService.getDocument(),
stepService.getLabel(getClass())
);
}
}
private Article getDocument() {
return (Article) stepService.getDocument();
} }
} }

View File

@ -100,10 +100,10 @@
<ul class="list-group"> <ul class="list-group">
<c:forEach items="#{CmsSelectedDocumentModel.authoringStepsList}" <c:forEach items="#{CmsSelectedDocumentModel.authoringStepsList}"
var="step"> var="step">
<li aria-current="#{step.pathFragment == authoringStep ? 'true' : ''} <li aria-current="#{step.path == authoringStep ? 'true' : ''}
class="list-group-item #{step.pathFragment == authoringStep ? 'active' : ''}"> class="list-group-item #{step.path == authoringStep ? 'active' : ''}">
<a class="list-group-item-action" <a class="list-group-item-action"
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{step.pathFragment}"> href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{step.path}">
#{step.label} #{step.label}
</a> </a>
</li> </li>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
<ui:define name="main">
<div class="container">
<h1>Example Authoring Step</h1>
#{sectionIdentifier}
#{documentPath}
</div>
</ui:define>
</ui:composition>
</html>