From 3f8aa20c32be29c92ad575eaae246d69b48b4a54 Mon Sep 17 00:00:00 2001 From: Scott Reeves Date: Mon, 29 Aug 2016 09:41:31 -0400 Subject: [PATCH] =?UTF-8?q?Issue=20#2574767=20by=20dipakmdhrm,=20Manjit.Si?= =?UTF-8?q?ngh,=20malavya,=20Dom.,=20yoroy,=20xjm,=20tkoleary,=20G=C3=A1bo?= =?UTF-8?q?r=20Hojtsy,=20dawehner,=20Cottser,=20eelkeblok,=20Bojhan,=20fgm?= =?UTF-8?q?,=20DuaelFr:=20Views=20listing=20page=20displays=20too=20few=20?= =?UTF-8?q?items=20on=20a=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views_ui/css/views_ui.admin.theme.css | 29 ++-- .../views_ui/src/Tests/DefaultViewsTest.php | 5 +- core/modules/views_ui/src/Tests/XssTest.php | 3 - core/modules/views_ui/src/ViewListBuilder.php | 128 ++++++++---------- .../views-ui-view-displays-list.html.twig | 24 ++++ .../views-ui-views-listing-table.html.twig | 49 +++++++ .../tests/src/Unit/ViewListBuilderTest.php | 29 ++-- core/modules/views_ui/views_ui.module | 15 +- core/modules/views_ui/views_ui.theme.inc | 26 ++++ .../css/views_ui/views_ui.admin.theme.css | 29 ++-- .../views-ui-view-displays-list.html.twig | 22 +++ .../views-ui-views-listing-table.html.twig | 47 +++++++ 12 files changed, 305 insertions(+), 101 deletions(-) create mode 100644 core/modules/views_ui/templates/views-ui-view-displays-list.html.twig create mode 100644 core/modules/views_ui/templates/views-ui-views-listing-table.html.twig create mode 100644 core/themes/stable/templates/admin/views-ui-view-displays-list.html.twig create mode 100644 core/themes/stable/templates/admin/views-ui-views-listing-table.html.twig 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 4ab55c465754..460e75eaa2ad 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 3b3f2a734327..08fcae8ea4ee 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 8eac3a687368..1d61f1942725 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(', test', 'The view tag is properly escaped.'); - $this->drupalGet('admin/structure/views/view/sa_contrib_2013_035'); $this->assertEscaped('test', '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 112e1c457921..b38a15f75bdf 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(), + ), + ), + 'machine_name' => array( + 'data' => array( + '#plain_text' => $view->id(), ), ), 'description' => array( 'data' => array( '#plain_text' => $view->get('description'), ), - 'data-drupal-selector' => 'views-table-filter-text-source', ), - 'tag' => array( + 'displays' => array( 'data' => array( - '#plain_text' => $view->get('tag'), - ), - 'data-drupal-selector' => 'views-table-filter-text-source', - ), - 'path' => 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'), + '#attributes' => array( + 'class' => array('views-ui-description'), + ), ), - 'tag' => array( - 'data' => $this->t('Tag'), - 'class' => array('views-ui-tag'), - ), - '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,12 +245,28 @@ class ViewListBuilder extends ConfigEntityListBuilder { */ protected function getDisplaysList(EntityInterface $view) { $displays = array(); - foreach ($view->get('display') as $display) { - $definition = $this->displayManager->getDefinition($display['display_plugin']); + + $executable = $view->getExecutable(); + $executable->initDisplay(); + foreach ($executable->displayHandlers as $display) { + $rendered_path = FALSE; + $definition = $display->getPluginDefinition(); 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']; + 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, + ); } } @@ -255,33 +274,4 @@ class ViewListBuilder extends ConfigEntityListBuilder { 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; - } - } - } - return array_unique($all_paths); - } - } 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 000000000000..1c7d6210078c --- /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 + */ +#} + 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 000000000000..207462e88bcc --- /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 + */ +#} + + + + {% for header in headers %} + {{ header.data }} + {% endfor %} + + + + {% for row in rows %} + + +

{{ row.data.view_name.data }}

+ + + {{ row.data.machine_name.data }} + + + {{ row.data.description.data }} + + + {{ row.data.displays.data }} + + + {{ row.data.operations.data }} + + + {% endfor %} + + diff --git a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php index 215728cd5f43..9b646964eb04 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' => '/malformed_path', + ), + '2' => array( + 'display' => 'Page admin label', + 'path' => '/', + ), + '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, /malformed_path, /', 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 497636809baa..165e8669efb4 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 3b5867894d39..926f82ab309d 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. @@ -42,6 +43,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. * 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 3bf3290c8896..3a2afb56459d 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 000000000000..e6e5b021270a --- /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. + */ +#} +
    + {% for display in displays %} +
  • + {% if display.path %} + {{ display.display }} ({{ display.path }}) + {% else %} + {{ display.display }} + {% endif %} +
  • + {% endfor %} +
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 000000000000..bc12a0a6efba --- /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() + */ +#} + + + + {% for header in headers %} + {{ header.data }} + {% endfor %} + + + + {% for row in rows %} + + +

{{ row.data.view_name.data }}

+ + + {{ row.data.machine_name.data }} + + + {{ row.data.description.data }} + + + {{ row.data.displays.data }} + + + {{ row.data.operations.data }} + + + {% endfor %} + +