Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
edc21d6
!!! FEATURE: Extensible meta data properties
bwaidelich Apr 15, 2026
93294c7
Add `MetaDataManager::getMetaDataPropertyValues()`
bwaidelich Apr 15, 2026
c1debab
Respect asset source id
bwaidelich Apr 16, 2026
8fb2e4e
Split `MetaDataConfiguration` into `MetaDataPropertyDefinitions` and …
bwaidelich Apr 17, 2026
e7bd6e2
Apply suggestion from @bwaidelich
bwaidelich Apr 17, 2026
6f06c16
FIX: MetaDataDimensionSpacePointSet parsing
c4ll-m3-j4ck Apr 16, 2026
8d4df59
WIP: TASK: Add translations
c4ll-m3-j4ck Apr 16, 2026
e902ffb
FEATURE: Add AssetMetaData eel-helper
c4ll-m3-j4ck Apr 16, 2026
8d03077
FEATURE: Add migration for existing metadata from Asset
c4ll-m3-j4ck Apr 16, 2026
d81f183
FIX: Support assetSources in eel-helper, dsp hashing calculation
c4ll-m3-j4ck Apr 17, 2026
c4e076a
FIX: Adapt MigrationCommandController to new Reference interface
c4ll-m3-j4ck Apr 17, 2026
46c2194
FEATURE: Convenience initializer for MetaDataAssetReference
c4ll-m3-j4ck Apr 17, 2026
86a1684
FEATURE: Implement content-repository DSP adapter
c4ll-m3-j4ck Apr 17, 2026
adf9ad7
FEATURE: Implement i18n handling for properties from yaml
c4ll-m3-j4ck Apr 17, 2026
a7520f3
TASK: Replace dsp hash retrieval with generalized map
c4ll-m3-j4ck Apr 20, 2026
40967cb
WIP: Cleanup dimensions fetching from cr
c4ll-m3-j4ck Jun 3, 2026
e3cabbd
TASK: Rename and move createAllPresetCombinations
dlubitz Jun 4, 2026
f9fca9a
TASK: Add VO equals method for comparison
dlubitz Jun 4, 2026
0d37e45
TASK: Allow to fetch values without fallback and to fetch values from…
dlubitz Jun 4, 2026
b3df3e1
TASK: Allow unsetting values without DSP
dlubitz Jun 4, 2026
6df8fca
BUGFIX: Fix migration command
dlubitz Jun 4, 2026
f3719cb
TASK: Allow PHP 8.5
Sebobo Jul 22, 2026
4831e14
Fix doc comments of `MetaDataDimensionSpacePoint`
bwaidelich Jul 31, 2026
ac9d36c
Update README to reflect current version
bwaidelich Jul 31, 2026
7d4c8f1
BUGFIX: Enumerate dimension space points by preset value
bwaidelich Jul 31, 2026
4acb3bc
BUGFIX: Allow metadata properties without `ui` configuration
bwaidelich Jul 31, 2026
516c0c3
!!! FEATURE: Respect `globalScope` of metadata properties
bwaidelich Jul 31, 2026
85ac242
TASK: Cover enumeration of dimension space points by preset value
bwaidelich Jul 31, 2026
ec3cdf2
!!! FEATURE: Allow assets to be found by a metadata filter
bwaidelich Aug 3, 2026
543992c
FEATURE: Add `getMetaDataProperty()` to the Eel helper
bwaidelich Aug 3, 2026
af564ce
FEATURE: Coerce metadata values to the configured property type
bwaidelich Aug 3, 2026
bbeee7a
TASK: Rework the test suite along the seams of the package
bwaidelich Aug 3, 2026
79704fe
TASK: Move two paragraphs back out of the "Property types" section
bwaidelich Aug 3, 2026
fed6ec5
FIX: Add getOwnValue method to MetaDataPropertyValue for fusion access
c4ll-m3-j4ck Aug 4, 2026
802aa88
Merge pull request #15 from c4ll-m3-j4ck/feature/global-scope-metadat…
bwaidelich Aug 4, 2026
5ac2b4b
FEATURE: Skip metadata properties that are configured to `null`
bwaidelich Aug 5, 2026
ea8eba9
Merge branch 'feature/global-scope-metadata-properties' of https://gi…
bwaidelich Aug 5, 2026
59da8b9
FEATURE: Allow nullable ui configuration
c4ll-m3-j4ck Aug 5, 2026
e1c7540
Merge pull request #16 from c4ll-m3-j4ck/feature/global-scope-metadat…
bwaidelich Aug 6, 2026
190a95b
TASK: Add github action to run tests
Sebobo Sep 2, 2026
3026e13
TASK: Bump phpstan to max level and adjust code
Sebobo Sep 2, 2026
8f7421e
TASK: Split settings
Sebobo Sep 3, 2026
cc63a39
TASK: Allow translation shorthand strings for metadata property labels
Sebobo Sep 3, 2026
4835685
TASK: Narrow phpstan version to prevent errors in CI
Sebobo Sep 4, 2026
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
110 changes: 110 additions & 0 deletions Classes/Command/AssetMetaDataCommandController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

declare(strict_types=1);

namespace Neos\MetaData\Command;

use InvalidArgumentException;
use JsonException;
use Neos\Flow\Cli\CommandController;
use Neos\MetaData\Domain\Dto\MetaDataAssetReference;
use Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoint;
use Neos\MetaData\Domain\Dto\MetaDataPropertyName;
use Neos\MetaData\MetaDataManager;

final class AssetMetaDataCommandController extends CommandController
{

public function __construct(
private readonly MetaDataManager $metaDataManager,
)
{
parent::__construct();
}

/**
* Sets a metadata property for an asset to a specific value
*
* @param string $assetId ID of the asset to set the metadata property for
* @param string $property name of the metadata property to set
* @param string $value value of the metadata property
* @param string|null $assetSource optional asset source - default = "neos"
* @param string|null $dimensionSpacePoint optional dimension space point as JSON (e.g. `'{"language": "de"}') - default = the configured defaultDimensionSpacePoint
*/
public function setCommand(string $assetId, string $property, string $value, string|null $assetSource = null, string|null $dimensionSpacePoint = null): void
{
$dimensionSpacePointDecoded = $dimensionSpacePoint !== null ? self::parseDimensionSpacePoint($dimensionSpacePoint) : null;
$assetReference = MetaDataAssetReference::create($assetSource ?? 'neos', $assetId);
$this->metaDataManager->setMetaDataPropertyValue(
$assetReference,
MetaDataPropertyName::fromString($property),
$value,
$dimensionSpacePointDecoded,
);
$message = sprintf('Metadata property "%s" of asset "%s" was set to "%s"', $property, $value, $assetId);
if ($dimensionSpacePointDecoded !== null) {
$message .= sprintf(' for dimension space point "%s"', $dimensionSpacePointDecoded->hash);
}
$this->outputLine("<success>$message</success>");
}

/**
* Removes a metadata property for an asset
*
* @param string $assetId ID of the asset to unset the metadata property for
* @param string $property name of the metadata property to unset
* @param string|null $assetSource optional asset source - default = "neos"
* @param string|null $dimensionSpacePoint optional dimension space point as JSON (e.g. `'{"language": "de"}') - default = the configured defaultDimensionSpacePoint
*/
public function unsetCommand(string $assetId, string $property, string|null $assetSource = null, string|null $dimensionSpacePoint = null): void
{
$dimensionSpacePointDecoded = $dimensionSpacePoint !== null ? self::parseDimensionSpacePoint($dimensionSpacePoint) : null;
$assetReference = MetaDataAssetReference::create($assetSource ?? 'neos', $assetId);
$this->metaDataManager->unsetMetaDataPropertyValue(
$assetReference,
MetaDataPropertyName::fromString($property),
$dimensionSpacePointDecoded,
);
$message = sprintf('Metadata property "%s" of asset "%s" was unset', $property, $assetId);
if ($dimensionSpacePointDecoded !== null) {
$message .= sprintf(' for dimension space point "%s"', $dimensionSpacePointDecoded->hash);
}
$this->outputLine("<success>$message</success>");
}

/**
* Lists all metadata properties for an asset
*
* @param string $assetId ID of the asset to unset the metadata property for
* @param string|null $assetSource optional asset source - default = "neos"
* @param string|null $dimensionSpacePoint optional dimension space point as JSON (e.g. `'{"language": "de"}') - default = the configured defaultDimensionSpacePoint
*/
public function listCommand(string $assetId, string|null $assetSource = null, string|null $dimensionSpacePoint = null): void
{
$dimensionSpacePointDecoded = $dimensionSpacePoint !== null ? self::parseDimensionSpacePoint($dimensionSpacePoint) : null;
$assetReference = MetaDataAssetReference::create($assetSource ?? 'neos', $assetId);
$metaDataPropertyValues = $this->metaDataManager->getMetaDataPropertyValues(
$assetReference,
$dimensionSpacePointDecoded,
);
$message = sprintf('Metadata properties of asset "%s"', $assetId);
if ($dimensionSpacePointDecoded !== null) {
$message .= sprintf(' for dimension space point "%s"', $dimensionSpacePointDecoded->hash);
}
$message .= ':';
$this->outputLine($message);
foreach ($metaDataPropertyValues as $propertyName => $propertyValue) {
$this->outputLine(' <b>%s:</b> %s', [$propertyName, $propertyValue ?? '-']);
}
}

private static function parseDimensionSpacePoint(string $dimensionSpacePoint): MetaDataDimensionSpacePoint
{
try {
return MetaDataDimensionSpacePoint::fromCoordinates(json_decode($dimensionSpacePoint, true, 512, JSON_THROW_ON_ERROR));
} catch (JsonException $e) {
throw new InvalidArgumentException('Failed to parse dimension space point: ' . $e->getMessage(), 1776274597, $e);
}
}

}
48 changes: 48 additions & 0 deletions Classes/Command/AssetMetaDataMigrationCommandController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Neos\MetaData\Command;

use Neos\Flow\Cli\CommandController;
use Neos\Media\Domain\Model\Asset;
use Neos\Media\Domain\Repository\AssetRepository;
use Neos\MetaData\Domain\Dto\MetaDataAssetReference;
use Neos\MetaData\Domain\Dto\MetaDataPropertyName;
use Neos\MetaData\MetaDataManager;

final class AssetMetaDataMigrationCommandController extends CommandController
{
public function __construct(
private readonly MetaDataManager $metaDataManager,
private readonly AssetRepository $assetRepository,
) {
parent::__construct();
}

public function migrateExistingAssetPropertiesCommand(): void
{
foreach ($this->assetRepository->findAll() as $asset) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check what happens with assets from other asset sources

/** @var Asset $asset */
$title = $asset->getTitle();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the title being ignored here?

$caption = $asset->getCaption();
$copyrightNotice = $asset->getCopyrightNotice();
$metaDataAssetReference = MetaDataAssetReference::create($asset->assetSourceIdentifier, $asset->getIdentifier());

if (!empty($caption)) {
$this->metaDataManager->setMetaDataPropertyValue(
$metaDataAssetReference,
MetaDataPropertyName::fromString('caption'),
$asset->getCaption(),
);
}
if (!empty($copyrightNotice)) {
$this->metaDataManager->setMetaDataPropertyValue(
$metaDataAssetReference,
MetaDataPropertyName::fromString('copyright'),
$asset->getCopyrightNotice(),
);
}
}
}
}
14 changes: 14 additions & 0 deletions Classes/Configuration/MetaDataConfigurationProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);

namespace Neos\MetaData\Configuration;

use Neos\MetaData\Domain\Dto\MetaDataPropertyDefinitions;

/**
* Provider for global property configuration (usually from settings (YAML))
*/
interface MetaDataConfigurationProvider
{
public function getPropertyConfiguration(): MetaDataPropertyDefinitions;
}
59 changes: 59 additions & 0 deletions Classes/Configuration/MetaDataConfigurationProviderYamlAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);

namespace Neos\MetaData\Configuration;

use Neos\Flow\I18n\Translator;
use Neos\MetaData\Domain\Dto\MetaDataEditorDefinition;
use Neos\MetaData\Domain\Dto\MetaDataPropertyDefinition;
use Neos\MetaData\Domain\Dto\MetaDataPropertyDefinitions;
use Neos\MetaData\Domain\Dto\MetaDataPropertyName;
use Neos\MetaData\Domain\Dto\MetaDataPropertyType;
use Neos\MetaData\Domain\Dto\MetaDataPropertyUiDefinition;

class MetaDataConfigurationProviderYamlAdapter implements MetaDataConfigurationProvider
{
public function __construct(
private readonly array $propertyConfiguration,
private readonly Translator $translator,
)
{
}

public function getPropertyConfiguration(): MetaDataPropertyDefinitions
{
$propertyDefinitions = [];
foreach ($this->propertyConfiguration as $propertyName => $propertyDefinition) {
$propertyDefinitions[] = new MetaDataPropertyDefinition(
MetaDataPropertyName::fromString($propertyName),
match ($propertyDefinition['type'] ?? null) {
'integer' => MetaDataPropertyType::integer,
'boolean' => MetaDataPropertyType::boolean,
default => MetaDataPropertyType::string,
},
$propertyDefinition['globalScope'] ?? false,
new MetaDataPropertyUiDefinition(
$this->translatePropertyName($propertyName, $propertyDefinition['ui']['label']),
MetaDataEditorDefinition::create(
editorType: $propertyDefinition['ui']['inspector']['editor'] ?? null,
options: $propertyDefinition['ui']['inspector']['editorOptions'] ?? [],
)
)
);
}
return MetaDataPropertyDefinitions::create(...$propertyDefinitions);
}

// -----------------------

private function translatePropertyName(string $propertyName, ?string $label): string
{
if ($label === 'i18n') {
$translationShortHandString = sprintf('properties.%s', $propertyName);
return $this->translator->translateById($translationShortHandString, [], null, null, 'Main', 'Neos.MetaData') ?? $propertyName;
} elseif ($label !== null) {
return $label;
}
return $propertyName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);

namespace Neos\MetaData\Configuration;

use Neos\Flow\I18n\Translator;

class MetaDataConfigurationProviderYamlAdapterFactory
{
public function __construct(
private readonly array $propertyConfiguration,
private readonly Translator $translator,
)
{
}

public function create(): MetaDataConfigurationProvider
{
return new MetaDataConfigurationProviderYamlAdapter($this->propertyConfiguration, $this->translator);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Neos\MetaData\DimensionSpacePointProvider;

use Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoint;
use Neos\MetaData\Domain\Dto\MetaDataDimensionSpacePoints;

/**
* Provider for global dimension configuration (usually from the Neos Content Repository)
*/
interface DimensionSpacePointProvider
{
public function getDimensionSpacePoints(): MetaDataDimensionSpacePoints;

public function getDefaultDimensionSpacePoint(): MetaDataDimensionSpacePoint;

public function getDimensionSpacePointChain(MetaDataDimensionSpacePoint $dimensionSpacePoint): MetaDataDimensionSpacePoints;

public function isDimensionSpacePointValid(MetaDataDimensionSpacePoint $dimensionSpacePoint): bool;
}
Loading