Update to Drupal 8.2.2. For more information, see https://www.drupal.org/project/drupal/releases/8.2.2

This commit is contained in:
Pantheon Automation 2016-11-02 11:43:31 -07:00 committed by Greg Anderson
parent 23ffed3665
commit 507b45a0ed
378 changed files with 11434 additions and 5542 deletions

View file

@ -53,9 +53,11 @@
Drupal.Views.parseViewArgs = function (href, viewPath) {
var returnObj = {};
var path = Drupal.Views.getPath(href);
// Get viewPath url without baseUrl portion.
var viewHref = Drupal.url(viewPath).substring(drupalSettings.path.baseUrl.length);
// Ensure we have a correct path.
if (viewPath && path.substring(0, viewPath.length + 1) === viewPath + '/') {
returnObj.view_args = decodeURIComponent(path.substring(viewPath.length + 1, path.length));
if (viewHref && path.substring(0, viewHref.length + 1) === viewHref + '/') {
returnObj.view_args = decodeURIComponent(path.substring(viewHref.length + 1, path.length));
returnObj.view_path = path;
}
return returnObj;

View file

@ -298,7 +298,7 @@ class ManyToOneHelper {
}
$placeholders = array(
$placeholder => $value,
) + $this->placeholders;
);
$this->handler->query->addWhereExpression($options['group'], "$field $operator", $placeholders);
}
else {

View file

@ -1271,7 +1271,20 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
// @todo Views should expect and store a leading /. See
// https://www.drupal.org/node/2423913.
$more_link = ' ' . $this->linkGenerator()->generate($more_link_text, CoreUrl::fromUserInput('/' . $more_link_path, array('attributes' => array('class' => array('views-more-link')))));
$options = array(
'attributes' => array(
'class' => array(
'views-more-link',
),
),
);
if (UrlHelper::isExternal($more_link_path)) {
$more_link_url = CoreUrl::fromUri($more_link_path, $options);
}
else {
$more_link_url = CoreUrl::fromUserInput('/' . $more_link_path, $options);
}
$more_link = ' ' . $this->linkGenerator()->generate($more_link_text, $more_link_url);
}
}

View file

@ -3,6 +3,7 @@
namespace Drupal\views\Plugin\views\filter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Database\Database;
/**
* Filter handler which allows to search on multiple fields.
@ -138,6 +139,40 @@ class Combine extends StringFilter {
$this->query->addWhereExpression($this->options['group'], "$expression LIKE $placeholder", array($placeholder => '%' . db_like($this->value) . '%'));
}
/**
* Filters by one or more words.
*
* By default opContainsWord uses add_where, that doesn't support complex
* expressions.
*
* @param string $expression
*/
protected function opContainsWord($expression) {
$placeholder = $this->placeholder();
// Don't filter on empty strings.
if (empty($this->value)) {
return;
}
// Match all words separated by spaces or sentences encapsulated by double
// quotes.
preg_match_all(static::WORDS_PATTERN, ' ' . $this->value, $matches, PREG_SET_ORDER);
// Switch between the 'word' and 'allwords' operator.
$type = $this->operator == 'word' ? 'OR' : 'AND';
$group = $this->query->setWhereGroup($type);
$operator = Database::getConnection()->mapConditionOperator('LIKE');
$operator = isset($operator['operator']) ? $operator['operator'] : 'LIKE';
foreach ($matches as $match_key => $match) {
$temp_placeholder = $placeholder . '_' . $match_key;
// Clean up the user input and remove the sentence delimiters.
$word = trim($match[2], ',?!();:-"');
$this->query->addWhereExpression($group, "$expression $operator $temp_placeholder", array($temp_placeholder => '%' . Database::getConnection()->escapeLike($word) . '%'));
}
}
protected function opStartsWith($expression) {
$placeholder = $this->placeholder();
$this->query->addWhereExpression($this->options['group'], "$expression LIKE $placeholder", array($placeholder => db_like($this->value) . '%'));

View file

@ -14,6 +14,11 @@ use Drupal\Core\Form\FormStateInterface;
*/
class StringFilter extends FilterPluginBase {
/**
* All words separated by spaces or sentences encapsulated by double quotes.
*/
const WORDS_PATTERN = '/ (-?)("[^"]+"|[^" ]+)/i';
// exposed filter options
protected $alwaysMultiple = TRUE;
@ -267,7 +272,7 @@ class StringFilter extends FilterPluginBase {
return;
}
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
preg_match_all(static::WORDS_PATTERN, ' ' . $this->value, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$phrase = FALSE;
// Strip off phrase quotes

View file

@ -47,14 +47,17 @@ class Mini extends SqlBase {
public function query() {
parent::query();
// Don't query for the next page if we have a pager that has a limited
// amount of pages.
if ($this->getItemsPerPage() > 0 && (empty($this->options['total_pages']) || ($this->getCurrentPage() < $this->options['total_pages']))) {
// Increase the items in the query in order to be able to find out whether
// there is another page.
$limit = $this->view->query->getLimit();
$limit += 1;
$this->view->query->setLimit($limit);
// Only modify the query if we don't want to do a total row count
if (!$this->view->get_total_rows) {
// Don't query for the next page if we have a pager that has a limited
// amount of pages.
if ($this->getItemsPerPage() > 0 && (empty($this->options['total_pages']) || ($this->getCurrentPage() < $this->options['total_pages']))) {
// Increase the items in the query in order to be able to find out
// whether there is another page.
$limit = $this->view->query->getLimit();
$limit += 1;
$this->view->query->setLimit($limit);
}
}
}
@ -69,19 +72,16 @@ class Mini extends SqlBase {
* {@inheritdoc}
*/
public function postExecute(&$result) {
// In query() one more item might have been retrieved than necessary. If so,
// the next link needs to be displayed and the item removed.
if ($this->getItemsPerPage() > 0 && count($result) > $this->getItemsPerPage()) {
array_pop($result);
// Make sure the pager shows the next link by setting the total items to
// the biggest possible number but prevent failing calculations like
// ceil(PHP_INT_MAX) we take PHP_INT_MAX / 2.
$total = PHP_INT_MAX / 2;
// Only modify the result if we didn't do a total row count
if (!$this->view->get_total_rows) {
$this->total_items = $this->getCurrentPage() * $this->getItemsPerPage() + count($result);
// query() checks if we need a next link by setting limit 1 record past
// this page If we got the extra record we need to remove it before we
// render the result.
if ($this->getItemsPerPage() > 0 && count($result) > $this->getItemsPerPage()) {
array_pop($result);
}
}
else {
$total = $this->getCurrentPage() * $this->getItemsPerPage() + count($result);
}
$this->total_items = $total;
}
/**

View file

@ -70,6 +70,18 @@ class MiniPagerTest extends PluginTestBase {
$this->assertText($this->nodes[18]->label());
$this->assertText($this->nodes[19]->label());
// Test @total value in result summary
$view = Views::getView('test_mini_pager');
$view->setDisplay('page_4');
$this->executeView($view);
$this->assertIdentical($view->get_total_rows, TRUE, 'The query was set to calculate the total number of rows.');
$this->assertEqual(count($this->nodes), $view->total_rows, 'The total row count is equal to the number of nodes.');
$this->drupalGet('test_mini_pager_total', array('query' => array('page' => 1)));
$this->assertText('of ' . count($this->nodes), 'The first page shows the total row count.');
$this->drupalGet('test_mini_pager_total', array('query' => array('page' => 6)));
$this->assertText('of ' . count($this->nodes), 'The last page shows the total row count.');
// Test a mini pager with just one item per page.
$this->drupalGet('test_mini_pager_one');
$this->assertText('');

View file

@ -0,0 +1,58 @@
<?php
namespace Drupal\views\Tests;
use Drupal\taxonomy\Tests\TaxonomyTestTrait;
/**
* Tests glossary functionality of taxonomy views.
*
* @group views
*/
class TaxonomyGlossaryTest extends ViewTestBase {
use TaxonomyTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['taxonomy'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_taxonomy_glossary'];
/**
* Taxonomy terms used by this test.
*
* @var \Drupal\taxonomy\Entity\Term[]
*/
protected $taxonomyTerms;
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
$vocabulary = $this->createVocabulary();
for ($i = 0; $i < 10; $i++) {
$this->taxonomyTerms[] = $this->createTerm($vocabulary);
}
}
/**
* Tests a taxonomy glossary view.
*/
public function testTaxonomyGlossaryView() {
// Go the taxonomy glossary page for the first term.
$this->drupalGet('test_taxonomy_glossary/' . substr($this->taxonomyTerms[0]->getName(), 0, 1));
$this->assertText($this->taxonomyTerms[0]->getName());
}
}

View file

@ -5,56 +5,450 @@ dependencies:
- node
- user
id: test_glossary
label: test_glossary
module: views
description: ''
label: Test glossary
module: node
description: 'All content, by letter.'
tag: default
base_table: node_field_data
base_field: nid
core: '8'
display:
default:
id: default
display_title: Master
display_plugin: default
position: 0
display_options:
access:
type: perm
arguments:
title:
default_argument_type: fixed
field: title
glossary: true
id: title
limit: 1
summary:
format: default_summary
number_of_records: 0
summary_options:
items_per_page: 25
table: node_field_data
plugin_id: string
entity_type: node
entity_field: title
cache:
type: tag
exposed_form:
type: basic
fields:
title:
field: title
id: title
label: ''
table: node_field_data
plugin_id: field
entity_type: node
entity_field: title
pager:
type: full
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
use_ajax: true
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 36
offset: 0
id: 0
total_pages: 0
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
fields:
title:
id: title
table: node_field_data
field: title
plugin_id: field
relationship: none
group_type: group
admin_label: ''
label: Title
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
entity_type: node
entity_field: title
name:
id: name
table: users_field_data
field: name
label: Author
relationship: uid
plugin_id: field
type: user_name
group_type: group
admin_label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
entity_type: user
entity_field: name
changed:
id: changed
table: node_field_data
field: changed
label: 'Last update'
type: timestamp
settings:
date_format: long
custom_date_format: ''
timezone: ''
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
entity_type: node
entity_field: changed
arguments:
title:
id: title
table: node_field_data
field: title
default_action: default
exception:
title_enable: true
default_argument_type: fixed
default_argument_options:
argument: a
summary:
format: default_summary
specify_validation: true
glossary: true
limit: 1
case: upper
path_case: lower
transform_dash: false
plugin_id: string
relationship: none
group_type: group
admin_label: ''
title_enable: false
title: ''
default_argument_skip_url: false
summary_options: { }
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
entity_type: node
entity_field: title
relationships:
uid:
id: uid
table: node_field_data
field: uid
plugin_id: standard
relationship: none
group_type: group
admin_label: author
required: false
style:
type: default
type: table
options:
columns:
title: title
name: name
changed: changed
default: title
info:
title:
sortable: true
separator: ''
name:
sortable: true
separator: ''
changed:
sortable: true
separator: ''
override: true
sticky: false
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
order: asc
summary: ''
empty_table: false
row:
type: fields
display_plugin: default
display_title: Master
id: default
position: 0
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
header: { }
footer: { }
empty: { }
sorts: { }
filters:
langcode:
id: langcode
table: node_field_data
field: langcode
relationship: none
group_type: group
admin_label: ''
operator: in
value:
'***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: language
entity_type: node
entity_field: langcode
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: 0
tags: { }
attachment_1:
id: attachment_1
display_title: Attachment
display_plugin: attachment
position: 2
display_options:
query:
type: views_query
options: { }
pager:
type: none
options:
offset: 0
items_per_page: 0
defaults:
arguments: false
arguments:
title:
id: title
table: node_field_data
field: title
default_action: summary
exception:
title_enable: true
default_argument_type: fixed
default_argument_options:
argument: a
summary:
format: unformatted_summary
summary_options:
items_per_page: 25
inline: true
separator: ' | '
specify_validation: true
glossary: true
limit: 1
case: upper
path_case: lower
transform_dash: false
plugin_id: string
relationship: none
group_type: group
admin_label: ''
title_enable: false
title: ''
default_argument_skip_url: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
entity_type: node
entity_field: title
displays:
default: default
page_1: page_1
inherit_arguments: false
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: 0
tags: { }
page_1:
id: page_1
display_title: Page
display_plugin: page
position: 1
display_options:
query:
type: views_query
options: { }
path: test-glossary
menu:
type: normal
title: Glossary
weight: 0
menu_name: main
parent: ''
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
max-age: 0
tags: { }

View file

@ -0,0 +1,53 @@
langcode: en
status: true
dependencies: { }
id: test_invalid_tokens
label: 'Test invalid tokens'
module: views
description: 'Test view to invalid token replacement tests.'
tag: ''
base_table: views_test_data
base_field: id
core: 8.x
display:
default:
display_options:
title: 'Test token default'
defaults:
fields: false
pager: false
sorts: false
fields:
age:
field: age
id: age
relationship: none
table: views_test_data
plugin_id: numeric
id:
field: id
id: id
relationship: none
table: views_test_data
plugin_id: numeric
name:
field: name
id: name
relationship: none
table: views_test_data
plugin_id: string
pager:
type: full
options:
items_per_page: 10
display_plugin: default
display_title: Master
id: default
position: 0
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View file

@ -118,3 +118,26 @@ display:
type: mini
options:
items_per_page: 0
page_4:
display_plugin: page
id: page_4
display_title: 'Page 4'
position: 4
display_options:
display_extenders: { }
path: test_mini_pager_total
empty: { }
defaults:
empty: false
header: false
header:
result:
id: result
table: views
field: result
relationship: none
group_type: group
admin_label: ''
empty: false
content: 'Displaying @start - @end of @total'
plugin_id: result

View file

@ -0,0 +1,191 @@
langcode: en
status: true
dependencies:
module:
- taxonomy
id: test_taxonomy_glossary
label: test_taxonomy_glossary
module: views
description: ''
tag: ''
base_table: taxonomy_term_field_data
base_field: tid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: none
options: { }
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
row:
type: fields
fields:
name:
id: name
table: taxonomy_term_field_data
field: name
entity_type: taxonomy_term
entity_field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
type: string
settings:
link_to_entity: true
plugin_id: term_name
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
convert_spaces: false
filters: { }
sorts: { }
title: test_taxonomy_glossary
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
name:
id: name
table: taxonomy_term_field_data
field: name
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
glossary: true
limit: 1
case: none
path_case: none
transform_dash: false
break_phrase: false
add_table: false
require_value: false
entity_type: taxonomy_term
entity_field: name
plugin_id: string
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
tags: { }
page_1:
display_plugin: page
id: page_1
display_title: Page
position: 1
display_options:
display_extenders: { }
path: test_taxonomy_glossary
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
tags: { }

View file

@ -0,0 +1,129 @@
<?php
namespace Drupal\Tests\views\FunctionalJavascript;
use Drupal\Core\Url;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\ContentTypeCreationTrait;
use Drupal\simpletest\NodeCreationTrait;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the basic AJAX functionality of the Glossary View.
*
* @group node
*/
class GlossaryViewTest extends JavascriptTestBase {
use ContentTypeCreationTrait;
use NodeCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'node', 'views', 'views_test_config'];
/**
* @var array
* The test Views to enable.
*/
public static $testViews = ['test_glossary'];
/**
* @var
* The additional language to use.
*/
protected $language;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
ViewTestData::createTestViews(get_class($this), array('views_test_config'));
// Create a Content type and some test nodes with titles that start with
// different letters.
$this->createContentType(['type' => 'page']);
$titles = [
'Page One',
'Page Two',
'Another page',
];
foreach ($titles as $title) {
$this->createNode([
'title' => $title,
'language' => 'en',
]);
$this->createNode([
'title' => $title,
'language' => 'nl',
]);
}
// Create a user privileged enough to use exposed filters and view content.
$user = $this->drupalCreateUser([
'administer site configuration',
'access content',
'access content overview',
]);
$this->drupalLogin($user);
}
/**
* Tests the AJAX callbacks for the glossary view.
*/
public function testGlossaryDefault() {
// Visit the default Glossary page.
$url = Url::fromRoute('view.test_glossary.page_1');
$this->drupalGet($url);
$session = $this->getSession();
$web_assert = $this->assertSession();
$page = $session->getPage();
$rows = $page->findAll('css', '.view-test-glossary tr');
// We expect 2 rows plus the header row.
$this->assertCount(3, $rows);
// Click on the P link, this should show 4 rows plus the header row.
$page->clickLink('P');
$web_assert->assertWaitOnAjaxRequest();
$rows = $page->findAll('css', '.view-test-glossary tr');
$this->assertCount(5, $rows);
}
/**
* Test that the glossary also works on a language prefixed URL.
*/
public function testGlossaryLanguagePrefix() {
$this->language = ConfigurableLanguage::createFromLangcode('nl')->save();
$config = $this->config('language.negotiation');
$config->set('url.prefixes', ['en' => 'en', 'nl' => 'nl'])
->save();
\Drupal::service('kernel')->rebuildContainer();
$url = Url::fromRoute('view.test_glossary.page_1');
$this->drupalGet($url);
$session = $this->getSession();
$web_assert = $this->assertSession();
$page = $session->getPage();
$rows = $page->findAll('css', '.view-test-glossary tr');
// We expect 2 rows plus the header row.
$this->assertCount(3, $rows);
// Click on the P link, this should show 4 rows plus the header row.
$page->clickLink('P');
$web_assert->assertWaitOnAjaxRequest();
$rows = $page->findAll('css', '.view-test-glossary tr');
$this->assertCount(5, $rows);
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace Drupal\Tests\views\FunctionalJavascript\Plugin\views\Handler;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the contextual filter handler UI.
*
* @group views
*/
class ContextualFilterTest extends JavascriptTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'views', 'views_ui', 'views_test_config'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_field_body'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
ViewTestData::createTestViews(get_class($this), ['views_test_config']);
// Disable automatic live preview to make the sequence of calls clearer.
\Drupal::configFactory()->getEditable('views.settings')->set('ui.show.advanced_column', TRUE)->save();
$account = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($account);
}
/**
* Test adding a contextual filter handler through the UI.
*/
public function testAddContextualFilterUI() {
$web_assert = $this->assertSession();
$this->drupalGet('/admin/structure/views/view/test_field_body');
$web_assert->assertWaitOnAjaxRequest();
$page = $this->getSession()->getPage();
$page->clickLink('views-add-argument');
$web_assert->assertWaitOnAjaxRequest();
$page->checkField('name[node_field_data.nid]');
$add_button = $page->find('css', '.ui-dialog-buttonset .button--primary');
$add_button->click();
$web_assert->assertWaitOnAjaxRequest();
$page->fillField('options[default_action]', 'default');
$page->selectFieldOption('options[default_argument_type]', 'node');
$add_button = $page->find('css', '.ui-dialog-buttonset .button--primary');
$add_button->click();
$web_assert->assertWaitOnAjaxRequest();
$page->pressButton('edit-actions-submit');
$web_assert->assertWaitOnAjaxRequest();
$page->clickLink('Content: ID');
// Check that the dialog opens.
$web_assert->assertWaitOnAjaxRequest();
$page->pressButton('Close');
}
}

View file

@ -95,6 +95,101 @@ class FilterCombineTest extends ViewsKernelTestBase {
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
}
/**
* Tests the Combine field filter with the 'word' operator.
*/
public function testFilterCombineWord() {
$view = Views::getView('test_view');
$view->setDisplay();
$fields = $view->displayHandlers->get('default')->getOption('fields');
$view->displayHandlers->get('default')->overrideOption('fields', $fields + array(
'job' => array(
'id' => 'job',
'table' => 'views_test_data',
'field' => 'job',
'relationship' => 'none',
),
));
// Change the filtering.
$view->displayHandlers->get('default')->overrideOption('filters', array(
'age' => array(
'id' => 'combine',
'table' => 'views',
'field' => 'combine',
'relationship' => 'none',
'operator' => 'word',
'fields' => array(
'name',
'job',
),
'value' => 'singer ringo',
),
));
$this->executeView($view);
$resultset = array(
array(
'name' => 'John',
'job' => 'Singer',
),
array(
'name' => 'George',
'job' => 'Singer',
),
array(
'name' => 'Ringo',
'job' => 'Drummer',
),
);
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
}
/**
* Tests the Combine field filter with the 'allwords' operator.
*/
public function testFilterCombineAllWords() {
$view = Views::getView('test_view');
$view->setDisplay();
$fields = $view->displayHandlers->get('default')->getOption('fields');
$view->displayHandlers->get('default')->overrideOption('fields', $fields + array(
'job' => array(
'id' => 'job',
'table' => 'views_test_data',
'field' => 'job',
'relationship' => 'none',
),
));
// Set the filtering to allwords and simulate searching for a phrase.
$view->displayHandlers->get('default')->overrideOption('filters', array(
'age' => array(
'id' => 'combine',
'table' => 'views',
'field' => 'combine',
'relationship' => 'none',
'operator' => 'allwords',
'fields' => array(
'name',
'job',
'age',
),
'value' => '25 "john singer"',
),
));
$this->executeView($view);
$resultset = array(
array(
'name' => 'John',
'job' => 'Singer',
),
);
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
}
/**
* Tests if the filter can handle removed fields.
*

View file

@ -19,7 +19,7 @@ class TokenReplaceTest extends ViewsKernelTestBase {
*
* @var array
*/
public static $testViews = array('test_tokens');
public static $testViews = array('test_tokens', 'test_invalid_tokens');
protected function setUp($import_test_views = TRUE) {
parent::setUp();
@ -90,4 +90,23 @@ class TokenReplaceTest extends ViewsKernelTestBase {
}
}
/**
* Tests path token replacements generated from a view without a path.
*/
function testTokenReplacementNoPath() {
$token_handler = \Drupal::token();
$view = Views::getView('test_invalid_tokens');
$view->setDisplay('block_1');
$this->executeView($view);
$expected = array(
'[view:url]' => '',
);
foreach ($expected as $token => $expected_output) {
$output = $token_handler->replace($token, array('view' => $view));
$this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token)));
}
}
}

View file

@ -227,11 +227,17 @@ class FieldPluginBaseTest extends UnitTestCase {
}
/**
* Test rendering as a link without a path.
* Test rendering with a more link.
*
* @param string $path
* An internal or external path.
* @param string $url
* The final url used by the more link.
*
* @dataProvider providerTestRenderTrimmedWithMoreLinkAndPath
* @covers ::renderText
*/
public function testRenderTrimmedWithMoreLink() {
public function testRenderTrimmedWithMoreLinkAndPath($path, $url) {
$alter = [
'trim' => TRUE,
'max_length' => 7,
@ -239,6 +245,7 @@ class FieldPluginBaseTest extends UnitTestCase {
// Don't invoke translation.
'ellipsis' => FALSE,
'more_link_text' => 'more link',
'more_link_path' => $path,
];
$this->display->expects($this->any())
@ -253,11 +260,38 @@ class FieldPluginBaseTest extends UnitTestCase {
$field->field_alias = 'key';
$row = new ResultRow(['key' => 'a long value']);
$expected_result = 'a long <a href="/%3Cfront%3E" class="views-more-link">more link</a>';
$expected_result = 'a long <a href="' . $url . '" class="views-more-link">more link</a>';
$result = $field->advancedRender($row);
$this->assertEquals($expected_result, $result);
}
/**
* Data provider for ::testRenderTrimmedWithMoreLinkAndPath().
*
* @return array
* Test data.
*/
public function providerTestRenderTrimmedWithMoreLinkAndPath() {
$data = [];
// Simple path with default options.
$data[] = ['test-path', '/test-path'];
// Add a fragment.
$data[] = ['test-path#test', '/test-path#test'];
// Query specified as part of the path.
$data[] = ['test-path?foo=bar', '/test-path?foo=bar'];
// Empty path.
$data[] = ['', '/%3Cfront%3E'];
// Front page path.
$data[] = ['<front>', '/%3Cfront%3E'];
// External URL.
$data[] = ['https://www.drupal.org', 'https://www.drupal.org'];
$data[] = ['http://www.drupal.org', 'http://www.drupal.org'];
$data[] = ['www.drupal.org', '/www.drupal.org'];
return $data;
}
/**
* Tests the "No results text" rendering.
*

View file

@ -101,9 +101,15 @@ function views_tokens($type, $tokens, array $data, array $options, BubbleableMet
break;
case 'url':
if ($url = $view->getUrl()) {
$replacements[$original] = $url->setOptions($url_options)
->toString();
try {
if ($url = $view->getUrl()) {
$replacements[$original] = $url->setOptions($url_options)
->toString();
}
}
catch (\InvalidArgumentException $e) {
// The view has no URL so we leave the value empty.
$replacements[$original] = '';
}
break;
case 'base-table':