Skip to content
Snippets Groups Projects
Commit 2de30642 authored by Andy Fowlston's avatar Andy Fowlston Committed by Marcos Cano
Browse files

Issue #3366527 by andyf, alexpott, marcoscano: Differentiate pending/old...

Issue #3366527 by andyf, alexpott, marcoscano: Differentiate pending/old revisions and default translations
parent 730dca7a
Branches
Tags 8.x-2.0-beta15
1 merge request!53Issue #3366527: Differentiate pending/old revisions and default translations
Pipeline #353606 failed
......@@ -26,6 +26,28 @@ class ListUsageController extends ControllerBase {
*/
const ITEMS_PER_PAGE_DEFAULT = 25;
/**
* The index for the default revision "group".
*
* @var int
*/
protected const REVISION_DEFAULT = 0;
/**
* The index for the pending revision "group".
*
* @var int
*/
protected const REVISION_PENDING = 1;
/**
* The index for the old revision "group".
*
* @var int
*/
protected const REVISION_OLD = -1;
/**
* The entity field manager.
*
......@@ -142,7 +164,11 @@ class ListUsageController extends ControllerBase {
// revision, we don't need the "Used in" column.
$used_in_previous_revisions = FALSE;
foreach ($page_rows as $row) {
if ($row[5] == $this->t('Translations or previous revisions')) {
$used_in = $row[5]['data'];
$only_default = fn(array $row) => count($row) === 1 &&
!empty($row[0]['#plain_text']) &&
$row[0]['#plain_text'] == $this->t('Default');
if (!$only_default($used_in)) {
$used_in_previous_revisions = TRUE;
break;
}
......@@ -193,6 +219,13 @@ class ListUsageController extends ControllerBase {
$entity_types = $this->entityTypeManager->getDefinitions();
$languages = $this->languageManager()->getLanguages(LanguageInterface::STATE_ALL);
$all_usages = $this->entityUsage->listSources($entity);
$revision_groups = [
static::REVISION_DEFAULT => $this->t("Default"),
static::REVISION_PENDING => $this->t("Pending revision(s) / Draft(s)"),
static::REVISION_OLD => $this->t("Old revision(s)"),
];
foreach ($all_usages as $source_type => $ids) {
$type_storage = $this->entityTypeManager->getStorage($source_type);
foreach ($ids as $source_id => $records) {
......@@ -208,16 +241,33 @@ class ListUsageController extends ControllerBase {
if ($source_entity instanceof RevisionableInterface) {
$default_revision_id = $source_entity->getRevisionId();
$default_langcode = $source_entity->language()->getId();
$used_in_default = FALSE;
$default_key = 0;
foreach ($records as $key => $record) {
if (($default_revision_id === NULL || $record['source_vid'] == $default_revision_id) && $record['source_langcode'] == $default_langcode) {
$default_key = $key;
$used_in_default = TRUE;
break;
$revisions = [];
foreach (array_reverse($records) as $record) {
[
'source_vid' => $source_vid,
'source_langcode' => $source_langcode,
'field_name' => $field_name,
] = $record;
// Track which languages are used in pending, default and old
// revisions.
$revision_group = (int) $source_vid <=> (int) $default_revision_id;
$revisions[$revision_group][$source_langcode] = $field_name;
}
$used_in = [];
foreach ($revision_groups as $index => $label) {
if (!empty($revisions[$index])) {
$used_in[] = $this->summariseRevisionGroup($default_langcode, $label, $revisions[$index]);
}
}
$used_in_text = $used_in_default ? $this->t('Default') : $this->t('Translations or previous revisions');
if (count($used_in) > 1) {
$used_in = [
'#theme' => 'item_list',
'#items' => $used_in,
'#list_type' => 'ul',
];
}
}
$link = $this->getSourceEntityLink($source_entity);
// If the label is empty it means this usage shouldn't be shown
......@@ -226,14 +276,22 @@ class ListUsageController extends ControllerBase {
continue;
}
$published = $this->getSourceEntityStatus($source_entity);
$field_label = isset($field_definitions[$records[$default_key]['field_name']]) ? $field_definitions[$records[$default_key]['field_name']]->getLabel() : $this->t('Unknown');
$get_field_name = function (array $field_names) use ($default_langcode, $revision_groups) {
foreach (array_keys($revision_groups) as $group) {
if (isset($field_names[$group])) {
return $field_names[$group][$default_langcode] ?? reset($field_names[$group]);
}
}
};
$field_name = $get_field_name($revisions);
$field_label = isset($field_definitions[$field_name]) ? $field_definitions[$field_name]->getLabel() : $this->t('Unknown');
$rows[] = [
$link,
$entity_types[$source_type]->getLabel(),
$languages[$default_langcode]->getName(),
$field_label,
$published,
$used_in_text,
['data' => $used_in],
];
}
}
......@@ -242,6 +300,53 @@ class ListUsageController extends ControllerBase {
return $this->allRows;
}
/**
* Returns a render array indicating a revision "type" and languages.
*
* For example it might return "Pending revision(s) / Draft(s): ES, NO.".
*
* @param string $default_langcode
* The default language code for the referencing entity.
* @param \Drupal\Core\StringTranslation\TranslatableMarkup $revision_label
* The translated revision type label eg 'Old revision(s)' or 'Default'.
* @param string[] $languages
* An indexed array of language codes that reference the entity in the given
* type.
*
* @return array
* A render array summarizing the information passed in.
*/
protected function summariseRevisionGroup($default_langcode, $revision_label, array $languages) {
$language_objects = $this->languageManager()->getLanguages(LanguageInterface::STATE_ALL);
if (count($languages) === 1 && !empty($languages[$default_langcode])) {
// If there's only one relevant revision and it's the entity's default
// language then just show the label.
return ['#plain_text' => $revision_label];
}
else {
// Otherwise show the languages enumerated, ensuring the default language
// comes first if present.
if (!empty($languages[$default_langcode])) {
$languages = [$default_langcode => TRUE] + $languages;
}
return [
'#type' => 'inline_template',
'#template' => '{{ label }}: {% for language in languages %}{{ language }}{{ loop.last ? "." : ", " }}{% endfor %}',
'#context' => [
'label' => $revision_label,
'languages' => array_map(fn ($code) => [
'#type' => 'inline_template',
'#template' => '<abbr title="{{ name|e("html_attr") }}">{{ code }}</abbr>',
'#context' => [
'code' => mb_strtoupper($code),
'name' => $language_objects[$code]->getName(),
],
], array_keys($languages)),
],
];
}
}
/**
* Get rows for a given page.
*
......
......@@ -204,7 +204,9 @@ class EntityUsageLayoutBuilderEntityBrowserBlockTest extends EntityUsageJavascri
$this->assertStringContainsString($host_node->toUrl()->toString(), $first_row_title_link->getAttribute('href'));
$first_row_field_label = $this->xpath('//table/tbody/tr[1]/td[4]')[0];
$this->assertEquals('Layout', $first_row_field_label->getText());
$assert_session->pageTextNotContains('Translations or previous revisions');
$assert_session->pageTextNotContains('Old revision(s)');
$assert_session->pageTextNotContains('Pending revision(s) / Draft(s)');
$assert_session->pageTextNotContains('Default:');
// Verify we can edit the layout and add another item to the same region.
$page->clickLink($host_node->getTitle());
......@@ -295,7 +297,7 @@ class EntityUsageLayoutBuilderEntityBrowserBlockTest extends EntityUsageJavascri
$first_row_field_label = $this->xpath('//table/tbody/tr[1]/td[4]')[0];
$this->assertEquals('Layout', $first_row_field_label->getText());
$first_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Translations or previous revisions', $first_row_used_in->getText());
$this->assertEquals('Old revision(s)', $first_row_used_in->getText());
}
}
......@@ -153,7 +153,9 @@ class ListControllerTest extends EntityUsageJavascriptTestBase {
// When all usages are shown on their default revisions, we don't see the
// extra column.
$assert_session->pageTextNotContains('Used in');
$assert_session->pageTextNotContains('Translations or previous revisions');
$assert_session->pageTextNotContains('Old revision(s)');
$assert_session->pageTextNotContains('Pending revision(s) / Draft(s)');
$assert_session->pageTextNotContains('Default:');
// If some sources reference our entity in a previous revision, an
// additional column is shown.
......@@ -165,7 +167,7 @@ class ListControllerTest extends EntityUsageJavascriptTestBase {
$second_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Default', $second_row_used_in->getText());
$second_row_used_in = $this->xpath('//table/tbody/tr[2]/td[6]')[0];
$this->assertEquals('Translations or previous revisions', $second_row_used_in->getText());
$this->assertEquals('Old revision(s)', $second_row_used_in->getText());
// Make sure we only have 2 rows (so no previous revision shows up).
$this->assertEquals(2, count($this->xpath('//table/tbody/tr')));
......@@ -212,10 +214,10 @@ class ListControllerTest extends EntityUsageJavascriptTestBase {
// Usage now should be the same as before.
$this->drupalGet("/admin/content/entity-usage/node/{$node1->id()}");
$assert_session->pageTextContains('Used in');
$second_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Default', $second_row_used_in->getText());
$first_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Default', $first_row_used_in->getText());
$second_row_used_in = $this->xpath('//table/tbody/tr[2]/td[6]')[0];
$this->assertEquals('Translations or previous revisions', $second_row_used_in->getText());
$this->assertEquals('Default: ES. Old revision(s)', $second_row_used_in->getText());
$this->assertEquals(2, count($this->xpath('//table/tbody/tr')));
// Verify that it's possible to control the number of items per page.
......
......@@ -2,6 +2,8 @@
namespace Drupal\Tests\entity_usage\FunctionalJavascript;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\Tests\entity_usage\Traits\EntityUsageLastEntityQueryTrait;
......@@ -24,8 +26,32 @@ class RevisionsTranslationsTest extends EntityUsageJavascriptTestBase {
protected static $modules = [
'language',
'content_translation',
// To test entities which implement RevisionableInterface but do have
// revisions.
'entity_test'
];
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
// Grant the logged-in user permission to entity_test entities.
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load('authenticated');
$this->grantPermissions($role, ['view test entity', 'access entity usage statistics']);
// Allow absolute links to be picked up by entity usage and the node tab to
// be reached.
$current_request = \Drupal::request();
$config = \Drupal::configFactory()->getEditable('entity_usage.settings');
$config
->set('site_domains', [$current_request->getHttpHost() . $current_request->getBasePath()])
->set('local_task_enabled_entity_types', ['node'])
->save();
\Drupal::service('router.builder')->rebuild();
}
/**
* Tests the tracking of nodes and revisions.
*/
......@@ -257,6 +283,46 @@ class RevisionsTranslationsTest extends EntityUsageJavascriptTestBase {
$usage = $usage_service->listSources($node3);
$this->assertEquals([], $usage);
// Test a revisionable entity type without revisions.
$entity_test_1 = EntityTest::create([
'name' => 'Test entity',
// Use an absolute URL so that tests running Drupal in a subdirectory
// still work.
'field_test_text' => '<a href="' . $node1->toUrl()->setAbsolute()->toString() . '">test</a>',
]);
$entity_test_1->save();
$this->assertInstanceOf(RevisionableInterface::class, $entity_test_1);
$this->assertNull($entity_test_1->getRevisionId());
$this->drupalGet("/node/{$node1->id()}/usage");
$assert_session->pageTextContains('Entity usage information for Node 1');
// Only two usages; the entity_test entity and node 2.
$assert_session->elementsCount('xpath', '//table/tbody/tr', 2);
$first_row_title = $this->xpath('//table/tbody/tr[1]/td[1]')[0];
$this->assertEquals('Test entity', $first_row_title->getText());
$first_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Default', $first_row_used_in->getText());
$second_row_title = $this->xpath('//table/tbody/tr[2]/td[1]')[0];
$this->assertEquals('Node 2', $second_row_title->getText());
$second_row_used_in = $this->xpath('//table/tbody/tr[2]/td[6]')[0];
$this->assertEquals('Old revision(s)', $second_row_used_in->getText());
// Create a pending revision of node 2 that links to node 1.
$node2 = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($node2->id());
$node2->setNewRevision();
$node2->isDefaultRevision(FALSE);
$node2->field_eu_test_related_nodes->target_id = $node1->id();
$node2->save();
$this->drupalGet("/node/{$node1->id()}/usage");
$assert_session->pageTextContains('Entity usage information for Node 1');
$assert_session->elementsCount('xpath', '//table/tbody/tr', 2);
$second_row_title = $this->xpath('//table/tbody/tr[2]/td[1]')[0];
$this->assertEquals('Node 2', $second_row_title->getText());
$second_row_used_in = $this->xpath('//table/tbody/tr[2]/td[6]/ul/li[1]')[0];
$this->assertEquals('Pending revision(s) / Draft(s)', $second_row_used_in->getText());
$second_row_used_in = $this->xpath('//table/tbody/tr[2]/td[6]/ul/li[2]')[0];
$this->assertEquals('Old revision(s)', $second_row_used_in->getText());
// If we remove a node only being targeted in previous revisions (N1), all
// usages tracked should also be deleted.
$node1->delete();
......@@ -289,7 +355,6 @@ class RevisionsTranslationsTest extends EntityUsageJavascriptTestBase {
$assert_session->pageTextContains('has been deleted.');
$usage = $usage_service->listSources($node2);
$this->assertEquals([], $usage);
}
/**
......@@ -463,7 +528,7 @@ class RevisionsTranslationsTest extends EntityUsageJavascriptTestBase {
$first_row_field_label = $this->xpath('//table/tbody/tr[1]/td[4]')[0];
$this->assertEquals('Related nodes', $first_row_field_label->getText());
$first_row_used_in = $this->xpath('//table/tbody/tr[1]/td[6]')[0];
$this->assertEquals('Translations or previous revisions', $first_row_used_in->getText());
$this->assertEquals('Default: ES.', $first_row_used_in->getText());
// There's no second row.
$assert_session->elementNotExists('xpath', '//table/tbody/tr[2]');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment