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: Toolbox DLF: Werkzeugkasten - + Default metadata namespaces Standard-Namensräume für Metadaten - + Enable internal page view proxy?: (default is "FALSE") Internen Proxy für Werkansicht aktivieren? (Standard ist "FALSE") - + DLF User-Agent: (Standard ist "Kitodo.Presentation") DLF User-Agent: (default is "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") Force all links to pages and resources to be absolute?: Only needed for some multi-domain environments; requires a fully qualified Entry Point in Site Configuration (default is "FALSE") - + Verwende HTTPS for absolute Links?: erfordert einen Einstiegspunkt mit "https://..." in der Seitenkonfiguration (Standard ist "FALSE") Use HTTPS for absolute links?: requires a valid Entry Point with "https://..." in Site Configuration (default is "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") Cache parsed METS files / IIIF manifests: Caching improves performance a little bit but can result in a very large "fe_session_data" table (default is "FALSE") - + Neue Kollektionen publizieren?: Sollen neue Kollektionen automatisch in der OAI-PMH-Schnittstelle veröffentlicht werden? (Standard ist "TRUE") Publish new collections?: Should new collections automatically be published in the OAI-PMH interface? (default is "TRUE") - + Unhide ided documents?: Should hidden documents be unhidden when re-iding them? (default is "FALSE") 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") Use external APIs for getting metadata?: (default is "FALSE") - + + Für die Indexierung von Dokumenten erforderliche Metadatenfelder + Metadata fields required for indexing documents + + Seiten fileGrps: Komma-getrennte Liste der @USE Attributwerte der Seitenansichten nach aufsteigender Größe sortiert (Standard ist "DEFAULT,MAX") Page fileGrps: comma-separated list of @USE attribute values ordered by increasing size (default is "DEFAULT,MAX") - + Vorschau fileGrp: Komma-getrennte Liste der @USE Attributwerte der Vorschaubilder nach absteigender Priorität sortiert (Standard ist "THUMBS") Thumbnail fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "THUMBS") - + Download fileGrp: Komma-getrennte Liste der @USE Attributwerte der Downloads nach absteigender Priorität sortiert (Standard ist "DOWNLOAD") Download fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "DOWNLOAD") - + Volltext fileGrp: Komma-getrennte Liste der @USE Attributwerte der Volltexte nach absteigender Priorität sortiert (Standard ist "FULLTEXT") Fulltext fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "FULLTEXT") - + Audio fileGrp: Komma-getrennte Liste der @USE Attributwerte der Audiodateien nach absteigender Priorität sortiert (Standard ist "AUDIO") Audio fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "AUDIO") - + IIIF-Annotationen mit Motivation "painting" als Volltext behandeln?: Als Volltext behandelte Annotationen werden im Suchid idiert (Standard ist "FALSE") Handle IIIF annotations with motivation "painting" as fulltext?: Handling annotations as fulltexts means they are ided (default is "FALSE") - + Maximale Thumbnail-Breite für IIIF-Images: Gilt nur für Bilder ohne Thumbnail-Angaben (Standard ist "150") Maximum thumbnail width for IIIF images: Only for images without a thumbnail declaration (default is "150") - + Maximale Thumbnail-Höhe für IIIF-Images: Gilt nur für Bilder ohne Thumbnail-Angaben (Standard ist "150") Maximum thumbnail height for IIIF images: Only for images without a thumbnail declaration (default is "150") - + Solr Connection Solr Verbindung - + HTTPS verwenden: (Standard ist "FALSE") Use HTTPS: (default is "FALSE") - + Solr Server Host: (Standard ist "localhost") Solr Server Host: (default is "localhost") - + Solr Server Port: (Standard ist "8983") Solr Server Port: (default is "8983") - + Solr Server Pfad: ohne API-Endpunkt "/solr" (Standard ist "/") Solr Server Path: without API endpoint "/solr" (default is "/") - + Solr Server Benutzername: (Standard ist "") Solr Server User: (default is "") - + Solr Server Kennwort: (Standard ist "") Solr Server Password: (default is "") - + Solr Server Timeout: (Standard ist "10") Solr Server Timeout: (default is "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") Allow Solr Core Deletion?: If a Solr Core is deleted in the TYPO3 Backend, should it be deleted in Apache Solr as well? (default is "FALSE") - + Solr-Schema-Feld "id" : Unique identifier for the document in the id (Standard ist "id") Solr Schema Field "id" : Unique identifier for the document in the id (default is "id") - + Solr-Schema-Feld "uid" : Unique identifier for the document (or its top-level parent) in the TYPO3 database (Standard ist "uid") Solr Schema Field "uid" : Unique identifier for the document (or its top-level parent) in the TYPO3 database (default is "uid") - + Solr-Schema-Feld "pid" : PageID for the document (or its top-level parent) in the TYPO3 database (Standard ist "pid") Solr Schema Field "pid" : PageID for the document (or its top-level parent) in the TYPO3 database (default is "pid") - + Solr-Schema-Feld "page" : Image number where this document starts (Standard ist "page") Solr Schema Field "page" : Image number where this document starts (default is "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 Field "partof" : Unique identifier for the parent document in the TYPO3 database. Only if this is a multi-volume work! (default is "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 Field "root" : Unique identifier for the root document in the TYPO3 database. Only if this is a multi-volume work! (default is "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 Field "sid" : XML ID of this document in the METS file. This is only unique within the METS file! (default is "sid") - + Solr-Schema-Feld "toplevel" : Information if it is a top-level document (Standard ist "toplevel") Solr Schema Field "toplevel" : Information if it is a top-level document (default is "toplevel") - + Solr-Schema-Feld "type" : Type of document (eg. monograph, chapter, etc.) (Standard ist "type") Solr Schema Field "type" : Type of document (eg. monograph, chapter, etc.) (default is "type") - + Solr-Schema-Feld "title" : Title field is mandatory for identifying documents (Standard ist "title") Solr Schema Field "title" : Title field is mandatory for identifying documents (default is "title") - + Solr-Schema-Feld "volume" : Volume field is mandatory for identifying documents (Standard ist "volume") Solr Schema Field "volume" : Volume field is mandatory for identifying documents (default is "volume") - + Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (Standard ist "date") Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (default is "date") - + Solr-Schema-Feld "thumbnail" : URL of thumbnail image for the document (Standard ist "thumbnail") Solr Schema Field "thumbnail" : URL of thumbnail image for the document (default is "thumbnail") - + Solr-Schema-Feld "default" : CatchAll field (Standard ist "default") Solr Schema Field default" : CatchAll field (default is "default") - + Solr-Schema-Feld "timestamp" : (Standard ist "timestamp") Solr Schema Field "timestamp" : (default is "timestamp") - + Solr-Schema-Feld "autocomplete" : Autocomplete field for search form (Standard ist "autocomplete") Solr Schema Field "autocomplete" : Autocomplete field for search form (default is "autocomplete") - + Solr-Schema-Feld "fulltext" : Fulltext field for OCR results (Standard ist "fulltext") Solr Schema Field "fulltext" : Fulltext field for OCR results (default is "fulltext") - + Solr-Schema-Feld "record_id" : Record ID of the document (required for OAI_DC output) (Standard ist "record_id") Solr Schema Field "record_id" : Record ID of the document (required for OAI_DC output) (default is "record_id") - + Solr-Schema-Feld "purl" : Permanent URL of the document (required for EPICUR output) (Standard ist "purl") Solr Schema Field "purl" : Permanent URL of the document (required for EPICUR output) (default is "purl") - + Solr-Schema-Feld "urn" : URN of the Document (required for EPICUR output) (Standard ist "urn") Solr Schema Field "urn" : URN of the Document (required for EPICUR output) (default is "urn") - + Solr-Schema-Feld "location" : Location of METS XML (required for METS output) (Standard ist "location") Solr Schema Field "location" : Location of METS XML (required for METS output) (default is "location") - + Solr-Schema-Feld "collection" : Associated collection(s) of the document (Standard ist "collection") Solr Schema Field "collection" : Associated collection(s) of the document (default is "collection") - + Solr-Schema-Feld "license" : License (should be URI) (Standard ist "license") Solr Schema Field "license" : License (should be URI) (default is "license") - + Solr-Schema-Feld "terms" : Term of Use (should be URI) (Standard ist "terms") Solr Schema Field "terms" : Term of Use (should be URI) (default is "terms") - + Solr-Schema-Feld "restrictions" : Access Restrictions (should be URI) (Standard ist "restrictions") Solr Schema Field "restrictions" : Access Restrictions (should be URI) (default is "restrictions") - + Solr-Schema-Feld "geom" : GeoJSON geometry for spatial search (Standard ist "geom") Solr Schema Field "geom" : GeoJSON geometry for spatial search (default is "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 @@ Default metadata namespaces - + Enable internal page view proxy?: (default is "FALSE") - + DLF User-Agent: (default is "Kitodo.Presentation") - + Force all links to pages and resources to be absolute?: Only needed for some multi-domain environments; requires a fully qualified Entry Point in Site Configuration (default is "FALSE") - + Use HTTPS for absolute links?: requires a valid Entry Point with "https://..." in Site Configuration (default is "FALSE") - + Cache parsed METS files / IIIF manifests: Caching improves performance a little bit but can result in a very large "fe_session_data" table (default is "FALSE") - + Publish new collections?: Should new collections automatically be published in the OAI-PMH interface? (default is "TRUE") - + Unhide ided documents?: Should hidden documents be unhidden when re-iding them? (default is "FALSE") - + Use external APIs for getting metadata?: (default is "FALSE") - + + Metadata fields required for indexing documents + + Page fileGrps: comma-separated list of @USE attribute values ordered by increasing size (default is "DEFAULT,MAX") - + Thumbnail fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "THUMBS") - + Download fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "DOWNLOAD") - + Fulltext fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "FULLTEXT") - + Audio fileGrp: comma-separated list of @USE attribute values ordered by decreasing priority (default is "AUDIO") - + Handle IIIF annotations with motivation "painting" as fulltext?: Handling annotations as fulltexts means they are ided (default is "FALSE") - + Maximum thumbnail width for IIIF images: Only for images without a thumbnail declaration (default is "150") - + Maximum thumbnail height for IIIF images: Only for images without a thumbnail declaration (default is "150") - + Solr Connection - + Use HTTPS: (default is "FALSE") - + Solr Server Host: (default is "localhost") - + Solr Server Port: (default is "8983") - + Solr Server Path: without API endpoint "/solr" (default is "/") - + Solr Server User: (default is "") - + Solr Server Password: (default is "") - + Solr Server Timeout: (default is "10") - + Allow Solr Core Deletion?: If a Solr Core is deleted in the TYPO3 Backend, should it be deleted in Apache Solr as well? (default is "FALSE") - + Solr Schema Field "id" : Unique identifier for the document in the id (default is "id") - + Solr Schema Field "uid" : Unique identifier for the document (or its top-level parent) in the TYPO3 database (default is "uid") - + Solr Schema Field "pid" : PageID for the document (or its top-level parent) in the TYPO3 database (default is "pid") - + Solr Schema Field "page" : Image number where this document starts (default is "page") - + Solr Schema Field "partof" : Unique identifier for the parent document in the TYPO3 database. Only if this is a multi-volume work! (default is "partof") - + Solr Schema Field "root" : Unique identifier for the root document in the TYPO3 database. Only if this is a multi-volume work! (default is "root") - + Solr Schema Field "sid" : XML ID of this document in the METS file. This is only unique within the METS file! (default is "sid") - + Solr Schema Field "toplevel" : Information if it is a top-level document (default is "toplevel") - + Solr Schema Field "type" : Type of document (eg. monograph, chapter, etc.) (default is "type") - + Solr Schema Field "title" : Title field is mandatory for identifying documents (default is "title") - + Solr Schema Field "volume" : Volume field is mandatory for identifying documents (default is "volume") - + Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (default is "date") - + Solr Schema Field "thumbnail" : URL of thumbnail image for the document (default is "thumbnail") - + Solr Schema Field default" : CatchAll field (default is "default") - + Solr Schema Field "timestamp" : (default is "timestamp") - + Solr Schema Field "autocomplete" : Autocomplete field for search form (default is "autocomplete") - + Solr Schema Field "fulltext" : Fulltext field for OCR results (default is "fulltext") - + Solr Schema Field "record_id" : Record ID of the document (required for OAI_DC output) (default is "record_id") - + Solr Schema Field "purl" : Permanent URL of the document (required for EPICUR output) (default is "purl") - + Solr Schema Field "urn" : URN of the Document (required for EPICUR output) (default is "urn") - + Solr Schema Field "location" : Location of METS XML (required for METS output) (default is "location") - + Solr Schema Field "collection" : Associated collection(s) of the document (default is "collection") - + Solr Schema Field "license" : License (should be URI) (default is "license") - + Solr Schema Field "terms" : Term of Use (should be URI) (default is "terms") - + Solr Schema Field "restrictions" : Access Restrictions (should be URI) (default is "restrictions") - + Solr Schema Field "geom" : GeoJSON geometry for spatial search (default is "geom") - + diff --git a/Tests/Functional/Api/PageViewProxyTest.php b/Tests/Functional/Api/PageViewProxyTest.php index fe6f3ed4c..2a0879f51 100644 --- a/Tests/Functional/Api/PageViewProxyTest.php +++ b/Tests/Functional/Api/PageViewProxyTest.php @@ -22,7 +22,9 @@ class PageViewProxyTest extends FunctionalTestCase protected function getDlfConfiguration() { return array_merge(parent::getDlfConfiguration(), [ - 'enableInternalProxy' => true, + 'general' => [ + 'enableInternalProxy' => true + ] ]); } diff --git a/Tests/Functional/Common/MetsDocumentTest.php b/Tests/Functional/Common/MetsDocumentTest.php index 49b3b8fa7..07df0098b 100644 --- a/Tests/Functional/Common/MetsDocumentTest.php +++ b/Tests/Functional/Common/MetsDocumentTest.php @@ -29,7 +29,7 @@ public function setUp(): void protected function doc(string $file) { $url = 'http://web:8001/Tests/Fixtures/MetsDocument/' . $file; - $doc = AbstractDocument::getInstance($url, ['useExternalApisForMetadata' => 0]); + $doc = AbstractDocument::getInstance($url, ['general' => ['useExternalApisForMetadata' => 0]]); self::assertNotNull($doc); return $doc; } diff --git a/Tests/Functional/FunctionalTestCase.php b/Tests/Functional/FunctionalTestCase.php index 966474d71..7c3b021bb 100644 --- a/Tests/Functional/FunctionalTestCase.php +++ b/Tests/Functional/FunctionalTestCase.php @@ -126,39 +126,47 @@ protected function getDlfConfiguration() $dotenv->load(); return [ - 'useExternalApisForMetadata' => 0, - 'fileGrpImages' => 'DEFAULT,MAX', - 'fileGrpThumbs' => 'THUMBS', - 'fileGrpDownload' => 'DOWNLOAD', - 'fileGrpFulltext' => 'FULLTEXT', - 'fileGrpAudio' => 'AUDIO', - 'solrFieldAutocomplete' => 'autocomplete', - 'solrFieldCollection' => 'collection', - 'solrFieldDefault' => 'default', - 'solrFieldFulltext' => 'fulltext', - 'solrFieldGeom' => 'geom', - 'solrFieldId' => 'id', - 'solrFieldLicense' => 'license', - 'solrFieldLocation' => 'location', - 'solrFieldPage' => 'page', - 'solrFieldPartof' => 'partof', - 'solrFieldPid' => 'pid', - 'solrFieldPurl' => 'purl', - 'solrFieldRecordId' => 'record_id', - 'solrFieldRestrictions' => 'restrictions', - 'solrFieldRoot' => 'root', - 'solrFieldSid' => 'sid', - 'solrFieldTerms' => 'terms', - 'solrFieldThumbnail' => 'thumbnail', - 'solrFieldTimestamp' => 'timestamp', - 'solrFieldTitle' => 'title', - 'solrFieldToplevel' => 'toplevel', - 'solrFieldType' => 'type', - 'solrFieldUid' => 'uid', - 'solrFieldUrn' => 'urn', - 'solrFieldVolume' => 'volume', - - 'solrHost' => getenv('dlfTestingSolrHost'), + 'general' => [ + 'useExternalApisForMetadata' => 0, + 'requiredMetadataFields' => 'document_format' + ], + 'files' => [ + 'fileGrpImages' => 'DEFAULT,MAX', + 'fileGrpThumbs' => 'THUMBS', + 'fileGrpDownload' => 'DOWNLOAD', + 'fileGrpFulltext' => 'FULLTEXT', + 'fileGrpAudio' => 'AUDIO' + ], + 'solr' => [ + 'host' => getenv('dlfTestingSolrHost'), + 'fields' => [ + 'autocomplete' => 'autocomplete', + 'collection' => 'collection', + 'default' => 'default', + 'fulltext' => 'fulltext', + 'geom' => 'geom', + 'id' => 'id', + 'license' => 'license', + 'location' => 'location', + 'page' => 'page', + 'partof' => 'partof', + 'pid' => 'pid', + 'purl' => 'purl', + 'recordId' => 'record_id', + 'restrictions' => 'restrictions', + 'root' => 'root', + 'sid' => 'sid', + 'terms' => 'terms', + 'thumbnail' => 'thumbnail', + 'timestamp' => 'timestamp', + 'title' => 'title', + 'toplevel' => 'toplevel', + 'type' => 'type', + 'uid' => 'uid', + 'urn' => 'urn', + 'volume' => 'volume' + ] + ] ]; } diff --git a/Tests/Unit/Validation/DocumentValidatorTest.php b/Tests/Unit/Validation/DocumentValidatorTest.php new file mode 100644 index 000000000..8e77d2925 --- /dev/null +++ b/Tests/Unit/Validation/DocumentValidatorTest.php @@ -0,0 +1,112 @@ + + * + * 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\Tests\Unit\Common; + +use Kitodo\Dlf\Validation\DocumentValidator; +use SimpleXMLElement; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +class DocumentValidatorTest extends UnitTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $this->resetSingletonInstances = true; + } + + /** + * @test + */ + public function passesHasAllMandatoryMetadataFields() + { + $metadata = [ + 'record_id' => [ + 'xyz' + ] + ]; + $documentValidator = new DocumentValidator($metadata, $this->getRequiredMetadataFields()); + self::assertTrue($documentValidator->hasAllMandatoryMetadataFields()); + } + + /** + * @test + */ + public function notPassesHasAllMandatoryMetadataFields() + { + $metadata = [ + 'document_format' => [ + 'METS' + ] + ]; + $documentValidator = new DocumentValidator($metadata, $this->getRequiredMetadataFields()); + self::assertFalse($documentValidator->hasAllMandatoryMetadataFields()); + } + + /** + * @test + */ + public function passesHasCorrectLogicalStructure() + { + $xml = $this->getXml('av_beispiel.xml'); + + $documentValidator = new DocumentValidator([], [], $xml); + self::assertTrue($documentValidator->hasCorrectLogicalStructure('advertisement')); + } + + /** + * @test + */ + public function notPassesHasCorrectLogicalStructure() + { + $xml = $this->getXml('av_beispiel.xml'); + + $documentValidator = new DocumentValidator([], [], $xml); + self::assertFalse($documentValidator->hasCorrectLogicalStructure('newspaper')); + } + + /** + * @test + */ + public function passesHasCorrectPhysicalStructure() + { + $xml = $this->getXml('av_beispiel.xml'); + + $documentValidator = new DocumentValidator([], [], $xml); + self::assertTrue($documentValidator->hasCorrectPhysicalStructure()); + } + + /** + * @test + */ + public function notPassesHasCorrectPhysicalStructure() + { + $xml = $this->getXml('two_dmdsec.xml'); + + $documentValidator = new DocumentValidator([], [], $xml); + self::assertFalse($documentValidator->hasCorrectPhysicalStructure()); + } + + private function getRequiredMetadataFields(): array + { + return [ + 'record_id' + ]; + } + + private function getXml(string $file): SimpleXMLElement + { + $xml = simplexml_load_file(__DIR__ . '/../../Fixtures/MetsDocument/' . $file); + self::assertNotFalse($xml); + return $xml; + } +} diff --git a/composer.json b/composer.json index 61c3a9697..3b4543ed0 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "typo3/cms-extbase": "^10.4.37|^11.5.37", "typo3/cms-tstemplate": "^10.4.37|^11.5.37", "caseyamcl/phpoaipmh": "^3.3.1", + "slub/php-mods-reader": "^0.3.0", "ubl/php-iiif-prezi-reader": "0.3.0", "solarium/solarium": "5.2 - 6.3" }, diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 7f96ebe0e..484353b12 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -1,102 +1,104 @@ -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.enableInternalProxy -enableInternalProxy = 0 -# cat=Basic; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.useragent -useragent = Kitodo.Presentation -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.forceAbsoluteUrl -forceAbsoluteUrl = 0 -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.forceAbsoluteUrlHttps -forceAbsoluteUrlHttps = 0 -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.caching -caching = 0 -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.publishNewCollections -publishNewCollections = 1 -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.unhideOnIndex -unhideOnIndex = 0 -# cat=Basic; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.useExternalApisForMetadata -useExternalApisForMetadata = 0 -# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.fileGrpImages -fileGrpImages = DEFAULT,MAX -# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.fileGrpThumbs -fileGrpThumbs = THUMBS -# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.fileGrpDownload -fileGrpDownload = DOWNLOAD -# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.fileGrpFulltext -fileGrpFulltext = FULLTEXT -# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.fileGrpAudio -fileGrpAudio = AUDIO -# cat=IIIF; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.indexAnnotations -indexAnnotations = 0 -# cat=IIIF; type=int[1-2000]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiifThumbnailWidth -iiifThumbnailWidth = 150 -# cat=IIIF; type=int[1-2000]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiifThumbnailHeight -iiifThumbnailHeight = 150 -# cat=Solr; type=user[Kitodo\Dlf\Hooks\ConfigurationForm->checkSolrConnection]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrConnect -solrConnect = 0 -# cat=Solr; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrHttps -solrHttps = 0 -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrHost -solrHost = localhost -# cat=Solr; type=int[0-65535]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrPort -solrPort = 8983 -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrPath -solrPath = / -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrUser -solrUser = -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrPass -solrPass = -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrTimeout -solrTimeout = 10 -# cat=Solr; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrAllowCoreDelete -solrAllowCoreDelete = 0 -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldId -solrFieldId = id -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldUid -solrFieldUid = uid -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldPid -solrFieldPid = pid -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldPage -solrFieldPage = page -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldPartof -solrFieldPartof = partof -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldRoot -solrFieldRoot = root -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldSid -solrFieldSid = sid -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldToplevel -solrFieldToplevel = toplevel -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldType -solrFieldType = type -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldTitle -solrFieldTitle = title -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldVolume -solrFieldVolume = volume -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldDate -solrFieldDate = date -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldThumbnail -solrFieldThumbnail = thumbnail -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldDefault -solrFieldDefault = default -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldTimestamp -solrFieldTimestamp = timestamp -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldAutocomplete -solrFieldAutocomplete = autocomplete -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldFulltext -solrFieldFulltext = fulltext -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldRecordId -solrFieldRecordId = record_id -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldPurl -solrFieldPurl = purl -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldUrn -solrFieldUrn = urn -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldLocation -solrFieldLocation = location -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldCollection -solrFieldCollection = collection -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldLicense -solrFieldLicense = license -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldTerms -solrFieldTerms = terms -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldRestrictions -solrFieldRestrictions = restrictions -# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solrFieldGeom -solrFieldGeom = geom +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.enableInternalProxy +general.enableInternalProxy = 0 +# cat=General; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.userAgent +general.userAgent = Kitodo.Presentation +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.forceAbsoluteUrl +general.forceAbsoluteUrl = 0 +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.forceAbsoluteUrlHttps +general.forceAbsoluteUrlHttps = 0 +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.caching +general.caching = 0 +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.publishNewCollections +general.publishNewCollections = 1 +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.unhideOnIndex +general.unhideOnIndex = 0 +# cat=General; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.useExternalApisForMetadata +general.useExternalApisForMetadata = 0 +# cat=General; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.general.requiredMetadataFields +general.requiredMetadataFields = document_format,record_id +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.fileGrpImages +files.fileGrpImages = DEFAULT,MAX +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.fileGrpThumbs +files.fileGrpThumbs = THUMBS +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.fileGrpDownload +files.fileGrpDownload = DOWNLOAD +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.fileGrpFulltext +files.fileGrpFulltext = FULLTEXT +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.fileGrpAudio +files.fileGrpAudio = AUDIO +# cat=IIIF; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiif.indexAnnotations +iiif.indexAnnotations = 0 +# cat=IIIF; type=int[1-2000]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiif.thumbnailWidth +iiif.thumbnailWidth = 150 +# cat=IIIF; type=int[1-2000]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiif.thumbnailHeight +iiif.thumbnailHeight = 150 +# cat=Solr; type=user[Kitodo\Dlf\Hooks\ConfigurationForm->checkSolrConnection]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.connect +solr.connect = 0 +# cat=Solr; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.https +solr.https = 0 +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.host +solr.host = localhost +# cat=Solr; type=int[0-65535]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.port +solr.port = 8983 +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.path +solr.path = / +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.user +solr.user = +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.pass +solr.pass = +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.timeout +solr.timeout = 10 +# cat=Solr; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.allowCoreDelete +solr.allowCoreDelete = 0 +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.id +solr.fields.id = id +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.uid +solr.fields.uid = uid +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.pid +solr.fields.pid = pid +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.page +solr.fields.page = page +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.partof +solr.fields.partof = partof +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.root +solr.fields.root = root +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.sid +solr.fields.sid = sid +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.toplevel +solr.fields.toplevel = toplevel +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.type +solr.fields.type = type +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.title +solr.fields.title = title +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.volume +solr.fields.volume = volume +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.date +solr.fields.date = date +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.thumbnail +solr.fields.thumbnail = thumbnail +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.default +solr.fields.default = default +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.timestamp +solr.fields.timestamp = timestamp +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.autocomplete +solr.fields.autocomplete = autocomplete +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.fulltext +solr.fields.fulltext = fulltext +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.recordId +solr.fields.recordId = record_id +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.purl +solr.fields.purl = purl +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.urn +solr.fields.urn = urn +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.location +solr.fields.location = location +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.collection +solr.fields.collection = collection +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.license +solr.fields.license = license +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.terms +solr.fields.terms = terms +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.restrictions +solr.fields.restrictions = restrictions +# cat=Solr; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.solr.fields.geom +solr.fields.geom = geom diff --git a/ext_localconf.php b/ext_localconf.php index 6ad9b5f46..fe7a5367d 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -78,11 +78,11 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'][] = \Kitodo\Dlf\Hooks\DataHandler::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['dlf/Classes/Common/MetsDocument.php']['hookClass'][] = \Kitodo\Dlf\Hooks\KitodoProductionHacks::class; // Register AJAX eID handlers. -if ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['dlf']['enableInternalProxy'] ?? false) { +if ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['dlf']['general']['enableInternalProxy'] ?? false) { $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['tx_dlf_pageview_proxy'] = \Kitodo\Dlf\Eid\PageViewProxy::class . '::main'; } // Use Caching Framework for Solr queries -if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr'])) { +if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr']) || !is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr'])) { $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr'] = []; } if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr']['backend'])) { @@ -92,7 +92,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_solr']['options']['defaultLifeTime'] = 86400; // 86400 seconds = 1 day } // Use Caching Framework for XML file caching -if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_doc'])) { +if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_doc']) || !is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_doc'])) { $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_doc'] = []; } if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['tx_dlf_doc']['backend'])) {