Problem/Motivation

#2404603: Add proper support for Url objects in FieldPluginBase::renderAsLink(), so we can remove EntityInterface::getSystemPath() caused that, because they get encoded, and it only decodes {/} back, but not the contained spaces. Nobody noticed that, because the current views in core with placeholders in paths did not use spaces.

I think that's bad because this is the common form that we use everywhere in actual twig templates, it also break existing views for no good reason (Which is why I found it, was using it in file_entity.module)

Proposed resolution

Also decode '{ ' and ' }', that should cover all variants. An alternative would be a preg_replace(), then we could support an arbitrary amount of spaces.

Remaining tasks

User interface changes

API changes

Comments

berdir’s picture

Status: Active » Needs review
StatusFileSize
new1.73 KB

Here is a patch. It's not pretty, but it seems to work for me.

Also updated one of the two relevant usages in core to use spaces, which should give us minimal test coverage of both cases.

Status: Needs review » Needs work

The last submitted patch, 1: support-spaces-in-twig-placeholders-2426447-1.patch, failed testing.

berdir’s picture

Title: Views no longer supports {{ something }} as twig placeholder, only {{something} » Views no longer supports {{ something }} as twig placeholder for a path, only {{something}
Version: 8.0.0-beta6 » 8.0.x-dev
Status: Needs work » Needs review

it is only broken when used in a path..

Also, wrong version, grml.

oriol_e9g’s picture

--deleted-- wrong comment

berdir’s picture

Here's a test only patch.

@oriol_e9g: You are not wrong, as written in the issue summary, using a preg_replace() to support the pattern and an arbitrary number of spaces might be a good idea, I was just too lazy.

berdir’s picture

Status: Needs review » Needs work

Hm, why did this not fail?

berdir’s picture

Title: Views no longer supports {{ something }} as twig placeholder for a path, only {{something} » Views no longer supports {{ something }} as twig placeholder for a path, only {{something}}
mpdonadio’s picture

Status: Needs work » Needs review

Failing patch using the view/path that I found the problem with.

mpdonadio’s picture

StatusFileSize
new589 bytes

Coffee hasn't kicked in...

mpdonadio’s picture

StatusFileSize
new1.61 KB

Here is a version using preg_replace that passes locally w/ the adjusted path from #10

The last submitted patch, 10: 2426447-09-test-only.patch, failed testing.

mpdonadio’s picture

Dug into why #6 doesn't fail. It has to do with the logic in Url::toUriString().

'node/{{ nid }}' in views.view.test_dropbutton.yml is being parsed as an unrouted URL before token replacement, and since there are no query parameters, nothing is getting escaped, and Twig is happy. See #2426399: FieldPluginBase::renderAsLink() loses language prefix for tokenized paths for a related issue to this.

'admin/content/files/usage/{{ fid }}' in views.view.files.yml is being parsed as a routed URL before token replacement; so the route parameters are being escaped by UrlHelper::buildQuery(), including the spaces (and the escaped spaces are what are causing Twig grief).

berdir’s picture

+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -1330,9 +1330,9 @@ protected function renderAsLink($alter, $text, $tokens) {
-      $path = str_replace(['%7B','%7D'], ['{','}'], $path);
+      $path = preg_replace(['/(\%7B){2}(\%20)+/', '/(\%20)+(\%7D){2}/'], ['{{','}}'], $path);

+ means at least once, wouldn't * be better, to support both both placeholders with spaces and those without?

mpdonadio’s picture

Assigned: Unassigned » mpdonadio
Status: Needs review » Needs work

Yup. Wasn't thinking there. Fixed locally, but I am going to see if I can add some proper unit test coverage for this.

mpdonadio’s picture

Assigned: mpdonadio » Unassigned
Status: Needs work » Needs review
StatusFileSize
new6.93 KB
new6.19 KB

Yikes. Not sure if I went too far (ie, did I make out of scope changes) to get the test coverage to actually work...

dawehner’s picture

  1. +++ b/core/modules/views/src/Plugin/views/PluginBase.php
    @@ -576,4 +582,18 @@ public static function queryLanguageSubstitutions() {
    +  /**
    +   * Returns the render API renderer.
    +   *
    +   * @return \Drupal\Core\Render\RendererInterface
    +   */
    +  protected function getRenderer() {
    +    if (!isset($this->renderer)) {
    +      $this->renderer = \Drupal::service('renderer');
    +    }
    +
    +    return $this->renderer;
    +  }
    +
    

    I'm curios whether we should instead introduce a RendererTrait ? Would that be helpful for other places as well?

  2. +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
    @@ -422,6 +434,65 @@ public function providerTestRenderAsLinkWithUrlAndOptions() {
    +
    +    $data[] = ['test-path/{{foo}}', $tokens, $link_html];
    +    $data[] = ['test-path/{{ foo}}', $tokens, $link_html];
    +    $data[] = ['test-path/{{  foo}}', $tokens, $link_html];
    +    $data[] = ['test-path/{{foo }}', $tokens, $link_html];
    +    $data[] = ['test-path/{{foo  }}', $tokens, $link_html];
    +    $data[] = ['test-path/{{ foo }}', $tokens, $link_html];
    +    $data[] = ['test-path/{{  foo }}', $tokens, $link_html];
    +    $data[] = ['test-path/{{ foo  }}', $tokens, $link_html];
    +    $data[] = ['test-path/{{  foo  }}', $tokens, $link_html];
    

    Nice coverage!

mpdonadio’s picture

Re #17-1, I had to do this for test mocking purposes, and there are one or two other places in core that do this (and a bunch that wrap drupal_render_root). drupal_render() and drupal_render_root() are marked as deprecated, but I am not finding an issue to actually remove them. How about we leave this here, and I open a followup? That way we have an in-scope issue to swap out the duplicate methods / direct calls with the trait. That would put likely put a big dent into the drupal_render() and drupal_render_root() calls in classes where the service isn't injected.

mpdonadio’s picture

+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -104,6 +104,12 @@
+  /**
+   * Stores the render API renderer.
+   *
+   * @var \Drupal\Core\Render\Renderer
+   */
+  protected $renderer;

Should probably be RendererInterface instead. Also in FieldPluginBase.

dawehner’s picture

Re #17-1, I had to do this for test mocking purposes, and there are one or two other places in core that do this (and a bunch that wrap drupal_render_root). drupal_render() and drupal_render_root() are marked as deprecated, but I am not finding an issue to actually remove them. How about we leave this here, and I open a followup? That way we have an in-scope issue to swap out the duplicate methods / direct calls with the trait. That would put likely put a big dent into the drupal_render() and drupal_render_root() calls in classes where the service isn't injected.

Fair, let's go with the follow up.

And yes, we do have an interface for that, so let's use it.

mpdonadio’s picture

StatusFileSize
new7.13 KB
new952 bytes

Updated type hints. This should be good to go.

berdir’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
@@ -147,11 +154,16 @@ protected function setUp() {
+    $this->renderer = $this->getMockBuilder('Drupal\Core\Render\RendererInterface')
+      ->disableOriginalConstructor()
+      ->getMock();

interfaces have no constructor, just getMock('interface...') is enough. not a big issue.

Awesome test coverage!

I think there's nothing left from my original patch, it was been reviewed by @dawehner, so I feel ok with RTBCing this. Thanks!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed d6458cd and pushed to 8.0.x. Thanks!

diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
index 326c43d..4e3bd73 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
@@ -154,9 +154,7 @@ protected function setUp() {
     $this->unroutedUrlAssembler = $this->getMock('Drupal\Core\Utility\UnroutedUrlAssemblerInterface');
     $this->linkGenerator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface');
 
-    $this->renderer = $this->getMockBuilder('Drupal\Core\Render\RendererInterface')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->renderer = $this->getMock('Drupal\Core\Render\RendererInterface');
 
     $container_builder = new ContainerBuilder();
     $container_builder->set('url_generator', $this->urlGenerator);

Fixed on commit.

  • alexpott committed d6458cd on 8.0.x
    Issue #2426447 by mpdonadio, Berdir: Views no longer supports {{...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.