Skip to content

Commit

Permalink
EZP-29137: As a developer, I want to replace missing images with plac…
Browse files Browse the repository at this point in the history
…eholders (ezsystems#2311)

* EZP-29137: As a developer, I want to replace missing images with placeholders

* EZP-29137: As a developer, I want to replace missing images with placeholders (impl. generic placeholder provider)

* EZP-29137: As a developer, I want to replace missing images with placeholders (impl. remote placeholder provider)

* EZP-29137: As a developer, I want to replace missing images with placeholders (configuration)
  • Loading branch information
adamwojs authored and Łukasz Serwatka committed May 24, 2018
1 parent 513898e commit 595f7b1
Show file tree
Hide file tree
Showing 18 changed files with 1,102 additions and 1 deletion.
@@ -0,0 +1,41 @@
<?php

/**
* This file is part of the eZ Publish Kernel package.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler;

use LogicException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class PlaceholderProviderPass implements CompilerPassInterface
{
const TAG_NAME = 'ezpublish.placeholder_provider';
const REGISTRY_DEFINITION_ID = 'ezpublish.image_alias.imagine.placeholder_provider.registry';

public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition(self::REGISTRY_DEFINITION_ID)) {
return;
}

$definition = $container->getDefinition(self::REGISTRY_DEFINITION_ID);
foreach ($container->findTaggedServiceIds(self::TAG_NAME) as $id => $attributes) {
foreach ($attributes as $attribute) {
if (!isset($attribute['type'])) {
throw new LogicException(self::TAG_NAME . ' service tag needs a "type" attribute to identify the placeholder provider type. None given.');
}

$definition->addMethodCall(
'addProvider',
[$attribute['type'], new Reference($id)]
);
}
}
}
}
Expand Up @@ -63,6 +63,7 @@ public function getConfigTreeBuilder()
$this->addRouterSection($rootNode);
$this->addRichTextSection($rootNode);
$this->addUrlAliasSection($rootNode);
$this->addImagePlaceholderSection($rootNode);

// Delegate SiteAccess config to configuration parsers
$this->mainConfigParser->addSemanticConfig($this->generateScopeBaseNode($rootNode));
Expand Down Expand Up @@ -570,6 +571,31 @@ function ($v) {
;
}

/**
* Defines configuration the images placeholder generation.
*
* @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode
*/
private function addImagePlaceholderSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('image_placeholder')
->info('Configuration for strategy of replacing missing images')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('provider')
->end()
->variableNode('options')
->defaultValue([])
->end()
->end()
->end()
->end()
->end();
}

/**
* Define Url Alias Slug converter Semantic Configuration.
*
Expand Down
Expand Up @@ -16,6 +16,7 @@
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilder;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface;
use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessConfigurationFilter;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
Expand Down Expand Up @@ -476,6 +477,19 @@ private function handleSiteAccessesRelation(ContainerBuilder $container)
private function handleImage(array $config, ContainerBuilder $container, FileLoader $loader)
{
$loader->load('image.yml');

$providers = [];
if (isset($config['image_placeholder'])) {
foreach ($config['image_placeholder'] as $name => $value) {
if (isset($providers[$name])) {
throw new InvalidConfigurationException("A image_placeholder named $name already exists");
}

$providers[$name] = $value;
}
}

$container->setParameter('image_alias.placeholder_providers', $providers);
}

private function handleUrlChecker($config, ContainerBuilder $container, FileLoader $loader)
Expand Down
2 changes: 2 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/EzPublishCoreBundle.php
Expand Up @@ -14,6 +14,7 @@
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ConfigResolverParameterPass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FragmentPass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\PlaceholderProviderPass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ImaginePass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\QueryTypePass;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterSearchEngineIndexerPass;
Expand Down Expand Up @@ -80,6 +81,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new URLHandlerPass());
$container->addCompilerPass(new BinaryContentDownloadPass());
$container->addCompilerPass(new ViewProvidersPass());
$container->addCompilerPass(new PlaceholderProviderPass());

// Storage passes
$container->addCompilerPass(new ExternalStorageRegistryPass());
Expand Down
103 changes: 103 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGenerator.php
@@ -0,0 +1,103 @@
<?php

/**
* This file is part of the eZ Publish Kernel package.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Bundle\EzPublishCoreBundle\Imagine;

use eZ\Publish\API\Repository\Values\Content\Field;
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
use eZ\Publish\Core\FieldType\Image\Value as ImageValue;
use eZ\Publish\Core\FieldType\Value;
use eZ\Publish\Core\IO\IOServiceInterface;
use eZ\Publish\SPI\Variation\VariationHandler;
use InvalidArgumentException;
use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException;
use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface;

class PlaceholderAliasGenerator implements VariationHandler
{
/**
* @var \eZ\Publish\SPI\Variation\VariationHandler
*/
private $aliasGenerator;

/**
* @var \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface
*/
private $ioResolver;

/**
* @var \eZ\Publish\Core\IO\IOServiceInterface
*/
private $ioService;

/**
* @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider|null
*/
private $placeholderProvider;

/**
* @var array
*/
private $placeholderOptions = [];

/**
* PlaceholderAliasGenerator constructor.
*
* @param \eZ\Publish\SPI\Variation\VariationHandler $aliasGenerator
* @param \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface $ioResolver
* @param \eZ\Publish\Core\IO\IOServiceInterface $ioService
*/
public function __construct(
VariationHandler $aliasGenerator,
ResolverInterface $ioResolver,
IOServiceInterface $ioService)
{
$this->aliasGenerator = $aliasGenerator;
$this->ioResolver = $ioResolver;
$this->ioService = $ioService;
}

/**
* {@inheritdoc}
*/
public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = [])
{
if ($this->placeholderProvider !== null) {
/** @var \eZ\Publish\Core\FieldType\Image\Value $imageValue */
$imageValue = $field->value;
if (!$this->supportsValue($imageValue)) {
throw new InvalidArgumentException("Value for field #{$field->id} ($field->fieldDefIdentifier) cannot be used for image placeholder generation.");
}

try {
$this->ioResolver->resolve($imageValue->id, IORepositoryResolver::VARIATION_ORIGINAL);
} catch (NotResolvableException $e) {
// Generate placeholder for original image
$binary = $this->ioService->newBinaryCreateStructFromLocalFile(
$this->placeholderProvider->getPlaceholder($imageValue, $this->placeholderOptions)
);
$binary->id = $imageValue->id;

$this->ioService->createBinaryFile($binary);
}
}

return $this->aliasGenerator->getVariation($field, $versionInfo, $variationName, $parameters);
}

public function setPlaceholderProvider(PlaceholderProvider $provider, array $options = [])
{
$this->placeholderProvider = $provider;
$this->placeholderOptions = $options;
}

public function supportsValue(Value $value): bool
{
return $value instanceof ImageValue;
}
}
@@ -0,0 +1,55 @@
<?php

/**
* This file is part of the eZ Publish Kernel package.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Bundle\EzPublishCoreBundle\Imagine;

use eZ\Publish\Core\MVC\ConfigResolverInterface;

class PlaceholderAliasGeneratorConfigurator
{
/**
* @var \eZ\Publish\Core\MVC\ConfigResolverInterface
*/
private $configResolver;

/**
* @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry
*/
private $providerRegistry;

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

/**
* PlaceholderAliasGeneratorConfigurator constructor.
*
* @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver
* @param \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry $providerRegistry
* @param array $providersConfig
*/
public function __construct(ConfigResolverInterface $configResolver, PlaceholderProviderRegistry $providerRegistry, array $providersConfig)
{
$this->configResolver = $configResolver;
$this->providerRegistry = $providerRegistry;
$this->providersConfig = $providersConfig;
}

public function configure(PlaceholderAliasGenerator $generator)
{
$binaryHandlerName = $this->configResolver->getParameter('io.binarydata_handler');

if (isset($this->providersConfig[$binaryHandlerName])) {
$providersConfig = $this->providersConfig[$binaryHandlerName];

$provider = $this->providerRegistry->getProvider($providersConfig['provider']);
$generator->setPlaceholderProvider($provider, $providersConfig['options']);
}
}
}
22 changes: 22 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider.php
@@ -0,0 +1,22 @@
<?php
/**
* This file is part of the eZ Publish Kernel package.
*
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
namespace eZ\Bundle\EzPublishCoreBundle\Imagine;

use eZ\Publish\Core\FieldType\Image\Value as ImageValue;

interface PlaceholderProvider
{
/**
* Provides a placeholder image path for a given Image FieldType value.
*
* @param \eZ\Publish\Core\FieldType\Image\Value $value
* @param array $options
* @return string Path to placeholder
*/
public function getPlaceholder(ImageValue $value, array $options = []): string;
}

0 comments on commit 595f7b1

Please sign in to comment.