Several bugfixes for workflow handling (for Content Items)

pull/20/head
Jens Pelzetter 2022-01-22 17:55:33 +01:00
parent ba96a3c280
commit 8a6797e3c9
7 changed files with 185 additions and 62 deletions

View File

@ -33,6 +33,7 @@ import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.security.Permission;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.Role;
import org.libreccm.security.RoleManager;
@ -49,12 +50,17 @@ import javax.persistence.TypedQuery;
import org.libreccm.security.Shiro;
import org.libreccm.security.User;
import org.libreccm.security.UserRepository;
import org.libreccm.workflow.AssignableTask;
import org.libreccm.workflow.AssignableTaskManager;
import org.libreccm.workflow.Task;
import org.libreccm.workflow.TaskAssignment;
import org.libreccm.workflow.TaskManager;
import org.libreccm.workflow.Workflow;
import org.libreccm.workflow.WorkflowRepository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
@ -69,6 +75,9 @@ public class ContentItemRepository
private static final long serialVersionUID = -145167586339461600L;
@Inject
private AssignableTaskManager assignableTaskManager;
@Inject
private CategoryManager categoryManager;
@ -691,12 +700,29 @@ public class ContentItemRepository
if (draft.getWorkflow() != null) {
final Workflow workflow = draft.getWorkflow();
for (final Task task : workflow.getTasks()) {
final List<Task> tasks = new ArrayList<>(workflow.getTasks());
for (final Task task : tasks) {
if (task instanceof AssignableTask) {
final AssignableTask assignable = (AssignableTask) task;
final List<Role> assignedRoles = assignable
.getAssignments()
.stream()
.map(TaskAssignment::getRole)
.collect(Collectors.toList());
for (final Role role : assignedRoles) {
assignableTaskManager.retractTask(assignable, role);
}
}
taskManager.removeTask(workflow, task);
}
workflowRepo.delete(workflow);
}
final List<Permission> permissions = draft.getPermissions();
for (final Permission permission : permissions) {
getEntityManager().remove(permission);
}
super.delete(draft);
}

View File

@ -197,7 +197,7 @@ public class ContentType extends CcmObject implements Serializable {
}
protected void setDefaultWorkflow(final Workflow defaultWorkflow) {
if (!defaultWorkflow.isAbstractWorkflow()) {
if (defaultWorkflow != null && !defaultWorkflow.isAbstractWorkflow()) {
throw new IllegalArgumentException(
"The provided workflow is not an abstract workflow.");
}

View File

@ -59,21 +59,31 @@ public class ContentTypeManager {
* of {@link ContentItem}.
*/
@SuppressWarnings("unchecked")
public Class<? extends ContentItem> classNameToClass(final String className) {
public Class<? extends ContentItem> classNameToClass(
final String className
) {
final Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(String.format(
"No class with the name \"%s\" exists.", className),
ex);
throw new IllegalArgumentException(
String.format(
"No class with the name \"%s\" exists.",
className
),
ex
);
}
if (ContentItem.class.isAssignableFrom(clazz)) {
return (Class<? extends ContentItem>) clazz;
} else {
throw new IllegalArgumentException(String.format(
"Class \"%s\" is not a content type.", className));
throw new IllegalArgumentException(
String.format(
"Class \"%s\" is not a content type.",
className
)
);
}
}
@ -89,13 +99,39 @@ public class ContentTypeManager {
public void setDefaultLifecycle(
@RequiresPrivilege(AdminPrivileges.ADMINISTER_CONTENT_TYPES)
final ContentType type,
final LifecycleDefinition definition) {
final LifecycleDefinition definition
) {
Objects.requireNonNull(
type,
"Can't set default lifecycle for ContentType null."
);
Objects.requireNonNull(
definition,
"Can't use LifecycleDefinition null as default lifecycle."
);
type.setDefaultLifecycle(definition);
typeRepo.save(type);
}
/**
* Removes the default lifecycle from a content type. This does not delete
* the lifecycle definition.
*
* @param type The type from which the default lifecycle is removed.
*/
@Transactional(Transactional.TxType.REQUIRED)
@AuthorizationRequired
public void removeDefaultLifecycle(final ContentType type) {
Objects
.requireNonNull(
type,
"Can't remove default lifecycle from ContentType null."
)
.setDefaultLifecycle(null);
typeRepo.save(type);
}
/**
* Sets the default workflow to use for new items of a content type.
*
@ -108,14 +144,22 @@ public class ContentTypeManager {
public void setDefaultWorkflow(
@RequiresPrivilege(AdminPrivileges.ADMINISTER_CONTENT_TYPES)
final ContentType type,
final Workflow template) {
Objects.requireNonNull(type);
Objects.requireNonNull(template);
final Workflow template
) {
Objects.requireNonNull(
type,
"Can't set default workflow for ContentType null."
);
Objects.requireNonNull(
template,
"Can't use Workflow template null as default workflow."
);
if (!template.isAbstractWorkflow()) {
throw new IllegalArgumentException(
"The provided workflow is not an abstract workflow.");
"The provided workflow is not a workflow template "
+ "(abstract workflow)."
);
}
type.setDefaultWorkflow(template);
@ -123,6 +167,23 @@ public class ContentTypeManager {
typeRepo.save(type);
}
/**
* Removes the default workflow from a {@link ContentType}. This does not
* delete the workflow template.
*
* @param type The type from which the default workflow is removed.
*/
public void removeDefaultWorkflow(final ContentType type) {
Objects
.requireNonNull(
type,
"Can't remove default workflow from ContentType null."
)
.setDefaultWorkflow(null);
typeRepo.save(type);
}
/**
* Creates a permission granting the {@link TypePrivileges#USE_TYPE}
* privilege to a role.
@ -135,8 +196,8 @@ public class ContentTypeManager {
public void grantUsageOfType(
@RequiresPrivilege(AdminPrivileges.ADMINISTER_CONTENT_TYPES)
final ContentType type,
final Role role) {
final Role role
) {
permissionManager.grantPrivilege(TypePrivileges.USE_TYPE, role, type);
}
@ -153,8 +214,8 @@ public class ContentTypeManager {
public void denyUsageOnType(
@RequiresPrivilege(AdminPrivileges.ADMINISTER_CONTENT_TYPES)
final ContentType type,
final Role role) {
final Role role
) {
permissionManager.revokePrivilege(TypePrivileges.USE_TYPE, role, type);
}

View File

@ -497,7 +497,9 @@ public class FolderManager {
final List<String> tokens = new ArrayList<>();
if (folder.getParentFolder() != null) {
tokens.add(folder.getName());
}
Folder current = folder;
while (getParentFolder(current).isPresent()) {
current = getParentFolder(current).get();

View File

@ -36,6 +36,8 @@ import org.libreccm.workflow.WorkflowManager;
import org.libreccm.workflow.WorkflowRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionManager;
import org.librecms.contentsection.ContentType;
import org.librecms.contentsection.ContentTypeManager;
import java.util.Collections;
import java.util.List;
@ -96,6 +98,12 @@ public class ConfigurationWorkflowController {
@Inject
private ContentSectionsUi sectionsUi;
/**
* Manager for content types.
*/
@Inject
private ContentTypeManager typeManager;
/**
* Used for globaliazation stuff.
*/
@ -381,6 +389,14 @@ public class ConfigurationWorkflowController {
sectionManager.removeWorkflowTemplateFromContentSection(
workflow, section
);
final List<ContentType> typesUsingWorkflow = section
.getContentTypes()
.stream()
.filter(type -> workflow.equals(type.getDefaultWorkflow()))
.collect(Collectors.toList());
for(final ContentType typeUsingWorkflow : typesUsingWorkflow) {
typeManager.removeDefaultWorkflow(typeUsingWorkflow);
}
workflowRepo.delete(workflow);
return String.format(

View File

@ -37,6 +37,7 @@ import javax.transaction.Transactional;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.persistence.TypedQuery;
@ -48,6 +49,7 @@ import javax.persistence.TypedQuery;
*/
@RequestScoped
public class TaskManager implements Serializable {
private static final long serialVersionUID = -5605541655413527137L;
private static final Logger LOGGER = LogManager.getLogger(TaskManager.class);
@ -91,6 +93,25 @@ public class TaskManager implements Serializable {
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void removeTask(final Workflow workflow, final Task task) {
final List<Task> blockedTasks = task
.getBlockedTasks()
.stream()
.map(TaskDependency::getBlockedTask)
.collect(Collectors.toList());
for (final Task blockedTask : blockedTasks) {
removeDependentTask(task, blockedTask);
}
final List<Task> blockingTasks = task
.getBlockingTasks()
.stream()
.map(TaskDependency::getBlockingTask)
.collect(Collectors.toList());
for (final Task blockingTask : blockingTasks) {
removeDependentTask(blockingTask, task);
}
workflow.removeTask(task);
task.setWorkflow(null);
@ -110,17 +131,19 @@ public class TaskManager implements Serializable {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void addDependentTask(final Task blockingTask,
final Task blockedTask)
throws CircularTaskDependencyException {
public void addDependentTask(
final Task blockingTask,
final Task blockedTask
) throws CircularTaskDependencyException {
Objects.requireNonNull(blockedTask);
Objects.requireNonNull(blockingTask);
LOGGER.debug("Adding a dependency between task {} (blocking task) "
LOGGER.debug(
"Adding a dependency between task {} (blocking task) "
+ "and task {} (blocked task)...",
Objects.toString(blockingTask),
Objects.toString(blockedTask));
Objects.toString(blockedTask)
);
LOGGER.debug("Checking for circular dependencies...");
checkForCircularDependencies(blockedTask, blockingTask);
@ -133,10 +156,12 @@ public class TaskManager implements Serializable {
final Boolean dependencyExists = query.getSingleResult();
if (dependencyExists) {
LOGGER.info("Dependency between task {} (blocking task) "
LOGGER.info(
"Dependency between task {} (blocking task) "
+ "and task {} (blocked task) already exists.",
Objects.toString(blockingTask),
Objects.toString(blockedTask));
Objects.toString(blockedTask)
);
return;
}
@ -150,7 +175,6 @@ public class TaskManager implements Serializable {
// blockingTask.addDependentTask(blockedTask);
// blockedTask.addDependsOn(blockingTask);
entityManager.persist(dependency);
taskRepo.save(blockedTask);
taskRepo.save(blockingTask);
@ -174,7 +198,7 @@ public class TaskManager implements Serializable {
query.setParameter("blockingTask", blockingTask);
final List<TaskDependency> dependencies = query.getResultList();
for(final TaskDependency dependency : dependencies) {
for (final TaskDependency dependency : dependencies) {
entityManager.remove(dependency);
blockingTask.removeBlockedTask(dependency);
@ -183,7 +207,6 @@ public class TaskManager implements Serializable {
// blockingTask.removeDependentTask(blockedTask);
// blockedTask.removeDependsOn(blockingTask);
taskRepo.save(blockedTask);
taskRepo.save(blockingTask);
}
@ -343,7 +366,6 @@ public class TaskManager implements Serializable {
* @param task
*/
protected void updateState(final Task task) {
Objects.requireNonNull(task);
LOGGER.debug("Updating state for task {}...",
@ -355,7 +377,8 @@ public class TaskManager implements Serializable {
return;
}
for (final TaskDependency blockingTaskDependency : task.getBlockingTasks()) {
for (final TaskDependency blockingTaskDependency : task
.getBlockingTasks()) {
final Task blockingTask = blockingTaskDependency.getBlockingTask();
LOGGER.debug("Checking dependency {}...",

View File

@ -291,6 +291,9 @@ public class WorkflowManager implements Serializable {
)
);
}
taskRepo.save(task);
workflowRepo.save(workflow);
}
/**
@ -434,21 +437,14 @@ public class WorkflowManager implements Serializable {
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public void start(final Workflow workflow) {
final WorkflowState oldState = workflow.getState();
workflow.setState(WorkflowState.STARTED);
if (oldState == WorkflowState.INIT) {
workflow.setActive(true);
updateState(workflow);
// for (final Task current : workflow.getTasks()) {
// current.setActive(true);
// taskManager.updateState(current);
// }
final List<Task> tasks = workflow.getTasks();
if (!tasks.isEmpty()) {
final Task firstTask = tasks.get(0);
firstTask.setActive(true);
firstTask.setTaskState(TaskState.ENABLED);
taskManager.updateState(firstTask);
if (firstTask instanceof AssignableTask) {
@ -465,7 +461,6 @@ public class WorkflowManager implements Serializable {
}
}
}
}
workflowRepo.save(workflow);
}