diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php index 102fa70..9c9d123 100644 --- a/core/includes/entity.api.php +++ b/core/includes/entity.api.php @@ -34,6 +34,10 @@ * different operations, the name of the operation is passed also to the * constructor of the form controller class. This way, one class can be used * for multiple entity forms. + * - list controller class: The name of the class that is used to provide + * listings of the entity. The class must implement + * Drupal\Core\Entity\EntityListControllerInterface. Defaults to + * Drupal\Core\Entity\EntityListController. * - base table: (used by Drupal\Core\Entity\DatabaseStorageController) The * name of the entity type's base table. * - static cache: (used by Drupal\Core\Entity\DatabaseStorageController) diff --git a/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php index 3a111fe..543ba6f 100644 --- a/core/lib/Drupal/Core/Entity/EntityListController.php +++ b/core/lib/Drupal/Core/Entity/EntityListController.php @@ -84,23 +84,12 @@ class EntityListController implements EntityListControllerInterface { } /** - * Retrieves the entity list path from the entity information. - * - * @return string - * The internal system path where the entity list will be rendered. - * - * @todo What is this method for, other than fetching the list path? Is this - * for http://drupal.org/node/1783964 ? Should it be on the interface? - */ - public function getPath() { - return $this->entityInfo['list path']; - } - - /** - * Builds the header row. + * Builds the header row for the entity listing. * * @return array - * An array of header strings. + * A render array structure of header strings. + * + * @see Drupal\Core\Entity\EntityListController::render() */ public function buildHeader() { $row['label'] = t('Label'); @@ -110,30 +99,34 @@ class EntityListController implements EntityListControllerInterface { } /** - * Builds an array of data for each row. + * Builds a row for an entity in the entity listing. * * @param Drupal\Core\Entity\EntityInterface $entity * The entity for this row of the list. * * @return array - * An array of fields to use for this entity. + * A render array structure of fields for this entity. + * + * @see Drupal\Core\Entity\EntityListController::render() */ public function buildRow(EntityInterface $entity) { $row['label'] = $entity->label(); $row['id'] = $entity->id(); $operations = $this->buildOperations($entity); - $row['operations'] = drupal_render($operations); + $row['operations']['data'] = $operations; return $row; } /** - * Renders a list of operation links. + * Builds a renderable list of operation links for the entity. * * @param Drupal\Core\Entity\EntityInterface $entity * The entity on which the linked operations will be performed. * * @return array * A renderable array of operation links. + * + * @see Drupal\Core\Entity\EntityListController::render() */ public function buildOperations(EntityInterface $entity) { // Retrieve and sort operations. @@ -148,16 +141,21 @@ class EntityListController implements EntityListControllerInterface { /** * Implements Drupal\Core\Entity\EntityListControllerInterface::render(). + * + * Builds the entity list as renderable array for theme_table(). + * + * @todo Add a link to add a new item to the #empty text once + * http://drupal.org/node/1783964 is resolved. */ public function render() { $build = array( '#theme' => 'table', '#header' => $this->buildHeader(), '#rows' => array(), - '#empty' => t('There is no @label yet. Add one.', array( - '@label' => $this->entityInfo['label'], - '@add-url' => url($this->getPath() . '/add'), - )), + '#empty' => t( + 'There is no @label yet.', + array('@label' => $this->entityInfo['label']) + ), ); foreach ($this->load() as $entity) { $build['#rows'][$entity->id()] = $this->buildRow($entity); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php index 52f7bc6..ac360ee 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php @@ -68,9 +68,6 @@ class ConfigEntityListTest extends WebTestBase { $actual_operations = $controller->getOperations($entity); $this->assertIdentical($expected_operations, $actual_operations, 'Return value from getOperations matches expected.'); - // Test getPath() method. - $this->assertIdentical($controller->getPath(), 'admin/structure/config_test', 'Return value from getPath is correct.'); - // Test buildHeader() method. $expected_items = array( 'label' => 'Label', @@ -85,7 +82,9 @@ class ConfigEntityListTest extends WebTestBase { $expected_items = array( 'label' => 'Default', 'id' => 'default', - 'operations' => render($build_operations), + 'operations' => array( + 'data' => $build_operations, + ), ); $actual_items = $controller->buildRow($entity); $this->assertIdentical($expected_items, $actual_items, 'Return value from buildRow matches expected.'); @@ -95,19 +94,21 @@ class ConfigEntityListTest extends WebTestBase { * Tests the listing UI. */ function testListUI() { + // Log in as an administrative user to access the full menu trail. + $this->drupalLogin($this->drupalCreateUser(array('access administration pages'))); + // Get the list callback page. $page = $this->drupalGet('admin/structure/config_test'); // Test for the page title. - $element = $this->xpath('//h1[@id="page-title"]'); - $this->assertEqual((string) $element[0], 'Test configuration', 'Page title found.'); + $this->assertTitle('Test configuration | Drupal'); // Test for the table. - $element = $this->xpath('//div[@id="content"]/table'); + $element = $this->xpath('//div[@id="content"]//table'); $this->assertTrue($element, 'Configuration entity list table found.'); // Test the table header. - $elements = $this->xpath('//div[@id="content"]/table/thead/tr/th'); + $elements = $this->xpath('//div[@id="content"]//table/thead/tr/th'); $this->assertEqual(count($elements), 3, 'Correct number of table header cells found.'); // Test the contents of each th cell. @@ -117,28 +118,60 @@ class ConfigEntityListTest extends WebTestBase { } // Check the number of table row cells. - $elements = $this->xpath('//div[@id="content"]/table/tbody/tr[@class="odd"]/td'); + $elements = $this->xpath('//div[@id="content"]//table/tbody/tr[@class="odd"]/td'); $this->assertEqual(count($elements), 3, 'Correct number of table row cells found.'); - // Check the contents of each row cell. + // Check the contents of each row cell. The first cell contains the label, + // the second contains the machine name, and the third contains the + // operations list. $this->assertIdentical((string) $elements[0], 'Default'); $this->assertIdentical((string) $elements[1], 'default'); $this->assertTrue($elements[2]->children()->xpath('//ul'), 'Operations list found.'); - // Verify the 'Add' action link is present. + // Confirm that the 'Add' link is present and works as expected. $this->assertLink('Add test configuration'); $this->clickLink('Add test configuration'); $this->assertResponse(200); - $this->drupalSetContent($page); + // Add a new entity and confirm that it is added to the list. + $edit = array('label' => 'Antelope', 'id' => 'antelope'); + $this->drupalPost(NULL, $edit, t('Save')); + $this->assertText('Antelope configuration has been created.'); + $this->assertFieldByXpath('//td', 'Antelope', "Label found for added 'Antelope' entity."); + $this->assertFieldByXpath('//td', 'antelope', "Machine name found for added 'Antelope' entity."); - // Verify that the expected operation links work. - foreach (array('Edit', 'Delete') as $link) { - $this->drupalSetContent($page); - $this->assertLink($link); - $this->clickLink($link); - $this->assertResponse(200); - } + // Confirm that the 'Edit' link is present and works as expected. + $this->assertLink('Edit'); + $this->clickLink('Edit'); + $this->assertResponse(200); + $this->assertTitle('Edit test configuration | Drupal'); + + // Update the entity and confirm that it is changed on the list. + $edit = array('label' => 'Albatross', 'id' => 'albatross'); + $this->drupalPost(NULL, $edit, t('Save')); + $this->assertText('Albatross configuration has been updated.'); + $this->assertFieldByXpath('//td', 'Albatross', "Label found for updated 'Albatross' entity."); + $this->assertFieldByXpath('//td', 'albatross', "Machine name found for updated 'Albatross' entity."); + + // Confirm that the 'Delete' link is works as expected. + $this->assertLink('Delete'); + $this->clickLink('Delete'); + $this->assertResponse(200); + $this->assertTitle('Are you sure you want to delete Albatross | Drupal'); + + // Delete the entity and confirm that it is removed from the list. + $this->drupalPost(NULL, array(), t('Delete')); + $this->assertNoFieldByXpath('//td', 'Albatross', "No label found for deleted 'Albatross' entity."); + $this->assertNoFieldByXpath('//td', 'albatross', "No machine name found for deleted 'Albatross' entity."); + + // Delete the original entity and confirm that the empty text is displayed. + $this->clickLink('Delete'); + $this->assertResponse(200); + $this->assertTitle('Are you sure you want to delete Default | Drupal'); + $this->drupalPost(NULL, array(), t('Delete')); + $this->assertNoFieldByXpath('//td', 'Default', "No label found for deleted 'Default' entity."); + $this->assertNoFieldByXpath('//td', 'default', "No machine name found for deleted 'Default' entity."); + $this->assertText('There is no Test configuration yet.'); } } diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module index 439d6a7..44df4da 100644 --- a/core/modules/config/tests/config_test/config_test.module +++ b/core/modules/config/tests/config_test/config_test.module @@ -83,7 +83,6 @@ function config_test_entity_info() { 'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController', 'entity class' => 'Drupal\config_test\ConfigTest', 'list controller class' => 'Drupal\Core\Config\Entity\ConfigEntityListController', - 'list path' => 'admin/structure/config_test', 'uri callback' => 'config_test_uri', 'config prefix' => 'config_test.dynamic', 'entity keys' => array(