diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php index 4ef6911..63d8591 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php @@ -9,14 +9,36 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityListController; +use Drupal\Core\Entity\EntityStorageControllerInterface; +use Drupal\Core\Form\FormInterface; /** * Defines the default list controller for ConfigEntity objects. */ -class ConfigEntityListController extends EntityListController { +class ConfigEntityListController extends EntityListController implements FormInterface { /** - * Overrides Drupal\Core\Entity\EntityListController::load(). + * Name of the entity's weight field or FALSE if no field is provided. + * + * @var string|bool + */ + protected $weightKey; + + /** + * {@inheritdoc} + */ + public function __construct($entity_type, EntityStorageControllerInterface $storage) { + parent::__construct($entity_type, $storage); + + $this->weightKey = FALSE; + // Check if the entity type supports weighting. + if (!empty($this->entityInfo['entity_keys']['weight'])) { + $this->weightKey = $this->entityInfo['entity_keys']['weight']; + } + } + + /** + * {@inheritdoc} */ public function load() { $entities = parent::load(); @@ -25,7 +47,7 @@ public function load() { } /** - * Overrides \Drupal\Core\Entity\EntityListController::getOperations(); + * {@inheritdoc} */ public function getOperations(EntityInterface $entity) { $operations = parent::getOperations($entity); @@ -51,4 +73,133 @@ public function getOperations(EntityInterface $entity) { return $operations; } + /** + * {@inheritdoc} + */ + public function buildHeader() { + $row = parent::buildHeader(); + // Override defaults. + $row['label'] = $this->entityInfo['label']; + unset($row['id']); + if (empty($this->weightKey)) { + return $row; + } + // @todo Simplify this http://drupal.org/node/1876718 + return $row = array_slice($row, 0, 2, TRUE) + array( + 'weight' => t('Weight'), + ) + array_slice($row, 0, NULL, TRUE); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + $row = parent::buildRow($entity); + // Configurable entities could have default link to their edit pages. + if ($uri = $entity->uri()) { + $row['label'] = array('data' => array( + '#type' => 'link', + '#title' => $row['label'], + '#href' => $uri['path'], + '#options' => $uri['options'], + )); + } + else { + $row['label'] = array('data' => array( + '#markup' => check_plain($row['label']), + )); + } + unset($row['id']); + if (empty($this->weightKey)) { + return $row; + } + // Override default values to markup elements. + $row['#attributes']['class'][] = 'draggable'; + $row['#weight'] = $entity->get($this->weightKey); + // 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; + } + + /** + * {@inheritdoc} + */ + public function render() { + if (empty($this->weightKey)) { + return parent::render(); + } + + return drupal_get_form($this); + } + + /** + * {@inheritdoc} + */ + public function getFormID() { + return $this->entityType . '_admin_list_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, array &$form_state) { + $form['entities'] = array( + '#type' => 'table', + '#header' => $this->buildHeader(), + '#empty' => t('There is no @label yet.', array('@label' => $this->entityInfo['label'])), + '#tabledrag' => array( + array('order', 'sibling', 'weight'), + ), + ); + + // Save header's order of columns for sorting data-rows. + $header_sort = array_flip(array_keys($form['entities']['#header'])); + foreach ($this->load() as $entity) { + $row = $this->buildRow($entity); + // Sort row columns by header's order. + $form['entities'][$entity->id()] = array_merge($header_sort, $row); + } + + $form['actions']['#type'] = 'actions'; + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save order'), + '#submit' => array(array($this, 'submit')), + '#button_type' => 'primary', + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + // No validation. + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + $values = $form_state['values']['entities']; + + $entities = \Drupal::service('plugin.manager.entity')->getStorageController($this->entitytType)->load(array_keys($values)); + foreach ($values as $id => $value) { + if (isset($entities[$id]) && $entities[$id]->get($this->weightKey) != $value['weight']) { + // Save entity only when its weight was changed. + $entities[$id]->set($this->weightKey, $value['weight']); + $entities[$id]->save(); + } + } + + drupal_set_message(t('The configuration options have been saved.')); + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php index 354b27a..394d566 100644 --- a/core/lib/Drupal/Core/Entity/EntityListController.php +++ b/core/lib/Drupal/Core/Entity/EntityListController.php @@ -112,8 +112,7 @@ public function buildHeader() { public function buildRow(EntityInterface $entity) { $row['label'] = $entity->label(); $row['id'] = $entity->id(); - $operations = $this->buildOperations($entity); - $row['operations']['data'] = $operations; + $row['operations']['data'] = $this->buildOperations($entity); return $row; } @@ -153,8 +152,12 @@ public function render() { '#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_sort = array_flip(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($header_sort, $row); } return $build; } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php index f851cbc..cac4e6f 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php @@ -79,21 +79,24 @@ function testList() { $this->assertIdentical($expected_operations, $actual_operations); // Test buildHeader() method. + $entity_info = $this->container->get('plugin.entity.manager')->getDefinition('config_test'); $expected_items = array( - 'label' => 'Label', - 'id' => 'Machine name', + 'label' => $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' => 'dotted.default', + 'label' => array('data' => array( + '#type' => 'link', + '#title' => 'Default', + '#href' => $uri['path'], + '#options' => $uri['options'], + )), 'operations' => array( - 'data' => $build_operations, + 'data' => $controller->buildOperations($entity), ), ); $actual_items = $controller->buildRow($entity); @@ -119,24 +122,25 @@ 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 = $this->container->get('plugin.entity.manager')->getDefinition('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], 'dotted.default'); - $this->assertTrue($elements[2]->children()->xpath('//ul'), 'Operations list found.'); + $title = $elements[0]->children(); + $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'); @@ -151,8 +155,7 @@ 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->assertFieldByXpath('//td/a', 'Antelope', "Label found for added 'Antelope' entity."); // Edit the entity using the operations link. $this->assertLink('Edit'); @@ -165,8 +168,7 @@ 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."); + $this->assertFieldByXpath('//td/a', 'Albatross', "Label found for updated 'Albatross' entity."); // Delete the added entity using the operations link. $this->assertLink('Delete'); @@ -178,7 +180,6 @@ function testListUI() { // Verify that the text of the label and machine name does not appear in // the list (though it may appear elsewhere on the page). $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 using the operations link. $this->clickLink('Delete'); @@ -189,7 +190,6 @@ function testListUI() { // Verify that the text of the label and machine name does not appear in // the list (though it may appear elsewhere on the page). $this->assertNoFieldByXpath('//td', 'Default', "No label found for deleted 'Default' entity."); - $this->assertNoFieldByXpath('//td', 'dotted.default', "No machine name found for deleted 'Default' entity."); // Confirm that the empty text is displayed. $this->assertText('There is no Test configuration yet.'); diff --git a/core/modules/contact/lib/Drupal/contact/CategoryListController.php b/core/modules/contact/lib/Drupal/contact/CategoryListController.php index a3ec886..f8e7b9b 100644 --- a/core/modules/contact/lib/Drupal/contact/CategoryListController.php +++ b/core/modules/contact/lib/Drupal/contact/CategoryListController.php @@ -1,7 +1,7 @@ moduleExists('field_ui')) { $uri = $entity->uri(); $operations['manage-fields'] = array( 'title' => t('Manage fields'), @@ -38,26 +38,33 @@ public function getOperations(EntityInterface $entity) { } /** - * Overrides Drupal\Core\Entity\EntityListController::buildHeader(). + * {@inheritdoc} */ public function buildHeader() { - $row['category'] = t('Category'); - $row['recipients'] = t('Recipients'); - $row['selected'] = t('Selected'); - $row['operations'] = t('Operations'); - return $row; + $row = parent::buildHeader(); + // The two array_slice work together to put additional columns after the + // first ones. + // @todo Simplify this http://drupal.org/node/1876718 + return array_slice($row, 0, 1, TRUE) + array( + 'default' => t('Default'), + 'recipients' => t('Recipients'), + ) + array_slice($row, 0, NULL, TRUE); } /** - * Overrides Drupal\Core\Entity\EntityListController::buildRow(). + * {@inheritdoc} */ public function buildRow(EntityInterface $entity) { - $row['category'] = check_plain($entity->label()); - $row['recipients'] = check_plain(implode(', ', $entity->recipients)); $default_category = config('contact.settings')->get('default_category'); - $row['selected'] = ($default_category == $entity->id() ? t('Yes') : t('No')); - $row['operations']['data'] = $this->buildOperations($entity); - return $row; + // 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)), + ), + ); } } diff --git a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php index 7e068da..25ee3a6 100644 --- a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php +++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php @@ -31,7 +31,8 @@ * entity_keys = { * "id" = "id", * "label" = "label", - * "uuid" = "uuid" + * "uuid" = "uuid", + * "weight" = "weight" * } * ) */ diff --git a/core/modules/menu/lib/Drupal/menu/MenuListController.php b/core/modules/menu/lib/Drupal/menu/MenuListController.php index e557d92..8144283 100644 --- a/core/modules/menu/lib/Drupal/menu/MenuListController.php +++ b/core/modules/menu/lib/Drupal/menu/MenuListController.php @@ -15,33 +15,32 @@ class MenuListController extends ConfigEntityListController { /** - * Overrides \Drupal\Core\Entity\EntityListController::buildHeader(). + * {@inheritdoc} */ 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 first too. + // @todo Simplify this http://drupal.org/node/1876718 + return array_slice($row, 0, 1, TRUE) + array( + 'description' => array( + 'data' => t('Description'), + 'class' => array(RESPONSIVE_PRIORITY_MEDIUM), + ), + ) + array_slice($row, 0, NULL, TRUE); } /** - * Overrides \Drupal\Core\Entity\EntityListController::buildRow(). + * {@inheritdoc} */ public function buildRow(EntityInterface $entity) { - $row['title'] = array( - 'data' => check_plain($entity->label()), - 'class' => array('menu-label'), - ); + $row = parent::buildRow($entity); + $row['label']['class'] = array('menu-label'); $row['description'] = filter_xss_admin($entity->description); - $row['operations']['data'] = $this->buildOperations($entity); return $row; } /** - * Overrides \Drupal\Core\Entity\EntityListController::getOperations(); + * {@inheritdoc} */ public function getOperations(EntityInterface $entity) { $operations = parent::getOperations($entity); @@ -67,7 +66,7 @@ public function getOperations(EntityInterface $entity) { } /** - * Overrides \Drupal\Core\Entity\EntityListController::render(); + * {@inheritdoc} */ public function render() { $build = parent::render(); diff --git a/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php b/core/modules/picture/lib/Drupal/picture/Tests/PictureAdminUITest.php index 114e30c..cca5880 100644 --- 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'); diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutListController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutListController.php index f1aaa60..cfa93d0 100644 --- 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(). + * {@inheritdoc} */ 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 ($entity->access('delete')) { - $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; - } - } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php index b2e79c9..fdd3ac6 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php @@ -98,7 +98,6 @@ function testTaxonomyAdminChangingWeights() { // Load the vocabularies from the database. $this->container->get('plugin.manager.entity')->getStorageController('taxonomy_vocabulary')->resetCache(); $new_vocabularies = taxonomy_vocabulary_load_multiple(); - taxonomy_vocabulary_sort($new_vocabularies); // Check that the weights are saved in the database correctly. foreach ($vocabularies as $key => $vocabulary) { diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php index e427b4e..c3a9053 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php @@ -120,20 +120,19 @@ function testTaxonomyVocabularyLoadMultiple() { $names = taxonomy_vocabulary_get_names(); $this->assertEqual($names[$vocabulary1->id()], $vocabulary1->id(), 'Vocabulary 1 name found.'); - // Fetch all of the vocabularies using taxonomy_vocabulary_load_multiple(). + // Fetch all vocabularies skipping a cache. // Confirm that the vocabularies are ordered by weight. - $vocabularies = taxonomy_vocabulary_load_multiple(); - taxonomy_vocabulary_sort($vocabularies); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary1->id(), 'Vocabulary was found in the vocabularies array.'); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary2->id(), 'Vocabulary was found in the vocabularies array.'); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary3->id(), 'Vocabulary was found in the vocabularies array.'); + $vocabularies = $this->container->get('plugin.manager.entity')->getStorageController('taxonomy_vocabulary')->load(NULL, TRUE); + $loaded_order = array_keys($vocabularies); + $expected_order = array($vocabulary1->id(), $vocabulary2->id(), $vocabulary3->id()); + $this->assertIdentical($loaded_order, $expected_order); // Fetch the vocabularies with taxonomy_vocabulary_load_multiple(), specifying IDs. // Ensure they are returned in the same order as the original array. $vocabularies = taxonomy_vocabulary_load_multiple(array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id())); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary3->id(), 'Vocabulary loaded successfully by ID.'); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary2->id(), 'Vocabulary loaded successfully by ID.'); - $this->assertEqual(array_shift($vocabularies)->id(), $vocabulary1->id(), 'Vocabulary loaded successfully by ID.'); + $loaded_order = array_keys($vocabularies); + $expected_order = array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id()); + $this->assertIdentical($loaded_order, $expected_order); } /** diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc index 929543d..b44ae1d 100644 --- a/core/modules/taxonomy/taxonomy.admin.inc +++ b/core/modules/taxonomy/taxonomy.admin.inc @@ -17,7 +17,6 @@ */ function taxonomy_overview_vocabularies($form) { $vocabularies = taxonomy_vocabulary_load_multiple(); - taxonomy_vocabulary_sort($vocabularies); $form['vocabularies'] = array( '#type' => 'table', '#empty' => t('No vocabularies available. Add vocabulary.', array('@link' => url('admin/structure/taxonomy/add'))), diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index e10e9ea..d824da9 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -889,18 +889,6 @@ function taxonomy_vocabulary_load_multiple(array $vids = NULL) { } /** - * Sorts vocabularies by its weight and label. - * - * @param array $vocabularies - * An array of \Drupal\taxonomy\Plugin\Core\Entity\Vocabulary objects. - */ -function taxonomy_vocabulary_sort(array &$vocabularies = array()) { - // @todo Remove error suppressing when http://drupal.org/node/1799600 is - // fixed. - @uasort($vocabularies, 'Drupal\Core\Config\Entity\ConfigEntityBase::sort'); -} - -/** * Return the taxonomy vocabulary entity matching a vocabulary ID. * * @param int $vid