From d907b8b166e55fc62c2110893f12f65190c6331b Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Tue, 2 Feb 2016 12:08:20 +0000 Subject: [PATCH] Issue #2567339 by Lendude, penyaskito, dawehner: PHP Warning when using link field tokens in a view --- .../src/Tests/Views/LinkViewsTokensTest.php | 102 +++++++++ .../link_test_views/link_test_views.info.yml | 10 + .../views.view.test_link_tokens.yml | 206 ++++++++++++++++++ .../views/src/Plugin/views/field/Field.php | 7 +- 4 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 core/modules/link/src/Tests/Views/LinkViewsTokensTest.php create mode 100644 core/modules/link/tests/modules/link_test_views/link_test_views.info.yml create mode 100644 core/modules/link/tests/modules/link_test_views/test_views/views.view.test_link_tokens.yml diff --git a/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php b/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php new file mode 100644 index 000000000000..558c5a8504cc --- /dev/null +++ b/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php @@ -0,0 +1,102 @@ +<?php + +/** + * @file + * Contains \Drupal\link\Tests\Views\LinkViewsTokensTest. + */ + +namespace Drupal\link\Tests\Views; + +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\views\Tests\ViewTestBase; +use Drupal\views\Tests\ViewTestData; + +/** + * Tests the views integration for link tokens. + * + * @group link + */ +class LinkViewsTokensTest extends ViewTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['link_test_views']; + + /** + * Views used by this test. + * + * @var array + */ + public static $testViews = ['test_link_tokens']; + + /** + * The field name used for the link field. + * + * @var string + */ + protected $fieldName = 'field_link'; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + ViewTestData::createTestViews(get_class($this), array('link_test_views')); + + // Create Basic page node type. + $this->drupalCreateContentType(array( + 'type' => 'page', + 'name' => 'Basic page' + )); + + // Create a field. + FieldStorageConfig::create(array( + 'field_name' => $this->fieldName, + 'type' => 'link', + 'entity_type' => 'node', + 'cardinality' => 1, + ))->save(); + FieldConfig::create(array( + 'field_name' => $this->fieldName, + 'entity_type' => 'node', + 'bundle' => 'page', + 'label' => 'link field', + ))->save(); + + } + + public function testLinkViewsTokens() { + // Array of URI's to test. + $uris = [ + 'http://www.drupal.org' => 'Drupal.org', + ]; + + // Add nodes with the URI's and titles. + foreach ($uris as $uri => $title) { + $values = array('type' => 'page'); + $values[$this->fieldName][] = ['uri' => $uri, 'title' => $title, 'options' => ['attributes' => ['class' => 'test-link-class']]]; + $this->drupalCreateNode($values); + } + + $this->drupalGet('test_link_tokens'); + + foreach ($uris as $uri => $title) { + // Formatted link: {{ field_link }}<br /> + $this->assertRaw("Formated: <a href=\"$uri\" class=\"test-link-class\">$title</a>"); + + // Raw uri: {{ field_link__uri }}<br /> + $this->assertRaw("Raw uri: $uri"); + + // Raw title: {{ field_link__title }}<br /> + $this->assertRaw("Raw title: $title"); + + // Raw options: {{ field_link__options }}<br /> + // Options is an array and should return empty after token replace. + $this->assertRaw("Raw options: ."); + } + } +} diff --git a/core/modules/link/tests/modules/link_test_views/link_test_views.info.yml b/core/modules/link/tests/modules/link_test_views/link_test_views.info.yml new file mode 100644 index 000000000000..235ecb693719 --- /dev/null +++ b/core/modules/link/tests/modules/link_test_views/link_test_views.info.yml @@ -0,0 +1,10 @@ +name: 'Link test views' +type: module +description: 'Provides default views for views link tests.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - node + - views + - link diff --git a/core/modules/link/tests/modules/link_test_views/test_views/views.view.test_link_tokens.yml b/core/modules/link/tests/modules/link_test_views/test_views/views.view.test_link_tokens.yml new file mode 100644 index 000000000000..2372b1251c64 --- /dev/null +++ b/core/modules/link/tests/modules/link_test_views/test_views/views.view.test_link_tokens.yml @@ -0,0 +1,206 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_link + module: + - link + - node + - user +id: test_link_tokens +label: link +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'access content' + 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: full + 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: '‹ previous' + next: 'next ›' + first: '« first' + last: 'last »' + quantity: 9 + style: + type: default + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + fields: + field_link: + id: field_link + table: node__field_link + field: field_link + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: true + text: "Formated: {{ field_link }}<br />\nRaw uri: {{ field_link__uri }}<br />\nRaw title: {{ field_link__title }}<br />\nRaw options: {{ field_link__options }}." + make_link: false + path: '{{ field_link__uri }}' + 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: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: uri + type: link + settings: + trim_length: 80 + url_only: false + url_plain: false + rel: '0' + target: '0' + group_column: '' + 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 + plugin_id: field + filters: + status: + value: true + table: node_field_data + field: status + plugin_id: boolean + entity_type: node + entity_field: status + id: status + expose: + operator: '' + group: 1 + sorts: + created: + id: created + table: node_field_data + field: created + order: DESC + entity_type: node + entity_field: created + plugin_id: date + relationship: none + group_type: group + admin_label: '' + exposed: false + expose: + label: '' + granularity: second + title: link + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + cacheable: false + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: test_link_tokens + cache_metadata: + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + cacheable: false diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index 3da56ab11444..af3dad1269c7 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -22,6 +22,7 @@ use Drupal\Core\Render\Element; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\TypedData\TypedDataInterface; use Drupal\views\FieldAPIHandlerTrait; use Drupal\views\Entity\Render\EntityFieldRenderer; use Drupal\views\Plugin\views\display\DisplayPluginBase; @@ -933,8 +934,10 @@ protected function addSelfTokens(&$tokens, $item) { if (is_object($raw)) { $property = $raw->get($id); - if (!empty($property)) { - $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($property->getValue()); + // Check if TypedDataInterface is implemented so we know how to render + // the item as a string. + if (!empty($property) && $property instanceof TypedDataInterface) { + $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($property->getString()); } else { // Make sure that empty values are replaced as well. -- GitLab