diff --git a/Classes/Command/HarvestCommand.php b/Classes/Command/HarvestCommand.php index f4ae394f7..5ddb9edc2 100644 --- a/Classes/Command/HarvestCommand.php +++ b/Classes/Command/HarvestCommand.php @@ -127,15 +127,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Abort if solrCoreUid is empty or not in the array of allowed solr cores. if (empty($solrCoreUid) || !in_array($solrCoreUid, $allSolrCores)) { - $output_solrCores = []; - foreach ($allSolrCores as $index_name => $uid) { - $output_solrCores[] = $uid . ' : ' . $index_name; + $outputSolrCores = []; + foreach ($allSolrCores as $indexName => $uid) { + $outputSolrCores[] = $uid . ' : ' . $indexName; } - if (empty($output_solrCores)) { + if (empty($outputSolrCores)) { $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. No valid cores found on PID ' . $this->storagePid . ".\n"); return BaseCommand::FAILURE; } else { - $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $output_solrCores) . "\n"); + $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $outputSolrCores) . "\n"); return BaseCommand::FAILURE; } } diff --git a/Classes/Command/IndexCommand.php b/Classes/Command/IndexCommand.php index 6120b0ed8..1a392e2e1 100644 --- a/Classes/Command/IndexCommand.php +++ b/Classes/Command/IndexCommand.php @@ -111,15 +111,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Abort if solrCoreUid is empty or not in the array of allowed solr cores. if (empty($solrCoreUid) || !in_array($solrCoreUid, $allSolrCores)) { - $output_solrCores = []; - foreach ($allSolrCores as $index_name => $uid) { - $output_solrCores[] = $uid . ' : ' . $index_name; + $outputSolrCores = []; + foreach ($allSolrCores as $indexName => $uid) { + $outputSolrCores[] = $uid . ' : ' . $indexName; } - if (empty($output_solrCores)) { + if (empty($outputSolrCores)) { $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. No valid cores found on PID ' . $this->storagePid . ".\n"); return BaseCommand::FAILURE; } else { - $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $output_solrCores) . "\n"); + $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $outputSolrCores) . "\n"); return BaseCommand::FAILURE; } } diff --git a/Classes/Command/ReindexCommand.php b/Classes/Command/ReindexCommand.php index 177cf923c..828c26c37 100644 --- a/Classes/Command/ReindexCommand.php +++ b/Classes/Command/ReindexCommand.php @@ -127,15 +127,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Abort if solrCoreUid is empty or not in the array of allowed solr cores. if (empty($solrCoreUid) || !in_array($solrCoreUid, $allSolrCores)) { - $output_solrCores = []; - foreach ($allSolrCores as $index_name => $uid) { - $output_solrCores[] = $uid . ' : ' . $index_name; + $outputSolrCores = []; + foreach ($allSolrCores as $indexName => $uid) { + $outputSolrCores[] = $uid . ' : ' . $indexName; } - if (empty($output_solrCores)) { + if (empty($outputSolrCores)) { $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. No valid cores found on PID ' . $this->storagePid . ".\n"); return BaseCommand::FAILURE; } else { - $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $output_solrCores) . "\n"); + $io->error('ERROR: No valid Solr core ("' . $input->getOption('solr') . '") given. ' . "Valid cores are (:):\n" . implode("\n", $outputSolrCores) . "\n"); return BaseCommand::FAILURE; } } diff --git a/Classes/Common/Helper.php b/Classes/Common/Helper.php index 8ca41ea5a..4a5a76182 100644 --- a/Classes/Common/Helper.php +++ b/Classes/Common/Helper.php @@ -12,6 +12,7 @@ namespace Kitodo\Dlf\Common; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Http\Uri; @@ -207,7 +208,7 @@ public static function decrypt(string $encrypted) self::log('OpenSSL library doesn\'t support cipher and/or hash algorithm', LOG_SEVERITY_ERROR); return false; } - if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) { + if (empty(self::getEncryptionKey())) { self::log('No encryption key set in TYPO3 configuration', LOG_SEVERITY_ERROR); return false; } @@ -222,7 +223,7 @@ public static function decrypt(string $encrypted) $binary = base64_decode($encrypted); $iv = substr($binary, 0, openssl_cipher_iv_length(self::$cipherAlgorithm)); $data = substr($binary, openssl_cipher_iv_length(self::$cipherAlgorithm)); - $key = openssl_digest($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], self::$hashAlgorithm, true); + $key = openssl_digest(self::getEncryptionKey(), self::$hashAlgorithm, true); // Decrypt data. return openssl_decrypt($data, self::$cipherAlgorithm, $key, OPENSSL_RAW_DATA, $iv); } @@ -342,13 +343,13 @@ public static function encrypt(string $string) self::log('OpenSSL library doesn\'t support cipher and/or hash algorithm', LOG_SEVERITY_ERROR); return false; } - if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) { + if (empty(self::getEncryptionKey())) { self::log('No encryption key set in TYPO3 configuration', LOG_SEVERITY_ERROR); return false; } // Generate random initialization vector. $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(self::$cipherAlgorithm)); - $key = openssl_digest($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], self::$hashAlgorithm, true); + $key = openssl_digest(self::getEncryptionKey(), self::$hashAlgorithm, true); // Encrypt data. $encrypted = openssl_encrypt($string, self::$cipherAlgorithm, $key, OPENSSL_RAW_DATA, $iv); // Merge initialization vector and encrypted data. @@ -358,23 +359,6 @@ public static function encrypt(string $string) return $encrypted; } - /** - * Get the unqualified name of a class - * - * @access public - * - * @static - * - * @param string $qualifiedClassName The qualified class name from get_class() - * - * @return string The unqualified class name - */ - public static function getUnqualifiedClassName(string $qualifiedClassName): string - { - $nameParts = explode('\\', $qualifiedClassName); - return end($nameParts); - } - /** * Clean up a string to use in an URL. * @@ -412,8 +396,8 @@ public static function getCleanString(string $string): string public static function getHookObjects(string $scriptRelPath): array { $hookObjects = []; - if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey . '/' . $scriptRelPath]['hookClass'])) { - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::$extKey . '/' . $scriptRelPath]['hookClass'] as $classRef) { + if (is_array(self::getOptions()[self::$extKey . '/' . $scriptRelPath]['hookClass'])) { + foreach (self::getOptions()[self::$extKey . '/' . $scriptRelPath]['hookClass'] as $classRef) { $hookObjects[] = GeneralUtility::makeInstance($classRef); } } @@ -556,94 +540,6 @@ public static function getDocumentStructures(int $pid = -1): array return array_column($allStructures, 'indexName', 'uid'); } - /** - * Get the URN of an object - * @see http://www.persistent-identifier.de/?link=316 - * - * @access public - * - * @static - * - * @param string $base The namespace and base URN - * @param string $id The object's identifier - * - * @return string Uniform Resource Name as string - */ - public static function getURN(string $base, string $id): string - { - $concordance = [ - '0' => 1, - '1' => 2, - '2' => 3, - '3' => 4, - '4' => 5, - '5' => 6, - '6' => 7, - '7' => 8, - '8' => 9, - '9' => 41, - 'a' => 18, - 'b' => 14, - 'c' => 19, - 'd' => 15, - 'e' => 16, - 'f' => 21, - 'g' => 22, - 'h' => 23, - 'i' => 24, - 'j' => 25, - 'k' => 42, - 'l' => 26, - 'm' => 27, - 'n' => 13, - 'o' => 28, - 'p' => 29, - 'q' => 31, - 'r' => 12, - 's' => 32, - 't' => 33, - 'u' => 11, - 'v' => 34, - 'w' => 35, - 'x' => 36, - 'y' => 37, - 'z' => 38, - '-' => 39, - ':' => 17, - ]; - $urn = strtolower($base . $id); - if (preg_match('/[^a-z\d:-]/', $urn)) { - self::log('Invalid chars in given parameters', LOG_SEVERITY_WARNING); - return ''; - } - $digits = ''; - for ($i = 0, $j = strlen($urn); $i < $j; $i++) { - $digits .= $concordance[substr($urn, $i, 1)]; - } - $checksum = 0; - for ($i = 0, $j = strlen($digits); $i < $j; $i++) { - $checksum += ($i + 1) * (int) substr($digits, $i, 1); - } - $checksum = substr((string) floor($checksum / (int) substr($digits, -1, 1)), -1, 1); - return $base . $id . $checksum; - } - - /** - * Check if given ID is a valid Pica Production Number (PPN) - * - * @access public - * - * @static - * - * @param string $id The identifier to check - * - * @return bool Is $id a valid PPN? - */ - public static function isPPN(string $id): bool - { - return self::checkIdentifier($id, 'PPN'); - } - /** * Determine whether or not $url is a valid URL using HTTP or HTTPS scheme. * @@ -670,28 +566,6 @@ public static function isValidHttpUrl(string $url): bool } } - /** - * Merges two arrays recursively and actually returns the modified array. - * @see ArrayUtility::mergeRecursiveWithOverrule() - * - * @access public - * - * @static - * - * @param array $original Original array - * @param array $overrule Overrule array, overruling the original array - * @param bool $addKeys If set to false, keys that are not found in $original will not be set - * @param bool $includeEmptyValues If set, values from $overrule will overrule if they are empty - * @param bool $enableUnsetFeature If set, special value "__UNSET" can be used in the overrule array to unset keys in the original array - * - * @return array Merged array - */ - public static function mergeRecursiveWithOverrule(array $original, array $overrule, bool $addKeys = true, bool $includeEmptyValues = true, bool $enableUnsetFeature = true): array - { - ArrayUtility::mergeRecursiveWithOverrule($original, $overrule, $addKeys, $includeEmptyValues, $enableUnsetFeature); - return $original; - } - /** * Process a data and/or command map with TYPO3 core engine as admin. * @@ -1018,4 +892,49 @@ public static function isValidXmlId($id): bool { return preg_match('/^[_a-z][_a-z0-9-.]*$/i', $id) === 1; } + + /** + * Get options from local configuration. + * + * @access private + * + * @static + * + * @return array + */ + private static function getOptions(): array + { + return self::getLocalConfigurationByPath('SC_OPTIONS'); + } + + /** + * Get encryption key from local configuration. + * + * @access private + * + * @static + * + * @return string|null + */ + private static function getEncryptionKey(): ?string + { + return self::getLocalConfigurationByPath('SYS/encryptionKey'); + } + + /** + * Get local configuration for given path. + * + * @access private + * + * @static + * + * @param string $path + * + * @return mixed + */ + private static function getLocalConfigurationByPath(string $path) + { + $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class); + return $configurationManager->getLocalConfigurationValueByPath($path); + } } diff --git a/Classes/Common/MetsDocument.php b/Classes/Common/MetsDocument.php index 31f1ab50e..455d7343a 100644 --- a/Classes/Common/MetsDocument.php +++ b/Classes/Common/MetsDocument.php @@ -12,6 +12,9 @@ namespace Kitodo\Dlf\Common; +use \DOMElement; +use \DOMXPath; +use \SimpleXMLElement; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; @@ -57,14 +60,14 @@ * @property-read string $thumbnail this holds the document's thumbnail location * @property bool $thumbnailLoaded flag with information if the thumbnail is loaded * @property-read string $toplevelId this holds the toplevel structure's "@ID" (METS) or the manifest's "@id" (IIIF) - * @property \SimpleXMLElement $xml this holds the whole XML file as \SimpleXMLElement object + * @property SimpleXMLElement $xml this holds the whole XML file as SimpleXMLElement object * @property-read array $mdSec associative array of METS metadata sections indexed by their IDs. * @property bool $mdSecLoaded flag with information if the array of METS metadata sections is loaded * @property-read array $dmdSec subset of `$mdSec` storing only the dmdSec entries; kept for compatibility. * @property-read array $fileGrps this holds the file ID -> USE concordance * @property bool $fileGrpsLoaded flag with information if file groups array is loaded * @property-read array $fileInfos additional information about files (e.g., ADMID), indexed by ID. - * @property-read \SimpleXMLElement $mets this holds the XML file's METS part as \SimpleXMLElement object + * @property-read SimpleXMLElement $mets this holds the XML file's METS part as SimpleXMLElement object * @property-read string $parentHref URL of the parent document (determined via mptr element), or empty string if none is available */ final class MetsDocument extends AbstractDocument @@ -130,9 +133,9 @@ final class MetsDocument extends AbstractDocument /** * @access protected - * @var \SimpleXMLElement This holds the XML file's METS part as \SimpleXMLElement object + * @var SimpleXMLElement This holds the XML file's METS part as SimpleXMLElement object */ - protected \SimpleXMLElement $mets; + protected SimpleXMLElement $mets; /** * @access protected @@ -352,12 +355,12 @@ public function getLogicalStructure(string $id, bool $recursive = false): array * * @access protected * - * @param \SimpleXMLElement $structure The logical structure node + * @param SimpleXMLElement $structure The logical structure node * @param bool $recursive Whether to include the child elements * * @return array Array of the element's id, label, type and physical page indexes/mptr link */ - protected function getLogicalStructureInfo(\SimpleXMLElement $structure, bool $recursive = false): array + protected function getLogicalStructureInfo(SimpleXMLElement $structure, bool $recursive = false): array { $attributes = $structure->attributes(); @@ -396,11 +399,61 @@ protected function getLogicalStructureInfo(\SimpleXMLElement $structure, bool $r $this->magicGetSmLinks(); // Load physical structure. $this->magicGetPhysicalStructure(); - // Get the physical page or external file this structure element is pointing at. - // Is there a mptr node? - if (count($structure->children('http://www.loc.gov/METS/')->mptr)) { + + $this->getPage($details, $structure->children('http://www.loc.gov/METS/')->mptr); + $this->getFiles($details, $structure->children('http://www.loc.gov/METS/')->fptr); + + // Keep for later usage. + $this->logicalUnits[$details['id']] = $details; + // Walk the structure recursively? And are there any children of the current element? + if ( + $recursive + && count($structure->children('http://www.loc.gov/METS/')->div) + ) { + $details['children'] = []; + foreach ($structure->children('http://www.loc.gov/METS/')->div as $child) { + // Repeat for all children. + $details['children'][] = $this->getLogicalStructureInfo($child, true); + } + } + return $details; + } + + /** + * Get the files this structure element is pointing at. + * + * @param ?SimpleXMLElement $filePointers + * + * @return void + */ + private function getFiles(array &$details, ?SimpleXMLElement $filePointers): void + { + $fileUse = $this->magicGetFileGrps(); + // Get the file representations from fileSec node. + foreach ($filePointers as $filePointer) { + $fileId = (string) $filePointer->attributes()->FILEID; + // Check if file has valid @USE attribute. + if (!empty($fileUse[$fileId])) { + $details['files'][$fileUse[$fileId]] = $fileId; + } + } + } + + /** + * Get the physical page or external file this structure element is pointing at. + * + * @access private + * + * @param array $details passed as reference + * @param ?SimpleXMLElement $metsPointers + * + * @return void + */ + private function getPage(array &$details, ?SimpleXMLElement $metsPointers): void + { + if (count($metsPointers)) { // Yes. Get the file reference. - $details['points'] = (string) $structure->children('http://www.loc.gov/METS/')->mptr[0]->attributes('http://www.w3.org/1999/xlink')->href; + $details['points'] = (string) $metsPointers[0]->attributes('http://www.w3.org/1999/xlink')->href; } elseif ( !empty($this->physicalStructure) && array_key_exists($details['id'], $this->smLinks['l2p']) @@ -418,29 +471,6 @@ protected function getLogicalStructureInfo(\SimpleXMLElement $structure, bool $r if ($details['thumbnailId'] === null) { unset($details['thumbnailId']); } - // Get the files this structure element is pointing at. - $fileUse = $this->magicGetFileGrps(); - // Get the file representations from fileSec node. - foreach ($structure->children('http://www.loc.gov/METS/')->fptr as $fptr) { - // Check if file has valid @USE attribute. - if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) { - $details['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID; - } - } - // Keep for later usage. - $this->logicalUnits[$details['id']] = $details; - // Walk the structure recursively? And are there any children of the current element? - if ( - $recursive - && count($structure->children('http://www.loc.gov/METS/')->div) - ) { - $details['children'] = []; - foreach ($structure->children('http://www.loc.gov/METS/')->div as $child) { - // Repeat for all children. - $details['children'][] = $this->getLogicalStructureInfo($child, true); - } - } - return $details; } /** @@ -557,8 +587,8 @@ private function processMetadataSections(string $id, int $cPid, array $metadata) // There is no metadata section for this structure node. return []; } - // Associative array used as set of available section types (dmdSec, techMD, ...) - $hasMetadataSection = []; + // Array used as set of available section types (dmdSec, techMD, ...) + $metadataSections = []; // Load available metadata formats and metadata sections. $this->loadFormats(); $this->magicGetMdSec(); @@ -568,19 +598,19 @@ private function processMetadataSections(string $id, int $cPid, array $metadata) foreach ($mdIds as $dmdId) { $mdSectionType = $this->mdSec[$dmdId]['section']; - if ($mdSectionType === 'dmdSec' && isset($hasMetadataSection['dmdSec'])) { + if ($this->hasMetadataSection($metadataSections, $mdSectionType, 'dmdSec')) { continue; } - if (!$this->extractAndProcessMetadata($dmdId, $mdSectionType, $metadata, $cPid, $hasMetadataSection)) { + if (!$this->extractAndProcessMetadata($dmdId, $mdSectionType, $metadata, $cPid, $metadataSections)) { continue; } - $hasMetadataSection[$mdSectionType] = true; + $metadataSections[] = $mdSectionType; } // Files are not expected to reference a dmdSec - if (isset($this->fileInfos[$id]) || isset($hasMetadataSection['dmdSec'])) { + if (isset($this->fileInfos[$id]) || in_array('dmdSec', $metadataSections)) { return $metadata; } else { $this->logger->warning('No supported descriptive metadata found for logical structure with @ID "' . $id . '"'); @@ -619,13 +649,13 @@ private function getLogicalUnitType(string $id): array * @param string $mdSectionType * @param array $metadata * @param integer $cPid - * @param array $hasMetadataSection + * @param array $metadataSections * * @return boolean */ - private function extractAndProcessMetadata(string $dmdId, string $mdSectionType, array &$metadata, int $cPid, array $hasMetadataSection): bool + private function extractAndProcessMetadata(string $dmdId, string $mdSectionType, array &$metadata, int $cPid, array $metadataSections): bool { - if ($mdSectionType === 'dmdSec' && isset($hasMetadataSection['dmdSec'])) { + if ($this->hasMetadataSection($metadataSections, $mdSectionType, 'dmdSec')) { return true; } @@ -638,7 +668,7 @@ private function extractAndProcessMetadata(string $dmdId, string $mdSectionType, $additionalMetadata = $this->getAdditionalMetadataFromDatabase($cPid, $dmdId); // We need a \DOMDocument here, because SimpleXML doesn't support XPath functions properly. $domNode = dom_import_simplexml($this->mdSec[$dmdId]['xml']); - $domXPath = new \DOMXPath($domNode->ownerDocument); + $domXPath = new DOMXPath($domNode->ownerDocument); $this->registerNamespaces($domXPath); $this->processAdditionalMetadata($additionalMetadata, $domXPath, $domNode, $metadata); @@ -646,19 +676,35 @@ private function extractAndProcessMetadata(string $dmdId, string $mdSectionType, return true; } + /** + * Check if searched metadata section is stored in the array. + * + * @access private + * + * @param array $metadataSections + * @param string $currentMetadataSection + * @param string $searchedMetadataSection + * + * @return boolean + */ + private function hasMetadataSection(array $metadataSections, string $currentMetadataSection, string $searchedMetadataSection): bool + { + return $currentMetadataSection === $searchedMetadataSection && in_array($searchedMetadataSection, $metadataSections); + } + /** * Process additional metadata. * * @access private * * @param array $additionalMetadata - * @param \DOMXPath $domXPath - * @param \DOMElement $domNode + * @param DOMXPath $domXPath + * @param DOMElement $domNode * @param array $metadata * * @return void */ - private function processAdditionalMetadata(array $additionalMetadata, \DOMXPath $domXPath, \DOMElement $domNode, array &$metadata): void + private function processAdditionalMetadata(array $additionalMetadata, DOMXPath $domXPath, DOMElement $domNode, array &$metadata): void { foreach ($additionalMetadata as $resArray) { $this->setMetadataFieldValues($resArray, $domXPath, $domNode, $metadata); @@ -673,13 +719,13 @@ private function processAdditionalMetadata(array $additionalMetadata, \DOMXPath * @access private * * @param array $resArray - * @param \DOMXPath $domXPath - * @param \DOMElement $domNode + * @param DOMXPath $domXPath + * @param DOMElement $domNode * @param array $metadata * * @return void */ - private function setMetadataFieldValues(array $resArray, \DOMXPath $domXPath, \DOMElement $domNode, array &$metadata): void + private function setMetadataFieldValues(array $resArray, DOMXPath $domXPath, DOMElement $domNode, array &$metadata): void { if ($resArray['format'] > 0 && !empty($resArray['xpath'])) { $values = $domXPath->evaluate($resArray['xpath'], $domNode); @@ -717,13 +763,13 @@ private function setDefaultMetadataValue(array $resArray, array &$metadata): voi * @access private * * @param array $resArray - * @param \DOMXPath $domXPath - * @param \DOMElement $domNode + * @param $domXPath + * @param DOMElement $domNode * @param array $metadata * * @return void */ - private function setSortableMetadataValue(array $resArray, \DOMXPath $domXPath, \DOMElement $domNode, array &$metadata): void + private function setSortableMetadataValue(array $resArray, DOMXPath $domXPath, DOMElement $domNode, array &$metadata): void { if (!empty($metadata[$resArray['index_name']]) && $resArray['is_sortable']) { if ($resArray['format'] > 0 && !empty($resArray['xpath_sorting'])) { @@ -1030,7 +1076,7 @@ protected function ensureHasFulltextIsSet(): void protected function setPreloadedDocument($preloadedDocument): bool { - if ($preloadedDocument instanceof \SimpleXMLElement) { + if ($preloadedDocument instanceof SimpleXMLElement) { $this->xml = $preloadedDocument; return true; } @@ -1040,7 +1086,7 @@ protected function setPreloadedDocument($preloadedDocument): bool /** * @see AbstractDocument::getDocument() */ - protected function getDocument(): \SimpleXMLElement + protected function getDocument(): SimpleXMLElement { return $this->mets; } @@ -1113,11 +1159,11 @@ protected function magicGetDmdSec(): array * * @access protected * - * @param \SimpleXMLElement $element + * @param SimpleXMLElement $element * * @return array|null The processed metadata section */ - protected function processMdSec(\SimpleXMLElement $element): ?array + protected function processMdSec(SimpleXMLElement $element): ?array { $mdId = (string) $element->attributes()->ID; if (empty($mdId)) { @@ -1232,9 +1278,9 @@ protected function prepareMetadataArray(int $cPid): void * * @access protected * - * @return \SimpleXMLElement The XML's METS part as \SimpleXMLElement object + * @return SimpleXMLElement The XML's METS part as SimpleXMLElement object */ - protected function magicGetMets(): \SimpleXMLElement + protected function magicGetMets(): SimpleXMLElement { return $this->mets; } @@ -1263,70 +1309,109 @@ protected function magicGetPhysicalStructure(): array $this->physicalStructureInfo[$id]['orderlabel'] = isset($firstNode['ORDERLABEL']) ? (string) $firstNode['ORDERLABEL'] : ''; $this->physicalStructureInfo[$id]['type'] = (string) $firstNode['TYPE']; $this->physicalStructureInfo[$id]['contentIds'] = isset($firstNode['CONTENTIDS']) ? (string) $firstNode['CONTENTIDS'] : ''; - // Get the file representations from fileSec node. - foreach ($physNode[0]->children('http://www.loc.gov/METS/')->fptr as $fptr) { - $fileNode = isset($fptr->area)? $fptr->area : $fptr; - // Check if file has valid @USE attribute. - if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) { - $this->physicalStructureInfo[$id]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID; - } + $this->getFileRepresentation($id, $firstNode); + + $this->physicalStructure = $this->getPhysicalElements($elementNodes, $fileUse); + } + $this->physicalStructureLoaded = true; + + } + + return $this->physicalStructure; + } + + /** + * Get the file representations from fileSec node. + * + * @access private + * + * @param string $id + * @param SimpleXMLElement $physicalNode + * + * @return void + */ + private function getFileRepresentation(string $id, SimpleXMLElement $physicalNode): void + { + // Get file groups. + $fileUse = $this->magicGetFileGrps(); + + foreach ($physicalNode->children('http://www.loc.gov/METS/')->fptr as $fptr) { + $fileNode = isset($fptr->area)? $fptr->area : $fptr; + $fileId = (string) $fileNode->attributes()->FILEID; + + // Check if file has valid @USE attribute. + if (!empty($fileUse[$fileId])) { + $this->physicalStructureInfo[$id]['files'][$fileUse[$fileId]] = $fileId; + } + } + } + + /** + * Build the physical elements' array from the physical structMap node. + * + * @access private + * + * @param array $elementNodes + * @param array $fileUse + * + * @return array + */ + private function getPhysicalElements(array $elementNodes, array $fileUse): array + { + $elements = []; + $id = ''; + + foreach ($elementNodes as $elementNode) { + $id = (string) $elementNode['ID']; + $order = (int) $elementNode['ORDER']; + $elements[$order] = $id; + $this->physicalStructureInfo[$elements[$order]]['id'] = $id; + $this->physicalStructureInfo[$elements[$order]]['dmdId'] = isset($elementNode['DMDID']) ? (string) $elementNode['DMDID'] : ''; + $this->physicalStructureInfo[$elements[$order]]['admId'] = isset($elementNode['ADMID']) ? (string) $elementNode['ADMID'] : ''; + $this->physicalStructureInfo[$elements[$order]]['order'] = isset($elementNode['ORDER']) ? (string) $elementNode['ORDER'] : ''; + $this->physicalStructureInfo[$elements[$order]]['label'] = isset($elementNode['LABEL']) ? (string) $elementNode['LABEL'] : ''; + $this->physicalStructureInfo[$elements[$order]]['orderlabel'] = isset($elementNode['ORDERLABEL']) ? (string) $elementNode['ORDERLABEL'] : ''; + $this->physicalStructureInfo[$elements[$order]]['type'] = (string) $elementNode['TYPE']; + $this->physicalStructureInfo[$elements[$order]]['contentIds'] = isset($elementNode['CONTENTIDS']) ? (string) $elementNode['CONTENTIDS'] : ''; + // Get the file representations from fileSec node. + foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) { + $fileNode = isset($fptr->area)? $fptr->area : $fptr; + $fileId = (string) $fileNode->attributes()->FILEID; + + // Check if file has valid @USE attribute. + if (!empty($fileUse[(string) $fileId])) { + $this->physicalStructureInfo[$elements[$order]]['files'][$fileUse[$fileId]] = $fileId; } - // Build the physical elements' array from the physical structMap node. - $elements = []; - foreach ($elementNodes as $elementNode) { - $id = (string) $elementNode['ID']; - $order = (int) $elementNode['ORDER']; - $elements[$order] = $id; - $this->physicalStructureInfo[$elements[$order]]['id'] = $id; - $this->physicalStructureInfo[$elements[$order]]['dmdId'] = isset($elementNode['DMDID']) ? (string) $elementNode['DMDID'] : ''; - $this->physicalStructureInfo[$elements[$order]]['admId'] = isset($elementNode['ADMID']) ? (string) $elementNode['ADMID'] : ''; - $this->physicalStructureInfo[$elements[$order]]['order'] = isset($elementNode['ORDER']) ? (string) $elementNode['ORDER'] : ''; - $this->physicalStructureInfo[$elements[$order]]['label'] = isset($elementNode['LABEL']) ? (string) $elementNode['LABEL'] : ''; - $this->physicalStructureInfo[$elements[$order]]['orderlabel'] = isset($elementNode['ORDERLABEL']) ? (string) $elementNode['ORDERLABEL'] : ''; - $this->physicalStructureInfo[$elements[$order]]['type'] = (string) $elementNode['TYPE']; - $this->physicalStructureInfo[$elements[$order]]['contentIds'] = isset($elementNode['CONTENTIDS']) ? (string) $elementNode['CONTENTIDS'] : ''; - // Get the file representations from fileSec node. - foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) { - $fileNode = isset($fptr->area)? $fptr->area : $fptr; + } + // Get track info wtih begin end extent time for later assignment with musical + if ((string) $elementNode['TYPE'] === 'track') { + foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) { + if (isset($fptr->area) && ((string) $fptr->area->attributes()->BETYPE === 'TIME')) { // Check if file has valid @USE attribute. - if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) { - $this->physicalStructureInfo[$elements[$order]]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID; - } - } - - // Get track info wtih begin end extent time for later assignment with musical - if ((string) $elementNode['TYPE'] === 'track') { - foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) { - if (isset($fptr->area) && ((string) $fptr->area->attributes()->BETYPE === 'TIME')) { - // Check if file has valid @USE attribute. - if (!empty($fileUse[(string) $fptr->area->attributes()->FILEID])) { - $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['tracks'][$fileUse[(string) $fptr->area->attributes()->FILEID]] = [ - 'fileid' => (string)$fptr->area->attributes()->FILEID, - 'begin' => (string)$fptr->area->attributes()->BEGIN, - 'betype' => (string)$fptr->area->attributes()->BETYPE, - 'extent' => (string)$fptr->area->attributes()->EXTENT, - 'exttype' => (string)$fptr->area->attributes()->EXTTYPE, - ]; - } - } + if (!empty($fileUse[(string) $fptr->area->attributes()->FILEID])) { + $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['tracks'][$fileUse[(string) $fptr->area->attributes()->FILEID]] = [ + 'fileid' => (string)$fptr->area->attributes()->FILEID, + 'begin' => (string)$fptr->area->attributes()->BEGIN, + 'betype' => (string)$fptr->area->attributes()->BETYPE, + 'extent' => (string)$fptr->area->attributes()->EXTENT, + 'exttype' => (string)$fptr->area->attributes()->EXTTYPE, + ]; } } } - // Sort array by keys (= @ORDER). - ksort($elements); - // Set total number of pages/tracks. - $this->numPages = count($elements); - // Merge and re-index the array to get numeric indexes. - array_unshift($elements, $id); - $this->physicalStructure = $elements; } - $this->physicalStructureLoaded = true; - } - return $this->physicalStructure; + // Sort array by keys (= @ORDER). + ksort($elements); + // Set total number of pages/tracks. + $this->numPages = count($elements); + // Merge and re-index the array to get numeric indexes. + array_unshift($elements, $id); + + return $elements; } /** @@ -1484,7 +1569,7 @@ public function magicGetParentHref(): string */ public function __sleep(): array { - // \SimpleXMLElement objects can't be serialized, thus save the XML as string for serialization + // SimpleXMLElement objects can't be serialized, thus save the XML as string for serialization $this->asXML = $this->xml->asXML(); return ['pid', 'recordId', 'parentId', 'asXML']; } diff --git a/Classes/Controller/BasketController.php b/Classes/Controller/BasketController.php index e3e3ced84..2240cbd27 100644 --- a/Classes/Controller/BasketController.php +++ b/Classes/Controller/BasketController.php @@ -268,20 +268,18 @@ protected function getBasketData(): Basket * * @access protected * - * @param array $data DocumentData + * @param bool|null|object $data DocumentData * * @return array One basket entry */ - protected function getEntry(array $data): array + protected function getEntry($data): array { - // TODO: Call to function is_object() with array will always evaluate to false. - // @phpstan-ignore-next-line if (is_object($data)) { $data = get_object_vars($data); } $id = $data['id']; - $startpage = $data['startpage']; - $endpage = $data['endpage']; + $startPage = $data['startpage']; + $endPage = $data['endpage']; $startX = $data['startX']; $startY = $data['startY']; $endX = $data['endX']; @@ -290,9 +288,7 @@ protected function getEntry(array $data): array $docData = $this->getDocumentData((int) $id, $data); - $entryArray['BASKETDATA'] = $docData; - - $entryKey = $id . '_' . $startpage; + $entryKey = $id . '_' . $startPage; if (!empty($startX)) { $entryKey .= '_' . $startX; } @@ -300,10 +296,16 @@ protected function getEntry(array $data): array $entryKey .= '_' . $endX; } - $entryArray['id'] = $id; - $entryArray['CONTROLS'] = [ - 'startpage' => $startpage, - 'endpage' => $endpage, + $entry = [ + 'BASKETDATA' => $docData, + 'id' => $id, + 'NUMBER' => $docData['record_id'], + 'key' => $entryKey + ]; + + $entry['CONTROLS'] = [ + 'startpage' => $startPage, + 'endpage' => $endPage, 'startX' => $startX, 'startY' => $startY, 'endX' => $endX, @@ -311,11 +313,8 @@ protected function getEntry(array $data): array 'rotation' => $rotation, ]; - $entryArray['NUMBER'] = $docData['record_id']; - $entryArray['key'] = $entryKey; - // return one entry - return $entryArray; + return $entry; } /** diff --git a/Classes/Controller/CollectionController.php b/Classes/Controller/CollectionController.php index c44822b5c..ed1e2d44b 100644 --- a/Classes/Controller/CollectionController.php +++ b/Classes/Controller/CollectionController.php @@ -81,9 +81,7 @@ public function listAction(): void $this->logger->error('Apache Solr not available'); return; } - // We only care about the UID and partOf in the results and want them sorted - $params['fields'] = 'uid,partof'; - $params['sort'] = ['uid' => 'asc']; + $collections = []; // Sort collections according to order in plugin flexform configuration @@ -220,22 +218,29 @@ private function processCollections($collections, Solr $solr): array // Process results. foreach ($collections as $collection) { - $solr_query = ''; + $solrQuery = ''; if ($collection->getIndexSearch() != '') { - $solr_query .= '(' . $collection->getIndexSearch() . ')'; + $solrQuery .= '(' . $collection->getIndexSearch() . ')'; } else { - $solr_query .= 'collection:("' . Solr::escapeQuery($collection->getIndexName()) . '")'; + $solrQuery .= 'collection:("' . Solr::escapeQuery($collection->getIndexName()) . '")'; } + // We only care about the UID and partOf in the results and want them sorted + $params = [ + 'fields' => 'uid,partof', + 'sort' => [ + 'uid' => 'asc' + ] + ]; // virtual collection might yield documents, that are not toplevel true or partof anything if ($collection->getIndexSearch()) { - $params['query'] = $solr_query; + $params['query'] = $solrQuery; } else { - $params['query'] = $solr_query . ' AND partof:0 AND toplevel:true'; + $params['query'] = $solrQuery . ' AND partof:0 AND toplevel:true'; } $partOfNothing = $solr->searchRaw($params); - $params['query'] = $solr_query . ' AND NOT partof:0 AND toplevel:true'; + $params['query'] = $solrQuery . ' AND NOT partof:0 AND toplevel:true'; $partOfSomething = $solr->searchRaw($params); $collectionInfo = []; diff --git a/Classes/Controller/TableOfContentsController.php b/Classes/Controller/TableOfContentsController.php index 8beaf70c3..7bbcefd43 100644 --- a/Classes/Controller/TableOfContentsController.php +++ b/Classes/Controller/TableOfContentsController.php @@ -131,44 +131,8 @@ private function getMenuEntry(array $entry, bool $recursive = false): array $entryArray['doNotLinkIt'] = 1; $entryArray['ITEM_STATE'] = 'NO'; - // Build menu links based on the $entry['points'] array. - if ( - !empty($entry['points']) - && MathUtility::canBeInterpretedAsInteger($entry['points']) - ) { - $entryArray['page'] = $entry['points']; + $this->buildMenuLinks($entryArray, $entry['id'], $entry['points'], $entry['targetUid']); - $entryArray['doNotLinkIt'] = 0; - if (isset($this->settings['basketButton'])) { - $entryArray['basketButton'] = [ - 'logId' => $entry['id'], - 'startpage' => $entry['points'] - ]; - } - } elseif ( - !empty($entry['points']) - && is_string($entry['points']) - ) { - $entryArray['id'] = $entry['points']; - $entryArray['page'] = 1; - $entryArray['doNotLinkIt'] = 0; - if (isset($this->settings['basketButton'])) { - $entryArray['basketButton'] = [ - 'logId' => $entry['id'], - 'startpage' => $entry['points'] - ]; - } - } elseif (!empty($entry['targetUid'])) { - $entryArray['id'] = $entry['targetUid']; - $entryArray['page'] = 1; - $entryArray['doNotLinkIt'] = 0; - if (isset($this->settings['basketButton'])) { - $entryArray['basketButton'] = [ - 'logId' => $entry['id'], - 'startpage' => $entry['targetUid'] - ]; - } - } // Set "ITEM_STATE" to "CUR" if this entry points to current page. if (in_array($entry['id'], $this->activeEntries)) { $entryArray['ITEM_STATE'] = 'CUR'; @@ -202,6 +166,61 @@ private function getMenuEntry(array $entry, bool $recursive = false): array return $entryArray; } + /** + * Build menu links based on the $entry['points'] array. + * + * @access private + * + * @param array &$entryArray passed by reference + * @param mixed $id + * @param mixed $points + * @param mixed $targetUid + * + * @return void + */ + private function buildMenuLinks(array &$entryArray, $id, $points, $targetUid): void + { + if ( + !empty($points) + && MathUtility::canBeInterpretedAsInteger($points) + ) { + $entryArray['page'] = $points; + $entryArray['doNotLinkIt'] = 0; + $this->setBasket($entryArray, $id, $points); + } elseif ( + !empty($points) + && is_string($points) + ) { + $entryArray['id'] = $points; + $entryArray['page'] = 1; + $entryArray['doNotLinkIt'] = 0; + $this->setBasket($entryArray, $id, $points); + } elseif (!empty($targetUid)) { + $entryArray['id'] = $targetUid; + $entryArray['page'] = 1; + $entryArray['doNotLinkIt'] = 0; + $this->setBasket($entryArray, $id, $targetUid); + } + } + + /** + * Set basket if basket is included in settings. + * + * @param array $entryArray passed by reference + * @param mixed $id + * @param mixed $startPage + * @return void + */ + private function setBasket(array &$entryArray, $id, $startPage): void + { + if (isset($this->settings['basketButton'])) { + $entryArray['basketButton'] = [ + 'logId' => $id, + 'startpage' => $startPage + ]; + } + } + /** * If $entry references an external METS file (as mptr), * try to resolve its database UID and return an updated $entry. @@ -241,18 +260,25 @@ private function resolveMenuEntry(array $entry): array */ private function getAllLogicalUnits(): void { + $page = $this->requestData['page']; + $physicalStructure = $this->document->getCurrentDocument()->physicalStructure; if ( - !empty($this->requestData['page']) - && !empty($this->document->getCurrentDocument()->physicalStructure) + !empty($page) + && !empty($physicalStructure) ) { - $this->activeEntries = array_merge((array) $this->document->getCurrentDocument()->smLinks['p2l'][$this->document->getCurrentDocument()->physicalStructure[0]], - (array) $this->document->getCurrentDocument()->smLinks['p2l'][$this->document->getCurrentDocument()->physicalStructure[$this->requestData['page']]]); + $structureMapLinks = $this->document->getCurrentDocument()->smLinks; + $this->activeEntries = array_merge( + (array) $structureMapLinks['p2l'][$physicalStructure[0]], + (array) $structureMapLinks['p2l'][$physicalStructure[$page]] + ); if ( !empty($this->requestData['double']) - && $this->requestData['page'] < $this->document->getCurrentDocument()->numPages + && $page < $this->document->getCurrentDocument()->numPages ) { - $this->activeEntries = array_merge($this->activeEntries, - (array) $this->document->getCurrentDocument()->smLinks['p2l'][$this->document->getCurrentDocument()->physicalStructure[$this->requestData['page'] + 1]]); + $this->activeEntries = array_merge( + $this->activeEntries, + (array) $structureMapLinks['p2l'][$physicalStructure[$page + 1]] + ); } } } @@ -302,7 +328,10 @@ private function isMultiElement(string $type): bool */ private function setTitle(array $entry): string { - if (empty($entry['label']) && empty($entry['orderlabel'])) { + $label = $entry['label']; + $orderLabel = $entry['orderlabel']; + + if (empty($label) && empty($orderLabel)) { foreach ($this->settings['titleReplacements'] as $titleReplacement) { if ($entry['type'] == $titleReplacement['type']) { $fields = explode(",", $titleReplacement['fields']); @@ -314,12 +343,11 @@ private function setTitle(array $entry): string $title .= $entry[$field] . ' '; } } - return trim($title); } } } - return $entry['label'] ?: $entry['orderlabel']; + return $label ?: $orderLabel; } /** @@ -352,11 +380,14 @@ private function sortMenu(array &$menu): void */ private function sortSubMenu(array &$menu): void { - usort($menu[0]['_SUB_MENU'], function ($firstElement, $secondElement) { - if (!empty($firstElement['orderlabel'])) { - return $firstElement['orderlabel'] <=> $secondElement['orderlabel']; + usort( + $menu[0]['_SUB_MENU'], + function ($firstElement, $secondElement) { + if (!empty($firstElement['orderlabel'])) { + return $firstElement['orderlabel'] <=> $secondElement['orderlabel']; + } + return $firstElement['year'] <=> $secondElement['year']; } - return $firstElement['year'] <=> $secondElement['year']; - }); + ); } } diff --git a/Classes/Hooks/DataHandler.php b/Classes/Hooks/DataHandler.php index cf2461210..6304efcbd 100644 --- a/Classes/Hooks/DataHandler.php +++ b/Classes/Hooks/DataHandler.php @@ -157,7 +157,8 @@ public function processDatamap_postProcessFieldArray(string $status, string $tab ->setMaxResults(1) ->execute(); - if ($resArray = $result->fetchAssociative()) { + $resArray = $result->fetchAssociative(); + if (is_array($resArray)) { // Reset storing to current. $fieldArray['index_stored'] = $resArray['is_listed']; } @@ -244,27 +245,12 @@ public function processDatamap_afterDatabaseOperations(string $status, string $t ->setMaxResults(1) ->execute(); - if ($resArray = $result->fetchAssociative()) { + $resArray = $result->fetchAssociative(); + if (is_array($resArray)) { if ($resArray['hidden']) { - // Establish Solr connection. - $solr = Solr::getInstance($resArray['core']); - if ($solr->ready) { - // Delete Solr document. - $updateQuery = $solr->service->createUpdate(); - $updateQuery->addDeleteQuery('uid:' . (int) $id); - $updateQuery->addCommit(); - $solr->service->update($updateQuery); - } + $this->deleteDocument($resArray['core'], $id); } else { - // Reindex document. - $document = $this->getDocumentRepository()->findByUid((int) $id); - $doc = AbstractDocument::getInstance($document->getLocation(), ['storagePid' => $document->getPid()], true); - if ($document !== null && $doc !== null) { - $document->setCurrentDocument($doc); - Indexer::add($document, $this->getDocumentRepository()); - } else { - $this->logger->error('Failed to re-index document with UID ' . (string) $id); - } + $this->reindexDocument($id); } } } @@ -321,32 +307,17 @@ public function processCmdmap_postProcess(string $command, string $table, $id): ->setMaxResults(1) ->execute(); - if ($resArray = $result->fetchAssociative()) { + $resArray = $result->fetchAssociative(); + if (is_array($resArray)) { switch ($command) { case 'move': case 'delete': - // Establish Solr connection. - $solr = Solr::getInstance($resArray['core']); - if ($solr->ready) { - // Delete Solr document. - $updateQuery = $solr->service->createUpdate(); - $updateQuery->addDeleteQuery('uid:' . (int) $id); - $updateQuery->addCommit(); - $solr->service->update($updateQuery); - if ($command == 'delete') { - break; - } + $this->deleteDocument($resArray['core'], $id); + if ($command == 'delete') { + break; } case 'undelete': - // Reindex document. - $document = $this->getDocumentRepository()->findByUid((int) $id); - $doc = AbstractDocument::getInstance($document->getLocation(), ['storagePid' => $document->getPid()], true); - if ($document !== null && $doc !== null) { - $document->setCurrentDocument($doc); - Indexer::add($document, $this->getDocumentRepository()); - } else { - $this->logger->error('Failed to re-index document with UID ' . (string) $id); - } + $this->reindexDocument($id); break; } } @@ -355,48 +326,106 @@ public function processCmdmap_postProcess(string $command, string $table, $id): $command === 'delete' && $table == 'tx_dlf_solrcores' ) { - // Is core deletion allowed in extension configuration? - $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'); - // Record in "tx_dlf_solrcores" is already deleted at this point. - $queryBuilder - ->getRestrictions() - ->removeByType(DeletedRestriction::class); + $this->deleteSolrCore($id); + } + } - $result = $queryBuilder - ->select( - 'tx_dlf_solrcores.index_name AS core' - ) - ->from('tx_dlf_solrcores') - ->where($queryBuilder->expr()->eq('tx_dlf_solrcores.uid', (int) $id)) - ->setMaxResults(1) - ->execute(); + /** + * Delete document from index. + * + * @access private + * + * @param mixed $core + * @param int|string $id + * + * @return void + */ + private function deleteDocument($core, $id): void + { + // Establish Solr connection. + $solr = Solr::getInstance($core); + if ($solr->ready) { + // Delete Solr document. + $updateQuery = $solr->service->createUpdate(); + $updateQuery->addDeleteQuery('uid:' . (int) $id); + $updateQuery->addCommit(); + $solr->service->update($updateQuery); + } + } - if ($resArray = $result->fetchAssociative()) { - // Establish Solr connection. - $solr = Solr::getInstance(); - if ($solr->ready) { - // Delete Solr core. - $query = $solr->service->createCoreAdmin(); - $action = $query->createUnload(); - $action->setCore($resArray['core']); - $action->setDeleteDataDir(true); - $action->setDeleteIndex(true); - $action->setDeleteInstanceDir(true); - $query->setAction($action); - try { - $response = $solr->service->coreAdmin($query); - if ($response->getWasSuccessful()) { - return; - } - } catch (\Exception $e) { - // Nothing to do here. + /** + * Reindex document. + * + * @access private + * + * @param int|string $id + * + * @return void + */ + private function reindexDocument($id):void + { + $document = $this->getDocumentRepository()->findByUid((int) $id); + $doc = AbstractDocument::getInstance($document->getLocation(), ['storagePid' => $document->getPid()], true); + if ($document !== null && $doc !== null) { + $document->setCurrentDocument($doc); + Indexer::add($document, $this->getDocumentRepository()); + } else { + $this->logger->error('Failed to re-index document with UID ' . (string) $id); + } + } + + /** + * Delete SOLR core if deletion is allowed. + * + * @access private + * + * @param int|string $id + * + * @return void + */ + private function deleteSolrCore($id): void + { + // Is core deletion allowed in extension configuration? + $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'); + // Record in "tx_dlf_solrcores" is already deleted at this point. + $queryBuilder + ->getRestrictions() + ->removeByType(DeletedRestriction::class); + + $result = $queryBuilder + ->select( + 'tx_dlf_solrcores.index_name AS core' + ) + ->from('tx_dlf_solrcores') + ->where($queryBuilder->expr()->eq('tx_dlf_solrcores.uid', (int) $id)) + ->setMaxResults(1) + ->execute(); + + $resArray = $result->fetchAssociative(); + if (is_array($resArray)) { + // Establish Solr connection. + $solr = Solr::getInstance(); + if ($solr->ready) { + // Delete Solr core. + $query = $solr->service->createCoreAdmin(); + $action = $query->createUnload(); + $action->setCore($resArray['core']); + $action->setDeleteDataDir(true); + $action->setDeleteIndex(true); + $action->setDeleteInstanceDir(true); + $query->setAction($action); + try { + $response = $solr->service->coreAdmin($query); + if ($response->getWasSuccessful() == false) { + $this->logger->warning('Core ' . $resArray['core'] . ' could not be deleted from Apache Solr'); } + } catch (\Exception $e) { + $this->logger->warning($e->getMessage()); } - $this->logger->warning('Core ' . $resArray['core'] . ' could not be deleted from Apache Solr'); } } } diff --git a/Classes/Hooks/ItemsProcFunc.php b/Classes/Hooks/ItemsProcFunc.php index c3c4277e2..f4f82c783 100644 --- a/Classes/Hooks/ItemsProcFunc.php +++ b/Classes/Hooks/ItemsProcFunc.php @@ -16,6 +16,7 @@ use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Configuration\ConfigurationManager; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\TypoScript\TemplateService; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -50,7 +51,9 @@ class ItemsProcFunc implements LoggerAwareInterface */ public function toolList(array &$params): void { - foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['dlf/Classes/Plugin/Toolbox.php']['tools'] as $class => $label) { + $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class); + $options = $configurationManager->getLocalConfigurationValueByPath('SC_OPTIONS'); + foreach ($options['dlf/Classes/Plugin/Toolbox.php']['tools'] as $class => $label) { $params['items'][] = [Helper::getLanguageService()->sL($label), $class]; } } @@ -70,9 +73,9 @@ public function getTyposcriptConfigFromPluginSiteRoot(array $params): void $pid = $params['flexParentDatabaseRow']['pid']; $rootLine = BackendUtility::BEgetRootLine($pid); $siteRootRow = []; - foreach ($rootLine as $_row) { - if ($_row['is_siteroot'] == '1') { - $siteRootRow = $_row; + foreach ($rootLine as $row) { + if ($row['is_siteroot'] == '1') { + $siteRootRow = $row; break; } } @@ -152,7 +155,7 @@ protected function generateList(array &$params, string $fields, string $table, s ->select(...explode(',', $fields)) ->from($table) ->where( - $queryBuilder->expr()->eq($table . '.pid', intval($this->storagePid)), + $queryBuilder->expr()->eq($table . '.pid', $this->storagePid), $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]), $andWhere ) diff --git a/Classes/Hooks/KitodoProductionHacks.php b/Classes/Hooks/KitodoProductionHacks.php index e0b247e01..526cdda20 100644 --- a/Classes/Hooks/KitodoProductionHacks.php +++ b/Classes/Hooks/KitodoProductionHacks.php @@ -39,12 +39,16 @@ public function postProcessRecordId(\SimpleXMLElement &$xml, &$recordId): void if (!$recordId) { $xml->registerXPathNamespace('mods', 'http://www.loc.gov/mods/v3'); // Get all logical structure nodes with metadata, but without associated METS-Pointers. - if (($divs = $xml->xpath('//mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID and not(./mets:mptr)]'))) { + $divs = $xml->xpath('//mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID and not(./mets:mptr)]'); + if (is_array($divs)) { $smLinks = $xml->xpath('//mets:structLink/mets:smLink'); if (!empty($smLinks)) { + $links = []; + foreach ($smLinks as $smLink) { $links[(string) $smLink->attributes('http://www.w3.org/1999/xlink')->from][] = (string) $smLink->attributes('http://www.w3.org/1999/xlink')->to; } + foreach ($divs as $div) { if (!empty($links[(string) $div['ID']])) { $id = (string) $div['DMDID']; diff --git a/Tests/Functional/FunctionalTestCase.php b/Tests/Functional/FunctionalTestCase.php index 7c3b021bb..c5631ed41 100644 --- a/Tests/Functional/FunctionalTestCase.php +++ b/Tests/Functional/FunctionalTestCase.php @@ -44,6 +44,9 @@ class FunctionalTestCase extends \TYPO3\TestingFramework\Core\Functional\Functio ], 'displayErrors' => '1' ], + 'SC_OPTIONS' => [ + 'dlf/Classes/Plugin/Toolbox.php' => [] + ], 'EXTENSIONS' => [ 'dlf' => [], // = $this->getDlfConfiguration(), set in constructor ],