diff --git a/.github/ISSUE_TEMPLATE/task-for-the-development-fund.md b/.github/ISSUE_TEMPLATE/task-for-the-development-fund.md
index f67eef870..d798baeac 100644
--- a/.github/ISSUE_TEMPLATE/task-for-the-development-fund.md
+++ b/.github/ISSUE_TEMPLATE/task-for-the-development-fund.md
@@ -2,7 +2,7 @@
name: Task for the development fund
about: A working package which may be sponsored by the Kitodo e.V. development fund.
title: "[FUND] "
-labels: ⭐ development fund 2024
+labels: ⭐ development fund 2025
assignees: ''
---
diff --git a/.github/codeql.yml b/.github/codeql.yml
index 56085d7bf..4e2fa3a6d 100644
--- a/.github/codeql.yml
+++ b/.github/codeql.yml
@@ -1,16 +1,16 @@
- name: "CodeQL Configuration"
+name: CodeQL Configuration
- queries:
- - uses: security-and-quality
+queries:
+ - uses: security-and-quality
- query-filters:
- - exclude:
- problem.severity:
- - note
+query-filters:
+ - exclude:
+ problem.severity:
+ - note
- paths-ignore:
- - Resources/Public/JavaScript/jPlayer
- - Resources/Public/JavaScript/jQuery
- - Resources/Public/JavaScript/jQueryUI
- - Resources/Public/JavaScript/OpenLayers
- - Resources/Public/JavaScript/Toastify
+paths-ignore:
+ - Resources/Public/JavaScript/jPlayer
+ - Resources/Public/JavaScript/jQuery
+ - Resources/Public/JavaScript/jQueryUI
+ - Resources/Public/JavaScript/OpenLayers
+ - Resources/Public/JavaScript/Toastify
diff --git a/.github/phpstan.neon b/.github/phpstan.neon
index 4f2fb950c..7307b130f 100644
--- a/.github/phpstan.neon
+++ b/.github/phpstan.neon
@@ -1,31 +1,31 @@
parameters:
- ignoreErrors:
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::countByPid\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findByIsListed\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findByIsSortable\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByFeUserId\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByIndexName\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByLocation\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByPid\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByRecordId\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneBySessionId\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByType\(\)\.#'
- - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByUid\(\)\.#'
- - '#Call to an undefined method Psr\\Http\\Message\\RequestFactoryInterface::request\(\)\.#'
- - '#Call to an undefined method Solarium\\Core\\Query\\DocumentInterface::setField\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getHeight\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getWidth\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getPossibleTextAnnotationContainers\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getTextAnnotations\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\ManifestInterface::getOriginalJsonArray\(\)\.#'
- - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\RangeInterface::getMemberRangesAndRanges\(\)\.#'
- - '#Constant LOG_SEVERITY_ERROR not found\.#'
- - '#Constant LOG_SEVERITY_NOTICE not found\.#'
- - '#Constant LOG_SEVERITY_WARNING not found\.#'
- - '#Constant TYPO3_MODE not found\.#'
- level: 5
- paths:
- - ../Classes/
- excludePaths:
- - ../Classes/Controller/OaiPmhController.php
- treatPhpDocTypesAsCertain: false
+ ignoreErrors:
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::countByPid\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findByIsListed\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findByIsSortable\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByFeUserId\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByIndexName\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByLocation\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByPid\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByRecordId\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneBySessionId\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByType\(\)\.#'
+ - '#Call to an undefined method Kitodo\\Dlf\\Domain\\Repository\\[a-zA-Z]+Repository::findOneByUid\(\)\.#'
+ - '#Call to an undefined method Psr\\Http\\Message\\RequestFactoryInterface::request\(\)\.#'
+ - '#Call to an undefined method Solarium\\Core\\Query\\DocumentInterface::setField\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getHeight\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getWidth\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getPossibleTextAnnotationContainers\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\IiifResourceInterface::getTextAnnotations\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\ManifestInterface::getOriginalJsonArray\(\)\.#'
+ - '#Call to an undefined method Ubl\\Iiif\\Presentation\\Common\\Model\\Resources\\RangeInterface::getMemberRangesAndRanges\(\)\.#'
+ - '#Constant LOG_SEVERITY_ERROR not found\.#'
+ - '#Constant LOG_SEVERITY_NOTICE not found\.#'
+ - '#Constant LOG_SEVERITY_WARNING not found\.#'
+ - '#Constant TYPO3_MODE not found\.#'
+ level: 5
+ paths:
+ - ../Classes/
+ excludePaths:
+ - ../Classes/Controller/OaiPmhController.php
+ treatPhpDocTypesAsCertain: false
diff --git a/.github/pull.yml b/.github/pull.yml
index e00fbe74a..8ef8ba932 100644
--- a/.github/pull.yml
+++ b/.github/pull.yml
@@ -18,3 +18,6 @@ rules:
- base: 4.x
upstream: kitodo:4.x
mergeMethod: hardreset
+ - base: 5.x
+ upstream: kitodo:5.x
+ mergeMethod: hardreset
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index c17f1b765..12969e1e1 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,14 +1,14 @@
-name: "CodeQL"
+name: CodeQL
on:
push:
- branches: [ "master", "1.x", "2.x", "3.2.x", "3.3.x", "4.x" ]
+ branches: [ "master", "1.x", "2.x", "3.2.x", "3.3.x", "4.x", "5.x" ]
pull_request:
branches: [ "master" ]
jobs:
analyze:
- name: Analyze
+ name: Static Code Analysis
runs-on: ubuntu-latest
permissions:
actions: read
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 8180669aa..769245370 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -1,10 +1,10 @@
-name: Build Documentation
+name: Documentation
on: [ push, pull_request ]
jobs:
tests:
- name: Documentation
+ name: Build Test
runs-on: ubuntu-latest
steps:
- name: Checkout
diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
index 35c5266de..e998d5de5 100644
--- a/.github/workflows/phpstan.yml
+++ b/.github/workflows/phpstan.yml
@@ -8,6 +8,7 @@ on:
jobs:
phpstan:
+ name: Static Code Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 49233f11d..52e4a0e4a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -8,6 +8,7 @@ on:
jobs:
test:
+ name: TYPO3
runs-on: ubuntu-latest
strategy:
matrix:
diff --git a/Classes/Command/BaseCommand.php b/Classes/Command/BaseCommand.php
index 9414bbd72..0c8208e3a 100644
--- a/Classes/Command/BaseCommand.php
+++ b/Classes/Command/BaseCommand.php
@@ -22,6 +22,7 @@
use Kitodo\Dlf\Domain\Model\Collection;
use Kitodo\Dlf\Domain\Model\Document;
use Kitodo\Dlf\Domain\Model\Library;
+use Kitodo\Dlf\Validation\DocumentValidator;
use Symfony\Component\Console\Command\Command;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -123,25 +124,16 @@ public function __construct(
*
* @param int $storagePid The storage pid
*
- * @return bool
+ * @return void
*/
- protected function initializeRepositories(int $storagePid): bool
+ protected function initializeRepositories(int $storagePid): void
{
- if (MathUtility::canBeInterpretedAsInteger($storagePid)) {
- $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
-
- $frameworkConfiguration['persistence']['storagePid'] = MathUtility::forceIntegerInRange((int) $storagePid, 0);
- $this->configurationManager->setConfiguration($frameworkConfiguration);
-
- // Get extension configuration.
- $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
- } else {
- return false;
- }
- $this->storagePid = MathUtility::forceIntegerInRange((int) $storagePid, 0);
+ $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
+ $frameworkConfiguration['persistence']['storagePid'] = MathUtility::forceIntegerInRange($storagePid, 0);
+ $this->configurationManager->setConfiguration($frameworkConfiguration);
+ $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
+ $this->storagePid = MathUtility::forceIntegerInRange($storagePid, 0);
$this->persistenceManager = GeneralUtility::makeInstance(PersistenceManager::class);
-
- return true;
}
/**
@@ -216,71 +208,70 @@ protected function saveToDatabase(Document $document): bool
$doc->cPid = $this->storagePid;
$metadata = $doc->getToplevelMetadata($this->storagePid);
+ $validator = new DocumentValidator($metadata, explode(',', $this->extConf['general']['requiredMetadataFields']));
+
+ if ($validator->hasAllMandatoryMetadataFields()) {
+ // set title data
+ $document->setTitle($metadata['title'][0] ? : '');
+ $document->setTitleSorting($metadata['title_sorting'][0] ? : '');
+ $document->setPlace(implode('; ', $metadata['place']));
+ $document->setYear(implode('; ', $metadata['year']));
+ $document->setAuthor($this->getAuthors($metadata['author']));
+ $document->setThumbnail($doc->thumbnail ? : '');
+ $document->setMetsLabel($metadata['mets_label'][0] ? : '');
+ $document->setMetsOrderlabel($metadata['mets_orderlabel'][0] ? : '');
+
+ $structure = $this->structureRepository->findOneByIndexName($metadata['type'][0]);
+ $document->setStructure($structure);
+
+ if (is_array($metadata['collection'])) {
+ $this->addCollections($document, $metadata['collection']);
+ }
- // set title data
- $document->setTitle($metadata['title'][0] ? : '');
- $document->setTitleSorting($metadata['title_sorting'][0] ? : '');
- $document->setPlace(implode('; ', $metadata['place']));
- $document->setYear(implode('; ', $metadata['year']));
-
- // Remove appended "valueURI" from authors' names for storing in database.
- foreach ($metadata['author'] as $i => $author) {
- $splitName = explode(pack('C', 31), $author);
- $metadata['author'][$i] = $splitName[0];
- }
- $document->setAuthor($this->getAuthors($metadata['author']));
- $document->setThumbnail($doc->thumbnail ? : '');
- $document->setMetsLabel($metadata['mets_label'][0] ? : '');
- $document->setMetsOrderlabel($metadata['mets_orderlabel'][0] ? : '');
-
- $structure = $this->structureRepository->findOneByIndexName($metadata['type'][0]);
- $document->setStructure($structure);
+ // set identifiers
+ $document->setProdId($metadata['prod_id'][0] ? : '');
+ $document->setOpacId($metadata['opac_id'][0] ? : '');
+ $document->setUnionId($metadata['union_id'][0] ? : '');
+
+ $document->setRecordId($metadata['record_id'][0]);
+ $document->setUrn($metadata['urn'][0] ? : '');
+ $document->setPurl($metadata['purl'][0] ? : '');
+ $document->setDocumentFormat($metadata['document_format'][0] ? : '');
+
+ // set access
+ $document->setLicense($metadata['license'][0] ? : '');
+ $document->setTerms($metadata['terms'][0] ? : '');
+ $document->setRestrictions($metadata['restrictions'][0] ? : '');
+ $document->setOutOfPrint($metadata['out_of_print'][0] ? : '');
+ $document->setRightsInfo($metadata['rights_info'][0] ? : '');
+ $document->setStatus(0);
+
+ $this->setOwner($metadata['owner'][0]);
+ $document->setOwner($this->owner);
+
+ // set volume data
+ $document->setVolume($metadata['volume'][0] ? : '');
+ $document->setVolumeSorting($metadata['volume_sorting'][0] ? : $metadata['mets_order'][0] ? : '');
+
+ // Get UID of parent document.
+ if ($document->getDocumentFormat() === 'METS') {
+ $document->setPartof($this->getParentDocumentUidForSaving($document));
+ }
- if (is_array($metadata['collection'])) {
- $this->addCollections($document, $metadata['collection']);
- }
+ if ($document->getUid() === null) {
+ // new document
+ $this->documentRepository->add($document);
+ } else {
+ // update of existing document
+ $this->documentRepository->update($document);
+ }
- // set identifiers
- $document->setProdId($metadata['prod_id'][0] ? : '');
- $document->setOpacId($metadata['opac_id'][0] ? : '');
- $document->setUnionId($metadata['union_id'][0] ? : '');
-
- $document->setRecordId($metadata['record_id'][0] ? : ''); // (?) $doc->recordId
- $document->setUrn($metadata['urn'][0] ? : '');
- $document->setPurl($metadata['purl'][0] ? : '');
- $document->setDocumentFormat($metadata['document_format'][0] ? : '');
-
- // set access
- $document->setLicense($metadata['license'][0] ? : '');
- $document->setTerms($metadata['terms'][0] ? : '');
- $document->setRestrictions($metadata['restrictions'][0] ? : '');
- $document->setOutOfPrint($metadata['out_of_print'][0] ? : '');
- $document->setRightsInfo($metadata['rights_info'][0] ? : '');
- $document->setStatus(0);
-
- $this->setOwner($metadata['owner'][0]);
- $document->setOwner($this->owner);
-
- // set volume data
- $document->setVolume($metadata['volume'][0] ? : '');
- $document->setVolumeSorting($metadata['volume_sorting'][0] ? : $metadata['mets_order'][0] ? : '');
-
- // Get UID of parent document.
- if ($document->getDocumentFormat() === 'METS') {
- $document->setPartof($this->getParentDocumentUidForSaving($document));
- }
+ $this->persistenceManager->persistAll();
- if ($document->getUid() === null) {
- // new document
- $this->documentRepository->add($document);
- } else {
- // update of existing document
- $this->documentRepository->update($document);
+ return true;
}
- $this->persistenceManager->persistAll();
-
- return true;
+ return false;
}
/**
@@ -346,7 +337,7 @@ private function addCollections(Document &$document, array $collections): void
$documentCollection = GeneralUtility::makeInstance(Collection::class);
$documentCollection->setIndexName($collection);
$documentCollection->setLabel($collection);
- $documentCollection->setOaiName((!empty($this->extConf['publishNewCollections']) ? Helper::getCleanString($collection) : ''));
+ $documentCollection->setOaiName((!empty($this->extConf['general']['publishNewCollections']) ? Helper::getCleanString($collection) : ''));
$documentCollection->setIndexSearch('');
$documentCollection->setDescription('');
// add to CollectionRepository
@@ -371,6 +362,12 @@ private function addCollections(Document &$document, array $collections): void
*/
private function getAuthors(array $metadataAuthor): string
{
+ // Remove appended "valueURI" from authors' names for storing in database.
+ foreach ($metadataAuthor as $i => $author) {
+ $splitName = explode(pack('C', 31), $author);
+ $metadataAuthor[$i] = $splitName[0];
+ }
+
$authors = '';
$delimiter = '; ';
$ellipsis = 'et al.';
diff --git a/Classes/Command/DeleteCommand.php b/Classes/Command/DeleteCommand.php
index cd084b613..11f77b604 100644
--- a/Classes/Command/DeleteCommand.php
+++ b/Classes/Command/DeleteCommand.php
@@ -81,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io = new SymfonyStyle($input, $output);
$io->title($this->getDescription());
- $this->initializeRepositories($input->getOption('pid'));
+ $this->initializeRepositories((int) $input->getOption('pid'));
if ($this->storagePid == 0) {
$io->error('ERROR: No valid PID (' . $this->storagePid . ') given.');
diff --git a/Classes/Command/HarvestCommand.php b/Classes/Command/HarvestCommand.php
index b3e565538..f4ae394f7 100644
--- a/Classes/Command/HarvestCommand.php
+++ b/Classes/Command/HarvestCommand.php
@@ -108,7 +108,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io = new SymfonyStyle($input, $output);
$io->title($this->getDescription());
- $this->initializeRepositories($input->getOption('pid'));
+ $this->initializeRepositories((int) $input->getOption('pid'));
if ($this->storagePid == 0) {
$io->error('ERROR: No valid PID (' . $this->storagePid . ') given.');
diff --git a/Classes/Command/IndexCommand.php b/Classes/Command/IndexCommand.php
index c8dbd4ce6..6120b0ed8 100644
--- a/Classes/Command/IndexCommand.php
+++ b/Classes/Command/IndexCommand.php
@@ -95,7 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io = new SymfonyStyle($input, $output);
$io->title($this->getDescription());
- $this->initializeRepositories($input->getOption('pid'));
+ $this->initializeRepositories((int) $input->getOption('pid'));
if ($this->storagePid == 0) {
$io->error('ERROR: No valid PID (' . $this->storagePid . ') given.');
@@ -180,20 +180,35 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($dryRun) {
$io->section('DRY RUN: Would index ' . $document->getUid() . ' ("' . $document->getLocation() . '") on PID ' . $this->storagePid . ' and Solr core ' . $solrCoreUid . '.');
+ $io->success('All done!');
+ return BaseCommand::SUCCESS;
} else {
+ $document->setCurrentDocument($doc);
+
if ($io->isVerbose()) {
- $io->section('Indexing ' . $document->getUid() . ' ("' . $document->getLocation() . '") on PID ' . $this->storagePid . ' and Solr core ' . $solrCoreUid . '.');
+ $io->section('Indexing ' . $document->getUid() . ' ("' . $document->getLocation() . '") on PID ' . $this->storagePid . '.');
}
- $document->setCurrentDocument($doc);
- // save to database
- $this->saveToDatabase($document);
- // add to index
- Indexer::add($document, $this->documentRepository);
- }
+ $isSaved = $this->saveToDatabase($document);
- $io->success('All done!');
+ if ($isSaved) {
+ if ($io->isVerbose()) {
+ $io->section('Indexing ' . $document->getUid() . ' ("' . $document->getLocation() . '") on Solr core ' . $solrCoreUid . '.');
+ }
+ $isSaved = Indexer::add($document, $this->documentRepository);
+ } else {
+ $io->error('ERROR: Document with UID "' . $document->getUid() . '" could not be indexed on PID ' . $this->storagePid . ' . There are missing mandatory fields (at least one of those: ' . $this->extConf['requiredMetadataFields'] . ') in this document.');
+ return BaseCommand::FAILURE;
+ }
+
+ if ($isSaved) {
+ $io->success('All done!');
+ return BaseCommand::SUCCESS;
+ }
- return BaseCommand::SUCCESS;
+ $io->error('ERROR: Document with UID "' . $document->getUid() . '" could not be indexed on Solr core ' . $solrCoreUid . ' . There are missing mandatory fields (at least one of those: ' . $this->extConf['requiredMetadataFields'] . ') in this document.');
+ $io->info('INFO: Document with UID "' . $document->getUid() . '" is already in database. If you want to keep the database and index consistent you need to remove it.');
+ return BaseCommand::FAILURE;
+ }
}
/**
diff --git a/Classes/Command/ReindexCommand.php b/Classes/Command/ReindexCommand.php
index 7d8dc45f6..776002849 100644
--- a/Classes/Command/ReindexCommand.php
+++ b/Classes/Command/ReindexCommand.php
@@ -111,7 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io = new SymfonyStyle($input, $output);
$io->title($this->getDescription());
- $this->initializeRepositories($input->getOption('pid'));
+ $this->initializeRepositories((int) $input->getOption('pid'));
if ($this->storagePid == 0) {
$io->error('ERROR: No valid PID (' . $this->storagePid . ') given.');
diff --git a/Classes/Common/AbstractDocument.php b/Classes/Common/AbstractDocument.php
index afa809479..9620c4799 100644
--- a/Classes/Common/AbstractDocument.php
+++ b/Classes/Common/AbstractDocument.php
@@ -562,8 +562,8 @@ public static function &getInstance(string $location, array $settings = [], bool
$contentAsJsonArray = json_decode($content, true);
if ($contentAsJsonArray !== null) {
IiifHelper::setUrlReader(IiifUrlReader::getInstance());
- IiifHelper::setMaxThumbnailHeight($extConf['iiifThumbnailHeight']);
- IiifHelper::setMaxThumbnailWidth($extConf['iiifThumbnailWidth']);
+ IiifHelper::setMaxThumbnailHeight($extConf['iiif']['thumbnailHeight']);
+ IiifHelper::setMaxThumbnailWidth($extConf['iiif']['thumbnailWidth']);
$iiif = IiifHelper::loadIiifResource($contentAsJsonArray);
if ($iiif instanceof IiifResourceInterface) {
$documentFormat = 'IIIF';
@@ -653,7 +653,7 @@ protected function getFullTextFromXml(string $id): string
// ... physical structure ...
$this->magicGetPhysicalStructure();
// ... and extension configuration.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
$fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']);
$textFormat = "";
if (!empty($this->physicalStructureInfo[$id])) {
diff --git a/Classes/Common/Helper.php b/Classes/Common/Helper.php
index 948b94480..06150fea6 100644
--- a/Classes/Common/Helper.php
+++ b/Classes/Common/Helper.php
@@ -976,14 +976,14 @@ public static function getUrl(string $url)
}
// Get extension configuration.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf', 'general');
/** @var RequestFactory $requestFactory */
$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
$configuration = [
'timeout' => 30,
'headers' => [
- 'User-Agent' => $extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
+ 'User-Agent' => $extConf['userAgent'] ?? 'Kitodo.Presentation Proxy',
],
];
try {
diff --git a/Classes/Common/IiifManifest.php b/Classes/Common/IiifManifest.php
index 3400f74cf..7dfbf8565 100644
--- a/Classes/Common/IiifManifest.php
+++ b/Classes/Common/IiifManifest.php
@@ -233,7 +233,7 @@ protected function getUseGroups(string $use)
{
if (!$this->useGrpsLoaded) {
// Get configured USE attributes.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
if (!empty($extConf['fileGrpImages'])) {
$this->useGrps['fileGrpImages'] = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
}
@@ -264,7 +264,7 @@ protected function magicGetPhysicalStructure(): array
if ($this->iiif == null || !($this->iiif instanceof ManifestInterface)) {
return [];
}
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif');
$iiifId = $this->iiif->getId();
$this->physicalStructureInfo[$iiifId]['id'] = $iiifId;
$this->physicalStructureInfo[$iiifId]['dmdId'] = $iiifId;
@@ -754,7 +754,7 @@ public function getFullText(string $id): string
$this->magicGetPhysicalStructure();
// ... and extension configuration.
$extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
- $fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']);
+ $fileGrpsFulltext = GeneralUtility::trimExplode(',', $extConf['files']['fileGrpFulltext']);
if (!empty($this->physicalStructureInfo[$id])) {
while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) {
if (!empty($this->physicalStructureInfo[$id]['files'][$fileGrpFulltext])) {
@@ -762,7 +762,7 @@ public function getFullText(string $id): string
break;
}
}
- if ($extConf['indexAnnotations'] == 1) {
+ if ($extConf['iiif']['indexAnnotations'] == 1) {
$iiifResource = $this->iiif->getContainedResourceById($id);
// Get annotation containers
$annotationContainerIds = $this->physicalStructureInfo[$id]['annotationContainers'];
@@ -807,10 +807,10 @@ protected function loadLocation(string $location): bool
{
$fileResource = GeneralUtility::getUrl($location);
if ($fileResource !== false) {
- $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif');
IiifHelper::setUrlReader(IiifUrlReader::getInstance());
- IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
- IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
+ IiifHelper::setMaxThumbnailHeight($conf['thumbnailHeight']);
+ IiifHelper::setMaxThumbnailWidth($conf['thumbnailWidth']);
$resource = IiifHelper::loadIiifResource($fileResource);
if ($resource instanceof ManifestInterface) {
$this->iiif = $resource;
@@ -865,7 +865,7 @@ protected function ensureHasFulltextIsSet(): void
$this->hasFulltext = true;
return;
}
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif');
if ($extConf['indexAnnotations'] == 1 && !empty($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING))) {
foreach ($canvas->getPossibleTextAnnotationContainers(Motivation::PAINTING) as $annotationContainer) {
$textAnnotations = $annotationContainer->getTextAnnotations(Motivation::PAINTING);
@@ -998,10 +998,10 @@ private function setFileUseFulltext(string $iiifId, $iiif): void
*/
public function __wakeup(): void
{
- $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif');
IiifHelper::setUrlReader(IiifUrlReader::getInstance());
- IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
- IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
+ IiifHelper::setMaxThumbnailHeight($conf['thumbnailHeight']);
+ IiifHelper::setMaxThumbnailWidth($conf['thumbnailWidth']);
$resource = IiifHelper::loadIiifResource($this->asJson);
if ($resource instanceof ManifestInterface) {
$this->asJson = '';
diff --git a/Classes/Common/Indexer.php b/Classes/Common/Indexer.php
index c48ad9415..dc6b194cd 100644
--- a/Classes/Common/Indexer.php
+++ b/Classes/Common/Indexer.php
@@ -15,6 +15,7 @@
use Kitodo\Dlf\Common\Solr\Solr;
use Kitodo\Dlf\Domain\Repository\DocumentRepository;
use Kitodo\Dlf\Domain\Model\Document;
+use Kitodo\Dlf\Validation\DocumentValidator;
use Solarium\Core\Query\DocumentInterface;
use Solarium\QueryType\Update\Query\Query;
use Symfony\Component\Console\Input\InputInterface;
@@ -334,59 +335,67 @@ protected static function processLogical(Document $document, array $logicalUnit)
// Get metadata for logical unit.
$metadata = $doc->metadataArray[$logicalUnit['id']];
if (!empty($metadata)) {
- $metadata['author'] = self::removeAppendsFromAuthor($metadata['author']);
- // set Owner if available
- if ($document->getOwner()) {
- $metadata['owner'][0] = $document->getOwner()->getIndexName();
- }
- // Create new Solr document.
- $updateQuery = self::$solr->service->createUpdate();
- $solrDoc = self::getSolrDocument($updateQuery, $document, $logicalUnit);
- if (MathUtility::canBeInterpretedAsInteger($logicalUnit['points'])) {
- $solrDoc->setField('page', $logicalUnit['points']);
- }
- if ($logicalUnit['id'] == $doc->toplevelId) {
- $solrDoc->setField('thumbnail', $doc->thumbnail);
- } elseif (!empty($logicalUnit['thumbnailId'])) {
- $solrDoc->setField('thumbnail', $doc->getFileLocation($logicalUnit['thumbnailId']));
- }
- // There can be only one toplevel unit per UID, independently of backend configuration
- $solrDoc->setField('toplevel', $logicalUnit['id'] == $doc->toplevelId ? true : false);
- $solrDoc->setField('title', $metadata['title'][0], self::$fields['fieldboost']['title']);
- $solrDoc->setField('volume', $metadata['volume'][0], self::$fields['fieldboost']['volume']);
- // verify date formatting
- if(strtotime($metadata['date'][0])) {
- $solrDoc->setField('date', self::getFormattedDate($metadata['date'][0]));
- }
- $solrDoc->setField('record_id', $metadata['record_id'][0]);
- $solrDoc->setField('purl', $metadata['purl'][0]);
- $solrDoc->setField('location', $document->getLocation());
- $solrDoc->setField('urn', $metadata['urn']);
- $solrDoc->setField('license', $metadata['license']);
- $solrDoc->setField('terms', $metadata['terms']);
- $solrDoc->setField('restrictions', $metadata['restrictions']);
- $coordinates = json_decode($metadata['coordinates'][0]);
- if (is_object($coordinates)) {
- $solrDoc->setField('geom', json_encode($coordinates->features[0]));
- }
- $autocomplete = self::processMetadata($document, $metadata, $solrDoc);
- // Add autocomplete values to index.
- if (!empty($autocomplete)) {
- $solrDoc->setField('autocomplete', $autocomplete);
- }
- // Add collection information to logical sub-elements if applicable.
- if (
- in_array('collection', self::$fields['facets'])
- && empty($metadata['collection'])
- && !empty($doc->metadataArray[$doc->toplevelId]['collection'])
- ) {
- $solrDoc->setField('collection_faceting', $doc->metadataArray[$doc->toplevelId]['collection']);
- }
- try {
- $updateQuery->addDocument($solrDoc);
- self::$solr->service->update($updateQuery);
- } catch (\Exception $e) {
- self::handleException($e->getMessage());
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'general');
+ $validator = new DocumentValidator($metadata, explode(',', $extConf['requiredMetadataFields']));
+
+ if ($validator->hasAllMandatoryMetadataFields()) {
+ $metadata['author'] = self::removeAppendsFromAuthor($metadata['author']);
+ // set Owner if available
+ if ($document->getOwner()) {
+ $metadata['owner'][0] = $document->getOwner()->getIndexName();
+ }
+ // Create new Solr document.
+ $updateQuery = self::$solr->service->createUpdate();
+ $solrDoc = self::getSolrDocument($updateQuery, $document, $logicalUnit);
+ if (MathUtility::canBeInterpretedAsInteger($logicalUnit['points'])) {
+ $solrDoc->setField('page', $logicalUnit['points']);
+ }
+ if ($logicalUnit['id'] == $doc->toplevelId) {
+ $solrDoc->setField('thumbnail', $doc->thumbnail);
+ } elseif (!empty($logicalUnit['thumbnailId'])) {
+ $solrDoc->setField('thumbnail', $doc->getFileLocation($logicalUnit['thumbnailId']));
+ }
+ // There can be only one toplevel unit per UID, independently of backend configuration
+ $solrDoc->setField('toplevel', $logicalUnit['id'] == $doc->toplevelId ? true : false);
+ $solrDoc->setField('title', $metadata['title'][0], self::$fields['fieldboost']['title']);
+ $solrDoc->setField('volume', $metadata['volume'][0], self::$fields['fieldboost']['volume']);
+ // verify date formatting
+ if(strtotime($metadata['date'][0])) {
+ $solrDoc->setField('date', self::getFormattedDate($metadata['date'][0]));
+ }
+ $solrDoc->setField('record_id', $metadata['record_id'][0]);
+ $solrDoc->setField('purl', $metadata['purl'][0]);
+ $solrDoc->setField('location', $document->getLocation());
+ $solrDoc->setField('urn', $metadata['urn']);
+ $solrDoc->setField('license', $metadata['license']);
+ $solrDoc->setField('terms', $metadata['terms']);
+ $solrDoc->setField('restrictions', $metadata['restrictions']);
+ $coordinates = json_decode($metadata['coordinates'][0]);
+ if (is_object($coordinates)) {
+ $solrDoc->setField('geom', json_encode($coordinates->features[0]));
+ }
+ $autocomplete = self::processMetadata($document, $metadata, $solrDoc);
+ // Add autocomplete values to index.
+ if (!empty($autocomplete)) {
+ $solrDoc->setField('autocomplete', $autocomplete);
+ }
+ // Add collection information to logical sub-elements if applicable.
+ if (
+ in_array('collection', self::$fields['facets'])
+ && empty($metadata['collection'])
+ && !empty($doc->metadataArray[$doc->toplevelId]['collection'])
+ ) {
+ $solrDoc->setField('collection_faceting', $doc->metadataArray[$doc->toplevelId]['collection']);
+ }
+ try {
+ $updateQuery->addDocument($solrDoc);
+ self::$solr->service->update($updateQuery);
+ } catch (\Exception $e) {
+ self::handleException($e->getMessage());
+ return false;
+ }
+ } else {
+ Helper::log('Tip: If "record_id" field is missing then there is possibility that METS file still contains it but with the wrong source type attribute in "recordIdentifier" element', LOG_SEVERITY_NOTICE);
return false;
}
}
@@ -423,7 +432,7 @@ protected static function processPhysical(Document $document, int $page, array $
$doc->cPid = $document->getPid();
if ($doc->hasFulltext && $fullText = $doc->getFullText($physicalUnit['id'])) {
// Read extension configuration.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
// Create new Solr document.
$updateQuery = self::$solr->service->createUpdate();
$solrDoc = self::getSolrDocument($updateQuery, $document, $physicalUnit, $fullText);
diff --git a/Classes/Common/MetsDocument.php b/Classes/Common/MetsDocument.php
index 7f317feb8..61ce57ac4 100644
--- a/Classes/Common/MetsDocument.php
+++ b/Classes/Common/MetsDocument.php
@@ -189,10 +189,10 @@ public function getDownloadLocation(string $id): string
$file = $this->getFileInfo($id);
if ($file['mimeType'] === 'application/vnd.kitodo.iiif') {
$file['location'] = (strrpos($file['location'], 'info.json') === strlen($file['location']) - 9) ? $file['location'] : (strrpos($file['location'], '/') === strlen($file['location']) ? $file['location'] . 'info.json' : $file['location'] . '/info.json');
- $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'iiif');
IiifHelper::setUrlReader(IiifUrlReader::getInstance());
- IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
- IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
+ IiifHelper::setMaxThumbnailHeight($conf['thumbnailHeight']);
+ IiifHelper::setMaxThumbnailWidth($conf['thumbnailWidth']);
$service = IiifHelper::loadIiifResource($file['location']);
if ($service instanceof AbstractImageService) {
return $service->getImageUrl();
@@ -400,7 +400,7 @@ protected function getLogicalStructureInfo(\SimpleXMLElement $structure, bool $r
private function getThumbnail(string $id = '')
{
// Load plugin configuration.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
$fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
$thumbnail = null;
@@ -736,7 +736,7 @@ private function extractMetadataIfTypeSupported(string $dmdId, string $mdSection
if (class_exists($class)) {
$obj = GeneralUtility::makeInstance($class);
if ($obj instanceof MetadataInterface) {
- $obj->extractMetadata($this->mdSec[$dmdId]['xml'], $metadata, GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey)['useExternalApisForMetadata']);
+ $obj->extractMetadata($this->mdSec[$dmdId]['xml'], $metadata, GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'general')['useExternalApisForMetadata']);
return true;
}
} else {
@@ -1108,7 +1108,7 @@ protected function magicGetFileGrps(): array
{
if (!$this->fileGrpsLoaded) {
// Get configured USE attributes.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
$useGrps = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
if (!empty($extConf['fileGrpThumbs'])) {
$useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']));
@@ -1280,7 +1280,7 @@ protected function magicGetThumbnail(bool $forceReload = false): string
return $this->thumbnail;
}
// Load extension configuration.
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
if (empty($extConf['fileGrpThumbs'])) {
$this->logger->warning('No fileGrp for thumbnails specified');
$this->thumbnailLoaded = true;
diff --git a/Classes/Common/Solr/Solr.php b/Classes/Common/Solr/Solr.php
index 9f2b0ce5c..acd6e3a7f 100644
--- a/Classes/Common/Solr/Solr.php
+++ b/Classes/Common/Solr/Solr.php
@@ -243,34 +243,34 @@ public static function escapeQueryKeepField(string $query, int $pid): string
public static function getFields(): array
{
if (empty(self::$fields)) {
- $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
-
- self::$fields['id'] = $conf['solrFieldId'];
- self::$fields['uid'] = $conf['solrFieldUid'];
- self::$fields['pid'] = $conf['solrFieldPid'];
- self::$fields['page'] = $conf['solrFieldPage'];
- self::$fields['partof'] = $conf['solrFieldPartof'];
- self::$fields['root'] = $conf['solrFieldRoot'];
- self::$fields['sid'] = $conf['solrFieldSid'];
- self::$fields['toplevel'] = $conf['solrFieldToplevel'];
- self::$fields['type'] = $conf['solrFieldType'];
- self::$fields['title'] = $conf['solrFieldTitle'];
- self::$fields['volume'] = $conf['solrFieldVolume'];
- self::$fields['date'] = $conf['solrFieldDate'];
- self::$fields['thumbnail'] = $conf['solrFieldThumbnail'];
- self::$fields['default'] = $conf['solrFieldDefault'];
- self::$fields['timestamp'] = $conf['solrFieldTimestamp'];
- self::$fields['autocomplete'] = $conf['solrFieldAutocomplete'];
- self::$fields['fulltext'] = $conf['solrFieldFulltext'];
- self::$fields['record_id'] = $conf['solrFieldRecordId'];
- self::$fields['purl'] = $conf['solrFieldPurl'];
- self::$fields['urn'] = $conf['solrFieldUrn'];
- self::$fields['location'] = $conf['solrFieldLocation'];
- self::$fields['collection'] = $conf['solrFieldCollection'];
- self::$fields['license'] = $conf['solrFieldLicense'];
- self::$fields['terms'] = $conf['solrFieldTerms'];
- self::$fields['restrictions'] = $conf['solrFieldRestrictions'];
- self::$fields['geom'] = $conf['solrFieldGeom'];
+ $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'solr');
+ $solrFields = $conf['fields'];
+ self::$fields['id'] = $solrFields['id'];
+ self::$fields['uid'] = $solrFields['uid'];
+ self::$fields['pid'] = $solrFields['pid'];
+ self::$fields['page'] = $solrFields['page'];
+ self::$fields['partof'] = $solrFields['partof'];
+ self::$fields['root'] = $solrFields['root'];
+ self::$fields['sid'] = $solrFields['sid'];
+ self::$fields['toplevel'] = $solrFields['toplevel'];
+ self::$fields['type'] = $solrFields['type'];
+ self::$fields['title'] = $solrFields['title'];
+ self::$fields['volume'] = $solrFields['volume'];
+ self::$fields['date'] = $solrFields['date'];
+ self::$fields['thumbnail'] = $solrFields['thumbnail'];
+ self::$fields['default'] = $solrFields['default'];
+ self::$fields['timestamp'] = $solrFields['timestamp'];
+ self::$fields['autocomplete'] = $solrFields['autocomplete'];
+ self::$fields['fulltext'] = $solrFields['fulltext'];
+ self::$fields['record_id'] = $solrFields['recordId'];
+ self::$fields['purl'] = $solrFields['purl'];
+ self::$fields['urn'] = $solrFields['urn'];
+ self::$fields['location'] = $solrFields['location'];
+ self::$fields['collection'] = $solrFields['collection'];
+ self::$fields['license'] = $solrFields['license'];
+ self::$fields['terms'] = $solrFields['terms'];
+ self::$fields['restrictions'] = $solrFields['restrictions'];
+ self::$fields['geom'] = $solrFields['geom'];
}
return self::$fields;
@@ -350,25 +350,25 @@ protected function loadSolrConnectionInfo(): void
if (empty($this->config)) {
$config = [];
// Extract extension configuration.
- $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
+ $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'solr');
// Derive Solr scheme
- $config['scheme'] = empty($conf['solrHttps']) ? 'http' : 'https';
+ $config['scheme'] = empty($conf['https']) ? 'http' : 'https';
// Derive Solr host name.
- $config['host'] = ($conf['solrHost'] ? $conf['solrHost'] : '127.0.0.1');
+ $config['host'] = ($conf['host'] ? $conf['host'] : '127.0.0.1');
// Set username and password.
- $config['username'] = $conf['solrUser'];
- $config['password'] = $conf['solrPass'];
+ $config['username'] = $conf['user'];
+ $config['password'] = $conf['pass'];
// Set port if not set.
- $config['port'] = MathUtility::forceIntegerInRange($conf['solrPort'], 1, 65535, 8983);
+ $config['port'] = MathUtility::forceIntegerInRange($conf['port'], 1, 65535, 8983);
// Trim path of slashes and (re-)add trailing slash if path not empty.
- $config['path'] = trim($conf['solrPath'], '/');
+ $config['path'] = trim($conf['path'], '/');
if (!empty($config['path'])) {
$config['path'] .= '/';
}
// Set connection timeout lower than PHP's max_execution_time.
$maxExecutionTime = (int) ini_get('max_execution_time') ? : 30;
- $config['timeout'] = MathUtility::forceIntegerInRange($conf['solrTimeout'], 1, $maxExecutionTime, 10);
+ $config['timeout'] = MathUtility::forceIntegerInRange($conf['timeout'], 1, $maxExecutionTime, 10);
$this->config = $config;
}
}
diff --git a/Classes/Controller/AbstractController.php b/Classes/Controller/AbstractController.php
index e7db9f0ee..cc617ad09 100644
--- a/Classes/Controller/AbstractController.php
+++ b/Classes/Controller/AbstractController.php
@@ -166,7 +166,7 @@ protected function configureProxyUrl(string &$url): void
{
$this->uriBuilder->reset()
->setTargetPageUid($GLOBALS['TSFE']->id)
- ->setCreateAbsoluteUri(!empty($this->settings['forceAbsoluteUrl']))
+ ->setCreateAbsoluteUri(!empty($this->settings['general']['forceAbsoluteUrl']))
->setArguments(
[
'eID' => 'tx_dlf_pageview_proxy',
diff --git a/Classes/Controller/AudioPlayerController.php b/Classes/Controller/AudioPlayerController.php
index c546cd5b7..1ac7dfc87 100644
--- a/Classes/Controller/AudioPlayerController.php
+++ b/Classes/Controller/AudioPlayerController.php
@@ -85,7 +85,7 @@ public function mainAction(): void
$this->setDefaultPage();
// Check if there are any audio files available.
- $fileGrpsAudio = GeneralUtility::trimExplode(',', $this->extConf['fileGrpAudio']);
+ $fileGrpsAudio = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpAudio']);
while ($fileGrpAudio = array_shift($fileGrpsAudio)) {
$physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$this->requestData['page']]];
$fileId = $physicalStructureInfo['files'][$fileGrpAudio];
diff --git a/Classes/Controller/PageGridController.php b/Classes/Controller/PageGridController.php
index e0b864b1d..539d0b66b 100644
--- a/Classes/Controller/PageGridController.php
+++ b/Classes/Controller/PageGridController.php
@@ -37,7 +37,7 @@ public function mainAction(): void
$this->loadDocument();
if (
$this->isDocMissingOrEmpty()
- || empty($this->extConf['fileGrpThumbs'])
+ || empty($this->extConf['files']['fileGrpThumbs'])
) {
// Quit without doing anything if required variables are not set.
return;
@@ -48,7 +48,7 @@ public function mainAction(): void
$numPages = $this->document->getCurrentDocument()->numPages;
// Iterate through visible page set and display thumbnails.
for ($i = 1; $i <= $numPages; $i++) {
- $foundEntry = $this->getEntry($i, $this->extConf['fileGrpThumbs']);
+ $foundEntry = $this->getEntry($i, $this->extConf['files']['fileGrpThumbs']);
$foundEntry['state'] = ($i == $this->requestData['page']) ? 'cur' : 'no';
$entryArray[] = $foundEntry;
}
diff --git a/Classes/Controller/PageViewController.php b/Classes/Controller/PageViewController.php
index dc7d0089b..5cb106079 100644
--- a/Classes/Controller/PageViewController.php
+++ b/Classes/Controller/PageViewController.php
@@ -84,7 +84,7 @@ public function mainAction(): void
// Get the controls for the map.
$this->controls = explode(',', $this->settings['features']);
- $this->view->assign('forceAbsoluteUrl', $this->settings['forceAbsoluteUrl']);
+ $this->view->assign('forceAbsoluteUrl', $this->settings['general']['forceAbsoluteUrl']);
$this->addViewerJS();
@@ -106,7 +106,7 @@ protected function getFulltext(int $page): array
{
$fulltext = [];
// Get fulltext link.
- $fileGrpsFulltext = GeneralUtility::trimExplode(',', $this->extConf['fileGrpFulltext']);
+ $fileGrpsFulltext = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpFulltext']);
while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) {
$physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
$fileId = $physicalStructureInfo['files'][$fileGrpFulltext];
@@ -123,7 +123,7 @@ protected function getFulltext(int $page): array
}
}
if (empty($fulltext)) {
- $this->logger->notice('No full-text file found for page "' . $page . '" in fileGrps "' . $this->extConf['fileGrpFulltext'] . '"');
+ $this->logger->notice('No full-text file found for page "' . $page . '" in fileGrps "' . $this->extConf['files']['fileGrpFulltext'] . '"');
}
return $fulltext;
}
@@ -223,7 +223,7 @@ protected function getImage(int $page): array
{
$image = [];
// Get @USE value of METS fileGrp.
- $fileGrpsImages = GeneralUtility::trimExplode(',', $this->extConf['fileGrpImages']);
+ $fileGrpsImages = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpImages']);
while ($fileGrpImages = array_pop($fileGrpsImages)) {
// Get image link.
$physicalStructureInfo = $this->document->getCurrentDocument()->physicalStructureInfo[$this->document->getCurrentDocument()->physicalStructure[$page]];
@@ -244,7 +244,7 @@ protected function getImage(int $page): array
}
}
if (empty($image)) {
- $this->logger->warning('No image file found for page "' . $page . '" in fileGrps "' . $this->extConf['fileGrpImages'] . '"');
+ $this->logger->warning('No image file found for page "' . $page . '" in fileGrps "' . $this->extConf['files']['fileGrpImages'] . '"');
}
return $image;
}
diff --git a/Classes/Controller/ToolboxController.php b/Classes/Controller/ToolboxController.php
index 353c5522b..67c0580a2 100644
--- a/Classes/Controller/ToolboxController.php
+++ b/Classes/Controller/ToolboxController.php
@@ -160,7 +160,7 @@ private function renderFulltextDownloadTool(): void
{
if (
$this->isDocMissingOrEmpty()
- || empty($this->extConf['fileGrpFulltext'])
+ || empty($this->extConf['files']['fileGrpFulltext'])
) {
// Quit without doing anything if required variables are not set.
return;
@@ -184,7 +184,7 @@ private function renderFulltextTool(): void
{
if (
$this->isDocMissingOrEmpty()
- || empty($this->extConf['fileGrpFulltext'])
+ || empty($this->extConf['files']['fileGrpFulltext'])
) {
// Quit without doing anything if required variables are not set.
return;
@@ -297,7 +297,7 @@ private function renderPdfDownloadTool(): void
{
if (
$this->isDocMissingOrEmpty()
- || empty($this->extConf['fileGrpDownload'])
+ || empty($this->extConf['files']['fileGrpDownload'])
) {
// Quit without doing anything if required variables are not set.
return;
@@ -324,7 +324,7 @@ private function getPageLink(): array
$secondPageLink = '';
$pageLinkArray = [];
$pageNumber = $this->requestData['page'];
- $fileGrpsDownload = GeneralUtility::trimExplode(',', $this->extConf['fileGrpDownload']);
+ $fileGrpsDownload = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpDownload']);
// Get image link.
while ($fileGrpDownload = array_shift($fileGrpsDownload)) {
$firstFileGroupDownload = $this->currentDocument->physicalStructureInfo[$this->currentDocument->physicalStructure[$pageNumber]]['files'][$fileGrpDownload];
@@ -346,7 +346,7 @@ private function getPageLink(): array
empty($firstPageLink)
&& empty($secondPageLink)
) {
- $this->logger->warning('File not found in fileGrps "' . $this->extConf['fileGrpDownload'] . '"');
+ $this->logger->warning('File not found in fileGrps "' . $this->extConf['files']['fileGrpDownload'] . '"');
}
if (!empty($firstPageLink)) {
@@ -368,7 +368,7 @@ private function getPageLink(): array
private function getWorkLink(): string
{
$workLink = '';
- $fileGrpsDownload = GeneralUtility::trimExplode(',', $this->extConf['fileGrpDownload']);
+ $fileGrpsDownload = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpDownload']);
// Get work link.
while ($fileGrpDownload = array_shift($fileGrpsDownload)) {
$fileGroupDownload = $this->currentDocument->physicalStructureInfo[$this->currentDocument->physicalStructure[0]]['files'][$fileGrpDownload];
@@ -384,7 +384,7 @@ private function getWorkLink(): string
}
}
if (empty($workLink)) {
- $this->logger->warning('File not found in fileGrps "' . $this->extConf['fileGrpDownload'] . '"');
+ $this->logger->warning('File not found in fileGrps "' . $this->extConf['files']['fileGrpDownload'] . '"');
}
return $workLink;
}
@@ -401,7 +401,7 @@ private function renderSearchInDocumentTool(): void
{
if (
$this->isDocMissingOrEmpty()
- || empty($this->extConf['fileGrpFulltext'])
+ || empty($this->extConf['files']['fileGrpFulltext'])
|| empty($this->settings['solrcore'])
) {
// Quit without doing anything if required variables are not set.
@@ -496,7 +496,7 @@ private function getEncryptedCoreName(): string
*/
private function isFullTextEmpty(): bool
{
- $fileGrpsFulltext = GeneralUtility::trimExplode(',', $this->extConf['fileGrpFulltext']);
+ $fileGrpsFulltext = GeneralUtility::trimExplode(',', $this->extConf['files']['fileGrpFulltext']);
while ($fileGrpFulltext = array_shift($fileGrpsFulltext)) {
$fullTextFile = $this->currentDocument->physicalStructureInfo[$this->currentDocument->physicalStructure[$this->requestData['page']]]['files'][$fileGrpFulltext];
if (!empty($fullTextFile)) {
diff --git a/Classes/Eid/PageViewProxy.php b/Classes/Eid/PageViewProxy.php
index d745b5064..99d3fc37c 100644
--- a/Classes/Eid/PageViewProxy.php
+++ b/Classes/Eid/PageViewProxy.php
@@ -58,7 +58,7 @@ class PageViewProxy
public function __construct()
{
$this->requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
- $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
+ $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf', 'general');
}
/**
@@ -148,7 +148,7 @@ protected function handleHead(ServerRequestInterface $request): ResponseInterfac
try {
$targetResponse = $this->requestFactory->request($url, 'HEAD', [
'headers' => [
- 'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
+ 'User-Agent' => $this->extConf['userAgent'] ?? 'Kitodo.Presentation Proxy',
]
]);
} catch (\Exception $e) {
@@ -193,7 +193,7 @@ protected function handleGet(ServerRequestInterface $request): ResponseInterface
try {
$targetResponse = $this->requestFactory->request($url, 'GET', [
'headers' => [
- 'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
+ 'User-Agent' => $this->extConf['userAgent'] ?? 'Kitodo.Presentation Proxy',
],
// For performance, don't download content up-front. Rather, we'll
diff --git a/Classes/Format/Mods.php b/Classes/Format/Mods.php
index c62f3605c..9a4ced0cd 100644
--- a/Classes/Format/Mods.php
+++ b/Classes/Format/Mods.php
@@ -15,6 +15,8 @@
use Kitodo\Dlf\Api\Orcid\Profile as OrcidProfile;
use Kitodo\Dlf\Api\Viaf\Profile as ViafProfile;
use Kitodo\Dlf\Common\MetadataInterface;
+use Slub\Mods\Element\Name;
+use Slub\Mods\ModsReader;
/**
* Metadata MODS format class for the 'dlf' extension
@@ -32,6 +34,12 @@ class Mods implements MetadataInterface
**/
private $xml;
+ /**
+ * @access private
+ * @var ModsReader The metadata XML
+ **/
+ private $modsReader;
+
/**
* @access private
* @var array The metadata array
@@ -61,7 +69,7 @@ public function extractMetadata(\SimpleXMLElement $xml, array &$metadata, bool $
$this->metadata = $metadata;
$this->useExternalApis = $useExternalApis;
- $this->xml->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
+ $this->modsReader = new ModsReader($this->xml);
$this->getAuthors();
$this->getHolders();
@@ -80,20 +88,17 @@ public function extractMetadata(\SimpleXMLElement $xml, array &$metadata, bool $
*/
private function getAuthors(): void
{
- $authors = $this->xml->xpath('./mods:name[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="aut"]');
-
+ $authors = $this->modsReader->getNames('[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="aut"]');
// Get "author" and "author_sorting" again if that was too sophisticated.
if (empty($authors)) {
// Get all names which do not have any role term assigned and assume these are authors.
- $authors = $this->xml->xpath('./mods:name[not(./mods:role)]');
+ $authors = $this->modsReader->getNames('[not(./mods:role)]');
}
if (!empty($authors)) {
for ($i = 0, $j = count($authors); $i < $j; $i++) {
- $authors[$i]->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
-
- $identifier = $authors[$i]->xpath('./mods:name/mods:nameIdentifier[@type="orcid"]');
- if ($this->useExternalApis && !empty((string) $identifier[0])) {
- $this->getAuthorFromOrcidApi((string) $identifier[0], $authors, $i);
+ $identifiers = $authors[$i]->getNameIdentifiers('[@type="orcid"]');
+ if ($this->useExternalApis && !empty($identifiers)) {
+ $this->getAuthorFromOrcidApi($identifiers[0]->getValue(), $authors, $i);
} else {
$this->getAuthorFromXml($authors, $i);
}
@@ -141,34 +146,33 @@ private function getAuthorFromXml(array $authors, int $i): void
{
$this->getAuthorFromXmlDisplayForm($authors, $i);
- $nameParts = $authors[$i]->xpath('./mods:namePart');
-
+ $nameParts = $authors[$i]->getNameParts();
if (empty($this->metadata['author'][$i]) && $nameParts) {
$name = [];
$k = 4;
foreach ($nameParts as $namePart) {
if (
- isset($namePart['type'])
- && (string) $namePart['type'] == 'family'
+ !empty($namePart->getType())
+ && $namePart->getType() == 'family'
) {
- $name[0] = (string) $namePart;
+ $name[0] = $namePart->getValue();
} elseif (
- isset($namePart['type'])
- && (string) $namePart['type'] == 'given'
+ !empty($namePart->getType())
+ && $namePart->getType() == 'given'
) {
- $name[1] = (string) $namePart;
+ $name[1] = $namePart->getValue();
} elseif (
- isset($namePart['type'])
- && (string) $namePart['type'] == 'termsOfAddress'
+ !empty($namePart->getType())
+ && $namePart->getType() == 'termsOfAddress'
) {
- $name[2] = (string) $namePart;
+ $name[2] = $namePart->getValue();
} elseif (
- isset($namePart['type'])
- && (string) $namePart['type'] == 'date'
+ !empty($namePart->getType())
+ && $namePart->getType() == 'date'
) {
- $name[3] = (string) $namePart;
+ $name[3] = $namePart->getValue();
} else {
- $name[$k] = (string) $namePart;
+ $name[$k] = $namePart->getValue();
}
$k++;
}
@@ -176,8 +180,8 @@ private function getAuthorFromXml(array $authors, int $i): void
$this->metadata['author'][$i] = trim(implode(', ', $name));
}
// Append "valueURI" to name using Unicode unit separator.
- if (isset($authors[$i]['valueURI'])) {
- $this->metadata['author'][$i] .= pack('C', 31) . (string) $authors[$i]['valueURI'];
+ if (!empty($authors[$i]->getValueURI())) {
+ $this->metadata['author'][$i] .= pack('C', 31) . $authors[$i]->getValueURI();
}
}
@@ -186,16 +190,16 @@ private function getAuthorFromXml(array $authors, int $i): void
*
* @access private
*
- * @param array $authors
+ * @param Name[] $authors
* @param int $i
*
* @return void
*/
private function getAuthorFromXmlDisplayForm(array $authors, int $i): void
{
- $displayForm = $authors[$i]->xpath('./mods:displayForm');
- if ($displayForm) {
- $this->metadata['author'][$i] = (string) $displayForm[0];
+ $displayForms = $authors[$i]->getDisplayForms();
+ if ($displayForms) {
+ $this->metadata['author'][$i] = $displayForms[0]->getValue();
}
}
@@ -208,15 +212,13 @@ private function getAuthorFromXmlDisplayForm(array $authors, int $i): void
*/
private function getHolders(): void
{
- $holders = $this->xml->xpath('./mods:name[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="prv"]');
+ $holders = $this->modsReader->getNames('[./mods:role/mods:roleTerm[@type="code" and @authority="marcrelator"]="prv"]');
if (!empty($holders)) {
for ($i = 0, $j = count($holders); $i < $j; $i++) {
- $holders[$i]->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3');
-
- $identifier = $holders[$i]->xpath('./mods:name/mods:nameIdentifier[@type="viaf"]');
- if ($this->useExternalApis && !empty((string) $identifier[0])) {
- $this->getHolderFromViafApi((string) $identifier[0], $holders, $i);
+ $identifiers = $holders[$i]->getNameIdentifiers('[@type="viaf"]');
+ if ($this->useExternalApis && !empty($identifiers)) {
+ $this->getHolderFromViafApi($identifiers[0]->getValue(), $holders, $i);
} else {
$this->getHolderFromXml($holders, $i);
}
@@ -264,8 +266,8 @@ private function getHolderFromXml(array $holders, int $i): void
{
$this->getHolderFromXmlDisplayForm($holders, $i);
// Append "valueURI" to name using Unicode unit separator.
- if (isset($holders[$i]['valueURI'])) {
- $this->metadata['holder'][$i] .= pack('C', 31) . (string) $holders[$i]['valueURI'];
+ if (!empty($holders[$i]->getValueURI())) {
+ $this->metadata['holder'][$i] .= pack('C', 31) . $holders[$i]->getValueURI();
}
}
@@ -282,9 +284,9 @@ private function getHolderFromXml(array $holders, int $i): void
private function getHolderFromXmlDisplayForm(array $holders, int $i): void
{
// Check if there is a display form.
- $displayForm = $holders[$i]->xpath('./mods:displayForm');
- if ($displayForm) {
- $this->metadata['holder'][$i] = (string) $displayForm[0];
+ $displayForms = $holders[$i]->getDisplayForm();
+ if ($displayForms) {
+ $this->metadata['holder'][$i] = $displayForms[0]->getValue();
}
}
@@ -297,17 +299,34 @@ private function getHolderFromXmlDisplayForm(array $holders, int $i): void
*/
private function getPlaces(): void
{
- $places = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:place/mods:placeTerm');
+ $places = [];
+ $originInfos = $this->modsReader->getOriginInfos('[not(./mods:edition="[Electronic ed.]")]');
+ foreach ($originInfos as $originInfo) {
+ foreach ($originInfo->getPlaces() as $place) {
+ foreach ($place->getPlaceTerms() as $placeTerm) {
+ $places[] = $placeTerm->getValue();
+ }
+ }
+ }
+
// Get "place" and "place_sorting" again if that was to sophisticated.
if (empty($places)) {
// Get all places and assume these are places of publication.
- $places = $this->xml->xpath('./mods:originInfo/mods:place/mods:placeTerm');
+ $originInfos = $this->modsReader->getOriginInfos();
+ foreach ($originInfos as $originInfo) {
+ foreach ($originInfo->getPlaces() as $place) {
+ foreach ($place->getPlaceTerms() as $placeTerm) {
+ $places[] = $placeTerm->getValue();
+ }
+ }
+ }
}
+
if (!empty($places)) {
foreach ($places as $place) {
- $this->metadata['place'][] = (string) $place;
+ $this->metadata['place'][] = $place;
if (!$this->metadata['place_sorting'][0]) {
- $this->metadata['place_sorting'][0] = preg_replace('/[[:punct:]]/', '', (string) $place);
+ $this->metadata['place_sorting'][0] = preg_replace('/[[:punct:]]/', '', $place);
}
}
}
@@ -323,31 +342,37 @@ private function getPlaces(): void
private function getYears(): void
{
// Get "year_sorting".
- $yearsSorting = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:dateOther[@type="order" and @encoding="w3cdtf"]');
+ $yearsSorting = $this->modsReader->getOriginInfos('[not(./mods:edition="[Electronic ed.]")]/mods:dateOther[@type="order" and @encoding="w3cdtf"]');
if ($yearsSorting) {
foreach ($yearsSorting as $yearSorting) {
- $this->metadata['year_sorting'][0] = (int) $yearSorting;
+ $otherDates = $yearSorting->getOtherDates();
+ if (!empty($otherDates)) {
+ $this->metadata['year_sorting'][0] = $otherDates[0]->getValue();
+ }
}
}
// Get "year" and "year_sorting" if not specified separately.
- $years = $this->xml->xpath('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:dateIssued[@keyDate="yes"]');
+ $years = $this->modsReader->getOriginInfos('./mods:originInfo[not(./mods:edition="[Electronic ed.]")]/mods:dateIssued[@keyDate="yes"]');
// Get "year" and "year_sorting" again if that was to sophisticated.
if (empty($years)) {
// Get all dates and assume these are dates of publication.
- $years = $this->xml->xpath('./mods:originInfo/mods:dateIssued');
+ $years = $this->modsReader->getOriginInfos();
}
if (!empty($years)) {
foreach ($years as $year) {
- $this->metadata['year'][] = (string) $year;
- if (!$this->metadata['year_sorting'][0]) {
- $yearSorting = str_ireplace('x', '5', preg_replace('/[^\d.x]/i', '', (string) $year));
- if (
- strpos($yearSorting, '.')
- || strlen($yearSorting) < 3
- ) {
- $yearSorting = (((int) trim($yearSorting, '.') - 1) * 100) + 50;
+ $issued = $year->getIssuedDates();
+ if (!empty($issued)) {
+ $this->metadata['year'][] = $issued[0]->getValue();
+ if (!$this->metadata['year_sorting'][0]) {
+ $yearSorting = str_ireplace('x', '5', preg_replace('/[^\d.x]/i', '', $issued[0]->getValue()));
+ if (
+ strpos($yearSorting, '.')
+ || strlen($yearSorting) < 3
+ ) {
+ $yearSorting = (((int) trim($yearSorting, '.') - 1) * 100) + 50;
+ }
+ $this->metadata['year_sorting'][0] = (int) $yearSorting;
}
- $this->metadata['year_sorting'][0] = (int) $yearSorting;
}
}
}
diff --git a/Classes/Hooks/DataHandler.php b/Classes/Hooks/DataHandler.php
index fcce799ec..cf2461210 100644
--- a/Classes/Hooks/DataHandler.php
+++ b/Classes/Hooks/DataHandler.php
@@ -356,8 +356,8 @@ public function processCmdmap_postProcess(string $command, string $table, $id):
&& $table == 'tx_dlf_solrcores'
) {
// Is core deletion allowed in extension configuration?
- $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
- if (!empty($extConf['solrAllowCoreDelete'])) {
+ $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf', 'solr');
+ if (!empty($extConf['allowCoreDelete'])) {
// Delete core from Apache Solr as well.
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_dlf_solrcores');
diff --git a/Classes/Validation/DocumentValidator.php b/Classes/Validation/DocumentValidator.php
new file mode 100644
index 000000000..82d3f0f37
--- /dev/null
+++ b/Classes/Validation/DocumentValidator.php
@@ -0,0 +1,141 @@
+
+ *
+ * This file is part of the Kitodo and TYPO3 projects.
+ *
+ * @license GNU General Public License version 3 or later.
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ */
+
+namespace Kitodo\Dlf\Validation;
+
+use TYPO3\CMS\Core\Log\Logger;
+use TYPO3\CMS\Core\Log\LogManager;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Class for document validation. Currently used for validating metadata
+ * fields but in the future should be extended also for other fields.
+ *
+ * @package TYPO3
+ * @subpackage dlf
+ *
+ * @access public
+ */
+class DocumentValidator
+{
+ /**
+ * @access protected
+ * @var Logger This holds the logger
+ */
+ protected Logger $logger;
+
+ /**
+ * @access private
+ * @var array
+ */
+ private array $metadata;
+
+ /**
+ * @access private
+ * @var array
+ */
+ private array $requiredMetadataFields;
+
+ /**
+ * @access private
+ * @var ?\SimpleXMLElement
+ */
+ private ?\SimpleXMLElement $xml;
+
+ /**
+ * Constructs DocumentValidator instance.
+ *
+ * @access public
+ *
+ * @param array $metadata
+ * @param array $requiredMetadataFields
+ *
+ * @return void
+ */
+ public function __construct(array $metadata = [], array $requiredMetadataFields = [], ?\SimpleXMLElement $xml = null)
+ {
+ $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
+ $this->metadata = $metadata;
+ $this->requiredMetadataFields = $requiredMetadataFields;
+ $this->xml = $xml;
+ }
+
+ /**
+ * Check if metadata array contains all mandatory fields before save.
+ *
+ * @access public
+ *
+ * @return bool
+ */
+ public function hasAllMandatoryMetadataFields(): bool
+ {
+ foreach ($this->requiredMetadataFields as $requiredMetadataField) {
+ if (empty($this->metadata[$requiredMetadataField][0])) {
+ $this->logger->error('Missing required metadata field "' . $requiredMetadataField . '".');
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if xml contains at least one logical structure with given type.
+ *
+ * @access public
+ *
+ * @param string $type e.g. documentary, newspaper or object
+ *
+ * @return bool
+ */
+ public function hasCorrectLogicalStructure(string $type): bool
+ {
+ $expectedNodes = $this->xml->xpath('./mets:structMap[@TYPE="LOGICAL"]/mets:div[@TYPE="' . $type . '"]');
+ if ($expectedNodes) {
+ return true;
+ }
+
+ $existingNodes = $this->xml->xpath('./mets:structMap[@TYPE="LOGICAL"]/mets:div');
+ if ($existingNodes) {
+ $this->logger->error('Document contains logical structure but @TYPE="' . $type . '" is missing.');
+ return false;
+ }
+
+ $this->logger->error('Document does not contain logical structure.');
+ return false;
+ }
+
+ /**
+ * Check if xml contains at least one physical structure with type 'physSequence'.
+ *
+ * @access public
+ *
+ * @return bool
+ */
+ public function hasCorrectPhysicalStructure(): bool
+ {
+ $physSequenceNodes = $this->xml->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div[@TYPE="physSequence"]');
+ if ($physSequenceNodes) {
+ return true;
+ }
+
+ $physicalStructureNodes = $this->xml->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div');
+ if ($physicalStructureNodes) {
+ $this->logger->error('Document contains physical structure but @TYPE="physSequence" is missing.');
+ return false;
+ }
+
+ $this->logger->error('Document does not contain physical structure.');
+ return false;
+ }
+}
diff --git a/Resources/Private/Language/de.locallang_labels.xlf b/Resources/Private/Language/de.locallang_labels.xlf
index 0d3adafda..597c15ac3 100644
--- a/Resources/Private/Language/de.locallang_labels.xlf
+++ b/Resources/Private/Language/de.locallang_labels.xlf
@@ -629,211 +629,215 @@
DLF: Werkzeugkasten
-
+
Standard-Namensräume für Metadaten
-
+
Internen Proxy für Werkansicht aktivieren? (Standard ist "FALSE")
-
+
DLF User-Agent: (Standard ist "Kitodo.Presentation")
-
+
Verwende nur absolute Links für Seiten und Ressourcen?: Wird nur in speziellen Multi-Domain-Umgebungen benötigt; erfordert einen voll qualifizierten Einstiegspunkt in der Seitenkonfiguration (Standard ist "FALSE")
-
+
Verwende HTTPS for absolute Links?: erfordert einen Einstiegspunkt mit "https://..." in der Seitenkonfiguration (Standard ist "FALSE")
-
+
Eingelesene METS Dateien / IIIF-Manifeste zwischenspeichern: Dies kann die Geschwindigkeit geringfügig verbessern, führt aber zu einer sehr großen "fe_session_data" Tabelle (Standard ist "FALSE")
-
+
Neue Kollektionen publizieren?: Sollen neue Kollektionen automatisch in der OAI-PMH-Schnittstelle veröffentlicht werden? (Standard ist "TRUE")
-
+
Indexierte Dokumente einblenden?: Sollen ausgeblendete Dokumente bei der erneuten Indexierung wieder eingeblendet werden? (Standard ist "FALSE")
-
+
Verwende externe APIs zum Abrufen von Metadaten?: (Standard ist "FALSE")
-
+
+ Für die Indexierung von Dokumenten erforderliche Metadatenfelder
+
+
+
Seiten fileGrps: Komma-getrennte Liste der @USE Attributwerte der Seitenansichten nach aufsteigender Größe sortiert (Standard ist "DEFAULT,MAX")
-
+
Vorschau fileGrp: Komma-getrennte Liste der @USE Attributwerte der Vorschaubilder nach absteigender Priorität sortiert (Standard ist "THUMBS")
-
+
Download fileGrp: Komma-getrennte Liste der @USE Attributwerte der Downloads nach absteigender Priorität sortiert (Standard ist "DOWNLOAD")
-
+
Volltext fileGrp: Komma-getrennte Liste der @USE Attributwerte der Volltexte nach absteigender Priorität sortiert (Standard ist "FULLTEXT")
-
+
Audio fileGrp: Komma-getrennte Liste der @USE Attributwerte der Audiodateien nach absteigender Priorität sortiert (Standard ist "AUDIO")
-
+
IIIF-Annotationen mit Motivation "painting" als Volltext behandeln?: Als Volltext behandelte Annotationen werden im Suchid idiert (Standard ist "FALSE")
-
+
Maximale Thumbnail-Breite für IIIF-Images: Gilt nur für Bilder ohne Thumbnail-Angaben (Standard ist "150")
-
+
Maximale Thumbnail-Höhe für IIIF-Images: Gilt nur für Bilder ohne Thumbnail-Angaben (Standard ist "150")
-
+
Solr Verbindung
-
+
HTTPS verwenden: (Standard ist "FALSE")
-
+
Solr Server Host: (Standard ist "localhost")
-
+
Solr Server Port: (Standard ist "8983")
-
+
Solr Server Pfad: ohne API-Endpunkt "/solr" (Standard ist "/")
-
+
Solr Server Benutzername: (Standard ist "")
-
+
Solr Server Kennwort: (Standard ist "")
-
+
Solr Server Timeout: (Standard ist "10")
-
+
Löschen von Solr Kern zulassen?: Soll beim Löschen eines Solr Kerns im TYPO3 Backend auch der entsprechende Index in Apache Solr gelöscht werden? (Standard ist "FALSE")
-
+
Solr-Schema-Feld "id" : Unique identifier for the document in the id (Standard ist "id")
-
+
Solr-Schema-Feld "uid" : Unique identifier for the document (or its top-level parent) in the TYPO3 database (Standard ist "uid")
-
+
Solr-Schema-Feld "pid" : PageID for the document (or its top-level parent) in the TYPO3 database (Standard ist "pid")
-
+
Solr-Schema-Feld "page" : Image number where this document starts (Standard ist "page")
-
+
Solr-Schema-Feld "partof" : Unique identifier for the parent document in the TYPO3 database. Only if this is a multi-volume work! (Standard ist "partof")
-
+
Solr-Schema-Feld "root" : Unique identifier for the root document in the TYPO3 database. Only if this is a multi-volume work! (Standard ist "root")
-
+
Solr-Schema-Feld "sid" : XML ID of this document in the METS file. This is only unique within the METS file! (Standard ist "sid")
-
+
Solr-Schema-Feld "toplevel" : Information if it is a top-level document (Standard ist "toplevel")
-
+
Solr-Schema-Feld "type" : Type of document (eg. monograph, chapter, etc.) (Standard ist "type")
-
+
Solr-Schema-Feld "title" : Title field is mandatory for identifying documents (Standard ist "title")
-
+
Solr-Schema-Feld "volume" : Volume field is mandatory for identifying documents (Standard ist "volume")
-
+
Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (Standard ist "date")
-
+
Solr-Schema-Feld "thumbnail" : URL of thumbnail image for the document (Standard ist "thumbnail")
-
+
Solr-Schema-Feld "default" : CatchAll field (Standard ist "default")
-
+
Solr-Schema-Feld "timestamp" : (Standard ist "timestamp")
-
+
Solr-Schema-Feld "autocomplete" : Autocomplete field for search form (Standard ist "autocomplete")
-
+
Solr-Schema-Feld "fulltext" : Fulltext field for OCR results (Standard ist "fulltext")
-
+
Solr-Schema-Feld "record_id" : Record ID of the document (required for OAI_DC output) (Standard ist "record_id")
-
+
Solr-Schema-Feld "purl" : Permanent URL of the document (required for EPICUR output) (Standard ist "purl")
-
+
Solr-Schema-Feld "urn" : URN of the Document (required for EPICUR output) (Standard ist "urn")
-
+
Solr-Schema-Feld "location" : Location of METS XML (required for METS output) (Standard ist "location")
-
+
Solr-Schema-Feld "collection" : Associated collection(s) of the document (Standard ist "collection")
-
+
Solr-Schema-Feld "license" : License (should be URI) (Standard ist "license")
-
+
Solr-Schema-Feld "terms" : Term of Use (should be URI) (Standard ist "terms")
-
+
Solr-Schema-Feld "restrictions" : Access Restrictions (should be URI) (Standard ist "restrictions")
-
+
Solr-Schema-Feld "geom" : GeoJSON geometry for spatial search (Standard ist "geom")
diff --git a/Resources/Private/Language/locallang_labels.xlf b/Resources/Private/Language/locallang_labels.xlf
index 89938425c..abacff0e9 100644
--- a/Resources/Private/Language/locallang_labels.xlf
+++ b/Resources/Private/Language/locallang_labels.xlf
@@ -476,159 +476,162 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+