Skip to content

Commit

Permalink
Merge pull request kitodo#6245 from thomaslow/allow-to-save-default-m…
Browse files Browse the repository at this point in the history
…etadata-editor-layout-fix-5888

Allow to save metadata editor default column layout
  • Loading branch information
solth authored Nov 2, 2024
2 parents bc0970f + 6a9e1f2 commit 37e966f
Show file tree
Hide file tree
Showing 14 changed files with 410 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class DataEditorSetting extends BaseBean {
@Column(name = "user_id")
private int userId;

@Column(name = "task_id")
private int taskId;
@Column(name = "task_id", nullable = true)
private Integer taskId;

@Column(name = "structure_width")
private float structureWidth;
Expand All @@ -40,6 +40,19 @@ public class DataEditorSetting extends BaseBean {
public DataEditorSetting() {
}

/**
* Copy constructor (without id).
*
* @param setting the data editor settings that are copied
*/
public DataEditorSetting(DataEditorSetting setting) {
setUserId(setting.getUserId());
setTaskId(setting.getTaskId());
setStructureWidth(setting.getStructureWidth());
setMetadataWidth(setting.getMetadataWidth());
setGalleryWidth(setting.getGalleryWidth());
}

/**
* Get userId.
*
Expand All @@ -59,20 +72,20 @@ public void setUserId(int userId) {
}

/**
* Get taskId.
* Get taskId. Either the id of the task or null, for a task-independent default layout.
*
* @return value of taskId
*/
public int getTaskId() {
public Integer getTaskId() {
return taskId;
}

/**
* Set taskId.
* Set taskId. Either the id of the task or null, for a task-independent default layout.
*
* @param taskId as int
*/
public void setTaskId(int taskId) {
public void setTaskId(Integer taskId) {
this.taskId = taskId;
}

Expand Down
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;

-- allow null in column task_id of table dataeditor_setting to store task-independent layout
ALTER TABLE dataeditor_setting MODIFY COLUMN task_id INT(11) NULL;

SET SQL_SAFE_UPDATES = 1;
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.kitodo.data.database.beans.DataEditorSetting;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.beans.Project;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.exceptions.InvalidImagesException;
Expand Down Expand Up @@ -184,12 +185,12 @@ public class DataEditorForm implements MetadataTreeTableInterface, RulesetSetupI
private List<Pair<PhysicalDivision, LogicalDivision>> selectedMedia;

/**
* The id of the template's task corresponding to the current task that is under edit.
* The template task corresponding to the current task that is under edit.
* This is used for saving and loading the metadata editor settings.
* The current task, the corresponding template task id and the settings are only available
* if the user opened the editor from a task.
*/
private int templateTaskId;
private Task templateTask;

private DataEditorSetting dataEditorSetting;

Expand Down Expand Up @@ -271,9 +272,7 @@ public void open(String processID, String referringView, String taskId) {
this.currentChildren.addAll(process.getChildren());
this.user = ServiceManager.getUserService().getCurrentUser();
this.checkProjectFolderConfiguration();
if (StringUtils.isNotBlank(taskId) && StringUtils.isNumeric(taskId)) {
this.templateTaskId = Integer.parseInt(taskId);
}
this.loadTemplateTask(taskId);
this.loadDataEditorSettings();
errorMessage = "";

Expand Down Expand Up @@ -313,10 +312,15 @@ public void open(String processID, String referringView, String taskId) {
}

private void showDataEditorSettingsLoadedMessage() throws DAOException {
String task = ServiceManager.getTaskService().getById(templateTaskId).getTitle();
Locale locale = LocaleHelper.getCurrentLocale();
String title = Helper.getString(locale, "dataEditor.layoutLoadedSuccessfullyTitle");
String text = MessageFormat.format(Helper.getString(locale, "dataEditor.layoutLoadedSuccessfullyText"), task);
String text = Helper.getString(locale, "dataEditor.layoutLoadedSuccessfullyDefaultText");
if (Objects.nonNull(this.templateTask) && Objects.nonNull(dataEditorSetting)
&& templateTask.getId().equals(dataEditorSetting.getTaskId())) {
text = MessageFormat.format(
Helper.getString(locale, "dataEditor.layoutLoadedSuccessfullyForTaskText"), this.templateTask.getTitle()
);
}
String script = GROWL_MESSAGE.replace("SUMMARY", title).replace("DETAIL", text)
.replace("SEVERITY", "info");
PrimeFaces.current().executeScript("PF('notifications').removeAll();");
Expand All @@ -337,17 +341,47 @@ private void checkProjectFolderConfiguration() {
}
}

private void loadDataEditorSettings() {
if (templateTaskId > 0) {
dataEditorSetting = ServiceManager.getDataEditorSettingService().loadDataEditorSetting(user.getId(),
templateTaskId);
if (Objects.isNull(dataEditorSetting)) {
dataEditorSetting = new DataEditorSetting();
dataEditorSetting.setUserId(user.getId());
dataEditorSetting.setTaskId(templateTaskId);
/**
* Load template task from database.
*
* @param taskId the id of the template task
* @throws DAOException if loading fails
*/
private void loadTemplateTask(String taskId) throws DAOException {
if (StringUtils.isNotBlank(taskId) && StringUtils.isNumeric(taskId)) {
try {
int templateTaskId = Integer.parseInt(taskId);
if (templateTaskId > 0) {
this.templateTask = ServiceManager.getTaskService().getById(templateTaskId);
}
} catch (NumberFormatException e) {
logger.warn("view parameter 'templateTaskId' is not a valid integer");
}
} else {
dataEditorSetting = null;
}
}

/**
* Load data editor settings (width of metadata editor columns) from database. Either load task-specific
* configuration (if it exists) or default configuration (if it exists) or initialize new empty data editor setting.
*/
private void loadDataEditorSettings() {
// use template task id if it exists, otherwise use null for task-independent layout
Integer taskId = Objects.nonNull(this.templateTask) ? this.templateTask.getId() : null;
int userId = user.getId();

// try to load data editor setting from database
dataEditorSetting = ServiceManager.getDataEditorSettingService().loadDataEditorSetting(userId, taskId);

// try to load task-independent data editor setting from database
if (Objects.isNull(dataEditorSetting)) {
dataEditorSetting = ServiceManager.getDataEditorSettingService().loadDataEditorSetting(userId, null);
}

// initialize empty data editor setting if none were previously saved by the user
if (Objects.isNull(dataEditorSetting)) {
dataEditorSetting = new DataEditorSetting();
dataEditorSetting.setUserId(userId);
dataEditorSetting.setTaskId(taskId);
}
}

Expand Down Expand Up @@ -1025,8 +1059,8 @@ public String getShortcuts() {
*
* @return value of templateTaskId
*/
public int getTemplateTaskId() {
return templateTaskId;
public Task getTemplateTask() {
return templateTask;
}

/**
Expand Down Expand Up @@ -1066,19 +1100,43 @@ public void updateNumberOfScans() {


/**
* Save current metadata editor layout.
* Save current metadata editor layout. Either save it as task-specific layout (if editor was opened from a task)
* or as task-independent layout (otherwise).
*/
public void saveDataEditorSetting() {
if (Objects.nonNull(dataEditorSetting) && dataEditorSetting.getTaskId() > 0) {
if (Objects.nonNull(dataEditorSetting)) {
if (Objects.nonNull(templateTask) && !templateTask.getId().equals(dataEditorSetting.getTaskId())) {
// create a copy of the task-independent configuration
// in case the user wants to save it as task-specific config
dataEditorSetting = new DataEditorSetting(dataEditorSetting);
dataEditorSetting.setTaskId(templateTask.getId());
}
try {
ServiceManager.getDataEditorSettingService().saveToDatabase(dataEditorSetting);
PrimeFaces.current().executeScript("PF('dataEditorSavingResultDialog').show();");
} catch (DAOException e) {
Helper.setErrorMessage("errorSaving", new Object[] {ObjectType.USER.getTranslationSingular() }, logger, e);
Helper.setErrorMessage("errorSaving", new Object[] {ObjectType.DATAEDITORSETTING.getTranslationSingular() }, logger, e);
}
} else {
logger.error("Could not save DataEditorSettings with userId {} and templateTaskId {}", user.getId(),
templateTaskId);
// should never happen any more, since layout settings are always created (even outside of task context)
int taskId = Objects.nonNull(this.templateTask) ? this.templateTask.getId() : 0;
int userId = user.getId();
logger.error("Could not save DataEditorSettings with userId {} and templateTaskId {}", userId, taskId);
}
}

/**
* Delete current metadata editor layout.
*/
public void deleteDataEditorSetting() {
if (Objects.nonNull(dataEditorSetting)) {
try {
ServiceManager.getDataEditorSettingService().removeFromDatabase(dataEditorSetting);
this.loadDataEditorSettings();
PrimeFaces.current().executeScript("PF('dataEditorDeletedResultDialog').show();");
} catch (DAOException e) {
Helper.setErrorMessage("errorDeleting", new Object[] { ObjectType.DATAEDITORSETTING.getTranslationSingular() }, logger, e);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ public boolean areDataEditorSettingsDefinedForWorkflow(Workflow workflow) {

/**
* Delete data editor settings identified by task id.
* @param taskId ID of the associated task
* @param taskId ID of the associated task (or null for task-independent default layout)
* @throws DAOException if data editor setting could not be deleted from database
*
*/
public void removeFromDatabaseByTaskId(int taskId) throws DAOException {
public void removeFromDatabaseByTaskId(Integer taskId) throws DAOException {
List<DataEditorSetting> dataEditorSettings = getByTaskId(taskId);
for (DataEditorSetting dataEditorSetting: dataEditorSettings) {
dao.remove(dataEditorSetting.getId());
Expand All @@ -102,30 +102,41 @@ public void removeFromDatabaseByTaskId(int taskId) throws DAOException {

/**
* Retrieve data editor settings by task id.
* @param taskId ID of the task
* @param taskId ID of the task (or null for task-independent default layout)
*
* @return List of DataEditorSetting objects
*/
public List<DataEditorSetting> getByTaskId(int taskId) {
public List<DataEditorSetting> getByTaskId(Integer taskId) {
Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("taskId", taskId);
return getByQuery("FROM DataEditorSetting WHERE task_id = :taskId ORDER BY id ASC", parameterMap);
if (Objects.nonNull(taskId)) {
parameterMap.put("taskId", taskId);
return getByQuery("FROM DataEditorSetting WHERE task_id = :taskId ORDER BY id ASC", parameterMap);
}
return getByQuery("FROM DataEditorSetting WHERE task_id is NULL ORDER BY id ASC", parameterMap);
}

private List<DataEditorSetting> getByUserAndTask(int userId, int taskId) {
private List<DataEditorSetting> getByUserAndTask(int userId, Integer taskId) {
Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("userId", userId);
parameterMap.put("taskId", taskId);
return getByQuery("FROM DataEditorSetting WHERE user_id = :userId AND task_id = :taskId ORDER BY id ASC", parameterMap);
if (Objects.nonNull(taskId)) {
parameterMap.put("taskId", taskId);
return getByQuery(
"FROM DataEditorSetting WHERE user_id = :userId AND task_id = :taskId ORDER BY id ASC", parameterMap
);
}
return getByQuery(
"FROM DataEditorSetting WHERE user_id = :userId AND task_id IS NULL ORDER BY id ASC", parameterMap
);
}

/**
* Load DataEditorSetting from database or return null if no entry matches the specified ids.
* @param userId id of the user
* @param taskId id of the corresponding template task for the task that is currently edited
* @param taskId id of the corresponding template task for the task that is currently edited
* (or null for task-independent default layout)
* @return settings for the data editor
*/
public DataEditorSetting loadDataEditorSetting(int userId, int taskId) {
public DataEditorSetting loadDataEditorSetting(int userId, Integer taskId) {
List<DataEditorSetting> results = getByUserAndTask(userId, taskId);
if (Objects.nonNull(results) && !results.isEmpty()) {
return results.get(0);
Expand Down
15 changes: 11 additions & 4 deletions Kitodo/src/main/resources/messages/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ dataEditor.addMetadata.newMetadata=Neue Metadaten hinzuf\u00FCgen
dataEditor.addMetadata.noMetadataAddable=Zu dem ausgew\u00E4hlten Strukturelement k\u00F6nnen keine weiteren Metadaten hinzugef\u00FCgt werden!
dataEditor.addMetadata.noMetadataAddableToGroup=Zu dieser Gruppe k\u00F6nnen keine weiteren Metadaten hinzugef\u00FCgt werden!
dataEditor.addMetadata.toGroup=Metadaten zu Gruppe hinzuf\u00FCgen
dataEditor.cannotSaveLayout=Layout kann nur gespeichert werden, wenn der Editor \u00FCber eine Aufgabe ge\u00F6ffnet wird.
dataEditor.childNotContainedError=Elternelement von Struktur {0} enth\u00E4lt die Struktur nicht!
dataEditor.comment.correctionWorkflowAlreadyActive=Dieser Vorgang befindet sich bereits in einem Korrektur-Workflow
dataEditor.comment.firstTaskInWorkflow=Sie k\u00F6nnen f\u00FCr diesen Vorgang derzeit keinen Korrekturkommentar verfassen, da noch keine seiner Aufgaben abgeschlossen sind.
Expand Down Expand Up @@ -309,7 +308,6 @@ dataEditor.galleryStructuredView=Strukturierte Ansicht
dataEditor.galleryDetailView=Detailansicht
dataEditor.invalidMetadataValue=\u201E{0}\u201C kann nicht gespeichert werden: Der Wert ist ung\u00FCltig. Wert: {1}
dataEditor.invalidStructureField=\u201E{0}\u201C kann nicht gespeichert werden: Es gibt kein solches Feld ({1}).
dataEditor.saveLayout=Aktuelle Spaltenaufteilung als Standard f\u00FCr diese Aufgabe speichern
dataEditor.mediaNotFound=Es wurden keine Medien gefunden, aber mindestens eine Dateireferenz ist vorhanden. Die automatische Entfernung der fehlenden Medien aus dem Werkst\u00FCck wurde \u00FCbersprungen.
dataEditor.multipleMetadataTasksText=Sie haben mehrere Aufgaben zum Editieren von Metadaten in Bearbeitung. Bitte w\u00E4hlen Sie eine dieser Aufgaben aus!
dataEditor.noParentsError=Elternelement von {0} konnte nicht gefunden werden!
Expand All @@ -322,10 +320,19 @@ dataEditor.position.beforeCurrentElement=Vor dem aktuellen Element
dataEditor.position.currentPosition=Aktuelle Position
dataEditor.removeElement.noConsecutivePagesSelected=Strukturelemente k\u00F6nnen nur aus fortlaufenden Medien erstellt werden!
dataEditor.selectMetadataTask=Aufgabe w\u00E4hlen
dataEditor.layoutDeletedSuccessfullyTitle=Metadaten-Editor-Layout gelöscht
dataEditor.layoutDeletedSuccessfullyText=Die Standard-Spalteneinstellungen wurden erfolgreich gelöscht
dataEditor.layoutLoadedSuccessfullyTitle=Metadaten-Editor-Layout geladen
dataEditor.layoutLoadedSuccessfullyText=Spalteneinstellungen f\u00FCr Aufgabe "{0}" erfolgreich geladen
dataEditor.layoutLoadedSuccessfullyDefaultText=Standard-Spalteneinstellungen erfolgreich geladen
dataEditor.layoutLoadedSuccessfullyForTaskText=Standard-Spalteneinstellungen f\u00FCr Aufgabe "{0}" erfolgreich geladen
dataEditor.layoutMenuActiveText=aktiv
dataEditor.layoutMenuButtonTitle=Menu zum Speichern des Layout (Spaltenaufteilung) öffnen
dataEditor.layoutMenuDeleteTitle=Gespeicherte Spaltenaufteilung löschen
dataEditor.layoutMenuSaveDefaultText=Spaltenaufteilung als Standard speichern
dataEditor.layoutMenuSaveForTaskText=Spaltenaufteilung für Aufgabentyp speichern
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.layoutSavedSuccessfullyDefaultText=Ihre Einstellungen wurden erfolgreich als Standard-Einstellungen gespeichert! Beim n\u00E4chsten \u00D6ffnen des Matadaten-Editors wird die Breite der drei Spalten (Strukturdaten, Metadaten und Galerie) entsprechend ihrer aktuellen Einstellung geladen.
dataEditor.layoutSavedSuccessfullyForTaskText=Ihre Einstellungen wurden erfolgreich f\u00FCr alle zuk\u00FCnftigen Aufgaben des Typs "{0}" gespeichert! Beim n\u00E4chsten \u00D6ffnen des Matadaten-Editors f\u00FCr eine Aufgabe des gleichen Typs wird die Breite der drei Spalten (Strukturdaten, Metadaten und Galerie) entsprechend ihrer aktuellen Einstellung geladen.
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
Loading

0 comments on commit 37e966f

Please sign in to comment.