Bugfixes for workflow handling.

pull/10/head
Jens Pelzetter 2021-12-11 18:57:53 +01:00
parent eef4210785
commit 7dd51f4c46
8 changed files with 268 additions and 76 deletions

View File

@ -301,19 +301,19 @@ public class DocumentWorkflowController {
* @param documentPath The path of the currentd document. * @param documentPath The path of the currentd document.
* @param newWorkflowUuid The UUID of the the workflow definition form * @param newWorkflowUuid The UUID of the the workflow definition form
* which the new workflow is created. * which the new workflow is created.
* @param returnUrl The URL to return to. * @param returnUrlParam The URL to return to.
* *
* @return A redirect to the {@code returnUrl}. * @return A redirect to the {@code returnUrl}.
*/ */
@POST @POST
@Path("@workflow/@applyAlternative/{workflowIdentifier}") @Path("/apply-alternative")
@AuthorizationRequired @AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public String applyAlternateWorkflow( public String applyAlternateWorkflow(
@PathParam("sectionIdentifier") 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 returnUrlParam
) { ) {
final Optional<ContentSection> sectionResult = sectionsUi final Optional<ContentSection> sectionResult = sectionsUi
.findContentSection(sectionIdentifier); .findContentSection(sectionIdentifier);
@ -347,18 +347,35 @@ public class DocumentWorkflowController {
); );
} }
final Optional<Workflow> workflowResult = section final Optional<Workflow> workflowTemplateResult = section
.getWorkflowTemplates() .getWorkflowTemplates()
.stream() .stream()
.filter(template -> template.getUuid().equals(newWorkflowUuid)) .filter(template -> template.getUuid().equals(newWorkflowUuid))
.findAny(); .findAny();
if (!workflowResult.isPresent()) { if (!workflowTemplateResult.isPresent()) {
models.put("section", section.getLabel()); models.put("section", section.getLabel());
models.put("workflowUuid", newWorkflowUuid); models.put("workflowUuid", newWorkflowUuid);
return "org/librecms/ui/contentsection/documents/workflow-not-found.xhtml"; return "org/librecms/ui/contentsection/documents/workflow-not-found.xhtml";
} }
workflowManager.createWorkflow(workflowResult.get(), item); final String returnUrl;
if (returnUrlParam.startsWith("/@contentsections")) {
returnUrl = returnUrlParam.substring("/@contentsections".length());
} else if(returnUrlParam.startsWith("@contentsections")) {
returnUrl = returnUrlParam.substring("@contentsections".length());
} else {
returnUrl = returnUrlParam;
}
final Workflow oldWorkflow = item.getWorkflow();
if (oldWorkflow != null) {
workflowManager.removeWorkflowFromObject(oldWorkflow, item);
}
final Workflow workflow = workflowManager.createWorkflow(
workflowTemplateResult.get(), item
);
item.setWorkflow(workflow);
return String.format("redirect:%s", returnUrl); return String.format("redirect:%s", returnUrl);
} }

View File

@ -32,6 +32,7 @@ import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderManager; import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.privileges.ItemPrivileges; import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.ui.contentsections.FolderBreadcrumbsModel; import org.librecms.ui.contentsections.FolderBreadcrumbsModel;
import org.librecms.ui.contentsections.WorkflowTemplateListModel;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -45,6 +46,7 @@ import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
/** /**
@ -166,6 +168,11 @@ public class SelectedDocumentModel {
*/ */
private List<TaskListEntry> allTasks; private List<TaskListEntry> allTasks;
/**
* Available workflows that can be assigned to the item.
*/
private List<WorkflowTemplateListModel> availableWorkflows;
public String getItemName() { public String getItemName() {
return itemName; return itemName;
} }
@ -216,12 +223,17 @@ public class SelectedDocumentModel {
); );
} }
public List<WorkflowTemplateListModel> getAvailableWorkflows() {
return Collections.unmodifiableList(availableWorkflows);
}
/** /**
* Sets the current content item/document and sets the properties of this * Sets the current content item/document and sets the properties of this
* model based on the item. * model based on the item.
* *
* @param item * @param item
*/ */
@Transactional(Transactional.TxType.REQUIRED)
void setContentItem(final ContentItem item) { void setContentItem(final ContentItem item) {
this.item = Objects.requireNonNull(item); this.item = Objects.requireNonNull(item);
itemName = item.getDisplayName(); itemName = item.getDisplayName();
@ -261,6 +273,14 @@ public class SelectedDocumentModel {
currentTask.setCurrentTask(true); currentTask.setCurrentTask(true);
} }
} }
availableWorkflows = item
.getContentType()
.getContentSection()
.getWorkflowTemplates()
.stream()
.map(this::buildWorkflowTemplateListModel)
.collect(Collectors.toList());
} }
/** /**
@ -395,4 +415,30 @@ public class SelectedDocumentModel {
.toString(); .toString();
} }
/**
* Helper method for building a {@link WorkflowTemplateListModel} for a
* {@link Workflow}.
*
* @param workflow The workflow.
*
* @return A {@link WorkflowTemplateListModel} for the {@code workflow}.
*/
private WorkflowTemplateListModel buildWorkflowTemplateListModel(
final Workflow workflow
) {
final WorkflowTemplateListModel model = new WorkflowTemplateListModel();
model.setDescription(
globalizationHelper.getValueFromLocalizedString(
workflow.getDescription()
)
);
model.setHasTasks(!workflow.getTasks().isEmpty());
model.setName(
globalizationHelper.getValueFromLocalizedString(workflow.getName())
);
model.setUuid(workflow.getUuid());
model.setWorkflowId(workflow.getWorkflowId());
return model;
}
} }

View File

@ -1,5 +1,6 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]> <!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml" <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml"> <ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
@ -29,10 +30,72 @@
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_workflow_label']}: #{CmsAdminMessages['contentsection.document.authoring.workflow.active_workflow_label']}:
#{CmsSelectedDocumentModel.workflowName} #{CmsSelectedDocumentModel.workflowName}
<button class="btn btn-secondary btn-sm" <button class="btn btn-secondary btn-sm"
data-toggle="modal"
data-target="#change-workflow-dialog"
disabled="#{!CmsSelectedDocumentModel.canChangeWorkflow ? 'disabled': ''}" disabled="#{!CmsSelectedDocumentModel.canChangeWorkflow ? 'disabled': ''}"
type="button"> type="button">
#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow']} #{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow']}
</button> </button>
<c:if test="#{CmsSelectedDocumentModel.canChangeWorkflow}">
<div aria-labelledby="change-workflow-dialog-title"
aria-hidden="true"
class="modal fade"
id="change-workflow-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/apply-alternative"
class="modal-content"
method="post">
<div class="modal-header">
<h3 class="modal-title"
id="change-workflow-dialog-title">
#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow.title']}
</h3>
<button aria-label="#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow.close']} "
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</div>
<div class="modal-body">
<c:forEach items="#{CmsSelectedDocumentModel.availableWorkflows}"
var="workflow">
<div class="form-group form-check">
<input
aria-describedby="workflow-#{workflow.uuid}-description"
id="workflow-#{workflow.uuid}"
name="newWorkflowUuid"
type="radio"
value="#{workflow.uuid}"/>
<label for="workflow-#{workflow.uuid}">#{workflow.name}</label>
<span class="form-text text-muted"
id="workflow-#{workflow.uuid}-description">
#{workflow.description}
</span>
</div>
</c:forEach>
<input
name="returnUrl"
type="hidden"
value="#{authoringStep}" />
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsAdminMessages['contentsection.document.authoring.workflow.change_workflow.submit']}
</button>
</div>
</form>
</div>
</div>
</c:if>
</p> </p>
<p> <p>
<c:choose> <c:choose>
@ -40,7 +103,7 @@
#{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']} #{CmsAdminMessages['contentsection.document.authoring.workflow.active_task']}
#{CmsSelectedDocumentModel.currentTask.label} #{CmsSelectedDocumentModel.currentTask.label}
</c:when> </c:when>
<c:otherwise> <c:when test="#{CmsSelectedDocumentModel.workflowName != null}">
#{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" <form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@workflow/@start"
method="post"> method="post">
@ -53,6 +116,9 @@
#{CmsAdminMessages['contentsection.document.authoring.workflow.start']} #{CmsAdminMessages['contentsection.document.authoring.workflow.start']}
</button> </button>
</form> </form>
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.document.authoring.workflow.none']}
</c:otherwise> </c:otherwise>
</c:choose> </c:choose>
<c:if test="#{CmsSelectedDocumentModel.currentTask != null}"> <c:if test="#{CmsSelectedDocumentModel.currentTask != null}">

View File

@ -7,7 +7,7 @@
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml"> <ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:param name="authoringStep" <ui:param name="authoringStep"
value="/libreccm/@contentsections/info/documents/test-article/@article-basicproperties" /> value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-basicproperties" />
<ui:define name="authoringStep"> <ui:define name="authoringStep">
<h2>#{CmsArticleMessageBundle.getMessage('basicproperties.header', [CmsArticlePropertiesStep.name])}</h2> <h2>#{CmsArticleMessageBundle.getMessage('basicproperties.header', [CmsArticlePropertiesStep.name])}</h2>

View File

@ -1010,3 +1010,7 @@ pages.page.details.dialog.category.label=Category
pages.page.details.dialog.properties.label=Properties pages.page.details.dialog.properties.label=Properties
pages.page.details.dialog.properties.key=Name pages.page.details.dialog.properties.key=Name
pages.page.details.dialog.properties.value=Value pages.page.details.dialog.properties.value=Value
contentsection.document.authoring.workflow.none=No workflow assigned
contentsection.document.authoring.workflow.change_workflow.title=Assign workflow
contentsection.document.authoring.workflow.change_workflow.submit=Assign workflow
contentsection.document.authoring.workflow.change_workflow.close=Cancel

View File

@ -1011,3 +1011,7 @@ pages.page.details.dialog.category.label=Kategorie
pages.page.details.dialog.properties.label=Eigenschaften pages.page.details.dialog.properties.label=Eigenschaften
pages.page.details.dialog.properties.key=Name pages.page.details.dialog.properties.key=Name
pages.page.details.dialog.properties.value=Wert pages.page.details.dialog.properties.value=Wert
contentsection.document.authoring.workflow.none=Kein Arbeitsablauf zugewiesen
contentsection.document.authoring.workflow.change_workflow.title=Arbeitsablauf zuweisen
contentsection.document.authoring.workflow.change_workflow.submit=Arbeitsablauf zuweisen
contentsection.document.authoring.workflow.change_workflow.close=Abbrechen

View File

@ -71,10 +71,12 @@ import java.util.Optional;
*/ */
@RequestScoped @RequestScoped
public class WorkflowManager implements Serializable { public class WorkflowManager implements Serializable {
private static final long serialVersionUID = -6939804120313699606L; private static final long serialVersionUID = -6939804120313699606L;
private final static Logger LOGGER = LogManager.getLogger( private final static Logger LOGGER = LogManager.getLogger(
WorkflowManager.class); WorkflowManager.class
);
@Inject @Inject
private EntityManager entityManager; private EntityManager entityManager;
@ -105,7 +107,8 @@ public class WorkflowManager implements Serializable {
@PostConstruct @PostConstruct
private void init() { private void init() {
final KernelConfig kernelConfig = confManager.findConfiguration( final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class); KernelConfig.class
);
defaultLocale = kernelConfig.getDefaultLocale(); defaultLocale = kernelConfig.getDefaultLocale();
} }
@ -121,23 +124,29 @@ public class WorkflowManager implements Serializable {
@AuthorizationRequired @AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public Workflow createWorkflow(final Workflow template, public Workflow createWorkflow(
final CcmObject object) { final Workflow template, final CcmObject object
Objects.requireNonNull(template, ) {
"Can't create a workflow without a template."); Objects.requireNonNull(
template, "Can't create a workflow without a template."
);
if (!template.isAbstractWorkflow()) { if (!template.isAbstractWorkflow()) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"The provided template is not an abstract workflow"); "The provided template is not an abstract workflow"
);
} }
Objects.requireNonNull(object, Objects.requireNonNull(
"Can't create a workflow without an object."); object, "Can't create a workflow without an object."
);
final Workflow workflow = new Workflow(); final Workflow workflow = new Workflow();
final LocalizedString name = new LocalizedString(); final LocalizedString name = new LocalizedString();
template.getName().getValues().forEach( template
(locale, str) -> name.addValue(locale, str)); .getName()
.getValues()
.forEach((locale, str) -> name.addValue(locale, str));
workflow.setName(name); workflow.setName(name);
final LocalizedString description = new LocalizedString(); final LocalizedString description = new LocalizedString();
@ -156,13 +165,16 @@ public class WorkflowManager implements Serializable {
.forEach(taskTemplate -> createTask(workflow, taskTemplate, tasks)); .forEach(taskTemplate -> createTask(workflow, taskTemplate, tasks));
template template
.getTasks() .getTasks()
.forEach(taskTemplate -> { .forEach(
fixTaskDependencies(taskTemplate, taskTemplate -> {
tasks.get(taskTemplate.getTaskId()), fixTaskDependencies(
tasks); taskTemplate,
tasks.get(taskTemplate.getTaskId()),
tasks
);
}); });
for(final Map.Entry<Long, Task> task : tasks.entrySet()) { for (final Map.Entry<Long, Task> task : tasks.entrySet()) {
task.getValue().setTaskState(TaskState.DISABLED); task.getValue().setTaskState(TaskState.DISABLED);
} }
@ -175,6 +187,20 @@ public class WorkflowManager implements Serializable {
return workflow; return workflow;
} }
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void removeWorkflowFromObject(
final Workflow workflow, final CcmObject object) {
Objects.requireNonNull(workflow, "Can't delete null.");
Objects.requireNonNull(object);
for(final Task task : workflow.getTasks()) {
taskRepo.delete(task);
}
workflowRepo.delete(workflow);
}
/** /**
* Helper method for * Helper method for
* {@link #createWorkflow(org.libreccm.workflow.WorkflowTemplate, org.libreccm.core.CcmObject)} * {@link #createWorkflow(org.libreccm.workflow.WorkflowTemplate, org.libreccm.core.CcmObject)}
@ -184,15 +210,19 @@ public class WorkflowManager implements Serializable {
* @param template The template for the task from the workflow template. * @param template The template for the task from the workflow template.
* @param tasks A map for storing the new tasks. * @param tasks A map for storing the new tasks.
*/ */
private void createTask(final Workflow workflow, private void createTask(
final Task template, final Workflow workflow,
final Map<Long, Task> tasks) { final Task template,
final Map<Long, Task> tasks
) {
final Class<? extends Task> templateClass = template.getClass(); final Class<? extends Task> templateClass = template.getClass();
final Task task; final Task task;
try { try {
task = templateClass.getDeclaredConstructor().newInstance(); task = templateClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) { } catch (InstantiationException
| IllegalAccessException
| NoSuchMethodException
| InvocationTargetException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
@ -227,8 +257,9 @@ public class WorkflowManager implements Serializable {
final LocalizedString localized = (LocalizedString) value; final LocalizedString localized = (LocalizedString) value;
final LocalizedString copy = new LocalizedString(); final LocalizedString copy = new LocalizedString();
localized.getValues().forEach( localized
(locale, str) -> copy.addValue(locale, str)); .getValues()
.forEach((locale, str) -> copy.addValue(locale, str));
writeMethod.invoke(task, copy); writeMethod.invoke(task, copy);
} else { } else {
@ -247,16 +278,18 @@ public class WorkflowManager implements Serializable {
if (template instanceof AssignableTask) { if (template instanceof AssignableTask) {
final AssignableTask assignableTemplate final AssignableTask assignableTemplate
= (AssignableTask) template; = (AssignableTask) template;
final AssignableTask assignableTask = (AssignableTask) task; final AssignableTask assignableTask = (AssignableTask) task;
assignableTemplate assignableTemplate
.getAssignments() .getAssignments()
.stream() .stream()
.map(TaskAssignment::getRole) .map(TaskAssignment::getRole)
.forEach(role -> { .forEach(
assignableTaskManager.assignTask(assignableTask, role); role -> assignableTaskManager.assignTask(
}); assignableTask, role
)
);
} }
} }
@ -270,19 +303,19 @@ public class WorkflowManager implements Serializable {
* @param task * @param task
* @param tasks * @param tasks
*/ */
private void fixTaskDependencies(final Task template, private void fixTaskDependencies(
final Task task, final Task template, final Task task, final Map<Long, Task> tasks
final Map<Long, Task> tasks) { ) {
if (template.getBlockedTasks() != null if (template.getBlockedTasks() != null
&& !template.getBlockedTasks().isEmpty()) { && !template.getBlockedTasks().isEmpty()) {
for (final TaskDependency blocked : template.getBlockedTasks()) { for (final TaskDependency blocked : template.getBlockedTasks()) {
final Task blockingTask = tasks final Task blockingTask = tasks.get(
.get(blocked.getBlockingTask().getTaskId()); blocked.getBlockingTask().getTaskId()
final Task blockedTask = tasks );
.get(blocked.getBlockedTask().getTaskId()); final Task blockedTask = tasks.get(
blocked.getBlockedTask().getTaskId()
);
try { try {
taskManager.addDependentTask(blockingTask, blockedTask); taskManager.addDependentTask(blockingTask, blockedTask);
} catch (CircularTaskDependencyException ex) { } catch (CircularTaskDependencyException ex) {
@ -297,14 +330,17 @@ public class WorkflowManager implements Serializable {
// -> task.addDependentTask(tasks.get(dependent.getTaskId()))); // -> task.addDependentTask(tasks.get(dependent.getTaskId())));
// } // }
for (final TaskDependency blocking : template.getBlockingTasks()) { for (final TaskDependency blocking : template.getBlockingTasks()) {
final Task blockingTask = tasks.get(
final Task blockingTask = tasks blocking.getBlockingTask().getTaskId()
.get(blocking.getBlockingTask().getTaskId()); );
final Task blockedTask = tasks final Task blockedTask = tasks.get(
.get(blocking.getBlockedTask().getTaskId()); blocking.getBlockedTask().getTaskId()
);
try { try {
taskManager.addDependentTask(blockingTask, blockedTask); taskManager.addDependentTask(
} catch(CircularTaskDependencyException ex) { blockingTask, blockedTask
);
} catch (CircularTaskDependencyException ex) {
throw new UnexpectedErrorException(ex); throw new UnexpectedErrorException(ex);
} }
@ -331,14 +367,18 @@ public class WorkflowManager implements Serializable {
public List<Task> findEnabledTasks(final Workflow workflow) { public List<Task> findEnabledTasks(final Workflow workflow) {
if (workflow.getState() == WorkflowState.DELETED if (workflow.getState() == WorkflowState.DELETED
|| workflow.getState() == WorkflowState.STOPPED) { || workflow.getState() == WorkflowState.STOPPED) {
LOGGER.debug(String.format("Workflow state is \"%s\". Workflow " LOGGER.debug(
+ "has no enabled tasks.", String.format(
workflow.getState().toString())); "Workflow state is \"%s\". Workflow has no enabled tasks.",
workflow.getState().toString()
)
);
return Collections.emptyList(); return Collections.emptyList();
} }
final TypedQuery<Task> query = entityManager.createNamedQuery( final TypedQuery<Task> query = entityManager.createNamedQuery(
"Task.findEnabledTasks", Task.class); "Task.findEnabledTasks", Task.class
);
query.setParameter("workflow", workflow); query.setParameter("workflow", workflow);
return Collections.unmodifiableList(query.getResultList()); return Collections.unmodifiableList(query.getResultList());
@ -357,7 +397,8 @@ public class WorkflowManager implements Serializable {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<Task> findFinishedTasks(final Workflow workflow) { public List<Task> findFinishedTasks(final Workflow workflow) {
final TypedQuery<Task> query = entityManager.createNamedQuery( final TypedQuery<Task> query = entityManager.createNamedQuery(
"Task.findFinishedTasks", Task.class); "Task.findFinishedTasks", Task.class
);
query.setParameter("workflow", workflow); query.setParameter("workflow", workflow);
return Collections.unmodifiableList(query.getResultList()); return Collections.unmodifiableList(query.getResultList());
@ -376,7 +417,8 @@ public class WorkflowManager implements Serializable {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public List<AssignableTask> findOverdueTasks(final Workflow workflow) { public List<AssignableTask> findOverdueTasks(final Workflow workflow) {
final TypedQuery<AssignableTask> query = entityManager.createNamedQuery( final TypedQuery<AssignableTask> query = entityManager.createNamedQuery(
"AssignableTask.findOverdueTasks", AssignableTask.class); "AssignableTask.findOverdueTasks", AssignableTask.class
);
query.setParameter("workflow", workflow); query.setParameter("workflow", workflow);
query.setParameter("now", new Date()); query.setParameter("now", new Date());
@ -392,7 +434,6 @@ public class WorkflowManager implements Serializable {
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public void start(final Workflow workflow) { public void start(final Workflow workflow) {
final WorkflowState oldState = workflow.getState(); final WorkflowState oldState = workflow.getState();
workflow.setState(WorkflowState.STARTED); workflow.setState(WorkflowState.STARTED);
@ -414,10 +455,13 @@ public class WorkflowManager implements Serializable {
final Optional<User> currentUser = shiro.getUser(); final Optional<User> currentUser = shiro.getUser();
if (currentUser.isPresent() if (currentUser.isPresent()
&& assignableTaskManager && assignableTaskManager
.isAssignedTo((AssignableTask) firstTask, .isAssignedTo(
currentUser.get())) { (AssignableTask) firstTask,
assignableTaskManager currentUser.get()
.lockTask((AssignableTask) firstTask); )) {
assignableTaskManager.lockTask(
(AssignableTask) firstTask
);
} }
} }
} }
@ -437,7 +481,8 @@ public class WorkflowManager implements Serializable {
private void updateState(final Workflow workflow) { private void updateState(final Workflow workflow) {
if (workflow.getTasksState() == TaskState.ENABLED) { if (workflow.getTasksState() == TaskState.ENABLED) {
final TypedQuery<Long> query = entityManager.createNamedQuery( final TypedQuery<Long> query = entityManager.createNamedQuery(
"Task.countUnfinishedAndActiveTasksForWorkflow", Long.class); "Task.countUnfinishedAndActiveTasksForWorkflow", Long.class
);
query.setParameter("workflow", workflow); query.setParameter("workflow", workflow);
final Long result = query.getSingleResult(); final Long result = query.getSingleResult();
@ -451,7 +496,8 @@ public class WorkflowManager implements Serializable {
if (workflow.getTasksState() == TaskState.FINISHED) { if (workflow.getTasksState() == TaskState.FINISHED) {
final TypedQuery<Long> query = entityManager.createNamedQuery( final TypedQuery<Long> query = entityManager.createNamedQuery(
"Task.countUnfinishedTasksForWorkflow", Long.class); "Task.countUnfinishedTasksForWorkflow", Long.class
);
query.setParameter("workflow", workflow); query.setParameter("workflow", workflow);
final Long result = query.getSingleResult(); final Long result = query.getSingleResult();
@ -485,9 +531,12 @@ public class WorkflowManager implements Serializable {
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
public void finish(final Workflow workflow) { public void finish(final Workflow workflow) {
if (workflow.getTasksState() != TaskState.ENABLED) { if (workflow.getTasksState() != TaskState.ENABLED) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(
"Workflow \"%s\" is not enabled.", String.format(
workflow.getName().getValue(defaultLocale))); "Workflow \"%s\" is not enabled.",
workflow.getName().getValue(defaultLocale)
)
);
} }
workflow.setTasksState(TaskState.FINISHED); workflow.setTasksState(TaskState.FINISHED);
@ -510,8 +559,10 @@ public class WorkflowManager implements Serializable {
switch (workflow.getTasksState()) { switch (workflow.getTasksState()) {
case DISABLED: case DISABLED:
LOGGER.debug("Workflow \"{}\" is disabled; enabling it.", LOGGER.debug(
workflow.getName().getValue(defaultLocale)); "Workflow \"{}\" is disabled; enabling it.",
workflow.getName().getValue(defaultLocale)
);
workflow.setTasksState(TaskState.ENABLED); workflow.setTasksState(TaskState.ENABLED);
workflowRepo.save(workflow); workflowRepo.save(workflow);
break; break;
@ -521,10 +572,12 @@ public class WorkflowManager implements Serializable {
workflowRepo.save(workflow); workflowRepo.save(workflow);
break; break;
default: default:
LOGGER.debug("Workflow \"{}\" has tasksState \"{}\", " LOGGER.debug(
+ "#enable(Workflow) does nothing.", "Workflow \"{}\" has tasksState \"{}\", "
workflow.getName().getValue(defaultLocale), + "#enable(Workflow) does nothing.",
workflow.getTasksState()); workflow.getName().getValue(defaultLocale),
workflow.getTasksState()
);
break; break;
} }
} }

View File

@ -76,6 +76,7 @@ public class WorkflowRepository extends AbstractEntityRepository<Long, Workflow>
* @return An {@link Optional} containing the {@link Workflow} identified by * @return An {@link Optional} containing the {@link Workflow} identified by
* the provided UUID. * the provided UUID.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public Optional<Workflow> findByUuid(final String uuid) { public Optional<Workflow> findByUuid(final String uuid) {
if (uuid == null) { if (uuid == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -102,6 +103,7 @@ public class WorkflowRepository extends AbstractEntityRepository<Long, Workflow>
* {@code object} if the object has a workflow. Otherwise an empty * {@code object} if the object has a workflow. Otherwise an empty
* {@link Optional} is returned. * {@link Optional} is returned.
*/ */
@Transactional(Transactional.TxType.REQUIRED)
public Optional<Workflow> findWorkflowForObject(final CcmObject object) { public Optional<Workflow> findWorkflowForObject(final CcmObject object) {
if (object == null) { if (object == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(