Problem/Motivation

We have more and more configurable entities in core some of them have "weighting" so their administrative listings are forms that allows to drug-n-drop items.

Vocabularies, Contact category, Roles

Proposed resolution

Implement a generic form()/validate()/submit() methids in ConfigEntityListController.
This change have to allow config entity type's list controller implementations to simply it's administrative tasks.
Just override a buildHeader(), buildRow(), getOperations() methods to injects own columns.

Also there's should be a room for a kind of mixed form and list controller overrides for #663946: Merge "List links" page into "Edit menu" page
Also operations|actions should be integrated latter #1798540: Add link to add a new entity in an empty entity list controller table

Related issues
#1803586: Give all entities their own URI
#1783964-7: Allow entity types to provide menu items
#1798540: Add link to add a new entity in an empty entity list controller table
#1839516: Introduce entity operation providers

#1016056: Hide tabledrag handle and "Show row weights" when there is only one item in list

#663946: Merge "List links" page into "Edit menu" page

Depends on this issue
#1891690: Use EntityListController for vocabularies
#599770-15: Clean up the contact forms listing UI: Allow to set the default category and weights on the listing page
#1809376-15: Use EntityListController for image styles
#1872870: Implement a RoleListController and RoleFormController
#1787942-41: Allow assigning layouts to pages

Original report by @sun

We need that functionality in many places in core and contrib so let's refactor it and make it a part of the ConfigEntityListController.

CommentFileSizeAuthor
#100 tabledrag-1855402-100.patch23.25 KBtim.plunkett
#100 interdiff.txt2.52 KBtim.plunkett
#97 interdiff.txt7.77 KBtim.plunkett
#97 tabledrag-1855402-96.patch23.08 KBtim.plunkett
#93 tabledrag-1855402-93.patch25.66 KBtim.plunkett
#89 interdiff.txt665 bytesandypost
#89 1855402-sort-config-89.patch27.5 KBandypost
#87 1855402-sort-config-87.patch27.51 KBandypost
#84 interdiff.txt775 bytesandypost
#84 1855402-sort-config-84.patch28.16 KBandypost
#82 interdiff.txt2.97 KBandypost
#82 1855402-sort-config-82.patch27.4 KBandypost
#80 interdiff.txt1 KBandypost
#80 1855402-sort-config-80.patch26.33 KBandypost
#79 interdiff.txt1.71 KBandypost
#79 1855402-sort-config-79.patch26.26 KBandypost
#78 1855402-sort-config-77.patch26.61 KBandypost
#77 interdiff.txt6.71 KBandypost
#77 1855402-sort-config-77.patch0 bytesandypost
#75 1855402-sort-config-75.patch19.01 KBandypost
#72 1855402-sort-config-72.patch19.86 KBandypost
#66 interdiff.txt1.32 KBandypost
#66 1855402-sort-config-66.patch19.76 KBandypost
#64 interdiff.txt1001 bytesandypost
#64 1855402-sort-config-64.patch19.76 KBandypost
#63 interdiff.txt2.66 KBandypost
#63 1855402-sort-config-63.patch19.78 KBandypost
#61 1855402-sort-config-61.patch19.66 KBandypost
#60 interdiff.txt5.91 KBandypost
#60 1855402-config-sort-60.patch19.67 KBandypost
#56 interdiff.txt665 bytesandypost
#56 1855402-config-sort-56.patch20.55 KBandypost
#55 interdiff.txt3.44 KBandypost
#55 1855402-config-sort-55.patch20.63 KBandypost
#52 1855402-config-sort-52.patch20.64 KBandypost
#51 1855402-interdiff-51.txt691 bytesandypost
#51 1855402-config-sort-51.patch20.4 KBandypost
#48 contact.png14.76 KBandypost
#48 menus.png25.81 KBandypost
#48 1855402-interdiff-48.txt6.86 KBandypost
#48 1855402-config-sort-48.patch19.73 KBandypost
#39 1855402-interdiff-39.txt14.82 KBandypost
#39 1855402-config-sort-39.patch18.4 KBandypost
#39 contact1.png13.54 KBandypost
#39 contact2.png10.25 KBandypost
#39 menu1.png15 KBandypost
#39 menu2.png28.82 KBandypost
#38 interdiff-20.txt24.14 KBdawehner
#34 1855402-interdiff-34.txt5.34 KBandypost
#34 1855402-config-sort-34.patch29.23 KBandypost
#32 1855402-config-sort-32.patch23.88 KBandypost
#31 before.png9.08 KBandypost
#31 after.png9.49 KBandypost
#20 drupal-1855402-20.patch8.99 KBdawehner
#20 interdiff.txt6.05 KBdawehner
#17 1855402-17.patch11.57 KBfubhy
#13 1855402-interdiff-13.txt1.38 KBandypost
#13 1855402-config-sort-13.patch11.57 KBandypost
#12 1855402-interdiff-12.txt5.36 KBandypost
#12 1855402-config-sort-12.patch11.48 KBandypost
#10 drupal8.entity-list-tabledrag.10.patch8.38 KBsun
#5 1855402-5.patch25.04 KBfubhy
#5 1855402-5-do-not-test.patch4.77 KBfubhy
#3 1855402-3.patch25.82 KBfubhy
#3 1855402-3-do-not-test.patch5.54 KBfubhy
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

fubhy’s picture

Status: Active » Postponed
fubhy’s picture

Status: Postponed » Needs review
FileSize
5.54 KB
25.82 KB

Since that other issue is already RTBC I started writing a patch for this real quick.

We still need to do something about ConfigEntityBase::sort() if we agree on this approach:


/**
 * Helper callback for uasort() to sort configuration entities by weight and label.
 */
public static function sort($a, $b) {
  $a_weight = isset($a->weight) ? $a->weight : 0;
  $b_weight = isset($b->weight) ? $b->weight : 0;
  if ($a_weight == $b_weight) {
    $a_label = $a->label();
    $b_label = $b->label();
    return strnatcasecmp($a_label, $b_label);
  }
  return ($a_weight < $b_weight) ? -1 : 1;
}

fubhy’s picture

Just fixing the fails in the previous patch. This still needs test and we should also directly implement all this for all the places where we using weighting with config entities in core.

tim.plunkett’s picture

Awesome!

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -15,7 +17,29 @@
+   * Overrides EntityListController::__construct().
...
+   * Overrides EntityListController::load().

@@ -23,4 +47,119 @@ public function load() {
+   * Overrides EntityListController::buildHeader().
...
+   * Overrides EntityListController::buildRow().
...
+   * Overrides EntityListController::render().

Needs the full namespace \Drupal\Core\Entity\EntityListController

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -23,4 +47,119 @@ public function load() {
+    if (empty($this->weightKey)) {
...
+    if (empty($this->weightKey)) {

Each of these empty() checks feel like they should just be !$this->weightKey

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -23,4 +47,119 @@ public function load() {
+    if (empty($this->weightKey)) {
+      return parent::buildRow($entity);
+    }
+
+    $row['label']['#markup'] = $entity->label();
+    $row['id']['#markup'] = $entity->id();
+    $row['operations']['data'] = $this->buildOperations($entity);

This could just be

$row = parent::buildRow($entity);
if (empty($this->weightKey)) {
  return $row;
}
fubhy’s picture

Needs the full namespace \Drupal\Core\Entity\EntityListController

The latest iteration of http://drupal.org/coding-standards/docs#classes suggests we don't use full namespaces in cases like this!

Will get back to this issue once #80855: Add element #type table and merge tableselect/tabledrag into it has been commited.

fubhy’s picture

Status: Needs review » Needs work

#80855: Add element #type table and merge tableselect/tabledrag into it got committed. Will provide a new patch tomorrow!

tim.plunkett’s picture

Issue tags: +VDC, +Configurables

Tagging.

sun’s picture

Status: Needs work » Needs review
FileSize
8.38 KB

Let's see how it goes.

andypost’s picture

Title: Add generic weighting (tabledrag) support for config entities (EntityListController) » Add generic weighting (tabledrag) support for config entities (ConfigEntityListController)
FileSize
11.48 KB
5.36 KB

Fixed broken test by moving sorting support exactly to ConfigEntityListController
This shows that #theme(table) and #type(table) still different in render

andypost’s picture

Patch now green again so here doc-block fixes following current http://drupal.org/coding-standards/docs#classes

andypost’s picture

Status: Needs review » Needs work

There's a some suggestions to fix JS for tabledrag
#1869328-32: Restore simplicity of language list

andypost’s picture

Status: Needs work » Needs review

Does not mean to change a status

Not straightforward to do from the JS side: #1016056: Hide tabledrag handle and "Show row weights" when there is only one item in list

tstoeckler’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -2,12 +2,14 @@
- * Definition of Drupal\Core\Config\Entity\ConfigEntityListController.
+ * Definition of \Drupal\Core\Config\Entity\ConfigEntityListController.

+++ b/core/lib/Drupal/Core/Entity/EntityListController.php
@@ -2,7 +2,7 @@
- * Definition of Drupal\Core\Entity\EntityListController.
+ * Definition of \Drupal\Core\Entity\EntityListController.

If we're already changing this, it should be "Contains" instead of "Definition of"

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -23,4 +47,122 @@ public function load() {
+    drupal_set_message(t('Your settings have been saved.'));

To be consistent with system_config_form_submit() this should be 'The configuration options have been saved.'

Leaving at needs review, because those are minor, and since I think this is blocking stuff elsewhere, might be adjusted in a follow-up.

fubhy’s picture

FileSize
11.57 KB

Addressing #16.

sun’s picture

Status: Needs review » Reviewed & tested by the community

This looks awesome to me.

Let's move forward with this patch, and perform any possible improvements in follow-up issues.

Existing test coverage should be sufficient here, I think.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -2,12 +2,14 @@
- * Definition of Drupal\Core\Config\Entity\ConfigEntityListController.
+ * Contains \Drupal\Core\Config\Entity\ConfigEntityListController.

@@ -15,7 +17,29 @@
-   * Overrides Drupal\Core\Entity\EntityListController::load().
...
+   * Overrides EntityListController::__construct().
...
+   * Overrides EntityListController::load().

+++ b/core/lib/Drupal/Core/Entity/EntityListController.phpundefined
@@ -2,7 +2,7 @@
- * Definition of Drupal\Core\Entity\EntityListController.
+ * Contains \Drupal\Core\Entity\EntityListController.

@@ -15,7 +15,7 @@ class EntityListController implements EntityListControllerInterface {
-   * @var Drupal\Core\Entity\EntityStorageControllerInterface
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface

@@ -40,7 +40,7 @@ class EntityListController implements EntityListControllerInterface {
-   * @param Drupal\Core\Entity\EntityStorageControllerInterface $storage.
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage.

@@ -50,21 +50,21 @@ public function __construct($entity_type, EntityStorageControllerInterface $stor
-   * Implements Drupal\Core\Entity\EntityListControllerInterface::getStorageController().
+   * Implements EntityListControllerInterface::getStorageController().
...
-   * Implements Drupal\Core\Entity\EntityListControllerInterface::load().
+   * Implements EntityListControllerInterface::load().
...
-   * Implements Drupal\Core\Entity\EntityListControllerInterface::getOperations().
+   * Implements EntityListControllerInterface::getOperations().

@@ -89,7 +89,7 @@ public function getOperations(EntityInterface $entity) {
-   * @see Drupal\Core\Entity\EntityListController::render()
+   * @see \Drupal\Core\Entity\EntityListController::render()

@@ -101,32 +101,31 @@ public function buildHeader() {
-   * @param Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\EntityInterface $entity
...
-   * @see Drupal\Core\Entity\EntityListController::render()
+   * @see \Drupal\Core\Entity\EntityListController::render()

@@ -140,7 +139,7 @@ public function buildOperations(EntityInterface $entity) {
-   * Implements Drupal\Core\Entity\EntityListControllerInterface::render().
+   * Implements EntityListControllerInterface::render().

Absolutely none of this is in scope.

+++ b/core/modules/contact/lib/Drupal/contact/CategoryListController.phpundefined
@@ -41,10 +41,12 @@ public function getOperations(EntityInterface $entity) {
+    // @see http://drupal.org/node/1876718

@@ -52,11 +54,17 @@ public function buildHeader() {
+    // @see http://drupal.org/node/1876718

Can we have an actual comment here about what the array_slice is doing, beyond linking to that issue?

+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.phpundefined
@@ -28,7 +28,8 @@
+ *     "weight" = "weight"

This needs to be documented in EntityManager, under entity_keys

dawehner’s picture

Status: Needs work » Needs review
FileSize
6.05 KB
8.99 KB

Let's fix the stuff in the review.

One problem is that currently we just have a single entityManager so weight just for on config entities.

sun’s picture

Status: Needs review » Reviewed & tested by the community

Thanks! Let's get this in now.

With this, the need for #1876718: Allow modules to inject columns into tables more easily becomes more apparent. (already linked in the @todos)

Once filter formats are converted (#1779026: Convert Text Formats to Configuration System), we can leverage it there, which means that we can remove even more unnecessary custom code.

Dries’s picture

+++ b/core/modules/contact/lib/Drupal/contact/CategoryListController.phpundefined
@@ -52,11 +56,19 @@ public function buildHeader() {
   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);
+    $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(
+      '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);

This looks a bit messy to me. It's both slow (un-doing work) and pretty ugly to read. Is there a way we can improve this? Sounds like #1876718: Allow modules to inject columns into tables more easily could help with this, although I'm skeptical it would make it a lot easier.

andypost’s picture

tstoeckler’s picture

Re #22: Actually #1876718: Allow modules to inject columns into tables more easily will (almost certainly) allow to completely remove/revert that hunk entirely. What is done in addition to the array_splice()ing in that hunk is the usage of render elements (albeit #type => 'markup') instead of a pure string. That might (or might not) be retained, but that can be discussed independently.

sun’s picture

Yes, that's a known issue (hence the @todos). #1876718: Allow modules to inject columns into tables more easily will clean that up, and we're actively discussing the best solution over there already.

I'd prefer if we could move forward here and accept the array_slice() ugliness as a temporary thing only.

fubhy’s picture

Also, I would like point out that #1833716: WYSIWYG: Introduce "Text editors" as part of filter format configuration got committed with the same, temporary hack :)

andypost’s picture

Maybe better to store columns & operations separate? so we can inject weight column only when needed

andypost’s picture

Issue summary: View changes

Updated issue summary.

andypost’s picture

Another list that needs this added to summary #1891690: Use EntityListController for vocabularies

andypost’s picture

I think this issues should investigate most common needs for configurable entities, probably some code could be moved to EntityListController. We have a lot of implementations in Core that could be generalized also taking into account translatability of the entities.

Current implementations are Views, PictureMappingListController, Menus, Contact, vocabularies, imagestyles

For vocabularies, contact, filters, languages, layout-pages we need forms with weight.
For menus, shortcuts, image-styles - we use simple render-tables
For forums, menu-items, image-effects - sortable trees

For most of fieldable entities we need 2 menu-items + edit and delete operations

Also we need a way to remove Save button and table-weight behavior when there's only 1 item in list.
There's a lot of issues waiting for this and more menu-routers could be simplified

Related issues:
#1787942-41: Allow assigning layouts to pages
#599770-15: Clean up the contact forms listing UI: Allow to set the default category and weights on the listing page
#1809376-15: Use EntityListController for image styles
#1783964-7: Allow entity types to provide menu items
#1891690: Use EntityListController for vocabularies
#1798540: Add link to add a new entity in an empty entity list controller table
#1803586: Give all entities their own URI
#1479454: Convert user roles to configurables

andypost’s picture

Issue summary: View changes

Updated issue summary.

andypost’s picture

Issue summary: View changes

Updated

andypost’s picture

Issue summary: View changes

Updated issue summary.

andypost’s picture

Issue summary: View changes

Updated issue summary.

tim.plunkett’s picture

Issue tags: +D8MI

I don't see the reason to do anything more in this issue. Furthermore, we'd ideally not use EntityListController for non-config, using Views. See #1864980: [meta] Figure out how to integrate Views into core

andypost’s picture

Status: Reviewed & tested by the community » Needs work
FileSize
9.49 KB
9.08 KB

This patch changes a contact listing by adding a machine name which seems wrong. see screens attached

Before
before.png

After
after.png

EDITED
Also I tried to implement controller for vocabularies and it works #1891690-5: Use EntityListController for vocabularies

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -23,4 +47,122 @@ public function load() {
+      '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')),

Why not use #type=weight for weight field?

andypost’s picture

Status: Needs work » Needs review
FileSize
23.88 KB

Patch limits the scope of "column-injection" to buildHeader() only by adding sorting for each row by table header so no matter in order the buildRow() adds additional data.

The weight field added at the end of table because mostly they hidded in UI.

This affects all configurables, vocabulary part could be out of scope but issue should care all cases.

Let's see how many tests is broken by this.

andypost’s picture

Fix broken tests

sun’s picture

Status: Needs review » Needs work

Can we please go back to #20 and get that in first?

Regarding the "regression" of an additional machine name being output in the contact category listing (as depicted in #31), IMHO that's perfectly acceptable for now. I think we want to re-evaluate what the default listing for config entities should contain and look like either way. Quite potentially, we actually want to output the machine name (but not necessarily in an own column).

sun’s picture

Status: Needs work » Needs review

#20: drupal-1855402-20.patch queued for re-testing.

dawehner’s picture

FileSize
24.14 KB

So first here is an interdiff from 34 to 20. Thanks andypost for improving the entity list controllers in all that places.

The changes for vocabularies seems to be fine, but maybe out of scope, as we can improve the actual instances later?
The same can be said for pieces like the menu list controller etc.
Let the patch size be as small as possible, and iterate on it. That's the proper way to get progress at the end.

I don't see the point why adding the machine name in the tabledrag issue makes any sense.

andypost’s picture

Issue tags: +Usability, +mobile, +D8MI
FileSize
28.82 KB
15 KB
10.25 KB
13.54 KB
18.4 KB
14.82 KB

So ok, I moved mack machine name but into responsive column and no taxonomy now to minimize a patch

Patch adds back #20 way to insert columns with @todo for follow-up
Also conversion columns to render is the same for label and id because we need a way to play easily with css rules like Menu does.

Also let's get reviews from usability and mobile team!

contact1.png

contact2.png

menu1.png

menu2.png

PS: there's no reason to rush this patch, we need a generic implementation

sun’s picture

Issue tags: -D8MI

@andypost:
I've the impression you're mistaking this issue as the issue that wants to introduce a super-generic/catch-all ConfigEntityListController implementation for all config entities... but that's not the case. We're trying to focus on the tabledrag/weighting aspect only. (As the issue title states.) Evaluating what the generic default implementation for config entity listings should be is left for a separate issue. The original patch in #20 does not affect any config entity listings, unless they specifically opt-in for the weight/tabledrag handling. That's really all we want to achieve in this issue.

andypost’s picture

Issue tags: -Usability, -mobile, -D8MI

@sun #39 is the same as #20 but fixes regressions in UI and tests.

And the main difference that better to remove array_slice() code from render() see code below. Custom List controllers are just add own columns in buildRow() but the order is defined in header so this change should improve DX.

Changes in default controller are affects current implementations so patch must fix this regressions.

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -23,4 +47,140 @@ public function load() {
+    // 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);

this

+++ b/core/lib/Drupal/Core/Entity/EntityListController.php
@@ -153,8 +152,12 @@ public function render() {
+    // 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);
     }

this

Bojhan’s picture

I dont get what you want me to review

andypost’s picture

@Bojhan the UI changes in #31 #39 for contact category listing. Menus in #39 gets new Machine name column. Is there any suggestions for machine names?

andypost’s picture

#39: 1855402-config-sort-39.patch queued for re-testing.

EDIT can't reproduce translation test failure locally

Bojhan’s picture

I see no reason to have a machine name column, I'm sure there are loads of usecases that you would need them - I do not see a 80% usecase. I would side against them for now, because 1) its relatively easy to have them show up, through a custom view, or just adjusting this listing through a module, 2) you can get the machine name, if you click edit, 3) the consistent pattern we employ in core currently and received no negative feedback is to edit the thing to see the machine name (albeit not on content types, but the Field UI is a one off).

Bojhan’s picture

Issue summary: View changes

Updated issue summary.

andypost’s picture

Should be postpone this for #1876718: Allow modules to inject columns into tables more easily ?

Or implement a generic and use it for a all form-tables like #1876712: [meta] Convert all tables in core to new #type 'table'

andypost’s picture

patch without machine names

Contact categories
contact.png

Menus
menus.png

gdd’s picture

Andypost asked me to weigh in on the machine name issue and I pretty much agree with everything Bojhan said.

andypost’s picture

Fixed broken test

andypost’s picture

andypost’s picture

andypost’s picture

I think EntityFormController is not related to ListController so any reviews are welcome because other issues depends on this one

andypost’s picture

Build on top of FormInterface like BlockListController does

andypost’s picture

FileSize
20.55 KB
665 bytes

Remove copy/paste error

andypost’s picture

Issue tags: -VDC, -Configurables

#56: 1855402-config-sort-56.patch queued for re-testing.

andypost’s picture

Issue tags: +VDC, +Configurables

#56: 1855402-config-sort-56.patch queued for re-testing.

andypost’s picture

Current doc blocks

andypost’s picture

FileSize
19.66 KB

Re-roll

tim.plunkett’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -9,14 +9,38 @@
+  protected $weightKey;
...
+    else {
+      $this->weightKey = FALSE;

You could just start with protected $weightKey = FALSE; and skip the else

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -51,4 +75,133 @@ public function getOperations(EntityInterface $entity) {
+    $entities = entity_load_multiple($this->entityType, array_keys($values));

Should switch to

$entities = \Drupal::service('plugin.manager.entity')->getStorageController($this->entitytType)->load(array_keys($values));
to make it easier to inject later.

+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.phpundefined
@@ -79,21 +79,24 @@ function testList() {
+    $entity_info = entity_get_info('config_test');

@@ -119,24 +122,25 @@ function testListUI() {
+    $entity_info = entity_get_info('config_test');

$entity_info = $this->container->get('plugin.entity.manager')->getDefinition('config_test');

andypost’s picture

Here it is

andypost’s picture

FileSize
19.76 KB
1001 bytes

Default value moved out of constructor

andypost’s picture

copy/paste error :(

andypost’s picture

#66: 1855402-sort-config-66.patch queued for re-testing.

andypost’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.phpundefined
@@ -51,4 +72,133 @@ public function getOperations(EntityInterface $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']),
+      ));

Also currently in core we provide links only to "Content Entities"

+++ b/core/lib/Drupal/Core/Entity/EntityListController.phpundefined
@@ -153,8 +152,12 @@ public function render() {
+    // 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);

Is strict but #1876718-7: Allow modules to inject columns into tables more easily seems better

+++ b/core/modules/contact/lib/Drupal/contact/CategoryListController.phpundefined
@@ -38,26 +38,33 @@ public function getOperations(EntityInterface $entity) {
+    // @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);

still depends on it

jibran’s picture

Issue tags: -VDC, -Configurables

#66: 1855402-sort-config-66.patch queued for re-testing.

andypost’s picture

Issue tags: +VDC, +Configurables
FileSize
19.86 KB

re-roll

andypost’s picture

andypost’s picture

Status: Needs work » Needs review
FileSize
19.01 KB

reroll

andypost’s picture

Fix tests

andypost’s picture

FileSize
26.61 KB

patch

andypost’s picture

Remove debug

andypost’s picture

Render needed to alter properly

andypost’s picture

Fix broken tests

andypost’s picture

FileSize
28.16 KB
775 bytes

Fix taxonomy test

andypost’s picture

Issue tags: -VDC, -Configurables

#84: 1855402-sort-config-84.patch queued for re-testing.

andypost’s picture

Issue tags: +VDC, +Configurables
FileSize
27.51 KB

merge HEAD

tim.plunkett’s picture

@@ -60,4 +82,124 @@ public function getOperations(EntityInterface $entity) {
+    // @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);

I still don't see how this is reasonable. If EntityListController changes its header, we'll have plenty of other problems. As a base class, why not just dictate the header and row data?

@@ -60,4 +82,124 @@ public function getOperations(EntityInterface $entity) {
+      '#markup' => check_plain($row['label']),

Double check_plain'd now.

@@ -60,4 +82,124 @@ public function getOperations(EntityInterface $entity) {
+    return drupal_get_form($this);

Are we sure we want to go this direction, and not just have a separate base class? This certainly convolutes this class.

@@ -44,33 +45,33 @@ public function getOperations(EntityInterface $entity) {
+    // @todo Simplify this http://drupal.org/node/1876718
+    return array_slice($row, 0, 1, TRUE) + array(
+      'recipients' => t('Recipients'),
+      'selected' => t('Selected'),
+    ) + array_slice($row, 0, NULL, TRUE);

This seems more reasonable, since we *don't* want to overwrite the parent.

andypost’s picture

FileSize
27.5 KB
665 bytes

Fixed double escape after

@tim.plunkett I've no idea how to implement this in base class, also please point the files you are referring in dreditor

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListController.php
@@ -9,14 +9,36 @@
+    // Check if the entity type supports weighting.
+    if (!empty($this->entityInfo['entity_keys']['weight'])) {

The change applies to config entities only, so this is a base class that dictates behavior

tim.plunkett’s picture

Not all config entities need tabledrag. And some content entities do.

The 1st and 3rd hunks are both in ConfigEntityListController, the 2nd you fixed and the 4th was an observation.

I think it should be a separate class, WeightedConfigEntityListController or something.

tim.plunkett’s picture

tim.plunkett’s picture

Assigned: fubhy » tim.plunkett

Working on this.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
25.66 KB

Since #2064557: Improve strange coupling in EntityListControllers by improving buildRow() and buildHeader() went in, this didn't apply at all.

I also took a different approach, in order to keep ConfigEntityListController as is.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs review

Oops!

Also, I'm not expecting this to pass the first time, I'll update for any failures.

tim.plunkett’s picture

+++ b/core/modules/editor/editor.module
@@ -156,19 +156,19 @@ function editor_menu() {
-  foreach (element_children($form['formats']) as $format_id) {
+  foreach (element_children($form['entities']) as $format_id) {

+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php
@@ -90,7 +90,7 @@ function testTaxonomyAdminChangingWeights() {
-      $edit['vocabularies[' . $key . '][weight]'] = $weight;
+      $edit['entities[' . $key . '][weight]'] = $weight;

If we're really concerned about backward compatibility, I can introduce a key like DefaultPluginBag::$pluginKey has, something like DraggableListController::$entitiesKey, set the default to 'entities', and let these two override it...

tim.plunkett’s picture

FileSize
23.08 KB
7.77 KB

Yep, #95 works nicely and should resolve most if not all of those failures.

andypost’s picture

Looks awesome!

--- a/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
+++ b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php

@@ -91,15 +98,12 @@ public function buildForm(array $form, array &$form_state) {
-    // Save header's order of columns for sorting data-rows.
-    $header_sort = array_flip(array_keys($form['entities']['#header']));
...
-      // Sort row columns by header's order.
-      $form['entities'][$entity->id()] = array_merge($header_sort, $row);

This needs follow-up at least to display only columns that have header (allowing to re-sort table columns by weight too)

tim.plunkett’s picture

FileSize
2.52 KB
23.25 KB

I only tested the language list without locale on, whoops.

Not sure what you mean in #98, that seems out of scope.

andypost’s picture

Status: Needs review » Reviewed & tested by the community

Yep, follow-up in #1876718: Allow modules to inject columns into tables more easily
I found no nitpicks with code

tstoeckler’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/DraggableListController.php
@@ -0,0 +1,147 @@
+    // Check if the entity type supports weighting.
...
+      $this->weightKey = $this->entityInfo['entity_keys']['weight'];
+    }
...
+      if (isset($this->entities[$id]) && $this->entities[$id]->get($this->weightKey) != $value['weight']) {
...
+        $this->entities[$id]->set($this->weightKey, $value['weight']);

Couldn't we just document that entities using this need a weight key? I like the split classes but it's sad to have all those checks in there, as without a weight key the whole class is pointless.

Also it seems $this->weightKey is already used unconditionally in submitForm() so...

Since this has been sitting for so long, I'm not downgrading this. I'm fine with doing this as a follow-up. I couldn't find anything substantially wrong with this patch and my proposal would merely be a minor code clean-up not actual technical debt in any sense of the word.

tim.plunkett’s picture

We can't and shouldn't assume that the key will be called weight, it could be order or position.
Also, submitForm() is only called if the weightKey is set during render(), hence the lack of check for it.

tstoeckler’s picture

Ahh, I totally missed render(). Yes, that makes sense.

But I still think we should just remove the checks. No, we can't enforce that weightKey == 'weight', but IMO we can enforce that !empty(weightKey) == TRUE. Then we just set $this->weightKey in __construct() and be done with it.

tim.plunkett’s picture

And what if weightKey isn't set? Random notices? A thrown exception (of what type)?

tim.plunkett’s picture

Also, VocabularyListController has the pre-existing functionality of NOT being a draggable form when there is only one vocab.
It relies on these weightKey checks to function.

tstoeckler’s picture

Re #105: Since the DraggableListController is explicitly opt-in I think a "Undefined index 'weightKey'" would actually be quite helpful.

Re #106: I think that should be handled generically in #1016056: Hide tabledrag handle and "Show row weights" when there is only one item in list

But apparently my suggestion is more controversial than I had anticipated so I opened a follow-up issue. Let's discuss there and finally get this one in:
#2070621: Make the existence of a weight key mandatory in DraggableListController

tim.plunkett’s picture

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 9e01ce3 and pushed to 8.x. Thanks!

Berdir’s picture

Title: Add generic weighting (tabledrag) support for config entities (ConfigEntityListController) » Change notice: Add generic weighting (tabledrag) support for config entities (ConfigEntityListController)
Priority: Normal » Critical
Status: Fixed » Active
Issue tags: +Needs change record

This should probably get a change notice? I can use this in contrib modules and others probably too.

Using $this->entities seems to be pretty weird here? That means we serialize all of them and then save the version that was previously serialized, so that could be an older version that was already changed in the meantime. Wouldn't be hard to just load them again? This was introduced in #100, without further discussion on it... I see that it's also used in one other method, that probably makes sense there, but not so sure about the submit method.

andypost’s picture

About $entities in submit - each time when submit should be called the build happens, there's no serialization
By the same time 100 introduces

+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -81,6 +81,7 @@ public function buildRow(EntityInterface $entity) {
+    $form[$this->entitiesKey]['#languages'] = $this->entities;

so we have 2 lists of languages and probably other types affected.

PS: seems there's a room for follow-up to clean-up things for better contrib DX ("entities" stored in form array could be changed via form alter so we need a competent list of entities)

andypost’s picture

andypost’s picture

Taxonomy page should be updated when there's only one vocabulary to hide weight header column

catch’s picture

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned
Status: Active » Needs review
Berdir’s picture

Title: Change notice: Add generic weighting (tabledrag) support for config entities (ConfigEntityListController) » Add generic weighting (tabledrag) support for config entities (DraggableListController)
Priority: Major » Normal
Status: Needs review » Fixed
Issue tags: -Needs change record

Looks fine, added a bit example code as an entity type always needs a custom subclass. Also changing the issue title to contain the name of the class that was added.

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.