Commit 7b8e2041 authored by alexpott's avatar alexpott

Issue #2133469 by tim.plunkett, damiankloip, dawehner: Replace path-based...

Issue #2133469 by tim.plunkett, damiankloip, dawehner: Replace path-based entity links with route names.
parent c4b089f5
......@@ -267,9 +267,7 @@ class EntityType extends Plugin {
*
* @var array
*/
public $links = array(
'canonical' => '/entity/{entityType}/{id}',
);
public $links = array();
/**
* Specifies whether a module exposing permissions for the current entity type
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
/**
* Defines a base entity class.
......@@ -36,6 +37,13 @@ abstract class Entity implements EntityInterface {
*/
protected $enforceIsNew;
/**
* The route provider service.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* Constructs an Entity object.
*
......@@ -144,10 +152,18 @@ public function uri($rel = 'canonical') {
// The links array might contain URI templates set in annotations.
$link_templates = isset($entity_info['links']) ? $entity_info['links'] : array();
$template = NULL;
if (isset($link_templates[$rel])) {
try {
$template = $this->routeProvider()->getRouteByName($link_templates[$rel])->getPath();
}
catch (RouteNotFoundException $e) {
// Fall back to a non-template-based URI.
}
}
if ($template) {
// If there is a template for the given relationship type, do the
// placeholder replacement and use that as the path.
$template = $link_templates[$rel];
$replacements = $this->uriPlaceholderReplacements();
$uri['path'] = str_replace(array_keys($replacements), array_values($replacements), $template);
......@@ -369,4 +385,17 @@ public function changed() {
}
}
/**
* Wraps the route provider service.
*
* @return \Drupal\Core\Routing\RouteProviderInterface
* The route provider.
*/
protected function routeProvider() {
if (!$this->routeProvider) {
$this->routeProvider = \Drupal::service('router.route_provider');
}
return $this->routeProvider;
}
}
......@@ -64,5 +64,5 @@ function action_entity_info(&$entity_info) {
$entity_info['action']['controllers']['form']['edit'] = 'Drupal\action\ActionEditFormController';
$entity_info['action']['controllers']['form']['delete'] = 'Drupal\action\Form\ActionDeleteForm';
$entity_info['action']['controllers']['list'] = 'Drupal\action\ActionListController';
$entity_info['action']['links']['edit-form'] = 'admin/config/system/actions/configure/{action}';
$entity_info['action']['links']['edit-form'] = 'action.admin_configure';
}
......@@ -38,8 +38,8 @@
* revision_table = "custom_block_revision",
* route_base_path = "admin/structure/block/custom-blocks/manage/{bundle}",
* links = {
* "canonical" = "/block/{custom_block}",
* "edit-form" = "/block/{custom_block}"
* "canonical" = "custom_block.edit",
* "edit-form" = "custom_block.edit"
* },
* fieldable = TRUE,
* translatable = TRUE,
......
......@@ -38,7 +38,7 @@
* "uuid" = "uuid"
* },
* links = {
* "edit-form" = "admin/structure/block/custom-blocks/manage/{custom_block_type}"
* "edit-form" = "custom_block.type_edit"
* }
* )
*/
......
......@@ -38,7 +38,7 @@
* "uuid" = "uuid"
* },
* links = {
* "edit-form" = "admin/structure/block/manage/{block}"
* "edit-form" = "block.admin_edit"
* }
* )
*/
......
......@@ -47,8 +47,8 @@
* "bundle" = "field_id"
* },
* links = {
* "canonical" = "/comment/{comment}",
* "edit-form" = "/comment/{comment}/edit"
* "canonical" = "comment.permalink",
* "edit-form" = "comment.edit_page"
* }
* )
*/
......
......@@ -76,7 +76,7 @@ function testList() {
$actual_operations = $controller->getOperations($entity);
// Sort the operations to normalize link order.
uasort($actual_operations, 'drupal_sort_weight');
$this->assertIdentical($expected_operations, $actual_operations);
$this->assertIdentical($expected_operations, $actual_operations, 'The operations are identical.');
// Test buildHeader() method.
$expected_items = array(
......@@ -149,7 +149,7 @@ function testList() {
$actual_operations = $controller->getOperations($entity);
// Sort the operations to normalize link order.
uasort($actual_operations, 'drupal_sort_weight');
$this->assertIdentical($expected_operations, $actual_operations);
$this->assertIdentical($expected_operations, $actual_operations, 'The operations are identical.');
}
/**
......
......@@ -28,9 +28,6 @@
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "edit-form" = "admin/structure/config_test/manage/{config_query_test}"
* }
* )
*
......
......@@ -35,7 +35,7 @@
* "status" = "status"
* },
* links = {
* "edit-form" = "admin/structure/config_test/manage/{config_test}"
* "edit-form" = "config_test.entity"
* }
* )
*/
......
......@@ -37,7 +37,7 @@
* "uuid" = "uuid"
* },
* links = {
* "edit-form" = "admin/structure/contact/manage/{contact_category}"
* "edit-form" = "contact.category_edit"
* }
* )
*/
......
......@@ -90,19 +90,13 @@ function content_translation_entity_info_alter(array &$entity_info) {
}
if (!empty($info['links']['canonical'])) {
// Provide default links for the translation paths.
// Provide default route names for the translation paths.
$info['links'] += array(
'drupal:content-translation-overview' => $info['links']['canonical'] . '/translations',
'drupal:content-translation-overview' => "content_translation.translation_overview_$entity_type",
);
$info['translation']['content_translation'] += array(
'access_callback' => 'content_translation_translate_access',
);
$parts = explode('/', trim($info['links']['canonical'], '/'));
$entity_position = array_search("{{$entity_type}}", $parts);
if ($entity_position !== FALSE) {
$info['translation']['content_translation'] += array(
'access_callback' => 'content_translation_translate_access',
'access_arguments' => array($entity_position),
);
}
}
}
}
......@@ -162,7 +156,7 @@ function content_translation_menu() {
if (content_translation_enabled($entity_type)) {
$path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']);
$entity_position = count(explode('/', $path)) - 1;
$keys = array_flip(array('theme_callback', 'theme_arguments', 'access_callback', 'access_arguments', 'load_arguments'));
$keys = array_flip(array('theme_callback', 'theme_arguments', 'load_arguments'));
$menu_info = array_intersect_key($info['translation']['content_translation'], $keys) + array('file' => 'content_translation.pages.inc');
$item = array();
......@@ -174,7 +168,7 @@ function content_translation_menu() {
$items["$path/translations"] = array(
'title' => 'Translate',
'route_name' => "content_translation.translation_overview_$entity_type",
'route_name' => $info['links']['drupal:content-translation-overview'],
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE,
'weight' => 2,
......@@ -496,11 +490,13 @@ function content_translation_set_config($entity_type, $bundle, $setting, $value)
* @returns
* TRUE if the specified bundle is translatable. If no bundle is provided
* returns TRUE if at least one of the entity bundles is translatable.
*
* @todo Move to \Drupal\content_translation\ContentTranslationManager.
*/
function content_translation_enabled($entity_type, $bundle = NULL) {
$enabled = FALSE;
if (content_translation_supported($entity_type)) {
if (\Drupal::service('content_translation.manager')->isSupported($entity_type)) {
$bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
foreach ($bundles as $bundle) {
if (content_translation_get_config($entity_type, $bundle, 'enabled')) {
......@@ -513,20 +509,6 @@ function content_translation_enabled($entity_type, $bundle = NULL) {
return $enabled;
}
/**
* Checks whether an entity type supports translation.
*
* @param string $entity_type
* The entity type.
*
* @return bool
* TRUE if an entity type is supported, FALSE otherwise.
*/
function content_translation_supported($entity_type) {
$info = entity_get_info($entity_type);
return !empty($info['translatable']) && !empty($info['links']['drupal:content-translation-overview']);
}
/**
* Content translation controller factory.
*
......@@ -535,6 +517,8 @@ function content_translation_supported($entity_type) {
*
* @return \Drupal\content_translation\ContentTranslationControllerInterface
* An instance of the content translation controller interface.
*
* @todo Move to \Drupal\content_translation\ContentTranslationManager.
*/
function content_translation_controller($entity_type) {
$entity_info = entity_get_info($entity_type);
......@@ -551,6 +535,8 @@ function content_translation_controller($entity_type) {
* @return \Drupal\Core\Entity\EntityFormControllerInterface;
* An instance of the content translation form interface or FALSE if not an
* entity form.
*
* @todo Move to \Drupal\content_translation\ContentTranslationManager.
*/
function content_translation_form_controller(array $form_state) {
return isset($form_state['controller']) && $form_state['controller'] instanceof EntityFormControllerInterface ? $form_state['controller'] : FALSE;
......@@ -570,6 +556,8 @@ function content_translation_form_controller(array $form_state) {
*
* @return
* TRUE if the current user is allowed to view the translation.
*
* @todo Move to \Drupal\content_translation\ContentTranslationManager.
*/
function content_translation_access(EntityInterface $entity, $op) {
return content_translation_controller($entity->entityType())->getTranslationAccess($entity, $op) ;
......
......@@ -5,7 +5,7 @@ services:
content_translation.subscriber:
class: Drupal\content_translation\Routing\ContentTranslationRouteSubscriber
arguments: ['@plugin.manager.entity']
arguments: ['@content_translation.manager', '@router.route_provider']
tags:
- { name: event_subscriber }
......@@ -20,3 +20,7 @@ services:
arguments: ['@plugin.manager.entity']
tags:
- { name: access_check }
content_translation.manager:
class: Drupal\content_translation\ContentTranslationManager
arguments: ['@entity.manager']
<?php
/**
* @file
* Contains \Drupal\content_translation\ContentTranslationManager.
*/
namespace Drupal\content_translation;
use Drupal\Core\Entity\EntityManagerInterface;
/**
* Provides common functionality for content translation.
*/
class ContentTranslationManager implements ContentTranslationManagerInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs a ContentTranslationManageAccessCheck object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
* The entity type manager.
*/
public function __construct(EntityManagerInterface $manager) {
$this->entityManager = $manager;
}
/**
* {@inheritdoc}
*/
public function isSupported($entity_type) {
$info = $this->entityManager->getDefinition($entity_type);
return !empty($info['translatable']) && !empty($info['links']['drupal:content-translation-overview']);
}
/**
* {@inheritdoc}
*/
public function getSupportedEntityTypes() {
$supported_types = array();
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
if ($this->isSupported($entity_type)) {
$supported_types[$entity_type] = $entity_info;
}
}
return $supported_types;
}
}
<?php
/**
* @file
* Contains \Drupal\content_translation\ContentTranslationManagerInterface.
*/
namespace Drupal\content_translation;
/**
* Provides an interface for common functionality for content translation.
*/
interface ContentTranslationManagerInterface {
/**
* Gets the entity types that support content translation.
*
* @return array
* An array of entity types that support content translation.
*/
public function getSupportedEntityTypes();
/**
* Checks whether an entity type supports translation.
*
* @param string $entity_type
* The entity type.
*
* @return bool
* TRUE if an entity type is supported, FALSE otherwise.
*/
public function isSupported($entity_type);
}
......@@ -54,7 +54,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
if ($entity_info['translatable'] && isset($entity_info['translation'])) {
$this->derivatives[$entity_type]['title'] = t('Translate');
$this->derivatives[$entity_type]['route_name'] = "content_translation.translation_overview_$entity_type";
$this->derivatives[$entity_type]['route_name'] = $entity_info['links']['drupal:content-translation-overview'];
$this->derivatives[$entity_type]['group'] = $entity_type;
}
}
......
......@@ -8,9 +8,8 @@
namespace Drupal\content_translation\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DerivativeBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\content_translation\ContentTranslationManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -19,40 +18,30 @@
class ContentTranslationLocalTasks extends DerivativeBase implements ContainerDerivativeInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The route provider.
* The base plugin ID
*
* @var \Drupal\Core\Routing\RouteProviderInterface
* @var string
*/
protected $routeProvider;
protected $basePluginId;
/**
* The base plugin ID
* The content translation manager.
*
* @var string
* @var \Drupal\content_translation\ContentTranslationManagerInterface
*/
protected $basePluginId;
protected $contentTranslationManager;
/**
* Constructs a new ContentTranslationLocalTasks.
*
* @param string $base_plugin_id
* The base plugin ID.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
* @param \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager
* The content translation manager.
*/
public function __construct($base_plugin_id, EntityManagerInterface $entity_manager, RouteProviderInterface $route_provider) {
$this->entityManager = $entity_manager;
$this->routeProvider = $route_provider;
public function __construct($base_plugin_id, ContentTranslationManagerInterface $content_translation_manager) {
$this->basePluginId = $base_plugin_id;
$this->contentTranslationManager = $content_translation_manager;
}
/**
......@@ -61,8 +50,7 @@ public function __construct($base_plugin_id, EntityManagerInterface $entity_mana
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$base_plugin_id,
$container->get('entity.manager'),
$container->get('router.route_provider')
$container->get('content_translation.manager')
);
}
......@@ -71,18 +59,15 @@ public static function create(ContainerInterface $container, $base_plugin_id) {
*/
public function getDerivativeDefinitions(array $base_plugin_definition) {
// Create tabs for all possible entity types.
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
if (!empty($entity_info['translatable'])) {
// Find the route name for the translation overview.
$translation_route_name = "content_translation.translation_overview_$entity_type";
$translation_tab = $translation_route_name;
$this->derivatives[$translation_tab] = $base_plugin_definition + array(
'entity_type' => $entity_type,
);
$this->derivatives[$translation_tab]['title'] = 'Translate';
$this->derivatives[$translation_tab]['route_name'] = $translation_route_name;
}
foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type => $entity_info) {
// Find the route name for the translation overview.
$translation_route_name = $entity_info['links']['drupal:content-translation-overview'];
$this->derivatives[$translation_route_name] = array(
'entity_type' => $entity_type,
'title' => 'Translate',
'route_name' => $translation_route_name,
) + $base_plugin_definition;
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
......@@ -91,20 +76,15 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
* Alters the local tasks to find the proper tab_root_id for each task.
*/
public function alterLocalTasks(array &$local_tasks) {
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
if (!empty($entity_info['translatable']) && !empty($entity_info['links']['canonical'])) {
$path = $entity_info['links']['canonical'];
if ($routes = $this->routeProvider->getRoutesByPattern($path)->all()) {
// Find the route name for the entity page.
$entity_route_name = key($routes);
// Find the route name for the translation overview.
$translation_route_name = "content_translation.translation_overview_$entity_type";
$translation_tab = $this->basePluginId . ':' . $translation_route_name;
$local_tasks[$translation_tab]['tab_root_id'] = $this->getTaskFromRoute($entity_route_name, $local_tasks);
}
}
foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_info) {
// Find the route name for the entity page.
$entity_route_name = $entity_info['links']['canonical'];
// Find the route name for the translation overview.
$translation_route_name = $entity_info['links']['drupal:content-translation-overview'];
$translation_tab = $this->basePluginId . ':' . $translation_route_name;
$local_tasks[$translation_tab]['tab_root_id'] = $this->getTaskFromRoute($entity_route_name, $local_tasks);
}
}
......@@ -131,14 +111,4 @@ protected function getTaskFromRoute($route_name, &$local_tasks) {
return $parent_local_task;
}
/**
* Translates a string to the current language or to a given language.
*
* See the t() documentation for details.
*
* @todo Move to derivative base. https://drupal.org/node/2112575
*/
public function t($string, array $args = array(), array $options = array()) {
\Drupal::translation()->translate($string, $args, $options);
}
}
......@@ -7,10 +7,13 @@
namespace Drupal\content_translation\Routing;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\content_translation\ContentTranslationManagerInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
/**
* Subscriber for entity translation routes.
......@@ -18,125 +21,154 @@
class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
/**
* The entity type manager.
* The content translation manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\content_translation\ContentTranslationManagerInterface
*/
protected $entityManager;
protected $contentTranslationManager;
/**
* The route provider.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected $routeProvider;
/**
* Constructs a ContentTranslationRouteSubscriber object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
* The entity type manager.
* @param \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager
* The content translation manager.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider.
*/
public function __construct(EntityManagerInterface $entityManager) {
$this->entityManager = $entityManager;
public function __construct(ContentTranslationManagerInterface $content_translation_manager, RouteProviderInterface $route_provider) {
$this->contentTranslationManager = $content_translation_manager;
$this->routeProvider = $route_provider;
}
/**
* {@inheritdoc}
*/
protected function routes(RouteCollection $collection) {
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
if (!empty($entity_info['translatable']) && !empty($entity_info['links']['drupal:content-translation-overview'])) {
$path = $entity_info['links']['drupal:content-translation-overview'];
foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type => $entity_info) {
// First try to get the route from the dynamic_routes collection.
if (!$entity_route = $collection->get($entity_info['links']['canonical'])) {
// Then try to get the route from the route provider itself, checking
// all previous collections.
try {
$entity_route = $this->routeProvider->getRouteByName($entity_info['links']['canonical']);
}
// If the route was not found, skip this entity type.
catch (RouteNotFoundException $e) {
continue;
}
}
$path = $entity_route->getPath() . '/translations';
$route = new Route(
$path,
array(
'_content' => '\Drupal\content_translation\Controller\ContentTranslationController::overview',
'_title' => 'Translate',
'account' => 'NULL',
'_entity_type' => $entity_type,
),
array(
'_access_content_translation_overview' => $entity_type,
'_permission' => 'translate any entity',
),
array(
'_access_mode' => 'ANY',
'parameters' => array(
'entity' => array(
'type' => 'entity:' . $entity_type,
),
$route = new Route(
$path,
array(
'_content' => '\Drupal\content_translation\Controller\ContentTranslationController::overview',
'_title' => 'Translate',
'account' => 'NULL',
'_entity_type' => $entity_type,
),
array(
'_access_content_translation_overview' => $entity_type,
'_permission' => 'translate any entity',
),
array(
'_access_mode' => 'ANY',
'parameters' => array(
'entity' => array(
'type' => 'entity:' . $entity_type,
),
)
);
$collection->add("content_translation.translation_overview_$entity_type", $route);
),
)
);
$collection->add($entity_info['links']['drupal:content-translation-overview'], $route);
$route = new Route(
$path . '/add/{source}/{target}',
array(
'_content' => '\Drupal\content_translation\Controller\ContentTranslationController::add',
'source' => NULL,
'target' => NULL,
'_title' => 'Add',
'_entity_type' => $entity_type,
$route = new Route(
$path . '/add/{source}/{target}',
array(
'_content' => '\Drupal\content_translation\Controller\ContentTranslationController::add',
'source' => NULL,
'target' => NULL,
'_title' => 'Add',
'_entity_type' => $entity_type,
),
array(
'_permission' => 'translate any entity',