Several bugfixes for workflow and task handling

pull/10/head
Jens Pelzetter 2021-05-01 17:55:30 +02:00
parent b140923d86
commit 2be640f8d4
15 changed files with 735 additions and 318 deletions

View File

@ -22,8 +22,13 @@ import org.libreccm.api.Identifier;
import org.libreccm.api.IdentifierParser; import org.libreccm.api.IdentifierParser;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired; 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.CircularTaskDependencyException;
import org.libreccm.workflow.Task; import org.libreccm.workflow.Task;
import org.libreccm.workflow.TaskAssignment;
import org.libreccm.workflow.TaskManager; import org.libreccm.workflow.TaskManager;
import org.libreccm.workflow.TaskRepository; import org.libreccm.workflow.TaskRepository;
import org.libreccm.workflow.Workflow; import org.libreccm.workflow.Workflow;
@ -32,6 +37,7 @@ import org.libreccm.workflow.WorkflowRepository;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionManager; import org.librecms.contentsection.ContentSectionManager;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
@ -66,6 +72,12 @@ public class ConfigurationWorkflowController {
@Inject @Inject
private AdminPermissionsChecker adminPermissionsChecker; private AdminPermissionsChecker adminPermissionsChecker;
@Inject
private AssignableTaskManager assignableTaskManager;
@Inject
private AssignableTaskRepository assignableTaskRepo;
/** /**
* Used for actions involving content sections. * 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"; 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 * @param sectionIdentifierParam The identifier of the current content
* section. * section.
* @param workflowIdentiferParam The identifier of the current workflow * @param workflowIdentiferParam The identifier of the current workflow
* template. * template.
* @param label The label of the new task. * @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. * @return A redirect to the details view of the workflow.
*
* @see AssignableTask
*/ */
@POST @POST
@Path("/{workflowIdentifier}/tasks/@add") @Path("/{workflowIdentifier}/tasks/@add")
@ -855,7 +908,8 @@ public class ConfigurationWorkflowController {
public String addTask( public String addTask(
@PathParam("sectionIdentifier") final String sectionIdentifierParam, @PathParam("sectionIdentifier") final String sectionIdentifierParam,
@PathParam("workflowIdentifier") final String workflowIdentiferParam, @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 final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifierParam); .findContentSection(sectionIdentifierParam);
@ -877,12 +931,23 @@ public class ConfigurationWorkflowController {
return showWorkflowTemplateNotFound(section, workflowIdentiferParam); return showWorkflowTemplateNotFound(section, workflowIdentiferParam);
} }
final Workflow workflow = workflowResult.get(); final Workflow workflow = workflowResult.get();
final Task task = new Task(); final AssignableTask task = new AssignableTask();
task.getLabel().addValue( task.getLabel().addValue(
globalizationHelper.getNegotiatedLocale(), label 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); taskManager.addTask(workflow, task);
return String.format( 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. * Helper method for retrieving a workflow template.
* *

View File

@ -28,16 +28,13 @@ import org.librecms.ui.contentsections.documents.DocumentWorkflowController;
import org.librecms.ui.contentsections.documents.MvcAuthoringSteps; 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 java.util.stream.Collectors;
import javax.enterprise.inject.Any; import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance; import javax.enterprise.inject.Instance;
import javax.inject.Inject; 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;
/** /**
@ -73,11 +70,11 @@ 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.addAll(getAuthoringSteps());
classes.add(IsAuthenticatedFilter.class); classes.add(IsAuthenticatedFilter.class);
return classes; return classes;

View File

@ -99,6 +99,14 @@ public class SelectedWorkflowTaskTemplateModel {
*/ */
private Map<String, String> noneBlockingTasks; private Map<String, String> noneBlockingTasks;
private List<String> assignedRoles;
private List<String> assignedRoleKeys;
private Map<String, String> availableRoles;
public long getTaskId() { public long getTaskId() {
return taskId; return taskId;
} }
@ -196,4 +204,33 @@ public class SelectedWorkflowTaskTemplateModel {
this.noneBlockingTasks = new HashMap<>(noneBlockingTasks); 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);
}
} }

View File

@ -26,12 +26,12 @@ import org.librecms.contentsection.ContentItemRepository;
import org.librecms.contentsection.ContentSection; import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.ContentSectionModel; import org.librecms.ui.contentsections.ContentSectionModel;
import org.librecms.ui.contentsections.ContentSectionsUi; import org.librecms.ui.contentsections.ContentSectionsUi;
import org.librecms.ui.contentsections.ItemPermissionChecker;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.annotation.PostConstruct;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mvc.Models; import javax.mvc.Models;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -52,6 +52,9 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
@Inject @Inject
private ContentItemManager itemManager; private ContentItemManager itemManager;
@Inject
private ItemPermissionChecker itemPermissionChecker;
@Inject @Inject
private ContentItemRepository itemRepo; private ContentItemRepository itemRepo;
@ -108,7 +111,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
) )
); );
sectionModel.setSection(contentSection); sectionModel.setSection(contentSection);
document = itemRepo document = itemRepo
.findByPath(contentSection, documentPathParam) .findByPath(contentSection, documentPathParam)
.orElseThrow( .orElseThrow(
@ -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 @Override
public String getLabel() { public String getLabel() {
return Optional return Optional
@ -210,6 +227,55 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
documentPath = itemManager.getItemPath(document).substring(1); // Without leading slash 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 @Override
public String buildRedirectPathForStep() { public String buildRedirectPathForStep() {
final ContentSection section = Optional final ContentSection section = Optional
@ -236,7 +302,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
) )
) )
); );
final Map<String, String> values = new HashMap<>(); final Map<String, String> values = new HashMap<>();
values.put( values.put(
MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM, MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM,
@ -254,6 +320,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
path -> UriBuilder path -> UriBuilder
.fromPath(path) .fromPath(path)
.buildFromMap(values) .buildFromMap(values)
.toString()
) )
.map(path -> String.format("redirect:%s", path)) .map(path -> String.format("redirect:%s", path))
.orElse(""); .orElse("");
@ -304,6 +371,7 @@ public abstract class AbstractMvcAuthoringStep implements MvcAuthoringStep {
.fromPath(path) .fromPath(path)
.path(subPath) .path(subPath)
.buildFromMap(values) .buildFromMap(values)
.toString()
) )
.map(path -> String.format("redirect:%s", path)) .map(path -> String.format("redirect:%s", path))
.orElse(""); .orElse("");

View File

@ -132,7 +132,7 @@ public class DocumentWorkflowController {
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String lockTask( public String lockTask(
@PathParam("sectionIdentifider") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, @PathParam("documentPath") final String documentPath,
@PathParam("taskIdentifier") final String taskIdentifier, @PathParam("taskIdentifier") final String taskIdentifier,
@FormParam("returnUrl") final String returnUrl @FormParam("returnUrl") final String returnUrl
@ -189,7 +189,7 @@ public class DocumentWorkflowController {
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String unlockTask( public String unlockTask(
@PathParam("sectionIdentifider") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, @PathParam("documentPath") final String documentPath,
@PathParam("taskIdentifier") final String taskIdentifier, @PathParam("taskIdentifier") final String taskIdentifier,
@FormParam("returnUrl") final String returnUrl @FormParam("returnUrl") final String returnUrl
@ -247,7 +247,7 @@ public class DocumentWorkflowController {
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String finishTask( public String finishTask(
@PathParam("sectionIdentifider") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, @PathParam("documentPath") final String documentPath,
@PathParam("taskIdentifier") final String taskIdentifier, @PathParam("taskIdentifier") final String taskIdentifier,
@FormParam("comment") @DefaultValue("") final String comment, @FormParam("comment") @DefaultValue("") final String comment,
@ -310,7 +310,7 @@ public class DocumentWorkflowController {
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String applyAlternateWorkflow( public String applyAlternateWorkflow(
@PathParam("sectionIdentifider") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, @PathParam("documentPath") final String documentPath,
@FormParam("newWorkflowUuid") final String newWorkflowUuid, @FormParam("newWorkflowUuid") final String newWorkflowUuid,
@FormParam("returnUrl") final String returnUrl @FormParam("returnUrl") final String returnUrl
@ -362,6 +362,27 @@ public class DocumentWorkflowController {
return String.format("redirect:%s", returnUrl); 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. * Restarts the workflow assigned to an content item.
* *
@ -376,7 +397,7 @@ public class DocumentWorkflowController {
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String restartWorkflow( public String restartWorkflow(
@PathParam("sectionIdentifider") final String sectionIdentifier, @PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("documentPath") final String documentPath, @PathParam("documentPath") final String documentPath,
@FormParam("returnUrl") final String returnUrl @FormParam("returnUrl") final String returnUrl
) { ) {

View File

@ -42,6 +42,20 @@ public interface MvcAuthoringStep {
String getDocumentPath() throws ContentSectionNotFoundException, String getDocumentPath() throws ContentSectionNotFoundException,
DocumentNotFoundException; 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. * Gets the label for an authoring step.
* *
@ -73,6 +87,8 @@ public interface MvcAuthoringStep {
void updateDocumentPath() throws ContentSectionNotFoundException, void updateDocumentPath() throws ContentSectionNotFoundException,
DocumentNotFoundException; DocumentNotFoundException;
String getStepPath();
/** /**
* Builds the redirect path of the authoring step.This path is most often * Builds the redirect path of the authoring step.This path is most often
* used to implement the redirect after post pattern. * used to implement the redirect after post pattern.

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/@articleproperties", "redirect:/%s/documents/%s/%s/@article-basicproperties",
getContentSectionLabel(), getContentSectionLabel(),
getFolderPath(), getFolderPath(),
name name

View File

@ -112,6 +112,79 @@
values="#{SelectedWorkflowTaskTemplateModel.description}" 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> <h2>#{CmsAdminMessages['contentsection.configuration.workflows.task_details.blocking_tasks.title']}</h2>
<div class="mb-2"> <div class="mb-2">
@ -139,7 +212,7 @@
id="add-blocking-task-dialog-title"> id="add-blocking-task-dialog-title">
#{CmsAdminMessages['contentsection.configuration.workflows.task_details.blocking_tasks.add.dialog.title']} #{CmsAdminMessages['contentsection.configuration.workflows.task_details.blocking_tasks.add.dialog.title']}
</h3> </h3>
<button aria-label="#{CmsAdminMessages['contentsection.configuration.workflow.task_details.blocking_tasks.add.dialog.close']}" <button aria-label="#{CmsAdminMessages['contentsection.configuration.workflow.task_details.blocking_tasks.add.dialog.close']}"
class="close" class="close"
data-dismiss="modal" data-dismiss="modal"
type="button"> type="button">
@ -170,87 +243,87 @@
</div> </div>
</div> </div>
</div> </div>
<table class="table table-hover workflow-tasks-table"> <table class="table table-hover workflow-tasks-table">
<thead> <thead>
<tr>
<th scope="col">
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.cols.label']}
</th>
<th colspan="2">
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.cols.actions']}
</th>
</tr>
</thead>
<tbody>
<c:forEach items="#{SelectedWorkflowTaskTemplateModel.blockingTasks}"
var="task">
<tr> <tr>
<th scope="col"> <td>
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.cols.label']} <a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows/UUID-#{SelectedWorkflowTemplateModel.uuid}/tasks/UUID-#{task.uuid}">
</th> #{task.label}
<th colspan="2"> </a>
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.cols.actions']} </td>
</th> <td class="info-col">
</tr> <button class="btn btn-info"
</thead> data-toggle="modal"
<tbody> data-target="#task-#{task.uuid}-info-dialog"
<c:forEach items="#{SelectedWorkflowTaskTemplateModel.blockingTasks}" type="button">
var="task"> <bootstrap:svgIcon icon="info-circle" />
<tr> <span class="sr-only">
<td> #{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.info_button.label']}
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows/UUID-#{SelectedWorkflowTemplateModel.uuid}/tasks/UUID-#{task.uuid}"> </span>
#{task.label} </button>
</a> <div aria-hidden="true"
</td> aria-labelledby="task-#{task.uuid}-info-dialog-title"
<td class="info-col"> class="modal fade"
<button class="btn btn-info" id="task-#{task.uuid}-info-dialog"
data-toggle="modal" tabindex="-1">
data-target="#task-#{task.uuid}-info-dialog" <div class="modal-dialog">
type="button"> <div class="modal-content">
<bootstrap:svgIcon icon="info-circle" /> <div class="modal-header">
<span class="sr-only"> <h2 class="modal-title"
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.table.info_button.label']} id="task-#{task.uuid}-info-dialog-title">
</span> #{CmsAdminMessages.getMessage('contentsection.configuration.workflow.tasks.info.dialog.title', [ContentSectionModel.sectionName, SelectedWorkflowTemplateModel.displayName, task.label])}
</button> </h2>
<div aria-hidden="true" <button aria-label="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.info.dialog.close']}"
aria-labelledby="task-#{task.uuid}-info-dialog-title" class="close"
class="modal fade" data-dismiss="modal"
id="task-#{task.uuid}-info-dialog" type="button">
tabindex="-1"> <bootstrap:svgIcon icon="x" />
<div class="modal-dialog"> </button>
<div class="modal-content"> </div>
<div class="modal-header"> <div class="modal-body">
<h2 class="modal-title" <p>
id="task-#{task.uuid}-info-dialog-title"> #{task.description}
#{CmsAdminMessages.getMessage('contentsection.configuration.workflow.tasks.info.dialog.title', [ContentSectionModel.sectionName, SelectedWorkflowTemplateModel.displayName, task.label])} </p>
</h2> </div>
<button aria-label="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.info.dialog.close']}" <div class="modal-footer">
class="close" <button class="btn btn-warning"
data-dismiss="modal" data-dismiss="modal"
type="button"> type="button">
<bootstrap:svgIcon icon="x" /> #{CmsAdminMessages['contentsection.configuration.workflow.tasks.info.dialog.close']}
</button> </button>
</div>
<div class="modal-body">
<p>
#{task.description}
</p>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['contentsection.configuration.workflow.tasks.info.dialog.close']}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</td> </div>
<td class="del-col"> </td>
<libreccm:deleteDialog <td class="del-col">
actionTarget="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows/UUID-#{SelectedWorkflowTemplateModel.uuid}/tasks/UUID-#{SelectedWorkflowTaskTemplateModel.uuid}/blockingTasks/#{task.uuid}/@remove" <libreccm:deleteDialog
buttonText="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.button.label']}" actionTarget="#{mvc.basePath}/#{ContentSectionModel.sectionName}/configuration/workflows/UUID-#{SelectedWorkflowTemplateModel.uuid}/tasks/UUID-#{SelectedWorkflowTaskTemplateModel.uuid}/blockingTasks/#{task.uuid}/@remove"
cancelLabel="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.close']}" buttonText="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.button.label']}"
confirmLabel="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.confirm']}" cancelLabel="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.close']}"
dialogId="task-#{task.taskId}-remove-dialog" confirmLabel="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.confirm']}"
dialogTitle="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.title']}" dialogId="task-#{task.taskId}-remove-dialog"
message="#{CmsAdminMessages.getMessage('contentsection.configuration.workflow.tasks.remove.dialog.message', [ContentSectionModel.sectionName, SelectedWorkflowTemplateModel.displayName, task.label])}" dialogTitle="#{CmsAdminMessages['contentsection.configuration.workflow.tasks.remove.dialog.title']}"
/> message="#{CmsAdminMessages.getMessage('contentsection.configuration.workflow.tasks.remove.dialog.message', [ContentSectionModel.sectionName, SelectedWorkflowTemplateModel.displayName, task.label])}"
</td> />
</tr> </td>
</c:forEach> </tr>
</tbody> </c:forEach>
</table> </tbody>
</table>
</div> </div>
</ui:define> </ui:define>

View File

@ -9,13 +9,13 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link #{activeDocumentTab == 'editTab' ? 'active' : ''}" <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']} #{CmsAdminMessages['contentsection.document.tabs.edit.title']}
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link #{activeDocumentTab == 'history' ? 'active' : ''}" <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']} #{CmsAdminMessages['contentsection.document.tabs.history.title']}
</a> </a>
</li> </li>
@ -35,33 +35,44 @@
</button> </button>
</p> </p>
<p> <p>
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}
<c:choose> <c:choose>
<c:when test="#{CmsSelectedDocumentModel.currentTask != null}"> <c:when test="#{CmsSelectedDocumentModel.currentTask != null}">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}
#{CmsSelectedDocumentModel.currentTask.label} #{CmsSelectedDocumentModel.currentTask.label}
</c:when> </c:when>
<c:otherwise> <c:otherwise>
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.none']} #{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:otherwise>
</c:choose> </c:choose>
<c:if test="#{CmsSelectedDocumentModel.currentTask != null}"> <c:if test="#{CmsSelectedDocumentModel.currentTask != null}">
<c:choose> <c:choose>
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser and CmsSelectedDocumentModel.currentTask.locked}"> <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"> method="post">
<input name="returnUrl" <input name="returnUrl"
type="hidden" 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" <button class="btn btn-secondary btn-sm"
type="submit"> type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.release']} #{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.release']}
</button> </button>
</form> </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"> method="post">
<input name="returnUrl" <input name="returnUrl"
type="hidden" 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" <button class="btn btn-secondary btn-sm"
type="submit"> type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.finish']} #{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.finish']}
@ -69,11 +80,11 @@
</form> </form>
</c:when> </c:when>
<c:when test="#{CmsSelectedDocumentModel.currentTask.locked and CmsSelectedDocumentModel.canChangeWorkflow}"> <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"> method="post">
<input name="returnUrl" <input name="returnUrl"
type="hidden" 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" <button class="btn btn-secondary btn-sm"
type="submit"> type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.takeover']} #{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.takeover']}
@ -81,10 +92,10 @@
</form> </form>
</c:when> </c:when>
<c:when test="#{CmsSelectedDocumentModel.currentTask.assignedToCurrentUser or CmsSelectedDocumentModel.canChangeWorkflow}"> <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" <input name="returnUrl"
type="hidden" 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" <button class="btn btn-secondary btn-sm"
type="button"> type="button">
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.lock']} #{CmsAdminMessages['contentsection.document.authoring.workflow.active_task.lock']}
@ -113,14 +124,14 @@
<li aria-current="#{'categorize' == authoringStep ? 'true' : ''}" <li aria-current="#{'categorize' == authoringStep ? 'true' : ''}"
class="list-group-item #{'categorize' == authoringStep ? 'active' : ''}"> class="list-group-item #{'categorize' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action" <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']} #{CmsAdminMessages['contentsection.document.authoring.steps.categorize.label']}
</a> </a>
</li> </li>
<li aria-current="#{'relatedInfo' == authoringStep ? 'true' : ''}" <li aria-current="#{'relatedInfo' == authoringStep ? 'true' : ''}"
class="list-group-item #{'relatedInfo' == authoringStep ? 'active' : ''}"> class="list-group-item #{'relatedInfo' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action" <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']} #{CmsAdminMessages['contentsection.document.authoring.steps.relatedInfo.label']}
</a> </a>
</li> </li>
@ -129,7 +140,7 @@
<li aria-current="#{'publish' == authoringStep ? 'true' : ''}" <li aria-current="#{'publish' == authoringStep ? 'true' : ''}"
class="list-group-item #{'publish' == authoringStep ? 'active' : ''}"> class="list-group-item #{'publish' == authoringStep ? 'active' : ''}">
<a class="list-group-item-action" <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']} #{CmsAdminMessages['contentsection.document.authoring.steps.publish.label']}
</a> </a>
</li> </li>

View File

@ -12,61 +12,66 @@
<h3>#{CmsArticleMessageBundle.getMessage('basicproperties.name.header')}</h3> <h3>#{CmsArticleMessageBundle.getMessage('basicproperties.name.header')}</h3>
<div class="d-flex"> <div class="d-flex">
<pre class="mr-2">#{CmsArticlePropertiesStep.name}</pre> <pre class="mr-2">#{CmsArticlePropertiesStep.name}</pre>
<button class="btn btn-primary btn-sm" <c:if test="#{CmsArticlePropertiesStep.canEdit}">
data-toggle="modal" <button class="btn btn-primary btn-sm"
data-target="#name-edit-dialog" data-toggle="modal"
type="button"> data-target="#name-edit-dialog"
<bootstrap:svgIcon icon="pen" /> type="button">
<span class="sr-only"> <bootstrap:svgIcon icon="pen" />
#{CmsArticleMessageBundle['basicproperties.name.edit']} <span class="sr-only">
</span> #{CmsArticleMessageBundle['basicproperties.name.edit']}
</button> </span>
</button>
</c:if>
</div> </div>
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title" <c:if test="#{CmsArticlePropertiesStep.canEdit}">
class="modal fade" <div aria-hidden="true"
id="name-edit-dialog" aria-labelledby="name-edit-dialog-title"
tabindex="-1"> class="modal fade"
<div class="modal-dialog"> id="name-edit-dialog"
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-basicproperties/name" tabindex="-1">
class="modal-content" <div class="modal-dialog">
method="post"> <form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-basicproperties/name"
<div class="modal-header"> class="modal-content"
<h4 class="modal-title" method="post">
id="name-edit-dialog-title"> <div class="modal-header">
#{CmsArticleMessageBundle['basicproperties.name.edit.title']} <h4 class="modal-title"
</h4> id="name-edit-dialog-title">
<button aria-label="#{CmsArticleMessageBundle['basicproperties.name.edit.close']}" #{CmsArticleMessageBundle['basicproperties.name.edit.title']}
class="close" </h4>
data-dismiss="modal" <button aria-label="#{CmsArticleMessageBundle['basicproperties.name.edit.close']}"
type="button"> class="close"
<bootstrap:svgIcon icon="x-circle" /> data-dismiss="modal"
</button> type="button">
</div> <bootstrap:svgIcon icon="x-circle" />
<div class="modal-body"> </button>
<bootstrap:formGroupText </div>
help="#{CmsArticleMessageBundle['basicproperties.name.help']}" <div class="modal-body">
inputId="name" <bootstrap:formGroupText
label="#{CmsArticleMessageBundle['basicproperties.name.label']}" help="#{CmsArticleMessageBundle['basicproperties.name.help']}"
name="name" inputId="name"
pattern="^([a-zA-Z0-9_-]*)$" label="#{CmsArticleMessageBundle['basicproperties.name.label']}"
required="true" name="name"
value="#{CmsArticlePropertiesStep.name}"/> pattern="^([a-zA-Z0-9_-]*)$"
</div> required="true"
<div class="modal-footer"> value="#{CmsArticlePropertiesStep.name}"/>
<button class="btn btn-warning" </div>
data-dismiss="modal" <div class="modal-footer">
type="button"> <button class="btn btn-warning"
#{CmsArticleMessageBundle['basicproperties.name.edit.close']} data-dismiss="modal"
</button> type="button">
<button class="btn btn-success" #{CmsArticleMessageBundle['basicproperties.name.edit.close']}
type="submit"> </button>
#{CmsArticleMessageBundle['basicproperties.name.edit.submit']} <button class="btn btn-success"
</button> type="submit">
</div> #{CmsArticleMessageBundle['basicproperties.name.edit.submit']}
</form> </button>
</div>
</form>
</div>
</div> </div>
</div> </c:if>
<libreccm:localizedStringEditor <libreccm:localizedStringEditor
@ -90,6 +95,7 @@
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedTitleLocales.isEmpty()}" hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedTitleLocales.isEmpty()}"
headingLevel="3" headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}" objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
readOnly="#{!CmsArticlePropertiesStep.canEdit}"
removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.title.remove']}" removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.title.remove']}"
removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.cancel']}" removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.cancel']}"
removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.submit']}" removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.title.remove.submit']}"
@ -123,6 +129,7 @@
hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedDescriptionLocales.isEmpty()}" hasUnusedLocales="#{!CmsArticlePropertiesStep.unusedDescriptionLocales.isEmpty()}"
headingLevel="3" headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}" objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
readOnly="#{!CmsArticlePropertiesStep.canEdit}"
removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.description.remove']}" removeButtonLabel="#{CmsArticleMessageBundle['basicproperties.description.remove']}"
removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.cancel']}" removeDialogCancelLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.cancel']}"
removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.submit']}" removeDialogSubmitLabel="#{CmsArticleMessageBundle['basicproperties.description.remove.submit']}"

View File

@ -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.help=The type of the new document
contentsection.documentfolder.new_document_dialog.documenttype.label=Document Type contentsection.documentfolder.new_document_dialog.documenttype.label=Document Type
contentsection.documentfolder.new_document_dialog.submit=Create document 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

View File

@ -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.help=Der Typ des neuen Dokumentes
contentsection.documentfolder.new_document_dialog.documenttype.label=Dokument Typ contentsection.documentfolder.new_document_dialog.documenttype.label=Dokument Typ
contentsection.documentfolder.new_document_dialog.submit=Dokument anlegen 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

View File

@ -26,8 +26,10 @@ import org.libreccm.security.User;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -60,6 +62,13 @@ public class AssignableTaskRepository
public boolean isNew(final AssignableTask task) { public boolean isNew(final AssignableTask task) {
return task.getTaskId() == 0; 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. * Finds a {@link AssignableTask} by its uuid.
@ -110,4 +119,6 @@ public class AssignableTaskRepository
return query.getResultList(); return query.getResultList();
} }
} }

View File

@ -162,6 +162,10 @@ public class WorkflowManager implements Serializable {
tasks); tasks);
}); });
for(final Map.Entry<Long, Task> task : tasks.entrySet()) {
task.getValue().setTaskState(TaskState.DISABLED);
}
workflow.setObject(object); workflow.setObject(object);
workflow.setState(WorkflowState.INIT); workflow.setState(WorkflowState.INIT);

View File

@ -102,6 +102,11 @@
required="true" required="true"
shortDescription="Identifier of the object to which the localized string belongs" shortDescription="Identifier of the object to which the localized string belongs"
type="String" /> type="String" />
<cc:attribute name="readOnly"
default="false"
required="false"
shortDescription="Don't show edit buttons."
type="boolean" />
<cc:attribute name="removeButtonLabel" <cc:attribute name="removeButtonLabel"
default="Remove" default="Remove"
required="false" required="false"
@ -190,7 +195,7 @@
<div>#{cc.attrs.title}</div> <div>#{cc.attrs.title}</div>
</c:otherwise> </c:otherwise>
</c:choose> </c:choose>
<c:if test="#{cc.attrs.hasUnusedLocales}"> <c:if test="#{cc.attrs.hasUnusedLocales and !cc.attrs.readOnly}">
<div class="mb-2"> <div class="mb-2">
<div class="text-right"> <div class="text-right">
<button class="btn btn-secondary" <button class="btn btn-secondary"
@ -331,174 +336,178 @@
<td>#{entry.key}</td> <td>#{entry.key}</td>
<td>#{entry.value}</td> <td>#{entry.value}</td>
<td> <td>
<div class="text-center"> <c:if test="#{!cc.attrs.readOnly}">
<button class="btn btn-info" <div class="text-center">
data-target="##{cc.attrs.editorId}-#{entry.key}-edit-dialog" <button class="btn btn-info"
data-toggle="modal" data-target="##{cc.attrs.editorId}-#{entry.key}-edit-dialog"
type="button"> data-toggle="modal"
<bootstrap:svgIcon icon="pen" /> type="button">
<span>#{cc.attrs.editButtonLabel}</span> <bootstrap:svgIcon icon="pen" />
</button> <span>#{cc.attrs.editButtonLabel}</span>
</div> </button>
<div aria-labelledby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-title" </div>
aria-hidden="true" <div aria-labelledby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-title"
class="modal fade" aria-hidden="true"
data-backdrop="static" class="modal fade"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog" data-backdrop="static"
tabindex="-1"> id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog"
<div class="modal-dialog"> tabindex="-1">
<form accept-charset="UTF-8" <div class="modal-dialog">
action="#{cc.attrs.editMethod}/#{entry.key}" <form accept-charset="UTF-8"
class="modal-content" action="#{cc.attrs.editMethod}/#{entry.key}"
method="post"> class="modal-content"
<div class="modal-header"> method="post">
<c:choose> <div class="modal-header">
<c:when test="#{cc.attrs.headingLevel == 1}">
<h2>#{cc.attrs.editDialogTitle}</h2>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 2}">
<h3>#{cc.attrs.editDialogTitle}</h3>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 3}">
<h4>#{cc.attrs.editDialogTitle}</h4>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 4}">
<h5>#{cc.attrs.editDialogTitle}</h5>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 5}">
<h6>#{cc.attrs.editDialogTitle}</h6>
</c:when>
<c:otherwise>
<div>#{cc.attrs.editDialogTitle}</div>
</c:otherwise>
</c:choose>
<button aria-label="#{cc.attrs.editDialogCancelLabel}"
class="close"
data-dismiss="modal"
type="button" >
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input name="locale"
value="#{entry.key}"
type="hidden" />
<div class="form-group">
<label for="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value}">
#{cc.attrs.editDialogValueLabel}
</label>
<c:choose> <c:choose>
<c:when test="#{cc.attrs.useTextarea}"> <c:when test="#{cc.attrs.headingLevel == 1}">
<textarea aria-describedby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help" <h2>#{cc.attrs.editDialogTitle}</h2>
class="form-control" </c:when>
cols="80" <c:when test="#{cc.attrs.headingLevel == 2}">
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value" <h3>#{cc.attrs.editDialogTitle}</h3>
name="value" </c:when>
required="true" <c:when test="#{cc.attrs.headingLevel == 3}">
rows="10">#{entry.value}</textarea> <h4>#{cc.attrs.editDialogTitle}</h4>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 4}">
<h5>#{cc.attrs.editDialogTitle}</h5>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 5}">
<h6>#{cc.attrs.editDialogTitle}</h6>
</c:when> </c:when>
<c:otherwise> <c:otherwise>
<input aria-describedby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help" <div>#{cc.attrs.editDialogTitle}</div>
class="form-control"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value"
name="value"
required="true"
type="text"
value="#{entry.value}" />
</c:otherwise> </c:otherwise>
</c:choose> </c:choose>
<small class="form-text text-muted" <button aria-label="#{cc.attrs.editDialogCancelLabel}"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help"> class="close"
#{cc.attrs.editDialogValueHelp} data-dismiss="modal"
</small> type="button" >
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input name="locale"
value="#{entry.key}"
type="hidden" />
<div class="form-group">
<label for="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value}">
#{cc.attrs.editDialogValueLabel}
</label>
<c:choose>
<c:when test="#{cc.attrs.useTextarea}">
<textarea aria-describedby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help"
class="form-control"
cols="80"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value"
name="value"
required="true"
rows="10">#{entry.value}</textarea>
</c:when>
<c:otherwise>
<input aria-describedby="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help"
class="form-control"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value"
name="value"
required="true"
type="text"
value="#{entry.value}" />
</c:otherwise>
</c:choose>
<small class="form-text text-muted"
id="#{cc.attrs.editorId}-#{entry.key}-edit-dialog-value-help">
#{cc.attrs.editDialogValueHelp}
</small>
</div>
</div>
<div class="modal-footer">
<div class="modal-footer">
<button class="btn btn-secondary"
data-dismiss="modal"
type="button" >
#{cc.attrs.editDialogCancelLabel}
</button>
<button type="submit" class="btn btn-primary">
#{cc.attrs.editDialogSubmitLabel}
</button>
</div>
</div>
</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"
data-toggle="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
<span>#{cc.attrs.removeButtonLabel}</span>
</button>
</div>
<div aria-describedby="#{cc.attrs.editorId}-#{entry.key}-remove-dialog-title"
aria-hidden="true"
class="modal fade"
data-backdrop="static"
id="#{cc.attrs.editorId}-#{entry.key}-remove-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{cc.attrs.removeMethod}/#{entry.key}"
class="modal-content"
method="post">
<div class="modal-header">
<c:choose>
<c:when test="#{cc.attrs.headingLevel == 1}">
<h2>#{cc.attrs.removeDialogTitle}</h2>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 2}">
<h3>#{cc.attrs.removeDialogTitle}</h3>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 3}">
<h4>#{cc.attrs.removeDialogTitle}</h4>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 4}">
<h5>#{cc.attrs.removeDialogTitle}</h5>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 5}">
<h6>#{cc.attrs.removeDialogTitle}</h6>
</c:when>
<c:otherwise>
<div>#{cc.attrs.removeDialogTitle}</div>
</c:otherwise>
</c:choose>
<button aria-label="#{cc.attrs.removeDialogCancelLabel}"
class="close"
data-dismiss="modal"
type="button" >
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
#{cc.attrs.removeDialogText}
</p>
<pre>#{entry.key}: #{entry.value}</pre>
<input name="confirmed"
type="hidden"
value="true" />
</div> </div>
</div>
<div class="modal-footer">
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-secondary" <button class="btn btn-secondary"
data-dismiss="modal" data-dismiss="modal"
type="button" > type="button" >
#{cc.attrs.editDialogCancelLabel} #{cc.attrs.removeDialogCancelLabel}
</button> </button>
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-danger">
#{cc.attrs.editDialogSubmitLabel} #{cc.attrs.removeDialogSubmitLabel}
</button> </button>
</div> </div>
</div> </form>
</form> </div>
</div> </div>
</div> </c:if>
</td>
<td>
<div class="text-center">
<button class="btn btn-danger"
data-target="##{cc.attrs.editorId}-#{entry.key}-remove-dialog"
data-toggle="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
<span>#{cc.attrs.removeButtonLabel}</span>
</button>
</div>
<div aria-describedby="#{cc.attrs.editorId}-#{entry.key}-remove-dialog-title"
aria-hidden="true"
class="modal fade"
data-backdrop="static"
id="#{cc.attrs.editorId}-#{entry.key}-remove-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{cc.attrs.removeMethod}/#{entry.key}"
class="modal-content"
method="post">
<div class="modal-header">
<c:choose>
<c:when test="#{cc.attrs.headingLevel == 1}">
<h2>#{cc.attrs.removeDialogTitle}</h2>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 2}">
<h3>#{cc.attrs.removeDialogTitle}</h3>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 3}">
<h4>#{cc.attrs.removeDialogTitle}</h4>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 4}">
<h5>#{cc.attrs.removeDialogTitle}</h5>
</c:when>
<c:when test="#{cc.attrs.headingLevel == 5}">
<h6>#{cc.attrs.removeDialogTitle}</h6>
</c:when>
<c:otherwise>
<div>#{cc.attrs.removeDialogTitle}</div>
</c:otherwise>
</c:choose>
<button aria-label="#{cc.attrs.removeDialogCancelLabel}"
class="close"
data-dismiss="modal"
type="button" >
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
#{cc.attrs.removeDialogText}
</p>
<pre>#{entry.key}: #{entry.value}</pre>
<input name="confirmed"
type="hidden"
value="true" />
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-dismiss="modal"
type="button" >
#{cc.attrs.removeDialogCancelLabel}
</button>
<button type="submit" class="btn btn-danger">
#{cc.attrs.removeDialogSubmitLabel}
</button>
</div>
</form>
</div>
</div>
</td> </td>
</tr> </tr>
</c:forEach> </c:forEach>