Skip to content

Commit

Permalink
Adding "ezxhtml:class="ez-embed-type-image" attribute to embedded images
Browse files Browse the repository at this point in the history
  • Loading branch information
vidarl committed May 3, 2018
1 parent a139832 commit 773d778
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 47 deletions.
188 changes: 148 additions & 40 deletions bundle/Command/ConvertXmlTextToRichTextCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ class ConvertXmlTextToRichTextCommand extends ContainerAwareCommand
*/
private $logger;

public function __construct(Connection $dbal, LoggerInterface $logger = null)
/**
* @var RichTextConverter
*/
private $converter;

public function __construct(Connection $dbal, RichTextConverter $converter, LoggerInterface $logger = null)
{
parent::__construct();

$this->dbal = $dbal;
$this->logger = $logger;
$this->converter = $converter;
}

protected function configure()
Expand Down Expand Up @@ -64,11 +70,26 @@ protected function configure()
null,
InputOption::VALUE_OPTIONAL,
'Test if converting object with the given id succeeds'
)
->addOption(
'image-content-types',
null,
InputOption::VALUE_OPTIONAL,
'Comma separated list of content types which are considered as images when converting embedded tags. Default value is 27'
)
->addOption(
'fix-embedded-images-only',
null,
InputOption::VALUE_NONE,
"Use this option to ensure that embedded images in a database are tagget correctly so that the editor will detect them as such.\n
This option is needed if you have an existing ezplatform database which was converted with an earlier version of\n
'ezxmltext:convert-to-richtext' which did not convert embedded images correctly."
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$this->loginAsAdmin();
$dryRun = false;
if ($input->getOption('dry-run')) {
$output->writeln("Running in dry-run mode. No changes will actually be written to database\n");
Expand All @@ -77,14 +98,82 @@ protected function execute(InputInterface $input, OutputInterface $output)

$testContentObjectId = $input->getOption('test-content-object');

if ($input->getOption('image-content-types')) {
$contentTypes = explode(',', $input->getOption('image-content-types'));
} else {
$contentTypes = array(27);
}
$this->converter->setImageContentTypes($contentTypes);

if ($input->getOption('fix-embedded-images-only')) {
$output->writeln("Fixing embedded images only. No other changes are done to the database\n");
$this->fixEmbeddedImages($dryRun, $testContentObjectId, $output);
return;
}

if ($testContentObjectId === null) {
$this->convertFieldDefinitions($dryRun, $output);
} else {
$dryRun = true;
}

$this->convertFields($dryRun, $testContentObjectId, !$input->getOption('disable-duplicate-id-check'), $output);
}

protected function loginAsAdmin()
{
$userService = $this->getContainer()->get('ezpublish.api.service.user');
$permissionResolver = $this->getContainer()->get('date_based_publisher.permission_resolver');
$permissionResolver->setCurrentUserReference($userService->loadUserByLogin('admin'));
}

protected function fixEmbeddedImages($dryRun, $contentId, OutputInterface $output)
{
$count = $this->getRowCountOfContentObjectAttributes('ezrichtext', $contentId);

$output->writeln("Found $count field rows to convert.");

$statement = $this->getFieldRows('ezrichtext', $contentId);

$totalCount = 0;
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
if (empty($row['data_text'])) {
$inputValue = Value::EMPTY_VALUE;
} else {
$inputValue = $row['data_text'];
}

$xmlDoc = $this->createDocument($inputValue);
$count = $this->converter->tagEmbeddedImages($xmlDoc);
if ($count > 0) {
++$totalCount;
}
$converted = $xmlDoc->saveXML();

if ($count === 0) {
$this->logger->info(
"No embedded image(s) in ezrichtext field #{$row['id']} needed to be updated",
[
'original' => $inputValue
]
);

} else {
$this->updateFieldRow($dryRun, $row['id'], $row['version'], $converted);

$this->logger->info(
"Updated $count embded image(s) in ezrichtext field #{$row['id']}",
[
'original' => $inputValue,
'converted' => $converted
]
);
}
}

$output->writeln("Updated ezembed tags in $totalCount field(s)");
}

protected function convertFieldDefinitions($dryRun, OutputInterface $output)
{
$query = $this->dbal->createQueryBuilder();
Expand Down Expand Up @@ -127,9 +216,8 @@ protected function convertFieldDefinitions($dryRun, OutputInterface $output)
$output->writeln("Converted $count ezxmltext field definitions to ezrichtext");
}

protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds, OutputInterface $output)
protected function getRowCountOfContentObjectAttributes($datatypeString, $contentObjectId)
{
$converter = new RichTextConverter($this->logger);
$query = $this->dbal->createQueryBuilder();
$query->select('count(a.id)')
->from('ezcontentobject_attribute', 'a')
Expand All @@ -139,7 +227,7 @@ protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds,
':datatypestring'
)
)
->setParameter(':datatypestring', 'ezxmltext');
->setParameter(':datatypestring',$datatypeString);

if ($contentObjectId !== null) {
$query->andWhere(
Expand All @@ -148,14 +236,20 @@ protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds,
':contentobjectid'
)
)
->setParameter(':contentobjectid', $contentObjectId);
->setParameter(':contentobjectid', $contentObjectId);
}

$statement = $query->execute();
$count = (int) $statement->fetchColumn();

$output->writeln("Found $count field rows to convert.");
return (int) $statement->fetchColumn();
}

/**
* @param $datatypeString
* @param $contentId
* @return \Doctrine\DBAL\Driver\Statement|int
*/
protected function getFieldRows($datatypeString, $contentId)
{
$query = $this->dbal->createQueryBuilder();
$query->select('a.*')
->from('ezcontentobject_attribute', 'a')
Expand All @@ -165,18 +259,57 @@ protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds,
':datatypestring'
)
)
->setParameter(':datatypestring', 'ezxmltext');
->setParameter(':datatypestring',$datatypeString);

if ($contentObjectId !== null) {
if ($contentId !== null) {
$query->andWhere(
$query->expr()->eq(
'a.contentobject_id',
':contentobjectid'
)
)
->setParameter(':contentobjectid', $contentObjectId);
->setParameter(':contentobjectid', $contentId);
}
$statement = $query->execute();
return $query->execute();
}

protected function updateFieldRow($dryRun, $id, $version, $datatext)
{
$updateQuery = $this->dbal->createQueryBuilder();
$updateQuery->update('ezcontentobject_attribute', 'a')
->set('a.data_type_string', ':datatypestring')
->set('a.data_text', ':datatext')
->where(
$updateQuery->expr()->eq(
'a.id',
':id'
)
)
->andWhere(
$updateQuery->expr()->eq(
'a.version',
':version'
)
)
->setParameters(array(
':datatypestring' => 'ezrichtext',
':datatext' => $datatext,
':id' => $id,
':version' => $version
));

if (!$dryRun) {
$updateQuery->execute();
}
}

protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds, OutputInterface $output)
{
$count = $this->getRowCountOfContentObjectAttributes('ezxmltext', $contentObjectId);

$output->writeln("Found $count field rows to convert.");

$statement = $this->getFieldRows('ezxmltext', $contentObjectId);

while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
if (empty($row['data_text'])) {
Expand All @@ -185,34 +318,9 @@ protected function convertFields($dryRun, $contentObjectId, $checkDuplicateIds,
$inputValue = $row['data_text'];
}

$converted = $converter->convert($this->createDocument($inputValue), $checkDuplicateIds, $row['id']);

$updateQuery = $this->dbal->createQueryBuilder();
$updateQuery->update('ezcontentobject_attribute', 'a')
->set('a.data_type_string', ':datatypestring')
->set('a.data_text', ':datatext')
->where(
$updateQuery->expr()->eq(
'a.id',
':id'
)
)
->andWhere(
$updateQuery->expr()->eq(
'a.version',
':version'
)
)
->setParameters([
':datatypestring' => 'ezrichtext',
':datatext' => $converted,
':id' => $row['id'],
':version' => $row['version'],
]);

if (!$dryRun) {
$updateQuery->execute();
}
$converted = $this->converter->convert($this->createDocument($inputValue), $checkDuplicateIds, $row['id']);

$this->updateFieldRow($dryRun, $row['id'], $row['version'], $converted);

$this->logger->info(
"Converted ezxmltext field #{$row['id']} to richtext",
Expand Down
7 changes: 7 additions & 0 deletions bundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ services:
class: EzSystems\EzPlatformXmlTextFieldTypeBundle\Command\ConvertXmlTextToRichTextCommand
arguments:
- "@ezpublish.persistence.connection"
- "@ezxmltext.richtext_converter"
- "@?logger"
tags:
- { name: console.command }

ezxmltext.richtext_converter:
class: eZ\Publish\Core\FieldType\XmlText\Converter\RichText
arguments:
- "@?logger"
- "@ezpublish.api.repository"
74 changes: 72 additions & 2 deletions lib/FieldType/XmlText/Converter/RichText.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@ class RichText implements Converter
*/
private $converter;

/**
* @var int[]
*/
private $imageContentTypes;
/**
* @var \eZ\Publish\Core\FieldType\RichText\Validator
*/
private $validator;

public function __construct(LoggerInterface $logger = null)
private $apiRepository;

public function __construct(LoggerInterface $logger = null, $apiRepository = null, $imageContentTypes = array())
{
$this->logger = $logger;
$this->imageContentTypes = $imageContentTypes;
$this->apiRepository = $apiRepository;

$this->converter = new Aggregate(
[
Expand All @@ -56,6 +64,14 @@ public function __construct(LoggerInterface $logger = null)
);
}

/**
* @param array $imageContentTypes List of ContentType Ids which are considered as images
*/
public function setImageContentTypes(array $imageContentTypes)
{
$this->imageContentTypes = $imageContentTypes;
}

protected function removeComments(DOMDocument $document)
{
$xpath = new DOMXpath($document);
Expand All @@ -81,6 +97,59 @@ protected function reportNonUniqueIds(DOMDocument $document, $contentObjectAttri
}
}

protected function isImageClass($contentId)
{
$contentService = $this->apiRepository->getContentService();
$contentInfo = $contentService->loadContentInfo($contentId);
return in_array($contentInfo->contentTypeId, $this->imageContentTypes);
}

/**
* Embedded images needs to include an attribute (ezxhtml:class="ez-embed-type-image) in order to be recognized by editor
*
* Before calling this function, make sure you are logged in as admin, or at least have access to all the objects
* being embedded in the $richtextDocument.
*
* @param DOMDocument $richtextDocument
* @return int Number of ezembed tags which where changed
*/
public function tagEmbeddedImages(DOMDocument $richtextDocument)
{
$count = 0;
$xpath = new DOMXPath($richtextDocument);
$ns = $richtextDocument->documentElement->namespaceURI;
$xpath->registerNamespace('doc', $ns);
$nodes = $xpath->query('//doc:ezembed');
foreach ($nodes as $node) {
//href is in format : ezcontent://123
$href=$node->attributes->getNamedItem('href')->nodeValue;
$contentId = (int) substr($href, strrpos($href, '/')+1);
$classAttribute = $node->attributes->getNamedItem('class');
if ($this->isImageClass($contentId)) {
if (($classAttribute === null) || (($classAttribute !== null) && ($node->attributes->getNamedItem('class')->nodeValue !== 'ez-embed-type-image'))) {
$node->setAttribute('ezxhtml:class', 'ez-embed-type-image');
++$count;
}
} else {
if (($classAttribute !== null) && ($node->attributes->getNamedItem('class')->nodeValue === 'ez-embed-type-image')) {
$node->removeAttribute('ezxhtml:class');
//$node->setAttribute('ezxhtml:class', 'ez-embed-type-image');
++$count;
}
}
}
return $count;
}

/**
* Before calling this function, make sure you are logged in as admin, or at least have access to all the objects
* being embedded in the $inputDocument.
*
* @param DOMDocument $inputDocument
* @param bool $checkDuplicateIds
* @param null $contentObjectAttributeId
* @return string
*/
public function convert(DOMDocument $inputDocument, $checkDuplicateIds = false, $contentObjectAttributeId = null)
{
$this->removeComments($inputDocument);
Expand All @@ -93,8 +162,9 @@ public function convert(DOMDocument $inputDocument, $checkDuplicateIds = false,
// Needed by some disabled output escaping (eg. legacy ezxml paragraph <line/> elements)
$convertedDocumentNormalized = new DOMDocument();
$convertedDocumentNormalized->loadXML($convertedDocument->saveXML());
$this->tagEmbeddedImages($convertedDocumentNormalized);

$errors = $this->validator->validate($convertedDocument);
$errors = $this->validator->validate($convertedDocumentNormalized);

$result = $convertedDocumentNormalized->saveXML();

Expand Down
Loading

0 comments on commit 773d778

Please sign in to comment.