Controller methods for workflow and lifecycle management of content items/documents
parent
b5178788c2
commit
c7a8756c9c
|
|
@ -5,24 +5,47 @@
|
||||||
*/
|
*/
|
||||||
package org.librecms.ui.contentsections.documents;
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
import org.libreccm.api.Identifier;
|
||||||
|
import org.libreccm.api.IdentifierParser;
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.security.AuthorizationRequired;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.workflow.AssignableTask;
|
||||||
|
import org.libreccm.workflow.AssignableTaskManager;
|
||||||
|
import org.libreccm.workflow.TaskManager;
|
||||||
|
import org.libreccm.workflow.Workflow;
|
||||||
|
import org.libreccm.workflow.WorkflowManager;
|
||||||
|
import org.libreccm.workflow.WorkflowRepository;
|
||||||
import org.librecms.contentsection.ContentItem;
|
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.contentsection.ContentSection;
|
||||||
import org.librecms.contentsection.Folder;
|
import org.librecms.contentsection.Folder;
|
||||||
import org.librecms.contentsection.FolderRepository;
|
import org.librecms.contentsection.FolderRepository;
|
||||||
import org.librecms.contentsection.FolderType;
|
import org.librecms.contentsection.FolderType;
|
||||||
|
import org.librecms.contentsection.privileges.ItemPrivileges;
|
||||||
|
import org.librecms.lifecycle.LifecycleDefinition;
|
||||||
|
import org.librecms.lifecycle.LifecycleManager;
|
||||||
|
import org.librecms.lifecycle.Phase;
|
||||||
import org.librecms.ui.contentsections.ContentSectionsUi;
|
import org.librecms.ui.contentsections.ContentSectionsUi;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.enterprise.inject.Instance;
|
import javax.enterprise.inject.Instance;
|
||||||
import javax.enterprise.util.AnnotationLiteral;
|
import javax.enterprise.util.AnnotationLiteral;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import javax.mvc.Controller;
|
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.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
|
@ -33,8 +56,12 @@ import javax.ws.rs.PathParam;
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
@Path("/{sectionIdentifier}/documents")
|
@Path("/{sectionIdentifier}/documents")
|
||||||
@Controller
|
@Controller
|
||||||
|
@Named("CmsDocumentController")
|
||||||
public class DocumentController {
|
public class DocumentController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ContentItemManager itemManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ContentSectionsUi sectionsUi;
|
private ContentSectionsUi sectionsUi;
|
||||||
|
|
||||||
|
|
@ -50,11 +77,42 @@ public class DocumentController {
|
||||||
@Inject
|
@Inject
|
||||||
private Instance<MvcDocumentCreateStep<?>> createSteps;
|
private Instance<MvcDocumentCreateStep<?>> createSteps;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private IdentifierParser identifierParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private LifecycleManager lifecycleManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private Models models;
|
private Models models;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PermissionChecker permissionChecker;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PublishStepModel publishStepModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SelectedDocumentModel selectedDocumentModel;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private WorkflowManager workflowManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AssignableTaskManager assignableTaskManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private TaskManager taskManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private WorkflowRepository workflowRepository;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/")
|
@Path("/")
|
||||||
|
@AuthorizationRequired
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public String redirectToDocumentFolders(
|
public String redirectToDocumentFolders(
|
||||||
@PathParam("sectionIdentifider") final String sectionIdentifier
|
@PathParam("sectionIdentifider") final String sectionIdentifier
|
||||||
|
|
@ -66,6 +124,7 @@ public class DocumentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/@create/{documentType}")
|
@Path("/@create/{documentType}")
|
||||||
|
@AuthorizationRequired
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public MvcDocumentCreateStep<? extends ContentItem> createDocument(
|
public MvcDocumentCreateStep<? extends ContentItem> createDocument(
|
||||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
|
@ -75,6 +134,7 @@ public class DocumentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/{folderPath:(.+)?}/@create/{documentType}")
|
@Path("/{folderPath:(.+)?}/@create/{documentType}")
|
||||||
|
@AuthorizationRequired
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public MvcDocumentCreateStep<? extends ContentItem> createDocument(
|
public MvcDocumentCreateStep<? extends ContentItem> createDocument(
|
||||||
|
|
@ -146,6 +206,7 @@ public class DocumentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}")
|
@Path("/{documentPath:(.+)?}/@authoringsteps/{authoringStep}")
|
||||||
|
@AuthorizationRequired
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public MvcAuthoringStep editDocument(
|
public MvcAuthoringStep editDocument(
|
||||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
|
@ -192,12 +253,518 @@ public class DocumentController {
|
||||||
return new UnsupportedDocumentType();
|
return new UnsupportedDocumentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
authoringStep.setContentSection(section);
|
authoringStep.setContentSection(section);
|
||||||
authoringStep.setContentItem(item);
|
authoringStep.setContentItem(item);
|
||||||
|
|
||||||
return authoringStep;
|
return authoringStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@history")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String showHistory(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
if (!permissionChecker.isPermitted(ItemPrivileges.EDIT, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
models.put(
|
||||||
|
"revisions",
|
||||||
|
itemRepo.retrieveRevisions(item, item.getObjectId())
|
||||||
|
);
|
||||||
|
|
||||||
|
return "org/librecms/ui/contentsection/documents/history.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{documentPath:(.+)?}/@publish")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String showPublishStep(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
if (!permissionChecker.isPermitted(ItemPrivileges.PUBLISH, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
publishStepModel.setAvailableLifecycles(
|
||||||
|
section
|
||||||
|
.getLifecycleDefinitions()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildLifecycleListEntry)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
if (item.getLifecycle() != null) {
|
||||||
|
publishStepModel.setPhases(
|
||||||
|
item
|
||||||
|
.getLifecycle()
|
||||||
|
.getPhases()
|
||||||
|
.stream()
|
||||||
|
.map(this::buildPhaseListEntry)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "org/librecms/ui/contentsection/documents/publish.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@lifecycle/phases/{phaseId}")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String updatePhaseDates(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@PathParam("phaseId") final long phaseId,
|
||||||
|
@FormParam("startDate") final String startDateParam,
|
||||||
|
@FormParam("endDate") final String endDateParam
|
||||||
|
) {
|
||||||
|
throw new UnsupportedOperationException("ToDo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@publish")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String publish(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@FormParam("selectedLifecycleUuid") @DefaultValue("")
|
||||||
|
final String selectedLifecycleUuid
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
if (!permissionChecker.isPermitted(ItemPrivileges.PUBLISH, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedLifecycleUuid.isEmpty()) {
|
||||||
|
itemManager.publish(item);
|
||||||
|
} else {
|
||||||
|
final Optional<LifecycleDefinition> result = section
|
||||||
|
.getLifecycleDefinitions()
|
||||||
|
.stream()
|
||||||
|
.filter(
|
||||||
|
definition -> definition.getUuid().equals(
|
||||||
|
selectedLifecycleUuid
|
||||||
|
)
|
||||||
|
).findAny();
|
||||||
|
if (!result.isPresent()) {
|
||||||
|
models.put("section", section.getLabel());
|
||||||
|
models.put("lifecycleDefUuid", selectedLifecycleUuid);
|
||||||
|
return "org/librecms/ui/contentsection/lifecycle-def-not-found.xhtml";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:/%s/documents/%s/@publish",
|
||||||
|
sectionIdentifier,
|
||||||
|
documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@republish")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String republish(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
if (!permissionChecker.isPermitted(ItemPrivileges.PUBLISH, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemManager.publish(item);
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"redirect:/%s/documents/%s/@publish",
|
||||||
|
sectionIdentifier,
|
||||||
|
documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String unpublish() {
|
||||||
|
throw new UnsupportedOperationException("ToDo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@workflow/tasks/${taskIdentifier}/@lock")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String lockTask(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||||
|
@FormParam("returnUrl") final String returnUrl
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
final Optional<AssignableTask> taskResult = findTask(
|
||||||
|
item, taskIdentifier
|
||||||
|
);
|
||||||
|
if (!taskResult.isPresent()) {
|
||||||
|
return showTaskNotFound(section, documentPath, taskIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AssignableTask task = taskResult.get();
|
||||||
|
assignableTaskManager.lockTask(task);
|
||||||
|
|
||||||
|
return String.format("redirect:%s", returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@workflow/tasks/${taskIdentifier}/@unlock")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String unlockTask(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||||
|
@FormParam("returnUrl") final String returnUrl
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
final Optional<AssignableTask> taskResult = findTask(
|
||||||
|
item, taskIdentifier
|
||||||
|
);
|
||||||
|
if (!taskResult.isPresent()) {
|
||||||
|
return showTaskNotFound(section, documentPath, taskIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AssignableTask task = taskResult.get();
|
||||||
|
assignableTaskManager.unlockTask(task);
|
||||||
|
|
||||||
|
return String.format("redirect:%s", returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@workflow/tasks/${taskIdentifier}/@finish")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String finishTask(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||||
|
@FormParam("comment") @DefaultValue("") final String comment,
|
||||||
|
@FormParam("returnUrl") final String returnUrl
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
final Optional<AssignableTask> taskResult = findTask(
|
||||||
|
item, taskIdentifier
|
||||||
|
);
|
||||||
|
if (!taskResult.isPresent()) {
|
||||||
|
return showTaskNotFound(section, documentPath, taskIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AssignableTask task = taskResult.get();
|
||||||
|
if (comment.isEmpty()) {
|
||||||
|
assignableTaskManager.finish(task);
|
||||||
|
} else {
|
||||||
|
assignableTaskManager.finish(task, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("redirect:%s", returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path(
|
||||||
|
"/{documentPath:(.+)?}/@workflow/@applyAlternative/{workflowIdentifier}")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String applyAlternateWorkflow(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@FormParam("newWorkflowUuid") final String newWorkflowUuid,
|
||||||
|
@FormParam("returnUrl") final String returnUrl
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
if (!permissionChecker.isPermitted(
|
||||||
|
ItemPrivileges.APPLY_ALTERNATE_WORKFLOW, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<Workflow> workflowResult = section
|
||||||
|
.getWorkflowTemplates()
|
||||||
|
.stream()
|
||||||
|
.filter(template -> template.getUuid().equals(newWorkflowUuid))
|
||||||
|
.findAny();
|
||||||
|
if (!workflowResult.isPresent()) {
|
||||||
|
models.put("section", section.getLabel());
|
||||||
|
models.put("workflowUuid", newWorkflowUuid);
|
||||||
|
return "org/librecms/ui/contentsection/workflow-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
workflowManager.createWorkflow(workflowResult.get(), item);
|
||||||
|
return String.format("redirect:%s", returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{documentPath:(.+)?}/@workflow/@restart")
|
||||||
|
@AuthorizationRequired
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public String restartWorkflow(
|
||||||
|
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||||
|
@PathParam("documentPath") final String documentPath,
|
||||||
|
@FormParam("returnUrl") final String returnUrl
|
||||||
|
) {
|
||||||
|
final Optional<ContentSection> sectionResult = sectionsUi
|
||||||
|
.findContentSection(sectionIdentifier);
|
||||||
|
if (!sectionResult.isPresent()) {
|
||||||
|
return sectionsUi.showContentSectionNotFound(sectionIdentifier);
|
||||||
|
}
|
||||||
|
final ContentSection section = sectionResult.get();
|
||||||
|
|
||||||
|
final Optional<ContentItem> itemResult = itemRepo
|
||||||
|
.findByPath(section, documentPath);
|
||||||
|
if (!itemResult.isPresent()) {
|
||||||
|
return showDocumentNotFound(section, documentPath);
|
||||||
|
}
|
||||||
|
final ContentItem item = itemResult.get();
|
||||||
|
selectedDocumentModel.setContentItem(item);
|
||||||
|
|
||||||
|
if (!permissionChecker.isPermitted(
|
||||||
|
ItemPrivileges.APPLY_ALTERNATE_WORKFLOW, item)) {
|
||||||
|
return sectionsUi.showAccessDenied(
|
||||||
|
"sectionIdentifier", sectionIdentifier,
|
||||||
|
"documentPath", documentPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getWorkflow() != null) {
|
||||||
|
workflowManager.start(item.getWorkflow());
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("redirect:%s", returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<AssignableTask> findTask(
|
||||||
|
final ContentItem item,
|
||||||
|
final String taskIdentifier
|
||||||
|
) {
|
||||||
|
final Workflow workflow = item.getWorkflow();
|
||||||
|
if (workflow == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Identifier identifier = identifierParser.parseIdentifier(
|
||||||
|
taskIdentifier
|
||||||
|
);
|
||||||
|
switch (identifier.getType()) {
|
||||||
|
case ID:
|
||||||
|
return workflow
|
||||||
|
.getTasks()
|
||||||
|
.stream()
|
||||||
|
.filter(task -> task instanceof AssignableTask)
|
||||||
|
.map(task -> (AssignableTask) task)
|
||||||
|
.filter(
|
||||||
|
task -> task.getTaskId() == Long
|
||||||
|
.parseLong(identifier.getIdentifier())
|
||||||
|
).findAny();
|
||||||
|
default:
|
||||||
|
return workflow
|
||||||
|
.getTasks()
|
||||||
|
.stream()
|
||||||
|
.filter(task -> task instanceof AssignableTask)
|
||||||
|
.map(task -> (AssignableTask) task)
|
||||||
|
.filter(
|
||||||
|
task -> task.getUuid().equals(
|
||||||
|
identifier.getIdentifier()
|
||||||
|
)
|
||||||
|
).findAny();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LifecycleListEntry buildLifecycleListEntry(
|
||||||
|
final LifecycleDefinition definition
|
||||||
|
) {
|
||||||
|
final LifecycleListEntry entry = new LifecycleListEntry();
|
||||||
|
entry.setDescription(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
definition.getDescription()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setLabel(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
definition.getLabel()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setUuid(definition.getUuid());
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PhaseListEntry buildPhaseListEntry(final Phase phase) {
|
||||||
|
final DateTimeFormatter dateTimeFormatter
|
||||||
|
= DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
.withZone(ZoneId.systemDefault());
|
||||||
|
final PhaseListEntry entry = new PhaseListEntry();
|
||||||
|
entry.setDescription(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
phase.getDefinition().getDescription()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setEndDateTime(
|
||||||
|
dateTimeFormatter.format(phase.getEndDateTime().toInstant())
|
||||||
|
);
|
||||||
|
entry.setFinished(phase.isFinished());
|
||||||
|
entry.setLabel(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
phase.getDefinition().getLabel()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setPhaseId(phase.getPhaseId());
|
||||||
|
entry.setStartDateTime(
|
||||||
|
dateTimeFormatter.format(phase.getStartDateTime().toInstant())
|
||||||
|
);
|
||||||
|
entry.setStarted(phase.isStarted());
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String showDocumentNotFound(
|
||||||
|
final ContentSection section, final String documentPath
|
||||||
|
) {
|
||||||
|
models.put("section", section.getLabel());
|
||||||
|
models.put("documentPath", documentPath);
|
||||||
|
return "org/librecms/ui/contentsection/document-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String showTaskNotFound(
|
||||||
|
final ContentSection section,
|
||||||
|
final String documentPath,
|
||||||
|
final String taskIdentifier
|
||||||
|
) {
|
||||||
|
models.put("section", section.getLabel());
|
||||||
|
models.put("documentPath", documentPath);
|
||||||
|
models.put("taskIdentifier", taskIdentifier);
|
||||||
|
return "org/librecms/ui/contentsection/task-not-found.xhtml";
|
||||||
|
}
|
||||||
|
|
||||||
private static class CreateDocumentOfTypeLiteral
|
private static class CreateDocumentOfTypeLiteral
|
||||||
extends AnnotationLiteral<CreatesDocumentOfType>
|
extends AnnotationLiteral<CreatesDocumentOfType>
|
||||||
implements CreatesDocumentOfType {
|
implements CreatesDocumentOfType {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class LifecycleListEntry {
|
||||||
|
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(final String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(final String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class PhaseListEntry {
|
||||||
|
|
||||||
|
private long phaseId;
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
private String startDateTime;
|
||||||
|
|
||||||
|
private String endDateTime;
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(final String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStarted() {
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStarted(final boolean started) {
|
||||||
|
this.started = started;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinished(final boolean finished) {
|
||||||
|
this.finished = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStartDateTime() {
|
||||||
|
return startDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartDateTime(final String startDateTime) {
|
||||||
|
this.startDateTime = startDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndDateTime() {
|
||||||
|
return endDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndDateTime(final String endDateTime) {
|
||||||
|
this.endDateTime = endDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPhaseId() {
|
||||||
|
return phaseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhaseId(long phaseId) {
|
||||||
|
this.phaseId = phaseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("CmsDocumentPublishStepModel")
|
||||||
|
public class PublishStepModel {
|
||||||
|
|
||||||
|
private List<LifecycleListEntry> availableListcycles;
|
||||||
|
|
||||||
|
private List<PhaseListEntry> phases;
|
||||||
|
|
||||||
|
public PublishStepModel() {
|
||||||
|
availableListcycles = new ArrayList<>();
|
||||||
|
phases = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LifecycleListEntry> getAvailableListcycles() {
|
||||||
|
return Collections.unmodifiableList(availableListcycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvailableLifecycles(
|
||||||
|
final List<LifecycleListEntry> availableListcycles
|
||||||
|
) {
|
||||||
|
this.availableListcycles = new ArrayList<>(availableListcycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PhaseListEntry> getPhases() {
|
||||||
|
return Collections.unmodifiableList(phases);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhases(final List<PhaseListEntry> phases) {
|
||||||
|
this.phases = new ArrayList<>(phases);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
import org.libreccm.l10n.GlobalizationHelper;
|
||||||
|
import org.libreccm.security.PermissionChecker;
|
||||||
|
import org.libreccm.security.Shiro;
|
||||||
|
import org.libreccm.workflow.AssignableTask;
|
||||||
|
import org.libreccm.workflow.AssignableTaskManager;
|
||||||
|
import org.libreccm.workflow.TaskState;
|
||||||
|
import org.libreccm.workflow.Workflow;
|
||||||
|
import org.librecms.contentsection.ContentItem;
|
||||||
|
import org.librecms.contentsection.privileges.ItemPrivileges;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
@Named("CmsSelectedDocumentModel")
|
||||||
|
public class SelectedDocumentModel {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AssignableTaskManager taskManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private GlobalizationHelper globalizationHelper;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PermissionChecker permissionChecker;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Shiro shiro;
|
||||||
|
|
||||||
|
private ContentItem item;
|
||||||
|
|
||||||
|
private Workflow workflow;
|
||||||
|
|
||||||
|
private TaskListEntry currentTask;
|
||||||
|
|
||||||
|
private List<TaskListEntry> allTasks;
|
||||||
|
|
||||||
|
void setContentItem(final ContentItem item) {
|
||||||
|
this.item = Objects.requireNonNull(item);
|
||||||
|
workflow = item.getWorkflow();
|
||||||
|
allTasks = workflow
|
||||||
|
.getTasks()
|
||||||
|
.stream()
|
||||||
|
.filter(task -> task instanceof AssignableTask)
|
||||||
|
.map(task -> (AssignableTask) task)
|
||||||
|
.map(this::buildTaskListEntry)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
currentTask = allTasks
|
||||||
|
.stream()
|
||||||
|
.filter(task -> task.getTaskState() == TaskState.ENABLED)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (currentTask != null) {
|
||||||
|
currentTask.setCurrentTask(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskListEntry getCurrentTask() {
|
||||||
|
return currentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getCanChangeWorkflow() {
|
||||||
|
return permissionChecker.isPermitted(
|
||||||
|
ItemPrivileges.APPLY_ALTERNATE_WORKFLOW, item
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskListEntry buildTaskListEntry(final AssignableTask task) {
|
||||||
|
final TaskListEntry entry = new TaskListEntry();
|
||||||
|
entry.setLabel(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
task.getLabel()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setDescription(
|
||||||
|
globalizationHelper.getValueFromLocalizedString(
|
||||||
|
task.getDescription()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
entry.setTaskState(task.getTaskState());
|
||||||
|
entry.setAssignedToCurrentUser(
|
||||||
|
shiro
|
||||||
|
.getUser()
|
||||||
|
.map(user -> taskManager.isAssignedTo(task, user))
|
||||||
|
.orElse(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.librecms.ui.contentsections.documents;
|
||||||
|
|
||||||
|
import org.libreccm.workflow.TaskState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
public class TaskListEntry {
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private TaskState taskState;
|
||||||
|
|
||||||
|
private boolean currentTask;
|
||||||
|
|
||||||
|
private boolean assignedToCurrentUser;
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(final String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskState getTaskState() {
|
||||||
|
return taskState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskState(TaskState taskState) {
|
||||||
|
this.taskState = taskState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrentTask() {
|
||||||
|
return currentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentTask(final boolean currentTask) {
|
||||||
|
this.currentTask = currentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAssignedToCurrentUser() {
|
||||||
|
return assignedToCurrentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAssignedToCurrentUser(final boolean assignedToCurrentUser) {
|
||||||
|
this.assignedToCurrentUser = assignedToCurrentUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue