DiffEntityComparison.php in Diff 8
Namespace
Drupal\diffFile
src/DiffEntityComparison.phpView source
<?php
namespace Drupal\diff;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Component\Diff\Diff;
use Drupal\Core\Entity\RevisionLogInterface;
use Drupal\Component\Utility\Xss;
/**
* Entity comparison service that prepares a diff of a pair of entities.
*/
class DiffEntityComparison {
/**
* Contains the configuration object factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Wrapper object for simple configuration from diff.plugins.yml.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $pluginsConfig;
/**
* The diff formatter.
*
* @var \Drupal\Core\Diff\DiffFormatter
*/
protected $diffFormatter;
/**
* A list of all the field types from the system and their definitions.
*
* @var array
*/
protected $fieldTypeDefinitions;
/**
* The entity parser.
*
* @var \Drupal\diff\DiffEntityParser
*/
protected $entityParser;
/**
* The field diff plugin manager service.
*
* @var \Drupal\diff\DiffBuilderManager
*/
protected $diffBuilderManager;
/**
* The content moderation service, if available.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* Constructs a DiffEntityComparison object.
*
* @param \Drupal\Core\Config\ConfigFactory $config_factory
* The configuration factory.
* @param \Drupal\diff\DiffFormatter $diff_formatter
* The diff formatter service.
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
* The plugin manager service.
* @param \Drupal\diff\DiffEntityParser $entity_parser
* The entity parser.
* @param \Drupal\diff\DiffBuilderManager $diff_builder_manager
* The diff builder manager.
*/
public function __construct(ConfigFactory $config_factory, DiffFormatter $diff_formatter, PluginManagerInterface $plugin_manager, DiffEntityParser $entity_parser, DiffBuilderManager $diff_builder_manager) {
$this->configFactory = $config_factory;
$this->pluginsConfig = $this->configFactory
->get('diff.plugins');
$this->diffFormatter = $diff_formatter;
$this->fieldTypeDefinitions = $plugin_manager
->getDefinitions();
$this->entityParser = $entity_parser;
$this->diffBuilderManager = $diff_builder_manager;
}
/**
* This method should return an array of items ready to be compared.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $left_entity
* The left entity.
* @param \Drupal\Core\Entity\ContentEntityInterface $right_entity
* The right entity.
*
* @return array
* Items ready to be compared by the Diff component.
*/
public function compareRevisions(ContentEntityInterface $left_entity, ContentEntityInterface $right_entity) {
$result = array();
$left_values = $this->entityParser
->parseEntity($left_entity);
$right_values = $this->entityParser
->parseEntity($right_entity);
foreach ($left_values as $left_key => $values) {
list(, $field_key) = explode(':', $left_key);
// Get the compare settings for this field type.
$compare_settings = $this->pluginsConfig
->get('fields.' . $field_key);
$result[$left_key] = [
'#name' => isset($compare_settings['settings']['show_header']) && $compare_settings['settings']['show_header'] == 0 ? '' : $values['label'],
'#settings' => $compare_settings,
'#data' => [],
];
// Fields which exist on the right entity also.
if (isset($right_values[$left_key])) {
$result[$left_key]['#data'] += $this
->combineFields($left_values[$left_key], $right_values[$left_key]);
// Unset the field from the right entity so that we know if the right
// entity has any fields that left entity doesn't have.
unset($right_values[$left_key]);
}
else {
$result[$left_key]['#data'] += $this
->combineFields($left_values[$left_key], []);
}
}
// Fields which exist only on the right entity.
foreach ($right_values as $right_key => $values) {
list(, $field_key) = explode(':', $right_key);
$compare_settings = $this->pluginsConfig
->get('fields.' . $field_key);
$result[$right_key] = [
'#name' => isset($compare_settings['settings']['show_header']) && $compare_settings['settings']['show_header'] == 0 ? '' : $values['label'],
'#settings' => $compare_settings,
'#data' => [],
];
$result[$right_key]['#data'] += $this
->combineFields([], $right_values[$right_key]);
}
return $result;
}
/**
* Combine two fields into an array with keys '#left' and '#right'.
*
* @param array $left_values
* Entity field formatted into an array of strings.
* @param array $right_values
* Entity field formatted into an array of strings.
*
* @return array
* Array resulted after combining the left and right values.
*/
protected function combineFields(array $left_values, array $right_values) {
$result = array(
'#left' => array(),
'#right' => array(),
);
$max = max(array(
count($left_values),
count($right_values),
));
for ($delta = 0; $delta < $max; $delta++) {
// EXPERIMENTAL: Transform thumbnail from ImageFieldBuilder.
// @todo Make thumbnail / rich diff data pluggable.
// @see https://www.drupal.org/node/2840566
if (isset($left_values[$delta])) {
$value = $left_values[$delta];
if (isset($value['#thumbnail'])) {
$result['#left_thumbnail'][] = $value['#thumbnail'];
}
else {
$result['#left'][] = is_array($value) ? implode("\n", $value) : $value;
}
}
if (isset($right_values[$delta])) {
$value = $right_values[$delta];
if (isset($value['#thumbnail'])) {
$result['#right_thumbnail'][] = $value['#thumbnail'];
}
else {
$result['#right'][] = is_array($value) ? implode("\n", $value) : $value;
}
}
}
// If a field has multiple values combine them into one single string.
$result['#left'] = implode("\n", $result['#left']);
$result['#right'] = implode("\n", $result['#right']);
return $result;
}
/**
* Prepare the table rows for #type 'table'.
*
* @param string $a
* The source string to compare from.
* @param string $b
* The target string to compare to.
* @param bool $show_header
* Display diff context headers. For example, "Line x".
* @param array $line_stats
* Tracks line numbers across multiple calls to DiffFormatter.
*
* @see \Drupal\Component\Diff\DiffFormatter::format
*
* @return array
* Array of rows usable with #type => 'table' returned by the core diff
* formatter when format a diff.
*/
public function getRows($a, $b, $show_header = FALSE, array &$line_stats = NULL) {
if (!isset($line_stats)) {
$line_stats = array(
'counter' => array(
'x' => 0,
'y' => 0,
),
'offset' => array(
'x' => 0,
'y' => 0,
),
);
}
// Header is the line counter.
$this->diffFormatter->show_header = $show_header;
$diff = new Diff($a, $b);
return $this->diffFormatter
->format($diff);
}
/**
* Splits the strings into lines and counts the resulted number of lines.
*
* @param array $diff
* Array of strings.
*/
public function processStateLine(array &$diff) {
$data = $diff['#data'];
if (isset($data['#left']) && $data['#left'] != '') {
if (is_string($data['#left'])) {
$diff['#data']['#left'] = explode("\n", $data['#left']);
}
$diff['#data']['#count_left'] = count($diff['#data']['#left']);
}
else {
$diff['#data']['#count_left'] = 0;
$diff['#data']['#left'] = [];
}
if (isset($data['#right']) && $data['#right'] != '') {
if (is_string($data['#right'])) {
$diff['#data']['#right'] = explode("\n", $data['#right']);
}
$diff['#data']['#count_right'] = count($diff['#data']['#right']);
}
else {
$diff['#data']['#count_right'] = 0;
$diff['#data']['#right'] = [];
}
}
/**
* Gets the revision description of the revision.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $revision
* The current revision.
* @param \Drupal\Core\Entity\ContentEntityInterface $previous_revision
* (optional) The previous revision. Defaults to NULL.
*
* @return string
* The revision log message.
*/
public function getRevisionDescription(ContentEntityInterface $revision, ContentEntityInterface $previous_revision = NULL) {
$revision_summary = '';
// Check if the revision has a revision log message.
if ($revision instanceof RevisionLogInterface) {
$revision_summary = Xss::filter($revision
->getRevisionLogMessage());
}
// @todo Autogenerate summary again.
// @see https://www.drupal.org/project/diff/issues/2880936
// Add workflow/content moderation state information.
if ($state = $this
->getModerationState($revision)) {
$revision_summary .= " ({$state})";
}
return $revision_summary;
}
/**
* Creates an log message based on the changes of entity fields.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $revision
* The current revision.
*
* @return array
* Array of the revision fields with their value and label.
*/
protected function summary(ContentEntityInterface $revision) {
$result = [];
$entity_type_id = $revision
->getEntityTypeId();
// Loop through entity fields and transform every FieldItemList object
// into an array of strings according to field type specific settings.
/** @var \Drupal\Core\Field\FieldItemListInterface $field_items */
foreach ($revision as $field_items) {
$show_delta = FALSE;
// Create a plugin instance for the field definition.
$plugin = $this->diffBuilderManager
->createInstanceForFieldDefinition($field_items
->getFieldDefinition());
if ($plugin && $this->diffBuilderManager
->showDiff($field_items
->getFieldDefinition()
->getFieldStorageDefinition())) {
// Create the array with the fields of the entity. Recursive if the
// field contains entities.
if ($plugin instanceof FieldReferenceInterface) {
foreach ($plugin
->getEntitiesToDiff($field_items) as $entity_key => $reference_entity) {
foreach ($this
->summary($reference_entity) as $key => $build) {
if ($field_items
->getFieldDefinition()
->getFieldStorageDefinition()
->getCardinality() != 1) {
$show_delta = TRUE;
}
$result[$key] = $build;
$delta = $show_delta ? '<sub>' . ($entity_key + 1) . '</sub> ' : ' - ';
$result[$key]['label'] = $field_items
->getFieldDefinition()
->getLabel() . $delta . $result[$key]['label'];
}
}
}
else {
// Create a unique flat key.
$key = $revision
->id() . ':' . $entity_type_id . '.' . $field_items
->getName();
$result[$key]['value'] = $field_items
->getValue();
$result[$key]['label'] = $field_items
->getFieldDefinition()
->getLabel();
}
}
}
return $result;
}
/**
* Gets the revision's content moderation state, if available.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity revision.
*
* @return string|bool
* Returns the label of the moderation state, if available, otherwise FALSE.
*/
protected function getModerationState(ContentEntityInterface $entity) {
if ($this->moderationInformation && $this->moderationInformation
->isModeratedEntity($entity)) {
if ($state = $entity->moderation_state->value) {
$workflow = $this->moderationInformation
->getWorkflowForEntity($entity);
return $workflow
->getTypePlugin()
->getState($state)
->label();
}
}
return FALSE;
}
/**
* Sets the content moderation service if available.
*
* @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
* The moderation information service.
*/
public function setModerationInformation(ModerationInformationInterface $moderation_information) {
$this->moderationInformation = $moderation_information;
}
}
Classes
Name | Description |
---|---|
DiffEntityComparison | Entity comparison service that prepares a diff of a pair of entities. |