diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index f26ff20..7b5c359 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -149,8 +149,7 @@ function content_translation_menu() { $items["$path/translations"] = array( 'title' => 'Translate', - 'page callback' => 'content_translation_overview', - 'page arguments' => array($entity_position), + 'route_name' => "content_translation.translation_overview_$entity_type", 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, 'weight' => 2, @@ -169,25 +168,17 @@ function content_translation_menu() { $args = array($entity_position, $language_position, $language_position + 1); $items["$path/translations/add/%language/%language"] = array( 'title' => 'Add', - 'page callback' => 'content_translation_add_page', - 'page arguments' => $args, - 'access callback' => 'content_translation_add_access', - 'access arguments' => $args, - 'type' => MENU_LOCAL_TASK, + 'route_name' => "content_translation.translation_add_$entity_type", 'weight' => 1, - ) + $item; + ); // Edit translation callback. $args = array($entity_position, $language_position); $items["$path/translations/edit/%language"] = array( 'title' => 'Edit', - 'page callback' => 'content_translation_edit_page', - 'page arguments' => $args, - 'access callback' => 'content_translation_edit_access', - 'access arguments' => $args, - 'type' => MENU_LOCAL_TASK, + 'route_name' => "content_translation.translation_edit_$entity_type", 'weight' => 1, - ) + $item; + ); // Delete translation callback. $items["$path/translations/delete/%language"] = array( diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc index a7ce4af..eb80f32 100644 --- a/core/modules/content_translation/content_translation.pages.inc +++ b/core/modules/content_translation/content_translation.pages.inc @@ -14,6 +14,8 @@ * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity whose translation overview should be displayed. + * + * @deprecated Use \Drupal\content_translation\Controller\ContentTranslationController::overview() */ function content_translation_overview(EntityInterface $entity) { $controller = content_translation_controller($entity->entityType()); @@ -188,6 +190,8 @@ function _content_translation_get_switch_links($path) { * * @return array * A processed form array ready to be rendered. + * + * @deprecated Use \Drupal\content_translation\Controller\ContentTranslationController::add() */ function content_translation_add_page(EntityInterface $entity, Language $source = NULL, Language $target = NULL) { $source = !empty($source) ? $source : $entity->language(); @@ -215,6 +219,8 @@ function content_translation_add_page(EntityInterface $entity, Language $source * * @return array * A processed form array ready to be rendered. + * + * @deprecated Use \Drupal\content_translation\Controller\ContentTranslationController::edit() */ function content_translation_edit_page(EntityInterface $entity, Language $language = NULL) { $language = !empty($language) ? $language : language(Language::TYPE_CONTENT); diff --git a/core/modules/content_translation/content_translation.services.yml b/core/modules/content_translation/content_translation.services.yml index 678724d..e81c13d 100644 --- a/core/modules/content_translation/content_translation.services.yml +++ b/core/modules/content_translation/content_translation.services.yml @@ -2,3 +2,21 @@ services: content_translation.synchronizer: class: Drupal\content_translation\FieldTranslationSynchronizer arguments: ['@entity.manager'] + + content_translation.subscriber: + class: Drupal\content_translation\Routing\ContentTranslationRouteSubscriber + arguments: ['@plugin.manager.entity'] + tags: + - { name: event_subscriber } + + content_translation.overview_access: + class: Drupal\content_translation\Access\ContentTranslationOverviewAccess + arguments: ['@plugin.manager.entity'] + tags: + - { name: access_check } + + content_translation.manage_access: + class: Drupal\content_translation\Access\ContentTranslationManageAccessCheck + arguments: ['@plugin.manager.entity'] + tags: + - { name: access_check } diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php new file mode 100644 index 0000000..21eeea8 --- /dev/null +++ b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php @@ -0,0 +1,85 @@ +entityManager = $manager; + } + + /** + * {@inheritdoc} + */ + public function appliesTo() { + return array('_access_content_translation_manage'); + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, Request $request) { + if ($entity = $request->attributes->get('entity')) { + $route_requirements = $route->getRequirements(); + $operation = $route_requirements['_access_content_translation_manage']; + $entity_type = $entity->entityType(); + $controller_class = $this->entityManager->getControllerClass($entity_type, 'translation'); + $controller = new $controller_class($entity_type, $entity->entityInfo()); + + // Load translation. + $translations = $entity->getTranslationLanguages(); + $languages = language_list(); + + if ($operation == 'create') { + $source = language_load($request->attributes->get('source')); + $target = language_load($request->attributes->get('target')); + $source = !empty($source) ? $source : $entity->language(); + $target = !empty($target) ? $target : language(Language::TYPE_CONTENT); + return ($source->id != $target->id + && isset($languages[$source->id]) + && isset($languages[$target->id]) + && !isset($translations[$target->id]) + && $controller->getTranslationAccess($entity, $operation)) + ? static::ALLOW : static::DENY; + } + elseif ($operation == 'update') { + $language = language_load($request->attributes->get('language')); + $language = !empty($language) ? $language : language(Language::TYPE_CONTENT); + return isset($languages[$language->id]) + && $language->id != $entity->getUntranslated()->language()->id + && isset($translations[$language->id]) + && $controller->getTranslationAccess($entity, $operation) + ? static::ALLOW : static::DENY; + } + } + return static::DENY; + } + +} diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php new file mode 100644 index 0000000..116e9c5 --- /dev/null +++ b/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationOverviewAccess.php @@ -0,0 +1,75 @@ +entityManager = $manager; + } + + /** + * {@inheritdoc} + */ + public function appliesTo() { + return array('_access_content_translation_overview'); + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, Request $request) { + if ($entity = $request->attributes->get('entity')) { + // Get entity base info. + $entity_type = $entity->entityType(); + $bundle = $entity->bundle(); + + // Get account details from request. + $account = \Drupal::currentUser(); + + // Get entity access callback. + $definitions = $this->entityManager->getDefinitions(); + $access_callback = $definitions[$entity_type]['translation']['content_translation']['access_callback']; + if (call_user_func($access_callback, $entity)) { + return static::ALLOW; + } + + // Check per entity permission. + $permission = "translate {$entity_type}"; + if ($definitions[$entity_type]['permission_granularity'] == 'bundle') { + $permission = "translate {$bundle} {$entity_type}"; + } + if ($account->hasPermission($permission)) { + return static::ALLOW; + } + } + + return static::DENY; + } +} diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php new file mode 100644 index 0000000..ec229e1 --- /dev/null +++ b/core/modules/content_translation/lib/Drupal/content_translation/Controller/ContentTranslationController.php @@ -0,0 +1,44 @@ +entityManager = $entityManager; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[RoutingEvents::DYNAMIC] = 'routes'; + return $events; + } + + /** + * Adds routes for entity translations. + */ + public function routes(RouteBuildEvent $event) { + $collection = $event->getRouteCollection(); + foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) { + if ($entity_info['translatable'] && isset($entity_info['translation'])) { + $route = new Route( + '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations", + array( + '_content' => '\Drupal\content_translation\Controller\ContentTranslationController::overview', + '_title' => 'Translate', + 'account' => 'NULL', + ), + array( + '_access_content_translation_overview' => $entity_type, + '_permission' => 'translate any entity', + ), + array( + 'parameters' => array( + 'entity' => array( + 'type' => 'entity:' . $entity_type, + ), + ), + ) + ); + $collection->add("content_translation.translation_overview_$entity_type", $route); + + $route = new Route( + '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations/add/{source}/{target}", + array( + '_content' => '\Drupal\content_translation\Controller\ContentTranslationController::add', + 'source' => NULL, + 'target' => NULL, + '_title' => 'Add', + + ), + array( + '_permission' => 'translate any entity', + '_access_content_translation_manage' => 'create', + ), + array( + 'parameters' => array( + 'entity' => array( + 'type' => 'entity:' . $entity_type, + ), + ), + ) + ); + $collection->add("content_translation.translation_add_$entity_type", $route); + + $route = new Route( + '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations/edit/{language}", + array( + '_content' => '\Drupal\content_translation\Controller\ContentTranslationController::edit', + 'language' => NULL, + '_title' => 'Edit', + ), + array( + '_permission' => 'translate any entity', + '_access_content_translation_manage' => 'update', + ), + array( + 'parameters' => array( + 'entity' => array( + 'type' => 'entity:' . $entity_type, + ), + ), + ) + ); + $collection->add("content_translation.translation_edit_$entity_type", $route); + } + } + } + +} diff --git a/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php index aca491d..c44e1f6 100644 --- a/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php +++ b/core/modules/node/lib/Drupal/node/Access/NodeRevisionAccessCheck.php @@ -73,8 +73,18 @@ public function applies(Route $route) { * {@inheritdoc} */ public function access(Route $route, Request $request) { - $revision = $this->nodeStorage->loadRevision($request->attributes->get('node_revision')); - return $this->checkAccess($revision, $route->getRequirement('_access_node_revision')) ? static::ALLOW : static::DENY; + // If the route has a {node_revision} placeholder, load the node for that + // revision. Otherwise, try to use a {node} placeholder. + if ($request->attributes->has('node_revision')) { + $node = $this->nodeStorage->loadRevision($request->attributes->get('node_revision')); + } + elseif ($request->attributes->has('node')) { + $node = $request->attributes->get('node'); + } + else { + return static::DENY; + } + return $this->checkAccess($node, $route->getRequirement('_access_node_revision')) ? static::ALLOW : static::DENY; } /** diff --git a/core/modules/translation/lib/Drupal/translation/Access/TranslationNodeOverviewAccessCheck.php b/core/modules/translation/lib/Drupal/translation/Access/TranslationNodeOverviewAccessCheck.php new file mode 100644 index 0000000..3b54fe1 --- /dev/null +++ b/core/modules/translation/lib/Drupal/translation/Access/TranslationNodeOverviewAccessCheck.php @@ -0,0 +1,38 @@ +getRequirement('_access_translation_tab'); + if ($request->attributes->has($key)) { + // @todo Remove _translation_tab_access(). + return _translation_tab_access($request->attributes->get($key)) ? static::ALLOW : static::DENY; + } + return static::DENY; + } + +} diff --git a/core/modules/translation/lib/Drupal/translation/Controller/TranslationController.php b/core/modules/translation/lib/Drupal/translation/Controller/TranslationController.php new file mode 100644 index 0000000..68911ec --- /dev/null +++ b/core/modules/translation/lib/Drupal/translation/Controller/TranslationController.php @@ -0,0 +1,25 @@ + 'Translate', - 'page callback' => 'translation_node_overview', - 'page arguments' => array(1), - 'access callback' => '_translation_tab_access', - 'access arguments' => array(1), + 'route_name' => 'translation.node_overview', 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, 'weight' => 2, - 'file' => 'translation.pages.inc', ); return $items; } diff --git a/core/modules/translation/translation.pages.inc b/core/modules/translation/translation.pages.inc index 4fff31d..a1f6360 100644 --- a/core/modules/translation/translation.pages.inc +++ b/core/modules/translation/translation.pages.inc @@ -18,6 +18,8 @@ * A render array for a page containing a list of content. * * @see translation_menu() + * + * @deprecated Use \Drupal\translation\Controller\TranslationController::nodeOverview() */ function translation_node_overview(EntityInterface $node) { include_once DRUPAL_ROOT . '/core/includes/language.inc'; @@ -91,7 +93,7 @@ function translation_node_overview(EntityInterface $node) { $rows[] = $row; } - drupal_set_title(t('Translations of %title', array('%title' => $node->label())), PASS_THROUGH); + $build['#title'] = t('Translations of %title', array('%title' => $node->label())); $build['translation_node_overview'] = array( '#theme' => 'table', diff --git a/core/modules/translation/translation.routing.yml b/core/modules/translation/translation.routing.yml new file mode 100644 index 0000000..7129075 --- /dev/null +++ b/core/modules/translation/translation.routing.yml @@ -0,0 +1,7 @@ +translation.node_overview: + path: '/node/{node}/translate' + defaults: + _title: 'Translate' + _content: '\Drupal\translation\Controller\TranslationController::nodeOverview' + requirements: + _access_translation_tab: 'node' diff --git a/core/modules/translation/translation.services.yml b/core/modules/translation/translation.services.yml new file mode 100644 index 0000000..e6520d5 --- /dev/null +++ b/core/modules/translation/translation.services.yml @@ -0,0 +1,5 @@ +services: + access_check.translation.node_overview: + class: Drupal\translation\Access\TranslationNodeOverviewAccessCheck + tags: + - { name: access_check }