diff --git a/core/modules/config/config.module b/core/modules/config/config.module index b3d9bbc..7b4432c 100644 --- a/core/modules/config/config.module +++ b/core/modules/config/config.module @@ -1 +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 new file mode 100644 index 0000000..4601ecf --- /dev/null +++ b/core/modules/config/lib/Drupal/config/EntityListControllerBase.php @@ -0,0 +1,174 @@ +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 new file mode 100644 index 0000000..6b4ea1e --- /dev/null +++ b/core/modules/config/lib/Drupal/config/EntityListControllerInterface.php @@ -0,0 +1,89 @@ + 'Configuration entity list', + 'description' => 'Tests configuration entity listing.', + 'group' => 'Configuration', + ); + } + + /** + * Tests basic listing plugin functionilty. + */ + function testListingPlugin() { + $controller = config_get_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'); + } + + /** + * Tests the listing UI. + */ + function testListingUI() { + $page = $this->drupalGet('config-listing-test'); + + // Test that the page exists. + $this->assertText('Config test', 'Config test listing page title found.'); + + // Check we have the default id and label on the page too. + $this->assertText('default', '"default" ID found.'); + $this->assertText('Default', '"Default" label found'); + + // Check each link. + foreach (array('edit', 'add', 'delete') as $link) { + $this->drupalSetContent($page); + $this->assertLink($link); + $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 44d4087..40dd3fd 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_test\ConfigTestListController', + 'list path' => 'config-listing-test', 'uri callback' => 'config_test_uri', 'config prefix' => 'config_test.dynamic', 'entity keys' => array( 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 new file mode 100644 index 0000000..462359f --- /dev/null +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestListController.php @@ -0,0 +1,55 @@ +entityInfo['list path']; + + $items = parent::hookMenu(); + $items[$path]['title'] = 'Config test'; + $items[$path]['description'] = 'Config test listing page.'; + return $items; + } + + /** + * Implements Drupal\config\EntityListControllerInterface::defineOperationLinks(). + */ + 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, + ), + ); + } + +}