Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Two-level metadata: Model and appearance #910

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8739af1
[FEATURE] Create new table tx_dlf_metadata_subentries and change DB s…
fraschz Apr 13, 2022
b84bf8f
[FEATURE] Update document model with subentries.
fraschz May 4, 2022
506589c
[BUGFIX] Rename table for subentries, removed inappropriate '_'
fraschz May 25, 2022
c952c16
[GITIGNORE] Added .bak-files created by Notepad++
fraschz May 25, 2022
d6c11c8
[FEATURE] New domain model for subentries.
fraschz May 25, 2022
fca0b69
[BUGFIX] Add required class mappings in TypoScript config
fraschz May 29, 2022
402e5da
[FEATURE] Add output for metadata subentries.
fraschz May 29, 2022
826b6be
[FEATURE] Make the appearance of subentries configurable.
fraschz Jun 3, 2022
a98f693
[TODO] TODOs for future changes in Indexer and Controller.
fraschz Jun 2, 2022
6185995
[CODACY] Cleanup open issues.
fraschz Jul 20, 2022
0b78d14
Add sorting for tx_dlf_subentries
chrizzor Feb 10, 2023
8e91be8
Consider the return of a string of evaluate()
chrizzor Feb 10, 2023
d93ecd3
Rendering all subentry metadata if available
chrizzor Feb 10, 2023
a0ef467
Merge branch 'master' into two-level-metadata
sebastian-meyer Mar 15, 2023
1e87169
Check if the nodevalue is empty and prevent adding an empty array as …
chrizzor Mar 23, 2023
de795d8
Merge branch 'master' into two-level-metadata
chrizzor May 16, 2023
d8394fd
Remove unused variable
chrizzor May 16, 2023
9b425be
Fix overwritten code from merge with master
chrizzor May 17, 2023
e2258f1
Remove var_dump
chrizzor May 17, 2023
fc4ceb7
Merge branch 'master' into two-level-metadata
sebastian-meyer Jun 12, 2023
c1200ca
Merge branch 'project-slub' into two-level-metadata
sebastian-meyer Aug 8, 2023
22d2c13
Merge branch 'master' into two-level-metadata-5.x
chrizzor Jun 11, 2024
e528658
Fix conflict
chrizzor Jun 13, 2024
e4fe841
Add changed language files
chrizzor Jun 13, 2024
f93bee6
Add subentries for new metadata processing
chrizzor Jun 13, 2024
4c6b57a
Fix codacy and remove todos comments
chrizzor Jun 14, 2024
709711b
Codacy fix
chrizzor Jun 18, 2024
c8200eb
Codacy fix
chrizzor Jun 18, 2024
2311c83
Fix function nesting level
chrizzor Jun 28, 2024
246d785
Fix assignment of the merge result
chrizzor Jun 28, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.bak
/.buildpath
/.cache
/.idea/
Expand Down
4 changes: 2 additions & 2 deletions Classes/Common/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ public static function getIndexNameFromUid(int $uid, string $table, int $pid = -
if (
!$uid
// NOTE: Only use tables that don't have too many entries!
|| !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures', 'tx_dlf_solrcores'])
|| !in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_metadatasubentries','tx_dlf_structures', 'tx_dlf_solrcores'])
) {
self::log('Invalid UID "' . $uid . '" or table "' . $table . '"', LOG_SEVERITY_ERROR);
return '';
Expand Down Expand Up @@ -846,7 +846,7 @@ public static function translate(string $indexName, string $table, string $pid):
// Check if we already got a translation.
if (empty($labels[$table][$pid][$languageContentId][$indexName])) {
// Check if this table is allowed for translation.
if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_structures'])) {
if (in_array($table, ['tx_dlf_collections', 'tx_dlf_libraries', 'tx_dlf_metadata', 'tx_dlf_metadatasubentries', 'tx_dlf_structures'])) {
$additionalWhere = $queryBuilder->expr()->in($table . '.sys_language_uid', [-1, 0]);
if ($languageContentId > 0) {
$additionalWhere = $queryBuilder->expr()->andX(
Expand Down
2 changes: 2 additions & 0 deletions Classes/Common/Indexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ private static function processMetadata($document, $metadata, &$solrDoc): array
{
$autocomplete = [];
foreach ($metadata as $indexName => $data) {
// TODO: Include also subentries if available.
if (
!empty($data)
&& substr($indexName, -8) !== '_sorting'
Expand Down Expand Up @@ -551,6 +552,7 @@ private static function processMetadata($document, $metadata, &$solrDoc): array
*/
private static function addFaceting($doc, &$solrDoc): void
{
// TODO: Include also subentries if available.
foreach ($doc->metadataArray[$doc->toplevelId] as $indexName => $data) {
if (
!empty($data)
Expand Down
146 changes: 135 additions & 11 deletions Classes/Common/MetsDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,6 @@
if (!$this->extractAndProcessMetadata($dmdId, $mdSectionType, $metadata, $cPid, $hasMetadataSection)) {
continue;
}

$hasMetadataSection[$mdSectionType] = true;
}

Expand All @@ -533,6 +532,66 @@
}
}

/**
* @param array $allSubentries
* @param string $parentIndex
* @param \DOMNode $parentNode
* @return array|false
*/
private function getSubentries($allSubentries, string $parentIndex, \DOMNode $parentNode)
{
$domXPath = new \DOMXPath($parentNode->ownerDocument);

Check notice on line 543 in Classes/Common/MetsDocument.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Classes/Common/MetsDocument.php#L543

Missing class import via use statement (line '543', column '25').
$this->registerNamespaces($domXPath);
$theseSubentries = [];
foreach ($allSubentries as $subentry) {
if ($subentry['parent_index_name'] == $parentIndex) {
$values = $domXPath->evaluate($subentry['xpath'], $parentNode);
if (!empty($subentry['xpath']) && ($values)) {
$theseSubentries = array_merge($theseSubentries, $this->getSubentryValue($values, $subentry));
}
// Set default value if applicable.
if (
empty($theseSubentries[$subentry['index_name']][0])
&& strlen($subentry['default_value']) > 0
) {
$theseSubentries[$subentry['index_name']] = [$subentry['default_value']];
}
}
}
if (empty($theseSubentries)) {
return false;
}
return $theseSubentries;
}

/**
* @param $values
* @param $subentry
* @return array
*/
private function getSubentryValue($values, $subentry)
{
$theseSubentries = [];
if (
($values instanceof \DOMNodeList
&& $values->length > 0) || is_string($values)
) {
if (is_string($values)) {
// if concat is used evaluate returns a string
$theseSubentries[$subentry['index_name']][] = trim($values);
} else {
foreach ($values as $value) {
if (!empty(trim((string) $value->nodeValue))) {
$theseSubentries[$subentry['index_name']][] = trim((string) $value->nodeValue);
}
}
}
} elseif (!($values instanceof \DOMNodeList)) {
$theseSubentries[$subentry['index_name']] = [trim((string) $values->nodeValue)];
}
return $theseSubentries;
}

/**
* Get logical unit type.
*
Expand Down Expand Up @@ -605,8 +664,13 @@
*/
private function processAdditionalMetadata(array $additionalMetadata, \DOMXPath $domXPath, \DOMElement $domNode, array &$metadata): void
{
$subentries = [];
if (isset($additionalMetadata['subentries'])) {
$subentries = $additionalMetadata['subentries'];
unset($additionalMetadata['subentries']);
}
foreach ($additionalMetadata as $resArray) {
$this->setMetadataFieldValues($resArray, $domXPath, $domNode, $metadata);
$this->setMetadataFieldValues($resArray, $domXPath, $domNode, $metadata, $subentries);
$this->setDefaultMetadataValue($resArray, $metadata);
$this->setSortableMetadataValue($resArray, $domXPath, $domNode, $metadata);
}
Expand All @@ -621,17 +685,23 @@
* @param \DOMXPath $domXPath
* @param \DOMElement $domNode
* @param array $metadata
* @param array $subentryResults
*
* @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, array $subentryResults): void
{
if ($resArray['format'] > 0 && !empty($resArray['xpath'])) {
$values = $domXPath->evaluate($resArray['xpath'], $domNode);
if ($values instanceof \DOMNodeList && $values->length > 0) {
$metadata[$resArray['index_name']] = [];
foreach ($values as $value) {
$metadata[$resArray['index_name']][] = trim((string) $value->nodeValue);
$subentries = $this->getSubentries($subentryResults, $resArray['index_name'], $value);
if ($subentries) {
$metadata[$resArray['index_name']][] = $subentries;
} else {
$metadata[$resArray['index_name']][] = trim((string) $value->nodeValue);
}
}
} elseif (!($values instanceof \DOMNodeList)) {
$metadata[$resArray['index_name']] = [trim((string) $values)];
Expand Down Expand Up @@ -670,17 +740,25 @@
*/
private function setSortableMetadataValue(array $resArray, \DOMXPath $domXPath, \DOMElement $domNode, array &$metadata): void
{
if (!empty($metadata[$resArray['index_name']]) && $resArray['is_sortable']) {
$indexName = $resArray['index_name'];
$currentMetadata = $metadata[$indexName][0];

if (!empty($metadata[$indexName]) && $resArray['is_sortable']) {
if ($resArray['format'] > 0 && !empty($resArray['xpath_sorting'])) {
$values = $domXPath->evaluate($resArray['xpath_sorting'], $domNode);
if ($values instanceof \DOMNodeList && $values->length > 0) {
$metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values->item(0)->nodeValue);
$metadata[$indexName . '_sorting'][0] = trim((string) $values->item(0)->nodeValue);
} elseif (!($values instanceof \DOMNodeList)) {
$metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values);
$metadata[$indexName . '_sorting'][0] = trim((string) $values);
}
}
if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) {
$metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0];
if (empty($metadata[$indexName . '_sorting'][0])) {
if (is_array($currentMetadata)) {
$sortingValue = implode(',', array_column($currentMetadata, 0));
$metadata[$indexName . '_sorting'][0] = $sortingValue;
} else {
$metadata[$indexName . '_sorting'][0] = $currentMetadata;
}
}
}
}
Expand Down Expand Up @@ -768,6 +846,7 @@
->getRestrictions()
->removeByType(HiddenRestriction::class);
// Get all metadata with configured xpath and applicable format first.
// Exclude metadata with subentries, we will fetch them later.
$resultWithFormat = $queryBuilder
->select(
'tx_dlf_metadata.index_name AS index_name',
Expand Down Expand Up @@ -806,7 +885,7 @@
// Get all metadata without a format, but with a default value next.
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_dlf_metadata');
// Get hidden records, too.
// Get hidden records, too.
$queryBuilder
->getRestrictions()
->removeByType(HiddenRestriction::class);
Expand All @@ -826,7 +905,52 @@
)
->execute();
// Merge both result sets.
return array_merge($resultWithFormat->fetchAllAssociative(), $resultWithoutFormat->fetchAllAssociative());
$allResults = array_merge($resultWithFormat->fetchAllAssociative(), $resultWithoutFormat->fetchAllAssociative());

// Get subentries separately.
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_dlf_metadata');
// Get hidden records, too.
$queryBuilder
->getRestrictions()
->removeByType(HiddenRestriction::class);
$subentries = $queryBuilder
->select(
'tx_dlf_subentries_joins.index_name AS index_name',
'tx_dlf_metadata.index_name AS parent_index_name',
'tx_dlf_subentries_joins.xpath AS xpath',
'tx_dlf_subentries_joins.default_value AS default_value'
)
->from('tx_dlf_metadata')
->innerJoin(
'tx_dlf_metadata',
'tx_dlf_metadataformat',
'tx_dlf_metadataformat_joins',
$queryBuilder->expr()->eq(
'tx_dlf_metadataformat_joins.parent_id',
'tx_dlf_metadata.uid'
)
)
->innerJoin(
'tx_dlf_metadataformat_joins',
'tx_dlf_metadatasubentries',
'tx_dlf_subentries_joins',
$queryBuilder->expr()->eq(
'tx_dlf_subentries_joins.parent_id',
'tx_dlf_metadataformat_joins.uid'
)
)
->where(
$queryBuilder->expr()->eq('tx_dlf_metadata.pid', (int) $cPid),
$queryBuilder->expr()->gt('tx_dlf_metadataformat_joins.subentries', 0),
$queryBuilder->expr()->eq('tx_dlf_subentries_joins.l18n_parent', 0),
$queryBuilder->expr()->eq('tx_dlf_subentries_joins.pid', (int) $cPid)
)
->orderBy('tx_dlf_subentries_joins.sorting')
->execute();
$subentriesResult = $subentries->fetchAll();

return array_merge($allResults, ['subentries' => $subentriesResult]);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions Classes/Controller/MetadataController.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ protected function printMetadata(array $metadata): void
foreach ($section as $name => $value) {
// NOTE: Labels are to be escaped in Fluid template

$metadata[$i][$name] = is_array($value)
? implode($this->settings['separator'], $value)
: $value;

if ($metadata[$i][$name] === 'Array') {
$metadata[$i][$name] = [];
foreach ($value as $subKey => $subValue) {
$metadata[$i][$name][$subKey] = $subValue;
}
}

$this->parseMetadata($i, $name, $value, $metadata);

if (is_array($metadata[$i][$name])) {
Expand Down Expand Up @@ -372,6 +383,8 @@ private function parseMetadata(int $i, string $name, $value, array &$metadata) :
foreach ($metadata[$i][$name] as &$langValue) {
$langValue = Helper::getLanguageName($langValue);
}
} elseif (!empty($value)) {
$metadata[$i][$name][0] = $metadata[$i][$name][0];
}
}

Expand Down
63 changes: 62 additions & 1 deletion Classes/Domain/Model/MetadataFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

namespace Kitodo\Dlf\Domain\Model;

use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

/**
Expand Down Expand Up @@ -55,11 +57,36 @@ class MetadataFormat extends AbstractEntity
protected $xpathSorting;

/**
* Collection of ``tx_dlf_metadatasubentries`` specified with this metadata entry.
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Kitodo\Dlf\Domain\Model\MetadataSubentry>
* @Extbase\ORM\Lazy
* @Extbase\ORM\Cascade("remove")
*/
protected $subentries;

/**
* Whether or not the field is mandatory. Not used at the moment (originally planned to be used in METS validator).
*
* @var int
* @access protected
* @var int Whether or not the field is mandatory. Not used at the moment (originally planned to be used in METS validator).
*/
protected $mandatory;

/**
* constructor
*/
public function __construct()
{
// Do not remove the next line: It would break the functionality
$this->initStorageObjects();
}

protected function initStorageObjects()
{
$this->subentries = new ObjectStorage();
}

/**
* @return int
*/
Expand Down Expand Up @@ -124,6 +151,40 @@ public function setXpathSorting(string $xpathSorting): void
$this->xpathSorting = $xpathSorting;
}

public function getSubentries()
{
return $this->subentries;
}

public function setSubentries(ObjectStorage $subentries): void
{
$this->subentries = $subentries;
}

/**
* Adds a Subentry
*
* @param \Kitodo\Dlf\Domain\Model\MetadataSubentry $subentry
*
* @return void
*/
public function addSubentry(MetadataSubentry $subentry)
{
$this->subentries->attach($subentry);
}

/**
* Removes a Subentry
*
* @param \Kitodo\Dlf\Domain\Model\MetadataSubentry $subentryToRemove
*
* @return void
*/
public function removeSubentry(MetadataSubentry $subentryToRemove)
{
$this->subentries->detach($subentryToRemove);
}

/**
* @return int
*/
Expand Down
Loading