Skip to content

Commit

Permalink
Merge pull request #2080 from alongosz/ezp-27842-improve-remove-trans…
Browse files Browse the repository at this point in the history
…l-from-content

EZP-27842: Improve process of Translation removal from all Versions
  • Loading branch information
andrerom committed Sep 20, 2017
2 parents ab984a9 + 4db1785 commit d0f50d7
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<?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\Command;

use eZ\Publish\API\Repository\Repository;
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Exception;

/**
* Console Command which removes a given Translation from all the Versions of a given Content Object.
*/
class RemoveContentTranslationCommand extends Command
{
/**
* @var \eZ\Publish\API\Repository\Repository
*/
private $repository;

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

/**
* @var \Symfony\Component\Console\Input\InputInterface
*/
private $input;

/**
* @var \Symfony\Component\Console\Output\OutputInterface
*/
private $output;

/**
* @var \Symfony\Component\Console\Helper\QuestionHelper
*/
private $questionHelper;

public function __construct(Repository $repository)
{
parent::__construct(null);
$this->repository = $repository;
}

/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('ezplatform:remove-content-translation')
->addArgument('content-id', InputArgument::REQUIRED, 'Content Object Id')
->addArgument(
'language-code',
InputArgument::REQUIRED,
'Language code of the Translation to be removed'
)
->addOption(
'user',
'u',
InputOption::VALUE_OPTIONAL,
'eZ Platform username (with Role containing at least Content policies: read, versionread, edit, remove, versionremove)',
'admin'
)
->setDescription('Remove Translation from all the Versions of a Content Object');
}

protected function initialize(InputInterface $input, OutputInterface $output)
{
parent::initialize($input, $output);
$this->input = $input;
$this->output = $output;
$this->questionHelper = $this->getHelper('question');
$this->contentService = $this->repository->getContentService();

$this->repository->getPermissionResolver()->setCurrentUserReference(
$this->repository->getUserService()->loadUserByLogin($input->getOption('user'))
);
}

/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$contentId = intval($input->getArgument('content-id'));
$languageCode = $input->getArgument('language-code');

if ($contentId === 0) {
throw new InvalidArgumentException(
'content-id',
'Content Object Id has to be an integer'
);
}

$this->output->writeln(
'<comment>**NOTE**: Make sure to run this command using the same SYMFONY_ENV setting as your eZ Platform installation does</comment>'
);

$contentInfo = $this->contentService->loadContentInfo($contentId);

$this->repository->beginTransaction();
try {
$allLanguages = $this->removeAffectedSingularLanguageVersions(
$contentInfo,
$languageCode
);
if ($contentInfo->mainLanguageCode === $languageCode) {
$contentInfo = $this->promptUserForMainLanguageChange(
$contentInfo,
$languageCode,
$allLanguages
);
}

// Confirm operation
$contentName = "#{$contentInfo->id} ($contentInfo->name)";
$question = new ConfirmationQuestion(
"Are you sure you want to remove {$languageCode} Translation from the Content {$contentName}? This operation is permanent. [y/N] ",
false
);
if (!$this->questionHelper->ask($this->input, $this->output, $question)) {
// Rollback any cleanup change (see above)
$this->repository->rollback();
$this->output->writeln('Reverting and aborting.');

return;
}

// Remove Translation
$output->writeln(
"<info>Removing {$languageCode} Translation of the Content {$contentName}</info>"
);
$this->contentService->removeTranslation($contentInfo, $languageCode);

$output->writeln('<info>Translation removed</info>');

$this->repository->commit();
} catch (Exception $e) {
$this->repository->rollback();
throw $e;
}
}

/**
* Cleanup Versions before removing Translation and collect existing Translations languages.
*
* @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
* @param string $languageCode
*
* @return string[] unique Language codes across all Versions of the Content.
*/
private function removeAffectedSingularLanguageVersions(ContentInfo $contentInfo, $languageCode)
{
$languages = [];
foreach ($this->contentService->loadVersions($contentInfo) as $versionInfo) {
// if this is the only one Translation, just delete entire Version
if (count($versionInfo->languageCodes) === 1 && $versionInfo->languageCodes[0] === $languageCode) {
// Note: won't work on published Versions and last remaining Version
$this->contentService->deleteVersion($versionInfo);
continue;
}

foreach ($versionInfo->languageCodes as $lang) {
if ($lang === $languageCode || in_array($lang, $languages)) {
continue;
}
$languages[] = $lang;
}
}

return $languages;
}

/**
* Interact with user to update main Language of a Content Object.
*
* @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
* @param string $languageCode language code of the Translation to be removed
* @param string[] $allLanguages all languages Content Object Versions have, w/o $languageCode
*
* @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
*/
private function promptUserForMainLanguageChange(
ContentInfo $contentInfo,
$languageCode,
array $allLanguages
) {
$contentName = "#{$contentInfo->id} ($contentInfo->name)";
$this->output->writeln(
"<comment>The specified language '{$languageCode}' is the main language of the Content {$contentName}. It needs to be changed before removal.</comment>"
);

$question = new ChoiceQuestion(
"Set the main language of the Content {$contentName} to:",
$allLanguages
);

$newMainLanguageCode = $this->questionHelper->ask($this->input, $this->output, $question);
$this->output->writeln(
"<info>Updating Main Language of the Content {$contentName} to {$newMainLanguageCode}</info>"
);

$contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
$contentMetadataUpdateStruct->mainLanguageCode = $newMainLanguageCode;

return $this->contentService->updateContentMetadata(
$contentInfo,
$contentMetadataUpdateStruct
)->contentInfo;
}
}
7 changes: 7 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,10 @@ services:
class: eZ\Bundle\EzPublishCoreBundle\EventSubscriber\CrowdinRequestLocaleSubscriber
tags:
- {name: kernel.event_subscriber}

ezplatform.core.command.remove_content_translation:
class: eZ\Bundle\EzPublishCoreBundle\Command\RemoveContentTranslationCommand
arguments:
- '@ezpublish.api.repository'
tags:
- { name: console.command }
2 changes: 1 addition & 1 deletion eZ/Publish/API/Repository/Tests/ContentServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5294,7 +5294,7 @@ public function testRemoveTranslationMainLanguageThrowsBadStateException()
*
* @covers \eZ\Publish\Core\Repository\ContentService::removeTranslation
* @expectedException \eZ\Publish\API\Repository\Exceptions\BadStateException
* @expectedExceptionMessage Specified translation is the only one Content Object Version has
* @expectedExceptionMessageRegExp /The Version\(s\): \d+ of the ContentId=\d+ have only one language eng-US/
*/
public function testRemoveTranslationLastLanguageThrowsBadStateException()
{
Expand Down
22 changes: 15 additions & 7 deletions eZ/Publish/Core/Repository/ContentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,7 @@ public function removeTranslation(ContentInfo $contentInfo, $languageCode)
}
// before actual removal, collect information on Versions
$versions = [];
$singleLangVersions = [];
foreach ($this->loadVersions($contentInfo) as $versionInfo) {
// check if user is authorized to delete Version
if (!$this->repository->canUser('content', 'remove', $versionInfo)) {
Expand All @@ -1915,15 +1916,22 @@ public function removeTranslation(ContentInfo $contentInfo, $languageCode)
// if translation does not exist, simply ignore Version (see InvalidArgumentException later on)
continue;
}
// check if the specified translation is not the only one
// check if the specified translation is the only one
if (count($versionInfo->languageCodes) < 2) {
throw new BadStateException(
'$languageCode',
'Specified translation is the only one Content Object Version has'
);
$singleLangVersions[] = $versionInfo->versionNo;
} else {
// otherwise add Version to the list of valid Versions to be processed
$versions[] = $versionInfo->versionNo;
}
// add version to the list
$versions[] = $versionInfo->versionNo;
}

if (!empty($singleLangVersions)) {
$verList = implode(', ', $singleLangVersions);
throw new BadStateException(
'$languageCode',
"The Version(s): {$verList} of the ContentId={$contentInfo->id} have only one language {$languageCode}. Remove these Version(s) before proceeding.\n" .
"Hint: Command 'ezplatform:remove-content-translation' handles this for you, look into it to see how this can be handled in custom code."
);
}

// if there are no Versions with the given translation, $languageCode arg is invalid
Expand Down

0 comments on commit d0f50d7

Please sign in to comment.