diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 7c822ed..21cf7a9 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -6,6 +6,7 @@
*/
use Drupal\Component\Utility\Html;
+use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\editor\Entity\Editor;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
@@ -364,19 +365,39 @@ function editor_entity_update(EntityInterface $entity) {
// File references that existed both in the previous version of the revision
// and in the new one don't need their usage to be updated.
else {
- $original_uuids_by_field = _editor_get_file_uuids_by_field($entity->original);
- $uuids_by_field = _editor_get_file_uuids_by_field($entity);
- // Detect file usages that should be incremented.
- foreach ($uuids_by_field as $field => $uuids) {
- $added_files = array_diff($uuids_by_field[$field], $original_uuids_by_field[$field]);
- _editor_record_file_usage($added_files, $entity);
+ // When an entity is updated, any amount of its translations can be updated
+ // as well. Check them all.
+ $entities = [[$entity, $entity->original]];
+ if ($entity instanceof TranslatableInterface) {
+ $entities = [];
+ foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+ // If the original entity does not have a translation in the given
+ // language, it will be handled in editor_entity_translation_insert().
+ if ($entity->original->hasTranslation($langcode)) {
+ $entities[] = [
+ $entity->getTranslation($langcode),
+ $entity->original->getTranslation($langcode)
+ ];
+ }
+ }
}
- // Detect file usages that should be decremented.
- foreach ($original_uuids_by_field as $field => $uuids) {
- $removed_files = array_diff($original_uuids_by_field[$field], $uuids_by_field[$field]);
- _editor_delete_file_usage($removed_files, $entity, 1);
+ foreach ($entities as list($entity, $entity_original)) {
+ $original_uuids_by_field = _editor_get_file_uuids_by_field($entity_original);
+ $uuids_by_field = _editor_get_file_uuids_by_field($entity);
+
+ // Detect file usages that should be incremented.
+ foreach ($uuids_by_field as $field => $uuids) {
+ $added_files = array_diff($uuids_by_field[$field], $original_uuids_by_field[$field]);
+ _editor_record_file_usage($added_files, $entity);
+ }
+
+ // Detect file usages that should be decremented.
+ foreach ($original_uuids_by_field as $field => $uuids) {
+ $removed_files = array_diff($original_uuids_by_field[$field], $uuids_by_field[$field]);
+ _editor_delete_file_usage($removed_files, $entity, 1);
+ }
}
}
}
@@ -410,6 +431,28 @@ function editor_entity_revision_delete(EntityInterface $entity) {
}
/**
+ * Implements hook_entity_translation_insert().
+ */
+function editor_entity_translation_insert(EntityInterface $translation) {
+ // Process like entity insert.
+ editor_entity_insert($translation);
+}
+
+/**
+ * Implements hook_entity_translation_delete().
+ */
+function editor_entity_translation_delete(EntityInterface $translation) {
+ // Only act on content entities.
+ if (!($translation instanceof FieldableEntityInterface)) {
+ return;
+ }
+ $referenced_files_by_field = _editor_get_file_uuids_by_field($translation);
+ foreach ($referenced_files_by_field as $field => $uuids) {
+ _editor_delete_file_usage($uuids, $translation, 1);
+ }
+}
+
+/**
* Records file usage of files referenced by formatted text fields.
*
* Every referenced file that does not yet have the FILE_STATUS_PERMANENT state,
diff --git a/core/modules/editor/tests/src/Kernel/EditorFileUsageTest.php b/core/modules/editor/tests/src/Kernel/EditorFileUsageTest.php
index a8379d1..f0e9ca5 100644
--- a/core/modules/editor/tests/src/Kernel/EditorFileUsageTest.php
+++ b/core/modules/editor/tests/src/Kernel/EditorFileUsageTest.php
@@ -4,6 +4,7 @@
use Drupal\editor\Entity\Editor;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
+use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\file\Entity\File;
@@ -23,7 +24,14 @@ class EditorFileUsageTest extends EntityKernelTestBase {
*
* @var array
*/
- public static $modules = array('editor', 'editor_test', 'node', 'file');
+ public static $modules = array('editor', 'editor_test', 'node', 'file', 'language');
+
+ /**
+ * Languages to enable.
+ *
+ * @var array
+ */
+ protected $langcodes = array('fr', 'en');
protected function setUp() {
parent::setUp();
@@ -41,8 +49,14 @@ protected function setUp() {
));
$filtered_html_format->save();
+ // Add languages.
+ foreach ($this->langcodes as $langcode) {
+ ConfigurableLanguage::createFromLangcode($langcode)->save();
+ }
+
// Set cardinality for body field.
FieldStorageConfig::loadByName('node', 'body')
+ ->setTranslatable(TRUE)
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
->save();
@@ -69,6 +83,9 @@ public function testEditorEntityHooks() {
2 => 'core/misc/help.png',
);
+ /** @var \Drupal\file\FileUsage\FileUsageInterface $file_usage */
+ $file_usage = $this->container->get('file.usage');
+
$image_entities = array();
foreach ($image_paths as $key => $image_path) {
$image = File::create();
@@ -76,7 +93,6 @@ public function testEditorEntityHooks() {
$image->setFilename(drupal_basename($image->getFileUri()));
$image->save();
- $file_usage = $this->container->get('file.usage');
$this->assertIdentical(array(), $file_usage->listUsage($image), 'The image ' . $image_paths[$key] . ' has zero usages.');
$image_entities[] = $image;
@@ -104,6 +120,7 @@ public function testEditorEntityHooks() {
// Test editor_entity_insert(): increment.
$this->createUser();
$node = $node = Node::create([
+ 'language' => 'en',
'type' => 'page',
'title' => 'test',
'body' => $body,
@@ -177,11 +194,68 @@ public function testEditorEntityHooks() {
$this->assertIdentical(array('editor' => array('node' => array(1 => '2'))), $file_usage->listUsage($image_entity), 'The image ' . $image_paths[$key] . ' has 2 usages.');
}
+ // Test editor_entity_translation_insert(): increment, by adding a new
+ // translation.
+ $translation = $node->addTranslation('fr', ['title' => 'test fr', 'body' => $original_values]);
+ $translation->save();
+ foreach ($image_entities as $key => $image_entity) {
+ $this->assertIdentical(array('editor' => array('node' => array(1 => '3'))), $file_usage->listUsage($image_entity), 'The image ' . $image_paths[$key] . ' has 3 usages.');
+ }
+
+ // Test editor_entity_translation_delete(): decrement, by deleting a
+ // translation.
+ $node->removeTranslation('fr');
+ $node->save();
+ foreach ($image_entities as $key => $image_entity) {
+ $this->assertIdentical(array('editor' => array('node' => array(1 => '2'))), $file_usage->listUsage($image_entity), 'The image ' . $image_paths[$key] . ' has 2 usages.');
+ }
+
// Test editor_entity_delete().
$node->delete();
foreach ($image_entities as $key => $image_entity) {
$this->assertIdentical(array(), $file_usage->listUsage($image_entity), 'The image ' . $image_paths[$key] . ' has zero usages again.');
}
+
+ // Prepare a new node having only one file entity referenced in the body
+ // field.
+ $image_entity_1 = $image_entities[0];
+ $body_value = '';
+ $node = Node::create([
+ 'language' => 'en',
+ 'type' => 'page',
+ 'title' => 'test',
+ 'body' => [
+ 0 => [
+ 'value' => $body_value,
+ 'format' => 'filtered_html',
+ ]
+ ],
+ 'uid' => 1,
+ ]);
+ $node->save();
+ $nid = $node->id();
+ $this->assertIdentical(array('editor' => array('node' => array($nid => '1'))), $file_usage->listUsage($image_entity_1), 'The image ' . $image_entity_1->getFileUri() . ' has 1 usage.');
+
+ // Add a translation.
+ $translation = $node->addTranslation('fr', [
+ 'title' => 'test fr',
+ 'body' => $node->get('body')->getValue(),
+ ]);
+ $node->save();
+ $this->assertIdentical(array('editor' => array('node' => array($nid => '2'))), $file_usage->listUsage($image_entity_1), 'The image ' . $image_entity_1->getFileUri() . ' has 2 usages.');
+
+ // Replace image in the French translation.
+ $image_entity_2 = $image_entities[1];
+ $translation->get('body')->value = '';;
+ $node->save();
+ $this->assertIdentical(array('editor' => array('node' => array($nid => '1'))), $file_usage->listUsage($image_entity_1), 'The image ' . $image_entity_1->getFileUri() . ' has 1 usage.');
+ $this->assertIdentical(array('editor' => array('node' => array($nid => '1'))), $file_usage->listUsage($image_entity_2), 'The image ' . $image_entity_2->getFileUri() . ' has 1 usage.');
+
+ // Re-save translation with no image and check if usage changed
+ $translation->get('body')->value = '';
+ $node->save();
+ $this->assertIdentical(array('editor' => array('node' => array($nid => '1'))), $file_usage->listUsage($image_entity_1), 'The image ' . $image_entity_1->getFileUri() . ' has 1 usage.');
+ $this->assertIdentical(array(), $file_usage->listUsage($image_entity_2), 'The image ' . $image_entity_2->getFileUri() . ' has zero usages.');
}
}