Update to Drupal 8.0-dev-2015-11-17. Commits through da81cd220, Tue Nov 17 15:53:49 2015 +0000, Issue #2617224 by Wim Leers: Move around/fix some documentation.
This commit is contained in:
parent
4afb23bbd3
commit
7784f4c23d
929 changed files with 19798 additions and 5304 deletions
13
core/modules/language/language.install
Normal file
13
core/modules/language/language.install
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Update functions for Language module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rebuild the container as services changed.
|
||||
*/
|
||||
function language_update_8001() {
|
||||
\Drupal::service('kernel')->invalidateContainer();
|
||||
}
|
|
@ -9,7 +9,7 @@ services:
|
|||
- [initLanguageManager]
|
||||
language.config_subscriber:
|
||||
class: Drupal\language\EventSubscriber\ConfigSubscriber
|
||||
arguments: ['@language_manager', '@language.default', '@config.factory']
|
||||
arguments: ['@language_manager', '@language.default', '@config.factory', '@language_negotiator']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
language.config_factory_override:
|
||||
|
|
|
@ -14,6 +14,8 @@ use Drupal\Core\Language\LanguageManagerInterface;
|
|||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\language\ConfigurableLanguageManager;
|
||||
use Drupal\language\HttpKernel\PathProcessorLanguage;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +44,20 @@ class ConfigSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The language negotiator.
|
||||
*
|
||||
* @var \Drupal\language\LanguageNegotiatorInterface
|
||||
*/
|
||||
protected $languageNegotiator;
|
||||
|
||||
/**
|
||||
* The language path processor.
|
||||
*
|
||||
* @var \Drupal\language\HttpKernel\PathProcessorLanguage
|
||||
*/
|
||||
protected $pathProcessorLanguage;
|
||||
|
||||
/**
|
||||
* Constructs a new class object.
|
||||
*
|
||||
|
@ -51,11 +67,14 @@ class ConfigSubscriber implements EventSubscriberInterface {
|
|||
* The default language.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\language\LanguageNegotiatorInterface $language_negotiator
|
||||
* The language negotiator.
|
||||
*/
|
||||
public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory) {
|
||||
public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory, LanguageNegotiatorInterface $language_negotiator) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->languageDefault = $language_default;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->languageNegotiator = $language_negotiator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,6 +121,25 @@ class ConfigSubscriber implements EventSubscriberInterface {
|
|||
// Trigger a container rebuild on the next request by invalidating it.
|
||||
ConfigurableLanguageManager::rebuildServices();
|
||||
}
|
||||
elseif ($saved_config->getName() == 'language.types' && $event->isChanged('negotiation')) {
|
||||
// If the negotiation configuration changed the language negotiator and
|
||||
// the language path processor have to be reset so that they regenerate
|
||||
// the method instances and also sort them accordingly to the new config.
|
||||
$this->languageNegotiator->reset();
|
||||
if (isset($this->pathProcessorLanguage)) {
|
||||
$this->pathProcessorLanguage->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the language path processors on multilingual site configuration.
|
||||
*
|
||||
* @param \Drupal\language\HttpKernel\PathProcessorLanguage $path_processor_language
|
||||
* The language path processor.
|
||||
*/
|
||||
public function setPathProcessorLanguage(PathProcessorLanguage $path_processor_language) {
|
||||
$this->pathProcessorLanguage = $path_processor_language;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,7 +63,7 @@ class ContentLanguageSettingsForm extends FormBase {
|
|||
$bundles = $this->entityManager->getAllBundleInfo();
|
||||
$language_configuration = array();
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if (!$entity_type instanceof ContentEntityTypeInterface || !$entity_type->hasKey('langcode')) {
|
||||
if (!$entity_type instanceof ContentEntityTypeInterface || !$entity_type->hasKey('langcode') || !isset($bundles[$entity_type_id])) {
|
||||
continue;
|
||||
}
|
||||
$labels[$entity_type_id] = $entity_type->getLabel() ?: $entity_type_id;
|
||||
|
|
|
@ -13,6 +13,7 @@ use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
|||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\language\EventSubscriber\ConfigSubscriber;
|
||||
use Drupal\language\LanguageNegotiatorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
@ -57,6 +58,14 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
|
|||
*/
|
||||
protected $multilingual;
|
||||
|
||||
/**
|
||||
* The language configuration event subscriber.
|
||||
*
|
||||
* @var \Drupal\language\EventSubscriber\ConfigSubscriber
|
||||
*/
|
||||
protected $configSubscriber;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a PathProcessorLanguage object.
|
||||
*
|
||||
|
@ -68,12 +77,15 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
|
|||
* The language negotiator.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current active user.
|
||||
* @param \Drupal\language\EventSubscriber\ConfigSubscriber $config_subscriber
|
||||
* The language configuration event subscriber.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, AccountInterface $current_user) {
|
||||
public function __construct(ConfigFactoryInterface $config, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, AccountInterface $current_user, ConfigSubscriber $config_subscriber) {
|
||||
$this->config = $config;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->negotiator = $negotiator;
|
||||
$this->negotiator->setCurrentUser($current_user);
|
||||
$this->configSubscriber = $config_subscriber;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,4 +164,22 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the injected event subscriber with the language path processor.
|
||||
*
|
||||
* The language path processor service is registered only on multilingual
|
||||
* site configuration, thus we inject it in the event subscriber only when
|
||||
* it is initialized.
|
||||
*/
|
||||
public function initConfigSubscriber() {
|
||||
$this->configSubscriber->setPathProcessorLanguage($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the collected processors instances.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->processors = array();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ class LanguageServiceProvider extends ServiceProviderBase {
|
|||
->addArgument(new Reference('config.factory'))
|
||||
->addArgument(new Reference('language_manager'))
|
||||
->addArgument(new Reference('language_negotiator'))
|
||||
->addArgument(new Reference('current_user'));
|
||||
->addArgument(new Reference('current_user'))
|
||||
->addArgument(new Reference('language.config_subscriber'))
|
||||
->addMethodCall('initConfigSubscriber');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity.
|
||||
*/
|
||||
|
||||
namespace Drupal\language\Plugin\LanguageNegotiation;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\LanguageNegotiationMethodBase;
|
||||
use Drupal\language\LanguageSwitcherInterface;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Class for identifying the content translation language.
|
||||
*
|
||||
* @LanguageNegotiation(
|
||||
* id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::METHOD_ID,
|
||||
* types = {Drupal\Core\Language\LanguageInterface::TYPE_CONTENT},
|
||||
* weight = -9,
|
||||
* name = @Translation("Content language"),
|
||||
* description = @Translation("Determines the content language from a request parameter."),
|
||||
* )
|
||||
*/
|
||||
class LanguageNegotiationContentEntity extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The language negotiation method ID.
|
||||
*/
|
||||
const METHOD_ID = 'language-content-entity';
|
||||
|
||||
/**
|
||||
* The query string parameter.
|
||||
*/
|
||||
const QUERY_PARAMETER = 'language_content_entity';
|
||||
|
||||
/**
|
||||
* A list of all the link paths of enabled content entities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $contentEntityPaths;
|
||||
|
||||
/**
|
||||
* Static cache for the language negotiation order check.
|
||||
*
|
||||
* @see \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::hasLowerLanguageNegotiationWeight()
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasLowerLanguageNegotiationWeightResult;
|
||||
|
||||
/**
|
||||
* Static cache of outbound route paths per request.
|
||||
*
|
||||
* @var \SplObjectStorage
|
||||
*/
|
||||
protected $paths;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Constructs a new LanguageNegotiationContentEntity instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->paths = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static($container->get('entity.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode(Request $request = NULL) {
|
||||
$langcode = $request->get(static::QUERY_PARAMETER);
|
||||
|
||||
$language_enabled = array_key_exists($langcode, $this->languageManager->getLanguages());
|
||||
return $language_enabled ? $langcode : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
// If appropriate, process outbound to add a query parameter to the url and
|
||||
// remove the language option, so that url negotiator does not rewrite the
|
||||
// url.
|
||||
|
||||
// First, check if processing conditions are met.
|
||||
if (!($request && !empty($options['route']) && $this->hasLowerLanguageNegotiationWeight() && $this->meetsContentEntityRoutesCondition($options['route'], $request))) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if (isset($options['language']) || $langcode = $this->getLangcode($request)) {
|
||||
// If the language option is set, unset it, so that the url language
|
||||
// negotiator does not rewrite the url.
|
||||
if (isset($options['language'])) {
|
||||
$langcode = $options['language']->getId();
|
||||
unset($options['language']);
|
||||
}
|
||||
|
||||
if (isset($options['query']) && is_string($options['query'])) {
|
||||
$query = [];
|
||||
parse_str($options['query'], $query);
|
||||
$options['query'] = $query;
|
||||
}
|
||||
else {
|
||||
$options['query'] = [];
|
||||
}
|
||||
|
||||
if (!isset($options['query'][static::QUERY_PARAMETER])) {
|
||||
$query_addon = [static::QUERY_PARAMETER => $langcode];
|
||||
$options['query'] += $query_addon;
|
||||
// @todo Remove this once https://www.drupal.org/node/2507005 lands.
|
||||
$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($query_addon);
|
||||
}
|
||||
|
||||
if ($bubbleable_metadata) {
|
||||
// Cached URLs that have been processed by this outbound path
|
||||
// processor must be:
|
||||
$bubbleable_metadata
|
||||
// - varied by the content language query parameter.
|
||||
->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]);
|
||||
}
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
|
||||
$links = [];
|
||||
$query = [];
|
||||
parse_str($request->getQueryString(), $query);
|
||||
|
||||
foreach ($this->languageManager->getNativeLanguages() as $language) {
|
||||
$langcode = $language->getId();
|
||||
$query[static::QUERY_PARAMETER] = $langcode;
|
||||
$links[$langcode] = [
|
||||
'url' => $url,
|
||||
'title' => $language->getName(),
|
||||
'attributes' => ['class' => ['language-link']],
|
||||
'query' => $query,
|
||||
];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if content entity language negotiator has higher priority.
|
||||
*
|
||||
* The content entity language negotiator having higher priority than the url
|
||||
* language negotiator, is a criteria in
|
||||
* \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::processOutbound().
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the the content entity language negotiator has higher priority
|
||||
* than the url language negotiator, FALSE otherwise.
|
||||
*/
|
||||
protected function hasLowerLanguageNegotiationWeight() {
|
||||
if (!isset($this->hasLowerLanguageNegotiationWeightResult)) {
|
||||
// Only run if the LanguageNegotiationContentEntity outbound function is
|
||||
// being executed before the outbound function of LanguageNegotiationUrl.
|
||||
$content_method_weights = $this->config->get('language.types')->get('negotiation.language_content.enabled') ?: [];
|
||||
|
||||
// Check if the content language is configured to be dependent on the
|
||||
// url negotiator directly or indirectly over the interface negotiator.
|
||||
if (isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) && ($content_method_weights[static::METHOD_ID] > $content_method_weights[LanguageNegotiationUrl::METHOD_ID])) {
|
||||
$this->hasLowerLanguageNegotiationWeightResult = FALSE;
|
||||
}
|
||||
else {
|
||||
$check_interface_method = FALSE;
|
||||
if (isset($content_method_weights[LanguageNegotiationUI::METHOD_ID])) {
|
||||
$interface_method_weights = $this->config->get('language.types')->get('negotiation.language_interface.enabled') ?: [];
|
||||
$check_interface_method = isset($interface_method_weights[LanguageNegotiationUrl::METHOD_ID]);
|
||||
}
|
||||
if ($check_interface_method) {
|
||||
$max_weight = $content_method_weights[LanguageNegotiationUI::METHOD_ID];
|
||||
$max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? max($max_weight, $content_method_weights[LanguageNegotiationUrl::METHOD_ID]) : $max_weight;
|
||||
}
|
||||
else {
|
||||
$max_weight = isset($content_method_weights[LanguageNegotiationUrl::METHOD_ID]) ? $content_method_weights[LanguageNegotiationUrl::METHOD_ID] : PHP_INT_MAX;
|
||||
}
|
||||
|
||||
$this->hasLowerLanguageNegotiationWeightResult = $content_method_weights[static::METHOD_ID] < $max_weight;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->hasLowerLanguageNegotiationWeightResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if content entity route condition is met.
|
||||
*
|
||||
* Requirements: currently being on an content entity route and processing
|
||||
* outbound url pointing to the same content entity.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $outbound_route
|
||||
* The route object for the current outbound url being processed.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the content entity route condition is met, FALSE otherwise.
|
||||
*/
|
||||
protected function meetsContentEntityRoutesCondition(Route $outbound_route, Request $request) {
|
||||
$outbound_path_pattern = $outbound_route->getPath();
|
||||
$storage = isset($this->paths[$request]) ? $this->paths[$request] : [];
|
||||
if (!isset($storage[$outbound_path_pattern])) {
|
||||
$storage[$outbound_path_pattern] = FALSE;
|
||||
|
||||
// Check if the outbound route points to the current entity.
|
||||
if ($content_entity_type_id_for_current_route = $this->getContentEntityTypeIdForCurrentRequest($request)) {
|
||||
if (!empty($this->getContentEntityPaths()[$outbound_path_pattern]) && $content_entity_type_id_for_current_route == $this->getContentEntityPaths()[$outbound_path_pattern]) {
|
||||
$storage[$outbound_path_pattern] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$this->paths[$request] = $storage;
|
||||
}
|
||||
|
||||
return $storage[$outbound_path_pattern];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content entity type ID from the current request for the route.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*
|
||||
* @return string
|
||||
* The entity type ID for the route from the request.
|
||||
*/
|
||||
protected function getContentEntityTypeIdForCurrentRequest(Request $request) {
|
||||
$content_entity_type_id_for_current_route = '';
|
||||
|
||||
if ($current_route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) {
|
||||
$current_route_path = $current_route->getPath();
|
||||
$content_entity_type_id_for_current_route = isset($this->getContentEntityPaths()[$current_route_path]) ? $this->getContentEntityPaths()[$current_route_path] : '';
|
||||
}
|
||||
|
||||
return $content_entity_type_id_for_current_route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths for the link templates of all content entities.
|
||||
*
|
||||
* @return array
|
||||
* An array of all content entity type IDs, keyed by the corresponding link
|
||||
* template paths.
|
||||
*/
|
||||
protected function getContentEntityPaths() {
|
||||
if (!isset($this->contentEntityPaths)) {
|
||||
$this->contentEntityPaths = [];
|
||||
$entity_types = $this->entityManager->getDefinitions();
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if ($entity_type->isSubclassOf(ContentEntityInterface::class)) {
|
||||
$entity_paths = array_fill_keys($entity_type->getLinkTemplates(), $entity_type_id);
|
||||
$this->contentEntityPaths = array_merge($this->contentEntityPaths, $entity_paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->contentEntityPaths;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,22 +7,34 @@
|
|||
|
||||
namespace Drupal\language\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Tests the language of entity URLs.
|
||||
* @group language
|
||||
*/
|
||||
class EntityUrlLanguageTest extends KernelTestBase {
|
||||
class EntityUrlLanguageTest extends LanguageTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'entity_test', 'user', 'system'];
|
||||
public static $modules = ['entity_test', 'user'];
|
||||
|
||||
/**
|
||||
* The entity being used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
@ -37,33 +49,93 @@ class EntityUrlLanguageTest extends KernelTestBase {
|
|||
ConfigurableLanguage::create(['id' => 'es'])->save();
|
||||
ConfigurableLanguage::create(['id' => 'fr'])->save();
|
||||
|
||||
$this->config('language.types')->setData([
|
||||
'configurable' => ['language_interface'],
|
||||
'negotiation' => ['language_interface' => ['enabled' => ['language-url' => 0]]],
|
||||
])->save();
|
||||
$this->config('language.negotiation')->setData([
|
||||
'url' => [
|
||||
'source' => 'path_prefix',
|
||||
'prefixes' => ['en' => 'en', 'es' => 'es', 'fr' => 'fr']
|
||||
],
|
||||
])->save();
|
||||
$config = $this->config('language.negotiation');
|
||||
$config->set('url.prefixes', ['en' => 'en', 'es' => 'es', 'fr' => 'fr'])
|
||||
->save();
|
||||
$this->kernel->rebuildContainer();
|
||||
$this->container = $this->kernel->getContainer();
|
||||
\Drupal::setContainer($this->container);
|
||||
|
||||
$this->createTranslatableEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that entity URLs in a language have the right language prefix.
|
||||
*/
|
||||
public function testEntityUrlLanguage() {
|
||||
$entity = EntityTest::create();
|
||||
$entity->addTranslation('es', ['name' => 'name spanish']);
|
||||
$entity->addTranslation('fr', ['name' => 'name french']);
|
||||
$entity->save();
|
||||
$this->assertTrue(strpos($this->entity->urlInfo()->toString(), '/en/entity_test/' . $this->entity->id()) !== FALSE);
|
||||
$this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo()->toString(), '/es/entity_test/' . $this->entity->id()) !== FALSE);
|
||||
$this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo()->toString(), '/fr/entity_test/' . $this->entity->id()) !== FALSE);
|
||||
}
|
||||
|
||||
$this->assertTrue(strpos($entity->urlInfo()->toString(), '/en/entity_test/' . $entity->id()) !== FALSE);
|
||||
$this->assertTrue(strpos($entity->getTranslation('es')->urlInfo()->toString(), '/es/entity_test/' . $entity->id()) !== FALSE);
|
||||
$this->assertTrue(strpos($entity->getTranslation('fr')->urlInfo()->toString(), '/fr/entity_test/' . $entity->id()) !== FALSE);
|
||||
/**
|
||||
* Ensures correct entity URLs with the method language-content-entity enabled.
|
||||
*
|
||||
* Test case with the method language-content-entity enabled and configured
|
||||
* with higher and also with lower priority than the method language-url.
|
||||
*/
|
||||
public function testEntityUrlLanguageWithLanguageContentEnabled() {
|
||||
// Define the method language-content-entity with a higher priority than
|
||||
// language-url.
|
||||
$config = $this->config('language.types');
|
||||
$config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
|
||||
$config->set('negotiation.language_content.enabled', [
|
||||
LanguageNegotiationContentEntity::METHOD_ID => 0,
|
||||
LanguageNegotiationUrl::METHOD_ID => 1
|
||||
]);
|
||||
$config->save();
|
||||
|
||||
// Without being on an content entity route the default entity URL tests
|
||||
// should still pass.
|
||||
$this->testEntityUrlLanguage();
|
||||
|
||||
// Now switching to an entity route, so that the URL links are generated
|
||||
// while being on an entity route.
|
||||
$this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
|
||||
|
||||
// The method language-content-entity should run before language-url and
|
||||
// append query parameter for the content language and prevent language-url
|
||||
// from overwriting the url.
|
||||
$this->assertTrue(strpos($this->entity->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=en') !== FALSE);
|
||||
$this->assertTrue(strpos($this->entity->getTranslation('es')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=es') !== FALSE);
|
||||
$this->assertTrue(strpos($this->entity->getTranslation('fr')->urlInfo('canonical')->toString(), '/en/entity_test/' . $this->entity->id() . '?' . LanguageNegotiationContentEntity::QUERY_PARAMETER . '=fr') !== FALSE);
|
||||
|
||||
// Define the method language-url with a higher priority than
|
||||
// language-content-entity. This configuration should match the default one,
|
||||
// where the language-content-entity is turned off.
|
||||
$config->set('negotiation.language_content.enabled', [
|
||||
LanguageNegotiationUrl::METHOD_ID => 0,
|
||||
LanguageNegotiationContentEntity::METHOD_ID => 1
|
||||
]);
|
||||
$config->save();
|
||||
|
||||
// The default entity URL tests should pass again with the current
|
||||
// configuration.
|
||||
$this->testEntityUrlLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translated entity.
|
||||
*/
|
||||
protected function createTranslatableEntity() {
|
||||
$this->entity = EntityTest::create();
|
||||
$this->entity->addTranslation('es', ['name' => 'name spanish']);
|
||||
$this->entity->addTranslation('fr', ['name' => 'name french']);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current request to a specific path with the corresponding route.
|
||||
*
|
||||
* @param string $path
|
||||
* The path for which the current request should be created.
|
||||
* @param string $route_name
|
||||
* The route name for which the route object for the request should be
|
||||
* created.
|
||||
*/
|
||||
protected function setCurrentRequestForRoute($path, $route_name) {
|
||||
$request = Request::create($path);
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, $route_name);
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route($path));
|
||||
$this->container->get('request_stack')->push($request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\language\Tests\LanguageNegotiationLanguageContentEntityTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\language\Tests;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity;
|
||||
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Tests language negotiation with the language negotiator content entity.
|
||||
*
|
||||
* @group language
|
||||
*/
|
||||
class LanguageNegotiationContentEntityTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'language_test', 'entity_test', 'system');
|
||||
|
||||
/**
|
||||
* The entity being used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ConfigurableLanguage::create(['id' => 'es'])->save();
|
||||
ConfigurableLanguage::create(['id' => 'fr'])->save();
|
||||
|
||||
// In order to reflect the changes for a multilingual site in the container
|
||||
// we have to rebuild it.
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->createTranslatableEntity();
|
||||
|
||||
$user = $this->drupalCreateUser(array('view test entity'));
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests default with content language remaining same as interface language.
|
||||
*/
|
||||
public function testDefaultConfiguration() {
|
||||
$translation = $this->entity;
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
|
||||
|
||||
$translation = $this->entity->getTranslation('es');
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
|
||||
|
||||
$translation = $this->entity->getTranslation('fr');
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue(($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), new FormattableMarkup('Interface language %interface_language and Content language %content_language are the same as the translation language %translation_language of the entity.', ['%interface_language' => $last_interface_language, '%content_language' => $last_content_language, '%translation_language' => $translation->language()->getId()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling the language negotiator language_content_entity.
|
||||
*/
|
||||
public function testEnabledLanguageContentNegotiator() {
|
||||
// Define the method language-url with a higher priority than
|
||||
// language-content-entity. This configuration should match the default one,
|
||||
// where the language-content-entity is turned off.
|
||||
$config = $this->config('language.types');
|
||||
$config->set('configurable', [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT]);
|
||||
$config->set('negotiation.language_content.enabled', [
|
||||
LanguageNegotiationUrl::METHOD_ID => 0,
|
||||
LanguageNegotiationContentEntity::METHOD_ID => 1
|
||||
]);
|
||||
$config->save();
|
||||
|
||||
// In order to reflect the changes for a multilingual site in the container
|
||||
// we have to rebuild it.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// The tests for the default configuration should still pass.
|
||||
$this->testDefaultConfiguration();
|
||||
|
||||
// Define the method language-content-entity with a higher priority than
|
||||
// language-url.
|
||||
$config->set('negotiation.language_content.enabled', [
|
||||
LanguageNegotiationContentEntity::METHOD_ID => 0,
|
||||
LanguageNegotiationUrl::METHOD_ID => 1
|
||||
]);
|
||||
$config->save();
|
||||
|
||||
// In order to reflect the changes for a multilingual site in the container
|
||||
// we have to rebuild it.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// The method language-content-entity should run before language-url and
|
||||
// append query parameter for the content language and prevent language-url
|
||||
// from overwriting the URL.
|
||||
$default_site_langcode = $this->config('system.site')->get('default_langcode');
|
||||
|
||||
// Now switching to an entity route, so that the URL links are generated
|
||||
// while being on an entity route.
|
||||
$this->setCurrentRequestForRoute('/entity_test/{entity_test}', 'entity.entity_test.canonical');
|
||||
|
||||
$translation = $this->entity;
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue(($last_interface_language == $default_site_langcode) && ($last_interface_language == $last_content_language) && ($last_content_language == $translation->language()->getId()), 'Interface language and Content language are the same as the default translation language of the entity.');
|
||||
$this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
|
||||
$this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
|
||||
|
||||
$translation = $this->entity->getTranslation('es');
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
|
||||
$this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
|
||||
|
||||
$translation = $this->entity->getTranslation('fr');
|
||||
$this->drupalGet($translation->urlInfo());
|
||||
$last = $this->container->get('state')->get('language_test.language_negotiation_last');
|
||||
$last_content_language = $last[LanguageInterface::TYPE_CONTENT];
|
||||
$last_interface_language = $last[LanguageInterface::TYPE_INTERFACE];
|
||||
$this->assertTrue($last_interface_language == $default_site_langcode, 'Interface language did not change from the default site language.');
|
||||
$this->assertTrue($last_content_language == $translation->language()->getId(), 'Content language matches the current entity translation language.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translated entity.
|
||||
*/
|
||||
protected function createTranslatableEntity() {
|
||||
$this->entity = EntityTest::create();
|
||||
$this->entity->addTranslation('es', ['name' => 'name spanish']);
|
||||
$this->entity->addTranslation('fr', ['name' => 'name french']);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current request to a specific path with the corresponding route.
|
||||
*
|
||||
* @param string $path
|
||||
* The path for which the current request should be created.
|
||||
* @param string $route_name
|
||||
* The route name for which the route object for the request should be
|
||||
* created.
|
||||
*/
|
||||
protected function setCurrentRequestForRoute($path, $route_name) {
|
||||
$request = Request::create($path);
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, $route_name);
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route($path));
|
||||
$this->container->get('request_stack')->push($request);
|
||||
}
|
||||
|
||||
}
|
|
@ -84,7 +84,7 @@ class LanguageSelectorTranslatableTest extends WebTestBase {
|
|||
$this->drupalGet($path);
|
||||
|
||||
// Get en language from selector.
|
||||
$elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => 'edit-settings-node-node-settings-language-langcode', ':option' => 'en'));
|
||||
$elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => 'edit-settings-user-user-settings-language-langcode', ':option' => 'en'));
|
||||
|
||||
// Check that the language text is translated.
|
||||
$this->assertEqual((string) $elements[0], $name_translation, 'Checking the option string English is translated to Spanish.');
|
||||
|
|
|
@ -221,8 +221,13 @@ class LanguageUILanguageNegotiationTest extends WebTestBase {
|
|||
|
||||
// Unknown language prefix should return 404.
|
||||
$definitions = \Drupal::languageManager()->getNegotiator()->getNegotiationMethods();
|
||||
// Enable only methods, which are either not limited to a specific language
|
||||
// type or are supporting the interface language type.
|
||||
$language_interface_method_definitions = array_filter($definitions, function ($method_definition) {
|
||||
return !isset($method_definition['types']) || (isset($method_definition['types']) && in_array(LanguageInterface::TYPE_INTERFACE, $method_definition['types']));
|
||||
});
|
||||
$this->config('language.types')
|
||||
->set('negotiation.' . LanguageInterface::TYPE_INTERFACE . '.enabled', array_flip(array_keys($definitions)))
|
||||
->set('negotiation.' . LanguageInterface::TYPE_INTERFACE . '.enabled', array_flip(array_keys($language_interface_method_definitions)))
|
||||
->save();
|
||||
$this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
|
||||
$this->assertResponse(404, "Unknown language path prefix should return 404");
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for a language negotiation configuration form.
|
||||
*
|
||||
* Available variables:
|
||||
* - language_types: A list of language negotiation types. Each language type
|
||||
* contains the following:
|
||||
* - type: The machine name for the negotiation type.
|
||||
* - title: The language negotiation type name.
|
||||
* - description: A description for how the language negotiation type operates.
|
||||
* - configurable: A radio element to toggle the table.
|
||||
* - table: A draggable table for the language detection methods of this type.
|
||||
* - children: Remaining form items for the group.
|
||||
* - attributes: A list of HTML attributes for the wrapper element.
|
||||
* - children: Remaining form items for all groups.
|
||||
*
|
||||
* @see template_preprocess_language_negotiation_configure_form()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
* @file
|
||||
* Default theme implementation for a language negotiation configuration form.
|
||||
*
|
||||
* Available variables:
|
||||
* - language_types: A list of language negotiation types. Each language type
|
||||
* contains the following:
|
||||
* - type: The machine name for the negotiation type.
|
||||
* - title: The language negotiation type name.
|
||||
* - description: A description for how the language negotiation type
|
||||
* operates.
|
||||
* - configurable: A radio element to toggle the table.
|
||||
* - table: A draggable table for the language detection methods of this type.
|
||||
* - children: Remaining form items for the group.
|
||||
* - attributes: A list of HTML attributes for the wrapper element.
|
||||
* - children: Remaining form items for all groups.
|
||||
*
|
||||
* @see template_preprocess_language_negotiation_configure_form()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{% for language_type in language_types %}
|
||||
{%
|
||||
|
|
Reference in a new issue