Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -8,11 +8,16 @@
"use strict";
/**
* Ajax command for highlighting elements.
*
* @param {Drupal.Ajax} [ajax]
* An Ajax object.
* @param {object} response
* The Ajax response.
* @param {string} response.selector
* The selector in question.
* @param {number} [status]
* The HTTP status code.
*/
Drupal.AjaxCommands.prototype.viewsHighlight = function (ajax, response, status) {
$('.hilited').removeClass('hilited');
@ -20,11 +25,16 @@
};
/**
* Ajax command to show certain buttons in the views edit form.
*
* @param {Drupal.Ajax} [ajax]
* An Ajax object.
* @param {object} response
* The Ajax response.
* @param {bool} response.changed
* Whether the state changed for the buttons or not.
* @param {number} [status]
* The HTTP status code.
*/
Drupal.AjaxCommands.prototype.viewsShowButtons = function (ajax, response, status) {
$('div.views-edit-view div.form-actions').removeClass('js-hide');
@ -34,10 +44,14 @@
};
/**
* Ajax command for triggering preview.
*
* @param {Drupal.Ajax} [ajax]
* An Ajax object.
* @param {object} [response]
* The Ajax response.
* @param {number} [status]
* The HTTP status code.
*/
Drupal.AjaxCommands.prototype.viewsTriggerPreview = function (ajax, response, status) {
if ($('input#edit-displays-live-preview').is(':checked')) {
@ -46,12 +60,18 @@
};
/**
* Ajax command to replace the title of a page.
*
* @param {Drupal.Ajax} [ajax]
* An Ajax object.
* @param {object} response
* The Ajax response.
* @param {string} response.siteName
* The site name.
* @param {string} response.title
* The new page title.
* @param {number} [status]
* The HTTP status code.
*/
Drupal.AjaxCommands.prototype.viewsReplaceTitle = function (ajax, response, status) {
var doc = document;
@ -72,6 +92,7 @@
* Get rid of irritating tabledrag messages.
*
* @return {Array}
* An array of messages. Always empty array, to get rid of the messages.
*/
Drupal.theme.tableDragChangedWarning = function () {
return [];
@ -81,6 +102,10 @@
* Trigger preview when the "live preview" checkbox is checked.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior to trigger live preview if the live preview option is
* checked.
*/
Drupal.behaviors.livePreview = {
attach: function (context) {
@ -96,6 +121,9 @@
* Sync preview display.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior to sync the preview display when needed.
*/
Drupal.behaviors.syncPreviewDisplay = {
attach: function (context) {
@ -110,15 +138,19 @@
};
/**
* Ajax behaviors for the views_ui module.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches ajax behaviors to the elements with the classes in question.
*/
Drupal.behaviors.viewsAjax = {
collapseReplaced: false,
attach: function (context, settings) {
var base_element_settings = {
'event': 'click',
'progress': {'type': 'fullscreen'}
event: 'click',
progress: {type: 'fullscreen'}
};
// Bind AJAX behaviors to all items showing the class.
$('a.views-ajax-link', context).once('views-ajax').each(function () {

View file

@ -31,8 +31,14 @@
}
/**
* Functionality for views modals.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches modal functionality for views.
* @prop {Drupal~behaviorDetach} detach
* Detaches the modal functionality.
*/
Drupal.behaviors.viewsModalContent = {
attach: function (context) {

View file

@ -16,6 +16,9 @@
* Improve the user experience of the views edit interface.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches toggling of SQL rewrite warning on the corresponding checkbox.
*/
Drupal.behaviors.viewsUiEditView = {
attach: function () {
@ -32,6 +35,10 @@
* as page title and menu link.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior for prepopulating page title and menu links, based on
* view name.
*/
Drupal.behaviors.viewsUiAddView = {
attach: function (context) {
@ -142,14 +149,18 @@
var self = this;
/**
* Populate the target form field with the altered source field value.
*
* @return {*}
* The result of the _populate call, which should be undefined.
*/
this.populate = function () { return self._populate.call(self); };
/**
* Stop prepopulating the form fields.
*
* @return {*}
* The result of the _unbind call, which should be undefined.
*/
this.unbind = function () { return self._unbind.call(self); };
@ -174,6 +185,7 @@
* Get the source form field value as altered by the passed-in parameters.
*
* @return {string}
* The source form field value.
*/
getTransliterated: function () {
var from = this.source.val();
@ -208,6 +220,7 @@
* Bind event handlers to new form fields, after they're replaced via Ajax.
*
* @param {jQuery} $fields
* Fields to rebind functionality to.
*/
rebind: function ($fields) {
this.target = $fields;
@ -216,8 +229,13 @@
});
/**
* Adds functionality for the add item form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the functionality in {@link Drupal.viewsUi.AddItemForm} to the
* forms in question.
*/
Drupal.behaviors.addItemForm = {
attach: function (context) {
@ -236,10 +254,12 @@
};
/**
* Constructs a new AddItemForm.
*
* @constructor
*
* @param {jQuery} $form
* The form element used.
*/
Drupal.viewsUi.AddItemForm = function ($form) {
@ -264,8 +284,10 @@
};
/**
* Handles a checkbox check.
*
* @param {jQuery.Event} event
* The event triggered.
*/
Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) {
var $target = $(event.target);
@ -311,6 +333,9 @@
* tabs.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Fixes the input elements needed.
*/
Drupal.behaviors.viewsUiRenderAddViewButton = {
attach: function (context) {
@ -359,7 +384,10 @@
};
/**
* Toggle menu visibility.
*
* @param {jQuery} $trigger
* The element where the toggle was triggered.
*
*
* @note [@jessebeach] I feel like the following should be a more generic
@ -372,8 +400,13 @@
};
/**
* Add search options to the views ui.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches {@link Drupal.viewsUi.OptionsSearch} to the views ui filter
* options.
*/
Drupal.behaviors.viewsUiSearchOptions = {
attach: function (context) {
@ -400,6 +433,7 @@
* @constructor
*
* @param {jQuery} $form
* The form element.
*/
Drupal.viewsUi.OptionsSearch = function ($form) {
@ -441,6 +475,7 @@
* shown and hidden depending on the user's search terms.
*
* @return {Array}
* An array of all the filterable options.
*/
getOptions: function ($allOptions) {
var $label;
@ -454,11 +489,11 @@
$description = $option.find('div.description');
options[i] = {
// Search on the lowercase version of the label text + description.
'searchText': $label.text().toLowerCase() + " " + $description.text().toLowerCase(),
searchText: $label.text().toLowerCase() + " " + $description.text().toLowerCase(),
// Maintain a reference to the jQuery object for each row, so we don't
// have to create a new object inside the performance-sensitive keyup
// handler.
'$div': $option
$div: $option
};
}
return options;
@ -469,6 +504,7 @@
* options.
*
* @param {jQuery.Event} event
* The keyup event.
*/
handleKeyup: function (event) {
var found;
@ -514,8 +550,12 @@
});
/**
* Preview functionality in the views edit form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the preview functionality to the view edit form.
*/
Drupal.behaviors.viewsUiPreview = {
attach: function (context) {
@ -544,8 +584,14 @@
};
/**
* Rearranges the filters.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attach handlers to make it possible to rearange the filters in the form
* in question.
* @see Drupal.viewsUi.RearrangeFilterHandler
*/
Drupal.behaviors.viewsUiRearrangeFilter = {
attach: function (context) {
@ -568,7 +614,9 @@
* @constructor
*
* @param {jQuery} $table
* The table in the filter form.
* @param {jQuery} $operator
* The filter groups operator element.
*/
Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) {
@ -688,6 +736,7 @@
* Dynamically click the button that adds a new filter group.
*
* @param {jQuery.Event} event
* The event triggered.
*/
clickAddGroupButton: function (event) {
// Due to conflicts between Drupal core's AJAX system and the Views AJAX
@ -716,6 +765,7 @@
* duplicate it between any subsequent groups.
*
* @return {jQuery}
* An operator element.
*/
duplicateGroupsOperator: function () {
var dropdowns;
@ -778,6 +828,7 @@
* Forces all operator dropdowns to have the same value.
*
* @param {jQuery.Event} event
* The event triggered.
*/
operatorChangeHandler: function (event) {
var $target = $(event.target);
@ -804,15 +855,13 @@
* - The operator cells that span multiple rows need their rowspan
* attributes updated to reflect the number of rows in each group.
* - The operator labels that are displayed next to each filter need to
* be
* redrawn, to account for the row's new location.
* be redrawn, to account for the row's new location.
*/
tableDrag.row.prototype.onSwap = function () {
if (filterHandler.hasGroupOperator) {
// Make sure the row that just got moved (this.group) is inside one
// of
// the filter groups (i.e. below an empty marker row or a draggable).
// If it isn't, move it down one.
// of the filter groups (i.e. below an empty marker row or a
// draggable). If it isn't, move it down one.
var thisRow = $(this.group);
var previousRow = thisRow.prev('tr');
if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) {
@ -947,6 +996,9 @@
* Add a select all checkbox, which checks each checkbox at once.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches select all functionality to the views filter form.
*/
Drupal.behaviors.viewsFilterConfigSelectAll = {
attach: function (context) {
@ -978,6 +1030,9 @@
* Remove icon class from elements that are themed as buttons or dropbuttons.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Removes the icon class from certain views elements.
*/
Drupal.behaviors.viewsRemoveIconClass = {
attach: function (context) {
@ -989,6 +1044,9 @@
* Change "Expose filter" buttons into checkboxes.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Changes buttons into checkboxes via {@link Drupal.viewsUi.Checkboxifier}.
*/
Drupal.behaviors.viewsUiCheckboxify = {
attach: function (context, settings) {
@ -1006,6 +1064,9 @@
* selected widget for the exposed group.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Changes the default widget based on user input.
*/
Drupal.behaviors.viewsUiChangeDefaultWidget = {
attach: function (context) {
@ -1056,6 +1117,7 @@
* When the checkbox is checked or unchecked, simulate a button press.
*
* @param {jQuery.Event} e
* The event triggered.
*/
Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) {
this.$button
@ -1067,6 +1129,10 @@
* Change the Apply button text based upon the override select state.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior to change the Apply button according to the current
* state.
*/
Drupal.behaviors.viewsUiOverrideSelect = {
attach: function (context) {
@ -1103,8 +1169,12 @@
};
/**
* Functionality for the remove link in the views UI.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior for the remove view and remove display links.
*/
Drupal.behaviors.viewsUiHandlerRemoveLink = {
attach: function (context) {

View file

@ -15,6 +15,9 @@
* Source text: .views-table-filter-text-source
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the filter functionality to the views admin text search field.
*/
Drupal.behaviors.viewTableFilterByText = {
attach: function (context, settings) {

View file

@ -7,7 +7,6 @@
namespace Drupal\views_ui\Controller;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\views\ViewExecutable;
@ -89,11 +88,16 @@ class ViewsUIController extends ControllerBase {
$header = array(t('Field name'), t('Used in'));
$rows = array();
foreach ($fields as $field_name => $views) {
$rows[$field_name]['data'][0] = SafeMarkup::checkPlain($field_name);
$rows[$field_name]['data'][0]['data']['#plain_text'] = $field_name;
foreach ($views as $view) {
$rows[$field_name]['data'][1][] = $this->l($view, new Url('entity.view.edit_form', array('view' => $view)));
}
$rows[$field_name]['data'][1] = SafeMarkup::set(implode(', ', $rows[$field_name]['data'][1]));
$item_list = [
'#theme' => 'item_list',
'#items' => $rows[$field_name]['data'][1],
'#context' => ['list_style' => 'comma-list'],
];
$rows[$field_name]['data'][1] = ['data' => $item_list];
}
// Sort rows by field name.
@ -121,7 +125,11 @@ class ViewsUIController extends ControllerBase {
foreach ($row['views'] as $row_name => $view) {
$row['views'][$row_name] = $this->l($view, new Url('entity.view.edit_form', array('view' => $view)));
}
$row['views'] = SafeMarkup::set(implode(', ', $row['views']));
$row['views']['data'] = [
'#theme' => 'item_list',
'#items' => $row['views'],
'#context' => ['list_style' => 'comma-list'],
];
}
// Sort rows by field name.

View file

@ -7,7 +7,7 @@
namespace Drupal\views_ui\Form\Ajax;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ViewExecutable;
@ -52,7 +52,7 @@ class RearrangeFilter extends ViewsFormBase {
return $form;
}
$display = $executable->displayHandlers->get($display_id);
$form['#title'] = SafeMarkup::checkPlain($display->display['display_title']) . ': ';
$form['#title'] = Html::escape($display->display['display_title']) . ': ';
$form['#title'] .= $this->t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
$form['#section'] = $display_id . 'rearrange-item';

View file

@ -35,6 +35,7 @@ class DisplayPathTest extends UITestBase {
public function testPathUI() {
$this->doBasicPathUITest();
$this->doAdvancedPathsValidationTest();
$this->doPathXssFilterTest();
}
/**
@ -59,6 +60,28 @@ class DisplayPathTest extends UITestBase {
$this->assertLink(t('View @display', array('@display' => 'Page')), 0, 'view page link found on the page.');
}
/**
* Tests that View paths are properly filtered for XSS.
*/
public function doPathXssFilterTest() {
$this->drupalGet('admin/structure/views/view/test_view');
$this->drupalPostForm(NULL, array(), 'Add Page');
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_2/path', array('path' => '<object>malformed_path</object>'), t('Apply'));
$this->drupalPostForm(NULL, array(), 'Add Page');
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_3/path', array('path' => '<script>alert("hello");</script>'), t('Apply'));
$this->drupalPostForm(NULL, array(), 'Add Page');
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_4/path', array('path' => '<script>alert("hello I have placeholders %");</script>'), t('Apply'));
$this->drupalPostForm('admin/structure/views/view/test_view', array(), t('Save'));
$this->drupalGet('admin/structure/views');
// The anchor text should be escaped.
$this->assertEscaped('/<object>malformed_path</object>');
$this->assertEscaped('/<script>alert("hello");</script>');
$this->assertEscaped('/<script>alert("hello I have placeholders %");</script>');
// Links should be url-encoded.
$this->assertRaw('/%3Cobject%3Emalformed_path%3C/object%3E');
$this->assertRaw('/%3Cscript%3Ealert%28%22hello%22%29%3B%3C/script%3E');
}
/**
* Tests a couple of invalid path patterns.
*/

View file

@ -7,6 +7,8 @@
namespace Drupal\views_ui\Tests;
use Drupal\views\Entity\View;
/**
* Tests exposed forms UI functionality.
*
@ -151,5 +153,25 @@ class ExposedFormUITest extends UITestBase {
// Check the label of the expose button.
$this->helperButtonHasLabel('edit-options-expose-button-button', t('Hide sort'));
$this->assertFieldById('edit-options-expose-label', '', 'Make sure a label field is shown');
// Test adding a new exposed sort criteria.
$view_id = $this->randomView()['id'];
$this->drupalGet("admin/structure/views/nojs/add-handler/$view_id/default/sort");
$this->drupalPostForm(NULL, ['name[node_field_data.created]' => 1], t('Add and configure @handler', ['@handler' => t('sort criteria')]));
$this->assertFieldByXPath('//input[@name="options[order]" and @checked="checked"]', 'ASC', 'The default order is set.');
// Change the order and expose the sort.
$this->drupalPostForm(NULL, ['options[order]' => 'DESC'], t('Apply'));
$this->drupalPostForm("admin/structure/views/nojs/handler/$view_id/default/sort/created", [], t('Expose sort'));
$this->assertFieldByXPath('//input[@name="options[order]" and @checked="checked"]', 'DESC');
$this->assertFieldByName('options[expose][label]', 'Authored on', 'The default label is set.');
// Change the label and save the view.
$edit = ['options[expose][label]' => $this->randomString()];
$this->drupalPostForm(NULL, $edit, t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
// Check that the values were saved.
$display = View::load($view_id)->getDisplay('default');
$this->assertTrue($display['display_options']['sorts']['created']['exposed']);
$this->assertEqual($display['display_options']['sorts']['created']['expose'], ['label' => $edit['options[expose][label]']]);
$this->assertEqual($display['display_options']['sorts']['created']['order'], 'DESC');
}
}

View file

@ -150,7 +150,7 @@ class HandlerTest extends UITestBase {
$result = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
$this->assertEqual(count($result), 1, SafeMarkup::format('Handler (%type) edit link found.', array('%type' => $type)));
$text = t('Broken/missing handler');
$text = 'Broken/missing handler';
$this->assertIdentical((string) $result[0], $text, 'Ensure the broken handler text was found.');

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Tests\ReportFieldsTest.
*/
namespace Drupal\views_ui\Tests;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the Views fields report page.
*
* @group views_ui
*/
class ReportFieldsTest extends UITestBase {
/**
* {@inheritdoc}
*/
public static $testViews = ['test_field_field_test'];
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* Tests the Views fields report page.
*/
public function testReportFields() {
$this->drupalGet('admin/reports/fields/views-fields');
$this->assertRaw('Used in views', 'Title appears correctly');
$this->assertRaw('No fields have been used in views yet.', 'No results message appears correctly.');
// Set up the field_test field.
$field_storage = FieldStorageConfig::create([
'field_name' => 'field_test',
'type' => 'integer',
'entity_type' => 'entity_test',
]);
$field_storage->save();
$field = FieldConfig::create([
'field_name' => 'field_test',
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
]);
$field->save();
$this->drupalGet('admin/reports/fields/views-fields');
// Assert that the newly created field appears in the overview.
$this->assertRaw('<td>field_test</td>', 'Field name appears correctly');
$this->assertRaw('>test_field_field_test</a>', 'View name appears correctly');
$this->assertRaw('Used in views', 'Title appears correctly');
}
}

View file

@ -21,6 +21,14 @@ class SettingsTest extends UITestBase {
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests the settings for the edit ui.
*/

View file

@ -7,7 +7,7 @@
namespace Drupal\views_ui\Tests;
use Drupal\views\Tests\ViewUnitTestBase;
use Drupal\views\Tests\ViewKernelTestBase;
use Drupal\views_ui\Controller\ViewsUIController;
/**
@ -15,7 +15,7 @@ use Drupal\views_ui\Controller\ViewsUIController;
*
* @group views_ui
*/
class TagTest extends ViewUnitTestBase {
class TagTest extends ViewKernelTestBase {
/**
* Modules to enable.

View file

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\views_ui\Tests\ViewsListTest.
*/
namespace Drupal\views_ui\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\views\Entity\View;
use Drupal\views\Views;
/**
* Tests the views list.
*
* @group views_ui
*/
class ViewsListTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('block', 'views_ui');
/**
* A user with permission to administer views.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('local_actions_block');
$this->adminUser = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($this->adminUser);
}
/**
* Tests that the views list does not use a pager.
*/
public function testViewsListLimit() {
// Check if we can access the main views admin page.
$this->drupalGet('admin/structure/views');
$this->assertResponse(200);
$this->assertLink(t('Add new view'));
// Count default views to be subtracted from the limit.
$views = count(Views::getEnabledViews());
// Create multiples views.
$limit = 51;
$values = $this->config('views.view.test_view_storage')->get();
for ($i = 1; $i <= $limit - $views; $i++) {
$values['id'] = 'test_view_storage_new' . $i;
unset($values['uuid']);
$created = View::create($values);
$created->save();
}
$this->drupalGet('admin/structure/views');
// Check that all the rows are listed.
$this->assertEqual(count($this->xpath('//tbody/tr[contains(@class,"views-ui-list-enabled")]')), $limit);
}
}

View file

@ -23,10 +23,10 @@ class XssTest extends UITestBase {
public function testViewsUi() {
$this->drupalGet('admin/structure/views');
$this->assertRaw('&lt;script&gt;alert(&quot;foo&quot;);&lt;/script&gt;, &lt;marquee&gt;test&lt;/marquee&gt;', 'The view tag is properly escaped.');
$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->assertRaw('&amp;lt;marquee&amp;gt;test&amp;lt;/marquee&amp;gt;', 'Field admin label is properly escaped.');
$this->assertEscaped('<marquee>test</marquee>', 'Field admin label is properly escaped.');
$this->drupalGet('admin/structure/views/nojs/handler/sa_contrib_2013_035/page_1/header/area');
$this->assertRaw('[title] == &amp;lt;marquee&amp;gt;test&amp;lt;/marquee&amp;gt;', 'Token label is properly escaped.');

View file

@ -192,7 +192,7 @@ class ViewAddForm extends ViewFormBase {
return;
}
$this->entity->save();
drupal_set_message($this->t('The view %name has been saved.', array('%name' => $form_state->getValue('label'))));
$form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
}

View file

@ -414,13 +414,20 @@ class ViewEditForm extends ViewFormBase {
elseif ($view->status() && $view->getExecutable()->displayHandlers->get($display['id'])->hasPath()) {
$path = $view->getExecutable()->displayHandlers->get($display['id'])->getPath();
if ($path && (strpos($path, '%') === FALSE)) {
$uri = "base:$path";
if (!parse_url($path, PHP_URL_SCHEME)) {
// @todo Views should expect and store a leading /. See:
// https://www.drupal.org/node/2423913
$url = Url::fromUserInput('/' . ltrim($uri, '/'));
}
else {
$url = Url::fromUri($uri);
}
$build['top']['actions']['path'] = array(
'#type' => 'link',
'#title' => $this->t('View !display_title', array('!display_title' => $display_title)),
'#options' => array('alt' => array($this->t("Go to the real page for this display"))),
// @todo Use Url::fromPath() once
// https://www.drupal.org/node/2351379 is resolved.
'#url' => Url::fromUri("base:$path"),
'#url' => $url,
'#prefix' => '<li class="view">',
"#suffix" => '</li>',
);
@ -487,7 +494,7 @@ class ViewEditForm extends ViewFormBase {
$build['top']['display_title'] = array(
'#theme' => 'views_ui_display_tab_setting',
'#description' => $this->t('Display name'),
'#link' => $view->getExecutable()->displayHandlers->get($display['id'])->optionLink(SafeMarkup::checkPlain($display_title), 'display_title'),
'#link' => $view->getExecutable()->displayHandlers->get($display['id'])->optionLink($display_title, 'display_title'),
);
}
@ -661,12 +668,10 @@ class ViewEditForm extends ViewFormBase {
// Regenerate the main display area.
$build = $this->getDisplayTab($view);
static::addMicroweights($build);
$response->addCommand(new HtmlCommand('#views-tab-' . $display_id, $build));
// Regenerate the top area so changes to display names and order will appear.
$build = $this->renderDisplayTop($view);
static::addMicroweights($build);
$response->addCommand(new ReplaceCommand('#views-display-top', $build));
}
@ -1055,7 +1060,7 @@ class ViewEditForm extends ViewFormBase {
continue;
}
$field_name = SafeMarkup::checkPlain($handler->adminLabel(TRUE));
$field_name = $handler->adminLabel(TRUE);
if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
$field_name = '(' . $relationships[$field['relationship']] . ') ' . $field_name;
}
@ -1141,22 +1146,4 @@ class ViewEditForm extends ViewFormBase {
return $build;
}
/**
* Recursively adds microweights to a render array, similar to what
* \Drupal::formBuilder()->doBuildForm() does for forms.
*
* @todo Submit a core patch to fix drupal_render() to do this, so that all
* render arrays automatically preserve array insertion order, as forms do.
*/
public static function addMicroweights(&$build) {
$count = 0;
foreach (Element::children($build) as $key) {
if (!isset($build[$key]['#weight'])) {
$build[$key]['#weight'] = $count/1000;
}
static::addMicroweights($build[$key]);
$count++;
}
}
}

View file

@ -7,7 +7,6 @@
namespace Drupal\views_ui;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
@ -30,6 +29,11 @@ class ViewListBuilder extends ConfigEntityListBuilder {
*/
protected $displayManager;
/**
* {@inheritdoc}
*/
protected $limit;
/**
* {@inheritdoc}
*/
@ -55,6 +59,11 @@ class ViewListBuilder extends ConfigEntityListBuilder {
parent::__construct($entity_type, $storage);
$this->displayManager = $display_manager;
// This list builder uses client-side filters which requires all entities to
// be listed, disable the pager.
// @todo https://www.drupal.org/node/2536826 change the filtering to support
// a pager.
$this->limit = FALSE;
}
/**
@ -81,12 +90,6 @@ class ViewListBuilder extends ConfigEntityListBuilder {
*/
public function buildRow(EntityInterface $view) {
$row = parent::buildRow($view);
$display_paths = '';
$separator = '';
foreach ($this->getDisplayPaths($view) as $display_path) {
$display_paths .= $separator . SafeMarkup::escape($display_path);
$separator = ', ';
}
return array(
'data' => array(
'view_name' => array(
@ -98,12 +101,18 @@ class ViewListBuilder extends ConfigEntityListBuilder {
),
'description' => array(
'data' => array(
'#markup' => SafeMarkup::checkPlain($view->get('description')),
'#plain_text' => $view->get('description'),
),
'class' => array('views-table-filter-text-source'),
),
'tag' => $view->get('tag'),
'path' => SafeMarkup::set($display_paths),
'path' => array(
'data' => array(
'#theme' => 'item_list',
'#items' => $this->getDisplayPaths($view),
'#context' => ['list_style' => 'comma-list'],
),
),
'operations' => $row['operations'],
),
'title' => $this->t('Machine name: @name', array('@name' => $view->id())),
@ -268,7 +277,7 @@ class ViewListBuilder extends ConfigEntityListBuilder {
$all_paths[] = \Drupal::l('/' . $path, Url::fromUserInput('/' . $path));
}
else {
$all_paths[] = SafeMarkup::checkPlain('/' . $path);
$all_paths[] = '/' . $path;
}
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\Tests\views_ui\Unit;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Tests\UnitTestCase;
@ -89,7 +90,10 @@ class ViewListBuilderTest extends UnitTestCase {
);
$page_display->expects($this->any())
->method('getPath')
->will($this->returnValue('test_page'));
->will($this->onConsecutiveCalls(
$this->returnValue('test_page'),
$this->returnValue('<object>malformed_path</object>'),
$this->returnValue('<script>alert("placeholder_page/%")</script>')));
$embed_display = $this->getMock('Drupal\views\Plugin\views\display\Embed', array('initDisplay'),
array(array(), 'default', $display_manager->getDefinition('embed'))
@ -106,6 +110,16 @@ class ViewListBuilderTest extends UnitTestCase {
$values['display']['page_1']['display_plugin'] = 'page';
$values['display']['page_1']['display_options']['path'] = 'test_page';
$values['display']['page_2']['id'] = 'page_2';
$values['display']['page_2']['display_title'] = 'Page 2';
$values['display']['page_2']['display_plugin'] = 'page';
$values['display']['page_2']['display_options']['path'] = '<object>malformed_path</object>';
$values['display']['page_3']['id'] = 'page_3';
$values['display']['page_3']['display_title'] = 'Page 3';
$values['display']['page_3']['display_plugin'] = 'page';
$values['display']['page_3']['display_options']['path'] = '<script>alert("placeholder_page/%")</script>';
$values['display']['embed']['id'] = 'embed';
$values['display']['embed']['display_title'] = 'Embedded';
$values['display']['embed']['display_plugin'] = 'embed';
@ -115,6 +129,8 @@ class ViewListBuilderTest extends UnitTestCase {
->will($this->returnValueMap(array(
array('default', $values['display']['default'], $default_display),
array('page', $values['display']['page_1'], $page_display),
array('page', $values['display']['page_2'], $page_display),
array('page', $values['display']['page_3'], $page_display),
array('embed', $values['display']['embed'], $embed_display),
)));
@ -141,8 +157,19 @@ class ViewListBuilderTest extends UnitTestCase {
$row = $view_list_builder->buildRow($view);
$this->assertEquals(array('Embed admin label', 'Page admin label'), $row['data']['view_name']['data']['#displays'], 'Wrong displays got added to view list');
$this->assertEquals($row['data']['path'], '/test_page', 'The path of the page display is not added.');
$expected_displays = array(
'Embed admin label',
'Page admin label',
'Page admin label',
'Page admin label',
);
$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));
$this->assertFalse(SafeMarkup::isSafe('/<object>malformed_path</object>'), '/<script>alert("/<object>malformed_path</object> is not marked safe.');
$this->assertFalse(SafeMarkup::isSafe('/<script>alert("placeholder_page/%")'), '/<script>alert("/<script>alert("placeholder_page/%") is not marked safe.');
}
}