Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch '7.3'
  • Loading branch information
andrerom committed Dec 20, 2018
2 parents 3e0b856 + 6085e25 commit 9d0d49d
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 78 deletions.
266 changes: 266 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Command/CleanupVersionsCommand.php
@@ -0,0 +1,266 @@
<?php

/**
* @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\Command;

use Doctrine\DBAL\Connection;
use Exception;
use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider;
use eZ\Publish\API\Repository\Repository;
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
use PDO;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Output\OutputInterface;

class CleanupVersionsCommand extends Command
{
const DEFAULT_REPOSITORY_USER = 'admin';

const VERSION_DRAFT = 'draft';
const VERSION_ARCHIVED = 'archived';
const VERSION_PUBLISHED = 'published';
const VERSION_ALL = 'all';

const VERSION_STATUS = [
self::VERSION_DRAFT => VersionInfo::STATUS_DRAFT,
self::VERSION_ARCHIVED => VersionInfo::STATUS_ARCHIVED,
self::VERSION_PUBLISHED => VersionInfo::STATUS_PUBLISHED,
];

/**
* @var \eZ\Publish\API\Repository\Repository
*/
private $repository;

/**
* @var \eZ\Publish\API\Repository\UserService
*/
private $userService;

/**
* @var \eZ\Publish\API\Repository\ContentService
*/
private $contentService;

/**
* @var \eZ\Publish\API\Repository\PermissionResolver
*/
private $permissionResolver;

/**
* @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider
*/
private $repositoryConfigurationProvider;

/**
* @var \Doctrine\DBAL\Driver\Connection
*/
private $connection;

public function __construct(
Repository $repository,
RepositoryConfigurationProvider $repositoryConfigurationProvider,
Connection $connection
) {
$this->repository = $repository;
$this->repositoryConfigurationProvider = $repositoryConfigurationProvider;
$this->connection = $connection;

parent::__construct();
}

protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);

$this->userService = $this->repository->getUserService();
$this->contentService = $this->repository->getContentService();
$this->permissionResolver = $this->repository->getPermissionResolver();

$this->permissionResolver->setCurrentUserReference(
$this->userService->loadUserByLogin($input->getOption('user'))
);
}

protected function configure()
{
$config = $this->repositoryConfigurationProvider->getRepositoryConfig();

$this
->setName('ezplatform:content:cleanup-versions')
->setDescription('Remove unwanted content versions. It keeps published version untouched. By default, it keeps also the last archived/draft version.')
->addOption(
'status',
't',
InputOption::VALUE_OPTIONAL,
sprintf(
"Select which version types should be removed: '%s', '%s', '%s'.",
self::VERSION_DRAFT,
self::VERSION_ARCHIVED,
self::VERSION_ALL
),
self::VERSION_ALL
)
->addOption(
'keep',
'k',
InputOption::VALUE_OPTIONAL,
"Sets number of the most recent versions (both drafts and archived) which won't be removed.",
$config['options']['default_version_archive_limit']
)
->addOption(
'user',
'u',
InputOption::VALUE_OPTIONAL,
'eZ Platform username (with Role containing at least Content policies: remove, read, versionread)',
self::DEFAULT_REPOSITORY_USER
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
if (($keep = (int) $input->getOption('keep')) < 0) {
throw new InvalidArgumentException(
'status',
'Keep value can not be negative.'
);
}

$status = $input->getOption('status');

$contentIds = $this->getObjectsIds($keep, $status);
$contentIdsCount = count($contentIds);

if ($contentIdsCount === 0) {
$output->writeln('<info>There is no Content matching given criteria.</info>');

return;
}

$output->writeln(sprintf(
'<info>Found %d Content IDs matching given criteria.</info>',
$contentIdsCount
));

$removedVersionsCounter = 0;

$removeAll = $status === self::VERSION_ALL;
$removeDrafts = $status === self::VERSION_DRAFT;
$removeArchived = $status === self::VERSION_ARCHIVED;

foreach ($contentIds as $contentId) {
try {
$contentInfo = $this->contentService->loadContentInfo((int) $contentId);
$versions = $this->contentService->loadVersions($contentInfo);
$versionsCount = count($versions);

$output->writeln(sprintf(
'<info>Content %d has %d version(s)</info>',
(int) $contentId,
$versionsCount
), Output::VERBOSITY_VERBOSE);

$versions = array_filter($versions, function ($version) use ($removeAll, $removeDrafts, $removeArchived) {
if (
($removeAll && $version->status !== VersionInfo::STATUS_PUBLISHED) ||
($removeDrafts && $version->status === VersionInfo::STATUS_DRAFT) ||
($removeArchived && $version->status === VersionInfo::STATUS_ARCHIVED)
) {
return $version;
}
});

if ($keep > 0) {
$versions = array_slice($versions, 0, -$keep);
}

$output->writeln(sprintf(
"Found %d content's (%d) version(s) to remove.",
count($versions),
(int) $contentId
), Output::VERBOSITY_VERBOSE);

/** @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $version */
foreach ($versions as $version) {
$this->contentService->deleteVersion($version);
++$removedVersionsCounter;
$output->writeln(sprintf(
"Content's (%d) version (%d) has been deleted.",
$contentInfo->id,
$version->id
), Output::VERBOSITY_VERBOSE);
}
} catch (Exception $e) {
$output->writeln(sprintf(
'<error>%s</error>',
$e->getMessage()
));
}
}

$output->writeln(sprintf(
'<info>Removed %d unwanted contents version(s).</info>',
$removedVersionsCounter
));
}

/**
* @param int $keep
* @param string $status
*
* @return array
*
* @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
*/
protected function getObjectsIds($keep, $status)
{
$query = $this->connection->createQueryBuilder()
->select('c.id')
->from('ezcontentobject', 'c')
->join('c', 'ezcontentobject_version', 'v', 'v.contentobject_id = c.id')
->groupBy('c.id', 'v.status')
->having('count(c.id) > :keep');
$query->setParameter('keep', $keep);

if ($status !== self::VERSION_ALL) {
$query->where('v.status = :status');
$query->setParameter('status', $this->mapStatusToVersionInfoStatus($status));
} else {
$query->andWhere('v.status != :status');
$query->setParameter('status', $this->mapStatusToVersionInfoStatus(self::VERSION_PUBLISHED));
}

$stmt = $query->execute();

return $stmt->fetchAll(PDO::FETCH_COLUMN);
}

/**
* @param string $status
*
* @return int
*
* @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
*/
private function mapStatusToVersionInfoStatus($status)
{
if (array_key_exists($status, self::VERSION_STATUS)) {
return self::VERSION_STATUS[$status];
}

throw new InvalidArgumentException(
'status',
sprintf(
"Status %s can't be mapped to VersionInfo status.",
$status
)
);
}
}
Expand Up @@ -115,12 +115,17 @@ function ($v) {
->end()
->end()
->end()
->end()
->scalarNode('image_host')
->info('Images host. All system images URLs are prefixed with given host if configured.')
->example('https://ezplatform.com')
->end();
}

public function preMap(array $config, ContextualizerInterface $contextualizer)
{
$contextualizer->mapConfigArray('image_variations', $config);
$contextualizer->mapSetting('image_host', $config);
}

public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer)
Expand Down
44 changes: 44 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Imagine/ResolverFactory.php
@@ -0,0 +1,44 @@
<?php

/**
* @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;
use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface;

class ResolverFactory
{
/**
* @var \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface
*/
private $resolver;

/**
* @var string
*/
private $proxyResolverClass;

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

public function __construct(ConfigResolverInterface $configResolver, ResolverInterface $resolver, $proxyResolverClass)
{
$this->resolver = $resolver;
$this->proxyResolverClass = $proxyResolverClass;

if ($configResolver->hasParameter('image_host') &&
($imageHost = $configResolver->getParameter('image_host')) !== '') {
$this->hosts = [$imageHost];
}
}

public function createCacheResolver()
{
return new $this->proxyResolverClass($this->resolver, $this->hosts);
}
}
Expand Up @@ -190,6 +190,8 @@ parameters:
resize: "-resize {1}"
optimize: "-strip"

ezsettings.default.image_host: ''

###
# default ezpage settings
##
Expand Down
14 changes: 14 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/image.yml
@@ -1,6 +1,8 @@
parameters:
ezpublish.image_alias.imagine.binary_loader.class: eZ\Bundle\EzPublishCoreBundle\Imagine\BinaryLoader
ezpublish.image_alias.imagine.decorated_cache_resolver_factory.class: eZ\Bundle\EzPublishCoreBundle\Imagine\ResolverFactory
ezpublish.image_alias.imagine.cache_resolver.class: eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver
ezpublish.image_alias.imagine.cache_resolver_decorator.class: Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver
ezpublish.image_alias.imagine.cache.alias_generator_decorator.class: eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator
ezpublish.image_alias.imagine.variation.imagine_alias_generator.class: eZ\Bundle\EzPublishCoreBundle\Imagine\Variation\ImagineAwareAliasGenerator
ezpublish.image_alias.imagine.alias_generator.class: eZ\Bundle\EzPublishCoreBundle\Imagine\AliasGenerator
Expand Down Expand Up @@ -81,6 +83,18 @@ services:
tags:
- { name: liip_imagine.cache.resolver, resolver: ezpublish }

ezpublish.image_alias.imagine.cache_resolver_decorator_factory:
class: '%ezpublish.image_alias.imagine.decorated_cache_resolver_factory.class%'
arguments:
- '@ezpublish.config.resolver'
- '@ezpublish.image_alias.imagine.cache_resolver_decorator.inner'
- '%ezpublish.image_alias.imagine.cache_resolver_decorator.class%'

ezpublish.image_alias.imagine.cache_resolver_decorator:
class: '%ezpublish.image_alias.imagine.cache_resolver_decorator.class%'
factory: 'ezpublish.image_alias.imagine.cache_resolver_decorator_factory:createCacheResolver'
decorates: ezpublish.image_alias.imagine.cache_resolver

ezpublish.image_alias.imagine.cache.alias_generator_decorator:
class: '%ezpublish.image_alias.imagine.cache.alias_generator_decorator.class%'
arguments:
Expand Down
9 changes: 9 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml
Expand Up @@ -250,6 +250,15 @@ services:
tags:
- { name: console.command }

ezplatform.core.command.cleanup_versions:
class: eZ\Bundle\EzPublishCoreBundle\Command\CleanupVersionsCommand
arguments:
- "@ezpublish.signalslot.repository"
- "@ezpublish.api.repository_configuration_provider"
- "@ezpublish.persistence.connection"
tags:
- { name: console.command }

ezplatform.core.session.handler.native_redis:
class: eZ\Bundle\EzPublishCoreBundle\Session\Handler\NativeSessionHandler
arguments:
Expand Down
7 changes: 3 additions & 4 deletions eZ/Publish/Core/Persistence/Cache/ContentHandler.php
Expand Up @@ -64,13 +64,12 @@ public function copy($contentId, $versionNo = null, $newOwnerId = null)

/**
* {@inheritdoc}
*
* @todo Add support for setting version number to null in order to reuse cache with loadContentList.
*/
public function load($contentId, $versionNo, array $translations = null)
public function load($contentId, $versionNo = null, array $translations = null)
{
$versionKey = $versionNo ? "-${versionNo}" : '';
$translationsKey = empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations);
$cacheItem = $this->cache->getItem("ez-content-${contentId}-${versionNo}-${translationsKey}");
$cacheItem = $this->cache->getItem("ez-content-${contentId}${versionKey}-${translationsKey}");
if ($cacheItem->isHit()) {
return $cacheItem->get();
}
Expand Down

0 comments on commit 9d0d49d

Please sign in to comment.