Skip to content

Introduced MediaGalleryMetadata services with XMP support #1514

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

Merged
merged 12 commits into from
Jul 4, 2020
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
39 changes: 39 additions & 0 deletions MediaGalleryMetadata/Model/AddMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\MediaGalleryMetadata\Model;

use Magento\MediaGalleryMetadataApi\Api\AddMetadataInterface;
use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterface;
use Magento\MediaGalleryMetadataApi\Model\AddMetadataComposite;

/**
* Add metadata to the asset by path
*/
class AddMetadata implements AddMetadataInterface
{
/**
* @var AddMetadataComposite
*/
private $addMetadataComposite;

/**
* @param AddMetadataComposite $addMetadataComposite
*/
public function __construct(AddMetadataComposite $addMetadataComposite)
{
$this->addMetadataComposite = $addMetadataComposite;
}

/**
* @inheritdoc
*/
public function execute(string $path, MetadataInterface $data): void
{
$this->addMetadataComposite->execute($path, $data);
}
}
79 changes: 79 additions & 0 deletions MediaGalleryMetadata/Model/AddXmpMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\MediaGalleryMetadata\Model;

use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterface;

/**
* Add metadata to the XMP data
*/
class AddXmpMetadata
{
private const XMP_XPATH_SELECTOR_TITLE = '//dc:title/rdf:Alt/rdf:li';
private const XMP_XPATH_SELECTOR_DESCRIPTION = '//dc:description/rdf:Alt/rdf:li';
private const XMP_XPATH_SELECTOR_KEYWORDS = '//dc:subject/rdf:Bag';
private const XMP_XPATH_SELECTOR_KEYWORDS_EACH = '//dc:subject/rdf:Bag/rdf:li';
private const XMP_XPATH_SELECTOR_KEYWORD_ITEM = 'rdf:li';

/**
* Parse metadata
*
* @param string $data
* @param MetadataInterface $metadata
* @return string
*/
public function execute(string $data, MetadataInterface $metadata): string
{
$xml = simplexml_load_string($data);
$namespaces = $xml->getNamespaces(true);

foreach ($namespaces as $prefix => $url) {
$xml->registerXPathNamespace($prefix, $url);
}

$this->setValueByXpath($xml, self::XMP_XPATH_SELECTOR_TITLE, $metadata->getTitle());
$this->setValueByXpath($xml, self::XMP_XPATH_SELECTOR_DESCRIPTION, $metadata->getDescription());
$this->updateKeywords($xml, $metadata->getKeywords());

$data = $xml->asXML();
return str_replace("<?xml version=\"1.0\"?>\n", '', $data);
}

/**
* Update keywords
*
* @param \SimpleXMLElement $xml
* @param array $keywords
*/
private function updateKeywords(\SimpleXMLElement $xml, array $keywords): void
{
foreach ($xml->xpath(self::XMP_XPATH_SELECTOR_KEYWORDS_EACH) as $keywordElement) {
unset($keywordElement[0]);
}

foreach ($xml->xpath(self::XMP_XPATH_SELECTOR_KEYWORDS) as $element) {
foreach ($keywords as $keyword) {
$element->addChild(self::XMP_XPATH_SELECTOR_KEYWORD_ITEM, $keyword);
}
}
}

/**
* Set value to xml node by xpath
*
* @param \SimpleXMLElement $xml
* @param string $xpath
* @param string $value
*/
private function setValueByXpath(\SimpleXMLElement $xml, string $xpath, string $value): void
{
foreach ($xml->xpath($xpath) as $element) {
$element[0] = $value;
}
}
}
64 changes: 64 additions & 0 deletions MediaGalleryMetadata/Model/ExtractMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\MediaGalleryMetadata\Model;

use Magento\MediaGalleryMetadataApi\Model\ExtractMetadataPool;
use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterface;
use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterfaceFactory;
use Magento\MediaGalleryMetadataApi\Api\ExtractMetadataInterface;

/**
* Extract metadata from the asset by path
*/
class ExtractMetadata implements ExtractMetadataInterface
{
/**
* @var ExtractMetadataPool
*/
private $extractorsPool;

/**
* @var MetadataInterfaceFactory
*/
private $metadataFactory;

/**
* @param ExtractMetadataPool $extractorsPool
* @param MetadataInterfaceFactory $metadataFactory
*/
public function __construct(ExtractMetadataPool $extractorsPool, MetadataInterfaceFactory $metadataFactory)
{
$this->extractorsPool = $extractorsPool;
$this->metadataFactory = $metadataFactory;
}

/**
* @inheritdoc
*/
public function execute(string $path): MetadataInterface
{
$title = '';
$description = '';
$keywords = [];
foreach ($this->extractorsPool->get() as $extractor) {
$data = $extractor->execute($path);
$title = $data->getTitle() ?? $title;
$description = $data->getDescription() ?? $description;
// phpcs:ignore Magento2.Performance.ForeachArrayMerge
$keywords = array_merge($keywords, $data->getKeywords());
if (!empty($title) && !empty($description) && !empty($keywords)) {
break;
}
}
return $this->metadataFactory->create([
'title' => $title,
'description' => $description,
'keywords' => array_unique($keywords)
]);
}
}
79 changes: 79 additions & 0 deletions MediaGalleryMetadata/Model/File.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\MediaGalleryMetadata\Model;

use Magento\MediaGalleryMetadataApi\Model\FileInterface;
use Magento\MediaGalleryMetadataApi\Model\FileExtensionInterface;

/**
* File internal data transfer object
*/
class File implements FileInterface
{
/**
* @var string
*/
private $path;

/**
* @var array
*/
private $segments;

/**
* @var FileExtensionInterface|null
*/
private $extensionAttributes;

/**
* @param string $path
* @param array $segments
* @param FileExtensionInterface|null $extensionAttributes
*/
public function __construct(
string $path,
array $segments,
?FileExtensionInterface $extensionAttributes = null
) {
$this->path = $path;
$this->segments = $segments;
$this->extensionAttributes = $extensionAttributes;
}

/**
* @inheritdoc
*/
public function getSegments(): array
{
return $this->segments;
}

/**
* @inheritdoc
*/
public function getPath(): string
{
return $this->path;
}

/**
* @inheritdoc
*/
public function getExtensionAttributes(): ?FileExtensionInterface
{
return $this->extensionAttributes;
}

/**
* @inheritdoc
*/
public function setExtensionAttributes(?FileExtensionInterface $extensionAttributes): void
{
$this->extensionAttributes = $extensionAttributes;
}
}
102 changes: 102 additions & 0 deletions MediaGalleryMetadata/Model/File/AddMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\MediaGalleryMetadata\Model\File;

use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\LocalizedException;
use Magento\MediaGalleryMetadataApi\Api\AddMetadataInterface;
use Magento\MediaGalleryMetadataApi\Api\Data\MetadataInterface;
use Magento\MediaGalleryMetadataApi\Model\FileInterface;
use Magento\MediaGalleryMetadataApi\Model\FileReaderInterface;
use Magento\MediaGalleryMetadataApi\Model\FileWriterInterface;
use Magento\MediaGalleryMetadataApi\Model\MetadataWriterInterface;

/**
* Add metadata to the asset by path. Should be used as a virtual type with a file type specific configuration
*/
class AddMetadata implements AddMetadataInterface
{
/**
* @var MetadataWriterInterface[]
*/
private $writers;

/**
* @var FileReaderInterface
*/
private $fileReader;

/**
* @var FileWriterInterface
*/
private $fileWriter;

/**
* @param FileReaderInterface $fileReader
* @param FileWriterInterface $fileWriter
* @param MetadataWriterInterface[] $writers
*/
public function __construct(FileReaderInterface $fileReader, FileWriterInterface $fileWriter, array $writers)
{
$this->fileReader = $fileReader;
$this->fileWriter = $fileWriter;
$this->writers = $writers;
}

/**
* @inheritdoc
*/
public function execute(string $path, MetadataInterface $metadata): void
{
if (!$this->fileReader->isApplicable($path)) {
return;
}

try {
$file = $this->fileReader->execute($path);
} catch (\Exception $exception) {
throw new LocalizedException(
__(
'Could not parse the image file for metadata: %path',
[
'path' => $path
]
)
);
}

try {
$this->writeMetadata($file, $metadata);
} catch (\Exception $exception) {
throw new LocalizedException(
__(
'Could not update the image file metadata: %path',
[
'path' => $path
]
)
);
}
}

/**
* Write metadata to the filesystem
*
* @param FileInterface $file
* @param MetadataInterface $metadata
* @throws LocalizedException
* @throws FileSystemException
*/
private function writeMetadata(FileInterface $file, MetadataInterface $metadata): void
{
foreach ($this->writers as $writer) {
$file = $writer->execute($file, $metadata);
}
$this->fileWriter->execute($file);
}
}
Loading