Skip to content

Commit

Permalink
Add list function for renaming media of multiple processes
Browse files Browse the repository at this point in the history
  • Loading branch information
solth committed Nov 16, 2023
1 parent a17b3d2 commit 1f51342
Show file tree
Hide file tree
Showing 20 changed files with 578 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,12 @@ public enum ParameterCore implements ParameterInterface {
* Secret is used to encrypt or decrypt LDAP manager passwords which are stored in the database in encrypted form.
* Once the secret value is set, it should not be changed since encrypted data can no longer be decrypted.
*/
SECURITY_SECRET_LDAPMANAGERPASSWORD(new Parameter<>("security.secret.ldapManagerPassword", ""));
SECURITY_SECRET_LDAPMANAGERPASSWORD(new Parameter<>("security.secret.ldapManagerPassword", "")),

/* Optional parameter can be used to limit the number of processes for which media renaming can be conducted as a
* list function. Values different from positive integers are interpreted as "unlimited".
*/
MAX_NUMBER_OF_PROCESSES_FOR_MEDIA_RENAMING(new Parameter<>("maxNumberOfProcessesForMediaRenaming", -1));

private final Parameter<?> parameter;

Expand Down
45 changes: 45 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 @@ -30,6 +30,7 @@
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.config.ConfigCore;
Expand Down Expand Up @@ -61,6 +62,7 @@
import org.kitodo.production.services.data.ProcessService;
import org.kitodo.production.services.file.FileService;
import org.kitodo.production.services.workflow.WorkflowControllerService;
import org.omnifaces.util.Ajax;
import org.primefaces.PrimeFaces;
import org.primefaces.event.SelectEvent;
import org.primefaces.event.ToggleSelectEvent;
Expand Down Expand Up @@ -89,6 +91,7 @@ public class ProcessForm extends TemplateBaseForm {

private String processEditReferer = DEFAULT_LINK;
private String taskEditReferer = DEFAULT_LINK;
private String errorMessage = "";

private List<SelectItem> customColumns;

Expand Down Expand Up @@ -1182,4 +1185,46 @@ private int getProcessId(Object process) {
public FilterMenu getFilterMenu() {
return filterMenu;
}

/**
* Rename media files of all selected processes.
*/
public void renameMedia() {
List<Process> processes = getSelectedProcesses();
errorMessage = ServiceManager.getFileService().tooManyProcessesSelectedForMediaRenaming(processes.size());
if (StringUtils.isBlank(errorMessage)) {
PrimeFaces.current().executeScript("PF('renameMediaConfirmDialog').show();");
} else {
Ajax.update("errorDialog");
PrimeFaces.current().executeScript("PF('errorDialog').show();");
}
}

/**
* Start renaming media files of selected processes.
*/
public void startRenaming() {
ServiceManager.getFileService().renameMedia(getSelectedProcesses());
PrimeFaces.current().executeScript("PF('notifications').renderMessage({'summary':'"
+ Helper.getTranslation("renamingMediaFilesOfSelectedProcessesStarted")
+ "','severity':'info'})");
}

/**
* Return media renaming confirmation message with number of processes affected.
*
* @return media renaming confirmation message
*/
public String getMediaRenamingConfirmMessage() {
return Helper.getTranslation("renameMediaForProcessesConfirmMessage",
String.valueOf(getSelectedProcesses().size()));
}

/**
* Get error message.
* @return error message
*/
public String getErrorMessage() {
return errorMessage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -33,6 +34,7 @@
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
Expand Down Expand Up @@ -64,11 +66,14 @@
import org.kitodo.production.helper.metadata.ImageHelper;
import org.kitodo.production.helper.metadata.legacytypeimplementations.LegacyMetsModsDigitalDocumentHelper;
import org.kitodo.production.helper.metadata.pagination.Paginator;
import org.kitodo.production.helper.tasks.TaskManager;
import org.kitodo.production.metadata.MetadataEditor;
import org.kitodo.production.metadata.MetadataLock;
import org.kitodo.production.model.Subfolder;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.command.CommandService;
import org.kitodo.production.services.data.RulesetService;
import org.kitodo.production.thread.RenameMediaThread;
import org.kitodo.serviceloader.KitodoServiceLoader;
import org.kitodo.utils.MediaUtil;

Expand Down Expand Up @@ -1524,6 +1529,52 @@ public int renameMediaFiles(Process process, Workpiece workpiece, DualHashBidiMa
return numberOfRenamedMedia;
}

/**
* Check whether too many processes are selected for media renaming and return corresponding error message.
* @param numberOfProcesses number of processes for media renaming
* @return error message if too many processes are selected; otherwise return empty string
*/
public String tooManyProcessesSelectedForMediaRenaming(int numberOfProcesses) {
int limit = ConfigCore.getIntParameterOrDefaultValue(ParameterCore.MAX_NUMBER_OF_PROCESSES_FOR_MEDIA_RENAMING);
if (0 < limit && limit < numberOfProcesses) {
return Helper.getTranslation("tooManyProcessesSelectedForMediaRenaming", String.valueOf(limit),
String.valueOf(numberOfProcesses));
} else {
return "";
}
}

/**
* Rename media files of given processes.
* @param processes Processes whose media is renamed
*/
public void renameMedia(List<Process> processes) {
processes = lockAndSortProcessesForRenaming(processes);
TaskManager.addTask(new RenameMediaThread(processes));
}

private List<Process> lockAndSortProcessesForRenaming(List<Process> processes) {
processes.sort(Comparator.comparing(Process::getId));
List<Integer> lockedProcesses = new LinkedList<>();
for (Process process : processes) {
int processId = process.getId();
if (MetadataLock.isLocked(processId)) {
lockedProcesses.add(processId);
if (ConfigCore.getBooleanParameterOrDefaultValue(ParameterCore.ANONYMIZE)) {
logger.error("Unable to lock process " + processId + " for media renaming because it is currently "
+ "being worked on by another user");
} else {
User currentUser = MetadataLock.getLockUser(processId);
logger.error("Unable to lock process " + processId + " for media renaming because it is currently "
+ "being worked on by another user (" + currentUser.getFullName() + ")");
}
} else {
MetadataLock.setLocked(processId, ServiceManager.getUserService().getCurrentUser());
}
}
return processes.stream().filter(p -> !lockedProcesses.contains(p.getId())).collect(Collectors.toList());
}

/**
* Revert renaming of media files when the user leaves the metadata editor without saving. This method uses a
* provided map object to rename media files identified by the map entries values to the corresponding map entries
Expand All @@ -1534,6 +1585,7 @@ public int renameMediaFiles(Process process, Workpiece workpiece, DualHashBidiMa
*/
public void revertRenaming(BidiMap<URI, URI> filenameMappings, Workpiece workpiece) {
// revert media variant URIs for all media files in workpiece to previous, original values
logger.info("Reverting to original media filenames of process " + workpiece.getId());
for (PhysicalDivision physicalDivision : workpiece
.getAllPhysicalDivisionChildrenFilteredByTypes(PhysicalDivision.TYPES)) {
for (Entry<MediaVariant, URI> mediaVariantURIEntry : physicalDivision.getMediaFiles().entrySet()) {
Expand All @@ -1545,8 +1597,14 @@ public void revertRenaming(BidiMap<URI, URI> filenameMappings, Workpiece workpie
try {
List<URI> tempUris = new LinkedList<>();
for (Entry<URI, URI> mapping : filenameMappings.entrySet()) {
tempUris.add(fileManagementModule.rename(mapping.getKey(), mapping.getValue().toString()
+ TEMP_EXTENSION));
if (mapping.getKey().toString().endsWith(TEMP_EXTENSION)) {
// if current URI has '.tmp' extension, directly revert to original name (without '.tmp' extension)
tempUris.add(fileManagementModule.rename(mapping.getKey(), mapping.getValue().toString()));
} else {
// rename to new filename with '.tmp' extension otherwise
tempUris.add(fileManagementModule.rename(mapping.getKey(), mapping.getValue().toString()
+ TEMP_EXTENSION));
}
}
for (URI tempUri : tempUris) {
fileManagementModule.rename(tempUri, StringUtils.removeEnd(tempUri.toString(), TEMP_EXTENSION));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* (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.
*/

package org.kitodo.production.thread;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Objects;

import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.api.dataformat.Workpiece;
import org.kitodo.data.database.beans.Process;
import org.kitodo.production.helper.Helper;
import org.kitodo.production.helper.LocaleHelper;
import org.kitodo.production.helper.tasks.EmptyTask;
import org.kitodo.production.metadata.MetadataLock;
import org.kitodo.production.services.ServiceManager;

/**
* This class is used to rename media files of multiple processes in a separate thread whose progress can be monitored
* in the task manager.
*/
public class RenameMediaThread extends EmptyTask {

private static final Logger logger = LogManager.getLogger(RenameMediaThread.class);
private final List<Process> processes;
private static final String THREAD_NAME = "renameMediaThread";
private static final String ERRORS_OCCURRED_KEY = "errorsOccurredCheckLog";
private static final String PROCESSES = "processes";

public RenameMediaThread(List<Process> processes) {
super(processes.size() + " " + Helper.getString(LocaleHelper.getCurrentLocale(), PROCESSES));
this.processes = processes;
}

@Override
protected void setNameDetail(String detail) {
String localThreadName = Helper.getString(LocaleHelper.getCurrentLocale(), THREAD_NAME);
super.setName(localThreadName + ": " + detail);
}

/**
* Run method of this thread. Iterates over processes and renames all media files in each process using the regular
* "media renaming" functionality of the FileService.
*/
@Override
public void run() {
for (Process process : processes) {
int processId = process.getId();
URI metaXmlUri = ServiceManager.getProcessService().getMetadataFileUri(process);
DualHashBidiMap<URI, URI> renamingMap = new DualHashBidiMap<>();
Workpiece workpiece = null;
try {
workpiece = ServiceManager.getMetsService().loadWorkpiece(metaXmlUri);
int numberOfRenamedFiles = ServiceManager.getFileService().renameMediaFiles(process, workpiece,
renamingMap);
try (OutputStream out = ServiceManager.getFileService().write(metaXmlUri)) {
ServiceManager.getMetsService().save(workpiece, out);
logger.info("Renamed " + numberOfRenamedFiles + " media files for process " + process.getId());
}
} catch (IOException | URISyntaxException e) {
logger.error(e.getMessage());
String nameDetailMessage = processes.size()
+ " " + Helper.getString(LocaleHelper.getCurrentLocale(), PROCESSES)
+ " (" + Helper.getString(LocaleHelper.getCurrentLocale(), ERRORS_OCCURRED_KEY) + ")";
this.setNameDetail(nameDetailMessage);
if (Objects.nonNull(workpiece)) {
ServiceManager.getFileService().revertRenaming(renamingMap.inverseBidiMap(), workpiece);
}
}
MetadataLock.setFree(processId);
setProgress((100 / processes.size()) * (processes.indexOf(process) + 1));
}
setProgress(100);
}
}
4 changes: 4 additions & 0 deletions Kitodo/src/main/resources/kitodo_config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -763,3 +763,7 @@ security.secret.ldapManagerPassword=
# largest data tables of tasks by processingBegin and process by creation date are considered.
# The dates can be defined & separated in the format YYYY, YYYY-MM or YYYY-MM-DD e.g. 2017-05-10,2018-06,2022
# database.subset.dates=

# This optional parameter can be used to limit the number of processes for which media renaming can be conducted as a
# list function. Values different from positive integers are interpreted as "unlimited".
#maxNumberOfProcessesForMediaRenaming=10000
2 changes: 2 additions & 0 deletions Kitodo/src/main/resources/messages/errors_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ noUserInStep=Der Aufgabe {0} wurden keine Nutzer zugeordnet.

# O
errorOccurred=Es ist ein Fehler aufgetreten:
errorsOccurredCheckLog=Es sind Fehler aufgetreten. Bitte \u00FCberprf\u00FCfen Sie kitodo.log f\u00FCr zus\u00E4tzliche Details.

# P
errorParsingFile=Parsingfehler: verschiedene {0} in der Datei (''{1}'' & ''{2}'').
Expand Down Expand Up @@ -167,6 +168,7 @@ templateAssignedError=Der Workflow kann nicht gel\u00F6scht werden, da diesem Pr
templateTitleAlreadyInUse=Der Produktionsvorlagentitel wird bereits verwendet.
templateTitleEmpty=Kein Produktionsvorlagentitel angegeben.
tooManyBatchesSelected=Es sind mehrere Batches ausgew\u00E4hlt. W\u00E4hlen Sie genau einen Batch aus, um diese Funktion zu nutzen.
tooManyProcessesSelectedForMediaRenaming=Es wurden {0} Vorg\u00E4nge f\u00FCr die Medienumbenennung ausgew\u00E4lt. Die maximal erlaubte Anzahl von Vorg\u00E4ngen f\u00FCr die Medienumbenennung liegt bei {1}. Bitte reduzieren Sie die Menge der ausgew\u00E4lten Vorg\u00E4nge und wiederholen Sie die Aktion.

# U
errorUploading=Fehler beim Hochladen von ''{0}''.
Expand Down
2 changes: 2 additions & 0 deletions Kitodo/src/main/resources/messages/errors_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ noUserInStep=No user assigned to step {0}.

# O
errorOccurred=An error has occurred:
errorsOccurredCheckLog=Errors occurred. Please check kitodo.log for Details.

# P
errorParsingFile=Error parsing: various {0} in the file (''{1}'' & ''{2}'').
Expand Down Expand Up @@ -166,6 +167,7 @@ templateAssignedError=The workflow could not be deleted because there are alread
templateTitleAlreadyInUse=The template title is already in use.
templateTitleEmpty=No template title stated.
tooManyBatchesSelected=Multiple batches are selected. To use this function, please select one batch only.
tooManyProcessesSelectedForMediaRenaming={0} have been selected for media renaming. The maximum number of processes for media renaming is {1}. Please reduce the number of selected processes and try again.

# U
errorUploading=Error uploading ''{0}''.
Expand Down
5 changes: 4 additions & 1 deletion Kitodo/src/main/resources/messages/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ dataEditor.removeElement.noConsecutivePagesSelected=Strukturelemente k\u00F6nnen
dataEditor.selectMetadataTask=Aufgabe w\u00E4hlen
dataEditor.layoutSavedSuccessfullyTitle=Aktuelle Spaltenaufteilung erfolgreich gespeichert
dataEditor.layoutSavedSuccessfullyText=Ihre aktuellen Metadaten-Editor-Einstellungen wurden erfolgreich gespeichert! Sie werden f\u00FCr alle zuk\u00FCnftigen Aufgaben dieses Typs wiederverwendet.
dataEditor.renameMedia=Medien umbenennen
dataEditor.renamingMediaComplete=Das Umbenennen der Medien ist abgeschlossen
dataEditor.renamingMediaError=Beim Umbenennen der Medien ist ein Fehler aufgetreten
dataEditor.renamingMediaText={0} Mediendateien in {1} Ordnern wurden umbenannt. Bitte Speichern Sie den Vorgang. Andernfalls werden die Dateiumbennungen beim Schlie\u00DFen des Metadateneditors verworfen.
Expand Down Expand Up @@ -953,6 +952,8 @@ removePhysicalDivision=Physisches Strukturelement entfernen
removeRole=Rolle entfernen
removeUserFromGroup=Benutzer aus der Benutzergruppe l\u00F6schen
renameBatch=Batch umbenennen
renameMediaFiles=Medien umbenennen
renamingMediaFilesOfSelectedProcessesStarted=Das Umbenennen der Medien f\u00FCr die ausgew\u00E4hlten Vorg\u00E4nge wurde erfolgreich gestartet.
requiredField=Mit * gekennzeichnete Felder sind Pflichtfelder
reset=Zur\u00FCcksetzen
resultPDF=Ergebnis-PDF
Expand Down Expand Up @@ -1273,6 +1274,8 @@ assignTask=Aufgabe zuweisen
overrideTask=Aufgabe \u00FCbernehmen
superviseTask=Aufgabe beobachten
renameMedia=Medien umbenennen
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

Expand Down
5 changes: 4 additions & 1 deletion Kitodo/src/main/resources/messages/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ dataEditor.removeElement.noConsecutivePagesSelected=Select consecutive pages to
dataEditor.selectMetadataTask=Select task
dataEditor.layoutSavedSuccessfullyTitle=Current layout successfully saved
dataEditor.layoutSavedSuccessfullyText=Your current editor settings have been saved successfully and will be applied to all future tasks of the same type.
dataEditor.renameMedia=Rename media
dataEditor.renamingMediaComplete=Finished renaming media
dataEditor.renamingMediaError=An error occurred while renaming media files
dataEditor.renamingMediaText={0} media files in {1} folders have been renamed. Please click the 'Save' button to persist changes to the filenames. Otherwise the renaming will be reverted upon closing the editor.
Expand Down Expand Up @@ -954,6 +953,8 @@ removePhysicalDivision=Remove physical structure element
removeRole=Remove role
removeUserFromGroup=Delete user from group
renameBatch=Rename batch
renameMediaFiles=Rename media
renamingMediaFilesOfSelectedProcessesStarted=Renaming media files of selected processes started successfully.
requiredField=Fields marked with * are required
reset=Reset
resultPDF=Result PDF
Expand Down Expand Up @@ -1275,6 +1276,8 @@ overrideTask=Take on task
superviseTask=Watch task
resetWorkflow=Reset workflow
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

viewAllAuthorities=View all authorities
Expand Down
Loading

0 comments on commit 1f51342

Please sign in to comment.