diff --git a/core/includes/entity.inc b/core/includes/entity.inc index b651e32..24e150f 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -530,3 +530,48 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st field_attach_submit($entity_type, $entity, $form, $form_state); } } + +/** + * Returns an entity list controller for a given entity type. + * + * Since there might be different scenarios in which an entity is edited, + * multiple form controllers suitable to the different operations may be defined. + * If no controller is found for the default operation, the base class will be + * used. If a non-existing non-default operation is specified an exception will + * be thrown. + * + * @see hook_entity_info() + * + * @param string $entity_type + * The type of the entity. + * + * @return Drupal\Core\Entity\EntityListControllerInterface + * An entity list controller instance. + */ +function entity_list_controller($entity_type) { + $instances = &drupal_static(__FUNCTION__, array()); + + if (isset($instances[$entity_type])) { + return $instances[$entity_type]; + } + + $info = entity_get_info($entity_type); + + // Check whether there is a controller class for the specified operation. + if (!empty($info['list controller class'])) { + $class = $info['list controller class']; + } + // If no controller is specified default to the base implementation. + // @todo Provide a sane base implementation. + /* + elseif (empty($info['list controller class'])) { + $class = 'Drupal\Core\Entity\EntityListControllerBase'; + } + */ + else { + throw new \InvalidArgumentException("Missing list controller for '$entity_type'"); + } + + $instances[$entity_type] = new $class($entity_type); + return $instances[$entity_type]; +} diff --git a/core/lib/Drupal/Core/Entity/EntityListControllerBase.php b/core/lib/Drupal/Core/Entity/EntityListControllerBase.php new file mode 100644 index 0000000..e251dcb --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityListControllerBase.php @@ -0,0 +1,117 @@ +entityType = $entity_type; + $this->storage = entity_get_controller($this->entityType); + $this->entityInfo = entity_get_info($this->entityType); + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::getStorageController(); + */ + public function getStorageController() { + return $this->storage; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::load(); + */ + public function load() { + return $this->storage->load(); + } + + public function getPath() { + return $this->entityInfo['list path']; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::buildHeader(); + */ + public function buildHeader() { + $row['label'] = t('Label'); + $row['id'] = t('Machine name'); + $row['operations'] = t('Operations'); + return $row; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::buildRow(); + */ + public function buildRow(EntityInterface $entity) { + $row['label'] = $entity->label(); + $row['id'] = $entity->id(); + $operations = $this->buildOperations($entity); + $row['operations'] = drupal_render($operations); + return $row; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::buildOperations(); + */ + public function buildOperations(EntityInterface $entity) { + // Retrieve and sort operations. + $operations = $this->getOperations($entity); + uasort($operations, 'drupal_sort_weight'); + $build = array( + '#theme' => 'links', + '#links' => $operations, + ); + return $build; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::render(); + */ + 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' => $this->getPath() . '/add', + )), + '#attributes' => array( + 'id' => 'config-entity-listing', + ), + ); + foreach ($this->load() as $entity) { + $build['#rows'][$entity->id()] = $this->buildRow($entity); + } + return $build; + } + +} diff --git a/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php new file mode 100644 index 0000000..d93d91c --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php @@ -0,0 +1,74 @@ +uri(); + $operations['edit'] = array( + 'title' => t('edit'), + 'href' => $uri['path'] . '/edit', + 'options' => $uri['options'], + 'weight' => 10, + ); + $operations['delete'] = array( + 'title' => t('delete'), + 'href' => $uri['path'] . '/delete', + 'options' => $uri['options'], + 'weight' => 100, + ); + return $operations; + } + +} diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php new file mode 100644 index 0000000..46d52f6 --- /dev/null +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php @@ -0,0 +1,66 @@ + 'Configuration entity list', + 'description' => 'Tests listing of configuration entities.', + 'group' => 'Configuration', + ); + } + + /** + * Tests entity list controller functionality. + */ + function testList() { + $controller = entity_list_controller('config_test'); + + // Get a list of ConfigTest entities. + $list = $controller->load(); + $this->assertEqual(count($list), 1, '1 ConfigTest entity found.'); + $this->assertTrue(!empty($list['default']), '"Default" ConfigTest entity ID found.'); + $this->assertTrue($list['default'] instanceof ConfigTest, '"Default" ConfigTest entity is an instance of ConfigTest.'); + } + + /** + * Tests the listing UI. + */ + function testListUI() { + $page = $this->drupalGet('admin/structure/config_test'); + $this->assertText('Test configuration'); + + // Verify that the default ConfigTest configuration appears. + $this->assertText('default'); + $this->assertText('Default'); + + // Verify that operation links appear. + foreach (array('edit', 'delete') as $link) { + $this->drupalSetContent($page); + $this->assertLink($link); + $this->clickLink($link); + $this->assertResponse(200); + } + } + +} diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module index 44d4087..a75eff8 100644 --- a/core/modules/config/tests/config_test/config_test.module +++ b/core/modules/config/tests/config_test/config_test.module @@ -82,6 +82,8 @@ function config_test_entity_info() { 'label' => 'Test configuration', 'controller class' => 'Drupal\config\ConfigStorageController', 'entity class' => 'Drupal\config_test\ConfigTest', + 'list controller class' => 'Drupal\config\ConfigEntityListController', + 'list path' => 'admin/structure/config_test', 'uri callback' => 'config_test_uri', 'config prefix' => 'config_test.dynamic', 'entity keys' => array( @@ -176,8 +178,10 @@ function config_test_delete($id) { * Page callback; Lists available ConfigTest objects. */ function config_test_list_page() { + $controller = entity_list_controller('config_test'); + return $controller->render(); + $entities = entity_load_multiple('config_test'); - uasort($entities, 'Drupal\config\ConfigEntityBase::sort'); $rows = array(); foreach ($entities as $config_test) { @@ -201,9 +205,6 @@ function config_test_list_page() { '#theme' => 'table', '#header' => array('Name', 'Operations'), '#rows' => $rows, - '#empty' => format_string('No test configuration defined. Add some', array( - '@add-url' => url('admin/structure/config_test/add'), - )), ); return $build; }