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..8d98df2 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityListControllerBase.php @@ -0,0 +1,110 @@ +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) { + $build = array( + '#theme' => 'links', + '#links' => $this->getOperations($entity), + ); + return $build; + } + + /** + * Implements Drupal\Core\Entity\EntityListControllerInterface::render(); + */ + public function render() { + $build = array( + '#theme' => 'table', + '#header' => $this->buildHeader(), + '#rows' => array(), + '#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 @@ + $type_info) { - if (isset($type_info['list controller class'])) { - $controller = config_get_list_controller($entity_type); - $items += $controller->hookMenu(); - } - } - return $items; -} - -/** - * Gets the entity list controller class for an entity type. - * - * @return Drupal\config\EntityListControllerInterface - */ -function config_get_list_controller($entity_type) { - $controllers = &drupal_static(__FUNCTION__, array()); - if (!isset($controllers[$entity_type])) { - $type_info = entity_get_info($entity_type); - $class = $type_info['list controller class']; - $controllers[$entity_type] = new $class($entity_type); - } - return $controllers[$entity_type]; -} - -/** - * Page callback: Displays a config listing page. - * - * @param Drupal\config\EntityListControllerInterface $controller - * The list controller for this entity. - * - * @return string - * The page markup for the page. - */ -function config_entity_listing_page($controller) { - return $controller->renderList(); -} - -/** - * Page callback: Calls a method on a config entity and reloads the listing page. - * - * @param Drupal\config\EntityListControllerInterface $controller - * The list controller for this entity. - * @param Drupal\Core\Entity\EntityInterface $entity - * The config entity being acted upon. - * @param string $op - * The operation to perform, e.g., 'enable' or 'disable'. - * - * @return mixed - * Either returns the listing page as JSON, or calls drupal_goto() to - * redirect back to the listing page. - */ -function config_ajax_callback(EntityListControllerInterface $controller, EntityInterface $entity, $op) { - // Perform the operation. - $entity->$op(); - - // If the request is via AJAX, return the rendered list as JSON. - if (drupal_container()->get('request')->request->get('js')) { - return $controller->renderListAJAX(); - } - // Otherwise, redirect back to the page. - else { - drupal_goto($controller->getPath()); - } -} diff --git a/core/modules/config/lib/Drupal/config/EntityListControllerBase.php b/core/modules/config/lib/Drupal/config/EntityListControllerBase.php deleted file mode 100644 index 4601ecf..0000000 --- a/core/modules/config/lib/Drupal/config/EntityListControllerBase.php +++ /dev/null @@ -1,174 +0,0 @@ -entityType = $entity_type; - $this->storage = entity_get_controller($this->entityType); - $this->entityInfo = entity_get_info($this->entityType); - } - - /** - * Implements Drupal\config\EntityListControllerInterface::getList(); - */ - public function getList() { - return $this->storage->load(); - } - - /** - * Implements Drupal\config\EntityListControllerInterface::getStorageController(); - */ - public function getStorageController() { - return $this->storage; - } - - public function getPath() { - return $this->entityInfo['list path']; - } - - /** - * Implements Drupal\config\EntityListControllerInterface::hookMenu(); - */ - public function hookMenu() { - $items = array(); - $items[$this->entityInfo['list path']] = array( - 'page callback' => 'config_entity_listing_page', - 'page arguments' => array($this), - // @todo Add a proper access callback here. - 'access callback' => TRUE, - ); - return $items; - } - - /** - * Implements Drupal\config\EntityListControllerInterface::getRowData(); - */ - public function getRowData(EntityInterface $entity) { - $row = array(); - - $row['id'] = $entity->id(); - $row['label'] = $entity->label(); - $operations = $this->buildOperationLinks($entity); - $row['operations'] = drupal_render($operations); - - return $row; - } - - /** - * Implements Drupal\config\EntityListControllerInterface::getHeaderData(); - */ - public function getHeaderData() { - $row = array(); - $row['id'] = t('Machine name'); - $row['label'] = t('Label'); - $row['operations'] = t('Operations'); - return $row; - } - - /** - * Implements Drupal\config\EntityListControllerInterface::buildOperationLinks(); - */ - public function buildOperationLinks(EntityInterface $entity) { - $links = array(); - - foreach ($this->defineOperationLinks($entity) as $definition) { - $attributes = array(); - - if (!empty($definition['ajax'])) { - $attributes['class'][] = 'use-ajax'; - // Set this to true if we haven't already. - if (!isset($this->usesAJAX)) { - $this->usesAJAX = TRUE; - } - } - - $links[] = array( - 'title' => $definition['title'], - 'href' => $definition['href'], - 'attributes' => $attributes, - ); - } - - return array( - '#theme' => 'links', - '#links' => $links, - ); - } - - /** - * Implements Drupal\config\EntityListControllerInterface::renderList(); - */ - public function renderList() { - $rows = array(); - - foreach ($this->getList() as $entity) { - $rows[] = $this->getRowData($entity); - } - - // Add core AJAX library if we need to. - if (!empty($this->usesAJAX)) { - drupal_add_library('system', 'drupal.ajax'); - } - - return array( - '#theme' => 'table', - '#header' => $this->getHeaderData(), - '#rows' => $rows, - '#attributes' => array( - 'id' => 'config-entity-listing', - ), - ); - } - - /** - * Implements Drupal\config\EntityListControllerInterface::renderList(); - */ - public function renderListAJAX() { - $list = $this->renderList(); - $commands = array(); - $commands[] = ajax_command_replace('#config-entity-listing', drupal_render($list)); - - return new JsonResponse(ajax_render($commands)); - } - -} diff --git a/core/modules/config/lib/Drupal/config/EntityListControllerInterface.php b/core/modules/config/lib/Drupal/config/EntityListControllerInterface.php deleted file mode 100644 index 6b4ea1e..0000000 --- a/core/modules/config/lib/Drupal/config/EntityListControllerInterface.php +++ /dev/null @@ -1,89 +0,0 @@ - 'Configuration entity list', - 'description' => 'Tests configuration entity listing.', + 'description' => 'Tests listing of configuration entities.', 'group' => 'Configuration', ); } /** - * Tests basic listing plugin functionilty. + * Tests entity list controller functionality. */ - function testListingPlugin() { - $controller = config_get_list_controller('config_test'); + function testList() { + $controller = entity_list_controller('config_test'); // Get a list of Config entities. $list = $controller->getList(); $this->assertEqual(count($list), 1, 'Correct number of plugins found.'); $this->assertTrue(!empty($list['default']), '"Default" config entity key found in list.'); - $this->assertTrue($list['default'] instanceof ConfigEntityBase, '"Default" config entity is an instance of ConfigEntityBase'); + $this->assertTrue($list['default'] instanceof ConfigTest, '"Default" config entity is an instance of ConfigTest.'); } /** * Tests the listing UI. */ - function testListingUI() { - $page = $this->drupalGet('config-listing-test'); + function testListUI() { + $page = $this->drupalGet('admin/structure/config_test'); // Test that the page exists. $this->assertText('Config test', 'Config test listing page title found.'); @@ -63,8 +63,6 @@ function testListingUI() { $this->clickLink($link); $this->assertResponse(200); } - - // @todo Test AJAX links. } } diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module index 40dd3fd..25bb89d 100644 --- a/core/modules/config/tests/config_test/config_test.module +++ b/core/modules/config/tests/config_test/config_test.module @@ -83,7 +83,7 @@ function config_test_entity_info() { 'controller class' => 'Drupal\config\ConfigStorageController', 'entity class' => 'Drupal\config_test\ConfigTest', 'list controller class' => 'Drupal\config_test\ConfigTestListController', - 'list path' => 'config-listing-test', + 'list path' => 'admin/structure/config_test', 'uri callback' => 'config_test_uri', 'config prefix' => 'config_test.dynamic', 'entity keys' => array( @@ -178,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) { diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestListController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestListController.php index 462359f..58d9e5f 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestListController.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestListController.php @@ -1,55 +1,47 @@ entityInfo['list path']; - - $items = parent::hookMenu(); - $items[$path]['title'] = 'Config test'; - $items[$path]['description'] = 'Config test listing page.'; - return $items; + public function load() { + $entities = parent::load(); + uasort($entities, 'Drupal\config\ConfigEntityBase::sort'); + return $entities; } /** - * Implements Drupal\config\EntityListControllerInterface::defineOperationLinks(). + * Implements Drupal\Core\Entity\EntityListControllerInterface::getOperations(). */ - public function defineOperationLinks(EntityInterface $entity) { - $id = $entity->id(); - - // @todo Add AJAX link to test. - return array( - 'edit' => array( - 'title' => 'edit', - 'href' => "admin/structure/config_test/manage/$id/edit", - 'ajax' => FALSE, - ), - 'add' => array( - 'title' => 'add', - 'href' => "admin/structure/config_test/add", - 'ajax' => FALSE, - ), - 'delete' => array( - 'title' => 'delete', - 'href' => "admin/structure/config_test/manage/$id/delete", - 'ajax' => FALSE, - ), + public function getOperations(EntityInterface $entity) { + $uri = $entity->uri(); + $operations['edit'] = array( + 'title' => t('edit'), + 'href' => $uri['path'] . '/edit', + 'options' => $uri['options'], + ); + $operations['delete'] = array( + 'title' => t('delete'), + 'href' => $uri['path'] . '/delete', + 'options' => $uri['options'], ); + return $operations; } }