Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Access\ConfigTranslationFormAccess.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Checks access for displaying the translation add, edit, and delete forms.
|
||||
*/
|
||||
class ConfigTranslationFormAccess extends ConfigTranslationOverviewAccess {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(Route $route, AccountInterface $account, $langcode = NULL) {
|
||||
// For the translation forms we have a target language, so we need some
|
||||
// checks in addition to the checks performed for the translation overview.
|
||||
$base_access = parent::access($route, $account);
|
||||
if ($base_access->isAllowed()) {
|
||||
$target_language = $this->languageManager->getLanguage($langcode);
|
||||
|
||||
// Make sure that the target language is not locked, and that the target
|
||||
// language is not the original submission language. Although technically
|
||||
// configuration can be overlaid with translations in the same language,
|
||||
// that is logically not a good idea.
|
||||
$access =
|
||||
!empty($target_language) &&
|
||||
!$target_language->isLocked() &&
|
||||
(empty($this->sourceLanguage) || ($target_language->getId() != $this->sourceLanguage->getId()));
|
||||
|
||||
return $base_access->andIf(AccessResult::allowedIf($access));
|
||||
}
|
||||
return $base_access;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Access\ConfigTranslationOverviewAccess.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Access;
|
||||
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Checks access for displaying the configuration translation overview.
|
||||
*/
|
||||
class ConfigTranslationOverviewAccess implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $configMapperManager;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The source language.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface
|
||||
*/
|
||||
protected $sourceLanguage;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigTranslationOverviewAccess object.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
*/
|
||||
public function __construct(ConfigMapperManagerInterface $config_mapper_manager, LanguageManagerInterface $language_manager) {
|
||||
$this->configMapperManager = $config_mapper_manager;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access to the overview based on permissions and translatability.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route to check against.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The currently logged in account.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(Route $route, AccountInterface $account) {
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
|
||||
$this->sourceLanguage = $this->languageManager->getLanguage($mapper->getLangcode());
|
||||
|
||||
// Allow access to the translation overview if the proper permission is
|
||||
// granted, the configuration has translatable pieces, and the source
|
||||
// language is not locked if it is present.
|
||||
$source_language_access = is_null($this->sourceLanguage) || !$this->sourceLanguage->isLocked();
|
||||
$access =
|
||||
$account->hasPermission('translate configuration') &&
|
||||
$mapper->hasSchema() &&
|
||||
$mapper->hasTranslatable() &&
|
||||
$source_language_access;
|
||||
|
||||
return AccessResult::allowedIf($access)->cachePerPermissions();
|
||||
}
|
||||
|
||||
}
|
270
core/modules/config_translation/src/ConfigEntityMapper.php
Normal file
270
core/modules/config_translation/src/ConfigEntityMapper.php
Normal file
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigEntityMapper.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Routing\RouteMatch;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\locale\LocaleConfigManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Configuration mapper for configuration entities.
|
||||
*/
|
||||
class ConfigEntityMapper extends ConfigNamesMapper {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Configuration entity type name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* Loaded entity instance to help produce the translation interface.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The label for the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $typeLabel;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigEntityMapper.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The config mapper plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* An array of plugin information as documented in
|
||||
* ConfigNamesMapper::__construct() with the following additional keys:
|
||||
* - entity_type: The name of the entity type this mapper belongs to.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The typed configuration manager.
|
||||
* @param \Drupal\locale\LocaleConfigManager $locale_config_manager
|
||||
* The locale configuration manager.
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
|
||||
* The string translation manager.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, LocaleConfigManager $locale_config_manager, ConfigMapperManagerInterface $config_mapper_manager, RouteProviderInterface $route_provider, TranslationInterface $translation_manager, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $config_factory, $typed_config, $locale_config_manager, $config_mapper_manager, $route_provider, $translation_manager, $language_manager);
|
||||
$this->setType($plugin_definition['entity_type']);
|
||||
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
// Note that we ignore the plugin $configuration because mappers have
|
||||
// nothing to configure in themselves.
|
||||
return new static (
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('config.factory'),
|
||||
$container->get('config.typed'),
|
||||
$container->get('locale.config_manager'),
|
||||
$container->get('plugin.manager.config_translation.mapper'),
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('string_translation'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function populateFromRequest(Request $request) {
|
||||
parent::populateFromRequest($request);
|
||||
$entity = $request->attributes->get($this->entityType);
|
||||
$this->setEntity($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity instance for this mapper.
|
||||
*
|
||||
* This method can only be invoked when the concrete entity is known, that is
|
||||
* in a request for an entity translation path. After this method is called,
|
||||
* the mapper is fully populated with the proper display title and
|
||||
* configuration names to use to check permissions or display a translation
|
||||
* screen.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to set.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE, if the entity was set successfully; FALSE otherwise.
|
||||
*/
|
||||
public function setEntity(EntityInterface $entity) {
|
||||
if (isset($this->entity)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$this->entity = $entity;
|
||||
|
||||
// Add the list of configuration IDs belonging to this entity. We add on a
|
||||
// possibly existing list of names. This allows modules to alter the entity
|
||||
// page with more names if form altering added more configuration to an
|
||||
// entity. This is not a Drupal 8 best practice (ideally the configuration
|
||||
// would have pluggable components), but this may happen as well.
|
||||
$entity_type_info = $this->entityManager->getDefinition($this->entityType);
|
||||
$this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $entity->id());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
// Title based on the entity label. Should be translated for display in the
|
||||
// current page language. The title placeholder is later escaped for
|
||||
// display.
|
||||
$entity_type_info = $this->entityManager->getDefinition($this->entityType);
|
||||
return $this->t($this->pluginDefinition['title'], array('!label' => $this->entity->label(), '!entity_type' => $entity_type_info->getLowercaseLabel()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseRouteParameters() {
|
||||
return array($this->entityType => $this->entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set entity type for this mapper.
|
||||
*
|
||||
* This should be set in initialization. A mapper that knows its type but
|
||||
* not yet its names is still useful for router item and tab generation. The
|
||||
* concrete entity only turns out later with actual controller invocations,
|
||||
* when the setEntity() method is invoked before the rest of the methods are
|
||||
* used.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to set.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity type was set correctly; FALSE otherwise.
|
||||
*/
|
||||
public function setType($entity_type) {
|
||||
if (isset($this->entityType)) {
|
||||
return FALSE;
|
||||
}
|
||||
$this->entityType = $entity_type;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity type from this mapper.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeName() {
|
||||
$entity_type_info = $this->entityManager->getDefinition($this->entityType);
|
||||
return $entity_type_info->getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeLabel() {
|
||||
$entityType = $this->entityManager->getDefinition($this->entityType);
|
||||
return $entityType->getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOperations() {
|
||||
return array(
|
||||
'list' => array(
|
||||
'title' => $this->t('List'),
|
||||
'url' => Url::fromRoute('config_translation.entity_list', [
|
||||
'mapper_id' => $this->getPluginId(),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextualLinkGroup() {
|
||||
// @todo Contextual groups do not map to entity types in a predictable
|
||||
// way. See https://www.drupal.org/node/2134841 to make them predictable.
|
||||
switch ($this->entityType) {
|
||||
case 'menu':
|
||||
case 'block':
|
||||
return $this->entityType;
|
||||
case 'view':
|
||||
return 'entity.view.edit_form';
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewRouteName() {
|
||||
return 'entity.' . $this->entityType . '.config_translation_overview';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function processRoute(Route $route) {
|
||||
// Add entity upcasting information.
|
||||
$parameters = $route->getOption('parameters') ?: array();
|
||||
$parameters += array(
|
||||
$this->entityType => array(
|
||||
'type' => 'entity:' . $this->entityType,
|
||||
)
|
||||
);
|
||||
$route->setOption('parameters', $parameters);
|
||||
}
|
||||
|
||||
}
|
52
core/modules/config_translation/src/ConfigFieldMapper.php
Normal file
52
core/modules/config_translation/src/ConfigFieldMapper.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigFieldMapper.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
/**
|
||||
* Configuration mapper for fields.
|
||||
*
|
||||
* On top of plugin definition values on ConfigEntityMapper, the plugin
|
||||
* definition for field mappers are required to contain the following
|
||||
* additional keys:
|
||||
* - base_entity_type: The name of the entity type the fields are attached to.
|
||||
*/
|
||||
class ConfigFieldMapper extends ConfigEntityMapper {
|
||||
|
||||
/**
|
||||
* Loaded entity instance to help produce the translation interface.
|
||||
*
|
||||
* @var \Drupal\field\FieldConfigInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseRouteParameters() {
|
||||
$parameters = parent::getBaseRouteParameters();
|
||||
$base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']);
|
||||
$parameters[$base_entity_info->getBundleEntityType()] = $this->entity->getTargetBundle();
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewRouteName() {
|
||||
return 'entity.field_config.config_translation_overview.' . $this->pluginDefinition['base_entity_type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeLabel() {
|
||||
$base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']);
|
||||
return $this->t('@label fields', array('@label' => $base_entity_info->getLabel()));
|
||||
}
|
||||
|
||||
}
|
283
core/modules/config_translation/src/ConfigMapperInterface.php
Normal file
283
core/modules/config_translation/src/ConfigMapperInterface.php
Normal file
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigMapperInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Defines an interface for configuration mapper.
|
||||
*/
|
||||
interface ConfigMapperInterface {
|
||||
|
||||
/**
|
||||
* Returns title of this translation page.
|
||||
*
|
||||
* @return string
|
||||
* The page title.
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Sets the route collection.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\RouteCollection $collection
|
||||
* The route collection.
|
||||
*/
|
||||
public function setRouteCollection(RouteCollection $collection);
|
||||
|
||||
/**
|
||||
* Returns the name of the base route the mapper is attached to.
|
||||
*
|
||||
* @return string
|
||||
* The name of the base route the mapper is attached to.
|
||||
*/
|
||||
public function getBaseRouteName();
|
||||
|
||||
/**
|
||||
* Returns the route parameters for the base route the mapper is attached to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBaseRouteParameters();
|
||||
|
||||
/**
|
||||
* Returns the base route object the mapper is attached to.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
* The base route object the mapper is attached to.
|
||||
*/
|
||||
public function getBaseRoute();
|
||||
|
||||
/**
|
||||
* Returns a processed path for the base route the mapper is attached to.
|
||||
*
|
||||
* @return string
|
||||
* Processed path with placeholders replaced.
|
||||
*/
|
||||
public function getBasePath();
|
||||
|
||||
/**
|
||||
* Returns route name for the translation overview route.
|
||||
*
|
||||
* @return string
|
||||
* Route name for the mapper.
|
||||
*/
|
||||
public function getOverviewRouteName();
|
||||
|
||||
/**
|
||||
* Returns the route parameters for the translation overview route.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOverviewRouteParameters();
|
||||
|
||||
/**
|
||||
* Returns the route object for a translation overview route.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
* The route object for the translation page.
|
||||
*/
|
||||
public function getOverviewRoute();
|
||||
|
||||
/**
|
||||
* Returns a processed path for the translation overview route.
|
||||
*
|
||||
* @return string
|
||||
* Processed path with placeholders replaced.
|
||||
*/
|
||||
public function getOverviewPath();
|
||||
|
||||
/**
|
||||
* Returns route name for the translation add form route.
|
||||
*
|
||||
* @return string
|
||||
* Route name for the mapper.
|
||||
*/
|
||||
public function getAddRouteName();
|
||||
|
||||
/**
|
||||
* Returns the route parameters for the translation add form route.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAddRouteParameters();
|
||||
|
||||
/**
|
||||
* Returns the route object for a translation add form route.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
* The route object for the translation page.
|
||||
*/
|
||||
public function getAddRoute();
|
||||
|
||||
/**
|
||||
* Returns route name for the translation edit form route.
|
||||
*
|
||||
* @return string
|
||||
* Route name for the mapper.
|
||||
*/
|
||||
public function getEditRouteName();
|
||||
|
||||
/**
|
||||
* Returns the route parameters for the translation edit form route.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEditRouteParameters();
|
||||
|
||||
/**
|
||||
* Returns the route object for a translation edit form route.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
* The route object for the translation page.
|
||||
*/
|
||||
public function getEditRoute();
|
||||
|
||||
/**
|
||||
* Returns route name for the translation deletion route.
|
||||
*
|
||||
* @return string
|
||||
* Route name for the mapper.
|
||||
*/
|
||||
public function getDeleteRouteName();
|
||||
|
||||
/**
|
||||
* Returns the route parameters for the translation deletion route.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDeleteRouteParameters();
|
||||
|
||||
/**
|
||||
* Returns the route object for the translation deletion route.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
* The route object for the translation page.
|
||||
*/
|
||||
public function getDeleteRoute();
|
||||
|
||||
/**
|
||||
* Returns an array of configuration names for the mapper.
|
||||
*
|
||||
* @return array
|
||||
* An array of configuration names for the mapper.
|
||||
*/
|
||||
public function getConfigNames();
|
||||
|
||||
/**
|
||||
* Adds the given configuration name to the list of names.
|
||||
*
|
||||
* @param string $name
|
||||
* Configuration name.
|
||||
*/
|
||||
public function addConfigName($name);
|
||||
|
||||
/**
|
||||
* Returns the weight of the mapper.
|
||||
*
|
||||
* @return int
|
||||
* The weight of the mapper.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Returns an array with all configuration data.
|
||||
*
|
||||
* @return array
|
||||
* Configuration data keyed by configuration names.
|
||||
*/
|
||||
public function getConfigData();
|
||||
|
||||
/**
|
||||
* Returns the original language code of the configuration.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* Throws an exception if the language codes in the config files don't
|
||||
* match.
|
||||
*/
|
||||
public function getLangcode();
|
||||
|
||||
/**
|
||||
* Returns the name of the type of data the mapper encapsulates.
|
||||
*
|
||||
* @return string
|
||||
* The name of the type of data the mapper encapsulates.
|
||||
*/
|
||||
public function getTypeName();
|
||||
|
||||
/**
|
||||
* Provides an array of information to build a list of operation links.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of operation link data for this list, keyed by
|
||||
* operation name, containing the following key-value pairs:
|
||||
* - title: The localized title of the operation.
|
||||
* - href: The path for the operation.
|
||||
* - options: An array of URL options for the path.
|
||||
* - weight: The weight of this operation.
|
||||
*/
|
||||
public function getOperations();
|
||||
|
||||
/**
|
||||
* Returns the label of the type of data the mapper encapsulates.
|
||||
*
|
||||
* @return string
|
||||
* The label of the type of data the mapper encapsulates.
|
||||
*/
|
||||
public function getTypeLabel();
|
||||
|
||||
/**
|
||||
* Checks that all pieces of this configuration mapper have a schema.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if all of the elements have schema, FALSE otherwise.
|
||||
*/
|
||||
public function hasSchema();
|
||||
|
||||
/**
|
||||
* Checks that all pieces of this configuration mapper have translatables.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if all of the configuration elements have translatables, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
public function hasTranslatable();
|
||||
|
||||
/**
|
||||
* Checks whether there is already a translation for this mapper.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $language
|
||||
* A language object.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if any of the configuration elements have a translation in the
|
||||
* given language, FALSE otherwise.
|
||||
*/
|
||||
public function hasTranslation(LanguageInterface $language);
|
||||
|
||||
/**
|
||||
* Populate the config mapper with request data.
|
||||
*
|
||||
* @todo Replace $request with RouteMatch https://www.drupal.org/node/2295255.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* Page request object.
|
||||
*/
|
||||
public function populateFromRequest(Request $request);
|
||||
|
||||
/**
|
||||
* Returns the name of the contextual link group to add contextual links to.
|
||||
*
|
||||
* @return string|null
|
||||
* A contextual link group name or null if no link should be added.
|
||||
*/
|
||||
public function getContextualLinkGroup();
|
||||
|
||||
}
|
204
core/modules/config_translation/src/ConfigMapperManager.php
Normal file
204
core/modules/config_translation/src/ConfigMapperManager.php
Normal file
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigMapperManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
|
||||
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
|
||||
use Drupal\Core\Plugin\Factory\ContainerFactory;
|
||||
use Drupal\Core\TypedData\TraversableTypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Manages plugins for configuration translation mappers.
|
||||
*/
|
||||
class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperManagerInterface {
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfigManager;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaults = array(
|
||||
'title' => '',
|
||||
'names' => array(),
|
||||
'weight' => 20,
|
||||
'class' => '\Drupal\config_translation\ConfigNamesMapper',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs a ConfigMapperManager.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
|
||||
* The typed config manager.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManagerInterface $typed_config_manager, ThemeHandlerInterface $theme_handler) {
|
||||
$this->typedConfigManager = $typed_config_manager;
|
||||
|
||||
$this->factory = new ContainerFactory($this, '\Drupal\config_translation\ConfigMapperInterface');
|
||||
|
||||
// Let others alter definitions with hook_config_translation_info_alter().
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->themeHandler = $theme_handler;
|
||||
|
||||
$this->alterInfo('config_translation_info');
|
||||
// Config translation only uses an info hook discovery, cache by language.
|
||||
$cache_key = 'config_translation_info_plugins' . ':' . $language_manager->getCurrentLanguage()->getId();
|
||||
$this->setCacheBackend($cache_backend, $cache_key, array('config_translation_info_plugins'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDiscovery() {
|
||||
if (!isset($this->discovery)) {
|
||||
// Look at all themes and modules.
|
||||
// @todo If the list of installed modules and themes is changed, new
|
||||
// definitions are not picked up immediately and obsolete definitions are
|
||||
// not removed, because the list of search directories is only compiled
|
||||
// once in this constructor. The current code only works due to
|
||||
// coincidence: The request that installs e.g. a new theme does not
|
||||
// instantiate this plugin manager at the beginning of the request; when
|
||||
// routes are being rebuilt at the end of the request, this service only
|
||||
// happens to get instantiated with the updated list of installed themes.
|
||||
$directories = array();
|
||||
foreach ($this->moduleHandler->getModuleList() as $name => $module) {
|
||||
$directories[$name] = $module->getPath();
|
||||
}
|
||||
foreach ($this->themeHandler->listInfo() as $theme) {
|
||||
$directories[$theme->getName()] = $theme->getPath();
|
||||
}
|
||||
|
||||
// Check for files named MODULE.config_translation.yml and
|
||||
// THEME.config_translation.yml in module/theme roots.
|
||||
$this->discovery = new YamlDiscovery('config_translation', $directories);
|
||||
$this->discovery = new InfoHookDecorator($this->discovery, 'config_translation_info');
|
||||
$this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
|
||||
}
|
||||
return $this->discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMappers(RouteCollection $collection = NULL) {
|
||||
$mappers = array();
|
||||
foreach($this->getDefinitions() as $id => $definition) {
|
||||
$mappers[$id] = $this->createInstance($id);
|
||||
if ($collection) {
|
||||
$mappers[$id]->setRouteCollection($collection);
|
||||
}
|
||||
}
|
||||
|
||||
return $mappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processDefinition(&$definition, $plugin_id) {
|
||||
parent::processDefinition($definition, $plugin_id);
|
||||
|
||||
if (!isset($definition['base_route_name'])) {
|
||||
throw new InvalidPluginDefinitionException($plugin_id, SafeMarkup::format("The plugin definition of the mapper '%plugin_id' does not contain a base_route_name.", array('%plugin_id' => $plugin_id)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildDataDefinition(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
|
||||
return $this->typedConfigManager->buildDataDefinition($definition, $value, $name, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findDefinitions() {
|
||||
$definitions = $this->getDiscovery()->getDefinitions();
|
||||
foreach ($definitions as $plugin_id => &$definition) {
|
||||
$this->processDefinition($definition, $plugin_id);
|
||||
}
|
||||
if ($this->alterHook) {
|
||||
$this->moduleHandler->alter($this->alterHook, $definitions);
|
||||
}
|
||||
|
||||
// If this plugin was provided by a module that does not exist, remove the
|
||||
// plugin definition.
|
||||
foreach ($definitions as $plugin_id => $plugin_definition) {
|
||||
if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array('core', 'component')) && (!$this->moduleHandler->moduleExists($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array_keys($this->themeHandler->listInfo())))) {
|
||||
unset($definitions[$plugin_id]);
|
||||
}
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTranslatable($name) {
|
||||
return $this->findTranslatable($this->typedConfigManager->get($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if at least one translatable element is found.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $element
|
||||
* Configuration schema element.
|
||||
*
|
||||
* @return bool
|
||||
* A boolean indicating if there is at least one translatable element.
|
||||
*/
|
||||
protected function findTranslatable(TypedDataInterface $element) {
|
||||
// In case this is a sequence or a mapping check whether any child element
|
||||
// is translatable.
|
||||
if ($element instanceof TraversableTypedDataInterface) {
|
||||
foreach ($element as $child_element) {
|
||||
if ($this->findTranslatable($child_element)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// If none of the child elements are translatable, return FALSE.
|
||||
return FALSE;
|
||||
}
|
||||
else {
|
||||
$definition = $element->getDataDefinition();
|
||||
return isset($definition['translatable']) && $definition['translatable'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigMapperManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Provides a common interface for config mapper managers.
|
||||
*/
|
||||
interface ConfigMapperManagerInterface extends PluginManagerInterface {
|
||||
|
||||
/**
|
||||
* Returns an array of all mappers.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\RouteCollection $collection
|
||||
* The route collection used to initialize the mappers.
|
||||
*
|
||||
* @return \Drupal\config_translation\ConfigMapperInterface[]
|
||||
* An array of all mappers.
|
||||
*/
|
||||
public function getMappers(RouteCollection $collection = NULL);
|
||||
|
||||
/**
|
||||
* Returns TRUE if the configuration data has translatable items.
|
||||
*
|
||||
* @param string $name
|
||||
* Configuration key.
|
||||
*
|
||||
* @return bool
|
||||
* A boolean indicating if the configuration data has translatable items.
|
||||
*/
|
||||
public function hasTranslatable($name);
|
||||
|
||||
}
|
483
core/modules/config_translation/src/ConfigNamesMapper.php
Normal file
483
core/modules/config_translation/src/ConfigNamesMapper.php
Normal file
|
@ -0,0 +1,483 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\ConfigNamesMapper.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\locale\LocaleConfigManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Configuration mapper base implementation.
|
||||
*/
|
||||
class ConfigNamesMapper extends PluginBase implements ConfigMapperInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfigManager;
|
||||
|
||||
/**
|
||||
* The typed configuration manager.
|
||||
*
|
||||
* @var \Drupal\locale\LocaleConfigManager
|
||||
*/
|
||||
protected $localeConfigManager;
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $configMapperManager;
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* The base route object that the mapper is attached to.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route
|
||||
*/
|
||||
protected $baseRoute;
|
||||
|
||||
/**
|
||||
* The available routes.
|
||||
*
|
||||
* @var \Symfony\Component\Routing\RouteCollection
|
||||
*/
|
||||
protected $routeCollection;
|
||||
|
||||
/**
|
||||
* The language code of the language this mapper, if any.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $langcode = NULL;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigNamesMapper.
|
||||
*
|
||||
* @param $plugin_id
|
||||
* The config mapper plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* An array of plugin information with the following keys:
|
||||
* - title: The title of the mapper, used for generating page titles.
|
||||
* - base_route_name: The route name of the base route this mapper is
|
||||
* attached to.
|
||||
* - names: (optional) An array of configuration names.
|
||||
* - weight: (optional) The weight of this mapper, used in mapper listings.
|
||||
* Defaults to 20.
|
||||
* - list_controller: (optional) Class name for list controller used to
|
||||
* generate lists of this type of configuration.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* The typed configuration manager.
|
||||
* @param \Drupal\locale\LocaleConfigManager $locale_config_manager
|
||||
* The locale configuration manager.
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface
|
||||
* The route provider.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation manager.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*
|
||||
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
|
||||
* Throws an exception if the route specified by the 'base_route_name' in
|
||||
* the plugin definition could not be found by the route provider.
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, LocaleConfigManager $locale_config_manager, ConfigMapperManagerInterface $config_mapper_manager, RouteProviderInterface $route_provider, TranslationInterface $string_translation, LanguageManagerInterface $language_manager) {
|
||||
$this->pluginId = $plugin_id;
|
||||
$this->pluginDefinition = $plugin_definition;
|
||||
$this->routeProvider = $route_provider;
|
||||
|
||||
$this->configFactory = $config_factory;
|
||||
$this->typedConfigManager = $typed_config;
|
||||
$this->localeConfigManager = $locale_config_manager;
|
||||
$this->configMapperManager = $config_mapper_manager;
|
||||
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
// Note that we ignore the plugin $configuration because mappers have
|
||||
// nothing to configure in themselves.
|
||||
return new static (
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('config.factory'),
|
||||
$container->get('config.typed'),
|
||||
$container->get('locale.config_manager'),
|
||||
$container->get('plugin.manager.config_translation.mapper'),
|
||||
$container->get('router.route_provider'),
|
||||
$container->get('string_translation'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRouteCollection(RouteCollection $collection) {
|
||||
$this->routeCollection = $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
// A title from a *.config_translation.yml. Should be translated for
|
||||
// display in the current page language.
|
||||
return $this->t($this->pluginDefinition['title']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseRouteName() {
|
||||
return $this->pluginDefinition['base_route_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseRouteParameters() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseRoute() {
|
||||
if ($this->routeCollection) {
|
||||
return $this->routeCollection->get($this->getBaseRouteName());
|
||||
}
|
||||
else {
|
||||
return $this->routeProvider->getRouteByName($this->getBaseRouteName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to process all config translation routes.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route object to process.
|
||||
*/
|
||||
protected function processRoute(Route $route) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBasePath() {
|
||||
return Url::fromRoute($this->getBaseRouteName(), $this->getBaseRouteParameters())->getInternalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewRouteName() {
|
||||
return 'config_translation.item.overview.' . $this->getBaseRouteName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewRouteParameters() {
|
||||
return $this->getBaseRouteParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewRoute() {
|
||||
$route = new Route(
|
||||
$this->getBaseRoute()->getPath() . '/translate',
|
||||
array(
|
||||
'_controller' => '\Drupal\config_translation\Controller\ConfigTranslationController::itemPage',
|
||||
'plugin_id' => $this->getPluginId(),
|
||||
),
|
||||
array('_config_translation_overview_access' => 'TRUE')
|
||||
);
|
||||
$this->processRoute($route);
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOverviewPath() {
|
||||
return Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters())->getInternalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAddRouteName() {
|
||||
return 'config_translation.item.add.' . $this->getBaseRouteName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAddRouteParameters() {
|
||||
// If sub-classes provide route parameters in getBaseRouteParameters(), they
|
||||
// probably also want to provide those for the add, edit, and delete forms.
|
||||
$parameters = $this->getBaseRouteParameters();
|
||||
$parameters['langcode'] = $this->langcode;
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAddRoute() {
|
||||
$route = new Route(
|
||||
$this->getBaseRoute()->getPath() . '/translate/{langcode}/add',
|
||||
array(
|
||||
'_form' => '\Drupal\config_translation\Form\ConfigTranslationAddForm',
|
||||
'plugin_id' => $this->getPluginId(),
|
||||
),
|
||||
array('_config_translation_form_access' => 'TRUE')
|
||||
);
|
||||
$this->processRoute($route);
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditRouteName() {
|
||||
return 'config_translation.item.edit.' . $this->getBaseRouteName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditRouteParameters() {
|
||||
return $this->getAddRouteParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEditRoute() {
|
||||
$route = new Route(
|
||||
$this->getBaseRoute()->getPath() . '/translate/{langcode}/edit',
|
||||
array(
|
||||
'_form' => '\Drupal\config_translation\Form\ConfigTranslationEditForm',
|
||||
'plugin_id' => $this->getPluginId(),
|
||||
),
|
||||
array('_config_translation_form_access' => 'TRUE')
|
||||
);
|
||||
$this->processRoute($route);
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDeleteRouteName() {
|
||||
return 'config_translation.item.delete.' . $this->getBaseRouteName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDeleteRouteParameters() {
|
||||
return $this->getAddRouteParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDeleteRoute() {
|
||||
$route = new Route(
|
||||
$this->getBaseRoute()->getPath() . '/translate/{langcode}/delete',
|
||||
array(
|
||||
'_form' => '\Drupal\config_translation\Form\ConfigTranslationDeleteForm',
|
||||
'plugin_id' => $this->getPluginId(),
|
||||
),
|
||||
array('_config_translation_form_access' => 'TRUE')
|
||||
);
|
||||
$this->processRoute($route);
|
||||
return $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigNames() {
|
||||
return $this->pluginDefinition['names'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addConfigName($name) {
|
||||
$this->pluginDefinition['names'][] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->pluginDefinition['weight'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function populateFromRequest(Request $request) {
|
||||
if ($request->attributes->has('langcode')) {
|
||||
$this->langcode = $request->attributes->get('langcode');
|
||||
}
|
||||
else {
|
||||
$this->langcode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeLabel() {
|
||||
return $this->getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLangcode() {
|
||||
$config_factory = $this->configFactory;
|
||||
$langcodes = array_map(function($name) use ($config_factory) {
|
||||
// Default to English if no language code was provided in the file.
|
||||
// Although it is a best practice to include a language code, if the
|
||||
// developer did not think about a multilingual use-case, we fall back
|
||||
// on assuming the file is English.
|
||||
return $config_factory->get($name)->get('langcode') ?: 'en';
|
||||
}, $this->getConfigNames());
|
||||
|
||||
if (count(array_unique($langcodes)) > 1) {
|
||||
throw new \RuntimeException('A config mapper can only contain configuration for a single language.');
|
||||
}
|
||||
|
||||
return reset($langcodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigData() {
|
||||
$config_data = array();
|
||||
foreach ($this->getConfigNames() as $name) {
|
||||
$config_data[$name] = $this->configFactory->getEditable($name)->get();
|
||||
}
|
||||
return $config_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasSchema() {
|
||||
foreach ($this->getConfigNames() as $name) {
|
||||
if (!$this->typedConfigManager->hasConfigSchema($name)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTranslatable() {
|
||||
foreach ($this->getConfigNames() as $name) {
|
||||
if (!$this->configMapperManager->hasTranslatable($name)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTranslation(LanguageInterface $language) {
|
||||
foreach ($this->getConfigNames() as $name) {
|
||||
if ($this->localeConfigManager->hasTranslation($name, $language->getId())) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeName() {
|
||||
return $this->t('Settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOperations() {
|
||||
return array(
|
||||
'translate' => array(
|
||||
'title' => $this->t('Translate'),
|
||||
'url' => Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContextualLinkGroup() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationBlockListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the config translation list builder for blocks.
|
||||
*/
|
||||
class ConfigTranslationBlockListBuilder extends ConfigTranslationEntityListBuilder {
|
||||
|
||||
/**
|
||||
* An array of theme info keyed by theme name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $themes = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeHandlerInterface $theme_handler) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->themes = $theme_handler->listInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('theme_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilterLabels() {
|
||||
$info = parent::getFilterLabels();
|
||||
|
||||
$info['placeholder'] = $this->t('Enter block, theme or category');
|
||||
$info['description'] = $this->t('Enter a part of the block, theme or category to filter by.');
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$theme = $entity->getTheme();
|
||||
$plugin_definition = $entity->getPlugin()->getPluginDefinition();
|
||||
|
||||
$row['label'] = array(
|
||||
'data' => $this->getLabel($entity),
|
||||
'class' => 'table-filter-text-source',
|
||||
);
|
||||
|
||||
$row['theme'] = array(
|
||||
'data' => SafeMarkup::checkPlain($this->themes[$theme]->info['name']),
|
||||
'class' => 'table-filter-text-source',
|
||||
);
|
||||
|
||||
$row['category'] = array(
|
||||
'data' => SafeMarkup::checkPlain($plugin_definition['category']),
|
||||
'class' => 'table-filter-text-source',
|
||||
);
|
||||
|
||||
$row['operations']['data'] = $this->buildOperations($entity);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Block');
|
||||
$header['theme'] = $this->t('Theme');
|
||||
$header['category'] = $this->t('Category');
|
||||
$header['operations'] = $this->t('Operations');
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortRows($a, $b) {
|
||||
return $this->sortRowsMultiple($a, $b, array('theme', 'category', 'label'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationController.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
|
||||
|
||||
/**
|
||||
* Provides page callbacks for the configuration translation interface.
|
||||
*/
|
||||
class ConfigTranslationController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The configuration mapper manager.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $configMapperManager;
|
||||
|
||||
/**
|
||||
* The menu link access service.
|
||||
*
|
||||
* @var \Drupal\Core\Access\AccessManagerInterface
|
||||
*/
|
||||
protected $accessManager;
|
||||
|
||||
/**
|
||||
* The dynamic router service.
|
||||
*
|
||||
* @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
/**
|
||||
* The path processor service.
|
||||
*
|
||||
* @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
|
||||
*/
|
||||
protected $pathProcessor;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigTranslationController.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The configuration mapper manager.
|
||||
* @param \Drupal\Core\Access\AccessManagerInterface $access_manager
|
||||
* The menu link access service.
|
||||
* @param \Symfony\Component\Routing\Matcher\RequestMatcherInterface $router
|
||||
* The dynamic router service.
|
||||
* @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $path_processor
|
||||
* The inbound path processor.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager) {
|
||||
$this->configMapperManager = $config_mapper_manager;
|
||||
$this->accessManager = $access_manager;
|
||||
$this->router = $router;
|
||||
$this->pathProcessor = $path_processor;
|
||||
$this->account = $account;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.config_translation.mapper'),
|
||||
$container->get('access_manager'),
|
||||
$container->get('router'),
|
||||
$container->get('path_processor_manager'),
|
||||
$container->get('current_user'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Language translations overview page for a configuration name.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* Page request object.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID of the mapper.
|
||||
*
|
||||
* @return array
|
||||
* Page render array.
|
||||
*/
|
||||
public function itemPage(Request $request, RouteMatchInterface $route_match, $plugin_id) {
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$mapper = $this->configMapperManager->createInstance($plugin_id);
|
||||
$mapper->populateFromRequest($request);
|
||||
|
||||
$page = array();
|
||||
$page['#title'] = $this->t('Translations for %label', array('%label' => $mapper->getTitle()));
|
||||
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
if (count($languages) == 1) {
|
||||
drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href="@url">languages</a>.', array('@url' => $this->url('entity.configurable_language.collection'))), 'warning');
|
||||
}
|
||||
$original_langcode = $mapper->getLangcode();
|
||||
if (!isset($languages[$original_langcode])) {
|
||||
// If the language is not configured on the site, create a dummy language
|
||||
// object for this listing only to ensure the user gets useful info.
|
||||
$language_name = $this->languageManager->getLanguageName($original_langcode);
|
||||
$languages[$original_langcode] = new Language(array('id' => $original_langcode, 'name' => $language_name));
|
||||
}
|
||||
|
||||
// We create a fake request object to pass into
|
||||
// ConfigMapperInterface::populateFromRequest() for the different languages.
|
||||
// Creating a separate request for each language and route is neither easily
|
||||
// possible nor performant.
|
||||
$fake_request = $request->duplicate();
|
||||
|
||||
$page['languages'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array($this->t('Language'), $this->t('Operations')),
|
||||
);
|
||||
foreach ($languages as $language) {
|
||||
$langcode = $language->getId();
|
||||
|
||||
// This is needed because
|
||||
// ConfigMapperInterface::getAddRouteParameters(), for example,
|
||||
// needs to return the correct language code for each table row.
|
||||
$fake_request->attributes->set('langcode', $langcode);
|
||||
$mapper->populateFromRequest($fake_request);
|
||||
|
||||
// Prepare the language name and the operations depending on whether this
|
||||
// is the original language or not.
|
||||
if ($langcode == $original_langcode) {
|
||||
$language_name = '<strong>' . $this->t('@language (original)', array('@language' => $language->getName())) . '</strong>';
|
||||
|
||||
// Check access for the path/route for editing, so we can decide to
|
||||
// include a link to edit or not.
|
||||
$edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $route_match->getRawParameters()->all(), $this->account);
|
||||
|
||||
// Build list of operations.
|
||||
$operations = array();
|
||||
if ($edit_access) {
|
||||
$operations['edit'] = array(
|
||||
'title' => $this->t('Edit'),
|
||||
'url' => Url::fromRoute($mapper->getBaseRouteName(), $mapper->getBaseRouteParameters(), ['query' => ['destination' => $mapper->getOverviewPath()]]),
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$language_name = $language->getName();
|
||||
|
||||
$operations = array();
|
||||
// If no translation exists for this language, link to add one.
|
||||
if (!$mapper->hasTranslation($language)) {
|
||||
$operations['add'] = array(
|
||||
'title' => $this->t('Add'),
|
||||
'url' => Url::fromRoute($mapper->getAddRouteName(), $mapper->getAddRouteParameters()),
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Otherwise, link to edit the existing translation.
|
||||
$operations['edit'] = array(
|
||||
'title' => $this->t('Edit'),
|
||||
'url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters()),
|
||||
);
|
||||
|
||||
$operations['delete'] = array(
|
||||
'title' => $this->t('Delete'),
|
||||
'url' => Url::fromRoute($mapper->getDeleteRouteName(), $mapper->getDeleteRouteParameters()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$page['languages'][$langcode]['language'] = array(
|
||||
'#markup' => $language_name,
|
||||
);
|
||||
|
||||
$page['languages'][$langcode]['operations'] = array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $operations,
|
||||
);
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationEntityListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
|
||||
/**
|
||||
* Defines the configuration translation list builder for entities.
|
||||
*/
|
||||
class ConfigTranslationEntityListBuilder extends ConfigEntityListBuilder implements ConfigTranslationEntityListBuilderInterface {
|
||||
|
||||
/**
|
||||
* Provides user facing strings for the filter element.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFilterLabels() {
|
||||
return array(
|
||||
'placeholder' => $this->t('Enter label'),
|
||||
'description' => $this->t('Enter a part of the label or description to filter by.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$build = parent::render();
|
||||
$filter = $this->getFilterLabels();
|
||||
|
||||
usort($build['table']['#rows'], array($this, 'sortRows'));
|
||||
|
||||
$build['filters'] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array(
|
||||
'class' => array('table-filter', 'js-show'),
|
||||
),
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
$build['filters']['text'] = array(
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Search'),
|
||||
'#size' => 30,
|
||||
'#placeholder' => $filter['placeholder'],
|
||||
'#attributes' => array(
|
||||
'class' => array('table-filter-text'),
|
||||
'data-table' => '.config-translation-entity-list',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $filter['description'],
|
||||
),
|
||||
);
|
||||
|
||||
$build['table']['#attributes']['class'][] = 'config-translation-entity-list';
|
||||
$build['table']['#weight'] = 0;
|
||||
$build['#attached']['library'][] = 'system/drupal.system.modules';
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label']['data'] = $this->getLabel($entity);
|
||||
$row['label']['class'][] = 'table-filter-text-source';
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Label');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
foreach (array_keys($operations) as $operation) {
|
||||
// This is a translation UI for translators. Show the translation
|
||||
// operation only.
|
||||
if (!($operation == 'translate')) {
|
||||
unset($operations[$operation]);
|
||||
}
|
||||
}
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortRows($a, $b) {
|
||||
return $this->sortRowsMultiple($a, $b, array('label'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array by multiple criteria.
|
||||
*
|
||||
* @param array $a
|
||||
* First item for comparison.
|
||||
* @param array $b
|
||||
* Second item for comparison.
|
||||
* @param array $keys
|
||||
* The array keys to sort on.
|
||||
*
|
||||
* @return int
|
||||
* The comparison result for uasort().
|
||||
*/
|
||||
protected function sortRowsMultiple($a, $b, $keys) {
|
||||
$key = array_shift($keys);
|
||||
$a_value = (is_array($a) && isset($a[$key]['data'])) ? $a[$key]['data'] : '';
|
||||
$b_value = (is_array($b) && isset($b[$key]['data'])) ? $b[$key]['data'] : '';
|
||||
|
||||
if ($a_value == $b_value && !empty($keys)) {
|
||||
return $this->sortRowsMultiple($a, $b, $keys);
|
||||
}
|
||||
|
||||
return strnatcasecmp($a_value, $b_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMapperDefinition($mapper_definition) {
|
||||
// @todo Why is this method called on all config list controllers?
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationEntityListBuilderInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\Core\Entity\EntityListBuilderInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for configuration translation entity list builders.
|
||||
*/
|
||||
interface ConfigTranslationEntityListBuilderInterface extends EntityListBuilderInterface {
|
||||
|
||||
/**
|
||||
* Sorts an array by value.
|
||||
*
|
||||
* @param array $a
|
||||
* First item for comparison.
|
||||
* @param array $b
|
||||
* Second item for comparison.
|
||||
*
|
||||
* @return int
|
||||
* The comparison result for uasort().
|
||||
*/
|
||||
public function sortRows($a, $b);
|
||||
|
||||
/**
|
||||
* Sets the config translation mapper definition.
|
||||
*
|
||||
* @param mixed $mapper_definition
|
||||
* The plugin definition of the config translation mapper.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMapperDefinition($mapper_definition);
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationFieldListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the config translation list builder for field entities.
|
||||
*/
|
||||
class ConfigTranslationFieldListBuilder extends ConfigTranslationEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The name of the entity type the fields are attached to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseEntityType = '';
|
||||
|
||||
/**
|
||||
* An array containing the base entity type's definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $baseEntityInfo = array();
|
||||
|
||||
/**
|
||||
* The bundle info for the base entity type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $baseEntityBundles = array();
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
$entity_manager = $container->get('entity.manager');
|
||||
return new static(
|
||||
$entity_type,
|
||||
$entity_manager->getStorage($entity_type->id()),
|
||||
$entity_manager
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigTranslationFieldListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, EntityManagerInterface $entity_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMapperDefinition($mapper_definition) {
|
||||
$this->baseEntityType = $mapper_definition['base_entity_type'];
|
||||
$this->baseEntityInfo = $this->entityManager->getDefinition($this->baseEntityType);
|
||||
$this->baseEntityBundles = $this->entityManager->getBundleInfo($this->baseEntityType);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
// It is not possible to use the standard load method, because this needs
|
||||
// all field entities only for the given baseEntityType.
|
||||
$ids = \Drupal::entityQuery('field_config')
|
||||
->condition('id', $this->baseEntityType . '.', 'STARTS_WITH')
|
||||
->execute();
|
||||
return $this->storage->loadMultiple($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilterLabels() {
|
||||
$info = parent::getFilterLabels();
|
||||
$bundle = $this->baseEntityInfo->getBundleLabel() ?: $this->t('Bundle');
|
||||
$bundle = Unicode::strtolower($bundle);
|
||||
|
||||
$info['placeholder'] = $this->t('Enter field or @bundle', array('@bundle' => $bundle));
|
||||
$info['description'] = $this->t('Enter a part of the field or @bundle to filter by.', array('@bundle' => $bundle));
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
$row['label'] = array(
|
||||
'data' => $this->getLabel($entity),
|
||||
'class' => 'table-filter-text-source',
|
||||
);
|
||||
|
||||
if ($this->displayBundle()) {
|
||||
$bundle = $entity->get('bundle');
|
||||
$row['bundle'] = array(
|
||||
'data' => SafeMarkup::checkPlain($this->baseEntityBundles[$bundle]['label']),
|
||||
'class' => 'table-filter-text-source',
|
||||
);
|
||||
}
|
||||
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Field');
|
||||
if ($this->displayBundle()) {
|
||||
$header['bundle'] = $this->baseEntityInfo->getBundleLabel() ?: $this->t('Bundle');
|
||||
}
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the visibility of the bundle column on field list pages.
|
||||
*
|
||||
* @return bool
|
||||
* Whenever the bundle is displayed or not.
|
||||
*/
|
||||
public function displayBundle() {
|
||||
// The bundle key is explicitly defined in the entity definition.
|
||||
if ($this->baseEntityInfo->getKey('bundle')) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// There is more than one bundle defined.
|
||||
if (count($this->baseEntityBundles) > 1) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The defined bundle ones not match the entity type name.
|
||||
if (!empty($this->baseEntityBundles) && !isset($this->baseEntityBundles[$this->baseEntityType])) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortRows($a, $b) {
|
||||
return $this->sortRowsMultiple($a, $b, array('bundle', 'label'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationListController.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Defines the configuration translation list controller.
|
||||
*/
|
||||
class ConfigTranslationListController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The mapper manager.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigTranslationListController object.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $mapper_manager
|
||||
* The config mapper manager.
|
||||
*/
|
||||
public function __construct(ConfigMapperManagerInterface $mapper_manager) {
|
||||
$this->mapperManager = $mapper_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.config_translation.mapper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the listing page for any entity type.
|
||||
*
|
||||
* @param string $mapper_id
|
||||
* The name of the mapper.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Throws an exception if a mapper plugin could not be instantiated from the
|
||||
* mapper definition in the constructor.
|
||||
*/
|
||||
public function listing($mapper_id) {
|
||||
$mapper_definition = $this->mapperManager->getDefinition($mapper_id);
|
||||
$mapper = $this->mapperManager->createInstance($mapper_id, $mapper_definition);
|
||||
if (!$mapper) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$entity_type = $mapper->getType();
|
||||
// If the mapper, for example the mapper for fields, has a custom list
|
||||
// controller defined, use it. Other mappers, for examples the ones for
|
||||
// node_type and block, fallback to the generic configuration translation
|
||||
// list controller.
|
||||
$build = $this->entityManager()
|
||||
->getHandler($entity_type, 'config_translation_list')
|
||||
->setMapperDefinition($mapper_definition)
|
||||
->render();
|
||||
$build['#title'] = $mapper->getTypeLabel();
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Controller\ConfigTranslationMapperList.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\config_translation\ConfigMapperInterface;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the configuration translation mapper list.
|
||||
*
|
||||
* Groups all defined configuration mapper instances by weight.
|
||||
*/
|
||||
class ConfigTranslationMapperList extends ControllerBase {
|
||||
|
||||
/**
|
||||
* A array of configuration mapper instances.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperInterface[]
|
||||
*/
|
||||
protected $mappers;
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigTranslationMapperList object.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperInterface[] $mappers
|
||||
* The configuration mapper manager.
|
||||
*/
|
||||
public function __construct(array $mappers) {
|
||||
$this->mappers = $mappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.config_translation.mapper')->getMappers()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the mappers as a renderable array for table.html.twig.
|
||||
*
|
||||
* @return array
|
||||
* Renderable array with config translation mappers.
|
||||
*/
|
||||
public function render() {
|
||||
$build = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $this->buildHeader(),
|
||||
'#rows' => array(),
|
||||
);
|
||||
|
||||
$mappers = array();
|
||||
|
||||
foreach ($this->mappers as $mapper) {
|
||||
if ($row = $this->buildRow($mapper)) {
|
||||
$mappers[$mapper->getWeight()][] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
// Group by mapper weight and sort by label.
|
||||
ksort($mappers);
|
||||
foreach ($mappers as $weight => $mapper) {
|
||||
usort($mapper, function ($a, $b) {
|
||||
$a_title = (isset($a['label'])) ? $a['label'] : '';
|
||||
$b_title = (isset($b['label'])) ? $b['label'] : '';
|
||||
return strnatcasecmp($a_title, $b_title);
|
||||
});
|
||||
$mappers[$weight] = $mapper;
|
||||
}
|
||||
|
||||
foreach ($mappers as $mapper) {
|
||||
$build['#rows'] = array_merge($build['#rows'], $mapper);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a row for a mapper in the mapper listing.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperInterface $mapper
|
||||
* The mapper.
|
||||
*
|
||||
* @return array
|
||||
* A render array structure of fields for this mapper.
|
||||
*/
|
||||
public function buildRow(ConfigMapperInterface $mapper) {
|
||||
$row['label'] = SafeMarkup::checkPlain($mapper->getTypeLabel());
|
||||
$row['operations']['data'] = $this->buildOperations($mapper);
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the header row for the mapper listing.
|
||||
*
|
||||
* @return array
|
||||
* A render array structure of header strings.
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$row['Label'] = $this->t('Label');
|
||||
$row['operations'] = $this->t('Operations');
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a renderable list of operation links for the entity.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperInterface $mapper
|
||||
* The mapper.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array of operation links.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityList::buildOperations()
|
||||
*/
|
||||
protected function buildOperations(ConfigMapperInterface $mapper) {
|
||||
// Retrieve and sort operations.
|
||||
$operations = $mapper->getOperations();
|
||||
uasort($operations, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
|
||||
$build = array(
|
||||
'#type' => 'operations',
|
||||
'#links' => $operations,
|
||||
);
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Form\ConfigTranslationAddForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines a form for adding configuration translations.
|
||||
*/
|
||||
class ConfigTranslationAddForm extends ConfigTranslationFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_translation_add_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL) {
|
||||
$form = parent::buildForm($form, $form_state, $request, $plugin_id, $langcode);
|
||||
$form['#title'] = $this->t('Add @language translation for %label', array(
|
||||
'%label' => $this->mapper->getTitle(),
|
||||
'@language' => $this->language->getName(),
|
||||
));
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
drupal_set_message($this->t('Successfully saved @language translation.', array('@language' => $this->language->getName())));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Form\ConfigTranslationDeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Form;
|
||||
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Builds a form to delete configuration translation.
|
||||
*/
|
||||
class ConfigTranslationDeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The configuration mapper manager.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $configMapperManager;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The configuration translation to be deleted.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperInterface
|
||||
*/
|
||||
protected $mapper;
|
||||
|
||||
/**
|
||||
* The language of configuration translation.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* Constructs a ConfigTranslationDeleteForm.
|
||||
*
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The language override configuration storage.
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The configuration mapper manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(ConfigurableLanguageManagerInterface $language_manager, ConfigMapperManagerInterface $config_mapper_manager, ModuleHandlerInterface $module_handler) {
|
||||
$this->languageManager = $language_manager;
|
||||
$this->configMapperManager = $config_mapper_manager;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('language_manager'),
|
||||
$container->get('plugin.manager.config_translation.mapper'),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete the @language translation of %label?', array('%label' => $this->mapper->getTitle(), '@language' => $this->language->getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url($this->mapper->getOverviewRouteName(), $this->mapper->getOverviewRouteParameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_translation_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL) {
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$mapper = $this->configMapperManager->createInstance($plugin_id);
|
||||
$mapper->populateFromRequest($request);
|
||||
|
||||
$language = $this->languageManager->getLanguage($langcode);
|
||||
if (!$language) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->mapper = $mapper;
|
||||
$this->language = $language;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach ($this->mapper->getConfigNames() as $name) {
|
||||
$this->languageManager->getLanguageConfigOverride($this->language->getId(), $name)->delete();
|
||||
}
|
||||
|
||||
// Flush all persistent caches.
|
||||
$this->moduleHandler->invokeAll('cache_flush');
|
||||
foreach (Cache::getBins() as $service_id => $cache_backend) {
|
||||
$cache_backend->deleteAll();
|
||||
}
|
||||
|
||||
drupal_set_message($this->t('@language translation of %label was deleted', array('%label' => $this->mapper->getTitle(), '@language' => $this->language->getName())));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Form\ConfigTranslationEditForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines a form for editing configuration translations.
|
||||
*/
|
||||
class ConfigTranslationEditForm extends ConfigTranslationFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_translation_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL) {
|
||||
$form = parent::buildForm($form, $form_state, $request, $plugin_id, $langcode);
|
||||
$form['#title'] = $this->t('Edit @language translation for %label', array(
|
||||
'%label' => $this->mapper->getTitle(),
|
||||
'@language' => $this->language->getName(),
|
||||
));
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
drupal_set_message($this->t('Successfully updated @language translation.', array('@language' => $this->language->getName())));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Form\ConfigTranslationFormBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Form;
|
||||
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TraversableTypedDataInterface;
|
||||
use Drupal\Core\Form\BaseFormIdInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides a base form for configuration translations.
|
||||
*/
|
||||
abstract class ConfigTranslationFormBase extends FormBase implements BaseFormIdInterface {
|
||||
|
||||
/**
|
||||
* The typed configuration manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfigManager;
|
||||
|
||||
/**
|
||||
* The configuration mapper manager.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $configMapperManager;
|
||||
|
||||
/**
|
||||
* The mapper for configuration translation.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperInterface
|
||||
*/
|
||||
protected $mapper;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\language\ConfigurableLanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The language of the configuration translation.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* The language of the configuration translation source.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageInterface
|
||||
*/
|
||||
protected $sourceLanguage;
|
||||
|
||||
/**
|
||||
* An array of base language configuration data keyed by configuration names.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $baseConfigData = array();
|
||||
|
||||
/**
|
||||
* Constructs a ConfigTranslationFormBase.
|
||||
*
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
|
||||
* The typed configuration manager.
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
|
||||
* The configuration mapper manager.
|
||||
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
|
||||
* The configurable language manager.
|
||||
*/
|
||||
public function __construct(TypedConfigManagerInterface $typed_config_manager, ConfigMapperManagerInterface $config_mapper_manager, ConfigurableLanguageManagerInterface $language_manager) {
|
||||
$this->typedConfigManager = $typed_config_manager;
|
||||
$this->configMapperManager = $config_mapper_manager;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.typed'),
|
||||
$container->get('plugin.manager.config_translation.mapper'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return 'config_translation_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Form\FormInterface::buildForm().
|
||||
*
|
||||
* Builds configuration form with metadata and values from the source
|
||||
* language.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* (optional) Page request object.
|
||||
* @param string $plugin_id
|
||||
* (optional) The plugin ID of the mapper.
|
||||
* @param string $langcode
|
||||
* (optional) The language code of the language the form is adding or
|
||||
* editing.
|
||||
*
|
||||
* @return array
|
||||
* The form structure.
|
||||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* Throws an exception if the language code provided as a query parameter in
|
||||
* the request does not match an active language.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL) {
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$mapper = $this->configMapperManager->createInstance($plugin_id);
|
||||
$mapper->populateFromRequest($request);
|
||||
|
||||
$language = $this->languageManager->getLanguage($langcode);
|
||||
if (!$language) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->mapper = $mapper;
|
||||
$this->language = $language;
|
||||
$this->sourceLanguage = $this->languageManager->getLanguage($this->mapper->getLangcode());
|
||||
|
||||
// Get base language configuration to display in the form before setting the
|
||||
// language to use for the form. This avoids repetitively settings and
|
||||
// resetting the language to get original values later.
|
||||
$this->baseConfigData = $this->mapper->getConfigData();
|
||||
|
||||
// Set the translation target language on the configuration factory.
|
||||
$original_language = $this->languageManager->getConfigOverrideLanguage();
|
||||
$this->languageManager->setConfigOverrideLanguage($this->language);
|
||||
|
||||
// Add some information to the form state for easier form altering.
|
||||
$form_state->set('config_translation_mapper', $this->mapper);
|
||||
$form_state->set('config_translation_language', $this->language);
|
||||
$form_state->set('config_translation_source_language', $this->sourceLanguage);
|
||||
|
||||
$form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin';
|
||||
|
||||
// Even though this is a nested form, we do not set #tree to TRUE because
|
||||
// the form value structure is generated by using #parents for each element.
|
||||
// @see \Drupal\config_translation\FormElement\FormElementBase::getElements()
|
||||
$form['config_names'] = array('#type' => 'container');
|
||||
foreach ($this->mapper->getConfigNames() as $name) {
|
||||
$form['config_names'][$name] = array('#type' => 'container');
|
||||
|
||||
$schema = $this->typedConfigManager->get($name);
|
||||
$source_config = $this->baseConfigData[$name];
|
||||
$translation_config = $this->configFactory()->get($name)->get();
|
||||
|
||||
if ($form_element = $this->createFormElement($schema)) {
|
||||
$parents = array('config_names', $name);
|
||||
$form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents);
|
||||
}
|
||||
}
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save translation'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
|
||||
// Set the configuration language back.
|
||||
$this->languageManager->setConfigOverrideLanguage($original_language);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_values = $form_state->getValue(array('translation', 'config_names'));
|
||||
|
||||
foreach ($this->mapper->getConfigNames() as $name) {
|
||||
$schema = $this->typedConfigManager->get($name);
|
||||
|
||||
// Set configuration values based on form submission and source values.
|
||||
$base_config = $this->configFactory()->getEditable($name);
|
||||
$config_translation = $this->languageManager->getLanguageConfigOverride($this->language->getId(), $name);
|
||||
|
||||
$element = $this->createFormElement($schema);
|
||||
$element->setConfig($base_config, $config_translation, $form_values[$name]);
|
||||
|
||||
// If no overrides, delete language specific configuration file.
|
||||
$saved_config = $config_translation->get();
|
||||
if (empty($saved_config)) {
|
||||
$config_translation->delete();
|
||||
}
|
||||
else {
|
||||
$config_translation->save();
|
||||
}
|
||||
}
|
||||
|
||||
$form_state->setRedirect(
|
||||
$this->mapper->getOverviewRoute(),
|
||||
$this->mapper->getOverviewRouteParameters()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a form element builder.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $schema
|
||||
* Schema definition of configuration.
|
||||
*
|
||||
* @return \Drupal\config_translation\FormElement\ElementInterface|null
|
||||
* The element builder object if possible.
|
||||
*/
|
||||
public static function createFormElement(TypedDataInterface $schema) {
|
||||
$definition = $schema->getDataDefinition();
|
||||
// Form element classes can be specified even for non-translatable elements
|
||||
// such as the ListElement form element which is used for Mapping and
|
||||
// Sequence schema elements.
|
||||
if (isset($definition['form_element_class'])) {
|
||||
if (!$definition->getLabel()) {
|
||||
$definition->setLabel(t('n/a'));
|
||||
}
|
||||
$class = $definition['form_element_class'];
|
||||
return $class::create($schema);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\DateFormat.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the date format element for the configuration translation interface.
|
||||
*/
|
||||
class DateFormat extends FormElementBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
|
||||
/** @var \Drupal\Core\Datetime\DateFormatter $date_formatter */
|
||||
$date_formatter = \Drupal::service('date.formatter');
|
||||
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php'));
|
||||
$format = $this->t('Displayed as %date_format', array('%date_format' => $date_formatter->format(REQUEST_TIME, 'custom', $translation_config)));
|
||||
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#description' => $description,
|
||||
'#field_suffix' => ' <small data-drupal-date-formatter="preview">' . $format . '</small>',
|
||||
'#attributes' => [
|
||||
'data-drupal-date-formatter' => 'source',
|
||||
],
|
||||
'#attached' => [
|
||||
'drupalSettings' => ['dateFormats' => $date_formatter->getSampleDateFormats($translation_language->getId())],
|
||||
'library' => ['system/drupal.system.date'],
|
||||
],
|
||||
] + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\ElementInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\language\Config\LanguageConfigOverride;
|
||||
|
||||
/**
|
||||
* Provides an interface for configuration translation form elements.
|
||||
*/
|
||||
interface ElementInterface {
|
||||
|
||||
/**
|
||||
* Creates a form element instance from a schema definition.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $schema
|
||||
* The configuration schema.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create(TypedDataInterface $schema);
|
||||
|
||||
/**
|
||||
* Builds a render array containg the source and translation form elements.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $source_language
|
||||
* The source language of the configuration object.
|
||||
* @param \Drupal\Core\Language\LanguageInterface $translation_language
|
||||
* The language to display the translation form for.
|
||||
* @param mixed $source_config
|
||||
* The configuration value of the element in the source language.
|
||||
* @param mixed $translation_config
|
||||
* The configuration value of the element in the language to translate to.
|
||||
* @param array $parents
|
||||
* Parents array for the element in the form.
|
||||
* @param string|null $base_key
|
||||
* (optional) Base key to be used for the elements in the form. NULL for
|
||||
* top-level form elements.
|
||||
*
|
||||
* @return array
|
||||
* A render array consisting of the source and translation elements for the
|
||||
* source value.
|
||||
*/
|
||||
public function getTranslationBuild(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, array $parents, $base_key = NULL);
|
||||
|
||||
/**
|
||||
* Sets configuration based on a nested form value array.
|
||||
*
|
||||
* If the configuration values are the same as the source configuration, the
|
||||
* override should be removed from the translation configuration.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Config $base_config
|
||||
* Base configuration values, in the source language.
|
||||
* @param \Drupal\language\Config\LanguageConfigOverride $config_translation
|
||||
* Translation configuration override data.
|
||||
* @param mixed $config_values
|
||||
* The configuration value of the element taken from the form values.
|
||||
* @param string|null $base_key
|
||||
* (optional) The base key that the schema and the configuration values
|
||||
* belong to. This should be NULL for the top-level configuration object and
|
||||
* be populated consecutively when recursing into the configuration
|
||||
* structure.
|
||||
*/
|
||||
public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL);
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\FormElementBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\language\Config\LanguageConfigOverride;
|
||||
|
||||
/**
|
||||
* Provides a common base class for form elements.
|
||||
*/
|
||||
abstract class FormElementBase implements ElementInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The schema element this form is for.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $element;
|
||||
|
||||
/**
|
||||
* The data definition of the element this form element is for.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Constructs a FormElementBase.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $element
|
||||
* The schema element this form element is for.
|
||||
*/
|
||||
public function __construct(TypedDataInterface $element) {
|
||||
$this->element = $element;
|
||||
$this->definition = $element->getDataDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(TypedDataInterface $schema) {
|
||||
return new static($schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationBuild(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, array $parents, $base_key = NULL) {
|
||||
$build['#theme'] = 'config_translation_manage_form_element';
|
||||
|
||||
// For accessibility we make source and translation appear next to each
|
||||
// other in the source for each element, which is why we utilize the
|
||||
// 'source' and 'translation' sub-keys for the form. The form values,
|
||||
// however, should mirror the configuration structure, so that we can
|
||||
// traverse the configuration schema and still access the right
|
||||
// configuration values in ConfigTranslationFormBase::setConfig().
|
||||
// Therefore we make the 'source' and 'translation' keys the top-level
|
||||
// keys in $form_state['values'].
|
||||
$build['source'] = $this->getSourceElement($source_language, $source_config);
|
||||
$build['translation'] = $this->getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
|
||||
$build['source']['#parents'] = array_merge(array('source'), $parents);
|
||||
$build['translation']['#parents'] = array_merge(array('translation'), $parents);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source element for a given configuration definition.
|
||||
*
|
||||
* This can be either a render array that actually outputs the source values
|
||||
* directly or a read-only form element with the source values depending on
|
||||
* what is considered to provide a more intuitive user interface for the
|
||||
* translator.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $source_language
|
||||
* Thee source language of the configuration object.
|
||||
* @param mixed $source_config
|
||||
* The configuration value of the element in the source language.
|
||||
*
|
||||
* @return array
|
||||
* A render array for the source value.
|
||||
*/
|
||||
protected function getSourceElement(LanguageInterface $source_language, $source_config) {
|
||||
// @todo Should support singular+plurals https://www.drupal.org/node/2454829
|
||||
if ($source_config) {
|
||||
$value = '<span lang="' . $source_language->getId() . '">' . nl2br($source_config) . '</span>';
|
||||
}
|
||||
else {
|
||||
$value = $this->t('(Empty)');
|
||||
}
|
||||
|
||||
return array(
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('!label <span class="visually-hidden">(!source_language)</span>', array(
|
||||
'!label' => $this->t($this->definition->getLabel()),
|
||||
'!source_language' => $source_language->getName(),
|
||||
)),
|
||||
'#markup' => $value,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation form element for a given configuration definition.
|
||||
*
|
||||
* For complex data structures (such as mappings) that are translatable
|
||||
* wholesale but contain non-translatable properties, the form element is
|
||||
* responsible for checking access to the source value of those properties. In
|
||||
* case of formatted text, for example, access to the source text format must
|
||||
* be checked. If the translator does not have access to the text format, the
|
||||
* textarea must be disabled and the translator may not be able to translate
|
||||
* this particular configuration element. If the translator does have access
|
||||
* to the text format, the element must be locked down to that particular text
|
||||
* format; in other words, the format may not be changed by the translator
|
||||
* (because the text format property is not itself translatable).
|
||||
*
|
||||
* In addition, the form element is responsible for checking whether the
|
||||
* value of such non-translatable properties in the translated configuration
|
||||
* is equal to the corresponding source values. If not, that means that the
|
||||
* source value has changed after the translation was added. In this case -
|
||||
* again - the translation of this element must be disabled if the translator
|
||||
* does not have access to the source value of the non-translatable property.
|
||||
* For example, if a formatted text element, whose source format was plain
|
||||
* text when it was first translated, gets changed to the Full HTML format,
|
||||
* simply changing the format of the translation would lead to an XSS
|
||||
* vulnerability as the translated text, that was intended to be escaped,
|
||||
* would now be displayed unescaped. Thus, if the translator does not have
|
||||
* access to the Full HTML format, the translation for this particular element
|
||||
* may not be updated at all (the textarea must be disabled). Only if access
|
||||
* to the Full HTML format is granted, an explicit translation taking into
|
||||
* account the updated source value(s) may be submitted.
|
||||
*
|
||||
* In the specific case of formatted text this logic is implemented by
|
||||
* utilizing a form element of type 'text_format' and its #format and
|
||||
* #allowed_formats properties. The access logic explained above is then
|
||||
* handled by the 'text_format' element itself, specifically by
|
||||
* filter_process_format(). In case such a rich element is not available for
|
||||
* translation of complex data, similar access logic must be implemented
|
||||
* manually.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageInterface $translation_language
|
||||
* The language to display the translation form for.
|
||||
* @param mixed $source_config
|
||||
* The configuration value of the element in the source language.
|
||||
* @param mixed $translation_config
|
||||
* The configuration value of the element in the language to translate to.
|
||||
*
|
||||
* @return array
|
||||
* Form API array to represent the form element.
|
||||
*
|
||||
* @see \Drupal\config_translation\FormElement\TextFormat
|
||||
* @see filter_process_format()
|
||||
*/
|
||||
protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
|
||||
// Add basic properties that apply to all form elements.
|
||||
// @todo Should support singular+plurals https://www.drupal.org/node/2454829
|
||||
return array(
|
||||
'#title' => $this->t('!label <span class="visually-hidden">(!source_language)</span>', array(
|
||||
'!label' => $this->t($this->definition['label']),
|
||||
'!source_language' => $translation_language->getName(),
|
||||
)),
|
||||
'#default_value' => $translation_config,
|
||||
'#attributes' => array('lang' => $translation_language->getId()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL) {
|
||||
// Save the configuration values, if they are different from the source
|
||||
// values in the base configuration. Otherwise remove the override.
|
||||
if ($base_config->get($base_key) !== $config_values) {
|
||||
$config_translation->set($base_key, $config_values);
|
||||
}
|
||||
else {
|
||||
$config_translation->clear($base_key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
136
core/modules/config_translation/src/FormElement/ListElement.php
Normal file
136
core/modules/config_translation/src/FormElement/ListElement.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\ListElement.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\config_translation\Form\ConfigTranslationFormBase;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\TraversableTypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\language\Config\LanguageConfigOverride;
|
||||
|
||||
/**
|
||||
* Defines the list element for the configuration translation interface.
|
||||
*/
|
||||
class ListElement implements ElementInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The schema element this form is for.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TraversableTypedDataInterface
|
||||
*/
|
||||
protected $element;
|
||||
|
||||
/**
|
||||
* Constructs a ListElement.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TraversableTypedDataInterface $element
|
||||
* The schema element this form element is for.
|
||||
*/
|
||||
public function __construct(TraversableTypedDataInterface $element) {
|
||||
$this->element = $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(TypedDataInterface $schema) {
|
||||
return new static($schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationBuild(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, array $parents, $base_key = NULL) {
|
||||
$build = array();
|
||||
foreach ($this->element as $key => $element) {
|
||||
$sub_build = array();
|
||||
$element_key = isset($base_key) ? "$base_key.$key" : $key;
|
||||
$definition = $element->getDataDefinition();
|
||||
|
||||
if ($form_element = ConfigTranslationFormBase::createFormElement($element)) {
|
||||
$element_parents = array_merge($parents, array($key));
|
||||
$sub_build += $form_element->getTranslationBuild($source_language, $translation_language, $source_config[$key], $translation_config[$key], $element_parents, $element_key);
|
||||
|
||||
if (empty($sub_build)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build the sub-structure and include it with a wrapper in the form if
|
||||
// there are any translatable elements there.
|
||||
$build[$key] = array();
|
||||
if ($element instanceof TraversableTypedDataInterface) {
|
||||
$build[$key] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->getGroupTitle($definition, $sub_build),
|
||||
'#open' => empty($base_key),
|
||||
);
|
||||
}
|
||||
$build[$key] += $sub_build;
|
||||
}
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL) {
|
||||
foreach ($this->element as $key => $element) {
|
||||
$element_key = isset($base_key) ? "$base_key.$key" : $key;
|
||||
if ($form_element = ConfigTranslationFormBase::createFormElement($element)) {
|
||||
// Traverse into the next level of the configuration.
|
||||
$value = isset($config_values[$key]) ? $config_values[$key] : NULL;
|
||||
$form_element->setConfig($base_config, $config_translation, $value, $element_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for the 'details' element of a group of schema elements.
|
||||
*
|
||||
* For some configuration elements the same element structure can be repeated
|
||||
* multiple times (for example views displays, filters, etc.). Thus, we try to
|
||||
* find a more usable title for the details summary. First check if there is
|
||||
* an element which is called title or label and use its value. Then check if
|
||||
* there is an element which contains these words and use those. Fall back
|
||||
* to the generic definition label if no such element is found.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* The definition of the schema element.
|
||||
* @param array $group_build
|
||||
* The renderable array for the group of schema elements.
|
||||
*
|
||||
* @return string
|
||||
* The title for the group of schema elements.
|
||||
*/
|
||||
protected function getGroupTitle(DataDefinitionInterface $definition, array $group_build) {
|
||||
$title = '';
|
||||
if (isset($group_build['title']['source'])) {
|
||||
$title = $group_build['title']['source']['#markup'];
|
||||
}
|
||||
elseif (isset($group_build['label']['source'])) {
|
||||
$title = $group_build['label']['source']['#markup'];
|
||||
}
|
||||
else {
|
||||
foreach (array_keys($group_build) as $title_key) {
|
||||
if (isset($group_build[$title_key]['source']) && (strpos($title_key, 'title') !== FALSE || strpos($title_key, 'label') !== FALSE)) {
|
||||
$title = $group_build[$title_key]['source']['#markup'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (!empty($title) ? (strip_tags($title) . ' ') : '') . $this->t($definition['label']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\TextFormat.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the text_format element for the configuration translation interface.
|
||||
*/
|
||||
class TextFormat extends FormElementBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceElement(LanguageInterface $source_language, $source_config) {
|
||||
// Instead of the formatted output show a disabled textarea. This allows for
|
||||
// easier side-by-side comparison, especially with formats with text
|
||||
// editors.
|
||||
return $this->getTranslationElement($source_language, $source_config, $source_config) + array(
|
||||
'#value' => $source_config['value'],
|
||||
'#disabled' => TRUE,
|
||||
'#allow_focus' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
|
||||
return array(
|
||||
'#type' => 'text_format',
|
||||
// Override the #default_value property from the parent class.
|
||||
'#default_value' => $translation_config['value'],
|
||||
'#format' => $translation_config['format'],
|
||||
// @see \Drupal\config_translation\Element\FormElementBase::getTranslationElement()
|
||||
'#allowed_formats' => array($source_config['format']),
|
||||
) + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
}
|
32
core/modules/config_translation/src/FormElement/Textarea.php
Normal file
32
core/modules/config_translation/src/FormElement/Textarea.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\Textarea.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the textarea element for the configuration translation interface.
|
||||
*/
|
||||
class Textarea extends FormElementBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
|
||||
// Estimate a comfortable size of the input textarea.
|
||||
$rows_words = ceil(str_word_count($translation_config) / 5);
|
||||
$rows_newlines = substr_count($translation_config, "\n" ) + 1;
|
||||
$rows = max($rows_words, $rows_newlines);
|
||||
|
||||
return array(
|
||||
'#type' => 'textarea',
|
||||
'#rows' => $rows,
|
||||
) + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\FormElement\Textfield.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\FormElement;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Defines the textfield element for the configuration translation interface.
|
||||
*/
|
||||
class Textfield extends FormElementBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
|
||||
return array(
|
||||
'#type' => 'textfield',
|
||||
) + parent::getTranslationElement($translation_language, $source_config, $translation_config);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Plugin\Derivative\ConfigTranslationContextualLinks.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic contextual links for configuration translation.
|
||||
*/
|
||||
class ConfigTranslationContextualLinks extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigTranslationContextualLinks.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
*/
|
||||
public function __construct(ConfigMapperManagerInterface $mapper_manager) {
|
||||
$this->mapperManager = $mapper_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.config_translation.mapper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
// Create contextual links for all mappers.
|
||||
$mappers = $this->mapperManager->getMappers();
|
||||
foreach ($mappers as $plugin_id => $mapper) {
|
||||
// @todo Contextual groups do not map to entity types in a predictable
|
||||
// way. See https://www.drupal.org/node/2134841 to make them
|
||||
// predictable.
|
||||
$group_name = $mapper->getContextualLinkGroup();
|
||||
if (empty($group_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$route_name = $mapper->getOverviewRouteName();
|
||||
$this->derivatives[$route_name] = $base_plugin_definition;
|
||||
$this->derivatives[$route_name]['config_translation_plugin_id'] = $plugin_id;
|
||||
$this->derivatives[$route_name]['class'] = '\Drupal\config_translation\Plugin\Menu\ContextualLink\ConfigTranslationContextualLink';
|
||||
$this->derivatives[$route_name]['route_name'] = $route_name;
|
||||
$this->derivatives[$route_name]['group'] = $group_name;
|
||||
}
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Plugin\Derivative\ConfigTranslationLocalTasks.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Plugin\Derivative;
|
||||
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides dynamic local tasks for config translation.
|
||||
*/
|
||||
class ConfigTranslationLocalTasks extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* The base plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePluginId;
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigTranslationLocalTasks.
|
||||
*
|
||||
* @param string $base_plugin_id
|
||||
* The base plugin ID.
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
*/
|
||||
public function __construct($base_plugin_id, ConfigMapperManagerInterface $mapper_manager) {
|
||||
$this->basePluginId = $base_plugin_id;
|
||||
$this->mapperManager = $mapper_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$base_plugin_id,
|
||||
$container->get('plugin.manager.config_translation.mapper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$mappers = $this->mapperManager->getMappers();
|
||||
foreach ($mappers as $plugin_id => $mapper) {
|
||||
/** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
|
||||
$route_name = $mapper->getOverviewRouteName();
|
||||
$base_route = $mapper->getBaseRouteName();
|
||||
if (!empty($base_route)) {
|
||||
$this->derivatives[$route_name] = $base_plugin_definition;
|
||||
$this->derivatives[$route_name]['config_translation_plugin_id'] = $plugin_id;
|
||||
$this->derivatives[$route_name]['class'] = '\Drupal\config_translation\Plugin\Menu\LocalTask\ConfigTranslationLocalTask';
|
||||
$this->derivatives[$route_name]['route_name'] = $route_name;
|
||||
$this->derivatives[$route_name]['base_route'] = $base_route;
|
||||
}
|
||||
}
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Plugin\Menu\ContextualLink\ConfigTranslationContextualLink.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Plugin\Menu\ContextualLink;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Menu\ContextualLinkDefault;
|
||||
|
||||
/**
|
||||
* Defines a contextual link plugin with a dynamic title.
|
||||
*/
|
||||
class ConfigTranslationContextualLink extends ContextualLinkDefault {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
$options = array();
|
||||
if (!empty($this->pluginDefinition['title_context'])) {
|
||||
$options['context'] = $this->pluginDefinition['title_context'];
|
||||
}
|
||||
|
||||
// Take custom 'config_translation_plugin_id' plugin definition key to
|
||||
// retrieve title. We need to retrieve a runtime title (as opposed to
|
||||
// storing the title on the plugin definition for the link) because
|
||||
// it contains translated parts that we need in the runtime language.
|
||||
$type_name = Unicode::strtolower($this->mapperManager()->createInstance($this->pluginDefinition['config_translation_plugin_id'])->getTypeLabel());
|
||||
return $this->t($this->pluginDefinition['title'], array('@type_name' => $type_name), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapper manager.
|
||||
*
|
||||
* @return \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
* The mapper manager.
|
||||
*/
|
||||
protected function mapperManager() {
|
||||
if (!$this->mapperManager) {
|
||||
$this->mapperManager = \Drupal::service('plugin.manager.config_translation.mapper');
|
||||
}
|
||||
return $this->mapperManager;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Plugin\Menu\LocalTask\ConfigTranslationLocalTask.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Plugin\Menu\LocalTask;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Menu\LocalTaskDefault;
|
||||
|
||||
/**
|
||||
* Defines a local task plugin with a dynamic title.
|
||||
*/
|
||||
class ConfigTranslationLocalTask extends LocalTaskDefault {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
$options = array();
|
||||
if (!empty($this->pluginDefinition['title_context'])) {
|
||||
$options['context'] = $this->pluginDefinition['title_context'];
|
||||
}
|
||||
|
||||
// Take custom 'config_translation_plugin_id' plugin definition key to
|
||||
// retrieve title. We need to retrieve a runtime title (as opposed to
|
||||
// storing the title on the plugin definition for the link) because
|
||||
// it contains translated parts that we need in the runtime language.
|
||||
$type_name = Unicode::strtolower($this->mapperManager()->createInstance($this->pluginDefinition['config_translation_plugin_id'])->getTypeLabel());
|
||||
return $this->t($this->pluginDefinition['title'], array('@type_name' => $type_name), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapper manager.
|
||||
*
|
||||
* @return \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
* The mapper manager.
|
||||
*/
|
||||
protected function mapperManager() {
|
||||
if (!$this->mapperManager) {
|
||||
$this->mapperManager = \Drupal::service('plugin.manager.config_translation.mapper');
|
||||
}
|
||||
return $this->mapperManager;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Routing\RouteSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Routing;
|
||||
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\config_translation\ConfigMapperManagerInterface;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Listens to the dynamic route events.
|
||||
*/
|
||||
class RouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* The mapper plugin discovery service.
|
||||
*
|
||||
* @var \Drupal\config_translation\ConfigMapperManagerInterface
|
||||
*/
|
||||
protected $mapperManager;
|
||||
|
||||
/**
|
||||
* Constructs a new RouteSubscriber.
|
||||
*
|
||||
* @param \Drupal\config_translation\ConfigMapperManagerInterface $mapper_manager
|
||||
* The mapper plugin discovery service.
|
||||
*/
|
||||
public function __construct(ConfigMapperManagerInterface $mapper_manager) {
|
||||
$this->mapperManager = $mapper_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection) {
|
||||
$mappers = $this->mapperManager->getMappers($collection);
|
||||
|
||||
foreach ($mappers as $mapper) {
|
||||
$collection->add($mapper->getOverviewRouteName(), $mapper->getOverviewRoute());
|
||||
$collection->add($mapper->getAddRouteName(), $mapper->getAddRoute());
|
||||
$collection->add($mapper->getEditRouteName(), $mapper->getEditRoute());
|
||||
$collection->add($mapper->getDeleteRouteName(), $mapper->getDeleteRoute());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Come after field_ui.
|
||||
$events[RoutingEvents::ALTER] = array('onAlterRoutes', -110);
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationFormTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests for altering configuration translation forms.
|
||||
*
|
||||
* @group config_translation
|
||||
*/
|
||||
class ConfigTranslationFormTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_translation', 'config_translation_test', 'editor');
|
||||
|
||||
/**
|
||||
* The plugin ID of the mapper to test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pluginId;
|
||||
|
||||
/**
|
||||
* The language code of the language to use for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $langcode;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$definitions = \Drupal::service('plugin.manager.config_translation.mapper')->getDefinitions();
|
||||
$this->pluginId = key($definitions);
|
||||
|
||||
$this->langcode = 'xx';
|
||||
ConfigurableLanguage::create(array('id' => $this->langcode, 'label' => 'XX'))->save();
|
||||
|
||||
\Drupal::state()->set('config_translation_test_alter_form_alter', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests altering of the configuration translation forms.
|
||||
*/
|
||||
public function testConfigTranslationFormAlter() {
|
||||
$form_builder = \Drupal::formBuilder();
|
||||
$add_form = $form_builder->getForm('Drupal\config_translation\Form\ConfigTranslationAddForm', \Drupal::request(), $this->pluginId, $this->langcode);
|
||||
$edit_form = $form_builder->getForm('Drupal\config_translation\Form\ConfigTranslationEditForm', \Drupal::request(), $this->pluginId, $this->langcode);
|
||||
|
||||
// Test that hook_form_BASE_FORM_ID_alter() was called for the base form ID
|
||||
// 'config_translation_form'.
|
||||
$this->assertTrue($add_form['#base_altered']);
|
||||
$this->assertTrue($edit_form['#base_altered']);
|
||||
|
||||
// Test that hook_form_FORM_ID_alter() was called for the form IDs
|
||||
// 'config_translation_add_form' and 'config_translation_edit_form'.
|
||||
$this->assertTrue($add_form['#altered']);
|
||||
$this->assertTrue($edit_form['#altered']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationListUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Visit all lists.
|
||||
*
|
||||
* @group config_translation
|
||||
* @see \Drupal\config_translation\Tests\ConfigTranslationViewListUiTest
|
||||
*/
|
||||
class ConfigTranslationListUiTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'block',
|
||||
'config_translation',
|
||||
'contact',
|
||||
'block_content',
|
||||
'field',
|
||||
'field_ui',
|
||||
'menu_ui',
|
||||
'node',
|
||||
'shortcut',
|
||||
'taxonomy',
|
||||
'image',
|
||||
'responsive_image',
|
||||
'toolbar',
|
||||
);
|
||||
|
||||
/**
|
||||
* Admin user with all needed permissions.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$permissions = array(
|
||||
'access site-wide contact form',
|
||||
'administer blocks',
|
||||
'administer contact forms',
|
||||
'administer content types',
|
||||
'administer block_content fields',
|
||||
'administer filters',
|
||||
'administer menu',
|
||||
'administer node fields',
|
||||
'administer permissions',
|
||||
'administer shortcuts',
|
||||
'administer site configuration',
|
||||
'administer taxonomy',
|
||||
'administer account settings',
|
||||
'administer languages',
|
||||
'administer image styles',
|
||||
'administer responsive images',
|
||||
'translate configuration',
|
||||
);
|
||||
|
||||
// Create and log in user.
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Enable import of translations. By default this is disabled for automated
|
||||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the block listing for the translate operation.
|
||||
*
|
||||
* There are no blocks placed in the testing profile. Add one, then check
|
||||
* for Translate operation.
|
||||
*/
|
||||
protected function doBlockListTest() {
|
||||
// Add a test block, any block will do.
|
||||
// Set the machine name so the translate link can be built later.
|
||||
$id = Unicode::strtolower($this->randomMachineName(16));
|
||||
$this->drupalPlaceBlock('system_powered_by_block', array('id' => $id));
|
||||
|
||||
// Get the Block listing.
|
||||
$this->drupalGet('admin/structure/block');
|
||||
|
||||
$translate_link = 'admin/structure/block/manage/' . $id . '/translate';
|
||||
// Test if the link to translate the block is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the menu listing for the translate operation.
|
||||
*/
|
||||
protected function doMenuListTest() {
|
||||
// Create a test menu to decouple looking for translate operations link so
|
||||
// this does not test more than necessary.
|
||||
$this->drupalGet('admin/structure/menu/add');
|
||||
// Lowercase the machine name.
|
||||
$menu_name = Unicode::strtolower($this->randomMachineName(16));
|
||||
$label = $this->randomMachineName(16);
|
||||
$edit = array(
|
||||
'id' => $menu_name,
|
||||
'description' => '',
|
||||
'label' => $label,
|
||||
);
|
||||
// Create the menu by posting the form.
|
||||
$this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
|
||||
|
||||
// Get the Menu listing.
|
||||
$this->drupalGet('admin/structure/menu');
|
||||
|
||||
$translate_link = 'admin/structure/menu/manage/' . $menu_name . '/translate';
|
||||
// Test if the link to translate the menu is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Check if the Link is not added if you are missing 'translate
|
||||
// configuration' permission.
|
||||
$permissions = array(
|
||||
'administer menu',
|
||||
);
|
||||
$this->drupalLogin($this->drupalCreateUser($permissions));
|
||||
|
||||
// Get the Menu listing.
|
||||
$this->drupalGet('admin/structure/menu');
|
||||
|
||||
$translate_link = 'admin/structure/menu/manage/' . $menu_name . '/translate';
|
||||
// Test if the link to translate the menu is NOT on the page.
|
||||
$this->assertNoLinkByHref($translate_link);
|
||||
|
||||
// Login as Admin again otherwise the rest will fail.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the vocabulary listing for the translate operation.
|
||||
*/
|
||||
protected function doVocabularyListTest() {
|
||||
// Create a test vocabulary to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
));
|
||||
$vocabulary->save();
|
||||
|
||||
// Get the Taxonomy listing.
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
|
||||
$translate_link = 'admin/structure/taxonomy/manage/' . $vocabulary->id() . '/translate';
|
||||
// Test if the link to translate the vocabulary is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the custom block listing for the translate operation.
|
||||
*/
|
||||
public function doCustomContentTypeListTest() {
|
||||
// Create a test custom block type to decouple looking for translate
|
||||
// operations link so this does not test more than necessary.
|
||||
$block_content_type = entity_create('block_content_type', array(
|
||||
'id' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'label' => $this->randomMachineName(),
|
||||
'revision' => FALSE
|
||||
));
|
||||
$block_content_type->save();
|
||||
|
||||
// Get the custom block type listing.
|
||||
$this->drupalGet('admin/structure/block/block-content/types');
|
||||
|
||||
$translate_link = 'admin/structure/block/block-content/manage/' . $block_content_type->id() . '/translate';
|
||||
// Test if the link to translate the custom block type is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the contact forms listing for the translate operation.
|
||||
*/
|
||||
public function doContactFormsListTest() {
|
||||
// Create a test contact form to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$contact_form = entity_create('contact_form', array(
|
||||
'id' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$contact_form->save();
|
||||
|
||||
// Get the contact form listing.
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
|
||||
$translate_link = 'admin/structure/contact/manage/' . $contact_form->id() . '/translate';
|
||||
// Test if the link to translate the contact form is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the content type listing for the translate operation.
|
||||
*/
|
||||
public function doContentTypeListTest() {
|
||||
// Create a test content type to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$content_type = $this->drupalCreateContentType(array(
|
||||
'type' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'name' => $this->randomMachineName(),
|
||||
));
|
||||
|
||||
// Get the content type listing.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
|
||||
$translate_link = 'admin/structure/types/manage/' . $content_type->id() . '/translate';
|
||||
// Test if the link to translate the content type is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formats listing for the translate operation.
|
||||
*/
|
||||
public function doFormatsListTest() {
|
||||
// Create a test format to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$filter_format = entity_create('filter_format', array(
|
||||
'format' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'name' => $this->randomMachineName(),
|
||||
));
|
||||
$filter_format->save();
|
||||
|
||||
// Get the format listing.
|
||||
$this->drupalGet('admin/config/content/formats');
|
||||
|
||||
$translate_link = 'admin/config/content/formats/manage/' . $filter_format->id() . '/translate';
|
||||
// Test if the link to translate the format is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the shortcut listing for the translate operation.
|
||||
*/
|
||||
public function doShortcutListTest() {
|
||||
// Create a test shortcut to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$shortcut = entity_create('shortcut_set', array(
|
||||
'id' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'label' => $this->randomString(),
|
||||
));
|
||||
$shortcut->save();
|
||||
|
||||
// Get the shortcut listing.
|
||||
$this->drupalGet('admin/config/user-interface/shortcut');
|
||||
|
||||
$translate_link = 'admin/config/user-interface/shortcut/manage/' . $shortcut->id() . '/translate';
|
||||
// Test if the link to translate the shortcut is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the role listing for the translate operation.
|
||||
*/
|
||||
public function doUserRoleListTest() {
|
||||
// Create a test role to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
$role_id = Unicode::strtolower($this->randomMachineName(16));
|
||||
$this->drupalCreateRole(array(), $role_id);
|
||||
|
||||
// Get the role listing.
|
||||
$this->drupalGet('admin/people/roles');
|
||||
|
||||
$translate_link = 'admin/people/roles/manage/' . $role_id . '/translate';
|
||||
// Test if the link to translate the role is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the language listing for the translate operation.
|
||||
*/
|
||||
public function doLanguageListTest() {
|
||||
// Create a test language to decouple looking for translate operations
|
||||
// link so this does not test more than necessary.
|
||||
ConfigurableLanguage::createFromLangcode('ga')->save();
|
||||
|
||||
// Get the language listing.
|
||||
$this->drupalGet('admin/config/regional/language');
|
||||
|
||||
$translate_link = 'admin/config/regional/language/edit/ga/translate';
|
||||
// Test if the link to translate the language is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the image style listing for the translate operation.
|
||||
*/
|
||||
public function doImageStyleListTest() {
|
||||
// Get the image style listing.
|
||||
$this->drupalGet('admin/config/media/image-styles');
|
||||
|
||||
$translate_link = 'admin/config/media/image-styles/manage/medium/translate';
|
||||
// Test if the link to translate the style is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the responsive image mapping listing for the translate operation.
|
||||
*/
|
||||
public function doResponsiveImageListTest() {
|
||||
$edit = array();
|
||||
$edit['label'] = $this->randomMachineName();
|
||||
$edit['id'] = strtolower($edit['label']);
|
||||
$edit['fallback_image_style'] = 'thumbnail';
|
||||
|
||||
$this->drupalPostForm('admin/config/media/responsive-image-style/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('Responsive image style %label saved.', array('%label' => $edit['label'])));
|
||||
|
||||
// Get the responsive image style listing.
|
||||
$this->drupalGet('admin/config/media/responsive-image-style');
|
||||
|
||||
$translate_link = 'admin/config/media/responsive-image-style/' . $edit['id'] . '/translate';
|
||||
// Test if the link to translate the style is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the field listing for the translate operation.
|
||||
*/
|
||||
public function doFieldListTest() {
|
||||
// Create a base content type.
|
||||
$content_type = $this->drupalCreateContentType(array(
|
||||
'type' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'name' => $this->randomMachineName(),
|
||||
));
|
||||
|
||||
// Create a block content type.
|
||||
$block_content_type = entity_create('block_content_type', array(
|
||||
'id' => 'basic',
|
||||
'label' => 'Basic',
|
||||
'revision' => FALSE
|
||||
));
|
||||
$block_content_type->save();
|
||||
$field = entity_create('field_config', array(
|
||||
// The field storage is guaranteed to exist because it is supplied by the
|
||||
// block_content module.
|
||||
'field_storage' => FieldStorageConfig::loadByName('block_content', 'body'),
|
||||
'bundle' => $block_content_type->id(),
|
||||
'label' => 'Body',
|
||||
'settings' => array('display_summary' => FALSE),
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Look at a few fields on a few entity types.
|
||||
$pages = array(
|
||||
array(
|
||||
'list' => 'admin/structure/types/manage/' . $content_type->id() . '/fields',
|
||||
'field' => 'node.' . $content_type->id() . '.body',
|
||||
),
|
||||
array(
|
||||
'list' => 'admin/structure/block/block-content/manage/basic/fields',
|
||||
'field' => 'block_content.basic.body',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($pages as $values) {
|
||||
// Get fields listing.
|
||||
$this->drupalGet($values['list']);
|
||||
|
||||
$translate_link = $values['list'] . '/' . $values['field'] . '/translate';
|
||||
// Test if the link to translate the field is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the date format listing for the translate operation.
|
||||
*/
|
||||
public function doDateFormatListTest() {
|
||||
// Get the date format listing.
|
||||
$this->drupalGet('admin/config/regional/date-time');
|
||||
|
||||
$translate_link = 'admin/config/regional/date-time/formats/manage/long/translate';
|
||||
// Test if the link to translate the format is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a given settings page for the translate operation.
|
||||
*
|
||||
* @param string $link
|
||||
* URL of the settings page to test.
|
||||
*/
|
||||
public function doSettingsPageTest($link) {
|
||||
// Get the settings page.
|
||||
$this->drupalGet($link);
|
||||
|
||||
$translate_link = $link . '/translate';
|
||||
// Test if the link to translate the settings page is present.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if translate link is added to operations in all configuration lists.
|
||||
*/
|
||||
public function testTranslateOperationInListUi() {
|
||||
// All lists based on paths provided by the module.
|
||||
$this->doBlockListTest();
|
||||
$this->doMenuListTest();
|
||||
$this->doVocabularyListTest();
|
||||
$this->doCustomContentTypeListTest();
|
||||
$this->doContactFormsListTest();
|
||||
$this->doContentTypeListTest();
|
||||
$this->doFormatsListTest();
|
||||
$this->doShortcutListTest();
|
||||
$this->doUserRoleListTest();
|
||||
$this->doLanguageListTest();
|
||||
$this->doImageStyleListTest();
|
||||
$this->doResponsiveImageListTest();
|
||||
$this->doDateFormatListTest();
|
||||
$this->doFieldListTest();
|
||||
|
||||
// Views is tested in Drupal\config_translation\Tests\ConfigTranslationViewListUiTest
|
||||
|
||||
// Test the maintenance settings page.
|
||||
$this->doSettingsPageTest('admin/config/development/maintenance');
|
||||
// Test the site information settings page.
|
||||
$this->doSettingsPageTest('admin/config/system/site-information');
|
||||
// Test the account settings page.
|
||||
$this->doSettingsPageTest('admin/config/people/accounts');
|
||||
// Test the RSS settings page.
|
||||
$this->doSettingsPageTest('admin/config/services/rss-publishing');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationOverviewTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Translate settings and entities to various languages.
|
||||
*
|
||||
* @group config_translation
|
||||
*/
|
||||
class ConfigTranslationOverviewTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('contact', 'config_translation', 'views', 'views_ui', 'contextual', 'config_test', 'config_translation_test');
|
||||
|
||||
/**
|
||||
* Languages to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $langcodes = array('fr', 'ta');
|
||||
|
||||
/**
|
||||
* String translation storage object.
|
||||
*
|
||||
* @var \Drupal\locale\StringStorageInterface
|
||||
*/
|
||||
protected $localeStorage;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$permissions = array(
|
||||
'translate configuration',
|
||||
'administer languages',
|
||||
'administer site configuration',
|
||||
'administer contact forms',
|
||||
'access site-wide contact form',
|
||||
'access contextual links',
|
||||
'administer views',
|
||||
);
|
||||
// Create and login user.
|
||||
$this->drupalLogin($this->drupalCreateUser($permissions));
|
||||
|
||||
// Add languages.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
}
|
||||
$this->localeStorage = $this->container->get('locale.storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the config translation mapper page.
|
||||
*/
|
||||
public function testMapperListPage() {
|
||||
$this->drupalGet('admin/config/regional/config-translation');
|
||||
$this->assertLinkByHref('admin/config/regional/config-translation/config_test');
|
||||
$this->assertLinkByHref('admin/config/people/accounts/translate');
|
||||
|
||||
$labels = array(
|
||||
'&$nxd~i0',
|
||||
'some "label" with quotes',
|
||||
$this->randomString(),
|
||||
);
|
||||
|
||||
foreach ($labels as $label) {
|
||||
$test_entity = entity_create('config_test', array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $label,
|
||||
));
|
||||
$test_entity->save();
|
||||
|
||||
$base_url = 'admin/structure/config_test/manage/' . $test_entity->id();
|
||||
$this->drupalGet('admin/config/regional/config-translation/config_test');
|
||||
$this->assertLinkByHref($base_url . '/translate');
|
||||
$this->assertText(SafeMarkup::checkPlain($test_entity->label()));
|
||||
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($test_entity->getEntityTypeId());
|
||||
$this->drupalGet($base_url . '/translate');
|
||||
|
||||
$title = t('!label !entity_type', array('!label' => $test_entity->label(), '!entity_type' => $entity_type->getLowercaseLabel()));
|
||||
$title = t('Translations for %label', array('%label' => $title));
|
||||
$this->assertRaw($title);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
|
||||
$this->drupalGet($base_url);
|
||||
$this->assertLink(t('Translate @title', array('@title' => $entity_type->getLowercaseLabel())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests availability of hidden entities in the translation overview.
|
||||
*/
|
||||
public function testHiddenEntities() {
|
||||
// Hidden languages are only available to translate through the
|
||||
// configuration translation listings.
|
||||
$this->drupalGet('admin/config/regional/config-translation/configurable_language');
|
||||
$this->assertText('Not applicable');
|
||||
$this->assertLinkByHref('admin/config/regional/language/edit/zxx/translate');
|
||||
$this->assertText('Not specified');
|
||||
$this->assertLinkByHref('admin/config/regional/language/edit/und/translate');
|
||||
|
||||
// Hidden date formats are only available to translate through the
|
||||
// configuration translation listings. Test a couple of them.
|
||||
$this->drupalGet('admin/config/regional/config-translation/date_format');
|
||||
$this->assertText('HTML Date');
|
||||
$this->assertLinkByHref('admin/config/regional/date-time/formats/manage/html_date/translate');
|
||||
$this->assertText('HTML Year');
|
||||
$this->assertLinkByHref('admin/config/regional/date-time/formats/manage/html_year/translate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect listing screens.
|
||||
*/
|
||||
public function testListingPageWithOverrides() {
|
||||
$original_label = 'Default';
|
||||
$overridden_label = 'Overridden label';
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['config_test.dynamic.dotted.default']['label'] = (object) array(
|
||||
'value' => $overridden_label,
|
||||
'required' => TRUE,
|
||||
);
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Test that the overridden label is loaded with the entity.
|
||||
$this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
|
||||
|
||||
// Test that the original label on the listing page is intact.
|
||||
$this->drupalGet('admin/config/regional/config-translation/config_test');
|
||||
$this->assertText($original_label);
|
||||
$this->assertNoText($overridden_label);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,939 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Translate settings and entities to various languages.
|
||||
*
|
||||
* @group config_translation
|
||||
*/
|
||||
class ConfigTranslationUiTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'contact', 'contact_test', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter', 'filter_test');
|
||||
|
||||
/**
|
||||
* Languages to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $langcodes = array('fr', 'ta');
|
||||
|
||||
/**
|
||||
* Administrator user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Translator user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $translatorUser;
|
||||
|
||||
/**
|
||||
* String translation storage object.
|
||||
*
|
||||
* @var \Drupal\locale\StringStorageInterface
|
||||
*/
|
||||
protected $localeStorage;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$translator_permissions = [
|
||||
'translate configuration',
|
||||
];
|
||||
|
||||
/** @var \Drupal\filter\FilterFormatInterface $filter_test_format */
|
||||
$filter_test_format = entity_load('filter_format', 'filter_test');
|
||||
/** @var \Drupal\filter\FilterFormatInterface $filtered_html_format */
|
||||
$filtered_html_format = entity_load('filter_format', 'filtered_html');
|
||||
/** @var \Drupal\filter\FilterFormatInterface $full_html_format */
|
||||
$full_html_format = entity_load('filter_format', 'full_html');
|
||||
|
||||
$admin_permissions = array_merge(
|
||||
$translator_permissions,
|
||||
[
|
||||
'administer languages',
|
||||
'administer site configuration',
|
||||
'link to any page',
|
||||
'administer contact forms',
|
||||
'administer filters',
|
||||
$filtered_html_format->getPermissionName(),
|
||||
$full_html_format->getPermissionName(),
|
||||
$filter_test_format->getPermissionName(),
|
||||
'access site-wide contact form',
|
||||
'access contextual links',
|
||||
'administer views',
|
||||
'administer account settings',
|
||||
'administer themes',
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
]
|
||||
);
|
||||
// Create and login user.
|
||||
$this->translatorUser = $this->drupalCreateUser($translator_permissions);
|
||||
$this->adminUser = $this->drupalCreateUser($admin_permissions);
|
||||
|
||||
// Add languages.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
}
|
||||
$this->localeStorage = $this->container->get('locale.storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the site information translation interface.
|
||||
*/
|
||||
public function testSiteInformationTranslationUi() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$site_name = 'Site name for testing configuration translation';
|
||||
$site_slogan = 'Site slogan for testing configuration translation';
|
||||
$fr_site_name = 'Nom du site pour tester la configuration traduction';
|
||||
$fr_site_slogan = 'Slogan du site pour tester la traduction de configuration';
|
||||
$translation_base_url = 'admin/config/system/site-information/translate';
|
||||
|
||||
// Set site name and slogan for default language.
|
||||
$this->setSiteInformation($site_name, $site_slogan);
|
||||
|
||||
$this->drupalGet('admin/config/system/site-information');
|
||||
// Check translation tab exist.
|
||||
$this->assertLinkByHref($translation_base_url);
|
||||
|
||||
$this->drupalGet($translation_base_url);
|
||||
|
||||
// Check that the 'Edit' link in the source language links back to the
|
||||
// original form.
|
||||
$this->clickLink(t('Edit'));
|
||||
// Also check that saving the form leads back to the translation overview.
|
||||
$this->drupalPostForm(NULL, [], t('Save configuration'));
|
||||
$this->assertUrl($translation_base_url);
|
||||
|
||||
// Check 'Add' link of French to visit add page.
|
||||
$this->assertLinkByHref("$translation_base_url/fr/add");
|
||||
$this->clickLink(t('Add'));
|
||||
|
||||
// Make sure original text is present on this page.
|
||||
$this->assertRaw($site_name);
|
||||
$this->assertRaw($site_slogan);
|
||||
|
||||
// Update site name and slogan for French.
|
||||
$edit = array(
|
||||
'translation[config_names][system.site][name]' => $fr_site_name,
|
||||
'translation[config_names][system.site][slogan]' => $fr_site_slogan,
|
||||
);
|
||||
|
||||
$this->drupalPostForm("$translation_base_url/fr/add", $edit, t('Save translation'));
|
||||
$this->assertRaw(t('Successfully saved @language translation.', array('@language' => 'French')));
|
||||
|
||||
// Check for edit, delete links (and no 'add' link) for French language.
|
||||
$this->assertNoLinkByHref("$translation_base_url/fr/add");
|
||||
$this->assertLinkByHref("$translation_base_url/fr/edit");
|
||||
$this->assertLinkByHref("$translation_base_url/fr/delete");
|
||||
|
||||
// Check translation saved proper.
|
||||
$this->drupalGet("$translation_base_url/fr/edit");
|
||||
$this->assertFieldByName('translation[config_names][system.site][name]', $fr_site_name);
|
||||
$this->assertFieldByName('translation[config_names][system.site][slogan]', $fr_site_slogan);
|
||||
|
||||
// Check French translation of site name and slogan are in place.
|
||||
$this->drupalGet('fr');
|
||||
$this->assertRaw($fr_site_name);
|
||||
$this->assertRaw($fr_site_slogan);
|
||||
|
||||
// Visit French site to ensure base language string present as source.
|
||||
$this->drupalGet("fr/$translation_base_url/fr/edit");
|
||||
$this->assertText($site_name);
|
||||
$this->assertText($site_slogan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the site information translation interface.
|
||||
*/
|
||||
public function testSourceValueDuplicateSave() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$site_name = 'Site name for testing configuration translation';
|
||||
$site_slogan = 'Site slogan for testing configuration translation';
|
||||
$translation_base_url = 'admin/config/system/site-information/translate';
|
||||
$this->setSiteInformation($site_name, $site_slogan);
|
||||
|
||||
$this->drupalGet($translation_base_url);
|
||||
|
||||
// Case 1: Update new value for site slogan and site name.
|
||||
$edit = array(
|
||||
'translation[config_names][system.site][name]' => 'FR ' . $site_name,
|
||||
'translation[config_names][system.site][slogan]' => 'FR ' . $site_slogan,
|
||||
);
|
||||
// First time, no overrides, so just Add link.
|
||||
$this->drupalPostForm("$translation_base_url/fr/add", $edit, t('Save translation'));
|
||||
|
||||
// Read overridden file from active config.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
|
||||
|
||||
// Expect both name and slogan in language specific file.
|
||||
$expected = array(
|
||||
'name' => 'FR ' . $site_name,
|
||||
'slogan' => 'FR ' . $site_slogan,
|
||||
);
|
||||
$this->assertEqual($expected, $override->get());
|
||||
|
||||
// Case 2: Update new value for site slogan and default value for site name.
|
||||
$this->drupalGet("$translation_base_url/fr/edit");
|
||||
// Assert that the language configuration does not leak outside of the
|
||||
// translation form into the actual site name and slogan.
|
||||
$this->assertNoText('FR ' . $site_name);
|
||||
$this->assertNoText('FR ' . $site_slogan);
|
||||
$edit = array(
|
||||
'translation[config_names][system.site][name]' => $site_name,
|
||||
'translation[config_names][system.site][slogan]' => 'FR ' . $site_slogan,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save translation'));
|
||||
$this->assertRaw(t('Successfully updated @language translation.', array('@language' => 'French')));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
|
||||
|
||||
// Expect only slogan in language specific file.
|
||||
$expected = 'FR ' . $site_slogan;
|
||||
$this->assertEqual($expected, $override->get('slogan'));
|
||||
|
||||
// Case 3: Keep default value for site name and slogan.
|
||||
$this->drupalGet("$translation_base_url/fr/edit");
|
||||
$this->assertNoText('FR ' . $site_slogan);
|
||||
$edit = array(
|
||||
'translation[config_names][system.site][name]' => $site_name,
|
||||
'translation[config_names][system.site][slogan]' => $site_slogan,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save translation'));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
|
||||
|
||||
// Expect no language specific file.
|
||||
$this->assertTrue($override->isNew());
|
||||
|
||||
// Check configuration page with translator user. Should have no access.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->translatorUser);
|
||||
$this->drupalGet('admin/config/system/site-information');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// While translator can access the translation page, the edit link is not
|
||||
// present due to lack of permissions.
|
||||
$this->drupalGet($translation_base_url);
|
||||
$this->assertNoLink(t('Edit'));
|
||||
|
||||
// Check 'Add' link for French.
|
||||
$this->assertLinkByHref("$translation_base_url/fr/add");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the contact form translation.
|
||||
*/
|
||||
public function testContactConfigEntityTranslation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/structure/contact');
|
||||
|
||||
// Check for default contact form configuration entity from Contact module.
|
||||
$this->assertLinkByHref('admin/structure/contact/manage/feedback');
|
||||
|
||||
// Save default language configuration.
|
||||
$label = 'Send your feedback';
|
||||
$edit = array(
|
||||
'label' => $label,
|
||||
'recipients' => 'sales@example.com,support@example.com',
|
||||
'reply' => 'Thank you for your mail',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/contact/manage/feedback', $edit, t('Save'));
|
||||
|
||||
// Ensure translation link is present.
|
||||
$translation_base_url = 'admin/structure/contact/manage/feedback/translate';
|
||||
$this->assertLinkByHref($translation_base_url);
|
||||
|
||||
// Make sure translate tab is present.
|
||||
$this->drupalGet('admin/structure/contact/manage/feedback');
|
||||
$this->assertLink(t('Translate @type', array('@type' => 'contact form')));
|
||||
|
||||
// Visit the form to confirm the changes.
|
||||
$this->drupalGet('contact/feedback');
|
||||
$this->assertText($label);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$this->drupalGet($translation_base_url);
|
||||
$this->assertLink(t('Translate @type', array('@type' => 'contact form')));
|
||||
|
||||
// 'Add' link should be present for $langcode translation.
|
||||
$translation_page_url = "$translation_base_url/$langcode/add";
|
||||
$this->assertLinkByHref($translation_page_url);
|
||||
|
||||
// Make sure original text is present on this page.
|
||||
$this->drupalGet($translation_page_url);
|
||||
$this->assertText($label);
|
||||
|
||||
// Update translatable fields.
|
||||
$edit = array(
|
||||
'translation[config_names][contact.form.feedback][label]' => 'Website feedback - ' . $langcode,
|
||||
'translation[config_names][contact.form.feedback][reply]' => 'Thank you for your mail - ' . $langcode,
|
||||
);
|
||||
|
||||
// Save language specific version of form.
|
||||
$this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
|
||||
|
||||
// Expect translated values in language specific file.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.form.feedback');
|
||||
$expected = array(
|
||||
'label' => 'Website feedback - ' . $langcode,
|
||||
'reply' => 'Thank you for your mail - ' . $langcode,
|
||||
);
|
||||
$this->assertEqual($expected, $override->get());
|
||||
|
||||
// Check for edit, delete links (and no 'add' link) for $langcode.
|
||||
$this->assertNoLinkByHref("$translation_base_url/$langcode/add");
|
||||
$this->assertLinkByHref("$translation_base_url/$langcode/edit");
|
||||
$this->assertLinkByHref("$translation_base_url/$langcode/delete");
|
||||
|
||||
// Visit language specific version of form to check label.
|
||||
$this->drupalGet($langcode . '/contact/feedback');
|
||||
$this->assertText('Website feedback - ' . $langcode);
|
||||
|
||||
// Submit feedback.
|
||||
$edit = array(
|
||||
'subject[0][value]' => 'Test subject',
|
||||
'message[0][value]' => 'Test message',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Send message'));
|
||||
}
|
||||
|
||||
// Now that all language translations are present, check translation and
|
||||
// original text all appear in any translated page on the translation
|
||||
// forms.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$langcode_prefixes = array_merge(array(''), $this->langcodes);
|
||||
foreach ($langcode_prefixes as $langcode_prefix) {
|
||||
$this->drupalGet(ltrim("$langcode_prefix/$translation_base_url/$langcode/edit", '/'));
|
||||
$this->assertFieldByName('translation[config_names][contact.form.feedback][label]', 'Website feedback - ' . $langcode);
|
||||
$this->assertText($label);
|
||||
}
|
||||
}
|
||||
|
||||
// We get all emails so no need to check inside the loop.
|
||||
$captured_emails = $this->drupalGetMails();
|
||||
|
||||
// Check language specific auto reply text in email body.
|
||||
foreach ($captured_emails as $email) {
|
||||
if ($email['id'] == 'contact_page_autoreply') {
|
||||
// Trim because we get an added newline for the body.
|
||||
$this->assertEqual(trim($email['body']), 'Thank you for your mail - ' . $email['langcode']);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that delete links work and operations perform properly.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$replacements = array('%label' => t('!label !entity_type', array('!label' => $label, '!entity_type' => Unicode::strtolower(t('Contact form')))), '@language' => \Drupal::languageManager()->getLanguage($langcode)->getName());
|
||||
|
||||
$this->drupalGet("$translation_base_url/$langcode/delete");
|
||||
$this->assertRaw(t('Are you sure you want to delete the @language translation of %label?', $replacements));
|
||||
// Assert link back to list page to cancel delete is present.
|
||||
$this->assertLinkByHref($translation_base_url);
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$this->assertRaw(t('@language translation of %label was deleted', $replacements));
|
||||
$this->assertLinkByHref("$translation_base_url/$langcode/add");
|
||||
$this->assertNoLinkByHref("translation_base_url/$langcode/edit");
|
||||
$this->assertNoLinkByHref("$translation_base_url/$langcode/delete");
|
||||
|
||||
// Expect no language specific file present anymore.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.form.feedback');
|
||||
$this->assertTrue($override->isNew());
|
||||
}
|
||||
|
||||
// Check configuration page with translator user. Should have no access.
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->translatorUser);
|
||||
$this->drupalGet('admin/structure/contact/manage/feedback');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// While translator can access the translation page, the edit link is not
|
||||
// present due to lack of permissions.
|
||||
$this->drupalGet($translation_base_url);
|
||||
$this->assertNoLink(t('Edit'));
|
||||
|
||||
// Check 'Add' link for French.
|
||||
$this->assertLinkByHref("$translation_base_url/fr/add");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the node type translation.
|
||||
*/
|
||||
public function testNodeTypeTranslation() {
|
||||
$type = Unicode::strtolower($this->randomMachineName(16));
|
||||
$name = $this->randomString();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalCreateContentType(array('type' => $type, 'name' => $name));
|
||||
|
||||
// Translate the node type name.
|
||||
$langcode = $this->langcodes[0];
|
||||
$translated_name = $langcode . '-' . $name;
|
||||
$edit = array(
|
||||
"translation[config_names][node.type.$type][name]" => $translated_name,
|
||||
);
|
||||
$this->drupalPostForm("admin/structure/types/manage/$type/translate/$langcode/add", $edit, t('Save translation'));
|
||||
|
||||
// Check the name is translated without admin theme for editing.
|
||||
$this->drupalPostForm('admin/appearance', array('use_admin_theme' => '0'), t('Save configuration'));
|
||||
$this->drupalGet("$langcode/node/add/$type");
|
||||
$this->assertRaw(t('Create @name', array('@name' => $translated_name)));
|
||||
|
||||
// Check the name is translated with admin theme for editing.
|
||||
$this->drupalPostForm('admin/appearance', array('use_admin_theme' => '1'), t('Save configuration'));
|
||||
$this->drupalGet("$langcode/node/add/$type");
|
||||
$this->assertRaw(t('Create @name', array('@name' => $translated_name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests date format translation.
|
||||
*/
|
||||
public function testDateFormatTranslation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/config/regional/date-time');
|
||||
|
||||
// Check for medium format.
|
||||
$this->assertLinkByHref('admin/config/regional/date-time/formats/manage/medium');
|
||||
|
||||
// Save default language configuration for a new format.
|
||||
$edit = array(
|
||||
'label' => 'Custom medium date',
|
||||
'id' => 'custom_medium',
|
||||
'date_format_pattern' => 'Y. m. d. H:i',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
|
||||
|
||||
// Test translating a default shipped format and our custom format.
|
||||
$formats = array(
|
||||
'medium' => 'Default medium date',
|
||||
'custom_medium' => 'Custom medium date',
|
||||
);
|
||||
foreach($formats as $id => $label) {
|
||||
$translation_base_url = 'admin/config/regional/date-time/formats/manage/' . $id . '/translate';
|
||||
|
||||
$this->drupalGet($translation_base_url);
|
||||
|
||||
// 'Add' link should be present for French translation.
|
||||
$translation_page_url = "$translation_base_url/fr/add";
|
||||
$this->assertLinkByHref($translation_page_url);
|
||||
|
||||
// Make sure original text is present on this page.
|
||||
$this->drupalGet($translation_page_url);
|
||||
$this->assertText($label);
|
||||
|
||||
// Make sure that the date library is added.
|
||||
$this->assertRaw('core/modules/system/js/system.date.js');
|
||||
|
||||
// Update translatable fields.
|
||||
$edit = array(
|
||||
'translation[config_names][core.date_format.' . $id . '][label]' => $id . ' - FR',
|
||||
'translation[config_names][core.date_format.' . $id . '][pattern]' => 'D',
|
||||
);
|
||||
|
||||
// Save language specific version of form.
|
||||
$this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
|
||||
|
||||
// Get translation and check we've got the right value.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'core.date_format.' . $id);
|
||||
$expected = array(
|
||||
'label' => $id . ' - FR',
|
||||
'pattern' => 'D',
|
||||
);
|
||||
$this->assertEqual($expected, $override->get());
|
||||
|
||||
// Formatting the date 8 / 27 / 1985 @ 13:37 EST with pattern D should
|
||||
// display "Tue".
|
||||
$formatted_date = format_date(494015820, $id, NULL, NULL, 'fr');
|
||||
$this->assertEqual($formatted_date, 'Tue', 'Got the right formatted date using the date format translation pattern.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the account settings translation interface.
|
||||
*
|
||||
* This is the only special case so far where we have multiple configuration
|
||||
* names involved building up one configuration translation form. Test that
|
||||
* the translations are saved for all configuration names properly.
|
||||
*/
|
||||
public function testAccountSettingsConfigurationTranslation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->assertLink(t('Translate @type', array('@type' => 'account settings')));
|
||||
|
||||
$this->drupalGet('admin/config/people/accounts/translate');
|
||||
$this->assertLink(t('Translate @type', array('@type' => 'account settings')));
|
||||
$this->assertLinkByHref('admin/config/people/accounts/translate/fr/add');
|
||||
|
||||
// Update account settings fields for French.
|
||||
$edit = array(
|
||||
'translation[config_names][user.settings][anonymous]' => 'Anonyme',
|
||||
'translation[config_names][user.mail][status_blocked][subject]' => 'Testing, your account is blocked.',
|
||||
'translation[config_names][user.mail][status_blocked][body]' => 'Testing account blocked body.',
|
||||
);
|
||||
|
||||
$this->drupalPostForm('admin/config/people/accounts/translate/fr/add', $edit, t('Save translation'));
|
||||
|
||||
// Make sure the changes are saved and loaded back properly.
|
||||
$this->drupalGet('admin/config/people/accounts/translate/fr/edit');
|
||||
foreach ($edit as $key => $value) {
|
||||
// Check the translations appear in the right field type as well.
|
||||
$xpath = '//' . (strpos($key, '[body]') ? 'textarea' : 'input') . '[@name="'. $key . '"]';
|
||||
$this->assertFieldByXPath($xpath, $value);
|
||||
}
|
||||
// Check that labels for email settings appear.
|
||||
$this->assertText(t('Account cancellation confirmation'));
|
||||
$this->assertText(t('Password recovery'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests source and target language edge cases.
|
||||
*/
|
||||
public function testSourceAndTargetLanguage() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Loading translation page for not-specified language (und)
|
||||
// should return 403.
|
||||
$this->drupalGet('admin/config/system/site-information/translate/und/add');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Check the source language doesn't have 'Add' or 'Delete' link and
|
||||
// make sure source language edit goes to original configuration page
|
||||
// not the translation specific edit page.
|
||||
$this->drupalGet('admin/config/system/site-information/translate');
|
||||
$this->assertNoLinkByHref('admin/config/system/site-information/translate/en/edit');
|
||||
$this->assertNoLinkByHref('admin/config/system/site-information/translate/en/add');
|
||||
$this->assertNoLinkByHref('admin/config/system/site-information/translate/en/delete');
|
||||
$this->assertLinkByHref('admin/config/system/site-information');
|
||||
|
||||
// Translation addition to source language should return 403.
|
||||
$this->drupalGet('admin/config/system/site-information/translate/en/add');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Translation editing in source language should return 403.
|
||||
$this->drupalGet('admin/config/system/site-information/translate/en/edit');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Translation deletion in source language should return 403.
|
||||
$this->drupalGet('admin/config/system/site-information/translate/en/delete');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Set default language of site information to not-specified language (und).
|
||||
$this->config('system.site')
|
||||
->set('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED)
|
||||
->save();
|
||||
|
||||
// Make sure translation tab does not exist on the configuration page.
|
||||
$this->drupalGet('admin/config/system/site-information');
|
||||
$this->assertNoLinkByHref('admin/config/system/site-information/translate');
|
||||
|
||||
// If source language is not specified, translation page should be 403.
|
||||
$this->drupalGet('admin/config/system/site-information/translate');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the views translation interface.
|
||||
*/
|
||||
public function testViewsTranslationUI() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Assert contextual link related to views.
|
||||
$ids = array('entity.view.edit_form:view=frontpage:location=page&name=frontpage&display_id=page_1');
|
||||
$response = $this->renderContextualLinks($ids, 'node');
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->assertTrue(strpos($json[$ids[0]], t('Translate view')), 'Translate view contextual link added.');
|
||||
|
||||
$description = 'All content promoted to the front page.';
|
||||
$human_readable_name = 'Frontpage';
|
||||
$display_settings_master = 'Master';
|
||||
$display_options_master = '(Empty)';
|
||||
$translation_base_url = 'admin/structure/views/view/frontpage/translate';
|
||||
|
||||
$this->drupalGet($translation_base_url);
|
||||
|
||||
// Check 'Add' link of French to visit add page.
|
||||
$this->assertLinkByHref("$translation_base_url/fr/add");
|
||||
$this->clickLink(t('Add'));
|
||||
|
||||
// Make sure original text is present on this page.
|
||||
$this->assertRaw($description);
|
||||
$this->assertRaw($human_readable_name);
|
||||
|
||||
// Update Views Fields for French.
|
||||
$edit = array(
|
||||
'translation[config_names][views.view.frontpage][description]' => $description . " FR",
|
||||
'translation[config_names][views.view.frontpage][label]' => $human_readable_name . " FR",
|
||||
'translation[config_names][views.view.frontpage][display][default][display_title]' => $display_settings_master . " FR",
|
||||
'translation[config_names][views.view.frontpage][display][default][display_options][title]' => $display_options_master . " FR",
|
||||
);
|
||||
$this->drupalPostForm("$translation_base_url/fr/add", $edit, t('Save translation'));
|
||||
$this->assertRaw(t('Successfully saved @language translation.', array('@language' => 'French')));
|
||||
|
||||
// Check for edit, delete links (and no 'add' link) for French language.
|
||||
$this->assertNoLinkByHref("$translation_base_url/fr/add");
|
||||
$this->assertLinkByHref("$translation_base_url/fr/edit");
|
||||
$this->assertLinkByHref("$translation_base_url/fr/delete");
|
||||
|
||||
// Check translation saved proper.
|
||||
$this->drupalGet("$translation_base_url/fr/edit");
|
||||
$this->assertFieldByName('translation[config_names][views.view.frontpage][description]', $description . " FR");
|
||||
$this->assertFieldByName('translation[config_names][views.view.frontpage][label]', $human_readable_name . " FR");
|
||||
$this->assertFieldByName('translation[config_names][views.view.frontpage][display][default][display_title]', $display_settings_master . " FR");
|
||||
$this->assertFieldByName('translation[config_names][views.view.frontpage][display][default][display_options][title]', $display_options_master . " FR");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translation storage in locale storage.
|
||||
*/
|
||||
public function testLocaleDBStorage() {
|
||||
// Enable import of translations. By default this is disabled for automated
|
||||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->save();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$langcode = 'xx';
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => Language::DIRECTION_LTR,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
|
||||
// Make sure there is no translation stored in locale storage before edit.
|
||||
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
|
||||
$this->assertTrue(empty($translation));
|
||||
|
||||
// Add custom translation.
|
||||
$edit = array(
|
||||
'translation[config_names][user.settings][anonymous]' => 'Anonyme',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/people/accounts/translate/fr/add', $edit, t('Save translation'));
|
||||
|
||||
// Make sure translation stored in locale storage after saved language
|
||||
// specific configuration translation.
|
||||
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
|
||||
$this->assertEqual('Anonyme', $translation->getString());
|
||||
|
||||
// revert custom translations to base translation.
|
||||
$edit = array(
|
||||
'translation[config_names][user.settings][anonymous]' => 'Anonymous',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/people/accounts/translate/fr/edit', $edit, t('Save translation'));
|
||||
|
||||
// Make sure there is no translation stored in locale storage after revert.
|
||||
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
|
||||
$this->assertEqual('Anonymous', $translation->getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the single language existing.
|
||||
*/
|
||||
public function testSingleLanguageUI() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Delete French language
|
||||
$this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
|
||||
$this->assertRaw(t('The %language (%langcode) language has been removed.', array('%language' => 'French', '%langcode' => 'fr')));
|
||||
|
||||
// Change default language to Tamil.
|
||||
$edit = array(
|
||||
'site_default_language' => 'ta',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
|
||||
$this->assertRaw(t('Configuration saved.'));
|
||||
|
||||
// Delete English language
|
||||
$this->drupalPostForm('admin/config/regional/language/delete/en', array(), t('Delete'));
|
||||
$this->assertRaw(t('The %language (%langcode) language has been removed.', array('%language' => 'English', '%langcode' => 'en')));
|
||||
|
||||
// Visit account setting translation page, this should not
|
||||
// throw any notices.
|
||||
$this->drupalGet('admin/config/people/accounts/translate');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the config_translation_info_alter() hook.
|
||||
*/
|
||||
public function testAlterInfo() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->container->get('state')->set('config_translation_test_config_translation_info_alter', TRUE);
|
||||
$this->container->get('plugin.manager.config_translation.mapper')->clearCachedDefinitions();
|
||||
|
||||
// Check out if the translation page has the altered in settings.
|
||||
$this->drupalGet('admin/config/system/site-information/translate/fr/add');
|
||||
$this->assertText(t('Feed channel'));
|
||||
$this->assertText(t('Feed description'));
|
||||
|
||||
// Check if the translation page does not have the altered out settings.
|
||||
$this->drupalGet('admin/config/people/accounts/translate/fr/add');
|
||||
$this->assertText(t('Name'));
|
||||
$this->assertNoText(t('Account cancellation confirmation'));
|
||||
$this->assertNoText(t('Password recovery'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the sequence data type translation.
|
||||
*/
|
||||
public function testSequenceTranslation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
|
||||
$expected = array(
|
||||
'kitten',
|
||||
'llama',
|
||||
'elephant'
|
||||
);
|
||||
$actual = $config_factory
|
||||
->getEditable('config_translation_test.content')
|
||||
->get('animals');
|
||||
$this->assertEqual($expected, $actual);
|
||||
|
||||
$edit = array(
|
||||
'translation[config_names][config_translation_test.content][content][value]' => '<p><strong>Hello World</strong> - FR</p>',
|
||||
'translation[config_names][config_translation_test.content][animals][0]' => 'kitten - FR',
|
||||
'translation[config_names][config_translation_test.content][animals][1]' => 'llama - FR',
|
||||
'translation[config_names][config_translation_test.content][animals][2]' => 'elephant - FR',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/media/file-system/translate/fr/add', $edit, t('Save translation'));
|
||||
|
||||
$this->container->get('language.config_factory_override')
|
||||
->setLanguage(new Language(array('id' => 'fr')));
|
||||
|
||||
$expected = array(
|
||||
'kitten - FR',
|
||||
'llama - FR',
|
||||
'elephant - FR',
|
||||
);
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->get('animals');
|
||||
$this->assertEqual($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test text_format translation.
|
||||
*/
|
||||
public function testTextFormatTranslation() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
|
||||
$expected = array(
|
||||
'value' => '<p><strong>Hello World</strong></p>',
|
||||
'format' => 'plain_text',
|
||||
);
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->getOriginal('content', FALSE);
|
||||
$this->assertEqual($expected, $actual);
|
||||
|
||||
$translation_base_url = 'admin/config/media/file-system/translate';
|
||||
$this->drupalGet($translation_base_url);
|
||||
|
||||
// 'Add' link should be present for French translation.
|
||||
$translation_page_url = "$translation_base_url/fr/add";
|
||||
$this->assertLinkByHref($translation_page_url);
|
||||
|
||||
$this->drupalGet($translation_page_url);
|
||||
|
||||
// Assert that changing the text format is not possible, even for an
|
||||
// administrator.
|
||||
$this->assertNoFieldByName('translation[config_names][config_translation_test.content][content][format]');
|
||||
|
||||
// Update translatable fields.
|
||||
$edit = array(
|
||||
'translation[config_names][config_translation_test.content][content][value]' => '<p><strong>Hello World</strong> - FR</p>',
|
||||
);
|
||||
|
||||
// Save language specific version of form.
|
||||
$this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
|
||||
|
||||
// Get translation and check we've got the right value.
|
||||
$expected = array(
|
||||
'value' => '<p><strong>Hello World</strong> - FR</p>',
|
||||
'format' => 'plain_text',
|
||||
);
|
||||
$this->container->get('language.config_factory_override')
|
||||
->setLanguage(new Language(array('id' => 'fr')));
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->get('content');
|
||||
$this->assertEqual($expected, $actual);
|
||||
|
||||
// Change the text format of the source configuration and verify that the
|
||||
// text format of the translation does not change because that could lead to
|
||||
// security vulnerabilities.
|
||||
$config_factory
|
||||
->getEditable('config_translation_test.content')
|
||||
->set('content.format', 'full_html')
|
||||
->save();
|
||||
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->get('content');
|
||||
// The translation should not have changed, so re-use $expected.
|
||||
$this->assertEqual($expected, $actual);
|
||||
|
||||
// Because the text is now in a text format that the translator does not
|
||||
// have access to, the translator should not be able to translate it.
|
||||
$translation_page_url = "$translation_base_url/fr/edit";
|
||||
$this->drupalLogin($this->translatorUser);
|
||||
$this->drupalGet($translation_page_url);
|
||||
$this->assertDisabledTextarea('edit-translation-config-names-config-translation-testcontent-content-value');
|
||||
$this->drupalPostForm(NULL, array(), t('Save translation'));
|
||||
// Check that submitting the form did not update the text format of the
|
||||
// translation.
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->get('content');
|
||||
$this->assertEqual($expected, $actual);
|
||||
|
||||
// The administrator must explicitly change the text format.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array(
|
||||
'translation[config_names][config_translation_test.content][content][format]' => 'full_html',
|
||||
);
|
||||
$this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
|
||||
$expected = array(
|
||||
'value' => '<p><strong>Hello World</strong> - FR</p>',
|
||||
'format' => 'full_html',
|
||||
);
|
||||
$actual = $config_factory
|
||||
->get('config_translation_test.content')
|
||||
->get('content');
|
||||
$this->assertEqual($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translation from locale storage.
|
||||
*
|
||||
* @param $config_name
|
||||
* Configuration object.
|
||||
* @param $key
|
||||
* Translation configuration field key.
|
||||
* @param $langcode
|
||||
* String language code to load translation.
|
||||
*
|
||||
* @return bool|mixed
|
||||
* Returns translation if exists, FALSE otherwise.
|
||||
*/
|
||||
protected function getTranslation($config_name, $key, $langcode) {
|
||||
$settings_locations = $this->localeStorage->getLocations(array('type' => 'configuration', 'name' => $config_name));
|
||||
$this->assertTrue(!empty($settings_locations), format_string('Configuration locations found for %config_name.', array('%config_name' => $config_name)));
|
||||
|
||||
if (!empty($settings_locations)) {
|
||||
$source = $this->container->get('config.factory')->get($config_name)->get($key);
|
||||
$source_string = $this->localeStorage->findString(array('source' => $source, 'type' => 'configuration'));
|
||||
$this->assertTrue(!empty($source_string), format_string('Found string for %config_name.%key.', array('%config_name' => $config_name, '%key' => $key)));
|
||||
|
||||
if (!empty($source_string)) {
|
||||
$conditions = array(
|
||||
'lid' => $source_string->lid,
|
||||
'language' => $langcode,
|
||||
);
|
||||
$translations = $this->localeStorage->getTranslations($conditions + array('translated' => TRUE));
|
||||
return reset($translations);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets site name and slogan for default language, helps in tests.
|
||||
*
|
||||
* @param string $site_name
|
||||
* @param string $site_slogan
|
||||
*/
|
||||
protected function setSiteInformation($site_name, $site_slogan) {
|
||||
$edit = array(
|
||||
'site_name' => $site_name,
|
||||
'site_slogan' => $site_slogan,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
|
||||
$this->assertRaw(t('The configuration options have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server-rendered contextual links for the given contextual link ids.
|
||||
*
|
||||
* @param array $ids
|
||||
* An array of contextual link ids.
|
||||
* @param string $current_path
|
||||
* The Drupal path for the page for which the contextual links are rendered.
|
||||
*
|
||||
* @return string
|
||||
* The response body.
|
||||
*/
|
||||
protected function renderContextualLinks($ids, $current_path) {
|
||||
$post = array();
|
||||
for ($i = 0; $i < count($ids); $i++) {
|
||||
$post['ids[' . $i . ']'] = $ids[$i];
|
||||
}
|
||||
return $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => $current_path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a textarea with a given ID has been disabled from editing.
|
||||
*
|
||||
* @param string $id
|
||||
* The HTML ID of the textarea.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion passed; FALSE otherwise.
|
||||
*/
|
||||
protected function assertDisabledTextarea($id) {
|
||||
$textarea = $this->xpath('//textarea[@id=:id and contains(@disabled, "disabled")]', array(
|
||||
':id' => $id,
|
||||
));
|
||||
$textarea = reset($textarea);
|
||||
$passed = $this->assertTrue($textarea instanceof \SimpleXMLElement, SafeMarkup::format('Disabled field @id exists.', array(
|
||||
'@id' => $id,
|
||||
)));
|
||||
$expected = 'This field has been disabled because you do not have sufficient permissions to edit it.';
|
||||
$passed = $passed && $this->assertEqual((string) $textarea, $expected, SafeMarkup::format('Disabled textarea @id hides text in an inaccessible text format.', array(
|
||||
'@id' => $id,
|
||||
)));
|
||||
// Make sure the text format select is not shown.
|
||||
$select_id = str_replace('value', 'format--2', $id);
|
||||
$select = $this->xpath('//select[@id=:id]', array(':id' => $select_id));
|
||||
return $passed && $this->assertFalse($select, SafeMarkup::format('Field @id does not exist.', array(
|
||||
'@id' => $id,
|
||||
)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationUiThemeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Verifies theme configuration translation settings.
|
||||
*
|
||||
* @group config_translation
|
||||
*/
|
||||
class ConfigTranslationUiThemeTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config_translation', 'config_translation_test'];
|
||||
|
||||
/**
|
||||
* Languages to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $langcodes = array('fr', 'ta');
|
||||
|
||||
/**
|
||||
* Administrator user for tests.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$admin_permissions = [
|
||||
'administer themes',
|
||||
'administer languages',
|
||||
'administer site configuration',
|
||||
'translate configuration',
|
||||
];
|
||||
// Create and login user.
|
||||
$this->adminUser = $this->drupalCreateUser($admin_permissions);
|
||||
|
||||
// Add languages.
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that theme provided *.config_translation.yml files are found.
|
||||
*/
|
||||
public function testThemeDiscovery() {
|
||||
// Install the test theme and rebuild routes.
|
||||
$theme = 'config_translation_test_theme';
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/appearance');
|
||||
$elements = $this->xpath('//a[normalize-space()=:label and contains(@href, :theme)]', [
|
||||
':label' => 'Install and set as default',
|
||||
':theme' => $theme,
|
||||
]);
|
||||
$this->drupalGet($GLOBALS['base_root'] . $elements[0]['href'], ['external' => TRUE]);
|
||||
|
||||
$translation_base_url = 'admin/config/development/performance/translate';
|
||||
$this->drupalGet($translation_base_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref("$translation_base_url/fr/add");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\config_translation\Tests\ConfigTranslationViewListUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config_translation\Tests;
|
||||
|
||||
use Drupal\views_ui\Tests\UITestBase;
|
||||
|
||||
/**
|
||||
* Visit view list and test if translate is available.
|
||||
*
|
||||
* @group config_translation
|
||||
*/
|
||||
class ConfigTranslationViewListUiTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('node', 'test_view');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_translation', 'views_ui');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$permissions = array(
|
||||
'administer views',
|
||||
'translate configuration',
|
||||
);
|
||||
|
||||
// Create and log in user.
|
||||
$this->drupalLogin($this->drupalCreateUser($permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests views_ui list to see if translate link is added to operations.
|
||||
*/
|
||||
public function testTranslateOperationInViewListUi() {
|
||||
// Views UI List 'admin/structure/views'.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$translate_link = 'admin/structure/views/view/test_view/translate';
|
||||
// Test if the link to translate the test_view is on the page.
|
||||
$this->assertLinkByHref($translate_link);
|
||||
|
||||
// Test if the link to translate actually goes to the translate page.
|
||||
$this->drupalGet($translate_link);
|
||||
$this->assertRaw('<th>' . t('Language') . '</th>');
|
||||
|
||||
// Test that the 'Edit' tab appears.
|
||||
$this->assertLinkByHref('admin/structure/views/view/test_view');
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue