Skip to content
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationBlockListController.
*/
namespace Drupal\config_translation\Controller;
use Drupal\Component\Utility\String;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
/**
* Defines the config translation controller for blocks.
*/
class ConfigTranslationBlockListController extends ConfigTranslationEntityListController {
/**
* An array of theme info keyed by theme name.
*
* @var array
*/
protected $themes = array();
/**
* {@inheritdoc}
*/
public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler) {
parent::__construct($entity_type, $entity_info, $storage, $module_handler);
$this->themes = list_themes();
}
/**
* {@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->get('theme');
$plugin_definition = $entity->getPlugin()->getPluginDefinition();
$row['label'] = array(
'data' => $this->getLabel($entity),
'class' => 'table-filter-text-source',
);
$row['theme'] = array(
'data' => String::checkPlain($this->themes[$theme]->info['name']),
'class' => 'table-filter-text-source',
);
$row['category'] = array(
'data' => String::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'));
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationController.
*/
namespace Drupal\config_translation\Controller;
use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\Core\Access\AccessManager;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Language\Language;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
use Symfony\Component\Routing\Route;
/**
* Provides page callbacks for the configuration translation interface.
*/
class ConfigTranslationController extends ControllerBase implements ContainerInjectionInterface {
/**
* Configuration mapper manager.
*
* @var \Drupal\config_translation\ConfigMapperManagerInterface
*/
protected $configMapperManager;
/**
* The menu link access service.
*
* @var \Drupal\Core\Access\AccessManager
*/
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;
/**
* Constructs a ConfigTranslationController.
*
* @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
* The configuration mapper manager.
* @param \Drupal\Core\Access\AccessManager $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.
*/
public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManager $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account) {
$this->configMapperManager = $config_mapper_manager;
$this->accessManager = $access_manager;
$this->router = $router;
$this->pathProcessor = $path_processor;
$this->account = $account;
}
/**
* {@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')
);
}
/**
* Language translations overview page for a configuration name.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Page request object.
* @param string $plugin_id
* The plugin ID of the mapper.
*
* @return array
* Page render array.
*/
public function itemPage(Request $request, $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()));
// It is possible the original language this configuration was saved with is
// not on the system. For example, the configuration shipped in English but
// the site has no English configured. Represent the original language in
// the table even if it is not currently configured.
$languages = language_list();
$original_langcode = $mapper->getLangcode();
if (!isset($languages[$original_langcode])) {
$language_name = language_name($original_langcode);
if ($original_langcode == 'en') {
$language_name = $this->t('Built-in English');
}
// Create a dummy language object for this listing only.
$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->id;
// This is needed because e.g.
// ConfigMapperInterface::getAddRouteParameters()
// 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->name)) . '</strong>';
// Check access for the path/route for editing, so we can decide to
// include a link to edit or not.
$route_request = $this->getRequestForPath($request, $mapper->getBasePath());
$edit_access = FALSE;
if (!empty($route_request)) {
$route_name = $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME);
// Note that the parameters don't really matter here since we're
// passing in the request which already has the upcast attributes.
$parameters = array();
$edit_access = $this->accessManager->checkNamedRoute($route_name, $parameters, $this->account, $route_request);
}
// Build list of operations.
$operations = array();
if ($edit_access) {
$operations['edit'] = array(
'title' => $this->t('Edit'),
'route_name' => $mapper->getBaseRouteName(),
'route_parameters' => $mapper->getBaseRouteParameters(),
'query' => array('destination' => $mapper->getOverviewPath()),
);
}
}
else {
$language_name = $language->name;
$operations = array();
// If no translation exists for this language, link to add one.
if (!$mapper->hasTranslation($language)) {
$operations['add'] = array(
'title' => $this->t('Add'),
'route_name' => $mapper->getAddRouteName(),
'route_parameters' => $mapper->getAddRouteParameters(),
);
}
else {
// Otherwise, link to edit the existing translation.
$operations['edit'] = array(
'title' => $this->t('Edit'),
'route_name' => $mapper->getEditRouteName(),
'route_parameters' => $mapper->getEditRouteParameters(),
);
$operations['delete'] = array(
'title' => $this->t('Delete'),
'route_name' => $mapper->getDeleteRouteName(),
'route_parameters' => $mapper->getDeleteRouteParameters(),
);
}
}
$page['languages'][$langcode]['language'] = array(
'#markup' => $language_name,
);
$page['languages'][$langcode]['operations'] = array(
'#type' => 'operations',
'#links' => $operations,
);
}
return $page;
}
/**
* Matches a path in the router.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Page request object.
* @param string $path
* Path to look up.
*
* @return \Symfony\Component\HttpFoundation\Request|null
* A populated request object or NULL if the patch couldn't be matched.
*/
protected function getRequestForPath(Request $request, $path) {
// @todo Use RequestHelper::duplicate once https://drupal.org/node/2090293
// is fixed.
$route_request = Request::create($request->getBaseUrl() . '/' . $path);
// Find the system path by resolving aliases, language prefix, etc.
$processed = $this->pathProcessor->processInbound($path, $route_request);
$route_request->attributes->set('_system_path', $processed);
// Attempt to match this path to provide a fully built request.
try {
$route_request->attributes->add($this->router->matchRequest($route_request));
return $route_request;
}
catch (NotFoundHttpException $e) {
return NULL;
}
catch (ResourceNotFoundException $e) {
return NULL;
}
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationEntityListController.
*/
namespace Drupal\config_translation\Controller;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListController;
/**
* Defines the configuration translation controller for entities.
*/
class ConfigTranslationEntityListController extends EntityListController implements ConfigTranslationEntityListControllerInterface {
/**
* 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() {
$table = parent::render();
$filter = $this->getFilterLabels();
usort($table['#rows'], array($this, 'sortRows'));
$build['filters'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array('table-filter', 'js-show'),
),
);
$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'] = $table;
$build['table']['#attributes']['class'][] = 'config-translation-entity-list';
$build['#attached']['library'][] = array('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 buildOperations(EntityInterface $entity) {
$operations = parent::buildOperations($entity);
foreach (array_keys($operations['#links']) as $operation) {
// This is a translation UI for translators. Show the translation
// operation only.
if (!($operation == 'translate')) {
unset($operations['#links'][$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);
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationEntityListControllerInterface.
*/
namespace Drupal\config_translation\Controller;
use Drupal\Core\Entity\EntityListControllerInterface;
/**
* Defines an interface for configuration translation entity list controllers.
*/
interface ConfigTranslationEntityListControllerInterface extends EntityListControllerInterface {
/**
* 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);
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationFieldInstanceListController.
*/
namespace Drupal\config_translation\Controller;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\field\Field;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the config translation controller for field instance entities.
*/
class ConfigTranslationFieldInstanceListController extends ConfigTranslationEntityListController {
/**
* The name of the entity type the field instances are attached to.
*
* @var string
*/
protected $baseEntityType = '';
/**
* An array containing the base entity type's definition.
*
* @var string
*/
protected $baseEntityInfo = array();
/**
* The bundle info for the base entity type.
*
* @var string
*/
protected $baseEntityBundles = array();
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* Instantiates a new instance of this entity controller.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The service container this object should use.
* @param string $entity_type
* The entity type which the controller handles.
* @param array $entity_info
* An array of entity info for the entity type.
* @param array $definition
* The plugin definition of the config translation mapper.
*
* @return static
* A new instance of the entity controller.
*/
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info, array $definition = array()) {
return new static(
$entity_type,
$entity_info,
$container->get('entity.manager')->getStorageController($entity_type),
$container->get('module_handler'),
$container->get('entity.manager'),
$definition
);
}
/**
* Constructs a new EntityListController object.
*
* @param string $entity_type
* The type of entity to be listed.
* @param array $entity_info
* An array of entity info for the entity type.
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
* The entity storage controller class.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke hooks on.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
* @param $definition
* The plugin definition of the config translation mapper.
*/
public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, EntityManager $entity_manager, array $definition) {
parent::__construct($entity_type, $entity_info, $storage, $module_handler);
$this->entityManager = $entity_manager;
$this->baseEntityType = $definition['base_entity_type'];
$this->baseEntityInfo = $this->entityManager->getDefinition($this->baseEntityType);
$this->baseEntityBundles = $this->entityManager->getBundleInfo($this->baseEntityType);
}
/**
* {@inheritdoc}
*/
public function load() {
$entities = array();
// It is not possible to use the standard load method, because this needs
// all field instance entities only for the given baseEntityType.
foreach (Field::fieldInfo()->getInstances($this->baseEntityType) as $fields) {
$entities = array_merge($entities, array_values($fields));
}
return $entities;
}
/**
* {@inheritdoc}
*/
public function getFilterLabels() {
$info = parent::getFilterLabels();
$bundle = isset($this->baseEntityInfo['bundle_label']) ? $this->baseEntityInfo['bundle_label'] : $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' => String::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'] = isset($this->baseEntityInfo['bundle_label']) ? $this->baseEntityInfo['bundle_label'] : $this->t('Bundle');
}
return $header + parent::buildHeader();
}
/**
* Controls the visibility of the bundle column on field instance 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 (isset($this->baseEntityInfo['bundle_keys']['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'));
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationListController.
*/
namespace Drupal\config_translation\Controller;
use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Defines the configuration translation list controller.
*/
class ConfigTranslationListController extends ControllerBase implements ContainerInjectionInterface {
/**
* Definition of the config mapper.
*
* @var array
*/
protected $mapperDefinition;
/**
* The config mapper.
*
* @var \Drupal\config_translation\ConfigEntityMapper
*/
protected $mapper;
/**
* Constructs a new ConfigTranslationListController object.
*
* @param \Drupal\config_translation\ConfigMapperManagerInterface $mapper_manager
* The config mapper manager.
* @param $config_translation_mapper
* The config mapper id.
*/
public function __construct(ConfigMapperManagerInterface $mapper_manager, $config_translation_mapper) {
$this->mapperDefinition = $mapper_manager->getDefinition($config_translation_mapper);
$this->mapper = $mapper_manager->createInstance($config_translation_mapper, $this->mapperDefinition);
}
/**
* {inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.config_translation.mapper'),
$container->get('request')->attributes->get('_raw_variables')->get('config_translation_mapper')
);
}
/**
* Provides the listing page for any entity type.
*
* @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() {
if (!$this->mapper) {
throw new NotFoundHttpException();
}
$entity_type = $this->mapper->getType();
// If the mapper, for example the mapper for field instances, 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.
$class = $this->mapperDefinition['list_controller'];
/** @var \Drupal\config_translation\Controller\ConfigTranslationEntityListControllerInterface $controller */
$controller = new $class($entity_type, $this->entityManager()->getDefinition($entity_type), $this->entityManager()->getStorageController($entity_type), $this->moduleHandler(), $this->entityManager(), $this->mapperDefinition);
$build = $controller->render();
$build['#title'] = $this->mapper->getTypeLabel();
return $build;
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Controller\ConfigTranslationMapperList.
*/
namespace Drupal\config_translation\Controller;
use Drupal\Component\Utility\String;
use Drupal\config_translation\ConfigMapperInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the configuration translation mapper list.
*
* Groups all defined configuration mapper instances by weight.
*/
class ConfigTranslationMapperList extends ControllerBase implements ContainerInjectionInterface {
/**
* A array of configuration mapper instances.
*
* @var \Drupal\config_translation\ConfigMapperInterface[]
*/
protected $mappers;
/**
* Constructs a new ConfigTranslationMapperIndex 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 theme_table().
*
* @return array
* Renderable array with config translation mappers.
*/
public function render() {
$build = array(
'#theme' => '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'] = String::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\EntityListController::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;
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Exception\InvalidMapperDefinitionException.
*/
namespace Drupal\config_translation\Exception;
use Drupal\Component\Plugin\Exception\PluginException;
/**
* Defines a class for invalid configuration mapper definition exceptions.
*/
class InvalidMapperDefinitionException extends PluginException {
/**
* The plugin ID of the mapper.
*
* @var string
*/
protected $pluginId;
/**
* Constructs a InvalidMapperDefinitionException.
*
* @param string $plugin_id
* The plugin ID of the mapper.
*
* @see \Exception for the remaining parameters.
*/
public function __construct($plugin_id, $message = '', $code = 0, \Exception $previous = NULL) {
$this->pluginId = $plugin_id;
parent::__construct($message, $code, $previous);
}
/**
* Returns the plugin ID of the mapper that raised the exception.
*
* @return string
* The plugin ID.
*/
public function getPluginId() {
return $this->pluginId;
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Form\ConfigTranslationAddForm.
*/
namespace Drupal\config_translation\Form;
use Symfony\Component\HttpFoundation\Request;
/**
* Defines a form controller for adding configuration translations.
*/
class ConfigTranslationAddForm extends ConfigTranslationFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_translation_add_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$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->name,
));
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
parent::submitForm($form, $form_state);
drupal_set_message($this->t('Successfully saved @language translation.', array('@language' => $this->language->name)));
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Form\ConfigTranslationDeleteForm.
*/
namespace Drupal\config_translation\Form;
use Drupal\config_translation\ConfigMapperInterface;
use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Language\Language;
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 configuration storage.
*
* @var \Drupal\Core\Config\StorageInterface $config_storage
*/
protected $configStorage;
/**
* 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\Language
*/
protected $language;
/**
* Constructs a ConfigTranslationDeleteForm.
*
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The 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(StorageInterface $config_storage, ConfigMapperManagerInterface $config_mapper_manager, ModuleHandlerInterface $module_handler) {
$this->configStorage = $config_storage;
$this->configMapperManager = $config_mapper_manager;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.storage'),
$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->name));
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function getCancelRoute() {
return array(
'route_name' => $this->mapper->getOverviewRouteName(),
'route_parameters' => $this->mapper->getOverviewRouteParameters(),
);
}
/**
* {@inheritdoc}
*/
public function getFormID() {
return 'config_translation_delete_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$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 = language_load($langcode);
if (!$language) {
throw new NotFoundHttpException();
}
$this->mapper = $mapper;
$this->language = $language;
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
foreach ($this->mapper->getConfigNames() as $name) {
$this->configStorage->delete('locale.config.' . $this->language->id . '.' . $name);
}
// Flush all persistent caches.
$this->moduleHandler->invokeAll('cache_flush');
foreach (Cache::getBins() as $service_id => $cache_backend) {
if ($service_id != 'cache.menu') {
$cache_backend->deleteAll();
}
}
drupal_set_message($this->t('@language translation of %label was deleted', array('%label' => $this->mapper->getTitle(), '@language' => $this->language->name)));
$form_state['redirect_route'] = array(
'route_name' => $this->mapper->getOverviewRoute(),
'route_parameters' => $this->mapper->getOverviewRouteParameters(),
);
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\Form\ConfigTranslationEditForm.
*/
namespace Drupal\config_translation\Form;
use Symfony\Component\HttpFoundation\Request;
/**
* Defines a form controller for editing configuration translations.
*/
class ConfigTranslationEditForm extends ConfigTranslationFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_translation_edit_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, array &$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->name,
));
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
parent::submitForm($form, $form_state);
drupal_set_message($this->t('Successfully updated @language translation.', array('@language' => $this->language->name)));
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\FormElement\DateFormat.
*/
namespace Drupal\config_translation\FormElement;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Language\Language;
/**
* Defines the date format element for the configuration translation interface.
*/
class DateFormat extends Element {
/**
* {@inheritdoc}
*/
public function getFormElement(array $definition, Language $language, $value) {
if (class_exists('intlDateFormatter')) {
$description = $this->t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://userguide.icu-project.org/formatparse/datetime'));
}
else {
$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' => \Drupal::service('date')->format(REQUEST_TIME, 'custom', $value)));
return array(
'#type' => 'textfield',
'#title' => $this->t($definition['label']) . '<span class="visually-hidden"> (' . $language->name . ')</span>',
'#description' => $description,
'#default_value' => $value,
'#attributes' => array('lang' => $language->id),
'#field_suffix' => ' <div class="edit-date-format-suffix"><small id="edit-date-format-suffix">' . $format . '</small></div>',
'#ajax' => array(
'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
'event' => 'keyup',
'progress' => array('type' => 'throbber', 'message' => NULL),
),
);
}
/**
* Ajax callback to render a sample of the input date format.
*
* @param array $form
* Form API array structure.
* @param array $form_state
* Form state information.
*
* @return AjaxResponse
* Ajax response with the rendered sample date using the given format. If
* the given format cannot be identified or was empty, the response will
* be empty as well.
*/
public static function ajaxSample(array $form, array $form_state) {
$response = new AjaxResponse();
$format_value = NestedArray::getValue($form_state['values'], $form_state['triggering_element']['#array_parents']);
if (!empty($format_value)) {
// Format the date with a custom date format with the given pattern.
// The object is not instantiated in an Ajax context, so $this->t()
// cannot be used here.
$format = t('Displayed as %date_format', array('%date_format' => \Drupal::service('date')->format(REQUEST_TIME, 'custom', $format_value)));
// Return a command instead of a string, since the Ajax framework
// automatically prepends an additional empty DIV element for a string,
// which breaks the layout.
$response->addCommand(new ReplaceCommand('#edit-date-format-suffix', '<small id="edit-date-format-suffix">' . $format . '</small>'));
}
return $response;
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\FormElement\Element.
*/
namespace Drupal\config_translation\FormElement;
/**
* Base class for form elements.
*/
abstract class Element implements ElementInterface {
/**
* The translation manager.
*
* @var \Drupal\Core\StringTranslation\TranslationInterface
*/
protected $translationManager;
/**
* Translates a string to the current language or to a given language.
*
* See the t() documentation for details.
*/
protected function t($string, array $args = array(), array $options = array()) {
return $this->translationManager()->translate($string, $args, $options);
}
/**
* Returns the translation manager.
*
* @return \Drupal\Core\StringTranslation\TranslationInterface
* The translation manager.
*/
protected function translationManager() {
if (!$this->translationManager) {
$this->translationManager = \Drupal::translation();
}
return $this->translationManager;
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\FormElement\ElementInterface.
*/
namespace Drupal\config_translation\FormElement;
use Drupal\Core\Language\Language;
/**
* Provides an interface for configuration translation form elements.
*/
interface ElementInterface {
/**
* Returns the translation form element for a given configuration definition.
*
* @param array $definition
* Configuration schema type definition of the element.
* @param \Drupal\Core\Language\Language $language
* Language object to display the translation form for.
* @param string $value
* Default value for the form element.
*
* @return array
* Form API array to represent the form element.
*/
public function getFormElement(array $definition, Language $language, $value);
}
<?php
/**
* @file
* Contains \Drupal\config_translation\FormElement\Textarea.
*/
namespace Drupal\config_translation\FormElement;
use Drupal\Core\Language\Language;
/**
* Defines the textarea element for the configuration translation interface.
*/
class Textarea extends Element {
/**
* {@inheritdoc}
*/
public function getFormElement(array $definition, Language $language, $value) {
// Estimate a comfortable size of the input textarea.
$rows_words = ceil(str_word_count($value) / 5);
$rows_newlines = substr_count($value, "\n" ) + 1;
$rows = max($rows_words, $rows_newlines);
return array(
'#type' => 'textarea',
'#default_value' => $value,
'#title' => $this->t($definition['label']) . '<span class="visually-hidden"> (' . $language->name . ')</span>',
'#rows' => $rows,
'#attributes' => array('lang' => $language->id),
);
}
}
<?php
/**
* @file
* Contains \Drupal\config_translation\FormElement\Textfield.
*/
namespace Drupal\config_translation\FormElement;
use Drupal\Core\Language\Language;
/**
* Defines the textfield element for the configuration translation interface.
*/
class Textfield extends Element {
/**
* {@inheritdoc}
*/
public function getFormElement(array $definition, Language $language, $value) {
return array(
'#type' => 'textfield',
'#default_value' => $value,
'#title' => $this->t($definition['label']) . '<span class="visually-hidden"> (' . $language->name . ')</span>',
'#attributes' => array('lang' => $language->id),
);
}
}