Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -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'));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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'));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}