diff --git a/README.md b/README.md index d7653514..b8a38d46 100644 --- a/README.md +++ b/README.md @@ -1 +1,31 @@ # XmlText field type for eZ Platform + +This is the XmlText FieldType for eZ Platform. It was extracted from the eZ Publish / Platform 5.x. + +## Migrating from XmlText to RichText + +**Warning: this part is a non-finalized work-in-progress. Always make a backup before using the migration tools.** + +This package provides tools to migration existing XmlText fields to RichText, the enriched text format eZ Platform uses. +The tool comes as a Symfony command, `ezxmltext:convert-to-richtext`. + +It will do two things: + +- convert `ezxmltext` field definitions to `ezrichtext` field definitions +- convert `ezxmltext` fields (content) to `ezrichtext` + +We recommend that you execute it this way: + +``` +php app/console ezxmltext:convert-to-richtext -v +``` + +The `-v` flag will output logs to the console, making it easy to track the conversion work that is being done. +This is an example of a successful conversion log entry for one field: + +``` +[2016-02-03 15:25:52] app.INFO: Converted ezxmltext field #745 to richtext {"original":"\n
\n","converted":"\n
\n"} +``` + +It contains, in a JSON structure, the `original` (ezxmltext) value, and the `converted` (ezrichtext) value that has been +written to the database. diff --git a/bundle/Command/ConvertXmlTextToRichTextCommand.php b/bundle/Command/ConvertXmlTextToRichTextCommand.php new file mode 100644 index 00000000..50e99829 --- /dev/null +++ b/bundle/Command/ConvertXmlTextToRichTextCommand.php @@ -0,0 +1,274 @@ +db = $db; + $this->logger = $logger; + + $this->converter = new Aggregate( + array( + new ToRichTextPreNormalize(new Expanding(), new EmbedLinking()), + new Xslt( + './vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/RichText/Resources/stylesheets/ezxml/docbook/docbook.xsl', + array( + array( + 'path' => './vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/RichText/Resources/stylesheets/ezxml/docbook/core.xsl', + 'priority' => 99, + ), + ) + ), + ) + ); + + $this->validator = new Validator( + array( + './vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/RichText/Resources/schemas/docbook/ezpublish.rng', + './vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/RichText/Resources/schemas/docbook/docbook.iso.sch.xsl', + ) + ); + } + + protected function configure() + { + $this + ->setName('ezxmltext:convert-to-richtext') + ->setDescription( <<< EOT +Converts XmlText fields from eZ Publish Platform to RichText fields. + +== WARNING == + +This is a non-finalized work in progress. ALWAYS make sure you have a restorable backup of your database before using it. +EOT +); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->convertFieldDefinitions($output); + $this->convertFields($output); + } + + function convertFieldDefinitions(OutputInterface $output) + { + $query = $this->db->createSelectQuery(); + $query->select($query->expr->count('*')); + $query->from('ezcontentclass_attribute'); + $query->where( + $query->expr->eq( + $this->db->quoteIdentifier('data_type_string'), + $query->bindValue('ezxmltext', null, PDO::PARAM_STR) + ) + ); + + $statement = $query->prepare(); + $statement->execute(); + $count = $statement->fetchColumn(); + + $output->writeln("Found $count field definiton to convert."); + + $query = $this->db->createSelectQuery(); + $query->select('*'); + $query->from('ezcontentclass_attribute'); + $query->where( + $query->expr->eq( + $this->db->quoteIdentifier('data_type_string'), + $query->bindValue('ezxmltext', null, PDO::PARAM_STR) + ) + ); + + $statement = $query->prepare(); + $statement->execute(); + + $updateQuery = $this->db->createUpdateQuery(); + $updateQuery->update($this->db->quoteIdentifier('ezcontentclass_attribute')); + $updateQuery->set( + $this->db->quoteIdentifier('data_type_string'), + $updateQuery->bindValue('ezrichtext', null, PDO::PARAM_STR) + ); + // was tagPreset in ezxmltext, unused in RichText + $updateQuery->set( + $this->db->quoteIdentifier('data_text2'), + $updateQuery->bindValue(null, null, PDO::PARAM_STR) + ); + $updateQuery->where( + $updateQuery->expr->eq( + $this->db->quoteIdentifier('data_type_string'), + $updateQuery->bindValue('ezxmltext', null, PDO::PARAM_STR) + ) + ); + + $updateQuery->prepare()->execute(); + + $output->writeln("Converted $count ezxmltext field definitions to ezrichtext"); + } + + function convertFields(OutputInterface $output) + { + $query = $this->db->createSelectQuery(); + $query->select($query->expr->count('*')); + $query->from('ezcontentobject_attribute'); + $query->where( + $query->expr->eq( + $this->db->quoteIdentifier('data_type_string'), + $query->bindValue('ezxmltext', null, PDO::PARAM_STR) + ) + ); + + $statement = $query->prepare(); + $statement->execute(); + $count = $statement->fetchColumn(); + + $output->writeln("Found $count field rows to convert."); + + $query = $this->db->createSelectQuery(); + $query->select('*'); + $query->from('ezcontentobject_attribute'); + $query->where( + $query->expr->eq( + $this->db->quoteIdentifier('data_type_string'), + $query->bindValue('ezxmltext', null, PDO::PARAM_STR) + ) + ); + + $statement = $query->prepare(); + $statement->execute(); + + while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { + if (empty($row['data_text'])) { + $inputValue = Value::EMPTY_VALUE; + } else { + $inputValue = $row['data_text']; + } + + $converted = $this->convert($inputValue); + + $updateQuery = $this->db->createUpdateQuery(); + $updateQuery->update($this->db->quoteIdentifier('ezcontentobject_attribute')); + $updateQuery->set( + $this->db->quoteIdentifier('data_type_string'), + $updateQuery->bindValue('ezrichtext', null, PDO::PARAM_STR) + ); + $updateQuery->set( + $this->db->quoteIdentifier('data_text'), + $updateQuery->bindValue($converted, null, PDO::PARAM_STR) + ); + $updateQuery->where( + $updateQuery->expr->lAnd( + $updateQuery->expr->eq( + $this->db->quoteIdentifier('id'), + $updateQuery->bindValue($row['id'], null, PDO::PARAM_INT) + ), + $updateQuery->expr->eq( + $this->db->quoteIdentifier('version'), + $updateQuery->bindValue($row['version'], null, PDO::PARAM_INT) + ) + ) + ); + $updateQuery->prepare()->execute(); + + $this->logger->info( + "Converted ezxmltext field #{$row['id']} to richtext", + [ + 'original' => $inputValue, + 'converted' => $converted + ] + ); + } + + + $output->writeln("Converted $count ezxmltext fields to richtext"); + } + + function createDocument($xmlString) + { + $document = new DOMDocument(); + + $document->preserveWhiteSpace = false; + $document->formatOutput = false; + + $document->loadXml($xmlString); + + return $document; + } + + function removeComments(DOMDocument $document) + { + $xpath = new DOMXpath($document); + $nodes = $xpath->query('//comment()'); + + for ($i = 0; $i < $nodes->length; ++$i) { + $nodes->item($i)->parentNode->removeChild($nodes->item($i)); + } + } + + function convert($xmlString) + { + $inputDocument = $this->createDocument($xmlString); + + $this->removeComments($inputDocument); + + $convertedDocument = $this->converter->convert($inputDocument); + + // Needed by some disabled output escaping (eg. legacy ezxml paragraph elements) + $convertedDocumentNormalized = new DOMDocument(); + $convertedDocumentNormalized->loadXML($convertedDocument->saveXML()); + + $errors = $this->validator->validate($convertedDocument); + + $result = $convertedDocumentNormalized->saveXML(); + + if (!empty($errors)) { + $this->logger->error( + "Validation errors when converting xmlstring", + ['result' => $result, 'errors' => $errors, 'xmlString' => $xmlString] + ); + } + + return $result; + } +} diff --git a/bundle/Resources/config/services.yml b/bundle/Resources/config/services.yml index 13780d6a..b014d498 100644 --- a/bundle/Resources/config/services.yml +++ b/bundle/Resources/config/services.yml @@ -6,3 +6,11 @@ services: class: %ezpublish_rest.field_type_processor.ezxmltext.class% tags: - { name: ezpublish_rest.field_type_processor, alias: ezxmltext } + + ezxmltext.command.convert_to_richtexst: + class: EzSystems\EzPlatformXmlTextFieldTypeBundle\Command\ConvertXmlTextToRichTextCommand + arguments: + - "@ezpublish.api.storage_engine.legacy.dbhandler" + - "@?logger" + tags: + - { name: console.command }