Skip to content

Commit

Permalink
Add authority to link to processes of unassigned projects
Browse files Browse the repository at this point in the history
  • Loading branch information
solth committed Jan 25, 2024
1 parent ea6ce02 commit bc2bb03
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ public Authority getByTitle(String title) throws DAOException {
if (!authorities.isEmpty()) {
return authorities.get(0);
}
throw new DAOException("Object cannot be found in database");
throw new DAOException(String.format("Authority '%s' cannot be found in database", title));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--
-- (c) Kitodo. Key to digital objects e. V. <[email protected]>
--
-- This file is part of the Kitodo project.
--
-- It is licensed under GNU General Public License version 3 or later.
--
-- For the full copyright and license information, please read the
-- GPL3-License.txt file that was distributed with this source code.
--

SET SQL_SAFE_UPDATES = 0;

-- add authorities/permission for linking to parent processes of unassigned projects
INSERT IGNORE INTO authority (title) VALUES ('linkToProcessesOfUnassignedProjects_clientAssignable');

SET SQL_SAFE_UPDATES = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -1099,4 +1099,13 @@ public boolean hasAuthorityToRunKitodoScripts() {
public boolean hasAuthorityToRenameMediaFiles() {
return securityAccessService.hasAuthorityToRenameMediaFiles();
}

/**
* Check if the current user has the permission to link processes to parent processes of unassigned projects.
*
* @return true if the current user has the permission to link processes to parent processes of unassigned projects.
*/
public boolean hasAuthorityToLinkToProcessesOfUnassignedProjects() {
return securityAccessService.hasAuthorityToLinkToProcessesOfUnassignedProjects();
}
}
21 changes: 21 additions & 0 deletions Kitodo/src/main/java/org/kitodo/production/forms/ProcessForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.annotation.PostConstruct;
Expand Down Expand Up @@ -60,6 +62,7 @@
import org.kitodo.production.process.ProcessValidator;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.command.KitodoScriptService;
import org.kitodo.production.services.data.ImportService;
import org.kitodo.production.services.data.ProcessService;
import org.kitodo.production.services.file.FileService;
import org.kitodo.production.services.workflow.WorkflowControllerService;
Expand Down Expand Up @@ -99,6 +102,7 @@ public class ProcessForm extends TemplateBaseForm {
private static final String CREATE_PROCESS_PATH = "/pages/processFromTemplate.jsf?faces-redirect=true";
private static final String PROCESS_TABLE_VIEW_ID = "/pages/processes.xhtml";
private static final String PROCESS_TABLE_ID = "processesTabView:processesForm:processesTable";
private final Map<Integer, Boolean> assignedProcesses = new HashMap<>();

@Inject
private CustomListColumnInitializer initializer;
Expand Down Expand Up @@ -1269,4 +1273,21 @@ public String getMediaRenamingConfirmMessage() {
public String getErrorMessage() {
return errorMessage;
}

/**
* Check and return whether process with ID 'processId' belongs to a project assigned to the current user or not.
* @param processId ID of process to check
* @return whether process belongs to project assigned to current user or not
*/
public boolean processInAssignedProject(int processId) {
try {
if (!assignedProcesses.containsKey(processId)) {
assignedProcesses.put(processId, ImportService.processInAssignedProject(processId));
}
return assignedProcesses.get(processId);
} catch (DAOException e) {
Helper.setErrorMessage(e);
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public void getRecordHierarchy() {
importChildren(projectId, templateId, importConfiguration, processes);
}

if (createProcessForm.getProcesses().size() > 0 && additionalImport) {
if (!createProcessForm.getProcesses().isEmpty() && additionalImport) {
extendsMetadataTableOfMetadataTab(processes);
} else {
createProcessForm.setProcesses(processes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Objects;
import java.util.Optional;

import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;

import org.apache.commons.lang3.StringUtils;
Expand All @@ -40,6 +41,7 @@
import org.kitodo.production.helper.Helper;
import org.kitodo.production.metadata.MetadataEditor;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.data.ImportService;
import org.kitodo.production.services.data.ProcessService;
import org.kitodo.production.services.dataformat.MetsService;
import org.omnifaces.util.Ajax;
Expand All @@ -53,6 +55,7 @@ public class TitleRecordLinkTab {
private static final Logger logger = LogManager.getLogger(TitleRecordLinkTab.class);

static final String INSERTION_TREE = "editForm:processFromTemplateTabView:insertionTree";
static final String PARENT_PROCESS_SELECTION = "editForm:processFromTemplateTabView:chooseParentGroup";
private static final MetsService metsService = ServiceManager.getMetsService();
private static final ProcessService processService = ServiceManager.getProcessService();

Expand Down Expand Up @@ -160,12 +163,12 @@ public void createInsertionPositionSelectionTree() throws DAOException, DataExce
priorityList);
logicalStructure.setExpanded(true);

if (selectableInsertionPositions.size() > 0) {
selectedInsertionPosition = (String) ((LinkedList<SelectItem>) selectableInsertionPositions).getLast()
.getValue();
} else {
if (selectableInsertionPositions.isEmpty()) {
selectedInsertionPosition = null;
Helper.setErrorMessage("createProcessForm.titleRecordLinkTab.noInsertionPosition");
} else {
selectedInsertionPosition = (String) ((LinkedList<SelectItem>) selectableInsertionPositions).getLast()
.getValue();
}
}

Expand Down Expand Up @@ -308,21 +311,30 @@ public void searchForParentProcesses() {
}
try {
List<ProcessDTO> processes = ServiceManager.getProcessService().findLinkableParentProcesses(searchQuery,
createProcessForm.getProject().getId(), createProcessForm.getTemplate().getRuleset().getId());
createProcessForm.getProject().getId(), createProcessForm.getTemplate().getRuleset().getId());
if (processes.isEmpty()) {
Helper.setMessage("createProcessForm.titleRecordLinkTab.searchButtonClick.noHits");
}
indicationOfMoreHitsVisible = processes.size() > MAXIMUM_NUMBER_OF_HITS;
possibleParentProcesses = new ArrayList<>();
for (ProcessDTO process : processes.subList(0, Math.min(processes.size(), MAXIMUM_NUMBER_OF_HITS))) {
possibleParentProcesses.add(new SelectItem(process.getId().toString(), process.getTitle()));
}
} catch (DataException e) {
possibleParentProcesses = ServiceManager.getImportService()
.getPotentialParentProcesses(processes, MAXIMUM_NUMBER_OF_HITS);
} catch (DataException | DAOException e) {
Helper.setErrorMessage("createProcessForm.titleRecordLinkTab.searchButtonClick.error", e.getMessage(),
logger, e);
logger, e);
indicationOfMoreHitsVisible = false;
possibleParentProcesses = Collections.emptyList();
}
for (SelectItem selectItem : possibleParentProcesses) {
if (!selectItem.isDisabled()) {
int processId = Integer.parseInt(selectItem.getValue().toString());
try {
setParentAsTitleRecord(ServiceManager.getProcessService().getById(processId));
break;
} catch (DAOException e) {
logger.error(e);
}
}
}
}

/**
Expand Down Expand Up @@ -429,15 +441,29 @@ public void setTitleRecordProcess(Process titleRecordProcess) {

/**
* Set given process "parentProcess" as parent title record of new process.
*
* @param parentProcess process to set as parent title record
*/
public void setParentAsTitleRecord(Process parentProcess) {
createProcessForm.setEditActiveTabIndex(CreateProcessForm.TITLE_RECORD_LINK_TAB_INDEX);
ArrayList<SelectItem> parentCandidates = new ArrayList<>();
parentCandidates.add(new SelectItem(parentProcess.getId().toString(), parentProcess.getTitle()));
createProcessForm.getTitleRecordLinkTab().setPossibleParentProcesses(parentCandidates);
createProcessForm.getTitleRecordLinkTab().setChosenParentProcess((String)parentCandidates.get(0).getValue());
createProcessForm.getTitleRecordLinkTab().chooseParentProcess();
Ajax.update(INSERTION_TREE);
try {
ProcessDTO parentProcessDto = ServiceManager.getProcessService().findById(parentProcess.getId());
possibleParentProcesses = ServiceManager.getImportService()
.getPotentialParentProcesses(Collections.singletonList(parentProcessDto), MAXIMUM_NUMBER_OF_HITS);

if (ImportService.userMayLinkToParent(parentProcess.getId())) {
setChosenParentProcess(String.valueOf(parentProcess.getId()));
} else {
setChosenParentProcess(null);
}
} catch (DAOException | DataException e) {
Helper.setErrorMessage(e);
}
chooseParentProcess();
// only update UI components if FacesContext exists (not the case during integration tests, for example)
if (Objects.nonNull(FacesContext.getCurrentInstance())) {
Ajax.update(PARENT_PROCESS_SELECTION);
Ajax.update(INSERTION_TREE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Objects;
import java.util.stream.Collectors;

import javax.faces.model.SelectItem;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
Expand Down Expand Up @@ -140,7 +141,7 @@ public class ImportService {
private static final String VOLUME = "Volume";
private static final String MULTI_VOLUME_WORK = "MultiVolumeWork";

private static Collection<RecordIdentifierMissingDetail> recordIdentifierMissingDetails = new ArrayList<>();
private static final Collection<RecordIdentifierMissingDetail> recordIdentifierMissingDetails = new ArrayList<>();
private String tiffDefinition = "";
private boolean usingTemplates;

Expand Down Expand Up @@ -991,7 +992,7 @@ public List<ProcessDTO> sortProcessesByProjectID(List<ProcessDTO> processDTOs, i
return 1; // Non-matching value comes later
}
});
Collections.sort(sortedList, comparator);
sortedList.sort(comparator);
return sortedList;
}

Expand Down Expand Up @@ -1413,4 +1414,62 @@ public boolean isRecordIdentifierMetadataConfigured(RulesetManagementInterface r
public Collection<RecordIdentifierMissingDetail> getDetailsOfRecordIdentifierMissingError() {
return recordIdentifierMissingDetails;
}

/**
* Create and return list of "SelectItem" objects for given list of "ProcessDTO"s. For each "ProcessDTO" object
* check whether current user can link to it as a parent process.
* - If a specific process belongs to a project that is not assigned to the current user, add a hint to message to
* the corresponding "SelectItem"
* - If the user cannot link to a specific process, because the process belongs to a project which is not assigned
* to him, and he also lacks the special permission to link to processes in unassigned projects, the corresponding
* "SelectItem" is disabled.
* @param parentCandidates list of "ProcessDTO"s
* @param maxNumber limit
* @return list of "SelectItem" objects corresponding to given "ProcessDTO" objects.
* @throws DAOException when checking whether user can link to given "ProcessDTO"s fails
*/
public ArrayList<SelectItem> getPotentialParentProcesses(List<ProcessDTO> parentCandidates, int maxNumber)
throws DAOException {
ArrayList<SelectItem> possibleParentProcesses = new ArrayList<>();
for (ProcessDTO process : parentCandidates.subList(0, Math.min(parentCandidates.size(), maxNumber))) {
SelectItem selectItem = new SelectItem(process.getId().toString(), process.getTitle());
selectItem.setDisabled(!userMayLinkToParent(process.getId()));
if (!processInAssignedProject(process.getId())) {
String problem = Helper.getTranslation("projectNotAssignedToCurrentUser", process.getProject().getTitle());
selectItem.setDescription(problem);
selectItem.setLabel(selectItem.getLabel() + " (" + problem + ")");
}
possibleParentProcesses.add(selectItem);
}
return possibleParentProcesses;
}

/**
* Check and return whether the process with the provided ID "processId" belongs to a project that is assigned to
* the current user or not.
* @param processId ID of the process to check
* @return whether the process with the provided ID belongs to a project assigned to the current user or not
* @throws DAOException when retrieving the process with the ID "processId" from the database fails
*/
public static Boolean processInAssignedProject(int processId) throws DAOException {
Process process = ServiceManager.getProcessService().getById(processId);
if (Objects.nonNull(process)) {
return ServiceManager.getUserService().getCurrentUser().getProjects().contains(process.getProject());
}
return false;
}

/**
* Check and return whether current user is allowed to link to process with provided ID "processId". For this
* method to return "true", one of the following two conditions has to be met:
* 1. the project of the process with the provided ID is assigned to the current user OR
* 2. the current user has the special permission to link to parent processes of unassigned projects
* @param processId the ID of the process to which a link is to be established
* @return whether the current user can link to the process with the provided ID or not
* @throws DAOException when checking whether the process with provided ID fails
*/
public static boolean userMayLinkToParent(int processId) throws DAOException {
return processInAssignedProject(processId)
|| ServiceManager.getSecurityAccessService().hasAuthorityToLinkToProcessesOfUnassignedProjects();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1086,4 +1086,13 @@ public boolean hasAuthorityToRunKitodoScripts() {
public boolean hasAuthorityToRenameMediaFiles() {
return hasAnyAuthorityForClient("renameMedia");
}

/**
* Check if the current user has the permission to link processes to parent processes of unassigned projects.
*
* @return true if the current user has the permission to link processes to parent processes of unassigned projects.
*/
public boolean hasAuthorityToLinkToProcessesOfUnassignedProjects() {
return hasAnyAuthorityForClient("linkToProcessesOfUnassignedProjects");
}
}
2 changes: 2 additions & 0 deletions Kitodo/src/main/resources/messages/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ projectConfiguration=Projektkonfiguration
projectIsActive=Projekt ist aktiv
projectIsArchived=Projekt ist inaktiv
projectList=Projektliste
projectNotAssignedToCurrentUser=Das Projekt "{0}" ist Ihnen nicht zugewiesen!
projectProgress=Projektentwicklung
projects=Projekte
projectsForMigration=Bitte Projekte f\u00FCr die Migration ausw\u00E4hlen
Expand Down Expand Up @@ -1294,6 +1295,7 @@ renameMediaThread=Medien umbenennen
renameMediaForProcessesConfirmMessage=Die Mediendateien von {0} Vorg\u00E4ngen werden gem\u00E4ss ihrer Reihenfolge in den jeweiligen Vorg\u00E4ngen umbenannt. Diese Aktion kann nicht r\u00FCckg\u00E4ngig gemacht werden. M\u00F6chten Sie fortfahren?
resetWorkflow=Workflow zur\u00FCcksetzen
runKitodoScript=KitodoScript ausf\u00FChren
linkToProcessesOfUnassignedProjects=Verkn\u00FCpfungen mit Vorg\u00E4ngen nicht-zugewiesener Projekte erstellen

viewAllAuthorities=Alle Berechtigungen anzeigen
viewAllBatches=Alle Batches anzeigen
Expand Down
2 changes: 2 additions & 0 deletions Kitodo/src/main/resources/messages/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ projectConfiguration=Project configuration
projectIsActive=project active
projectIsArchived=project inactive
projectList=Projects list
projectNotAssignedToCurrentUser=Project "{0}" not assigned to current user!
projectProgress=Project progress
projects=Projects
projectsForMigration=Please select projects for migration
Expand Down Expand Up @@ -1295,6 +1296,7 @@ renameMedia=Rename media
renameMediaThread=Rename media
renameMediaForProcessesConfirmMessage=The media files of {0} processes will be renamed according to their order in the individual processes. This change cannot be reverted. Do you want to continue?
runKitodoScript=Execute KitodoScript
linkToProcessesOfUnassignedProjects=Link to processes of unassigned projects

viewAllAuthorities=View all authorities
viewAllBatches=View all batches
Expand Down
2 changes: 1 addition & 1 deletion Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css
Original file line number Diff line number Diff line change
Expand Up @@ -1707,7 +1707,7 @@ Import form

#editForm\:processFromTemplateTabView\:logicalStructure {
height: calc(100% - 170px);
overflow: scroll;
overflow: auto;
}

#recordIdentifierMissingDialog ul {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
value="#{msgs.selectProcess}"
rendered="#{not empty CreateProcessForm.titleRecordLinkTab.possibleParentProcesses}"/>
<p:selectOneMenu id="chooseParent"
autoWidth="false"
value="#{CreateProcessForm.titleRecordLinkTab.chosenParentProcess}"
rendered="#{not empty CreateProcessForm.titleRecordLinkTab.possibleParentProcesses}">
<f:selectItem itemLabel="#{msgs.selectPlease}"
Expand Down
Loading

0 comments on commit bc2bb03

Please sign in to comment.