diff -u b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php --- b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php @@ -51,14 +51,14 @@ * Overrides EntityListController::buildHeader(). */ public function buildHeader() { - $row = parent::buildHeader(); - if (empty($this->weightKey)) { - return $row; + // Override defaults to sort data rows. + $row['title'] = $this->entityInfo['label']; + $row['operations'] = t('Operations'); + if (!empty($this->weightKey)) { + // Add weight column as last column as fallback for none-JS. + // @todo Make it easy: http://drupal.org/node/1876718 + $row['weight'] = t('Weight'); } - // @see http://drupal.org/node/1876718 - $row = array_slice($row, 0, 2, TRUE) + array( - 'weight' => t('Weight'), - ) + array_slice($row, 0, NULL, TRUE); return $row; } @@ -67,29 +67,34 @@ */ public function buildRow(EntityInterface $entity) { $row = parent::buildRow($entity); + // Configurable entities could have default link to their edit pages. + if ($uri = $entity->uri()) { + $row['title'] = array('data' => array( + '#markup' => l($row['label'], $uri['path'], $uri['options']), + )); + } + else { + $row['title'] = array('data' => array( + '#markup' => $row['label'], + )); + } + // Save to be reused by contrib. + unset($row['id']); + unset($row['label']); if (empty($this->weightKey)) { return $row; } // Override default values to markup elements. - $row['label'] = array( - '#markup' => check_plain($row['label']), - ); - $row['id'] = array( - '#markup' => check_plain($row['id']), - ); $row['#attributes']['class'][] = 'draggable'; $row['#weight'] = $entity->get($this->weightKey); - // @see http://drupal.org/node/1876718 - $row = array_slice($row, 0, 2, TRUE) + array( - 'weight' => array( - '#type' => 'textfield', - '#title' => t('Weight for @title', array('@title' => $entity->label())), - '#title_display' => 'invisible', - '#default_value' => $entity->get($this->weightKey), - '#size' => 4, - '#attributes' => array('class' => array('weight')), - ), - ) + array_slice($row, 0, NULL, TRUE); + // Add weight column. + $row['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $entity->label())), + '#title_display' => 'invisible', + '#default_value' => $entity->get($this->weightKey), + '#attributes' => array('class' => array('weight')), + ); return $row; } @@ -129,8 +134,12 @@ ), ); + // Save header's order of columns for sorting data-rows. + $header = array_keys($form['entities']['#header']); foreach ($this->load() as $entity) { - $form['entities'][$entity->id()] = $this->buildRow($entity); + $row = $this->buildRow($entity); + // Sort row columns by header's order. + $form['entities'][$entity->id()] = array_merge(array_flip($header), $row); } $form['actions']['#type'] = 'actions'; @@ -138,6 +147,7 @@ '#type' => 'submit', '#value' => t('Save'), '#submit' => array(array($this, 'submit')), + '#button_type' => 'primary', ); return $form; diff -u b/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php --- b/core/lib/Drupal/Core/Entity/EntityListController.php +++ b/core/lib/Drupal/Core/Entity/EntityListController.php @@ -152,8 +152,12 @@ '#rows' => array(), '#empty' => t('There is no @label yet.', array('@label' => $this->entityInfo['label'])), ); + // Save header's order of columns for sorting data-rows. + $header = array_keys($build['#header']); foreach ($this->load() as $entity) { - $build['#rows'][$entity->id()] = $this->buildRow($entity); + $row = $this->buildRow($entity); + // Sort row columns by the header's order. + $build['#rows'][$entity->id()] = array_merge(array_flip($header), $row); } return $build; } diff -u b/core/modules/contact/lib/Drupal/contact/CategoryListController.php b/core/modules/contact/lib/Drupal/contact/CategoryListController.php --- b/core/modules/contact/lib/Drupal/contact/CategoryListController.php +++ b/core/modules/contact/lib/Drupal/contact/CategoryListController.php @@ -1,7 +1,7 @@ $title, 'default' => t('Default'), 'recipients' => t('Recipients'), - ) + array_slice($row, 0, NULL, TRUE); - return $row; + ) + $row; } /** @@ -57,19 +58,15 @@ */ public function buildRow(EntityInterface $entity) { $default_category = config('contact.settings')->get('default_category'); - $row = parent::buildRow($entity); - // The two array_slice work together to put additional columns after the - // first ones. - // @see http://drupal.org/node/1876718 - $row = array_slice($row, 0, 2, TRUE) + array( + // Add own columns. + return parent::buildRow($entity) + array( 'default' => array( '#markup' => ($default_category == $entity->id() ? t('Yes') : t('No')), ), 'recipients' => array( '#markup' => check_plain(implode(', ', $entity->recipients)), ), - ) + array_slice($row, 0, NULL, TRUE); - return $row; + ); } } only in patch2: unchanged: --- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php @@ -70,22 +70,22 @@ function testList() { $this->assertIdentical($expected_operations, $actual_operations, 'Return value from getOperations matches expected.'); // Test buildHeader() method. + $entity_info = entity_get_info('config_test'); $expected_items = array( - 'label' => 'Label', - 'id' => 'Machine name', + 'title' => $entity_info['label'], 'operations' => 'Operations', ); $actual_items = $controller->buildHeader(); $this->assertIdentical($expected_items, $actual_items, 'Return value from buildHeader matches expected.'); // Test buildRow() method. - $build_operations = $controller->buildOperations($entity); $expected_items = array( - 'label' => 'Default', - 'id' => 'default', 'operations' => array( - 'data' => $build_operations, + 'data' => $controller->buildOperations($entity), ), + 'title' => array('data' => array( + '#markup' => l('Default', $uri['path'], $uri['options']), + )), ); $actual_items = $controller->buildRow($entity); $this->assertIdentical($expected_items, $actual_items, 'Return value from buildRow matches expected.'); @@ -110,24 +110,26 @@ function testListUI() { // Test the table header. $elements = $this->xpath('//div[@id="content"]//table/thead/tr/th'); - $this->assertEqual(count($elements), 3, 'Correct number of table header cells found.'); + $this->assertEqual(count($elements), 2, 'Correct number of table header cells found.'); // Test the contents of each th cell. - $expected_items = array('Label', 'Machine name', 'Operations'); + $entity_info = entity_get_info('config_test'); + $expected_items = array($entity_info['label'], 'Operations'); foreach ($elements as $key => $element) { $this->assertIdentical((string) $element[0], $expected_items[$key]); } // Check the number of table row cells. $elements = $this->xpath('//div[@id="content"]//table/tbody/tr[@class="odd"]/td'); - $this->assertEqual(count($elements), 3, 'Correct number of table row cells found.'); + $this->assertEqual(count($elements), 2, 'Correct number of table row cells found.'); // 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.'); + $title = $elements[0]->children(); + $this->assertIdentical((string) $title[0]['href'], '/admin/structure/config_test/manage/default'); + $this->assertIdentical((string) $title, 'Default'); + $this->assertTrue($elements[1]->children()->xpath('//ul'), 'Operations list found.'); // Add a new entity using the operations link. $this->assertLink('Add test configuration'); @@ -139,8 +141,8 @@ function testListUI() { // Confirm that the user is returned to the listing, and verify that the // text of the label and machine name appears in the list (versus elsewhere // on the page). - $this->assertFieldByXpath('//td', 'Antelope', "Label found for added 'Antelope' entity."); - $this->assertFieldByXpath('//td', 'antelope', "Machine name found for added 'Antelope' entity."); + $this->assertText('Antelope'); + $this->assertLinkByHref('/admin/structure/config_test/manage/antelope'); // Edit the entity using the operations link. $this->assertLink('Edit'); @@ -153,8 +155,9 @@ function testListUI() { // Confirm that the user is returned to the listing, and verify that the // text of the label and machine name appears in the list (versus elsewhere // on the page). - $this->assertFieldByXpath('//td', 'Albatross', "Label found for updated 'Albatross' entity."); - $this->assertFieldByXpath('//td', 'albatross', "Machine name found for updated 'Albatross' entity."); + $element = $this->xpath('//td/a'); + $this->assertFieldByXpath('//td/a', 'Albatross', "Label found for updated 'Albatross' entity."); + $this->assertEqual($element[0]['href'], '/admin/structure/config_test/manage/albatross', "Machine name found for updated 'Albatross' entity."); // Delete the added entity using the operations link. $this->assertLink('Delete'); only in patch2: unchanged: --- a/core/modules/menu/lib/Drupal/menu/MenuListController.php +++ b/core/modules/menu/lib/Drupal/menu/MenuListController.php @@ -15,28 +15,30 @@ class MenuListController extends ConfigEntityListController { /** - * Overrides \Drupal\Core\Entity\EntityListController::buildHeader(). + * Overrides ConfigEntityListController::buildHeader(). */ public function buildHeader() { - $row['title'] = t('Title'); - $row['description'] = array( - 'data' => t('Description'), - 'class' => array(RESPONSIVE_PRIORITY_MEDIUM), - ); - $row['operations'] = t('Operations'); - return $row; + $row = parent::buildHeader(); + // Add description column after title. + $title = $row['title']; + unset($row['title']); + // @todo Clean up this http://drupal.org/node/1876718 + return array( + 'title' => $title, + 'description' => array( + 'data' => t('Description'), + 'class' => array(RESPONSIVE_PRIORITY_MEDIUM), + ) + ) + $row; } /** - * Overrides \Drupal\Core\Entity\EntityListController::buildRow(). + * Overrides ConfigEntityListController::buildRow(). */ public function buildRow(EntityInterface $entity) { - $row['title'] = array( - 'data' => check_plain($entity->label()), - 'class' => array('menu-label'), - ); + $row = parent::buildRow($entity); + $row['title']['class'] = array('menu-label'); $row['description'] = filter_xss_admin($entity->description); - $row['operations']['data'] = $this->buildOperations($entity); return $row; } @@ -72,7 +74,7 @@ public function getOperations(EntityInterface $entity) { } /** - * Overrides \Drupal\Core\Entity\EntityListController::render(); + * Overrides ConfigEntityListController::render(); */ public function render() { $build = parent::render(); only in patch2: unchanged: --- a/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php +++ b/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php @@ -99,7 +99,6 @@ public function testPictureAdmin() { $this->drupalGet('admin/config/media/picturemapping'); $this->assertNoText('There is no Picture mapping yet.'); $this->assertText('Mapping One'); - $this->assertText('mapping_one'); // Edit the group. $this->drupalGet('admin/config/media/picturemapping/mapping_one/edit'); only in patch2: unchanged: --- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutListController.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutListController.php @@ -15,47 +15,25 @@ class ShortcutListController extends ConfigEntityListController { /** - * Overrides \Drupal\Core\Entity\EntityListController::buildHeader(). - */ - public function buildHeader() { - $row['label'] = t('Name'); - $row['operations'] = t('Operations'); - return $row; - } - - /** - * Overrides \Drupal\Core\Entity\EntityListController::getOperations(). + * Overrides ConfigEntityListController::getOperations(). */ public function getOperations(EntityInterface $entity) { + $operations = parent::getOperations($entity); $uri = $entity->uri(); $operations['list'] = array( 'title' => t('list links'), 'href' => $uri['path'], - ); - $operations['edit'] = array( - 'title' => t('edit set'), - 'href' => $uri['path'] . '/edit', 'options' => $uri['options'], - 'weight' => 10, + 'weight' => 0, ); + $operations['edit']['title'] = t('edit set'); if (shortcut_set_delete_access($entity)) { - $operations['delete'] = array( - 'title' => t('delete set'), - 'href' => $uri['path'] . '/delete', - 'options' => $uri['options'], - 'weight' => 100, - ); + $operations['delete']['title'] = t('delete set'); + } + else { + unset($operations['delete']); } return $operations; } - /** - * Overrides \Drupal\Core\Entity\EntityListController::buildRow(). - */ - public function buildRow(EntityInterface $entity) { - $row['name'] = check_plain($entity->label()); - $row['operations']['data'] = $this->buildOperations($entity); - return $row; - } - } only in patch2: unchanged: --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php @@ -19,13 +19,17 @@ * label = @Translation("Taxonomy vocabulary"), * module = "taxonomy", * controller_class = "Drupal\taxonomy\VocabularyStorageController", + * list_controller_class = "Drupal\taxonomy\VocabularyListController", * form_controller_class = { * "default" = "Drupal\taxonomy\VocabularyFormController" * }, + * uri_callback = "taxonomy_vocabulary_uri", * config_prefix = "taxonomy.vocabulary", * entity_keys = { * "id" = "vid", - * "label" = "name" + * "label" = "name", + * "uuid" = "uuid", + * "weight" = "weight" * }, * view_modes = { * "full" = { @@ -83,4 +87,5 @@ class Vocabulary extends ConfigEntityBase { public function id() { return $this->vid; } + } only in patch2: unchanged: --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php @@ -90,7 +90,7 @@ function testTaxonomyAdminChangingWeights() { foreach ($vocabularies as $key => $vocabulary) { $weight = -$vocabulary->weight; $vocabularies[$key]->weight = $weight; - $edit[$key . '[weight]'] = $weight; + $edit["entities[$key][weight]"] = $weight; } // Saving the new weights via the interface. $this->drupalPost('admin/structure/taxonomy', $edit, t('Save')); @@ -119,7 +119,8 @@ function testTaxonomyAdminNoVocabularies() { $this->assertFalse(taxonomy_vocabulary_load_multiple(), 'No vocabularies found.'); $this->drupalGet('admin/structure/taxonomy'); // Check the default message for no vocabularies. - $this->assertText(t('No vocabularies available.')); + $entity_info = entity_get_info('taxonomy_vocabulary'); + $this->assertText(t('There is no @label yet.', array('@label' => $entity_info['label']))); } /** only in patch2: unchanged: --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php @@ -0,0 +1,42 @@ +uri(); + + $operations['edit']['title'] = t('edit vocabulary'); + $operations['list'] = array( + 'title' => t('list terms'), + 'href' => $uri['path'], + 'options' => $uri['options'], + 'weight' => 0, + ); + $operations['add'] = array( + 'title' => t('add terms'), + 'href' => $uri['path'] . '/add', + 'options' => $uri['options'], + 'weight' => 30, + ); + unset($operations['delete']); + + return $operations; + } + +} only in patch2: unchanged: --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -9,109 +9,16 @@ use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary; /** - * Form builder to list and manage vocabularies. + * Page callback: Lists taxonomy vocabularies. * - * @ingroup forms - * @see taxonomy_overview_vocabularies_submit() - * @see theme_taxonomy_overview_vocabularies() - */ -function taxonomy_overview_vocabularies($form) { - $vocabularies = taxonomy_vocabulary_load_multiple(); - taxonomy_vocabulary_sort($vocabularies); - $form['#tree'] = TRUE; - foreach ($vocabularies as $vocabulary) { - $form[$vocabulary->id()]['#vocabulary'] = $vocabulary; - $form[$vocabulary->id()]['name'] = array('#markup' => check_plain($vocabulary->name)); - $form[$vocabulary->id()]['weight'] = array( - '#type' => 'weight', - '#title' => t('Weight for @title', array('@title' => $vocabulary->name)), - '#title_display' => 'invisible', - '#delta' => 10, - '#default_value' => $vocabulary->weight, - ); - $links = array(); - $links['edit'] = array( - 'title' => t('edit vocabulary'), - 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/edit", - ); - $links['list'] = array( - 'title' => t('list terms'), - 'href' => "admin/structure/taxonomy/{$vocabulary->id()}", - ); - $links['add'] = array( - 'title' => t('add terms'), - 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/add", - ); - $form[$vocabulary->id()]['operations'] = array( - '#type' => 'operations', - '#links' => $links, - ); - } - - // Only make this form include a submit button and weight if more than one - // vocabulary exists. - if (count($vocabularies) > 1) { - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#button_type' => 'primary'); - } - elseif (isset($vocabulary)) { - unset($form[$vocabulary->id()]['weight']); - } - return $form; -} - -/** - * Submit handler for vocabularies overview. Updates changed vocabulary weights. - * - * @see taxonomy_overview_vocabularies() - */ -function taxonomy_overview_vocabularies_submit($form, &$form_state) { - foreach ($form_state['values'] as $vid => $vocabulary) { - if (isset($form[$vid]['#vocabulary']) && $form[$vid]['#vocabulary']->weight != $form_state['values'][$vid]['weight']) { - $form[$vid]['#vocabulary']->weight = $form_state['values'][$vid]['weight']; - taxonomy_vocabulary_save($form[$vid]['#vocabulary']); - } - } - drupal_set_message(t('The configuration options have been saved.')); -} - -/** - * Returns HTML for the vocabulary overview form as a sortable list of vocabularies. + * @return array + * A build array in the format expected by drupal_render(). * - * @param $variables - * An associative array containing: - * - form: A render element representing the form. - * - * @see taxonomy_overview_vocabularies() - * @ingroup themeable + * @see taxonomy_menu() */ -function theme_taxonomy_overview_vocabularies($variables) { - $form = $variables['form']; - - $rows = array(); - - foreach (element_children($form) as $key) { - if (isset($form[$key]['name'])) { - $vocabulary = &$form[$key]; - - $row = array(); - $row[] = drupal_render($vocabulary['name']); - if (isset($vocabulary['weight'])) { - $vocabulary['weight']['#attributes']['class'] = array('vocabulary-weight'); - $row[] = drupal_render($vocabulary['weight']); - } - $row[] = drupal_render($vocabulary['operations']); - $rows[] = array('data' => $row, 'class' => array('draggable')); - } - } - - $header = array(t('Vocabulary name')); - if (isset($form['actions'])) { - $header[] = t('Weight'); - drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight'); - } - $header[] = t('Operations'); - return theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No vocabularies available. Add vocabulary.', array('@link' => url('admin/structure/taxonomy/add'))), 'attributes' => array('id' => 'taxonomy'))) . drupal_render_children($form); +function taxonomy_vocabulary_list() { + return drupal_container()->get('plugin.manager.entity') + ->getListController('taxonomy_vocabulary')->render(); } /** only in patch2: unchanged: --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -123,7 +123,7 @@ function taxonomy_entity_info(&$info) { } /** - * Entity URI callback. + * Entity URI callback for the taxonomy term. */ function taxonomy_term_uri($term) { return array( @@ -132,6 +132,19 @@ function taxonomy_term_uri($term) { } /** + * Entity URI callback for the taxonomy vocabulary. + * + * * + * @param \Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $entity + * A Taxonomy vocabulary entity. + */ +function taxonomy_vocabulary_uri($entity) { + return array( + 'path' => 'admin/structure/taxonomy/' . $entity->id(), + ); +} + +/** * Implements hook_field_extra_fields(). */ function taxonomy_field_extra_fields() { @@ -224,9 +237,6 @@ function taxonomy_select_nodes($tid, $pager = TRUE, $limit = FALSE, $order = arr */ function taxonomy_theme() { return array( - 'taxonomy_overview_vocabularies' => array( - 'render element' => 'form', - ), 'taxonomy_overview_terms' => array( 'render element' => 'form', ), @@ -244,8 +254,7 @@ function taxonomy_menu() { $items['admin/structure/taxonomy'] = array( 'title' => 'Taxonomy', 'description' => 'Manage tagging, categorization, and classification of your content.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('taxonomy_overview_vocabularies'), + 'page callback' => 'taxonomy_vocabulary_list', 'access arguments' => array('administer taxonomy'), 'file' => 'taxonomy.admin.inc', );