Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions Classes/Common/Indexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ class Indexer
*/
protected static array $processedDocs = [];

/**
* @access protected
* @static
* @var array List of already extracted structure nodes for structure path
*/
protected static array $extractedStructurePathNodes = [];

/**
* @access protected
* @static
Expand Down Expand Up @@ -319,6 +326,10 @@ protected static function processLogical(Document $document, array $logicalUnit)
$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']);
// extract structure path
self::$extractedStructurePathNodes[$logicalUnit['id']] = self::extractStructurePathNodes($doc->tableOfContents, $logicalUnit['id']);
$processedStructurePath = self::buildStructurePathData(self::$extractedStructurePathNodes[$logicalUnit['id']], $document->getCurrentDocument()->toplevelId);
$solrDoc->setField('structure_path', json_encode($processedStructurePath, JSON_UNESCAPED_UNICODE));
// verify date formatting
if(strtotime($metadata['date'][0])) {
$solrDoc->setField('date', self::getFormattedDate($metadata['date'][0]));
Expand Down Expand Up @@ -404,6 +415,21 @@ protected static function processPhysical(Document $document, int $page, array $
$solrDoc->setField('type', $physicalUnit['type'], self::$fields['fieldboost']['type']);
$solrDoc->setField('collection', $doc->metadataArray[$doc->toplevelId]['collection']);
$solrDoc->setField('location', $document->getLocation());
// pick only the deepest structure paths
$associatedPaths = [];
foreach ($doc->smLinks['p2l'][$physicalUnit['id']] as $logicalId) {
$path = self::$extractedStructurePathNodes[$logicalId] ?? [];
if (!empty($path)) {
$associatedPaths[$logicalId] = $path;
}
}
$deepestPaths = self::filterDeepestStructurePaths($associatedPaths);
$processedStructurePath = [];
foreach ($deepestPaths as $path) {
$segments = self::buildStructurePathData($path, $document->getCurrentDocument()->toplevelId);
$processedStructurePath[] = json_encode($segments, JSON_UNESCAPED_UNICODE);
}
$solrDoc->setField('structure_path', $processedStructurePath);

$solrDoc->setField('fulltext', $fullText);
if (is_array($doc->metadataArray[$doc->toplevelId])) {
Expand Down Expand Up @@ -639,6 +665,147 @@ private static function removeAppendsFromAuthor($authors)
return $authors;
}

/**
* Extract nodes alongside the structure map in direct line to the target id and return them as flattened array.
*
* @access private
*
* @static
*
* @param array $nodes Tree or Sub-Tree, where the target id should be extracted from if present
* @param string $targetId The ID of the logical structure element to be found
* @param array $path An intermediate array that keeps track of the current branch that is being looked up
*
* @return array
*/
private static function extractStructurePathNodes(array $nodes, string $targetId, array $path = []): array
{
foreach ($nodes as $node) {
// remember where we came from
$currentPath = array_merge($path, [$node]);
if ($node['id'] == $targetId) {
return $currentPath;
}
if (!empty($node['children'])) {
$result = self::extractStructurePathNodes($node['children'], $targetId, $currentPath);
if ($result) {
return $result;
}
}
}
return [];
}

/**
* Filters those structure path nodes that are the descending into the structure tree the most and removes any that resemble a "prefix" of another.
*
* @access private
*
* @static
*
* @param array $paths The array containing all structure path nodes associated with a physical page
*
* @return array
*/
private static function filterDeepestStructurePaths(array $paths): array
{
if (count($paths) <= 1) {
return $paths;
}

$deepestPath = [];
foreach ($paths as $currentLogicalId => $currentPath) {
$currentIds = array_column($currentPath, 'id');
$isPrefix = false;

foreach ($paths as $comparisonLogicalId => $comparisonPath) {
if ($currentLogicalId === $comparisonLogicalId) {
continue;
}
$comparisonIds = array_column($comparisonPath, 'id');
// check if structure path is part/prefix of another structure path
if (
count($currentIds) < count($comparisonIds)
&& array_slice($comparisonIds, 0, count($currentIds)) === $currentIds
) {
$isPrefix = true;
break;
}
}

if (!$isPrefix) {
$deepestPath[$currentLogicalId] = $currentPath;
}
}
return $deepestPath;
}

/**
* Create the actual array with the required data for the structure path that will be JSON encoded and indexed.
*
* @access private
*
* @static
*
* @param array $path The structure path nodes that shall be processed
* @param string $cutoffId The logical id at which ancestors and itself will not be part of the structure path data
*
* @return array
*/
private static function buildStructurePathData(array $path, string $cutoffId): array
{
$cutoffIndex = array_search($cutoffId, array_column($path, 'id'));
if ($cutoffIndex !== false) {
$path = array_slice($path, $cutoffIndex + 1);
}

$segments = [];
foreach ($path as $node) {
$segments[] = self::buildStructurePathSegments($node);
}
return $segments;
}

/**
* Gets the label or type of a structure path node with corresponding tag
*
* @access private
*
* @static
*
* @param array $node The current node that should be processed
*
* @return array
*/
private static function buildStructurePathSegments(array $node): array
{
if (!empty($node['label'])) {
return [
'label' => $node['label'],
];
}
if (!empty($node['orderlabel'])) {
return [
'label' => $node['orderlabel'],
];
}
if (!empty($node['volume'])) {
$value = !empty($node['year'])
? $node['volume'] . ' ' . $node['year']
: $node['volume'];

return [
'label' => $value,
];
}
if (!empty($node['type'])) {
return [
'type' => $node['type'],
];
}
return ['label' => ''];
}

/**
* Handle exception.
*
Expand Down
19 changes: 19 additions & 0 deletions Classes/Common/Solr/SearchResult/ResultDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ class ResultDocument
*/
private ?string $type;

/**
* @access private
* @var array The JSON encoded structure path(s)
*/
private array $structurePath = [];

/**
* @access private
* @var Page[] All pages in which search phrase was found
Expand Down Expand Up @@ -117,6 +123,7 @@ public function __construct(Document $record, array $highlighting, array $fields
$this->title = $record[$fields['title']];
$this->toplevel = $record[$fields['toplevel']] ?? false;
$this->type = $record[$fields['type']];
$this->structurePath = $record[$fields['structure_path']] ?? [];

if (!empty($highlighting[$this->id])) {
$highlightingForRecord = $highlighting[$this->id][$fields['fulltext']];
Expand Down Expand Up @@ -225,6 +232,18 @@ public function getType(): ?string
return $this->type;
}

/**
* Get the structure path(s)
*
* @access public
*
* @return array
*/
public function getStructurePath(): array
{
return $this->structurePath;
}

/**
* Get all result's pages which contain search phrase.
*
Expand Down
1 change: 1 addition & 0 deletions Classes/Common/Solr/Solr.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public static function getFields(): array
self::$fields['type'] = $solrFields['type'];
self::$fields['title'] = $solrFields['title'];
self::$fields['volume'] = $solrFields['volume'];
self::$fields['structure_path'] = $solrFields['structurePath'];
self::$fields['date'] = $solrFields['date'];
self::$fields['thumbnail'] = $solrFields['thumbnail'];
self::$fields['default'] = $solrFields['default'];
Expand Down
28 changes: 27 additions & 1 deletion Classes/Common/Solr/SolrSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ public function prepare()
$params['listMetadataRecords'] = [];

// Restrict the fields to the required ones.
$params['fields'] = 'uid,id,page,title,thumbnail,partof,toplevel,type';
$params['fields'] = 'uid,id,page,title,thumbnail,partof,toplevel,type,structure_path';

if ($this->listedMetadata) {
foreach ($this->listedMetadata as $metadata) {
Expand Down Expand Up @@ -525,6 +525,31 @@ public function submit($start, $rows, $processResults = true)
$searchResult['page'] = $doc['page'];
$searchResult['thumbnail'] = $doc['thumbnail'];
$searchResult['structure'] = $doc['type'];
// create string(s) from structure path(s)
$encodedStructurePaths = $doc['structure_path'] ?? [];
if (!is_array($encodedStructurePaths)) {
$encodedStructurePaths = [$encodedStructurePaths];
}
$structurePathStrings = [];
foreach ($encodedStructurePaths as $jsonString) {
if (!is_string($jsonString) || $jsonString === '') {
continue;
}
$segments = json_decode($jsonString, true);
if ($segments === null && json_last_error() !== JSON_ERROR_NONE) {
continue;
}
$structurePathLabels = [];
foreach ($segments as $currentSegment) {
if (isset($currentSegment['type'])) {
$structurePathLabels[] = Helper::translate($currentSegment['type'], 'tx_dlf_structures', $this->settings['storagePid']);
} elseif (!empty($currentSegment['label'])) {
$structurePathLabels[] = $currentSegment['label'];
}
}
$structurePathStrings[] = implode(' → ', $structurePathLabels);
}
$searchResult['structure_path'] = $structurePathStrings;
$searchResult['title'] = $doc['title'];
foreach ($params['listMetadataRecords'] as $indexName => $solrField) {
if (isset($doc['metadata'][$indexName])) {
Expand Down Expand Up @@ -826,6 +851,7 @@ private function getDocument(Document $record, array $highlighting, array $field
'title' => $resultDocument->getTitle(),
'toplevel' => $resultDocument->getToplevel(),
'type' => $resultDocument->getType(),
'structure_path' => $resultDocument->getStructurePath(),
'uid' => !empty($resultDocument->getUid()) ? $resultDocument->getUid() : $parameters['uid'],
'highlight' => $resultDocument->getHighlightsIds(),
];
Expand Down
8 changes: 8 additions & 0 deletions Configuration/FlexForms/ListView.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
</config>
</TCEforms>
</settings.getTitle>
<settings.getStructurePath>
<exclude>1</exclude>
<label>LLL:EXT:dlf/Resources/Private/Language/locallang_be.xlf:flexform.getStructurePath</label>
<config>
<type>check</type>
<default>0</default>
</config>
</settings.getStructurePath>
<settings.basketButton>
<TCEforms>
<onChange>reload</onChange>
Expand Down
4 changes: 4 additions & 0 deletions Resources/Private/Language/de.locallang_be.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<source><![CDATA[Show only documents from the selected collection(s)]]></source>
<target><![CDATA[Nur Dokumente der ausgewählten Kollektion(en) berücksichtigen]]></target>
</trans-unit>
<trans-unit id="flexform.getStructurePath" approved="yes">
<source><![CDATA[Show breadcrumb/path to result location within the structure map]]></source>
<target><![CDATA[Breadcrumb/Pfad des Treffers innerhalb des Strukturbaums anzeigen]]></target>
</trans-unit>
<trans-unit id="flexform.getTitle" approved="yes">
<source><![CDATA[Show title of parent document if document has no title itself]]></source>
<target><![CDATA[Bei Bedarf Titel des übergeordneten Dokuments anzeigen]]></target>
Expand Down
4 changes: 4 additions & 0 deletions Resources/Private/Language/de.locallang_labels.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,10 @@
<trans-unit id="config.solr.fields.volume">
<target>Solr-Schema-Feld "volume" : Volume field is mandatory for identifying documents (Standard ist "volume")</target>
<source>Solr Schema Field "volume" : Volume field is mandatory for identifying documents (default is "volume")</source>
</trans-unit>
<trans-unit id="config.solr.fields.structurePath">
<target>Solr-Schema-Feld "structure_path" : Field providing context about the location of a resource in the structure map (Standard ist "structure_path")</target>
<source>Solr Schema Field "structure_path" : Field providing context about the location of a resource in the structure map (default is "structure_path")</source>
</trans-unit>
<trans-unit id="config.solr.fields.date">
<target>Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (Standard ist "date")</target>
Expand Down
4 changes: 4 additions & 0 deletions Resources/Private/Language/de.locallang_metadata.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
<source><![CDATA[Rights Information]]></source>
<target><![CDATA[Rechteinformation]]></target>
</trans-unit>
<trans-unit id="metadata.structure_path" approved="yes">
<source><![CDATA[Structure Path]]></source>
<target><![CDATA[Strukturpfad]]></target>
</trans-unit>
<trans-unit id="metadata.shelfmark" approved="yes">
<source><![CDATA[Shelfmark]]></source>
<target><![CDATA[Signatur]]></target>
Expand Down
3 changes: 3 additions & 0 deletions Resources/Private/Language/locallang_be.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
<trans-unit id="flexform.excludeOtherCollections">
<source><![CDATA[Show only documents from the selected collection(s)]]></source>
</trans-unit>
<trans-unit id="flexform.getStructurePath">
<source><![CDATA[Show breadcrumb/path to result location within the structure map]]></source>
</trans-unit>
<trans-unit id="flexform.library">
<source><![CDATA[Providing library]]></source>
</trans-unit>
Expand Down
3 changes: 3 additions & 0 deletions Resources/Private/Language/locallang_labels.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,9 @@
<trans-unit id="config.solr.fields.volume">
<source>Solr Schema Field "volume" : Volume field is mandatory for identifying documents (default is "volume")</source>
</trans-unit>
<trans-unit id="config.solr.fields.structurePath">
<source>Solr Schema Field "structure_path" : Field providing context about the location of a resource in the structure map (default is "structure_path")</source>
</trans-unit>
<trans-unit id="config.solr.fields.date">
<source>Solr Schema Field "date" : The date a resource was issued or created. Used for datesearch (default is "date")</source>
</trans-unit>
Expand Down
3 changes: 3 additions & 0 deletions Resources/Private/Language/locallang_metadata.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<trans-unit id="metadata.rights_info">
<source><![CDATA[Rights Information]]></source>
</trans-unit>
<trans-unit id="metadata.structure_path">
<source><![CDATA[Structure Path]]></source>
</trans-unit>
<trans-unit id="metadata.terms">
<source><![CDATA[Terms of Use]]></source>
</trans-unit>
Expand Down
2 changes: 2 additions & 0 deletions ext_conf_template.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ solr.fields.partof = partof
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.structurePath
solr.fields.structurePath = structure_path
# 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
Expand Down