summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Reeves2016-08-29 13:41:31 (GMT)
committerScott Reeves2016-08-29 13:41:31 (GMT)
commit3f8aa20c32be29c92ad575eaae246d69b48b4a54 (patch)
tree6588612e8005fc4aef0a0ab436448fbe2d24302b
parentcea3af0f9441266a5490b8d754435a4488a3eb62 (diff)
Issue #2574767 by dipakmdhrm, Manjit.Singh, malavya, Dom., yoroy, xjm, tkoleary, Gábor Hojtsy, dawehner, Cottser, eelkeblok, Bojhan, fgm, DuaelFr: Views listing page displays too few items on a page
-rw-r--r--core/modules/views_ui/css/views_ui.admin.theme.css29
-rw-r--r--core/modules/views_ui/src/Tests/DefaultViewsTest.php5
-rw-r--r--core/modules/views_ui/src/Tests/XssTest.php3
-rw-r--r--core/modules/views_ui/src/ViewListBuilder.php124
-rw-r--r--core/modules/views_ui/templates/views-ui-view-displays-list.html.twig24
-rw-r--r--core/modules/views_ui/templates/views-ui-views-listing-table.html.twig49
-rw-r--r--core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php29
-rw-r--r--core/modules/views_ui/views_ui.module15
-rw-r--r--core/modules/views_ui/views_ui.theme.inc26
-rw-r--r--core/themes/stable/css/views_ui/views_ui.admin.theme.css29
-rw-r--r--core/themes/stable/templates/admin/views-ui-view-displays-list.html.twig22
-rw-r--r--core/themes/stable/templates/admin/views-ui-views-listing-table.html.twig47
12 files changed, 303 insertions, 99 deletions
diff --git a/core/modules/views_ui/css/views_ui.admin.theme.css b/core/modules/views_ui/css/views_ui.admin.theme.css
index 4ab55c4..460e75e 100644
--- a/core/modules/views_ui/css/views_ui.admin.theme.css
+++ b/core/modules/views_ui/css/views_ui.admin.theme.css
@@ -170,9 +170,9 @@ details.box-padding {
margin-bottom: 6px;
margin-top: 6px;
}
-.views-ui-view-title {
+.views-ui-view-name h3 {
font-weight: bold;
- margin-top: 0;
+ margin: 0.25em 0;
}
.view-changed {
margin-bottom: 21px;
@@ -183,22 +183,33 @@ details.box-padding {
margin-bottom: 0;
margin-top: 18px;
}
+.views-ui-view-displays ul {
+ margin-left: 0; /* LTR */
+ padding-left: 0; /* LTR */
+ list-style: none;
+}
+[dir="rtl"] .views-ui-view-displays ul {
+ margin-right: 0;
+ padding-right: 0;
+ margin-left: inherit;
+ padding-left: inherit;
+}
/* These header classes are ambiguous and should be scoped to th elements */
.views-ui-name {
- width: 18%;
+ width: 20%;
}
.views-ui-description {
- width: 26%;
+ width: 30%;
}
-.views-ui-tag {
- width: 8%;
+.views-ui-machine-name {
+ width: 15%;
}
-.views-ui-path {
- width: auto;
+.views-ui-displays {
+ width: 25%;
}
.views-ui-operations {
- width: 24%;
+ width: 10%;
}
/**
diff --git a/core/modules/views_ui/src/Tests/DefaultViewsTest.php b/core/modules/views_ui/src/Tests/DefaultViewsTest.php
index 3b3f2a7..08fcae8 100644
--- a/core/modules/views_ui/src/Tests/DefaultViewsTest.php
+++ b/core/modules/views_ui/src/Tests/DefaultViewsTest.php
@@ -161,10 +161,11 @@ class DefaultViewsTest extends UITestBase {
*/
function testSplitListing() {
// Build a re-usable xpath query.
- $xpath = '//div[@id="views-entity-list"]/div[@class = :status]/table//tr[@title = :title]';
+ $xpath = '//div[@id="views-entity-list"]/div[@class = :status]/table//td/text()[contains(., :title)]';
+
$arguments = array(
':status' => 'views-list-section enabled',
- ':title' => t('Machine name: test_view_status'),
+ ':title' => 'test_view_status',
);
$this->drupalGet('admin/structure/views');
diff --git a/core/modules/views_ui/src/Tests/XssTest.php b/core/modules/views_ui/src/Tests/XssTest.php
index 8eac3a6..1d61f19 100644
--- a/core/modules/views_ui/src/Tests/XssTest.php
+++ b/core/modules/views_ui/src/Tests/XssTest.php
@@ -17,9 +17,6 @@ class XssTest extends UITestBase {
public static $modules = array('node', 'user', 'views_ui', 'views_ui_test');
public function testViewsUi() {
- $this->drupalGet('admin/structure/views');
- $this->assertEscaped('<script>alert("foo");</script>, <marquee>test</marquee>', 'The view tag is properly escaped.');
-
$this->drupalGet('admin/structure/views/view/sa_contrib_2013_035');
$this->assertEscaped('<marquee>test</marquee>', 'Field admin label is properly escaped.');
diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php
index 112e1c4..b38a15f 100644
--- a/core/modules/views_ui/src/ViewListBuilder.php
+++ b/core/modules/views_ui/src/ViewListBuilder.php
@@ -89,34 +89,30 @@ class ViewListBuilder extends ConfigEntityListBuilder {
'data' => array(
'view_name' => array(
'data' => array(
- '#theme' => 'views_ui_view_info',
- '#view' => $view,
- '#displays' => $this->getDisplaysList($view)
+ '#plain_text' => $view->label(),
),
),
- 'description' => array(
+ 'machine_name' => array(
'data' => array(
- '#plain_text' => $view->get('description'),
+ '#plain_text' => $view->id(),
),
- 'data-drupal-selector' => 'views-table-filter-text-source',
),
- 'tag' => array(
+ 'description' => array(
'data' => array(
- '#plain_text' => $view->get('tag'),
+ '#plain_text' => $view->get('description'),
),
- 'data-drupal-selector' => 'views-table-filter-text-source',
),
- 'path' => array(
+ 'displays' => array(
'data' => array(
- '#theme' => 'item_list',
- '#items' => $this->getDisplayPaths($view),
- '#context' => ['list_style' => 'comma-list'],
+ '#theme' => 'views_ui_view_displays_list',
+ '#displays' => $this->getDisplaysList($view),
),
),
'operations' => $row['operations'],
),
- 'title' => $this->t('Machine name: @name', array('@name' => $view->id())),
- 'class' => array($view->status() ? 'views-ui-list-enabled' : 'views-ui-list-disabled'),
+ '#attributes' => array(
+ 'class' => array($view->status() ? 'views-ui-list-enabled' : 'views-ui-list-disabled'),
+ ),
);
}
@@ -127,23 +123,33 @@ class ViewListBuilder extends ConfigEntityListBuilder {
return array(
'view_name' => array(
'data' => $this->t('View name'),
- 'class' => array('views-ui-name'),
+ '#attributes' => array(
+ 'class' => array('views-ui-name'),
+ ),
+ ),
+ 'machine_name' => array(
+ 'data' => $this->t('Machine name'),
+ '#attributes' => array(
+ 'class' => array('views-ui-machine-name'),
+ ),
),
'description' => array(
'data' => $this->t('Description'),
- 'class' => array('views-ui-description'),
- ),
- 'tag' => array(
- 'data' => $this->t('Tag'),
- 'class' => array('views-ui-tag'),
+ '#attributes' => array(
+ 'class' => array('views-ui-description'),
+ ),
),
- 'path' => array(
- 'data' => $this->t('Path'),
- 'class' => array('views-ui-path'),
+ 'displays' => array(
+ 'data' => $this->t('Displays'),
+ '#attributes' => array(
+ 'class' => array('views-ui-displays'),
+ ),
),
'operations' => array(
'data' => $this->t('Operations'),
- 'class' => array('views-ui-operations'),
+ '#attributes' => array(
+ 'class' => array('views-ui-operations'),
+ ),
),
);
}
@@ -196,13 +202,13 @@ class ViewListBuilder extends ConfigEntityListBuilder {
'#type' => 'search',
'#title' => $this->t('Filter'),
'#title_display' => 'invisible',
- '#size' => 40,
- '#placeholder' => $this->t('Filter by view name or description'),
+ '#size' => 60,
+ '#placeholder' => $this->t('Filter by view name, machine name, description, or display path'),
'#attributes' => array(
'class' => array('views-filter-text'),
'data-table' => '.views-listing-table',
'autocomplete' => 'off',
- 'title' => $this->t('Enter a part of the view name or description to filter by.'),
+ 'title' => $this->t('Enter a part of the view name, machine name, description, or display path to filter by.'),
),
);
@@ -212,12 +218,9 @@ class ViewListBuilder extends ConfigEntityListBuilder {
$list[$status]['#type'] = 'container';
$list[$status]['#attributes'] = array('class' => array('views-list-section', $status));
$list[$status]['table'] = array(
- '#type' => 'table',
- '#attributes' => array(
- 'class' => array('views-listing-table'),
- ),
- '#header' => $this->buildHeader(),
- '#rows' => array(),
+ '#theme' => 'views_ui_views_listing_table',
+ '#headers' => $this->buildHeader(),
+ '#attributes' => array('class' => array('views-listing-table', $status)),
);
foreach ($entities[$status] as $entity) {
$list[$status]['table']['#rows'][$entity->id()] = $this->buildRow($entity);
@@ -242,46 +245,33 @@ class ViewListBuilder extends ConfigEntityListBuilder {
*/
protected function getDisplaysList(EntityInterface $view) {
$displays = array();
- foreach ($view->get('display') as $display) {
- $definition = $this->displayManager->getDefinition($display['display_plugin']);
- if (!empty($definition['admin'])) {
- // Cast the admin label to a string since it is an object.
- // @see \Drupal\Core\StringTranslation\TranslatableMarkup
- $displays[] = (string) $definition['admin'];
- }
- }
-
- sort($displays);
- return $displays;
- }
- /**
- * Gets a list of paths assigned to the view.
- *
- * @param \Drupal\Core\Entity\EntityInterface $view
- * The view entity.
- *
- * @return array
- * An array of paths for this view.
- */
- protected function getDisplayPaths(EntityInterface $view) {
- $all_paths = array();
$executable = $view->getExecutable();
$executable->initDisplay();
foreach ($executable->displayHandlers as $display) {
- if ($display->hasPath()) {
- $path = $display->getPath();
- if ($view->status() && strpos($path, '%') === FALSE) {
- // @todo Views should expect and store a leading /. See:
- // https://www.drupal.org/node/2423913
- $all_paths[] = \Drupal::l('/' . $path, Url::fromUserInput('/' . $path));
- }
- else {
- $all_paths[] = '/' . $path;
+ $rendered_path = FALSE;
+ $definition = $display->getPluginDefinition();
+ if (!empty($definition['admin'])) {
+ if ($display->hasPath()) {
+ $path = $display->getPath();
+ if ($view->status() && strpos($path, '%') === FALSE) {
+ // @todo Views should expect and store a leading /. See:
+ // https://www.drupal.org/node/2423913
+ $rendered_path = \Drupal::l('/' . $path, Url::fromUserInput('/' . $path));
+ }
+ else {
+ $rendered_path = '/' . $path;
+ }
}
+ $displays[] = array(
+ 'display' => $definition['admin'],
+ 'path' => $rendered_path,
+ );
}
}
- return array_unique($all_paths);
+
+ sort($displays);
+ return $displays;
}
}
diff --git a/core/modules/views_ui/templates/views-ui-view-displays-list.html.twig b/core/modules/views_ui/templates/views-ui-view-displays-list.html.twig
new file mode 100644
index 0000000..1c7d621
--- /dev/null
+++ b/core/modules/views_ui/templates/views-ui-view-displays-list.html.twig
@@ -0,0 +1,24 @@
+{#
+/**
+ * @file
+ * Default theme implementation for views displays on the views listing page.
+ *
+ * Available variables:
+ * - displays: Contains multiple display instances. Each display contains:
+ * - display: Display name.
+ * - path: Path to display, if any.
+ *
+ * @ingroup themeable
+ */
+#}
+<ul>
+ {% for display in displays %}
+ <li>
+ {% if display.path %}
+ {{ display.display }} <span data-drupal-selector="views-table-filter-text-source">({{ display.path }})</span>
+ {% else %}
+ {{ display.display }}
+ {% endif %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/core/modules/views_ui/templates/views-ui-views-listing-table.html.twig b/core/modules/views_ui/templates/views-ui-views-listing-table.html.twig
new file mode 100644
index 0000000..207462e
--- /dev/null
+++ b/core/modules/views_ui/templates/views-ui-views-listing-table.html.twig
@@ -0,0 +1,49 @@
+{#
+/**
+ * @file
+ * Default theme implementation for views listing table.
+ *
+ * Available variables:
+ * - headers: Contains table headers.
+ * - rows: Contains multiple rows. Each row contains:
+ * - view_name: The human-readable name of the view.
+ * - machine_name: Machine name of the view.
+ * - description: The description of the view.
+ * - displays: List of displays attached to the view.
+ * - operations: List of available operations.
+ *
+ * @see template_preprocess_views_ui_views_listing_table()
+ *
+ * @ingroup themeable
+ */
+#}
+<table{{ attributes.addClass('responsive-enabled') }}>
+ <thead>
+ <tr>
+ {% for header in headers %}
+ <th{{ header.attributes }}>{{ header.data }}</th>
+ {% endfor %}
+ </tr>
+ </thead>
+ <tbody>
+ {% for row in rows %}
+ <tr{{ row.attributes }}>
+ <td class="views-ui-view-name">
+ <h3 data-drupal-selector="views-table-filter-text-source">{{ row.data.view_name.data }}</h3>
+ </td>
+ <td class="views-ui-view-machine-name" data-drupal-selector="views-table-filter-text-source">
+ {{ row.data.machine_name.data }}
+ </td>
+ <td class="views-ui-view-description" data-drupal-selector="views-table-filter-text-source">
+ {{ row.data.description.data }}
+ </td>
+ <td class="views-ui-view-displays">
+ {{ row.data.displays.data }}
+ </td>
+ <td class="views-ui-view-operations">
+ {{ row.data.operations.data }}
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
diff --git a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php
index 215728c..9b64696 100644
--- a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php
+++ b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php
@@ -152,21 +152,34 @@ class ViewListBuilderTest extends UnitTestCase {
$view_list_builder = new TestViewListBuilder($entity_type, $storage, $display_manager);
$view_list_builder->setStringTranslation($this->getStringTranslationStub());
+ // Create new view with test values.
$view = new View($values, 'view');
+ // Get the row object created by ViewListBuilder for this test view.
$row = $view_list_builder->buildRow($view);
+ // Expected output array for view's displays.
$expected_displays = array(
- 'Embed admin label',
- 'Page admin label',
- 'Page admin label',
- 'Page admin label',
+ '0' => array(
+ 'display' => 'Embed admin label',
+ 'path' => FALSE,
+ ),
+ '1' => array(
+ 'display' => 'Page admin label',
+ 'path' => '/<object>malformed_path</object>',
+ ),
+ '2' => array(
+ 'display' => 'Page admin label',
+ 'path' => '/<script>alert("placeholder_page/%")</script>',
+ ),
+ '3' => array(
+ 'display' => 'Page admin label',
+ 'path' => '/test_page',
+ ),
);
- $this->assertEquals($expected_displays, $row['data']['view_name']['data']['#displays']);
- $display_paths = $row['data']['path']['data']['#items'];
- // These values will be escaped by Twig when rendered.
- $this->assertEquals('/test_page, /<object>malformed_path</object>, /<script>alert("placeholder_page/%")</script>', implode(', ', $display_paths));
+ // Compare the expected and generated output.
+ $this->assertEquals($expected_displays, $row['data']['displays']['data']['#displays']);
}
}
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 4976368..165e866 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -80,12 +80,25 @@ function views_ui_theme() {
'file' => 'views_ui.theme.inc',
),
- // list views
+ // Legacy theme hook for displaying views info.
'views_ui_view_info' => array(
'variables' => array('view' => NULL, 'displays' => NULL),
'file' => 'views_ui.theme.inc',
),
+ // List views.
+ 'views_ui_views_listing_table' => array(
+ 'variables' => array(
+ 'headers' => NULL,
+ 'rows' => NULL,
+ 'attributes' => array(),
+ ),
+ 'file' => 'views_ui.theme.inc',
+ ),
+ 'views_ui_view_displays_list' => array(
+ 'variables' => array('displays' => array()),
+ ),
+
// Group of filters.
'views_ui_build_group_filter_form' => array(
'render element' => 'form',
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index 3b58678..926f82a 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -11,6 +11,7 @@ use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\Checkboxes;
use Drupal\Core\Render\Element\Radios;
use Drupal\Core\Url;
+use Drupal\Core\Template\Attribute;
/**
* Prepares variables for Views UI display tab setting templates.
@@ -43,6 +44,31 @@ function template_preprocess_views_ui_display_tab_setting(&$variables) {
}
/**
+ * Prepares variables for Views UI view listing templates.
+ *
+ * Default template: views-ui-view-listing-table.html.twig.
+ *
+ * @param array $variables
+ * An associative array containing:
+ * - headers: An associative array containing the headers for the view
+ * listing table.
+ * - rows: An associative array containing the rows data for the view
+ * listing table.
+ */
+function template_preprocess_views_ui_views_listing_table(&$variables) {
+ // Convert the attributes to valid attribute objects.
+ foreach ($variables['headers'] as $key => $header) {
+ $variables['headers'][$key]['attributes'] = new Attribute($header['#attributes']);
+ }
+
+ if (!empty($variables['rows'])) {
+ foreach ($variables['rows'] as $key => $row) {
+ $variables['rows'][$key]['attributes'] = new Attribute($row['#attributes']);
+ }
+ }
+}
+
+/**
* Prepares variables for Views UI display tab bucket templates.
*
* Default template: views-ui-display-tab-bucket.html.twig.
diff --git a/core/themes/stable/css/views_ui/views_ui.admin.theme.css b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
index 3bf3290..3a2afb5 100644
--- a/core/themes/stable/css/views_ui/views_ui.admin.theme.css
+++ b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
@@ -170,9 +170,9 @@ details.box-padding {
margin-bottom: 6px;
margin-top: 6px;
}
-.views-ui-view-title {
+.views-ui-view-name h3 {
font-weight: bold;
- margin-top: 0;
+ margin: 0.25em 0;
}
.view-changed {
margin-bottom: 21px;
@@ -183,22 +183,33 @@ details.box-padding {
margin-bottom: 0;
margin-top: 18px;
}
+.views-ui-view-displays ul {
+ margin-left: 0; /* LTR */
+ padding-left: 0; /* LTR */
+ list-style: none;
+}
+[dir="rtl"] .views-ui-view-displays ul {
+ margin-right: 0;
+ padding-right: 0;
+ margin-left: inherit;
+ padding-left: inherit;
+}
/* These header classes are ambiguous and should be scoped to th elements */
.views-ui-name {
- width: 18%;
+ width: 20%;
}
.views-ui-description {
- width: 26%;
+ width: 30%;
}
-.views-ui-tag {
- width: 8%;
+.views-ui-machine-name {
+ width: 15%;
}
-.views-ui-path {
- width: auto;
+.views-ui-displays {
+ width: 25%;
}
.views-ui-operations {
- width: 24%;
+ width: 10%;
}
/**
diff --git a/core/themes/stable/templates/admin/views-ui-view-displays-list.html.twig b/core/themes/stable/templates/admin/views-ui-view-displays-list.html.twig
new file mode 100644
index 0000000..e6e5b02
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-view-displays-list.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Theme override for views displays on the views listing page.
+ *
+ * Available variables:
+ * - displays: Contains multiple display instances. Each display contains:
+ * - display: Display name.
+ * - path: Path to display, if any.
+ */
+#}
+<ul>
+ {% for display in displays %}
+ <li>
+ {% if display.path %}
+ {{ display.display }} <span data-drupal-selector="views-table-filter-text-source">({{ display.path }})</span>
+ {% else %}
+ {{ display.display }}
+ {% endif %}
+ </li>
+ {% endfor %}
+</ul>
diff --git a/core/themes/stable/templates/admin/views-ui-views-listing-table.html.twig b/core/themes/stable/templates/admin/views-ui-views-listing-table.html.twig
new file mode 100644
index 0000000..bc12a0a
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-views-listing-table.html.twig
@@ -0,0 +1,47 @@
+{#
+/**
+ * @file
+ * Theme override for views listing table.
+ *
+ * Available variables:
+ * - headers: Contains table headers.
+ * - rows: Contains multiple rows. Each row contains:
+ * - view_name: The human-readable name of the view.
+ * - machine_name: Machine name of the view.
+ * - description: The description of the view.
+ * - displays: List of displays attached to the view.
+ * - operations: List of available operations.
+ *
+ * @see template_preprocess_views_ui_views_listing_table()
+ */
+#}
+<table{{ attributes.addClass('responsive-enabled') }}>
+ <thead>
+ <tr>
+ {% for header in headers %}
+ <th{{ header.attributes }}>{{ header.data }}</th>
+ {% endfor %}
+ </tr>
+ </thead>
+ <tbody>
+ {% for row in rows %}
+ <tr{{ row.attributes }}>
+ <td class="views-ui-view-name">
+ <h3 data-drupal-selector="views-table-filter-text-source">{{ row.data.view_name.data }}</h3>
+ </td>
+ <td class="views-ui-view-machine-name" data-drupal-selector="views-table-filter-text-source">
+ {{ row.data.machine_name.data }}
+ </td>
+ <td class="views-ui-view-description" data-drupal-selector="views-table-filter-text-source">
+ {{ row.data.description.data }}
+ </td>
+ <td class="views-ui-view-displays">
+ {{ row.data.displays.data }}
+ </td>
+ <td class="views-ui-view-operations">
+ {{ row.data.operations.data }}
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>