diff --git a/core/modules/file/config/install/views.view.files.yml b/core/modules/file/config/install/views.view.files.yml index dc9f7a19ee84..6783c1bd3f80 100644 --- a/core/modules/file/config/install/views.view.files.yml +++ b/core/modules/file/config/install/views.view.files.yml @@ -489,7 +489,7 @@ display: alter_text: false text: '' make_link: true - path: 'admin/content/files/usage/{{fid}}' + path: 'admin/content/files/usage/{{ fid }}' absolute: false external: false replace_spaces: false diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 8cd191f104d2..760bae324c1e 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -104,6 +104,12 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor */ protected $usesOptions = FALSE; + /** + * Stores the render API renderer. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; /** * Constructs a PluginBase object. @@ -364,14 +370,14 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor // Non-Twig tokens are a straight string replacement, Twig tokens get run // through an inline template for rendering and replacement. $text = strtr($text, $other_tokens); - if ($twig_tokens) { + if ($twig_tokens && !empty($text)) { $build = array( '#type' => 'inline_template', '#template' => $text, '#context' => $twig_tokens, ); - return drupal_render($build); + return $this->getRenderer()->render($build); } else { return $text; @@ -576,4 +582,18 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor return $changes; } + + /** + * 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; + } + } diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index d683ea70f4e0..698e5d8b29c2 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -103,7 +103,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf /** * Stores the render API renderer. * - * @var \Drupal\Core\Render\Renderer + * @var \Drupal\Core\Render\RendererInterface */ protected $renderer; @@ -1330,9 +1330,9 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf // Url::toUriString() call above, because we support twig tokens in // rewrite settings of views fields. // In that case the original path looks like - // user-path:/admin/content/files/usage/{{fid}}, which will be escaped by + // user-path:/admin/content/files/usage/{{ fid }}, which will be escaped by // the toUriString() call above. - $path = str_replace(['%7B','%7D'], ['{','}'], $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 @@ -1730,7 +1730,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf /** * Returns the render API renderer. * - * @return \Drupal\Core\Render\Renderer + * @return \Drupal\Core\Render\RendererInterface */ protected function getRenderer() { if (!isset($this->renderer)) { 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 78d83253853c..4e3bd73a363a 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php @@ -119,6 +119,13 @@ class FieldPluginBaseTest extends UnitTestCase { */ protected $pathProcessor; + /** + * The mocked path renderer. + * + * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $renderer; + /** * {@inheritdoc} */ @@ -147,11 +154,14 @@ class FieldPluginBaseTest extends UnitTestCase { $this->unroutedUrlAssembler = $this->getMock('Drupal\Core\Utility\UnroutedUrlAssemblerInterface'); $this->linkGenerator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface'); + $this->renderer = $this->getMock('Drupal\Core\Render\RendererInterface'); + $container_builder = new ContainerBuilder(); $container_builder->set('url_generator', $this->urlGenerator); $container_builder->set('path.validator', $this->pathValidator); $container_builder->set('unrouted_url_assembler', $this->unroutedUrlAssembler); $container_builder->set('request_stack', $this->requestStack); + $container_builder->set('renderer', $this->renderer); \Drupal::setContainer($container_builder); } @@ -421,6 +431,65 @@ class FieldPluginBaseTest extends UnitTestCase { return $data; } + /** + * Test rendering of a link with a path and options. + * + * @dataProvider providerTestRenderAsLinkWithPathAndTokens + * @covers ::renderAsLink + */ + public function testRenderAsLinkWithPathAndTokens($path, $tokens, $link_html) { + $alter = [ + 'make_link' => TRUE, + 'path' => $path, + ]; + + $this->setUpUrlIntegrationServices(); + $this->setupDisplayWithEmptyArgumentsAndFields(); + $this->executable->build_info['substitutions'] = $tokens; + $field = $this->setupTestField(['alter' => $alter]); + $field->field_alias = 'key'; + $row = new ResultRow(['key' => 'value']); + + $build =[ + '#type' => 'inline_template', + '#template' => 'base:test-path/' . explode('/', $path)[1], + '#context' => ['foo' => 123], + ]; + + $this->renderer->expects($this->once()) + ->method('render') + ->with($build, FALSE) + ->willReturn('base:test-path/123'); + + $result = $field->advancedRender($row); + $this->assertEquals($link_html, $result); + } + + /** + * Data provider for ::testRenderAsLinkWithPathAndTokens(). + * + * @return array + * Test data. + */ + public function providerTestRenderAsLinkWithPathAndTokens() { + $tokens = ['{{ foo }}' => 123]; + $link_html = 'value'; + + $data = []; + + $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]; + + return $data; + } + /** * Sets up a test field. *