diff --git a/Kitodo-API/src/main/java/org/kitodo/api/externaldatamanagement/SearchInterfaceType.java b/Kitodo-API/src/main/java/org/kitodo/api/externaldatamanagement/SearchInterfaceType.java index c00f9065b93..64444485541 100644 --- a/Kitodo-API/src/main/java/org/kitodo/api/externaldatamanagement/SearchInterfaceType.java +++ b/Kitodo-API/src/main/java/org/kitodo/api/externaldatamanagement/SearchInterfaceType.java @@ -17,8 +17,8 @@ public enum SearchInterfaceType { ".//*[local-name()='diagnostic']/*[local-name()='message']/text()"), OAI("oai", "http://www.openarchives.org/OAI/2.0/", "record", null, null, null, null, null, ".//*[local-name()='error']/text()"), - FTP("ftp", null, null, null, null,null, null, null, - null); + FTP("ftp", null, null, null, null,null, null, null, null), + CUSTOM("custom", null, null, null, null, null, null, null, null); private final String typeString; private final String namespace; diff --git a/Kitodo-API/src/main/java/org/kitodo/api/schemaconverter/MetadataFormat.java b/Kitodo-API/src/main/java/org/kitodo/api/schemaconverter/MetadataFormat.java index ed42903512d..0d127e9076c 100644 --- a/Kitodo-API/src/main/java/org/kitodo/api/schemaconverter/MetadataFormat.java +++ b/Kitodo-API/src/main/java/org/kitodo/api/schemaconverter/MetadataFormat.java @@ -17,6 +17,7 @@ public enum MetadataFormat { MODS, MARC, PICA, + OTHER, KITODO; /** @@ -36,4 +37,42 @@ public static MetadataFormat getMetadataFormat(String formatString) { throw new UnknownFormatConversionException("Unable to find MetadataFormat for given String '" + formatString + "'!"); } + + /** + * Get default record ID XPath for given format. + * + * @param format MetadataFormat + * @return default record ID XPath for given format + */ + public static String getDefaultRecordIdXpath(String format) { + switch (format) { + case "MODS": + return ".//*[local-name()='recordInfo']/*[local-name()='recordIdentifier']/text()"; + case "MARC": + return ".//*[local-name()='datafield'][@tag='245']/*[local-name()='subfield'][@code='a']/text()"; + case "PICA": + return ".//*[local-name()='datafield'][@tag='003@']/*[local-name()='subfield'][@code='0']/text()"; + default: + return ""; + } + } + + /** + * Get default record title XPath for given format. + * + * @param format MetadataFormat + * @return default record title XPath for given format + */ + public static String getDefaultRecordTitleXpath(String format) { + switch (format) { + case "MODS": + return ".//*[local-name()='titleInfo']/*[local-name()='title']/text()"; + case "MARC": + return ".//*[local-name()='controlfield'][@tag='001']/text()"; + case "PICA": + return ".//*[local-name()='datafield'][@tag='021A']/*[local-name()='subfield'][@code='a']/text()"; + default: + return ""; + } + } } diff --git a/Kitodo-API/src/main/java/org/kitodo/config/OPACConfig.java b/Kitodo-API/src/main/java/org/kitodo/config/OPACConfig.java index 5126bdff42d..41b19bcd3ba 100644 --- a/Kitodo-API/src/main/java/org/kitodo/config/OPACConfig.java +++ b/Kitodo-API/src/main/java/org/kitodo/config/OPACConfig.java @@ -14,21 +14,21 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.ConversionException; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy; +import org.apache.commons.configuration.tree.ConfigurationNode; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.kitodo.api.externaldatamanagement.SearchInterfaceType; import org.kitodo.config.enums.KitodoConfigFile; import org.kitodo.exceptions.ConfigException; +import org.kitodo.exceptions.MandatoryParameterMissingException; import org.kitodo.exceptions.ParameterNotFoundException; /** @@ -39,13 +39,19 @@ * NOTE: the class is not removed because it will be used to create an "OPAC configuration importer" * that converts existing "kitodo_opac.xml" files to the new format saved to the database. */ -@SuppressWarnings("unused") public class OPACConfig { private static final Logger logger = LogManager.getLogger(OPACConfig.class); private static XMLConfiguration config; private static final String TRUE = "true"; private static final String DEFAULT = "[@default]"; private static final int DEFAULT_IMPORT_DEPTH = 2; + private static final String DEFAULT_RETURN_FORMAT = "XML"; + private static final String DESCRIPTION = "description"; + private static final String NOT_AVAILABLE = "N/A"; + private static final String HOST = "host"; + private static final String SCHEME = "scheme"; + private static final String PATH = "path"; + private static final String PORT = "port"; /** * Private constructor. @@ -103,15 +109,80 @@ public static SearchInterfaceType getInterfaceType(String catalogName) { return null; } + /** + * Retrieve the "description" of the catalog identified by its title. + * @param catalogName String identifying the catalog by title + * @return String description for catalog's "config" + */ + public static String getOPACDescription(String catalogName) { + HierarchicalConfiguration catalogConfiguration = getCatalog(catalogName); + List descriptionAttributes = catalogConfiguration.getRoot().getAttributes(DESCRIPTION); + if (descriptionAttributes.isEmpty()) { + return NOT_AVAILABLE; + } else { + return descriptionAttributes.stream().map(cn -> cn.getValue().toString()) + .collect(Collectors.joining(System.lineSeparator())); + } + } + /** * Retrieve the "config" of the catalog identified by its title. * @param catalogName String identifying the catalog by title * @return HierarchicalConfiguration for catalog's "config" */ - public static HierarchicalConfiguration getOPACConfiguration(String catalogName) { + private static HierarchicalConfiguration getOPACConfiguration(String catalogName) { return getCatalog(catalogName).configurationAt("config"); } + private static String getUrlConfigPart(String catalogName, String parameter) + throws MandatoryParameterMissingException { + HierarchicalConfiguration config = getOPACConfiguration(catalogName); + if (Objects.nonNull(config)) { + for (HierarchicalConfiguration param : config.configurationsAt("param")) { + if (parameter.equals(param.getString("[@name]"))) { + return param.getString("[@value]"); + } + } + } + throw new MandatoryParameterMissingException(parameter); + } + + /** + * Get host parameter of catalog configuration with name 'catalogName'. + * @param catalogName name of catalog configuration + * @return host value as String + */ + public static String getHost(String catalogName) throws MandatoryParameterMissingException { + return getUrlConfigPart(catalogName, HOST); + } + + /** + * Get scheme parameter of catalog configuration with name 'catalogName'. + * @param catalogName name of catalog configuration + * @return scheme value as String + */ + public static String getScheme(String catalogName) throws MandatoryParameterMissingException { + return getUrlConfigPart(catalogName, SCHEME); + } + + /** + * Get path parameter of catalog configuration with name 'catalogName'. + * @param catalogName name of catalog configuration + * @return path value as String + */ + public static String getPath(String catalogName) throws MandatoryParameterMissingException { + return getUrlConfigPart(catalogName, PATH); + } + + /** + * Get port parameter of catalog configuration with name 'catalogName'. + * @param catalogName name of catalog configuration + * @return port value as Integer + */ + public static String getPort(String catalogName) throws MandatoryParameterMissingException { + return getUrlConfigPart(catalogName, PORT); + } + /** * Retrieve the "searchFields" of the catalog identified by its title. * @param catalogName String identifying the catalog by title @@ -162,6 +233,31 @@ public static HierarchicalConfiguration getUrlParameters(String catalogName) { return getCatalog(catalogName).configurationAt("urlParameters"); } + private static String getUrlParameter(String catalogName, String parameter) + throws MandatoryParameterMissingException { + HierarchicalConfiguration urlParameters = getCatalog(catalogName).configurationAt("urlParameters"); + if (Objects.nonNull(urlParameters)) { + for (HierarchicalConfiguration param : urlParameters.configurationsAt("param")) { + if (parameter.equals(param.getString("[@name]"))) { + return param.getString("[@value]"); + } + } + } + throw new MandatoryParameterMissingException(parameter); + } + + public static String getSruVersion(String catalogName) throws MandatoryParameterMissingException { + return getUrlParameter(catalogName, "version"); + } + + public static String getSruRecordSchema(String catalogName) throws MandatoryParameterMissingException { + return getUrlParameter(catalogName, "recordSchema"); + } + + public static String getOaiMetadataPrefix(String catalogName) throws MandatoryParameterMissingException { + return getUrlParameter(catalogName, "metadataPrefix"); + } + /** * Retrieve the "fileUpload" configuration of the catalog identified by its title. * @param catalogName String identifying the catalog by its title @@ -274,7 +370,7 @@ public static String getExemplarFieldXPath(String catalogName) { * @return String XPath for owner information about an exemplar record */ public static String getExemplarFieldOwnerXPath(String catalogName) { - return getCatalog(catalogName).getString("exemplarField[@ownerSubPath]"); + return getCatalog(catalogName).getString("exemplarField[@ownerSubPath]", null); } /** @@ -340,11 +436,25 @@ public static boolean isParentInRecord(String catalogName) { * @return default import depth of catalog */ public static int getDefaultImportDepth(String catalogName) { - try { - return getCatalog(catalogName).getInt("defaultImportDepth"); - } catch (NoSuchElementException | ConversionException e) { - return DEFAULT_IMPORT_DEPTH; - } + return getCatalog(catalogName).getInt("defaultImportDepth", DEFAULT_IMPORT_DEPTH); + } + + /** + * Return 'returnFormat' of catalog 'catalogName' if configured. Return DEFAULT_RETURN_FORMAT "XML" otherwise. + * @param catalogName name of catalog + * @return 'returnFormat' of catalog + */ + public static String getReturnFormat(String catalogName) { + return getCatalog(catalogName).getString("returnFormat", DEFAULT_RETURN_FORMAT); + } + + /** + * Return 'metadataFormat' of catalog 'catalogName. + * @param catalogName name of catalog + * @return 'metadataFormat' of catalog + */ + public static String getMetadataFormat(String catalogName) { + return getCatalog(catalogName).getString("metadataFormat"); } /** @@ -365,7 +475,7 @@ public static List getCatalogs() { * @param catalogName String identifying the catalog by attribute "title" * @return HierarchicalConfiguration for single catalog */ - private static HierarchicalConfiguration getCatalog(String catalogName) { + public static HierarchicalConfiguration getCatalog(String catalogName) { XMLConfiguration conf = getConfig(); int countCatalogues = conf.getMaxIndex("catalogue"); HierarchicalConfiguration catalog = null; @@ -386,13 +496,8 @@ private static XMLConfiguration getConfig() { if (config != null) { return config; } - KitodoConfigFile kitodoConfigOpacFile = KitodoConfigFile.OPAC_CONFIGURATION; - if (!kitodoConfigOpacFile.exists()) { - String message = "File not found: " + kitodoConfigOpacFile.getAbsolutePath(); - throw new ConfigException(message, new FileNotFoundException(message)); - } try { - config = new XMLConfiguration(kitodoConfigOpacFile.getFile()); + config = getKitodoOpacConfiguration(); } catch (ConfigurationException e) { logger.error(e); config = new XMLConfiguration(); @@ -401,4 +506,18 @@ private static XMLConfiguration getConfig() { config.setReloadingStrategy(new FileChangedReloadingStrategy()); return config; } + + /** + * Retrieve and return the XML configuration of Kitodo OPACs from the 'kitodo_opac.xml' file. + * @return XMLConfiguration containing the catalog configurations of the 'kitodo_opac.xml' file + * @throws ConfigurationException if 'kitodo_opac.xml' file does not exist + */ + public static XMLConfiguration getKitodoOpacConfiguration() throws ConfigurationException { + KitodoConfigFile kitodoConfigOpacFile = KitodoConfigFile.OPAC_CONFIGURATION; + if (!kitodoConfigOpacFile.exists()) { + String message = "File not found: " + kitodoConfigOpacFile.getAbsolutePath(); + throw new ConfigException(message, new FileNotFoundException(message)); + } + return new XMLConfiguration(kitodoConfigOpacFile.getFile()); + } } diff --git a/Kitodo-API/src/main/java/org/kitodo/exceptions/MandatoryParameterMissingException.java b/Kitodo-API/src/main/java/org/kitodo/exceptions/MandatoryParameterMissingException.java new file mode 100644 index 00000000000..9cbc23dc6d9 --- /dev/null +++ b/Kitodo-API/src/main/java/org/kitodo/exceptions/MandatoryParameterMissingException.java @@ -0,0 +1,27 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.exceptions; + +/** + * This exception is thrown during the import of catalog configurations from 'kitodo_opac.xml' when + * a mandatory XML configuration element like "interfaceType" is missing. + */ +public class MandatoryParameterMissingException extends Exception { + + /** + * Constructor with given parameter name. + * @param parameterName name of missing parameter + */ + public MandatoryParameterMissingException(String parameterName) { + super("Mandatory XML parameter '" + parameterName + "' missing!"); + } +} diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/MappingFile.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/MappingFile.java index f0d441c40a7..6ff0338b41c 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/MappingFile.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/MappingFile.java @@ -16,6 +16,7 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.ManyToMany; import javax.persistence.Table; @@ -38,7 +39,7 @@ public class MappingFile extends BaseBean { @Column(name = "prestructured_import") private Boolean prestructuredImport = false; - @ManyToMany(mappedBy = "mappingFiles", cascade = CascadeType.PERSIST) + @ManyToMany(mappedBy = "mappingFiles", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) private List importConfigurations; /** diff --git a/Kitodo-DataManagement/src/main/resources/db/migration/V2_115__Make_identifier_metadata_nullable.sql b/Kitodo-DataManagement/src/main/resources/db/migration/V2_115__Make_identifier_metadata_nullable.sql new file mode 100644 index 00000000000..967cdc59bde --- /dev/null +++ b/Kitodo-DataManagement/src/main/resources/db/migration/V2_115__Make_identifier_metadata_nullable.sql @@ -0,0 +1,15 @@ +-- +-- (c) Kitodo. Key to digital objects e. V. +-- +-- 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. +-- +-- Migration: change 'identifier_metadata', 'metadata_record_id_xpath' and 'metadata_record_title_xpath' columns +-- to be nullable +ALTER TABLE importconfiguration MODIFY identifier_metadata varchar(255) NULL DEFAULT 'CatalogIDDigital'; +ALTER TABLE importconfiguration MODIFY metadata_record_id_xpath varchar(255) NULL; +ALTER TABLE importconfiguration MODIFY metadata_record_title_xpath varchar(255) NULL; diff --git a/Kitodo-Query-URL-Import/src/main/java/org/kitodo/queryurlimport/QueryURLImport.java b/Kitodo-Query-URL-Import/src/main/java/org/kitodo/queryurlimport/QueryURLImport.java index a062636fe33..fd3ce6d6272 100644 --- a/Kitodo-Query-URL-Import/src/main/java/org/kitodo/queryurlimport/QueryURLImport.java +++ b/Kitodo-Query-URL-Import/src/main/java/org/kitodo/queryurlimport/QueryURLImport.java @@ -89,6 +89,7 @@ public class QueryURLImport implements ExternalDataImportInterface { private static final String FTP_PROTOCOL = "ftp"; private static final String EQUALS_OPERAND = "="; private static final String AND = "&"; + private static final String OAI_IDENTIFIER = "identifier"; private final Charset encoding = StandardCharsets.UTF_8; private CloseableHttpClient httpClient = HttpClientBuilder.create().build(); @@ -160,7 +161,8 @@ public SearchResult search(DataImport dataImport, String key, String value, int return performFtpRequest(dataImport, value, start, numberOfRecords); case HTTP_PROTOCOL: case HTTPS_PROTOCOL: - if (dataImport.getSearchFields().containsKey(key)) { + if (dataImport.getSearchFields().containsKey(key) + || SearchInterfaceType.OAI.equals(dataImport.getSearchInterfaceType())) { return performHTTPRequest(dataImport, Collections.singletonMap(key, value), start, numberOfRecords); } @@ -257,7 +259,9 @@ private DataRecord performQueryToRecord(DataImport dataImport, String queryURL, } String idPrefix = dataImport.getIdPrefix(); String prefix = Objects.nonNull(idPrefix) && !identifier.startsWith(idPrefix) ? idPrefix : ""; - String queryParameter = dataImport.getIdParameter() + EQUALS_OPERAND + prefix + identifier; + String idParameter = SearchInterfaceType.OAI.equals(dataImport.getSearchInterfaceType()) ? OAI_IDENTIFIER + : dataImport.getIdParameter(); + String queryParameter = idParameter + EQUALS_OPERAND + prefix + identifier; if (SearchInterfaceType.SRU.equals(interfaceType)) { fullUrl += URLEncoder.encode(queryParameter, encoding); } else { @@ -424,6 +428,15 @@ private String nodeToString(Node node) throws TransformerException { private LinkedHashMap getSearchFieldMap(DataImport dataImport, Map searchFields) { LinkedHashMap searchFieldMap = new LinkedHashMap<>(); String idPrefix = dataImport.getIdPrefix(); + if (SearchInterfaceType.OAI.equals(dataImport.getSearchInterfaceType()) && searchFields.size() == 1) { + String value = new LinkedList<>(searchFields.values()).getFirst(); + if (StringUtils.isBlank(idPrefix) || value.startsWith(idPrefix)) { + searchFieldMap.put(OAI_IDENTIFIER, value); + } else { + searchFieldMap.put(OAI_IDENTIFIER, idPrefix + value); + } + return searchFieldMap; + } String idParameter = dataImport.getIdParameter(); for (Map.Entry entry : searchFields.entrySet()) { String searchField = dataImport.getSearchFields().get(entry.getKey()); diff --git a/Kitodo-Query-URL-Import/src/test/resources/kitodo_opac.xml b/Kitodo-Query-URL-Import/src/test/resources/kitodo_opac.xml deleted file mode 100644 index 9b65c25e34f..00000000000 --- a/Kitodo-Query-URL-Import/src/test/resources/kitodo_opac.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - Aa - Oa - - - - - Ab - Ob - - - - - Of - Af - OF - AF - - - - - Aj - Oj - - - - - sru - xml - MODS - - mods2kitodo.xsl - - - - - - - - - - - - - - - - - - - - - - - - - sru - xml - MODS - - mods2kitodo.xsl - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Kitodo/src/main/java/org/kitodo/exceptions/CatalogConfigurationImportException.java b/Kitodo/src/main/java/org/kitodo/exceptions/CatalogConfigurationImportException.java new file mode 100644 index 00000000000..106e5ed0277 --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/exceptions/CatalogConfigurationImportException.java @@ -0,0 +1,28 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.exceptions; + +/** + * This is the base exception class for errors that can occur during the import of catalog configurations from the file + * 'kitodo_opac.xml'. + */ +public class CatalogConfigurationImportException extends Exception { + + /** + * Constructor with given exception message. + * @param exceptionMessage as String + */ + public CatalogConfigurationImportException(String exceptionMessage) { + super(exceptionMessage); + } + +} diff --git a/Kitodo/src/main/java/org/kitodo/exceptions/InvalidPortException.java b/Kitodo/src/main/java/org/kitodo/exceptions/InvalidPortException.java new file mode 100644 index 00000000000..2b703315bad --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/exceptions/InvalidPortException.java @@ -0,0 +1,30 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.exceptions; + +import org.kitodo.production.helper.Helper; + +/** + * This exception is thrown during import of catalog configurations from 'kitodo_opac.xml' if the catalog + * configuration contains a 'port' value that is not an integer in the range of valid ports from 0 to 65535. + */ +public class InvalidPortException extends CatalogConfigurationImportException { + + /** + * Constructor with given invalid port value. + * + * @param portValue as String + */ + public InvalidPortException(String portValue) { + super(Helper.getTranslation("importConfig.migration.error.invalidPort", portValue)); + } +} diff --git a/Kitodo/src/main/java/org/kitodo/exceptions/MappingFilesMissingException.java b/Kitodo/src/main/java/org/kitodo/exceptions/MappingFilesMissingException.java new file mode 100644 index 00000000000..220fdf16e63 --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/exceptions/MappingFilesMissingException.java @@ -0,0 +1,28 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.exceptions; + +import org.kitodo.production.helper.Helper; + +/** + * This exception is thrown during the import of catalog configurations from 'kitodo_opac.xml' if the catalog + * configuration is missing the mandatory XML element 'mappingFiles'. + */ +public class MappingFilesMissingException extends CatalogConfigurationImportException { + + /** + * Constructor with given catalog configuration title. + */ + public MappingFilesMissingException() { + super(Helper.getTranslation("importConfig.migration.error.mappingFilesMissing")); + } +} diff --git a/Kitodo/src/main/java/org/kitodo/exceptions/UndefinedMappingFileException.java b/Kitodo/src/main/java/org/kitodo/exceptions/UndefinedMappingFileException.java new file mode 100644 index 00000000000..b442f4f0724 --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/exceptions/UndefinedMappingFileException.java @@ -0,0 +1,29 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.exceptions; + +import org.kitodo.production.helper.Helper; + +/** + * This exception is thrown during the import of catalog configurations from 'kitodo_opac.xml' when no MappingFile + * instance was found for an XML transformation file referenced in the catalog configuration. + */ +public class UndefinedMappingFileException extends CatalogConfigurationImportException { + + /** + * Constructor with given XSLT filename. + * @param xsltFilename as String + */ + public UndefinedMappingFileException(String xsltFilename) { + super(Helper.getTranslation("importConfig.migration.error.mappingFileMissing", xsltFilename)); + } +} diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/ImportCatalogConfigurationsView.java b/Kitodo/src/main/java/org/kitodo/production/forms/ImportCatalogConfigurationsView.java new file mode 100644 index 00000000000..a04eb9bab0b --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/production/forms/ImportCatalogConfigurationsView.java @@ -0,0 +1,254 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.forms; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.faces.view.ViewScoped; +import javax.inject.Named; + +import org.apache.commons.configuration.ConfigurationException; +import org.kitodo.api.schemaconverter.MetadataFormat; +import org.kitodo.config.OPACConfig; +import org.kitodo.data.database.beans.MappingFile; +import org.kitodo.data.database.exceptions.DAOException; +import org.kitodo.production.helper.CatalogConfigurationImporter; +import org.kitodo.production.helper.Helper; +import org.kitodo.production.services.ServiceManager; +import org.primefaces.PrimeFaces; + +@Named +@ViewScoped +public class ImportCatalogConfigurationsView implements Serializable { + + private Integer numberOfCatalogs; + private List catalogs; + private List selectedCatalogs; + private HashMap convertedCatalogs; + private MappingFile currentMappingFile = null; + private HashMap allMappingFiles; + private LinkedList mappingFilenames; + private int currentMappingFileIndex; + private final CatalogConfigurationImporter importer; + + public ImportCatalogConfigurationsView() { + importer = new CatalogConfigurationImporter(); + } + + /** + * Get numberOfCatalogs. + * + * @return value of numberOfCatalogs + */ + public Integer getNumberOfCatalogs() { + if (Objects.isNull(numberOfCatalogs)) { + numberOfCatalogs = OPACConfig.getCatalogs().size(); + } + return numberOfCatalogs; + } + + /** + * Set numberOfCatalogs. + * + * @param numberOfCatalogs as int + */ + public void setNumberOfCatalogs(Integer numberOfCatalogs) { + this.numberOfCatalogs = numberOfCatalogs; + } + + /** + * Get catalogs. + * + * @return value of catalogs + */ + public List getCatalogs() { + if (Objects.isNull(catalogs)) { + catalogs = OPACConfig.getCatalogs(); + } + return catalogs; + } + + /** + * Set catalogs. + * + * @param catalogs as List of String + */ + public void setCatalogs(List catalogs) { + this.catalogs = catalogs; + } + + /** + * Get selectedCatalogs. + * + * @return value of selectedCatalogs + */ + public List getSelectedCatalogs() { + if (Objects.isNull(selectedCatalogs) && Objects.nonNull(catalogs)) { + selectedCatalogs = new LinkedList<>(catalogs); + } + return selectedCatalogs; + } + + /** + * Set selectedCatalogs. + * + * @param selectedCatalogs as List of String + */ + public void setSelectedCatalogs(List selectedCatalogs) { + this.selectedCatalogs = selectedCatalogs; + } + + public void prepare() { + convertedCatalogs = new HashMap<>(); + currentMappingFileIndex = 0; + } + + /** + * Start catalog configuration import. + */ + public void startImport() { + try { + importMappingFiles(); + } catch (DAOException | ConfigurationException e) { + Helper.setErrorMessage(e); + } + } + + /** + * Import mapping files. + * @throws DAOException thrown when a mapping file could not be saved to the database. + */ + private void importMappingFiles() throws DAOException, ConfigurationException { + currentMappingFileIndex = 0; + allMappingFiles = importer.getAllMappingFiles(selectedCatalogs); + mappingFilenames = new LinkedList<>(allMappingFiles.keySet()); + if (!allMappingFiles.isEmpty() && currentMappingFileIndex < mappingFilenames.size()) { + importMappingFile(mappingFilenames.get(currentMappingFileIndex)); + } else { + importConfigurations(); + } + } + + private void importMappingFile(String filename) throws DAOException, ConfigurationException { + currentMappingFile = allMappingFiles.get(filename); + if (Objects.isNull(currentMappingFile)) { + currentMappingFile = new MappingFile(); + currentMappingFile.setFile(filename); + currentMappingFile.setTitle(filename); + PrimeFaces.current().ajax().update("mappingFileFormatsDialog"); + PrimeFaces.current().executeScript("PF('mappingFileFormatsDialog').show();"); + } else { + currentMappingFileIndex++; + if (currentMappingFileIndex < mappingFilenames.size()) { + importMappingFile(mappingFilenames.get(currentMappingFileIndex)); + } else { + importConfigurations(); + } + } + } + + /** + * Add mapping file to database. + * @throws DAOException when mapping file could not be saved to database + */ + public void addMappingFile() throws DAOException { + try { + ServiceManager.getMappingFileService().saveToDatabase(currentMappingFile); + currentMappingFileIndex++; + if (currentMappingFileIndex < mappingFilenames.size()) { + importMappingFile(mappingFilenames.get(currentMappingFileIndex)); + } else { + importConfigurations(); + } + } catch (ConfigurationException e) { + Helper.setErrorMessage(e); + } + } + + private void importConfigurations() throws DAOException, ConfigurationException { + convertedCatalogs = importer.importCatalogConfigurations(selectedCatalogs); + PrimeFaces.current().ajax().update("catalogConfigurationImportResultDialog", "configurationTable", + "mappingTable"); + PrimeFaces.current().executeScript("PF('catalogConfigurationImportResultDialog').show()"); + } + + /** + * Get list of catalog configurations successfully imported. + * @return list of catalog configurations successfully imported. + */ + public List getSuccessfulImports() { + if (Objects.nonNull(convertedCatalogs)) { + return convertedCatalogs.entrySet().stream().filter(e -> Objects.isNull(e.getValue())) + .map(Map.Entry::getKey).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + /** + * Get list of failed catalog configuration imports. + * @return list of failed catalog configuration imports. + */ + public List getFailedImports() { + if (Objects.nonNull(convertedCatalogs)) { + return convertedCatalogs.entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(Map.Entry::getKey).collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + /** + * Get error message for import of catalog configuration with name 'opacTitle'. Returns empty + * String for successful imports. + * @param opacTitle name of catalog configuration + * @return error message for failed catalog configuration import; empty String for successful imports + */ + public String getErrorMessage(String opacTitle) { + if (convertedCatalogs.containsKey(opacTitle)) { + return convertedCatalogs.get(opacTitle); + } + return ""; + } + + /** + * Get currentMappingFile. + * + * @return value of currentMappingFile + */ + public MappingFile getCurrentMappingFile() { + return currentMappingFile; + } + + /** + * Set currentMappingFile. + * + * @param currentMappingFile as org.kitodo.data.database.beans.MappingFile + */ + public void setCurrentMappingFile(MappingFile currentMappingFile) { + this.currentMappingFile = currentMappingFile; + } + + /** + * Get metadata formats. + * + * @return metadata formats + */ + public List getMetadataFormats() { + return List.of(MetadataFormat.values()); + } +} diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/ImportConfigurationListView.java b/Kitodo/src/main/java/org/kitodo/production/forms/ImportConfigurationListView.java index 907b352392a..0d5b7f23c03 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/ImportConfigurationListView.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/ImportConfigurationListView.java @@ -18,14 +18,17 @@ import javax.faces.view.ViewScoped; import javax.inject.Named; +import org.apache.commons.configuration.ConfigurationException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.kitodo.config.OPACConfig; import org.kitodo.data.database.beans.ImportConfiguration; import org.kitodo.data.database.exceptions.DAOException; import org.kitodo.exceptions.ImportConfigurationInUseException; import org.kitodo.production.enums.ObjectType; import org.kitodo.production.helper.Helper; import org.kitodo.production.services.ServiceManager; +import org.primefaces.PrimeFaces; @Named("ImportConfigurationListView") @ViewScoped @@ -74,4 +77,16 @@ public void deleteById(int id) { e.getMessage()); } } + + /** + * Start import of catalog configurations from 'kitodo_opac.xml' file. + */ + public void startCatalogConfigurationImport() { + try { + OPACConfig.getKitodoOpacConfiguration(); + PrimeFaces.current().executeScript("PF('importCatalogConfigurationsDialog').show();"); + } catch (ConfigurationException e) { + Helper.setErrorMessage(e.getMessage() + ": " + e.getCause().getMessage()); + } + } } diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/createprocess/CatalogImportDialog.java b/Kitodo/src/main/java/org/kitodo/production/forms/createprocess/CatalogImportDialog.java index e2a8db5317b..b27780d33e7 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/createprocess/CatalogImportDialog.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/createprocess/CatalogImportDialog.java @@ -108,7 +108,7 @@ public List getSearchFields() { public void search() { try { createProcessForm.setIdentifierMetadata(hitModel.getImportConfiguration().getIdentifierMetadata()); - if (SearchInterfaceType.OAI.name().equals(hitModel.getImportConfiguration().getInterfaceType())) { + if (skipHitList(hitModel.getImportConfiguration(), hitModel.getSelectedField())) { getRecordById(hitModel.getSearchTerm()); } else { List hits = hitModel.load(0, 10, null, SortOrder.ASCENDING, Collections.EMPTY_MAP); @@ -130,6 +130,15 @@ public void search() { } } + private boolean skipHitList(ImportConfiguration importConfiguration, String searchField) { + if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType()) + || searchField.equals(importConfiguration.getIdSearchField().getLabel())) { + return true; + } + return (Objects.isNull(importConfiguration.getMetadataRecordIdXPath()) + || Objects.isNull(importConfiguration.getMetadataRecordTitleXPath())); + } + /** * Get retrieved hits. Returns empty list if LazyHitModel instance is null. * diff --git a/Kitodo/src/main/java/org/kitodo/production/helper/CatalogConfigurationImporter.java b/Kitodo/src/main/java/org/kitodo/production/helper/CatalogConfigurationImporter.java new file mode 100644 index 00000000000..e5ab9463a93 --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/production/helper/CatalogConfigurationImporter.java @@ -0,0 +1,334 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.helper; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apache.commons.configuration.HierarchicalConfiguration; +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.kitodo.api.externaldatamanagement.ImportConfigurationType; +import org.kitodo.api.externaldatamanagement.SearchInterfaceType; +import org.kitodo.api.schemaconverter.MetadataFormat; +import org.kitodo.config.OPACConfig; +import org.kitodo.data.database.beans.ImportConfiguration; +import org.kitodo.data.database.beans.MappingFile; +import org.kitodo.data.database.beans.SearchField; +import org.kitodo.data.database.exceptions.DAOException; +import org.kitodo.exceptions.InvalidPortException; +import org.kitodo.exceptions.MandatoryParameterMissingException; +import org.kitodo.exceptions.MappingFilesMissingException; +import org.kitodo.exceptions.UndefinedMappingFileException; +import org.kitodo.production.services.ServiceManager; + +/** + * This class provides a way to convert catalog import configurations from existing 'kitodo_opac.xml' + * configuration files into new 'ImportConfiguration' objects. + */ +public class CatalogConfigurationImporter { + + private static final Logger logger = LogManager.getLogger(CatalogConfigurationImporter.class); + private static final String FILE_UPLOAD_DEFAULT_POSTFIX = " - " + + Helper.getTranslation("newProcess.fileUpload.heading"); + private static final String SEARCH_FIELD = "searchField"; + private static final String LABEL = "[@label]"; + private static final String VALUE = "[@value]"; + + /** + * Convert catalog configuration with title 'opacTitle' to new 'ImportConfiguration' object. Also creates + * corresponding SearchField objects and assigns MappingFile objects according to original XML configuration. + * @param catalogName title of catalog configuration in XML file + * @throws DAOException when available MappingFile objects could not be loaded from database or new + * ImportConfiguration object could not be saved to database + * @throws UndefinedMappingFileException when XML catalog configuration contains mapping file for which no + * corresponding MappingFile instance can be found + * @throws MappingFilesMissingException when XML catalog configuration does not contain mandatory 'mappingFiles' + * element. + * @throws InvalidPortException when XML catalog configuration contains an invalid port value. + */ + private void convertOpacConfig(String catalogName, List currentConfigurations) throws DAOException, + UndefinedMappingFileException, MappingFilesMissingException, MandatoryParameterMissingException, + InvalidPortException { + HierarchicalConfiguration opacConfiguration = OPACConfig.getCatalog(catalogName); + String fileUploadTitle = catalogName + FILE_UPLOAD_DEFAULT_POSTFIX; + if (OPACConfig.getFileUploadConfig(catalogName) && !currentConfigurations.contains(fileUploadTitle)) { + createFileUploadConfiguration(catalogName, fileUploadTitle); + } + ImportConfiguration importConfiguration = null; + if (Objects.nonNull(opacConfiguration)) { + importConfiguration = new ImportConfiguration(); + importConfiguration.setTitle(catalogName); + importConfiguration.setDescription(OPACConfig.getOPACDescription(catalogName)); + importConfiguration.setConfigurationType(ImportConfigurationType.OPAC_SEARCH.toString()); + importConfiguration.setReturnFormat(OPACConfig.getReturnFormat(catalogName).toUpperCase()); + setMetadataFormat(importConfiguration, catalogName); + setSearchInterfaceType(importConfiguration, catalogName); + setUrl(importConfiguration, catalogName); + setItemFields(importConfiguration, catalogName); + importConfiguration.setDefaultImportDepth(OPACConfig.getDefaultImportDepth(catalogName)); + importConfiguration.setIdentifierMetadata(OPACConfig.getIdentifierMetadata(catalogName)); + setCredentials(importConfiguration, catalogName); + importConfiguration.setIdPrefix(OPACConfig.getIdentifierPrefix(catalogName)); + importConfiguration.setParentElementTrimMode(OPACConfig.getParentIDTrimMode(catalogName)); + importConfiguration.setQueryDelimiter(OPACConfig.getQueryDelimiter(catalogName)); + importConfiguration.setPrestructuredImport(OPACConfig.isPrestructuredImport(catalogName)); + if (SearchInterfaceType.SRU.name().equals(importConfiguration.getInterfaceType()) + || SearchInterfaceType.CUSTOM.name().equals(importConfiguration.getInterfaceType())) { + setSearchFields(importConfiguration, catalogName); + } + importConfiguration.setMappingFiles(getMappingFiles(importConfiguration)); + importConfiguration.setPrestructuredImport(OPACConfig.isPrestructuredImport(catalogName)); + } + ServiceManager.getImportConfigurationService().saveToDatabase(importConfiguration); + } + + private void setUrl(ImportConfiguration importConfiguration, String opacTitle) + throws MandatoryParameterMissingException, InvalidPortException { + importConfiguration.setScheme(OPACConfig.getScheme(opacTitle)); + importConfiguration.setHost(OPACConfig.getHost(opacTitle)); + importConfiguration.setPath(OPACConfig.getPath(opacTitle)); + importConfiguration.setPort(getPort(opacTitle)); + } + + private void setCredentials(ImportConfiguration importConfiguration, String opacTitle) { + try { + importConfiguration.setUsername(OPACConfig.getUsername(opacTitle)); + importConfiguration.setPassword(OPACConfig.getPassword(opacTitle)); + } catch (IllegalArgumentException e) { + logger.info("No credentials configured for configuration '" + opacTitle + "'."); + } + } + + private void setSearchInterfaceType(ImportConfiguration importConfiguration, String opacTitle) + throws MandatoryParameterMissingException { + // use new "CUSTOM" type if SearchInterfaceType is null or could not be recognized + SearchInterfaceType type = OPACConfig.getInterfaceType(opacTitle); + if (Objects.nonNull(type) && Arrays.asList(SearchInterfaceType.values()).contains(type)) { + importConfiguration.setInterfaceType(type.name()); + } else { + importConfiguration.setInterfaceType(SearchInterfaceType.CUSTOM.name()); + } + if (SearchInterfaceType.SRU.equals(type)) { + importConfiguration.setSruVersion(OPACConfig.getSruVersion(opacTitle)); + importConfiguration.setSruRecordSchema(OPACConfig.getSruRecordSchema(opacTitle)); + } else if (SearchInterfaceType.OAI.equals(type)) { + importConfiguration.setOaiMetadataPrefix(OPACConfig.getOaiMetadataPrefix(opacTitle)); + } + } + + private void setMetadataFormat(ImportConfiguration importConfiguration, String opacTitle) { + // set MetadataFormat to "OTHER" if configuration contains a value different from the options in the + // "MetadataFormat.java" enum! + String metadataFormat = OPACConfig.getMetadataFormat(opacTitle).toUpperCase(); + if (Arrays.stream(MetadataFormat.values()).map(MetadataFormat::name).collect(Collectors.toList()) + .contains(metadataFormat)) { + importConfiguration.setMetadataFormat(metadataFormat); + } else { + importConfiguration.setMetadataFormat(MetadataFormat.OTHER.name()); + } + importConfiguration.setMetadataRecordIdXPath(MetadataFormat.getDefaultRecordIdXpath(metadataFormat)); + importConfiguration.setMetadataRecordTitleXPath(MetadataFormat.getDefaultRecordTitleXpath(metadataFormat)); + } + + private void setSearchFields(ImportConfiguration importConfiguration, String opacTitle) { + importConfiguration.setSearchFields(getSearchFields(importConfiguration)); + for (SearchField searchField : importConfiguration.getSearchFields()) { + if (OPACConfig.getDefaultSearchField(opacTitle).equals(searchField.getLabel())) { + importConfiguration.setDefaultSearchField(searchField); + break; + } + } + for (SearchField searchField : importConfiguration.getSearchFields()) { + if (OPACConfig.getIdentifierParameter(opacTitle).equals(searchField.getValue())) { + importConfiguration.setIdSearchField(searchField); + break; + } + } + } + + private void setItemFields(ImportConfiguration importConfiguration, String opacTitle) { + importConfiguration.setItemFieldXpath(OPACConfig.getExemplarFieldXPath(opacTitle)); + importConfiguration.setItemFieldOwnerSubPath(OPACConfig.getExemplarFieldOwnerXPath(opacTitle)); + importConfiguration.setItemFieldOwnerMetadata(OPACConfig.getExemplarFieldOwnerMetadata(opacTitle)); + importConfiguration.setItemFieldSignatureSubPath(OPACConfig.getExemplarFieldSignatureXPath(opacTitle)); + importConfiguration.setItemFieldSignatureMetadata(OPACConfig.getExemplarFieldSignatureMetadata(opacTitle)); + } + + private void createFileUploadConfiguration(String catalogTitle, String fileUploadConfigurationTitle) + throws DAOException, UndefinedMappingFileException, MappingFilesMissingException { + ImportConfiguration fileUploadConfiguration = new ImportConfiguration(); + fileUploadConfiguration.setConfigurationType(ImportConfigurationType.FILE_UPLOAD.name()); + fileUploadConfiguration.setTitle(catalogTitle); + fileUploadConfiguration.setDescription(OPACConfig.getOPACDescription(catalogTitle)); + fileUploadConfiguration.setReturnFormat(OPACConfig.getReturnFormat(catalogTitle).toUpperCase()); + setMetadataFormat(fileUploadConfiguration, catalogTitle); + MappingFile parentMappingFile = null; + for (MappingFile mappingFile : ServiceManager.getMappingFileService().getAll()) { + if (mappingFile.getFile().equals(OPACConfig.getXsltMappingFileForParentInRecord(catalogTitle))) { + parentMappingFile = mappingFile; + break; + } + } + if (Objects.nonNull(parentMappingFile)) { + fileUploadConfiguration.setParentMappingFile(parentMappingFile); + } + fileUploadConfiguration.setMappingFiles(getMappingFiles(fileUploadConfiguration)); + fileUploadConfiguration.setIdentifierMetadata(OPACConfig.getIdentifierMetadata(catalogTitle)); + // update title to include "File upload" postfix! (original title is required until here to load mapping files!) + fileUploadConfiguration.setTitle(fileUploadConfigurationTitle); + ServiceManager.getImportConfigurationService().saveToDatabase(fileUploadConfiguration); + } + + private List getSearchFields(ImportConfiguration configuration) { + List searchFields = new LinkedList<>(); + for (HierarchicalConfiguration searchFieldConfig : OPACConfig.getSearchFields(configuration.getTitle()) + .configurationsAt(SEARCH_FIELD)) { + SearchField searchField = new SearchField(); + searchField.setLabel(searchFieldConfig.getString(LABEL)); + searchField.setValue(searchFieldConfig.getString(VALUE)); + searchField.setImportConfiguration(configuration); + searchFields.add(searchField); + } + return searchFields; + } + + private List getMappingFiles(ImportConfiguration configuration) throws UndefinedMappingFileException, + DAOException, MappingFilesMissingException { + List mappingFiles = new LinkedList<>(); + List allMappingFiles = ServiceManager.getMappingFileService().getAll(); + try { + for (String filename : OPACConfig.getXsltMappingFiles(configuration.getTitle())) { + MappingFile mappingFile = null; + // Find mapping file object by filename + for (MappingFile currentFile : allMappingFiles) { + if (currentFile.getFile().equals(filename)) { + mappingFile = currentFile; + mappingFile.getImportConfigurations().add(configuration); + break; + } + } + if (Objects.isNull(mappingFile)) { + // Happens when user skips conversion of a mapping file used in the current import configuration! + throw new UndefinedMappingFileException(filename); + } + mappingFiles.add(mappingFile); + } + } catch (IllegalArgumentException e) { + throw new MappingFilesMissingException(); + } + return mappingFiles; + } + + /** + * Retrieve and return 'port' value of catalog with name 'catalogName' from 'kitodo_opac.xml' configuration file. + * + * @param catalogName name of catalog + * @return port value as integer + * @throws InvalidPortException if port value retrieved from 'kitodo_opac.xml' is not an integer between 0 and 65535 + */ + public static Integer getPort(String catalogName) throws InvalidPortException { + try { + String portString = OPACConfig.getPort(catalogName); + if (StringUtils.isNotBlank(portString)) { + try { + int port = Integer.parseInt(portString); + if (port < 0 || port > 65535) { + throw new InvalidPortException(portString); + } + return port; + } catch (NumberFormatException e) { + throw new InvalidPortException(portString); + } + } + } catch (MandatoryParameterMissingException e) { + // ignore exception because "port" is not mandatory + } + return null; + } + + /** + * Retrieve MappingFile objects from database for all catalog configurations in given list 'catalogs'. + * @param catalogs list of catalog names as Strings + * @return map containing mapping file names as keys and + * - corresponding MappingFile objects as values if it exists + * - null otherwise + * @throws DAOException when MappingFile objects could not be loaded from database + */ + public HashMap getAllMappingFiles(List catalogs) throws DAOException { + HashMap allMappingFiles = new HashMap<>(); + for (String catalogName : catalogs) { + try { + List mappingFileNames = OPACConfig.getXsltMappingFiles(catalogName); + String parentMappingFilename = OPACConfig.getXsltMappingFileForParentInRecord(catalogName); + if (Objects.nonNull(parentMappingFilename) && !mappingFileNames.contains(parentMappingFilename)) { + mappingFileNames.add(parentMappingFilename); + } + for (String xsltFilename : mappingFileNames) { + if (!allMappingFiles.containsKey(xsltFilename)) { + allMappingFiles.put(xsltFilename, null); + for (MappingFile mappingFile : ServiceManager.getMappingFileService().getAll()) { + if (mappingFile.getFile().equals(xsltFilename)) { + allMappingFiles.put(xsltFilename, mappingFile); + break; + } + } + } + } + } catch (IllegalArgumentException e) { + logger.error("Unable to import OPAC configuration '" + catalogName + "' (" + e.getMessage() + ")"); + } + } + return allMappingFiles; + } + + /** + * Import all catalog configurations in given list 'catalogs' from configuration file 'kitodo_opac.xml' to new + * ImportConfigurations. Return a map containing the results of the import process. The map contains the catalog + * names as keys and potential error messages as values. Map entries with empty values mark successful catalog + * configuration imports. + * + * @param catalogs list of catalog configurations to import from 'kitodo_opac.xml' by name + * @return success map of catalog configuration import process + * @throws DAOException when existing ImportConfigurations could not be retrieved from database + */ + public HashMap importCatalogConfigurations(List catalogs) throws DAOException { + HashMap conversions = new HashMap<>(); + List currentConfigurations = ServiceManager.getImportConfigurationService().getAll() + .stream().map(ImportConfiguration::getTitle).collect(Collectors.toList()); + for (String catalog : catalogs) { + if (currentConfigurations.contains(catalog)) { + conversions.put(catalog, Helper.getTranslation("importConfig.migration.error.configurationExists")); + } else { + try { + convertOpacConfig(catalog, currentConfigurations); + conversions.put(catalog, null); + } catch (UndefinedMappingFileException | MappingFilesMissingException + | MandatoryParameterMissingException | InvalidPortException e) { + conversions.put(catalog, e.getMessage()); + } catch (DAOException e) { + if (Objects.nonNull(e.getCause()) && Objects.nonNull(e.getCause().getCause())) { + conversions.put(catalog, e.getCause().getCause().getMessage()); + } else { + conversions.put(catalog, e.getMessage()); + } + } + } + } + return conversions; + } +} diff --git a/Kitodo/src/main/java/org/kitodo/production/model/LazyHitModel.java b/Kitodo/src/main/java/org/kitodo/production/model/LazyHitModel.java index 653e2388f63..5c60fbaf28b 100644 --- a/Kitodo/src/main/java/org/kitodo/production/model/LazyHitModel.java +++ b/Kitodo/src/main/java/org/kitodo/production/model/LazyHitModel.java @@ -21,6 +21,7 @@ import org.kitodo.api.externaldatamanagement.SingleHit; import org.kitodo.data.database.beans.ImportConfiguration; import org.kitodo.production.services.ServiceManager; +import org.kitodo.production.services.data.ImportService; import org.primefaces.model.LazyDataModel; import org.primefaces.model.SortOrder; @@ -100,7 +101,7 @@ public ImportConfiguration getImportConfiguration() { public void setImportConfiguration(ImportConfiguration importConfiguration) { this.importConfiguration = importConfiguration; if (Objects.nonNull(importConfiguration)) { - this.setSelectedField(ServiceManager.getImportService().getDefaultSearchField(importConfiguration)); + this.setSelectedField(ImportService.getDefaultSearchField(importConfiguration)); this.setImportDepth(ServiceManager.getImportService().getDefaultImportDepth(importConfiguration)); } } diff --git a/Kitodo/src/main/java/org/kitodo/production/services/data/ImportConfigurationService.java b/Kitodo/src/main/java/org/kitodo/production/services/data/ImportConfigurationService.java index 75c2159a775..0594b16a23a 100644 --- a/Kitodo/src/main/java/org/kitodo/production/services/data/ImportConfigurationService.java +++ b/Kitodo/src/main/java/org/kitodo/production/services/data/ImportConfigurationService.java @@ -77,7 +77,7 @@ public List loadData(int first, int pageSize, String sortField, SortOrder sortOr */ @Override public Long countDatabaseRows() throws DAOException { - return countDatabaseRows("SELECT COUNT(*) FROM importconfiguration"); + return countDatabaseRows("SELECT COUNT(*) FROM ImportConfiguration"); } /** diff --git a/Kitodo/src/main/java/org/kitodo/production/services/data/ImportService.java b/Kitodo/src/main/java/org/kitodo/production/services/data/ImportService.java index 6c5b844cbfc..1c91cda2ab5 100644 --- a/Kitodo/src/main/java/org/kitodo/production/services/data/ImportService.java +++ b/Kitodo/src/main/java/org/kitodo/production/services/data/ImportService.java @@ -201,9 +201,12 @@ private ExternalDataImportInterface initializeImportModule() { */ public List getAvailableSearchFields(ImportConfiguration importConfiguration) { try { - // FTP server do not support query parameters but only use the filename for OPAC search! if (SearchInterfaceType.FTP.name().equals(importConfiguration.getInterfaceType())) { + // FTP server do not support query parameters but only use the filename for OPAC search! return Collections.singletonList(Helper.getTranslation("filename")); + } else if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType())) { + // OAI PMH interfaces do not support query parameters but only use the ID of the record to retrieve it! + return Collections.singletonList(Helper.getTranslation("recordId")); } else { List fields = new ArrayList<>(); List searchFields = importConfiguration.getSearchFields(); @@ -231,14 +234,15 @@ public List getAvailableSearchFields(ImportConfiguration importConfigura * @param importConfiguration ImportConfiguration * @return label of default search field */ - public String getDefaultSearchField(ImportConfiguration importConfiguration) { + public static String getDefaultSearchField(ImportConfiguration importConfiguration) { if (SearchInterfaceType.FTP.name().equals(importConfiguration.getInterfaceType())) { return Helper.getTranslation("filename"); + } else if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType())) { + return Helper.getTranslation("recordId"); } else if (Objects.nonNull(importConfiguration.getDefaultSearchField())) { return importConfiguration.getDefaultSearchField().getLabel(); - } else { - return ""; } + return ""; } /** @@ -539,9 +543,9 @@ private void importParents(String recordId, ImportConfiguration importConfigurat } // always try to find a parent for last imported process (e.g. level == // importDepth) in the database! - if (Objects.nonNull(parentID) && level == importDepth) { - checkForParent(parentID, template.getRuleset().getId(), projectId, - importConfiguration.getIdentifierMetadata()); + String idMetadata = importConfiguration.getIdentifierMetadata(); + if (Objects.nonNull(parentID) && level == importDepth && Objects.nonNull(idMetadata)) { + checkForParent(parentID, template.getRuleset().getId(), projectId, idMetadata); } } diff --git a/Kitodo/src/main/java/org/kitodo/production/services/data/MappingFileService.java b/Kitodo/src/main/java/org/kitodo/production/services/data/MappingFileService.java index aa4cd81b038..b8f8b21b0be 100644 --- a/Kitodo/src/main/java/org/kitodo/production/services/data/MappingFileService.java +++ b/Kitodo/src/main/java/org/kitodo/production/services/data/MappingFileService.java @@ -74,7 +74,7 @@ public List loadData(int first, int pageSize, String sortField, SortOrder sortOr */ @Override public Long countDatabaseRows() throws DAOException { - return null; + return countDatabaseRows("SELECT COUNT(*) FROM MappingFile"); } /** diff --git a/Kitodo/src/main/resources/kitodo_opac.xml b/Kitodo/src/main/resources/kitodo_opac.xml index 3f634becf10..592cbb32017 100644 --- a/Kitodo/src/main/resources/kitodo_opac.xml +++ b/Kitodo/src/main/resources/kitodo_opac.xml @@ -16,212 +16,9 @@ - - sru - xml - MARC - - - false - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - sru - xml - MARC - false - - - - - - - - - - - - - - - - - - - - - - - - - - sru - xml - PICA - false - - - pica2kitodo.xsl - - - - - - - - - - - - - - - - - - - - - - - - - - sru - xml - MODS - false - - mods2kitodo.xsl - - parentMapping.xsl - - - - - - - - - - - - - - - - - - - - - - - - - sru - xml - MARC - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Kitodo/src/main/resources/messages/messages_de.properties b/Kitodo/src/main/resources/messages/messages_de.properties index fcd5649c359..103c3286bc3 100644 --- a/Kitodo/src/main/resources/messages/messages_de.properties +++ b/Kitodo/src/main/resources/messages/messages_de.properties @@ -239,7 +239,7 @@ correctionSolveProblem=Korrekturl\u00F6sung konnte nicht an folgende Aufgabe gem correctionTasks=Korrekturschritte correctionWorkflow=Korrektur-Workflow corruptData=Daten korrupt -corruptDataDescription=Die Aktion konnte nicht durchgef\u00FChrt werden, da ein Fehler in den Daten festgestellt wurde. Der Editor wird neu geladen und der zuletzt gespeicherte Stand wiederhergestellt. \u00C4nderungen nach dem letzten Speichern werden verworfen. Sollte das Problem bei der durchgef\u00FChrten Aktion wiederholt auftreten, versuchen Sie bitte die gleiche Aktion im Strukturbaum durchzuf\u00FChren und informieren Sie Ihren Administrator. +corruptDataDescription=Die Aktion konnte nicht durchgef\u00FChrt werden, da ein Fehler in den Daten festgestellt wurde. Der Editor wird neu geladen und der zuletzt gespeicherte Stand wiederhergestellt. \u00C4nderungen nach dem letzten Speichern werden verworfen. Sollte das Problem bei der durchgef\u00FChrten Aktion wiederholt auftreten, versuchen Sie bitte die gleiche Aktion im Strukturbaum durchzuf\u00FChren und informieren Sie Ihren Administrator. couldNotCreateImageFolder=Das Verzeichnis f\u00FCr die Images konnte nicht angelegt werden count=Anzahl counting=Z\u00E4hlung @@ -529,13 +529,25 @@ imageGenerationNotPossible=Das Generieren der Bilder ist auf Grund von fehlender images=Bilder imagesRead=Images lesen imagesWrite=Images schreiben +import.failedImports=Fehlgeschlagene Importe +import.successfulImports=Erfolgreiche Importe importConfig.addSearchField=Suchfeld hinzuf\u00fcgen -importConfig.assignedAsDefaultConfig=Importkonfiguration ''{0}'' wird derzeit als Standardkonfiguration von Projekt ''{1}'' verwendet! +importConfig.assignedAsDefaultConfig=Importkonfiguration "{0}" wird als Standardkonfiguration von Projekt "{1}" verwendet! importConfig.catalogIdMetadata=Metadatum f\u00fcr Katalog-ID importConfig.configuration=Importkonfiguration importConfig.configurations=Importkonfigurationen importConfig.defaultConfiguration=Default-Konfiguration importConfig.editSearchField=Suchfeld bearbeiten +importConfig.migration.importCatalogConfigurations=Katalogkonfigurationen importieren +importConfig.migration.numberOfCatalogConfigurations=Katalogkonfigurationen gefunden +importConfig.migration.importCatalogConfigurationsDesc=Diese Schaltfläche erlaubt es, Katalogkonfigurationen aus bestehenden 'kitodo_opac.xml'-Dateien in neue Importkonfigurationen zu konvertieren. Dies ist ein halbautomatischer Prozess, bei dem der Benutzer an verschiedenen Stellen aufgefordert wird, Eingaben zu machen. Z.B. muss der Benutzer für XSLT-Abbildungsdateien explizit angeben, welche Ein- und Ausgabegabeformate sie haben, da diese Information in den alten XML-Konfigurationen nicht vorgehalten wurde. +importConfig.migration.importCatalogConfigurationsResultHeader=Import von Katalogkonfigurationen: Ergebnisse +importConfig.migration.startImportDescription=Bitte w\u00E4hlen Sie die Katalogkonfigurationen aus, die Sie importieren wollen und klicken Sie auf "Start"! +importConfig.migration.error.configurationExists=Importkonfiguration existiert bereits +importConfig.migration.error.invalidPort=Port ''{0}'' ist kein ganzzahliger Wert zwischen 0 und 65535 +importConfig.migration.error.mappingFilesMissing=XML-Pflichtelement "mappingFiles" konnte nicht gefunden werden +importConfig.migration.error.mappingFileMissing=Keine Abbildungsdatei gefunden f\u00fcr XSLT-Datei "{0}"! +importConfig.migration.error.mandatoryParameterMissing=XML-Pflichtelement "{0}" konnte nicht gefunden werden importConfig.mode.create=Neue Importkonfiguration erstellen importConfig.mode.edit=Importkonfiguration bearbeiten importConfig.mode.view=Importkonfiguration anzeigen @@ -662,12 +674,11 @@ mappingFile.mode.edit=Abbildungsdatei bearbeiten mappingFile.mode.view=Abbildungsdatei anzeigen mappingFile.new=Neue Abbildungsdatei mappingFile.outputFormat=Metadatenausgabeformat +mappingFile.selectFormats=Bitte w\u00E4hlen Sie Ein- und Ausgabemetadatenformate f\u00FCr diese Abbildungsdatei massDownload=Massendownload massImport=Massenimport massImport.addRow=Zeile hinzuf\u00FCgen -massImport.failedImports=Fehlgeschlagene Importe massImport.results=Massenimport - Ergebnisse -massImport.successfulImports=Erfolgreiche Importe masterpieceProperties=Werkst\u00FCckeigenschaft mediaUploaded={0} wurde erfolgreich hochgeladen messageAdd=Nachricht hinzuf\u00FCgen @@ -896,6 +907,7 @@ createProcessForm.titleRecordLinkTab.searchButtonClick.noHits=Die Suche ist abge quarter=Quartal quarters=Quartale ready=Fertig +recordId=Datensatz-ID recordImport=Datensatz-Import records=Datens\u00E4tze regenerateAllImages=Das neu Generieren aller Bilder starten @@ -954,7 +966,7 @@ selectCatalog=OPAC ausw\u00E4hlen selectSearchField=Suchfeld ausw\u00E4hlen selectFilter=Vordefinierter Filter selectFromExistingProcesses=Ausw\u00E4hlen aus vorhandenen Vorg\u00E4ngen -selectInsertionPosition=Position zum Einf\u00FCgen w\u00E4hlen f\u00FCr +selectInsertionPosition=Position zum Einf\u00FCgen w\u00E4hlen f\u00FCr selectProcess=Vorgang ausw\u00E4hlen selectProject=Bitte Projekt ausw\u00E4hlen selectTemplate=Bitte Produktionsvorlage ausw\u00E4hlen diff --git a/Kitodo/src/main/resources/messages/messages_en.properties b/Kitodo/src/main/resources/messages/messages_en.properties index 2daf46a6e73..4e16721eb6e 100644 --- a/Kitodo/src/main/resources/messages/messages_en.properties +++ b/Kitodo/src/main/resources/messages/messages_en.properties @@ -541,14 +541,25 @@ imageGenerationNotPossible=The generation of images is not possible due to missi images=Images imagesRead=Read images imagesWrite=Write images +import.failedImports=Failed imports +import.successfulImports=Successful imports importChildren=Import child processes importConfig.addSearchField=Add search field -importConfig.assignedAsDefaultConfig=Import configuration ''{0}'' is configured as default configuration of project ''{1}''! +importConfig.assignedAsDefaultConfig=Import configuration "{0}" is configured as default configuration of project "{1}"! importConfig.catalogIdMetadata=Metadata for catalog ID importConfig.configuration=Import configuration importConfig.configurations=Import configurations importConfig.defaultConfiguration=Default configuration importConfig.editSearchField=Edit search field +importConfig.migration.importCatalogConfigurations=Import catalogue configurations +importConfig.migration.importCatalogConfigurationsDesc=Import catalogue configurations +importConfig.migration.importCatalogConfigurationsResultHeader=Catalog configurations import results +importConfig.migration.numberOfCatalogConfigurations=catalog configurations found +importConfig.migration.startImportDescription=Please select the catalog configurations you want to import and click "Start" to begin the import process +importConfig.migration.error.configurationExists=Import configuration already exists +importConfig.migration.error.invalidPort=Port ''{0}'' is not an integer between 0 und 65535 +importConfig.migration.error.mappingFilesMissing=Missing mandatory XML element "mappingFiles" +importConfig.migration.error.mappingFileMissing=No MappingFile found for XSLT file "{0}"! importConfig.mode.create=Create new import configuration importConfig.mode.edit=Edit import configuration importConfig.mode.view=View import configuration @@ -672,12 +683,11 @@ mappingFile.mode.edit=Edit mapping file mappingFile.mode.view=View mapping file mappingFile.new=New Mapping file mappingFile.outputFormat=Metadata output format +mappingFile.selectFormats=Please select input and output file formats for mapping file massDownload=Bulk downloads massImport=Mass import massImport.addRow=Add row -massImport.failedImports=Failed imports massImport.results=Mass import - results -massImport.successfulImports=Successful imports masterpieceProperties=workpiece property mediaUploaded={0} is uploaded successfully messageAdd=Add message @@ -908,6 +918,7 @@ createProcessForm.titleRecordLinkTab.searchButtonClick.noHits=The search complet quarter=quarter quarters=quarters ready=Ready +recordId=Record ID recordImport=record import records=records regenerateAllImages=Start the regeneration of all images diff --git a/Kitodo/src/main/resources/messages/messages_es.properties b/Kitodo/src/main/resources/messages/messages_es.properties index 054aa3cdf37..bc571dff80d 100644 --- a/Kitodo/src/main/resources/messages/messages_es.properties +++ b/Kitodo/src/main/resources/messages/messages_es.properties @@ -529,8 +529,10 @@ imageGenerationNotPossible=La generación de imágenes no es posible debido a qu images=Imágenes imagesRead=Leer imágenes imagesWrite=Escribir imágenes +import.failedImports=Importaciones fallidas +import.successfulImports=Importaciones exitosas importConfig.addSearchField=Añadir campo de búsqueda -importConfig.assignedAsDefaultConfig=La configuración de importación ''{0}'' está configurada como configuración por defecto del proyecto ''{1}''. +importConfig.assignedAsDefaultConfig=La configuración de importación "{0}" está configurada como configuración por defecto del proyecto "{1}". importConfig.catalogIdMetadata=Metadatos para la ID del catálogo importConfig.configuration=Importar configuración importConfig.configurations=Configuraciones de importación @@ -665,9 +667,7 @@ mappingFile.outputFormat=Formato de salida de metadatos massDownload=Descarga masiva massImport=Importación masiva massImport.addRow=Añadir línea -massImport.failedImports=Importaciones fallidas massImport.results=Importación masiva - resultados -massImport.successfulImports=Importaciones exitosas masterpieceProperties=Propiedad de la pieza de trabajo mediaUploaded={0} se ha cargado con éxito messageAdd=Añadir mensaje diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css index f33577f3311..7abf04f8682 100644 --- a/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css +++ b/Kitodo/src/main/webapp/WEB-INF/resources/css/kitodo.css @@ -642,12 +642,52 @@ section#content-section { opacity: 1; } -#editForm table.ui-selectmanycheckbox tbody { +#editForm table.ui-selectmanycheckbox tbody, +#importCatalogConfigurationsForm table.ui-selectmanycheckbox tbody { display: inline-block; overflow-y: scroll; width: 100%; } +#catalogConfigurationImportResultDialog_content { + overflow-y: hidden; +} + +#catalogConfigurationImportResultDialog_content h3 { + margin-bottom: 0; +} + +#importCatalogConfigurationsForm tbody { + height: 200px; +} + +#importResultsForm\:successfulImports, +#importResultsForm\:failedImports { + border-radius: var(--default-border-radius); + margin: var(--default-full-size) 0; +} + +#importResultsForm\:successfulImports { + border: 3px solid var(--green); +} + +#importResultsForm\:failedImports { + border: 3px solid var(--orange); +} + +#importResultsForm\:successfulImports th { + background-color: var(--light-green); +} + +#importResultsForm\:failedImports th { + background-color: var(--light-orange); +} + +.ui-dialog #importResultsForm .ui-datatable .ui-datatable-tablewrapper th, +.ui-dialog #importResultsForm .ui-datatable .ui-datatable-tablewrapper td { + padding-left: var(--default-half-size); +} + #editForm\:templateTabView\:project tbody { height: 90px; } @@ -3403,7 +3443,8 @@ footer { padding-right: var(--default-half-size); } -.ui-panelgrid-cell > div { +.ui-panelgrid-cell > div, +#mappingFileFormatsForm .input { margin-bottom: 1.3em; } diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/opacSearchFields.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/opacSearchFields.xhtml index 7c611b16dea..ff3635df38d 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/opacSearchFields.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/opacSearchFields.xhtml @@ -50,6 +50,18 @@ +
+ + + + + + +
@@ -88,18 +100,6 @@
-
- - - - - - -
@@ -144,9 +144,13 @@ diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/formatRow.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/formatRow.xhtml index 1f20986d6c3..c473e0923e7 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/formatRow.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/formatRow.xhtml @@ -36,6 +36,7 @@ value="#{msgs['tooltip.importConfig.returnFormatHelp']}"/> diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/interfaceUrlParameters.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/interfaceUrlParameters.xhtml index 4e3bbe63254..e4ebb12dcfb 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/interfaceUrlParameters.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/importConfigurationEdit/rows/interfaceUrlParameters.xhtml @@ -64,7 +64,7 @@
+ value="#{msgs['importConfig.field.urlparameter.oaimetadataprefix']} *"/>
- + diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/massImport/dialogs/massImportResults.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/massImport/dialogs/massImportResults.xhtml index dd5587c02d7..ba29933e928 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/massImport/dialogs/massImportResults.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/massImport/dialogs/massImportResults.xhtml @@ -29,7 +29,7 @@ + title="#{msgs['import.successfulImports']}: #{MassImportForm.successfulImports.size()}"> @@ -39,7 +39,7 @@ + title="#{msgs['import.failedImports']}: #{MassImportForm.failedImports.size()}"> diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/fileUpload.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/fileUpload.xhtml index df0adcb18fe..948d1483f10 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/fileUpload.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/processFromTemplate/dialogs/fileUpload.xhtml @@ -31,7 +31,7 @@
+ value="#{msgs['importConfig.configuration']}"/>
+ value="#{msgs['importConfig.configuration']}"/> -
- - -
+
+
+ +
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsPopup.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsPopup.xhtml new file mode 100644 index 00000000000..378157c6b23 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsPopup.xhtml @@ -0,0 +1,56 @@ + + + + +

#{importCatalogConfigurationsView.numberOfCatalogs} #{msgs['importConfig.migration.numberOfCatalogConfigurations']}

+ + #{msgs['importConfig.migration.startImportDescription']} + + + + + + + + + + +
+
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsResult.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsResult.xhtml new file mode 100644 index 00000000000..3a97d362d44 --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importCatalogConfigurationsResult.xhtml @@ -0,0 +1,72 @@ + + + + + +

#{msgs['importConfig.migration.importCatalogConfigurationsResultHeader']}

+ + + + + + + + + + + + + + + + + + + + +
+
diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importConfigurations.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importConfigurations.xhtml index f95e257f8ba..942a7947a02 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importConfigurations.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/importConfigurations.xhtml @@ -47,11 +47,6 @@ title="#{item.interfaceType}"/> - - - - diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/selectMappingFileFormats.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/selectMappingFileFormats.xhtml new file mode 100644 index 00000000000..b186aac61ee --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/projects/selectMappingFileFormats.xhtml @@ -0,0 +1,91 @@ + + + + +

#{msgs['mappingFile.selectFormats']}:

#{importCatalogConfigurationsView.getCurrentMappingFile().file}

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/Kitodo/src/main/webapp/pages/projects.xhtml b/Kitodo/src/main/webapp/pages/projects.xhtml index e6b873a3254..9583164cd8c 100644 --- a/Kitodo/src/main/webapp/pages/projects.xhtml +++ b/Kitodo/src/main/webapp/pages/projects.xhtml @@ -32,6 +32,16 @@

#{msgs.projects}

+ + + + + - + @@ -114,4 +126,10 @@
+ + + + + + diff --git a/Kitodo/src/test/java/org/kitodo/MockDatabase.java b/Kitodo/src/test/java/org/kitodo/MockDatabase.java index 4743bb17cb4..604bb46ae6b 100644 --- a/Kitodo/src/test/java/org/kitodo/MockDatabase.java +++ b/Kitodo/src/test/java/org/kitodo/MockDatabase.java @@ -181,8 +181,6 @@ public static void insertProcessesFull() throws Exception { insertUserFilters(); insertTasks(); insertWorkflows(); - insertMappingFiles(); - insertImportConfigurations(); insertRemovableObjects(); } @@ -374,6 +372,20 @@ public static void insertAuthorities() throws DAOException { authorities.add(new Authority("deleteRuleset" + CLIENT_ASSIGNABLE)); authorities.add(new Authority("addRuleset" + CLIENT_ASSIGNABLE)); + // ImportConfigurations + authorities.add(new Authority("addImportConfiguration" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("editImportConfiguration" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("viewImportConfiguration" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("viewAllImportConfigurations" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("deleteImportConfiguration" + CLIENT_ASSIGNABLE)); + + // MappingFiles + authorities.add(new Authority("addMappingFile" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("editMappingFile" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("viewMappingFile" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("viewAllMappingFiles" + CLIENT_ASSIGNABLE)); + authorities.add(new Authority("deleteMappingFile" + CLIENT_ASSIGNABLE)); + // Process authorities.add(new Authority("viewProcess" + CLIENT_ASSIGNABLE)); authorities.add(new Authority("viewAllProcesses" + CLIENT_ASSIGNABLE)); diff --git a/Kitodo/src/test/java/org/kitodo/production/services/catalogimport/CatalogImportIT.java b/Kitodo/src/test/java/org/kitodo/production/services/catalogimport/CatalogImportIT.java index 9e1264cd271..ece806917dd 100644 --- a/Kitodo/src/test/java/org/kitodo/production/services/catalogimport/CatalogImportIT.java +++ b/Kitodo/src/test/java/org/kitodo/production/services/catalogimport/CatalogImportIT.java @@ -56,6 +56,8 @@ public static void setup() throws Exception { setupServer(); MockDatabase.startNode(); MockDatabase.insertProcessesFull(); + MockDatabase.insertMappingFiles(); + MockDatabase.insertImportConfigurations(); MockDatabase.setUpAwaitility(); SecurityTestUtils.addUserDataToSecurityContext(ServiceManager.getUserService().getById(1), 1); } diff --git a/Kitodo/src/test/java/org/kitodo/production/services/data/AuthorityServiceIT.java b/Kitodo/src/test/java/org/kitodo/production/services/data/AuthorityServiceIT.java index 67d989655af..d10fc12bc6b 100644 --- a/Kitodo/src/test/java/org/kitodo/production/services/data/AuthorityServiceIT.java +++ b/Kitodo/src/test/java/org/kitodo/production/services/data/AuthorityServiceIT.java @@ -28,7 +28,7 @@ public class AuthorityServiceIT { private static final AuthorityService authorityService = ServiceManager.getAuthorityService(); - private static final int EXPECTED_AUTHORITIES_COUNT = 87; + private static final int EXPECTED_AUTHORITIES_COUNT = 97; @BeforeClass public static void prepareDatabase() throws Exception { @@ -82,6 +82,6 @@ public void shouldNotSaveAlreadyExistingAuthorities() throws Exception { @Test public void shouldGetAllClientAssignableAuthorities() throws DAOException { List authorities = authorityService.getAllAssignableToClients(); - assertEquals("Client assignable authorities were not found database!", 61, authorities.size()); + assertEquals("Client assignable authorities were not found database!", 71, authorities.size()); } } diff --git a/Kitodo/src/test/java/org/kitodo/production/services/data/ImportServiceIT.java b/Kitodo/src/test/java/org/kitodo/production/services/data/ImportServiceIT.java index bc8dba83cbe..08d194cc0af 100644 --- a/Kitodo/src/test/java/org/kitodo/production/services/data/ImportServiceIT.java +++ b/Kitodo/src/test/java/org/kitodo/production/services/data/ImportServiceIT.java @@ -56,6 +56,8 @@ public class ImportServiceIT { @BeforeClass public static void prepareDatabase() throws Exception { MockDatabase.startNode(); + MockDatabase.insertMappingFiles(); + MockDatabase.insertImportConfigurations(); MockDatabase.insertProcessesFull(); MockDatabase.insertProcessesForHierarchyTests(); MockDatabase.setUpAwaitility(); diff --git a/Kitodo/src/test/java/org/kitodo/production/services/security/SecurityAccessServiceIT.java b/Kitodo/src/test/java/org/kitodo/production/services/security/SecurityAccessServiceIT.java index b6a910e768a..f3f4a4a3621 100644 --- a/Kitodo/src/test/java/org/kitodo/production/services/security/SecurityAccessServiceIT.java +++ b/Kitodo/src/test/java/org/kitodo/production/services/security/SecurityAccessServiceIT.java @@ -51,7 +51,7 @@ public void shouldGetAuthorities() throws DAOException { SecurityTestUtils.addUserDataToSecurityContext(user, 1); Collection authorities = SecurityContextHolder.getContext().getAuthentication() .getAuthorities(); - Assert.assertEquals("Security context holder does not hold the corresponding authorities", 148, + Assert.assertEquals("Security context holder does not hold the corresponding authorities", 168, authorities.size()); } diff --git a/Kitodo/src/test/java/org/kitodo/selenium/AddingST.java b/Kitodo/src/test/java/org/kitodo/selenium/AddingST.java index 6af80ae2943..edc7e1bb521 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/AddingST.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/AddingST.java @@ -27,6 +27,7 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; +import org.kitodo.MockDatabase; import org.kitodo.data.database.beans.Client; import org.kitodo.data.database.beans.Docket; import org.kitodo.data.database.beans.LdapGroup; @@ -66,6 +67,8 @@ public static void setup() throws Exception { usersPage = Pages.getUsersPage(); userEditPage = Pages.getUserEditPage(); roleEditPage = Pages.getRoleEditPage(); + MockDatabase.insertMappingFiles(); + MockDatabase.insertImportConfigurations(); } @Before diff --git a/Kitodo/src/test/java/org/kitodo/selenium/ConfigConversionST.java b/Kitodo/src/test/java/org/kitodo/selenium/ConfigConversionST.java new file mode 100644 index 00000000000..fe4afb3620a --- /dev/null +++ b/Kitodo/src/test/java/org/kitodo/selenium/ConfigConversionST.java @@ -0,0 +1,138 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * 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.selenium; + +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.kitodo.data.database.beans.ImportConfiguration; +import org.kitodo.data.database.exceptions.DAOException; +import org.kitodo.production.services.ServiceManager; +import org.kitodo.selenium.testframework.BaseTestSelenium; +import org.kitodo.selenium.testframework.Browser; +import org.kitodo.selenium.testframework.Pages; +import org.kitodo.selenium.testframework.pages.ProjectsPage; + +public class ConfigConversionST extends BaseTestSelenium { + + private static final String MODS_2_KITODO = "mods2kitodo.xsl"; + private static final String KALLIOPE = "Kalliope"; + private static final String GBV = "GBV"; + private static final String K10PLUS = "K10Plus"; + private static final String EXPECTED_ERROR = "XML-Pflichtelement \"mappingFiles\" konnte nicht gefunden werden"; + + @Before + public void login() throws Exception { + Pages.getLoginPage().goTo().performLoginAsAdmin(); + } + + @After + public void logout() throws Exception { + Pages.getTopNavigation().logout(); + } + + /** + * Test import of catalog configurations. + * @throws Exception when thread is interrupted. + */ + @Test + public void shouldImportCatalogConfigurations() throws Exception { + ProjectsPage importConfigurationsTab = Pages.getProjectsPage().goToImportConfigurationsTab(); + + importConfigurationsTab.openOpacConfigurationImportDialog(); + importConfigurationsTab.startOpacConfigurationImport(); + + assertEquals(MODS_2_KITODO, importConfigurationsTab.getMappingFileTitle()); + importConfigurationsTab.selectInputFormatMods(); + importConfigurationsTab.selectOutputFormatKitodo(); + importConfigurationsTab.clickMappingFileOkButton(); + + await("Wait for 'Results' dialog to be displayed") + .atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(Browser.getDriver() + .findElementById("importResultsForm:successfulImports").isDisplayed() && Browser.getDriver() + .findElementById("importResultsForm:failedImports").isDisplayed())); + + // assert successful and failed opac config imports + assertTrue(importConfigurationsTab.allCatalogsImportedSuccessfully(Arrays.asList(GBV, KALLIOPE))); + assertTrue(importConfigurationsTab.allCatalogsFailedToImport(Collections.singletonList(K10PLUS))); + // assert error message of K10+ import! + assertTrue("List of error messages should contain '" + EXPECTED_ERROR + "'", + importConfigurationsTab.getCatalogConfigurationImportErrorsMessages().contains(EXPECTED_ERROR)); + + importConfigurationsTab.closeResultsDialog(); + + // assert number of ImportConfigurations and MappingFiles in database + assertEquals(Long.valueOf(2), ServiceManager.getImportConfigurationService().countDatabaseRows()); + assertEquals(Long.valueOf(1), ServiceManager.getMappingFileService().countDatabaseRows()); + + // assert that lists have been updated properly (counts include table header row, therefore each +1!) + assertEquals(Long.valueOf(3), importConfigurationsTab.getNumberOfImportConfigurations()); + ProjectsPage mappingFilesTab = importConfigurationsTab.goToMappingFilesTab(); + assertEquals(Long.valueOf(2), mappingFilesTab.getNumberOfMappingFiles()); + + checkGbvConfiguration(); + checkKalliopeConfiguration(); + } + + private void checkGbvConfiguration() throws DAOException { + ImportConfiguration gbvConfiguration = ServiceManager.getImportConfigurationService().getById(1); + assertEquals("GBV", gbvConfiguration.getTitle()); + assertEquals("SRU", gbvConfiguration.getInterfaceType()); + assertEquals("MODS", gbvConfiguration.getMetadataFormat()); + + // check GBV url + assertEquals("http", gbvConfiguration.getScheme()); + assertEquals("sru.gbv.de", gbvConfiguration.getHost()); + assertEquals("/gvk", gbvConfiguration.getPath()); + + // search fields + assertEquals(8, gbvConfiguration.getSearchFields().size()); + assertEquals("PPN", gbvConfiguration.getDefaultSearchField().getLabel()); + + // mapping files + assertEquals(1, gbvConfiguration.getMappingFiles().size()); + assertEquals(MODS_2_KITODO, gbvConfiguration.getMappingFiles().get(0).getFile()); + } + + private void checkKalliopeConfiguration() throws DAOException { + ImportConfiguration kalliopeConfiguration = ServiceManager.getImportConfigurationService().getById(2); + assertEquals("Kalliope", kalliopeConfiguration.getTitle()); + assertEquals("SRU", kalliopeConfiguration.getInterfaceType()); + assertEquals("MODS", kalliopeConfiguration.getMetadataFormat()); + + // check Kalliope url + assertEquals("http", kalliopeConfiguration.getScheme()); + assertEquals("localhost", kalliopeConfiguration.getHost()); + assertEquals("8888", String.valueOf(kalliopeConfiguration.getPort())); + assertEquals("/sru", kalliopeConfiguration.getPath()); + // sru parameter + assertEquals("1.2", kalliopeConfiguration.getSruVersion()); + assertEquals("mods", kalliopeConfiguration.getSruRecordSchema()); + + // search fields + assertEquals(6, kalliopeConfiguration.getSearchFields().size()); + assertNull(kalliopeConfiguration.getDefaultSearchField()); + + // mapping files + assertEquals(1, kalliopeConfiguration.getMappingFiles().size()); + assertEquals(MODS_2_KITODO, kalliopeConfiguration.getMappingFiles().get(0).getFile()); + } +} diff --git a/Kitodo/src/test/java/org/kitodo/selenium/ImportingST.java b/Kitodo/src/test/java/org/kitodo/selenium/ImportingST.java index d0baf77b357..a8a320f9d9b 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/ImportingST.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/ImportingST.java @@ -17,6 +17,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.kitodo.MockDatabase; import org.kitodo.selenium.testframework.BaseTestSelenium; import org.kitodo.selenium.testframework.Browser; import org.kitodo.selenium.testframework.Pages; @@ -35,6 +36,8 @@ public class ImportingST extends BaseTestSelenium { public static void setup() throws Exception { projectsPage = Pages.getProjectsPage(); importPage = Pages.getProcessFromTemplatePage(); + MockDatabase.insertMappingFiles(); + MockDatabase.insertImportConfigurations(); } @Before diff --git a/Kitodo/src/test/java/org/kitodo/selenium/testframework/enums/TabIndex.java b/Kitodo/src/test/java/org/kitodo/selenium/testframework/enums/TabIndex.java index 817c7029296..924006dd5ac 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/testframework/enums/TabIndex.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/testframework/enums/TabIndex.java @@ -34,6 +34,8 @@ public enum TabIndex { WORKFLOWS(2), DOCKETS(3), RULESETS(4), + IMPORT_CONFIGURATIONS(5), + MAPPING_FILES(6), // template edit page TEMPLATE_DETAILS(0), diff --git a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/Page.java b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/Page.java index f9c8d14558f..7baada38b16 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/Page.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/Page.java @@ -35,6 +35,7 @@ public abstract class Page { private static final Logger logger = LogManager.getLogger(Page.class); static final String DATA = "_data"; + static final String CSS_SELECTOR_DROPDOWN_TRIGGER = ".ui-selectonemenu-trigger"; @SuppressWarnings("unused") @FindBy(id = "user-menu") diff --git a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProcessFromTemplatePage.java b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProcessFromTemplatePage.java index a0d9d376e36..d2977cc097c 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProcessFromTemplatePage.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProcessFromTemplatePage.java @@ -26,7 +26,6 @@ public class ProcessFromTemplatePage extends EditPage { private static final String TAB_VIEW = EDIT_FORM + ":processFromTemplateTabView"; private static final String OPAC_SEARCH_FORM = "catalogSearchForm"; - private static final String CSS_SELECTOR_DROPDOWN_TRIGGER = ".ui-selectonemenu-trigger"; @SuppressWarnings("unused") @FindBy(id = TAB_VIEW) diff --git a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProjectsPage.java b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProjectsPage.java index ef3a99911d1..c448b610d33 100644 --- a/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProjectsPage.java +++ b/Kitodo/src/test/java/org/kitodo/selenium/testframework/pages/ProjectsPage.java @@ -12,12 +12,14 @@ package org.kitodo.selenium.testframework.pages; import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertTrue; import static org.kitodo.selenium.testframework.Browser.getCellsOfRow; import static org.kitodo.selenium.testframework.Browser.getRowsOfTable; import static org.kitodo.selenium.testframework.Browser.getTableDataByColumn; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -38,6 +40,9 @@ public class ProjectsPage extends Page { private static final String WORKFLOW_TABLE = "workflowTable"; private static final String DOCKET_TABLE = "docketTable"; private static final String RULESET_TABLE = "rulesetTable"; + private static final String IMPORT_CONFIGURATIONS_TABLE = "configurationTable"; + private static final String MAPPING_FILE_TABLE = "mappingTable"; + private static final String MAPPING_FILE_FORMAT_DIALOG = "mappingFileFormatsDialog"; @SuppressWarnings("unused") @FindBy(id = PROJECTS_TAB_VIEW) @@ -63,6 +68,18 @@ public class ProjectsPage extends Page { @FindBy(id = RULESET_TABLE + DATA) private WebElement rulesetsTable; + @SuppressWarnings("unused") + @FindBy(id = IMPORT_CONFIGURATIONS_TABLE + DATA) + private WebElement importConfigurationsTable; + + @SuppressWarnings("unused") + @FindBy(id = MAPPING_FILE_TABLE + DATA) + private WebElement mappingFilesTable; + + @SuppressWarnings("unused") + @FindBy(id = MAPPING_FILE_FORMAT_DIALOG) + private WebElement mappingFileFormatDialog; + @SuppressWarnings("unused") @FindBy(id = "projectForm:newElementButton_button") private WebElement newElementButton; @@ -87,6 +104,34 @@ public class ProjectsPage extends Page { @FindBy(id = "projectForm:newRulesetButton") private WebElement newRulesetButton; + @SuppressWarnings("unused") + @FindBy(id = "convertMenu:convertCatalogConfigurations") + private WebElement importOpacConfigsButton; + + @SuppressWarnings("unused") + @FindBy(id = "importCatalogConfigurationsForm:catalogConfigurationSelection") + private WebElement catalogSelection; + + @SuppressWarnings("unused") + @FindBy(id = "importCatalogConfigurationsForm:startCatalogConfigurationsImport") + private WebElement startOpacConfigurationImportButton; + + @SuppressWarnings("unused") + @FindBy(id = "mappingFileFormatsForm:mappingFileTitle") + private WebElement mappingFileTitle; + + @SuppressWarnings("unused") + @FindBy(id = "mappingFileFormatsForm:inputFormat") + private WebElement mappingFileInputFormatMenu; + + @SuppressWarnings("unused") + @FindBy(id = "mappingFileFormatsForm:outputFormat") + private WebElement mappingFileOutputFormatMenu; + + @SuppressWarnings("unused") + @FindBy(id = "mappingFileFormatsForm:ok") + private WebElement mappingFileOkButton; + @SuppressWarnings("unused") @FindBy(xpath = "//a[@href='/kitodo/pages/projectEdit.jsf?referer=projects&id=1']") private WebElement editProjectLink; @@ -107,6 +152,10 @@ public class ProjectsPage extends Page { @FindBy(xpath = "//a[@href='/kitodo/pages/rulesetEdit.jsf?id=1']") private WebElement editRulesetLink; + @SuppressWarnings("unused") + @FindBy(xpath = "//a[@href='/kitodo/pages/importConfigurationEdit.jsf?id=1']") + private WebElement editImportConfigurationLink; + @SuppressWarnings("unused") @FindBy(id = TEMPLATE_TABLE + ":0:templateActionForm:action22") private WebElement createProcess; @@ -401,7 +450,7 @@ public RulesetEditPage editRuleset() throws Exception { */ public void deleteDocket() throws Exception { deleteElement("Docket", MockDatabase.getRemovableObjectIDs().get(ObjectType.DOCKET.name()), - TabIndex.DOCKETS.getIndex(), projectsTabView); + TabIndex.DOCKETS.getIndex(), projectsTabView); } /** @@ -409,7 +458,7 @@ public void deleteDocket() throws Exception { */ public void deleteRuleset() throws Exception { deleteElement("Ruleset", MockDatabase.getRemovableObjectIDs().get(ObjectType.RULESET.name()), - TabIndex.RULESETS.getIndex(), projectsTabView); + TabIndex.RULESETS.getIndex(), projectsTabView); } /** @@ -430,12 +479,27 @@ public ProjectsPage goToWorkflowTab() throws Exception { } + /** + * Switch to import configurations tab. + */ + public ProjectsPage goToImportConfigurationsTab() throws Exception { + switchToTabByIndex(TabIndex.IMPORT_CONFIGURATIONS.getIndex()); + return this; + } + + /** + * Switch to mapping files tab. + */ + public ProjectsPage goToMappingFilesTab() throws Exception { + switchToTabByIndex(TabIndex.MAPPING_FILES.getIndex()); + return this; + } + /** * Clicks on the tab indicated by given index (starting with 0 for the first * tab). * - * @param index - * of tab to be clicked + * @param index of tab to be clicked */ private void switchToTabByIndex(int index) throws Exception { switchToTabByIndex(index, projectsTabView); @@ -445,4 +509,144 @@ public String getWorkflowStatusForWorkflow() { List tableDataByColumn = getTableDataByColumn(workflowsTable, 1); return tableDataByColumn.get(1); } + + /** + * Display the 'Import opac configuration' dialog. + */ + public void openOpacConfigurationImportDialog() throws Exception { + if (isNotAt()) { + goTo(); + } + clickElement(importOpacConfigsButton); + await("Wait for 'Import catalog configurations' dialog to be displayed") + .atMost(3, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(startOpacConfigurationImportButton + .isDisplayed())); + } + + /** + * Unselect catalogs with given indices. + * @param indices indices of catalogs to unselect + */ + public void unselectCatalogs(List indices) { + for (Integer index : indices) { + clickElement(catalogSelection.findElements(By.className("ui-chkbox")).get(index)); + } + } + + /** + * Start opac configuration import. + */ + public void startOpacConfigurationImport() { + clickElement(startOpacConfigurationImportButton); + } + + /** + * Get mapping file title. + * @return mapping file title + */ + public String getMappingFileTitle() { + await("Wait for 'Mapping file title' field to be displayed") + .atMost(10, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(mappingFileTitle + .isDisplayed())); + return mappingFileTitle.getAttribute("value"); + } + + /** + * Select "Pica" as input format on the "Select input and output formats for mapping file" dialog. + */ + public void selectInputFormatPica() { + clickElement(mappingFileInputFormatMenu.findElement(By.cssSelector(CSS_SELECTOR_DROPDOWN_TRIGGER))); + clickElement(Browser.getDriver().findElementById(mappingFileInputFormatMenu.getAttribute("id") + "_3")); + } + + /** + * Select "Mods" as input format on the "Select input and output formats for mapping file" dialog. + */ + public void selectInputFormatMods() { + clickElement(mappingFileInputFormatMenu.findElement(By.cssSelector(CSS_SELECTOR_DROPDOWN_TRIGGER))); + clickElement(Browser.getDriver().findElementById(mappingFileInputFormatMenu.getAttribute("id") + "_1")); + } + + /** + * Select "Kitodo" as output format on the "Select input and output formats for mapping file" dialog. + */ + public void selectOutputFormatKitodo() { + clickElement(mappingFileOutputFormatMenu.findElement(By.cssSelector(CSS_SELECTOR_DROPDOWN_TRIGGER))); + clickElement(Browser.getDriver().findElementById(mappingFileOutputFormatMenu.getAttribute("id") + "_5")); + } + + /** + * Click the "Ok" button on the "Select input and output formats for mapping file" dialog. + */ + public void clickMappingFileOkButton() { + clickElement(mappingFileOkButton); + } + + /** + * Check and return whether all catalog configurations in the given list are successfully imported or not. + * @param catalogTitles list of catalog configuration titles + * @return whether all given catalog configurations were successfully imported or not + */ + public boolean allCatalogsImportedSuccessfully(List catalogTitles) { + for (String catalog : catalogTitles) { + WebElement catalogCell = Browser.getDriver().findElementById("importResultsForm:successfulImports") + .findElement(By.xpath(".//span[@title='" + catalog + "']")); + if (Objects.isNull(catalogCell)) { + return false; + } + } + return true; + } + + /** + * Check and return whether all catalog configurations in the given list failed to be imported or not. + * @param catalogTitles list of catalog configuration titles. + * @return whether all given catalog configurations failed to be imported or not + */ + public boolean allCatalogsFailedToImport(List catalogTitles) { + for (String catalog : catalogTitles) { + WebElement catalogCell = Browser.getDriver().findElementById("importResultsForm:failedImports") + .findElement(By.xpath(".//span[@title='" + catalog + "']")); + if (Objects.isNull(catalogCell)) { + return false; + } + } + return true; + } + + /** + * Get catalog configuration import error messages. + * @return catalog configuration import error messages + */ + public List getCatalogConfigurationImportErrorsMessages() { + List errorMessages = Browser.getDriver().findElementById("importResultsForm:failedImports") + .findElements(By.xpath(".//td[@class='error-message-column']/span")); + return errorMessages.stream().map(WebElement::getText).collect(Collectors.toList()); + } + + /** + * Close results dialog. + */ + public void closeResultsDialog() { + WebElement closeButton = Browser.getDriver().findElementById("close"); + await("Wait for 'Close' button to be displayed") + .atMost(3, TimeUnit.SECONDS).untilAsserted(() -> assertTrue(closeButton.isDisplayed())); + closeButton.click(); + } + + /** + * Retrieve and return number of ImportConfiguration entries in ImportConfiguration list, including table header. + * @return number of ImportConfiguration entries in ImportConfiguration list + */ + public Long getNumberOfImportConfigurations() { + return (long) Browser.getRowsOfTable(Browser.getDriver().findElementById(IMPORT_CONFIGURATIONS_TABLE)).size(); + } + + /** + * Retrieve and return number of MappingFile entries in MappingFile list, including table header. + * @return number of MappingFile entries in MappingFile list + */ + public Long getNumberOfMappingFiles() { + return (long) Browser.getRowsOfTable(Browser.getDriver().findElementById(MAPPING_FILE_TABLE)).size(); + } }