Several bugfixes for workflow and task handling
parent
b140923d86
commit
2be640f8d4
|
|
@ -22,8 +22,13 @@ import org.libreccm.api.Identifier;
|
|||
import org.libreccm.api.IdentifierParser;
|
||||
import org.libreccm.l10n.GlobalizationHelper;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.libreccm.security.Role;
|
||||
import org.libreccm.workflow.AssignableTask;
|
||||
import org.libreccm.workflow.AssignableTaskManager;
|
||||
import org.libreccm.workflow.AssignableTaskRepository;
|
||||
import org.libreccm.workflow.CircularTaskDependencyException;
|
||||
import org.libreccm.workflow.Task;
|
||||
import org.libreccm.workflow.TaskAssignment;
|
||||
import org.libreccm.workflow.TaskManager;
|
||||
import org.libreccm.workflow.TaskRepository;
|
||||
import org.libreccm.workflow.Workflow;
|
||||
|
|
@ -32,6 +37,7 @@ import org.libreccm.workflow.WorkflowRepository;
|
|||
import org.librecms.contentsection.ContentSection;
|
||||
import org.librecms.contentsection.ContentSectionManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
|
@ -66,6 +72,12 @@ public class ConfigurationWorkflowController {
|
|||
@Inject
|
||||
private AdminPermissionsChecker adminPermissionsChecker;
|
||||
|
||||
@Inject
|
||||
private AssignableTaskManager assignableTaskManager;
|
||||
|
||||
@Inject
|
||||
private AssignableTaskRepository assignableTaskRepo;
|
||||
|
||||
/**
|
||||
* Used for actions involving content sections.
|
||||
*/
|
||||
|
|
@ -834,19 +846,60 @@ public class ConfigurationWorkflowController {
|
|||
)
|
||||
);
|
||||
|
||||
if (task instanceof AssignableTask) {
|
||||
final AssignableTask assignableTask = (AssignableTask) task;
|
||||
final List<Role> assignedRoles = assignableTask
|
||||
.getAssignments()
|
||||
.stream()
|
||||
.map(TaskAssignment::getRole)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
selectedWorkflowTaskTemplateModel.setAssignedRoles(
|
||||
assignedRoles
|
||||
.stream()
|
||||
.map(Role::getName)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
selectedWorkflowTaskTemplateModel.setAvailableRoles(
|
||||
section
|
||||
.getRoles()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Role::getUuid, Role::getName))
|
||||
);
|
||||
|
||||
selectedWorkflowTaskTemplateModel.setAssignedRoleKeys(
|
||||
assignedRoles
|
||||
.stream()
|
||||
.map(Role::getUuid)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else {
|
||||
selectedWorkflowTaskTemplateModel.setAssignedRoles(
|
||||
Collections.emptyList()
|
||||
);
|
||||
selectedWorkflowTaskTemplateModel.setAvailableRoles(
|
||||
Collections.emptyMap()
|
||||
);
|
||||
}
|
||||
|
||||
return "org/librecms/ui/contentsection/configuration/workflow-task.xhtml";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a task to a workflow template.
|
||||
* Adds a assignable task to a workflow template.
|
||||
*
|
||||
* @param sectionIdentifierParam The identifier of the current content
|
||||
* section.
|
||||
* @param workflowIdentiferParam The identifier of the current workflow
|
||||
* template.
|
||||
* @param label The label of the new task.
|
||||
* @param assignments UUIDs of the roles to which the task is
|
||||
* assigned.
|
||||
*
|
||||
* @return A redirect to the details view of the workflow.
|
||||
*
|
||||
* @see AssignableTask
|
||||
*/
|
||||
@POST
|
||||
@Path("/{workflowIdentifier}/tasks/@add")
|
||||
|
|
@ -855,7 +908,8 @@ public class ConfigurationWorkflowController {
|
|||
public String addTask(
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifierParam,
|
||||
@PathParam("workflowIdentifier") final String workflowIdentiferParam,
|
||||
@FormParam("label") final String label
|
||||
@FormParam("label") final String label,
|
||||
@FormParam("assignments") final List<String> assignments
|
||||
) {
|
||||
final Optional<ContentSection> sectionResult = sectionsUi
|
||||
.findContentSection(sectionIdentifierParam);
|
||||
|
|
@ -877,12 +931,23 @@ public class ConfigurationWorkflowController {
|
|||
return showWorkflowTemplateNotFound(section, workflowIdentiferParam);
|
||||
}
|
||||
final Workflow workflow = workflowResult.get();
|
||||
final Task task = new Task();
|
||||
final AssignableTask task = new AssignableTask();
|
||||
task.getLabel().addValue(
|
||||
globalizationHelper.getNegotiatedLocale(), label
|
||||
);
|
||||
|
||||
taskRepo.save(task);
|
||||
final List<Role> roles = section
|
||||
.getRoles()
|
||||
.stream()
|
||||
.filter(role -> assignments.contains(role.getUuid()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assignableTaskRepo.save(task);
|
||||
|
||||
for (final Role role : roles) {
|
||||
assignableTaskManager.assignTask(task, role);
|
||||
}
|
||||
|
||||
taskManager.addTask(workflow, task);
|
||||
|
||||
return String.format(
|
||||
|
|
@ -1479,6 +1544,84 @@ public class ConfigurationWorkflowController {
|
|||
);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{workflowIdentifier}/tasks/{taskIdentifier}/@assignments")
|
||||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String updateAssignments(
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifierParam,
|
||||
@PathParam("workflowIdentifier") final String workflowIdentiferParam,
|
||||
@PathParam("taskIdentifier") final String taskIdentifierParam,
|
||||
@FormParam("assignments") final List<String> assignments
|
||||
) {
|
||||
final Optional<ContentSection> sectionResult = sectionsUi
|
||||
.findContentSection(sectionIdentifierParam);
|
||||
if (!sectionResult.isPresent()) {
|
||||
sectionsUi.showContentSectionNotFound(sectionIdentifierParam);
|
||||
}
|
||||
final ContentSection section = sectionResult.get();
|
||||
sectionModel.setSection(section);
|
||||
if (!adminPermissionsChecker.canAdministerWorkflows(section)) {
|
||||
return sectionsUi.showAccessDenied(
|
||||
"sectionIdentifier", sectionIdentifierParam
|
||||
);
|
||||
}
|
||||
|
||||
final Optional<Workflow> workflowResult = findWorkflowTemplate(
|
||||
section, workflowIdentiferParam
|
||||
);
|
||||
if (!workflowResult.isPresent()) {
|
||||
return showWorkflowTemplateNotFound(section, workflowIdentiferParam);
|
||||
}
|
||||
final Workflow workflow = workflowResult.get();
|
||||
final Optional<Task> taskResult = findTaskTemplate(
|
||||
workflow, taskIdentifierParam
|
||||
);
|
||||
if (!taskResult.isPresent()) {
|
||||
return showWorkflowTaskTemplateNotFound(
|
||||
section, workflowIdentiferParam, taskIdentifierParam
|
||||
);
|
||||
}
|
||||
|
||||
final Task task = taskResult.get();
|
||||
if (task instanceof AssignableTask) {
|
||||
final AssignableTask assignableTask = (AssignableTask) task;
|
||||
|
||||
final List<Role> assignedRoles = assignableTask
|
||||
.getAssignments()
|
||||
.stream()
|
||||
.map(TaskAssignment::getRole)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final List<Role> newAssignements = section
|
||||
.getRoles()
|
||||
.stream()
|
||||
.filter(role -> assignments.contains(role.getUuid()))
|
||||
.filter(role -> !assignedRoles.contains(role))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final List<Role> removedAssignments = assignedRoles
|
||||
.stream()
|
||||
.filter(role -> !assignments.contains(role.getUuid()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final Role role : newAssignements) {
|
||||
assignableTaskManager.assignTask(assignableTask, role);
|
||||
}
|
||||
|
||||
for (final Role role : removedAssignments) {
|
||||
assignableTaskManager.retractTask(assignableTask, role);
|
||||
}
|
||||
}
|
||||
|
||||
return String.format(
|
||||
"redirect:/%s/configuration/workflows/%s/tasks/%s",
|
||||
sectionIdentifierParam,
|
||||
workflowIdentiferParam,
|
||||
taskIdentifierParam
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for retrieving a workflow template.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,16 +28,13 @@ import org.librecms.ui.contentsections.documents.DocumentWorkflowController;
|
|||
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.enterprise.inject.Any;
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.inject.Inject;
|
||||
import javax.mvc.Controller;
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
/**
|
||||
|
|
@ -73,11 +70,11 @@ public class ContentSectionApplication extends Application {
|
|||
classes.add(ContentSectionController.class);
|
||||
classes.add(DocumentFolderController.class);
|
||||
classes.add(DocumentController.class);
|
||||
classes.add(DocumentLifecyclesController.class);
|
||||
classes.add(DocumentWorkflowController.class);
|
||||
|
||||
classes.addAll(getAuthoringSteps());
|
||||
|
||||
classes.add(DocumentLifecyclesController.class);
|
||||
classes.add(DocumentWorkflowController.class);
|
||||
classes.add(IsAuthenticatedFilter.class);
|
||||
|
||||
return classes;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ public class SelectedWorkflowTaskTemplateModel {
|
|||
*/
|
||||
private Map<String, String> noneBlockingTasks;
|
||||
|
||||
private List<String> assignedRoles;
|
||||
|
||||
private List<String> assignedRoleKeys;
|
||||
|
||||
private Map<String, String> availableRoles;
|
||||
|
||||
|
||||
|
||||
public long getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
|
@ -196,4 +204,33 @@ public class SelectedWorkflowTaskTemplateModel {
|
|||
this.noneBlockingTasks = new HashMap<>(noneBlockingTasks);
|
||||
}
|
||||
|
||||
public Map<String, String> getAvailableRoles() {
|
||||
return Collections.unmodifiableMap(availableRoles);
|
||||
}
|
||||
|
||||
public void setAvailableRoles(
|
||||
final Map<String, String> availableRoles
|
||||
) {
|
||||
this.availableRoles = new HashMap<>(availableRoles);
|
||||
}
|
||||
|
||||
public List<String> getAssignedRoles() {
|
||||
return Collections.unmodifiableList(assignedRoles);
|
||||
}
|
||||
|
||||
public void setAssignedRoles(final List<String> assignedRoles) {
|
||||
this.assignedRoles = new ArrayList<>(assignedRoles);
|
||||
}
|
||||
|
||||
public List<String> getAssignedRoleKeys() {
|
||||
return Collections.unmodifiableList(assignedRoleKeys);
|
||||
}
|
||||
|
||||
public void setAssignedRoleKeys(final List<String> assignedRoleKeys) {
|
||||
this.assignedRoleKeys = new ArrayList<>(assignedRoleKeys);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@ import org.librecms.contentsection.ContentItemRepository;
|
|||
import org.librecms.contentsection.ContentSection;
|
||||
import org.librecms.ui.contentsections.ContentSectionModel;
|
||||
import org.librecms.ui.contentsections.ContentSectionsUi;
|
||||
import org.librecms.ui.contentsections.ItemPermissionChecker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import javax.mvc.Models;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
|
@ -52,6 +52,9 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
|
|||
@Inject
|
||||
private ContentItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private ItemPermissionChecker itemPermissionChecker;
|
||||
|
||||
@Inject
|
||||
private ContentItemRepository itemRepo;
|
||||
|
||||
|
|
@ -177,6 +180,20 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCanEdit() {
|
||||
if (!itemPermissionChecker.canEditItem(document)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (documentModel.getCurrentTask() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return documentModel.getCurrentTask().isAssignedToCurrentUser()
|
||||
|| itemPermissionChecker.canAdministerItems(document);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return Optional
|
||||
|
|
@ -210,6 +227,55 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
|
|||
documentPath = itemManager.getItemPath(document).substring(1); // Without leading slash
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStepPath() {
|
||||
final ContentSection section = Optional
|
||||
.ofNullable(contentSection)
|
||||
.orElseThrow(
|
||||
() -> new WebApplicationException(
|
||||
String.format(
|
||||
"Authoring Step %s was not initalized properly. "
|
||||
+ "Did you forget to call %s#init()?",
|
||||
getStepClass().getName(),
|
||||
AbstractMvcAuthoringStep.class.getName()
|
||||
)
|
||||
)
|
||||
);
|
||||
final String docPath = Optional
|
||||
.ofNullable(documentPath)
|
||||
.orElseThrow(
|
||||
() -> new WebApplicationException(
|
||||
String.format(
|
||||
"Authoring Step %s was not initalized properly. "
|
||||
+ "Did you forget to call %s#init()?",
|
||||
getStepClass().getName(),
|
||||
AbstractMvcAuthoringStep.class.getName()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
final Map<String, String> values = new HashMap<>();
|
||||
values.put(
|
||||
MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM,
|
||||
section.getLabel()
|
||||
);
|
||||
values.put(
|
||||
MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME,
|
||||
docPath
|
||||
);
|
||||
|
||||
return Optional
|
||||
.ofNullable(getStepClass().getAnnotation(Path.class))
|
||||
.map(Path::value)
|
||||
.map(
|
||||
path -> UriBuilder
|
||||
.fromPath(path)
|
||||
.buildFromMap(values)
|
||||
.toString()
|
||||
)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildRedirectPathForStep() {
|
||||
final ContentSection section = Optional
|
||||
|
|
@ -254,6 +320,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
|
|||
path -> UriBuilder
|
||||
.fromPath(path)
|
||||
.buildFromMap(values)
|
||||
.toString()
|
||||
)
|
||||
.map(path -> String.format("redirect:%s", path))
|
||||
.orElse("");
|
||||
|
|
@ -304,6 +371,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
|
|||
.fromPath(path)
|
||||
.path(subPath)
|
||||
.buildFromMap(values)
|
||||
.toString()
|
||||
)
|
||||
.map(path -> String.format("redirect:%s", path))
|
||||
.orElse("");
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ public class DocumentWorkflowController {
|
|||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String lockTask(
|
||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||
@FormParam("returnUrl") final String returnUrl
|
||||
|
|
@ -189,7 +189,7 @@ public class DocumentWorkflowController {
|
|||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String unlockTask(
|
||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||
@FormParam("returnUrl") final String returnUrl
|
||||
|
|
@ -247,7 +247,7 @@ public class DocumentWorkflowController {
|
|||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String finishTask(
|
||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@PathParam("taskIdentifier") final String taskIdentifier,
|
||||
@FormParam("comment") @DefaultValue("") final String comment,
|
||||
|
|
@ -310,7 +310,7 @@ public class DocumentWorkflowController {
|
|||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String applyAlternateWorkflow(
|
||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@FormParam("newWorkflowUuid") final String newWorkflowUuid,
|
||||
@FormParam("returnUrl") final String returnUrl
|
||||
|
|
@ -362,6 +362,27 @@ public class DocumentWorkflowController {
|
|||
return String.format("redirect:%s", returnUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the workflow assigned to an content item.
|
||||
*
|
||||
* @param sectionIdentifier The identifier of the current content section.
|
||||
* @param documentPath The path of the current document.
|
||||
* @param returnUrl The URL to return to.
|
||||
*
|
||||
* @return A redirect to the {@code returnUrl}.
|
||||
*/
|
||||
@POST
|
||||
@Path("/@start")
|
||||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String startWorkflow(
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@FormParam("returnUrl") final String returnUrl
|
||||
) {
|
||||
return restartWorkflow(sectionIdentifier, documentPath, returnUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the workflow assigned to an content item.
|
||||
*
|
||||
|
|
@ -376,7 +397,7 @@ public class DocumentWorkflowController {
|
|||
@AuthorizationRequired
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public String restartWorkflow(
|
||||
@PathParam("sectionIdentifider") final String sectionIdentifier,
|
||||
@PathParam("sectionIdentifier") final String sectionIdentifier,
|
||||
@PathParam("documentPath") final String documentPath,
|
||||
@FormParam("returnUrl") final String returnUrl
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,20 @@ public interface MvcAuthoringStep {
|
|||
String getDocumentPath() throws ContentSectionNotFoundException,
|
||||
DocumentNotFoundException;
|
||||
|
||||
/**
|
||||
* Can the current user edit the document. This method MUST only return
|
||||
* {@code true} if
|
||||
* <ul>
|
||||
* <li>The current user has the permission to edit the item.</li>
|
||||
* <li>The item has an active task.</li>
|
||||
* <li>The task is assigned to the current user or the current user has
|
||||
* admin priviliges for the content section of the item.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return {@code true} if the current user can edit the document/item, {@false} otherwise.
|
||||
*/
|
||||
boolean getCanEdit();
|
||||
|
||||
/**
|
||||
* Gets the label for an authoring step.
|
||||
*
|
||||
|
|
@ -73,6 +87,8 @@ public interface MvcAuthoringStep {
|
|||
void updateDocumentPath() throws ContentSectionNotFoundException,
|
||||
DocumentNotFoundException;
|
||||
|
||||
String getStepPath();
|
||||
|
||||
/**
|
||||
* Builds the redirect path of the authoring step.This path is most often
|
||||
* used to implement the redirect after post pattern.
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ public class MvcArticleCreateStep
|
|||
itemRepo.save(article);
|
||||
|
||||
return String.format(
|
||||
"redirect:/%s/documents/%s/%s/@articleproperties",
|
||||
"redirect:/%s/documents/%s/%s/@article-basicproperties",
|
||||
getContentSectionLabel(),
|
||||
getFolderPath(),
|
||||
name
|
||||
|
|
|
|||
|
|
@ -112,6 +112,79 @@
|
|||
values="#{SelectedWorkflowTaskTemplateModel.description}"
|
||||
/>
|
||||
|
||||
<h2>#{CmsAdminMessages['contentsection.configuration.workflows.task_details.assigned_to.title']}</h2>
|
||||
<div class="mb-2">
|
||||
<div class="text-right">
|
||||
<button class="btn btn-primary"
|
||||
data-toggle="modal"
|
||||
data-target="#update-assignments-dialog"
|
||||
type="button">
|
||||
<bootstrap:svgIcon icon="person-plus" />
|
||||
<span>#{CmsAdminMessages['contentsection.configuration.workflows.task_details.assigned_to.edit']}</span>
|
||||
</button>
|
||||
</div>
|
||||
<c:choose>
|
||||
<c:when test="#{SelectedWorkflowTaskTemplateModel.assignedRoles.isEmpty()}">
|
||||
<p>
|
||||
#{CmsAdminMessages['contentsection.configuration.workflows.task_details.not_assigned']}
|
||||
</p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<ul>
|
||||
<c:forEach items="#{SelectedWorkflowTaskTemplateModel.assignedRoles}"
|
||||
var="role">
|
||||
<li>#{role}</li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<div aria-hidden="true"
|
||||
aria-labelledby="update-assignments-dialog-title"
|
||||
class="modal fade"
|
||||
id="update-assignments-dialog"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows/UUID-#{SelectedWorkflowTemplateModel.uuid}/tasks/UUID-#{SelectedWorkflowTaskTemplateModel.uuid}/@assignments"
|
||||
class="modal-content"
|
||||
method="post">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title"
|
||||
id="update-assignments-modal-title">
|
||||
#{CmsAdminMessages['contentsection.configuration.workflows.task_details.assigned_to.dialog.title']}
|
||||
<button aria-label="#{CmsAdminMessages['contentsection.configuration.workflow.task_details.assignments.dialog.close']}"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
type="button">
|
||||
<bootstrap:svgIcon icon="x" />
|
||||
</button>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<bootstrap:formGroupChecks
|
||||
help="#{CmsAdminMessages['contentsection.configuration.workflow.task_details.assignments.dialog.roles.help']}"
|
||||
inputId="assigments"
|
||||
label="#{CmsAdminMessages['contentsection.configuration.workflow.task_details.assignments.dialog.roles.label']}"
|
||||
name="assignments"
|
||||
options="#{SelectedWorkflowTaskTemplateModel.availableRoles}"
|
||||
selectedOptions="#{SelectedWorkflowTaskTemplateModel.assignedRoleKeys}"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning"
|
||||
data-dismiss="modal"
|
||||
type="button">
|
||||
#{CmsAdminMessages['contentsection.configuration.workflow.task_details.assignments.dialog.close']}
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
type="submit" >
|
||||
#{CmsAdminMessages['contentsection.configuration.workflow.task_details.assignemnts.dialog.submit']}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>#{CmsAdminMessages['contentsection.configuration.workflows.task_details.blocking_tasks.title']}</h2>
|
||||
|
||||
<div class="mb-2">
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link #{activeDocumentTab == 'editTab' ? 'active' : ''}"
|
||||
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}">
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}">
|
||||
#{CmsAdminMessages['contentsection.document.tabs.edit.title']}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link #{activeDocumentTab == 'history' ? 'active' : ''}"
|
||||
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@history">
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@history">
|
||||
#{CmsAdminMessages['contentsection.document.tabs.history.title']}
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -35,33 +35,44 @@
|
|||
</button>
|
||||
</p>
|
||||
<p>
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}
|
||||
<c:choose>
|
||||
<c:when test="#{CmsSelectedDocumentModel.currentTask != null}">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}
|
||||
#{CmsSelectedDocumentModel.currentTask.label}
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.none']}
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/@start"
|
||||
method="post">
|
||||
<input name="returnUrl"
|
||||
type="hidden"
|
||||
value="#{CmsArticlePropertiesStep.stepPath}" />
|
||||
|
||||
<button class="btn btn-primary"
|
||||
type="submit">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.start']}
|
||||
</button>
|
||||
</form>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
<c:if test="#{CmsSelectedDocumentModel.currentTask != null}">
|
||||
<c:choose>
|
||||
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser and CmsSelectedDocumentModel.currentTask.locked}">
|
||||
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@unlock"
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@unlock"
|
||||
method="post">
|
||||
<input name="returnUrl"
|
||||
type="hidden"
|
||||
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
type="submit">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.release']}
|
||||
</button>
|
||||
</form>
|
||||
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@finish"
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@finish"
|
||||
method="post">
|
||||
<input name="returnUrl"
|
||||
type="hidden"
|
||||
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
type="submit">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.finish']}
|
||||
|
|
@ -69,11 +80,11 @@
|
|||
</form>
|
||||
</c:when>
|
||||
<c:when test="#{CmsSelectedDocumentModel.currentTask.locked and CmsSelectedDocumentModel.canChangeWorkflow}">
|
||||
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock"
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock"
|
||||
method="post">
|
||||
<input name="returnUrl"
|
||||
type="hidden"
|
||||
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
type="submit">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.takeover']}
|
||||
|
|
@ -81,10 +92,10 @@
|
|||
</form>
|
||||
</c:when>
|
||||
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser or CmsSelectedDocumentModel.canChangeWorkflow}">
|
||||
<form action="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock">
|
||||
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/tasks/UUID-#{CmsSelectedDocumentModel.currentTask.taskUuid}/@lock">
|
||||
<input name="returnUrl"
|
||||
type="hidden"
|
||||
value="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@authoringsteps/#{authoringStep}" />
|
||||
<button class="btn btn-secondary btn-sm"
|
||||
type="button">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.lock']}
|
||||
|
|
@ -113,14 +124,14 @@
|
|||
<li aria-current="#{'categorize' == authoringStep ? 'true' : ''}"
|
||||
class="list-group-item #{'categorize' == authoringStep ? 'active' : ''}">
|
||||
<a class="list-group-item-action"
|
||||
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@categorization">
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@categorization">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.steps.categorize.label']}
|
||||
</a>
|
||||
</li>
|
||||
<li aria-current="#{'relatedInfo' == authoringStep ? 'true' : ''}"
|
||||
class="list-group-item #{'relatedInfo' == authoringStep ? 'active' : ''}">
|
||||
<a class="list-group-item-action"
|
||||
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@relatedinfo">
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@relatedinfo">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.steps.relatedInfo.label']}
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -129,7 +140,7 @@
|
|||
<li aria-current="#{'publish' == authoringStep ? 'true' : ''}"
|
||||
class="list-group-item #{'publish' == authoringStep ? 'active' : ''}">
|
||||
<a class="list-group-item-action"
|
||||
href="#{mvc.basePath}/documents/#{CmsSelectedDocumentModel.itemPath}/@publish">
|
||||
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@publish">
|
||||
#{CmsAdminMessages['contentsection.document.authoring.steps.publish.label']}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<h3>#{CmsArticleMessageBundle.getMessage('basicproperties.name.header')}</h3>
|
||||
<div class="d-flex">
|
||||
<pre class="mr-2">#{CmsArticlePropertiesStep.name}</pre>
|
||||
<c:if test="#{CmsArticlePropertiesStep.canEdit}">
|
||||
<button class="btn btn-primary btn-sm"
|
||||
data-toggle="modal"
|
||||
data-target="#name-edit-dialog"
|
||||
|
|
@ -21,7 +22,10 @@
|
|||
#{CmsArticleMessageBundle['basicproperties.name.edit']}
|
||||
</span>
|
||||
</button>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<c:if test="#{CmsArticlePropertiesStep.canEdit}">
|
||||
<div aria-hidden="true"
|
||||
aria-labelledby="name-edit-dialog-title"
|
||||
class="modal fade"
|
||||
|
|
@ -67,6 +71,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
|
||||
<libreccm:localizedStringEditor
|
||||
|
|
@ -90,6 +95,7 @@
|
|||
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedTitleLocales.isEmpty()}"
|
||||
headingLevel="3"
|
||||
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
|
||||
readOnly="#{!CmsArticlePropertiesStep.canEdit}"
|
||||
removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.title.remove']}"
|
||||
removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.cancel']}"
|
||||
removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.submit']}"
|
||||
|
|
@ -123,6 +129,7 @@
|
|||
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedDescriptionLocales.isEmpty()}"
|
||||
headingLevel="3"
|
||||
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
|
||||
readOnly="#{!CmsArticlePropertiesStep.canEdit}"
|
||||
removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.description.remove']}"
|
||||
removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.cancel']}"
|
||||
removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.submit']}"
|
||||
|
|
|
|||
|
|
@ -756,3 +756,13 @@ contentsection.documentfolder.new_document_dialog.close=Cancel
|
|||
contentsection.documentfolder.new_document_dialog.documenttype.help=The type of the new document
|
||||
contentsection.documentfolder.new_document_dialog.documenttype.label=Document Type
|
||||
contentsection.documentfolder.new_document_dialog.submit=Create document
|
||||
contentsection.document.authoring.workflow.start=Start workflow
|
||||
ontentsection.configuration.workflows.task_details.assigned_to.title=Assigned to
|
||||
contentsection.configuration.workflows.task_details.assigned_to.edit=Edit assignments
|
||||
contentsection.configuration.workflows.task_details.not_assigned=This task is not assigned to any roles.
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.close=Cancel
|
||||
contentsection.configuration.workflow.task_details.assignemnts.dialog.submit=Update assigned roles
|
||||
contentsection.configuration.workflows.task_details.assigned_to.title=Assigned roles
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.roles.label=Roles
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.roles.help=Select the roles to which task is assigned.
|
||||
contentsection.configuration.workflows.task_details.assigned_to.dialog.title=Edit assigned roles
|
||||
|
|
|
|||
|
|
@ -757,3 +757,13 @@ contentsection.documentfolder.new_document_dialog.close=Abbrechen
|
|||
contentsection.documentfolder.new_document_dialog.documenttype.help=Der Typ des neuen Dokumentes
|
||||
contentsection.documentfolder.new_document_dialog.documenttype.label=Dokument Typ
|
||||
contentsection.documentfolder.new_document_dialog.submit=Dokument anlegen
|
||||
contentsection.document.authoring.workflow.start=Arbeitsablauf starten
|
||||
ontentsection.configuration.workflows.task_details.assigned_to.title=Zugewiesen an
|
||||
contentsection.configuration.workflows.task_details.assigned_to.edit=Zuweisungen bearbeiten
|
||||
contentsection.configuration.workflows.task_details.not_assigned=Diese Aufgabe ist keiner Rolle zugewiesen
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.close=Abbrechen
|
||||
contentsection.configuration.workflow.task_details.assignemnts.dialog.submit=Zugewiesene Rollen aktualisieren
|
||||
contentsection.configuration.workflows.task_details.assigned_to.title=Zugewiesen Rollen
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.roles.label=Rollen
|
||||
contentsection.configuration.workflow.task_details.assignments.dialog.roles.help=W\u00e4hlen Sie die Rollen, denen die Aufgabe zugewiesen ist.
|
||||
contentsection.configuration.workflows.task_details.assigned_to.dialog.title=Zugewiesene Rollen bearbeiten
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ import org.libreccm.security.User;
|
|||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -61,6 +63,13 @@ public class AssignableTaskRepository
|
|||
return task.getTaskId() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initNewEntity(final AssignableTask entity) {
|
||||
if (isNew(entity)) {
|
||||
entity.setUuid(UUID.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a {@link AssignableTask} by its uuid.
|
||||
*
|
||||
|
|
@ -110,4 +119,6 @@ public class AssignableTaskRepository
|
|||
return query.getResultList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ public class WorkflowManager implements Serializable {
|
|||
tasks);
|
||||
});
|
||||
|
||||
for(final Map.Entry<Long, Task> task : tasks.entrySet()) {
|
||||
task.getValue().setTaskState(TaskState.DISABLED);
|
||||
}
|
||||
|
||||
workflow.setObject(object);
|
||||
workflow.setState(WorkflowState.INIT);
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,11 @@
|
|||
required="true"
|
||||
shortDescription="Identifier of the object to which the localized string belongs"
|
||||
type="String" />
|
||||
<cc:attribute name="readOnly"
|
||||
default="false"
|
||||
required="false"
|
||||
shortDescription="Don't show edit buttons."
|
||||
type="boolean" />
|
||||
<cc:attribute name="removeButtonLabel"
|
||||
default="Remove"
|
||||
required="false"
|
||||
|
|
@ -190,7 +195,7 @@
|
|||
<div>#{cc.attrs.title}</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
<c:if test="#{cc.attrs.hasUnusedLocales}">
|
||||
<c:if test="#{cc.attrs.hasUnusedLocales and !cc.attrs.readOnly}">
|
||||
<div class="mb-2">
|
||||
<div class="text-right">
|
||||
<button class="btn btn-secondary"
|
||||
|
|
@ -331,6 +336,7 @@
|
|||
<td>#{entry.key}</td>
|
||||
<td>#{entry.value}</td>
|
||||
<td>
|
||||
<c:if test="#{!cc.attrs.readOnly}">
|
||||
<div class="text-center">
|
||||
<button class="btn btn-info"
|
||||
data-target="##{cc.attrs.editorId}-#{entry.key}-edit-dialog"
|
||||
|
|
@ -428,8 +434,10 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
</td>
|
||||
<td>
|
||||
<c:if test="#{!cc.attrs.readOnly}">
|
||||
<div class="text-center">
|
||||
<button class="btn btn-danger"
|
||||
data-target="##{cc.attrs.editorId}-#{entry.key}-remove-dialog"
|
||||
|
|
@ -499,6 +507,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
|
|
|
|||
Loading…
Reference in New Issue