Update to Drupal 8.0.2. For more information, see https://www.drupal.org/drupal-8.0.2-release-notes
This commit is contained in:
parent
1a0e9d9fac
commit
a6b049dd05
538 changed files with 5247 additions and 1594 deletions
|
@ -28,6 +28,9 @@ class ViewsBlock extends ViewsBlockBase {
|
|||
public function build() {
|
||||
$this->view->display_handler->preBlockBuild($this);
|
||||
|
||||
// We ask ViewExecutable::buildRenderable() to avoid creating a render cache
|
||||
// entry for the view output by passing FALSE, because we're going to cache
|
||||
// the whole block instead.
|
||||
if ($output = $this->view->buildRenderable($this->displayID, [], FALSE)) {
|
||||
// Override the label to the dynamic title configured in the view.
|
||||
if (empty($this->configuration['views_label']) && $this->view->getTitle()) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\views\Plugin\Block;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Provides a 'Views Exposed Filter' block.
|
||||
|
@ -18,11 +19,20 @@ namespace Drupal\views\Plugin\Block;
|
|||
*/
|
||||
class ViewsExposedFilterBlock extends ViewsBlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
$contexts = $this->view->display_handler->getCacheMetadata()->getCacheContexts();
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), $contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$output = $this->view->display_handler->viewExposedFormBlocks();
|
||||
|
||||
// Before returning the block output, convert it to a renderable array with
|
||||
// contextual links.
|
||||
$this->addContextualLinks($output, 'exposed_filter');
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Drupal\views\Plugin\views\area;
|
|||
*
|
||||
* @ingroup views_area_handlers
|
||||
*
|
||||
* @PluginID("messages")
|
||||
* @ViewsArea("messages")
|
||||
*/
|
||||
class Messages extends AreaPluginBase {
|
||||
|
||||
|
|
|
@ -270,6 +270,10 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
|
|||
// that parameter conversion options is carried over.
|
||||
$route->setOptions($route->getOptions() + $original_route->getOptions());
|
||||
|
||||
if ($original_route->hasDefault('_title_callback')) {
|
||||
$route->setDefault('_title_callback', $original_route->getDefault('_title_callback'));
|
||||
}
|
||||
|
||||
// Set the corrected path and the mapping to the route object.
|
||||
$route->setOption('_view_argument_map', $argument_map);
|
||||
$route->setPath($path);
|
||||
|
|
|
@ -359,6 +359,14 @@ abstract class ExposedFormPluginBase extends PluginBase implements CacheableDepe
|
|||
}
|
||||
}
|
||||
|
||||
// Merge in cache contexts for all exposed filters to prevent display of
|
||||
// cached forms.
|
||||
foreach ($this->displayHandler->getHandlers('filter') as $filter_hander) {
|
||||
if ($filter_hander->isExposed()) {
|
||||
$contexts = Cache::mergeContexts($contexts, $filter_hander->getCacheContexts());
|
||||
}
|
||||
}
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
|
|
|
@ -242,8 +242,11 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
|
|||
|
||||
// Go through the list and determine the actual column name from field api.
|
||||
$fields = array();
|
||||
$table_mapping = $this->getTableMapping();
|
||||
$field_definition = $this->getFieldStorageDefinition();
|
||||
|
||||
foreach ($options as $column) {
|
||||
$fields[$column] = $this->getTableMapping()->getFieldColumnName($this->getFieldStorageDefinition(), $column);
|
||||
$fields[$column] = $table_mapping->getFieldColumnName($field_definition, $column);
|
||||
}
|
||||
|
||||
$this->group_fields = $fields;
|
||||
|
|
|
@ -86,9 +86,11 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
|
|||
public $original_value = NULL;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* Stores additional fields which get's added to the query.
|
||||
* Stores additional fields that get added to the query.
|
||||
*
|
||||
* The generated aliases are stored in $aliases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $additional_fields = array();
|
||||
|
||||
|
@ -1384,7 +1386,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
|
|||
// In that case the original path looks like
|
||||
// internal:/admin/content/files/usage/{{ fid }}, which will be escaped by
|
||||
// the toUriString() call above.
|
||||
$path = preg_replace(['/(\%7B){2}(\%20)*/', '/(\%20)*(\%7D){2}/'], ['{{','}}'], $path);
|
||||
$path = preg_replace(['/(\%7B){2}(\%20)*/', '/(\%20)*(\%7D){2}/'], ['{{', '}}'], $path);
|
||||
|
||||
// Use strip tags as there should never be HTML in the path.
|
||||
// However, we need to preserve special characters like " that are escaped
|
||||
|
|
|
@ -138,7 +138,7 @@ class NumericField extends FieldPluginBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
// Merge plural format options into one string and drop the individual
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\views\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
@ -31,6 +32,20 @@ use Drupal\views\ViewExecutable;
|
|||
*/
|
||||
class BooleanOperator extends FilterPluginBase {
|
||||
|
||||
/**
|
||||
* The equal query operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EQUAL = '=';
|
||||
|
||||
/**
|
||||
* The non equal query operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NOT_EQUAL = '<>';
|
||||
|
||||
// exposed filter options
|
||||
protected $alwaysMultiple = TRUE;
|
||||
// Don't display empty space where the operator would be.
|
||||
|
@ -61,15 +76,17 @@ class BooleanOperator extends FilterPluginBase {
|
|||
return array(
|
||||
'=' => array(
|
||||
'title' => $this->t('Is equal to'),
|
||||
'method' => 'queryOpBoolean',
|
||||
'method' => '_queryOperatorBoolean',
|
||||
'short' => $this->t('='),
|
||||
'values' => 1,
|
||||
'query_operator' => static::EQUAL,
|
||||
),
|
||||
'!=' => array(
|
||||
'title' => $this->t('Is not equal to'),
|
||||
'method' => 'queryOpBoolean',
|
||||
'method' => '_queryOperatorBoolean',
|
||||
'short' => $this->t('!='),
|
||||
'values' => 1,
|
||||
'query_operator' => static::NOT_EQUAL,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -185,7 +202,7 @@ class BooleanOperator extends FilterPluginBase {
|
|||
// human-readable label based on the current value. The valueOptions
|
||||
// array is keyed with either 0 or 1, so if the current value is not
|
||||
// empty, use the label for 1, and if it's empty, use the label for 0.
|
||||
return $this->valueOptions[!empty($this->value)];
|
||||
return $this->operator . ' ' . $this->valueOptions[!empty($this->value)];
|
||||
}
|
||||
|
||||
public function defaultExposeOptions() {
|
||||
|
@ -204,34 +221,64 @@ class BooleanOperator extends FilterPluginBase {
|
|||
|
||||
$info = $this->operators();
|
||||
if (!empty($info[$this->operator]['method'])) {
|
||||
call_user_func(array($this, $info[$this->operator]['method']), $field);
|
||||
call_user_func(array($this, $info[$this->operator]['method']), $field, $info[$this->operator]['query_operator']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where condition to the query for a boolean value. This function
|
||||
* remains to prevent breaks in public-facing API's.
|
||||
*
|
||||
* @param string $field
|
||||
* The field name to add the where condition for.
|
||||
*/
|
||||
protected function queryOpBoolean($field) {
|
||||
$this->_queryOperatorBoolean($field, static::EQUAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where condition to the query for a boolean value.
|
||||
*
|
||||
* @param string $field
|
||||
* The field name to add the where condition for.
|
||||
* @param string $query_operator
|
||||
* Either static::EQUAL or static::NOT_EQUAL.
|
||||
*
|
||||
* @internal
|
||||
* This method will be removed in 8.1.0 and is here to maintain backwards-
|
||||
* compatibility in 8.0.x releases.
|
||||
*/
|
||||
protected function queryOpBoolean($field) {
|
||||
protected function _queryOperatorBoolean($field, $query_operator) {
|
||||
if (empty($this->value)) {
|
||||
if ($this->accept_null) {
|
||||
$or = db_or()
|
||||
->condition($field, 0, '=')
|
||||
->condition($field, NULL, 'IS NULL');
|
||||
$this->query->addWhere($this->options['group'], $or);
|
||||
if ($query_operator == static::EQUAL) {
|
||||
$condition = (new Condition('OR'))
|
||||
->condition($field, 0, $query_operator)
|
||||
->isNull($field);
|
||||
}
|
||||
else {
|
||||
$condition = (new Condition('AND'))
|
||||
->condition($field, 0, $query_operator)
|
||||
->isNotNull($field);
|
||||
}
|
||||
$this->query->addWhere($this->options['group'], $condition);
|
||||
}
|
||||
else {
|
||||
$this->query->addWhere($this->options['group'], $field, 0, '=');
|
||||
$this->query->addWhere($this->options['group'], $field, 0, $query_operator);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!empty($this->definition['use_equal'])) {
|
||||
$this->query->addWhere($this->options['group'], $field, 1, '=');
|
||||
// Forces an '=' operator instead of a '<>' for performance reasons.
|
||||
if ($query_operator == static::EQUAL) {
|
||||
$this->query->addWhere($this->options['group'], $field, 1, static::EQUAL);
|
||||
}
|
||||
else {
|
||||
$this->query->addWhere($this->options['group'], $field, 0, static::EQUAL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->query->addWhere($this->options['group'], $field, 0, '<>');
|
||||
$this->query->addWhere($this->options['group'], $field, 1, $query_operator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,11 @@ class Combine extends StringFilter {
|
|||
if ($this->view->style_plugin->usesFields()) {
|
||||
$options = array();
|
||||
foreach ($this->view->display_handler->getHandlers('field') as $name => $field) {
|
||||
$options[$name] = $field->adminLabel(TRUE);
|
||||
// Only allow clickSortable fields. Fields without clickSorting will
|
||||
// probably break in the Combine filter.
|
||||
if ($field->clickSortable()) {
|
||||
$options[$name] = $field->adminLabel(TRUE);
|
||||
}
|
||||
}
|
||||
if ($options) {
|
||||
$form['fields'] = array(
|
||||
|
@ -108,6 +112,13 @@ class Combine extends StringFilter {
|
|||
$errors[] = $this->t('Field %field set in %filter is not set in this display.', array('%field' => $id, '%filter' => $this->adminLabel()));
|
||||
break;
|
||||
}
|
||||
elseif (!$fields[$id]->clickSortable()) {
|
||||
// Combined field filter only works with simple fields. If the field is
|
||||
// not click sortable we can assume it is not a simple field.
|
||||
// @todo change this check to isComputed. See
|
||||
// https://www.drupal.org/node/2349465
|
||||
$errors[] = $this->t('Field %field set in %filter is not usable for this filter type. Combined field filter only works for simple fields.', array('%field' => $fields[$id]->adminLabel(), '%filter' => $this->adminLabel()));
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
|
|
@ -59,8 +59,8 @@ class InOperator extends FilterPluginBase {
|
|||
* This can use a guard to be used to reduce database hits as much as
|
||||
* possible.
|
||||
*
|
||||
* @return
|
||||
* Return the stored values in $this->valueOptions if someone expects it.
|
||||
* @return array|NULL
|
||||
* The stored values from $this->valueOptions.
|
||||
*/
|
||||
public function getValueOptions() {
|
||||
if (isset($this->valueOptions)) {
|
||||
|
|
|
@ -69,6 +69,7 @@ class LanguageFilter extends InOperator implements ContainerFactoryPluginInterfa
|
|||
// lost when there are changes in the language configuration.
|
||||
$this->valueOptions = $this->listLanguages(LanguageInterface::STATE_ALL | LanguageInterface::STATE_SITE_DEFAULT | PluginBase::INCLUDE_NEGOTIATED, array_keys($this->value));
|
||||
}
|
||||
return $this->valueOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -199,9 +199,10 @@ class NumericFilter extends FilterPluginBase {
|
|||
if ($which == 'all' || $which == 'minmax') {
|
||||
$form['value']['min'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => !$exposed ? $this->t('Min') : '',
|
||||
'#title' => !$exposed ? $this->t('Min') : $this->exposedInfo()['label'],
|
||||
'#size' => 30,
|
||||
'#default_value' => $this->value['min'],
|
||||
'#description' => !$exposed ? '' : $this->exposedInfo()['description']
|
||||
);
|
||||
$form['value']['max'] = array(
|
||||
'#type' => 'textfield',
|
||||
|
|
|
@ -134,6 +134,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy() {
|
||||
parent::destroy();
|
||||
|
||||
|
@ -255,6 +258,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
return !empty($this->definition['even empty']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['grouping'] = array('default' => array());
|
||||
|
@ -267,6 +273,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
// Only fields-based views can handle grouping. Style plugins can also exclude
|
||||
|
@ -358,6 +367,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
// Don't run validation on style plugins without the grouping setting.
|
||||
if ($form_state->hasValue(array('style_options', 'grouping'))) {
|
||||
|
@ -783,6 +795,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate() {
|
||||
$errors = parent::validate();
|
||||
|
||||
|
@ -801,6 +816,9 @@ abstract class StylePluginBase extends PluginBase {
|
|||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
parent::query();
|
||||
if (isset($this->view->rowPlugin)) {
|
||||
|
|
|
@ -1249,7 +1249,7 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createView(array $form, FormStateInterface $form_state) {
|
||||
$view = $this->retrieveValidatedView($form, $form_state);
|
||||
|
|
|
@ -29,7 +29,7 @@ class ResultRow {
|
|||
/**
|
||||
* An incremental number which represents the row in the entire result.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $index;
|
||||
|
||||
|
|
44
core/modules/views/src/Tests/Handler/AreaMessagesTest.php
Normal file
44
core/modules/views/src/Tests/Handler/AreaMessagesTest.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\Tests\Handler\AreaMessagesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\Tests\Handler;
|
||||
|
||||
use Drupal\views\Tests\ViewKernelTestBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the messages area handler.
|
||||
*
|
||||
* @group views
|
||||
* @see \Drupal\views\Plugin\views\area\Messages
|
||||
*/
|
||||
class AreaMessagesTest extends ViewKernelTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_area_messages');
|
||||
|
||||
/**
|
||||
* Tests the messages area handler.
|
||||
*/
|
||||
public function testMessageText() {
|
||||
drupal_set_message('My drupal set message.');
|
||||
|
||||
$view = Views::getView('test_area_messages');
|
||||
|
||||
$view->setDisplay('default');
|
||||
$this->executeView($view);
|
||||
$output = $view->render();
|
||||
$output = \Drupal::service('renderer')->renderRoot($output);
|
||||
$this->setRawContent($output);
|
||||
$this->assertText('My drupal set message.');
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ class FieldDropButtonTest extends HandlerTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(['access content overview', 'administer nodes', 'bypass node access']);
|
||||
|
|
|
@ -95,6 +95,30 @@ class FilterBooleanOperatorTest extends ViewKernelTestBase {
|
|||
|
||||
$this->assertEqual(3, count($view->result));
|
||||
$this->assertIdenticalResultset($view, $expected_result, $this->columnMap);
|
||||
|
||||
$view->destroy();
|
||||
$view->setDisplay();
|
||||
|
||||
// Testing the same scenario but using the reverse status and operation.
|
||||
$view->displayHandlers->get('default')->overrideOption('filters', array(
|
||||
'status' => array(
|
||||
'id' => 'status',
|
||||
'field' => 'status',
|
||||
'table' => 'views_test_data',
|
||||
'value' => 0,
|
||||
'operator' => '!=',
|
||||
),
|
||||
));
|
||||
$this->executeView($view);
|
||||
|
||||
$expected_result = array(
|
||||
array('id' => 1),
|
||||
array('id' => 3),
|
||||
array('id' => 5),
|
||||
);
|
||||
|
||||
$this->assertEqual(3, count($view->result));
|
||||
$this->assertIdenticalResultset($view, $expected_result, $this->columnMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,6 +157,24 @@ class FilterBooleanOperatorTest extends ViewKernelTestBase {
|
|||
|
||||
$this->assertEqual(2, count($view->result));
|
||||
$this->assertIdenticalResultset($view, $expected_result, $this->columnMap);
|
||||
|
||||
$view->destroy();
|
||||
|
||||
// Expecting the same results as for ['status' => 1].
|
||||
$view->setExposedInput(['status' => 3]);
|
||||
$view->setDisplay();
|
||||
$view->displayHandlers->get('default')->overrideOption('filters', $filters);
|
||||
|
||||
$this->executeView($view);
|
||||
|
||||
$expected_result = array(
|
||||
array('id' => 1),
|
||||
array('id' => 3),
|
||||
array('id' => 5),
|
||||
);
|
||||
|
||||
$this->assertEqual(3, count($view->result));
|
||||
$this->assertIdenticalResultset($view, $expected_result, $this->columnMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,6 +211,13 @@ class FilterBooleanOperatorTest extends ViewKernelTestBase {
|
|||
'operator' => '=',
|
||||
'value' => '0',
|
||||
),
|
||||
// This group should return the same results as group 1, because it
|
||||
// is the negation of group 2.
|
||||
3 => array(
|
||||
'title' => 'Active (reverse)',
|
||||
'operator' => '!=',
|
||||
'value' => '0',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -151,6 +151,7 @@ class ExposedFormTest extends ViewTestBase {
|
|||
* Tests the exposed block functionality.
|
||||
*/
|
||||
public function testExposedBlock() {
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$view = Views::getView('test_exposed_block');
|
||||
$view->setDisplay('page_1');
|
||||
$block = $this->drupalPlaceBlock('views_exposed_filter_block:test_exposed_block-page_1');
|
||||
|
@ -167,6 +168,15 @@ class ExposedFormTest extends ViewTestBase {
|
|||
// Test there is only one views exposed form on the page.
|
||||
$elements = $this->xpath('//form[@id=:id]', array(':id' => $this->getExpectedExposedFormId($view)));
|
||||
$this->assertEqual(count($elements), 1, 'One exposed form block found.');
|
||||
|
||||
// Test that the correct option is selected after form submission.
|
||||
$this->assertCacheContext('url');
|
||||
$this->assertOptionSelected('edit-type', 'All');
|
||||
foreach (['All', 'article', 'page'] as $argument) {
|
||||
$this->drupalGet('test_exposed_block', ['query' => ['type' => $argument]]);
|
||||
$this->assertCacheContext('url');
|
||||
$this->assertOptionSelected('edit-type', $argument);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,7 +24,7 @@ class PluginBaseTest extends KernelTestBase {
|
|||
*/
|
||||
var $testPluginBase;
|
||||
|
||||
public function setUp() {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->testPluginBase = new TestPluginBase();
|
||||
}
|
||||
|
|
|
@ -136,4 +136,99 @@ class StyleTableTest extends PluginTestBase {
|
|||
$this->assertTrue(count($result), 'Ensure that the baby\'s age is shown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that empty columns are hidden when empty_column is set.
|
||||
*/
|
||||
public function testEmptyColumn() {
|
||||
// Empty the 'job' data.
|
||||
\Drupal::database()->update('views_test_data')
|
||||
->fields(['job' => ''])
|
||||
->execute();
|
||||
|
||||
$this->drupalGet('test-table');
|
||||
|
||||
// Test that only one of the job columns still shows.
|
||||
$result = $this->xpath('//thead/tr/th/a[text()="Job"]');
|
||||
$this->assertEqual(count($result), 1, 'Ensure that empty column header is hidden.');
|
||||
|
||||
$result = $this->xpath('//tbody/tr/td[contains(concat(" ", @class, " "), " views-field-job-1 ")]');
|
||||
$this->assertEqual(count($result), 0, 'Ensure the empty table cells are hidden.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests grouping by a field.
|
||||
*/
|
||||
public function testGrouping() {
|
||||
/** @var \Drupal\views\ViewEntityInterface $view */
|
||||
$view = \Drupal::entityTypeManager()->getStorage('view')->load('test_table');
|
||||
// Get a reference to the display configuration so we can alter some
|
||||
// specific style options.
|
||||
$display = &$view->getDisplay('default');
|
||||
// Set job as the grouping field.
|
||||
$display['display_options']['style']['options']['grouping'][0] = array(
|
||||
'field' => 'job',
|
||||
'rendered' => TRUE,
|
||||
'rendered_strip' => FALSE,
|
||||
);
|
||||
// Clear the caption text, the rendered job field will be used as a caption.
|
||||
$display['display_options']['style']['options']['caption'] = '';
|
||||
$display['display_options']['style']['options']['summary'] = '';
|
||||
$display['display_options']['style']['options']['description'] = '';
|
||||
$view->save();
|
||||
|
||||
// Add a record containing unsafe markup to be sure it's filtered out.
|
||||
$unsafe_markup = '<script>alert("Rapper");</script>';
|
||||
$unsafe_markup_data = array(
|
||||
'name' => 'Marshall',
|
||||
'age' => 42,
|
||||
'job' => $unsafe_markup,
|
||||
'created' => gmmktime(0, 0, 0, 2, 15, 2001),
|
||||
'status' => 1,
|
||||
);
|
||||
$database = $this->container->get('database');
|
||||
$database->insert('views_test_data')
|
||||
->fields(array_keys($unsafe_markup_data))
|
||||
->values($unsafe_markup_data)
|
||||
->execute();
|
||||
|
||||
$this->drupalGet('test-table');
|
||||
$expected_captions = array(
|
||||
'Job: Speaker',
|
||||
'Job: Songwriter',
|
||||
'Job: Drummer',
|
||||
'Job: Singer',
|
||||
'Job: ' . $unsafe_markup,
|
||||
);
|
||||
|
||||
// Ensure that we don't find the caption containing unsafe markup.
|
||||
$this->assertNoRaw($unsafe_markup, "Didn't find caption containing unsafe markup.");
|
||||
|
||||
// Ensure that all expected captions are found.
|
||||
foreach ($expected_captions as $raw_caption) {
|
||||
$this->assertEscaped($raw_caption);
|
||||
}
|
||||
|
||||
$display = &$view->getDisplay('default');
|
||||
// Remove the label from the grouping field.
|
||||
$display['display_options']['fields']['job']['label'] = '';
|
||||
$view->save();
|
||||
|
||||
$this->drupalGet('test-table');
|
||||
$expected_captions = array(
|
||||
'Speaker',
|
||||
'Songwriter',
|
||||
'Drummer',
|
||||
'Singer',
|
||||
$unsafe_markup,
|
||||
);
|
||||
|
||||
// Ensure that we don't find the caption containing unsafe markup.
|
||||
$this->assertNoRaw($unsafe_markup, "Didn't find caption containing unsafe markup.");
|
||||
|
||||
// Ensure that all expected captions are found.
|
||||
foreach ($expected_captions as $raw_caption) {
|
||||
$this->assertEscaped($raw_caption);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ trait ViewResultAssertionTrait {
|
|||
|
||||
$this->verbose('<pre style="white-space: pre-wrap;">'
|
||||
. "\n\nQuery:\n" . $view->build_info['query']
|
||||
. "\n\nQuery arguments:\n" . var_export($view->build_info['query_args'], TRUE)
|
||||
. "\n\nQuery arguments:\n" . var_export($view->build_info['query']->getArguments(), TRUE)
|
||||
. "\n\nActual result:\n" . var_export($result, TRUE)
|
||||
. "\n\nExpected result:\n" . var_export($expected_result, TRUE));
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ class TaggedWithTest extends WizardTestBase {
|
|||
$this->nodeTypeWithoutTags = $this->drupalCreateContentType();
|
||||
|
||||
// Create the vocabulary for the tag field.
|
||||
$this->tagVocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
$this->tagVocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => 'Views testing tags',
|
||||
'vid' => 'views_testing_tags',
|
||||
));
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: test_area_messages
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: views_test_data
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
defaults:
|
||||
fields: false
|
||||
pager: false
|
||||
sorts: false
|
||||
fields:
|
||||
id:
|
||||
field: id
|
||||
id: id
|
||||
relationship: none
|
||||
table: views_test_data
|
||||
plugin_id: numeric
|
||||
pager:
|
||||
options:
|
||||
offset: 0
|
||||
type: none
|
||||
sorts:
|
||||
id:
|
||||
field: id
|
||||
id: id
|
||||
order: ASC
|
||||
relationship: none
|
||||
table: views_test_data
|
||||
plugin_id: numeric
|
||||
empty:
|
||||
title:
|
||||
field: title
|
||||
id: title
|
||||
table: views
|
||||
plugin_id: title
|
||||
title: test_title_empty
|
||||
header:
|
||||
messages:
|
||||
id: messages
|
||||
table: views
|
||||
field: messages
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
empty: true
|
||||
plugin_id: messages
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
|
@ -0,0 +1,23 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: test_preview_error
|
||||
label: test_preview_error
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: views_test_data
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: null
|
||||
display_options:
|
||||
row:
|
||||
type: fields
|
||||
fields: { }
|
|
@ -108,7 +108,7 @@ display:
|
|||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
empty_column: true
|
||||
responsive: ''
|
||||
default: id
|
||||
empty_table: true
|
||||
|
|
|
@ -135,7 +135,7 @@ class ResultTest extends UnitTestCase {
|
|||
$this->view->pager = $pager;
|
||||
$this->view->style_plugin = new \stdClass();
|
||||
$this->view->total_rows = 100;
|
||||
$this->view->result = array(1,2,3,4,5);
|
||||
$this->view->result = array(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -260,6 +260,44 @@ class PathPluginBaseTest extends UnitTestCase {
|
|||
$this->assertSame($collection->get('test_route_2'), $route_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the alter route method with preexisting title callback.
|
||||
*/
|
||||
public function testAlterRouteWithAlterCallback() {
|
||||
$collection = new RouteCollection();
|
||||
$collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content', '_title_callback' => '\Drupal\Tests\views\Unit\Plugin\display\TestController::testTitle')));
|
||||
$route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content'));
|
||||
$collection->add('test_route_2', $route_2);
|
||||
|
||||
list($view) = $this->setupViewExecutableAccessPlugin();
|
||||
|
||||
$display = array();
|
||||
$display['display_plugin'] = 'page';
|
||||
$display['id'] = 'page_1';
|
||||
$display['display_options'] = array(
|
||||
'path' => 'test_route',
|
||||
);
|
||||
$this->pathPlugin->initDisplay($view, $display);
|
||||
|
||||
$view_route_names = $this->pathPlugin->alterRoutes($collection);
|
||||
$this->assertEquals(array('test_id.page_1' => 'test_route'), $view_route_names);
|
||||
|
||||
// Ensure that the test_route is overridden.
|
||||
$route = $collection->get('test_route');
|
||||
$this->assertTrue($route instanceof Route);
|
||||
$this->assertEquals('test_id', $route->getDefault('view_id'));
|
||||
$this->assertEquals('\Drupal\Tests\views\Unit\Plugin\display\TestController::testTitle', $route->getDefault('_title_callback'));
|
||||
$this->assertEquals('page_1', $route->getDefault('display_id'));
|
||||
$this->assertEquals('my views title', $route->getDefault('_title'));
|
||||
|
||||
// Ensure that the test_route_2 is not overridden.
|
||||
$route = $collection->get('test_route_2');
|
||||
$this->assertTrue($route instanceof Route);
|
||||
$this->assertFalse($route->hasDefault('view_id'));
|
||||
$this->assertFalse($route->hasDefault('display_id'));
|
||||
$this->assertSame($collection->get('test_route_2'), $route_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the collectRoutes method with a path containing named parameters.
|
||||
*
|
||||
|
@ -457,3 +495,20 @@ class PathPluginBaseTest extends UnitTestCase {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A page controller for use by tests in this file.
|
||||
*/
|
||||
class TestController {
|
||||
|
||||
/**
|
||||
* A page title callback.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function testTitle() {
|
||||
return 'Test title';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class EntityOperationsUnitTest extends UnitTestCase {
|
|||
*
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function setUp() {
|
||||
protected function setUp() {
|
||||
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ class ViewsHandlerManagerTest extends UnitTestCase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
protected function setUp() {
|
||||
$this->viewsData = $this->getMockBuilder('Drupal\views\ViewsData')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Database\Query\AlterableInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
@ -804,3 +805,18 @@ function views_local_tasks_alter(&$local_tasks) {
|
|||
$local_task = ViewsLocalTask::create($container, 'views_view');
|
||||
$local_task->alterLocalTasks($local_tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete().
|
||||
*/
|
||||
function views_view_delete(EntityInterface $entity) {
|
||||
// Rebuild the routes in case there is a routed display.
|
||||
$executable = Views::executableFactory()->get($entity);
|
||||
$executable->initDisplay();
|
||||
foreach ($executable->displayHandlers as $display) {
|
||||
if ($display->getRoutedDisplay()) {
|
||||
\Drupal::service('router.builder')->setRebuildNeeded();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -547,18 +547,18 @@ function template_preprocess_views_view_table(&$variables) {
|
|||
$column_reference['attributes'] = new Attribute($column_reference['attributes']);
|
||||
}
|
||||
|
||||
// Remove columns if the option is hide empty column is checked and the
|
||||
// field is not empty.
|
||||
// Remove columns if the "empty_column" option is checked and the
|
||||
// field is empty.
|
||||
if (!empty($options['info'][$field]['empty_column'])) {
|
||||
$empty = TRUE;
|
||||
foreach ($variables['rows'] as $columns) {
|
||||
$empty &= empty($columns[$column]);
|
||||
$empty &= empty($columns['columns'][$column]['content']);
|
||||
}
|
||||
if ($empty) {
|
||||
foreach ($variables['rows'] as &$column_items) {
|
||||
unset($column_items[$column]);
|
||||
unset($variables['header'][$column]);
|
||||
unset($column_items['columns'][$column]);
|
||||
}
|
||||
unset($variables['header'][$column]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -598,6 +598,10 @@ function template_preprocess_views_view_table(&$variables) {
|
|||
$variables['caption'] = ['#markup' => $handler->options['caption']];
|
||||
$variables['caption_needed'] = TRUE;
|
||||
}
|
||||
elseif (!empty($variables['title'])) {
|
||||
$variables['caption'] = ['#markup' => $variables['title']];
|
||||
$variables['caption_needed'] = TRUE;
|
||||
}
|
||||
else {
|
||||
$variables['caption'] = '';
|
||||
$variables['caption_needed'] = FALSE;
|
||||
|
|
Reference in a new issue