Unverified Commit 93d251d7 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #2716019 by joseph.olstad, rodrigoaguilera, _Archy_, Lendude, mdupont,...

Issue #2716019 by joseph.olstad, rodrigoaguilera, _Archy_, Lendude, mdupont, anmolgoyal74, mpp, Krzysztof Domański, DamienMcKenna, adityasingh, Skymen, benelori, Suresh Prabhu Parkala, rensingh99, alexpott, paulocs, HeyJo, iiRealXz, dawehner, xjm, 5n00py, tstoeckler, Karsa, dbyers55, idebr: View titles in breadcrumb and metatag title don't get properly translated
parent 9edbbe5a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ protected function defineOptions() {
  protected function getRoute($view_id, $display_id) {
    $defaults = [
      '_controller' => 'Drupal\views\Routing\ViewPageController::handle',
      '_title' => $this->view->getTitle(),
      '_title_callback' => 'Drupal\views\Routing\ViewPageController::getTitle',
      'view_id' => $view_id,
      'display_id' => $display_id,
      '_view_display_show_admin_links' => $this->getOption('show_admin_links'),
+21 −0
Original line number Diff line number Diff line
@@ -2,8 +2,11 @@

namespace Drupal\views\Routing;

use Drupal\Component\Utility\Xss;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\views\display\Page;
use Drupal\views\Render\ViewsRenderPipelineMarkup;
use Drupal\views\Views;

/**
 * Defines a page controller to execute and render a view.
@@ -63,4 +66,22 @@ public function handle($view_id, $display_id, RouteMatchInterface $route_match)
    }
  }

  /**
   * Gets the title of the given view's display.
   *
   * @param string $view_id
   *   The id of the view.
   * @param string $display_id
   *   The id of the display from the view.
   *
   * @return string|\Drupal\Component\Render\MarkupInterface
   *   The title of the display of the view.
   */
  public function getTitle($view_id, $display_id = 'default') {
    $view = Views::getView($view_id);
    $view->setDisplay($display_id);

    return ViewsRenderPipelineMarkup::create(Xss::filter($view->getTitle()));
  }

}
+115 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\views\Functional;

use Drupal\Component\Utility\Xss;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\views\Views;

/**
 * Tests views title translation.
 *
 * @group views
 */
class ViewTranslationTest extends ViewTestBase {

  /**
   * {@inheritdoc}
   */
  public static $testViews = ['test_view'];

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['block', 'locale', 'language', 'config_translation', 'views_ui'];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * {@inheritdoc}
   */
  public function setUp($import_test_views = TRUE): void {
    parent::setUp($import_test_views);

    $this->enableViewsTestModule();
    $this->drupalPlaceBlock('system_breadcrumb_block');

    // Add Dutch language programmatically.
    ConfigurableLanguage::createFromLangcode('nl')->save();

    // Enable page caching.
    $config = $this->config('system.performance');
    $config->set('cache.page.max_age', 3600);
    $config->save();
  }

  /**
   * Tests that the view route title is translated.
   */
  public function testViewTitleTranslation() {
    $view = Views::getView('test_view');

    // Create a test display, add path and default language title.
    $view->storage->addDisplay('page');
    $displays = $view->storage->get('display');
    $displays['default']['display_options']['title'] = 'Title EN';
    $displays['page_1']['display_options']['path'] = 'test-view';
    $view->storage->set('display', $displays);
    $view->save();
    // We need to rebuild the routes to discover the route to the
    // view display.
    \Drupal::service('router.builder')->rebuild();

    $admin_user = $this->drupalCreateUser(['translate configuration']);
    $this->drupalLogin($admin_user);

    $edit = [
      'translation[config_names][views.view.test_view][display][default][display_options][title]' => 'Titel NL',
    ];
    $this->drupalGet('admin/structure/views/view/test_view/translate/nl/edit');
    $this->submitForm($edit, 'Save translation');
    $this->drupalLogout();

    $this->drupalGet('test-view');
    $this->assertSession()->titleEquals('Title EN | Drupal');
    $this->assertEquals('MISS', $this->getSession()->getResponseHeader('X-Drupal-Cache'));

    // Make sure the use of a title callback does not prevent caching of the
    // View page.
    $this->drupalGet('test-view');
    $this->assertEquals('HIT', $this->getSession()->getResponseHeader('X-Drupal-Cache'));

    // Test the breadcrumb on a deeper page because by default the breadcrumb
    // doesn't render the current page title. It doesn't matter for the
    // breadcrumb that the requested page does not exist.
    $this->drupalGet('test-view/not-relevant');
    $this->assertSession()->linkExists('Title EN');
    $this->assertSession()->linkNotExists('Titel NL');

    // Test that the title is translated.
    $this->drupalGet('nl/test-view');
    $this->assertSession()->titleEquals('Titel NL | Drupal');
    $this->assertEquals('MISS', $this->getSession()->getResponseHeader('X-Drupal-Cache'));
    $this->drupalGet('test-view');
    $this->assertEquals('HIT', $this->getSession()->getResponseHeader('X-Drupal-Cache'));

    // Test that the breadcrumb link is also translated.
    $this->drupalGet('nl/test-view/not-relevant');
    $this->assertSession()->linkExists('Titel NL');
    $this->assertSession()->linkNotExists('Title EN');

    // Make sure that the title gets sanitized.
    $displays = $view->storage->get('display');
    $unsafe_title = 'This is an unsafe title <script>alert("click me!")</script>';
    $safe_title = Xss::filter($unsafe_title);
    $displays['default']['display_options']['title'] = $unsafe_title;
    $view->storage->set('display', $displays);
    $view->save();
    $this->drupalGet('test-view');
    $this->assertSession()->titleEquals($safe_title . ' | Drupal');
  }

}
+12 −16
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ public function testCollectRoutes() {
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertFalse($route->getOption('returns_response'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -135,7 +135,7 @@ public function testCollectRoutesWithDisplayReturnResponse() {
    $this->pathPlugin->collectRoutes($collection);
    $route = $collection->get('view.test_id.page_1');
    $this->assertTrue($route->getOption('returns_response'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -163,7 +163,7 @@ public function testCollectRoutesWithArguments() {
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals(['arg_0' => 'arg_0'], $route->getOption('_view_argument_map'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -194,7 +194,7 @@ public function testCollectRoutesWithArgumentsNotSpecifiedInPath() {
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals(['arg_0' => 'arg_0'], $route->getOption('_view_argument_map'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -220,7 +220,7 @@ public function testCollectRoutesWithSpecialRouteName() {
    $this->assertInstanceOf(Route::class, $route);
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -250,7 +250,7 @@ public function testAlterRoute() {
    $this->assertInstanceOf(Route::class, $route);
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));

    // Ensure that the test_route_2 is not overridden.
    $route = $collection->get('test_route_2');
@@ -295,7 +295,7 @@ public function testAlterPostRestRoute() {
    $this->assertInstanceOf(Route::class, $route);
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -334,7 +334,7 @@ public function testGetRestRoute() {
    $this->assertInstanceOf(Route::class, $route);
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -365,7 +365,6 @@ public function testAlterRouteWithAlterCallback() {
    $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');
@@ -406,7 +405,7 @@ public function testCollectRoutesWithNamedParameters() {
    $this->assertEquals('/test_route/{node}/example', $route->getPath());
    $this->assertEquals('test_id', $route->getDefault('view_id'));
    $this->assertEquals('page_1', $route->getDefault('display_id'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
    $this->assertEquals(['arg_0' => 'node'], $route->getOption('_view_argument_map'));
  }

@@ -444,7 +443,7 @@ public function testAlterRoutesWithParameters() {
    // Ensure that the path did not changed and placeholders are respected.
    $this->assertEquals('/test_route/{parameter}', $route->getPath());
    $this->assertEquals(['arg_0' => 'parameter'], $route->getOption('_view_argument_map'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -482,7 +481,7 @@ public function testAlterRoutesWithParametersAndUpcasting() {
    // Ensure that the path did not changed and placeholders are respected  kk.
    $this->assertEquals('/test_route/{parameter}', $route->getPath());
    $this->assertEquals(['arg_0' => 'parameter'], $route->getOption('_view_argument_map'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -517,7 +516,7 @@ public function testAlterRoutesWithOptionalParameters() {
    // Ensure that the path did not changed and placeholders are respected.
    $this->assertEquals('/test_route/{parameter}/{arg_1}', $route->getPath());
    $this->assertEquals(['arg_0' => 'parameter'], $route->getOption('_view_argument_map'));
    $this->assertEquals('my views title', $route->getDefault('_title'));
    $this->assertEquals('Drupal\views\Routing\ViewPageController::getTitle', $route->getDefault('_title_callback'));
  }

  /**
@@ -552,9 +551,6 @@ protected function setupViewExecutableAccessPlugin() {
    $view = $this->getMockBuilder('Drupal\views\ViewExecutable')
      ->disableOriginalConstructor()
      ->getMock();
    $view->expects($this->any())
      ->method('getTitle')
      ->willReturn('my views title');

    $view->storage = $view_entity;

+7 −0
Original line number Diff line number Diff line
@@ -68,3 +68,10 @@ function views_post_update_rename_default_display_setting() {
function views_post_update_remove_sorting_global_text_field() {
  // Empty post-update hook.
}

/**
 * Rebuild routes to fix view title translations.
 */
function views_post_update_title_translations() {
  \Drupal::service('router.builder')->setRebuildNeeded();
}