diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php index 604bf5d..3dbbb39 100644 --- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php +++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php @@ -225,16 +225,11 @@ class EntityType extends Plugin { public $bundle_keys; /** - * The base router path for the entity type's field administration page. - * - * If the entity type has a bundle, include {bundle} in the path. - * - * For example, the node entity type specifies - * "admin/structure/types/manage/{bundle}" as its base field admin path. + * The name of the entity type which provides bundles. * * @var string (optional) */ - public $route_base_path; + public $bundle_entity_type = 'bundle'; /** * Link templates using the URI template syntax. diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 5fc2d16..221d4ee 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -301,9 +301,9 @@ public function getAdminPath($entity_type, $bundle) { $admin_path = ''; $entity_info = $this->getDefinition($entity_type); // Check for an entity type's admin base path. - if (isset($entity_info['route_base_path'])) { - // Replace any dynamic 'bundle' portion of the path with the actual bundle. - $admin_path = str_replace('{bundle}', $bundle, $entity_info['route_base_path']); + if (isset($entity_info['links']['admin-form'])) { + $route_parameters[$entity_info['bundle_entity_type']] = $bundle; + $admin_path = \Drupal::urlGenerator()->getPathFromRoute($entity_info['links']['admin-form'], $route_parameters); } return $admin_path; @@ -313,10 +313,11 @@ public function getAdminPath($entity_type, $bundle) { * {@inheritdoc} */ public function getAdminRouteInfo($entity_type, $bundle) { + $entity_info = $this->getDefinition($entity_type); return array( 'route_name' => "field_ui.overview_$entity_type", 'route_parameters' => array( - 'bundle' => $bundle, + $entity_info['bundle_entity_type'] => $bundle, ) ); } diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php index d561f53..e89966a 100644 --- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php @@ -164,6 +164,11 @@ public function getViewBuilder($entity_type); * * @return string * The administration path for an entity type bundle, if it exists. + * + * @deprecated since version 8.0 + * System paths should not be used - use route names and parameters. + * + * @see self::getAdminRouteInfo() */ public function getAdminPath($entity_type, $bundle); diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php index a83352b..055bfea 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php @@ -36,10 +36,10 @@ * admin_permission = "administer blocks", * base_table = "custom_block", * revision_table = "custom_block_revision", - * route_base_path = "admin/structure/block/custom-blocks/manage/{bundle}", * links = { * "canonical" = "custom_block.edit", - * "edit-form" = "custom_block.edit" + * "edit-form" = "custom_block.edit", + * "admin-form" = "custom_block.type_edit" * }, * fieldable = TRUE, * translatable = TRUE, @@ -52,7 +52,8 @@ * }, * bundle_keys = { * "bundle" = "type" - * } + * }, + * bundle_entity_type = "custom_block_type" * ) */ class CustomBlock extends ContentEntityBase implements CustomBlockInterface { diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 4d22fbf..8c5b999 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -36,7 +36,6 @@ * fieldable = TRUE, * translatable = TRUE, * render_cache = FALSE, - * route_base_path = "admin/structure/comments/manage/{bundle}", * entity_keys = { * "id" = "cid", * "bundle" = "field_id", @@ -48,7 +47,8 @@ * }, * links = { * "canonical" = "comment.permalink", - * "edit-form" = "comment.edit_page" + * "edit-form" = "comment.edit_page", + * "admin-form" = "comment.bundle" * } * ) */ diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php index 80e4855..f7cfd76 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php @@ -229,11 +229,11 @@ function testCommentFunctionality() { )); $this->drupalLogin($limited_user); // Test that default field exists. - $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields'); + $this->drupalGet('entity_test/structure/entity_test/fields'); $this->assertText(t('Comment settings')); - $this->assertLinkByHref('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment'); + $this->assertLinkByHref('entity_test/structure/entity_test/fields/entity_test.entity_test.comment'); // Test widget hidden option is not visible when there's no comments. - $this->drupalGet('admin/structure/entity-test/manage/entity_test/entity-test/fields/entity_test.entity_test.comment'); + $this->drupalGet('entity_test/structure/entity_test/entity-test/fields/entity_test.entity_test.comment'); $this->assertNoField('edit-default-value-input-comment-und-0-status-0'); $this->drupalLogin($this->admin_user); @@ -343,20 +343,20 @@ function testCommentFunctionality() { 'administer entity_test content', )); $this->drupalLogin($limited_user); - $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment'); + $this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment'); $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0'); $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-1'); $this->assertFieldChecked('edit-default-value-input-comment-0-status-2'); // Test comment option change in field settings. $edit = array('default_value_input[comment][0][status]' => COMMENT_CLOSED); $this->drupalPostForm(NULL, $edit, t('Save settings')); - $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields/entity_test.entity_test.comment'); + $this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.comment'); $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0'); $this->assertFieldChecked('edit-default-value-input-comment-0-status-1'); $this->assertNoFieldChecked('edit-default-value-input-comment-0-status-2'); // Add a new comment field. - $this->drupalGet('admin/structure/entity-test/manage/entity_test/fields'); + $this->drupalGet('entity_test/structure/entity_test/fields'); $edit = array( 'fields[_add_new_field][label]' => 'Foobar', 'fields[_add_new_field][field_name]' => 'foobar', diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php index ce4f1be..be497e4 100644 --- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php +++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigFieldInstanceMapper.php @@ -23,13 +23,10 @@ class ConfigFieldInstanceMapper extends ConfigEntityMapper { */ public function getBaseRouteParameters() { $parameters = parent::getBaseRouteParameters(); - // @todo All core content entity path placeholders can be fully filled in - // with an additional {bundle} value in their paths, but a more - // predictable solution would be ideal. See - // https://drupal.org/node/2134871 + $base_entity_info = $this->entityManager->getDefinition($this->pluginDefinition['base_entity_type']); // @todo Field instances have no method to return the bundle the instance is // attached to. See https://drupal.org/node/2134861 - $parameters['bundle'] = $this->entity->bundle; + $parameters[$base_entity_info['bundle_entity_type']] = $this->entity->bundle; return $parameters; } diff --git a/core/modules/contact/contact.local_tasks.yml b/core/modules/contact/contact.local_tasks.yml new file mode 100644 index 0000000..91bb65d --- /dev/null +++ b/core/modules/contact/contact.local_tasks.yml @@ -0,0 +1,4 @@ +contact.category_edit: + title: 'Edit' + route_name: contact.category_edit + tab_root_id: contact.category_edit diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module index 66a80ec..58e343e 100644 --- a/core/modules/contact/contact.module +++ b/core/modules/contact/contact.module @@ -67,10 +67,6 @@ function contact_menu() { 'title' => 'Edit contact category', 'route_name' => 'contact.category_edit', ); - $items['admin/structure/contact/manage/%contact_category/edit'] = array( - 'title' => 'Edit', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); $items['contact'] = array( 'title' => 'Contact', diff --git a/core/modules/contact/lib/Drupal/contact/Entity/Message.php b/core/modules/contact/lib/Drupal/contact/Entity/Message.php index 9e424fa..aaaa64e 100644 --- a/core/modules/contact/lib/Drupal/contact/Entity/Message.php +++ b/core/modules/contact/lib/Drupal/contact/Entity/Message.php @@ -26,10 +26,13 @@ * entity_keys = { * "bundle" = "category" * }, - * route_base_path = "admin/structure/contact/manage/{bundle}", + * bundle_entity_type = "contact_category", * fieldable = TRUE, * bundle_keys = { * "bundle" = "id" + * }, + * links = { + * "admin-form" = "contact.category_edit" * } * ) */ diff --git a/core/modules/field_ui/field_ui.local_tasks.yml b/core/modules/field_ui/field_ui.local_tasks.yml index e690491..1330c2d 100644 --- a/core/modules/field_ui/field_ui.local_tasks.yml +++ b/core/modules/field_ui/field_ui.local_tasks.yml @@ -2,3 +2,6 @@ field_ui.list: title: Entities route_name: field_ui.list tab_root_id: field_ui.list +field_ui.fields: + class: \Drupal\Core\Menu\LocalTaskDefault + derivative: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index f9d90c6..eefc199 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\entity\EntityViewModeInterface; +use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask; /** * Implements hook_help(). @@ -61,94 +62,6 @@ function field_ui_menu() { 'type' => MENU_NORMAL_ITEM, ); - // Create tabs for all possible bundles. - foreach (entity_get_info() as $entity_type => $entity_info) { - if ($entity_info['fieldable'] && isset($entity_info['route_base_path'])) { - // Extract path information from the entity type. - $path = $entity_info['route_base_path']; - $default_path = preg_replace('/{' . DRUPAL_PHP_FUNCTION_PATTERN . '}/', '%', $path); - // This is the position of the %field_ui_instance placeholder in the - // items below. - $field_position = count(explode('/', $path)) + 1; - - $items["$path/fields"] = array( - 'title' => 'Manage fields', - 'type' => MENU_LOCAL_TASK, - 'route_name' => "field_ui.overview_$entity_type", - 'weight' => 1, - ); - $items["$path/fields/%"] = array( - 'title callback' => 'entity_page_label', - 'title arguments' => array($field_position), - 'route_name' => "field_ui.instance_edit_$entity_type", - ); - $items["$default_path/fields/%/edit"] = array( - 'title' => 'Edit', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items["$path/fields/%/field"] = array( - 'title' => 'Field settings', - 'type' => MENU_LOCAL_TASK, - 'route_name' => "field_ui.field_edit_$entity_type", - ); - $items["$path/fields/%/delete"] = array( - 'title' => 'Delete', - 'type' => MENU_VISIBLE_IN_BREADCRUMB, - 'route_name' => "field_ui.delete_$entity_type", - 'weight' => 10, - ); - - // 'Manage form display' tab. - $items["$path/form-display"] = array( - 'title' => 'Manage form display', - 'type' => MENU_LOCAL_TASK, - 'route_name' => "field_ui.form_display_overview_$entity_type", - 'weight' => 2, - ); - - // 'Manage display' tab. - $items["$path/display"] = array( - 'title' => 'Manage display', - 'type' => MENU_LOCAL_TASK, - 'route_name' => "field_ui.display_overview_$entity_type", - 'weight' => 3, - ); - - // View and form modes secondary tabs. - // The same base $path for the menu item (with a placeholder) can be - // used for all bundles of a given entity type; but depending on - // administrator settings, each bundle has a different set of view - // modes available for customisation. So we define menu items for all - // view modes, and use a route requirement to determine which ones are - // actually visible for a given bundle. - $items["$default_path/form-display/default"] = array( - 'title' => t('Default'), - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items["$default_path/display/default"] = array( - 'title' => t('Default'), - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $weight = 0; - foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) { - $items["$path/form-display/$form_mode"] = array( - 'title' => $form_mode_info['label'], - 'type' => MENU_LOCAL_TASK, - 'weight' => $weight++, - 'route_name' => "field_ui.form_display_overview_$entity_type" . '_' . $form_mode, - ); - } - $weight = 0; - foreach (entity_get_view_modes($entity_type) as $view_mode => $view_mode_info) { - $items["$path/display/$view_mode"] = array( - 'title' => $view_mode_info['label'], - 'type' => MENU_LOCAL_TASK, - 'weight' => $weight++, - 'route_name' => "field_ui.display_overview_$entity_type" . '_' . $view_mode, - ); - } - } - } return $items; } @@ -334,12 +247,7 @@ function field_ui_entity_operation_alter(array &$operations, EntityInterface $en */ function field_ui_form_node_type_form_submit($form, &$form_state) { if ($form_state['triggering_element']['#parents'][0] === 'save_continue') { - $form_state['redirect_route'] = array( - 'route_name' => 'field_ui.overview_node', - 'route_parameters' => array( - 'bundle' => $form_state['values']['type'], - ), - ); + $form_state['redirect_route'] = \Drupal::entityManager()->getAdminRouteInfo('node', $form_state['values']['type']); } } @@ -462,3 +370,11 @@ function theme_field_ui_table($variables) { return drupal_render($table); } +/** + * Implements hook_local_tasks_alter(). + */ +function field_ui_local_tasks_alter(&$local_tasks) { + $container = \Drupal::getContainer(); + $local_task = FieldUiLocalTask::create($container, 'field_ui.fields'); + $local_task->alterLocalTasks($local_tasks); +} diff --git a/core/modules/field_ui/field_ui.services.yml b/core/modules/field_ui/field_ui.services.yml index 2ab3051..10752b0 100644 --- a/core/modules/field_ui/field_ui.services.yml +++ b/core/modules/field_ui/field_ui.services.yml @@ -1,14 +1,16 @@ services: field_ui.subscriber: class: Drupal\field_ui\Routing\RouteSubscriber - arguments: ['@entity.manager'] + arguments: ['@entity.manager', '@router.route_provider'] tags: - { name: event_subscriber } access_check.field_ui.view_mode: class: Drupal\field_ui\Access\ViewModeAccessCheck + arguments: ['@entity.manager'] tags: - { name: access_check } access_check.field_ui.form_mode: class: Drupal\field_ui\Access\FormModeAccessCheck + arguments: ['@entity.manager'] tags: - { name: access_check } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php index 92e3790..3322c9e 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/FormModeAccessCheck.php @@ -8,6 +8,7 @@ namespace Drupal\field_ui\Access; use Drupal\Core\Access\StaticAccessCheckInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; @@ -18,6 +19,23 @@ class FormModeAccessCheck implements StaticAccessCheckInterface { /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * Creates a new FormModeAccessCheck. + * + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + */ + public function __construct(EntityManagerInterface $entity_manager) { + $this->entityManager = $entity_manager; + } + + /** * {@inheritdoc} */ public function appliesTo() { @@ -29,17 +47,19 @@ public function appliesTo() { */ public function access(Route $route, Request $request, AccountInterface $account) { if ($entity_type = $request->attributes->get('entity_type')) { - $bundle = $request->attributes->get('bundle'); - $form_mode = $request->attributes->get('mode'); + $form_mode = $request->attributes->get('form_mode_name'); - if ($form_mode == 'default') { - $visibility = TRUE; + if (!($bundle = $request->attributes->get('bundle'))) { + $entity_info = $this->entityManager->getDefinition($entity_type); + $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); } - elseif ($entity_form_display = entity_load('entity_form_display', $entity_type . '.' . $bundle . '.' . $form_mode)) { - $visibility = $entity_form_display->status(); + + $visibility = FALSE; + if (!$form_mode || $form_mode == 'default') { + $visibility = TRUE; } - else { - $visibility = FALSE; + elseif ($entity_display = $this->entityManager->getStorageController('entity_form_display')->load($entity_type . '.' . $bundle . '.' . $form_mode)) { + $visibility = $entity_display->status(); } if ($visibility) { diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php index 2fe350b..5e69daf 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Access/ViewModeAccessCheck.php @@ -8,6 +8,7 @@ namespace Drupal\field_ui\Access; use Drupal\Core\Access\StaticAccessCheckInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; @@ -18,6 +19,23 @@ class ViewModeAccessCheck implements StaticAccessCheckInterface { /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * Creates a new ViewModeAccessCheck. + * + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + */ + public function __construct(EntityManagerInterface $entity_manager) { + $this->entityManager = $entity_manager; + } + + /** * {@inheritdoc} */ public function appliesTo() { @@ -29,18 +47,20 @@ public function appliesTo() { */ public function access(Route $route, Request $request, AccountInterface $account) { if ($entity_type = $request->attributes->get('entity_type')) { - $bundle = $request->attributes->get('bundle'); - $view_mode = $request->attributes->get('mode'); + $view_mode = $request->attributes->get('view_mode_name'); - if ($view_mode == 'default') { + if (!($bundle = $request->attributes->get('bundle'))) { + $entity_info = $this->entityManager->getDefinition($entity_type); + $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); + } + + $visibility = FALSE; + if (!$view_mode || $view_mode == 'default') { $visibility = TRUE; } - elseif ($entity_display = entity_load('entity_display', $entity_type . '.' . $bundle . '.' . $view_mode)) { + elseif ($entity_display = $this->entityManager->getStorageController('entity_display')->load($entity_type . '.' . $bundle . '.' . $view_mode)) { $visibility = $entity_display->status(); } - else { - $visibility = FALSE; - } if ($visibility) { $permission = $route->getRequirement('_field_ui_view_mode_access'); diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php index c25bd43..9834005 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php @@ -85,7 +85,16 @@ public function getRegions() { public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL, $mode = NULL) { parent::buildForm($form, $form_state, $entity_type, $bundle); - $this->mode = (isset($mode) ? $mode : 'default'); + $raw_variables = $this->getRequest()->attributes->get('_raw_variables')->all(); + if (isset($raw_variables['form_mode_name'])) { + $this->mode = $raw_variables['form_mode_name']; + } + elseif (isset($raw_variables['view_mode_name'])) { + $this->mode = $raw_variables['view_mode_name']; + } + else { + $this->mode = 'default'; + } // Gather type information. $instances = field_info_instances($this->entity_type, $this->bundle); diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php index 7e37efb..f51256b 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldDeleteForm.php @@ -79,12 +79,7 @@ public function submit(array $form, array &$form_state) { drupal_set_message($this->t('There was a problem removing the %field from the %type content type.', array('%field' => $this->entity->label(), '%type' => $bundle_label)), 'error'); } - $form_state['redirect_route'] = array( - 'route_name' => 'field_ui.overview_' . $this->entity->entity_type, - 'route_parameters' => array( - 'bundle' => $this->entity->bundle, - ) - ); + $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->entity->entity_type, $this->entity->bundle); // Fields are purged on cron. However field module prevents disabling modules // when field types they provided are used in a field until it is fully diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php index 062dd40..8d9ce26 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php @@ -206,12 +206,7 @@ public function submitForm(array &$form, array &$form_state) { $form_state['redirect'] = $next_destination; } else { - $form_state['redirect_route'] = array( - 'route_name' => 'field_ui.overview_' . $this->instance->entity_type, - 'route_parameters' => array( - 'bundle' => $this->instance->bundle, - ) - ); + $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->instance->entity_type, $this->instance->bundle); } } catch (\Exception $e) { diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php index 7b50745..8a7650f 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php @@ -193,12 +193,7 @@ public function submitForm(array &$form, array &$form_state) { $form_state['redirect'] = $next_destination; } else { - $form_state['redirect_route'] = array( - 'route_name' => 'field_ui.overview_' . $this->instance->entity_type, - 'route_parameters' => array( - 'bundle' => $this->instance->bundle, - ) - ); + $form_state['redirect_route'] = $this->entityManager->getAdminRouteInfo($this->instance->entity_type, $this->instance->bundle); } } @@ -212,10 +207,11 @@ public function delete(array &$form, array &$form_state) { $destination = drupal_get_destination(); $request->query->remove('destination'); } + $entity_info = $this->entityManager->getDefinition($this->instance->entity_type); $form_state['redirect_route'] = array( 'route_name' => 'field_ui.delete_' . $this->instance->entity_type, 'route_parameters' => array( - 'bundle' => $this->instance->bundle, + $entity_info['bundle_entity_type'] => $this->instance->bundle, 'field_instance' => $this->instance->id(), ), 'options' => array( diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php index c7db86e..aad6599 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php @@ -74,10 +74,16 @@ public static function create(ContainerInterface $container) { * {@inheritdoc} */ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) { - $entity_info = $this->entityManager->getDefinition($entity_type); + if (!isset($form_state['bundle'])) { + if (!$bundle) { + $entity_info = $this->entityManager->getDefinition($entity_type); + $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); + } + $form_state['bundle'] = $bundle; + } $this->entity_type = $entity_type; - $this->bundle = $bundle; + $this->bundle = $form_state['bundle']; $this->adminPath = $this->entityManager->getAdminPath($this->entity_type, $this->bundle); // When displaying the form, make sure the list of fields is up-to-date. diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php new file mode 100644 index 0000000..3e15548 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalTask.php @@ -0,0 +1,231 @@ +routeProvider = $route_provider; + $this->entityManager = $entity_manager; + $this->translationManager = $translation_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('router.route_provider'), + $container->get('entity.manager'), + $container->get('string_translation') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions(array $base_plugin_definition) { + $this->derivatives = array(); + + foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) { + if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) { + $this->derivatives["overview_$entity_type"] = array( + 'route_name' => "field_ui.overview_$entity_type", + 'weight' => 1, + 'title' => $this->t('Manage fields'), + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + ); + + // 'Manage form display' tab. + $this->derivatives["form_display_overview_$entity_type"] = array( + 'route_name' => "field_ui.form_display_overview_$entity_type", + 'weight' => 2, + 'title' => $this->t('Manage form display'), + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + ); + + // 'Manage display' tab. + $this->derivatives["display_overview_$entity_type"] = array( + 'route_name' => "field_ui.display_overview_$entity_type", + 'weight' => 3, + 'title' => $this->t('Manage display'), + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + ); + + // Field instance edit tab. + $this->derivatives["instance_edit_$entity_type"] = array( + 'route_name' => "field_ui.instance_edit_$entity_type", + 'title' => $this->t('Edit'), + 'tab_root_id' => "field_ui.fields:instance_edit_$entity_type", + ); + + // Field settings tab. + $this->derivatives["field_edit_$entity_type"] = array( + 'route_name' => "field_ui.field_edit_$entity_type", + 'title' => $this->t('Field settings'), + 'tab_root_id' => "field_ui.fields:instance_edit_$entity_type", + ); + + // View and form modes secondary tabs. + // The same base $path for the menu item (with a placeholder) can be + // used for all bundles of a given entity type; but depending on + // administrator settings, each bundle has a different set of view + // modes available for customisation. So we define menu items for all + // view modes, and use a route requirement to determine which ones are + // actually visible for a given bundle. + $this->derivatives['field_form_display_default_' . $entity_type] = array( + 'title' => 'Default', + 'route_name' => "field_ui.form_display_overview_$entity_type", + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + 'tab_parent_id' => "field_ui.fields:form_display_overview_$entity_type", + ); + $this->derivatives['field_display_default_' . $entity_type] = array( + 'title' => 'Default', + 'route_name' => "field_ui.display_overview_$entity_type", + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + 'tab_parent_id' => "field_ui.fields:display_overview_$entity_type", + ); + + // One local task for each form mode. + $weight = 0; + foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) { + $this->derivatives['field_form_display_' . $form_mode . '_' . $entity_type] = array( + 'title' => $form_mode_info['label'], + 'route_name' => "field_ui.form_display_overview_form_mode_$entity_type", + 'route_parameters' => array( + 'form_mode_name' => $form_mode, + ), + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + 'tab_parent_id' => "field_ui.fields:form_display_overview_$entity_type", + 'weight' => $weight++, + ); + } + + // One local task for each view mode. + $weight = 0; + foreach (entity_get_view_modes($entity_type) as $view_mode => $form_mode_info) { + $this->derivatives['field_display_' . $view_mode . '_' . $entity_type] = array( + 'title' => $form_mode_info['label'], + 'route_name' => "field_ui.display_overview_view_mode_$entity_type", + 'route_parameters' => array( + 'view_mode_name' => $view_mode, + ), + 'tab_root_id' => "field_ui.fields:overview_$entity_type", + 'tab_parent_id' => "field_ui.fields:display_overview_$entity_type", + 'weight' => $weight++, + ); + } + } + } + + foreach ($this->derivatives as &$entry) { + $entry += $base_plugin_definition; + } + + return $this->derivatives; + } + + /** + * Alters the tab_root_id definition for field_ui local tasks. + */ + public function alterLocalTasks(&$local_tasks) { + foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) { + if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) { + if ($parent_task = $this->getPluginIdFromRoute($entity_info['links']['admin-form'], $local_tasks)) { + $local_tasks["field_ui.fields:overview_$entity_type"]['tab_root_id'] = $parent_task; + $local_tasks["field_ui.fields:form_display_overview_$entity_type"]['tab_root_id'] = $parent_task; + $local_tasks["field_ui.fields:display_overview_$entity_type"]['tab_root_id'] = $parent_task; + $local_tasks["field_ui.fields:field_form_display_default_$entity_type"]['tab_root_id'] = $parent_task; + $local_tasks["field_ui.fields:field_display_default_$entity_type"]['tab_root_id'] = $parent_task; + + foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) { + $local_tasks['field_ui.fields:field_form_display_' . $form_mode . '_' . $entity_type]['tab_root_id'] = $parent_task; + } + + foreach (entity_get_view_modes($entity_type) as $view_mode => $form_mode_info) { + $local_tasks['field_ui.fields:field_display_' . $view_mode . '_' . $entity_type]['tab_root_id'] = $parent_task; + } + } + } + } + } + + /** + * Finds the local task ID of a route given the route name. + * + * @param string $route_name + * The route name. + * @param array $local_tasks + * An array of all local task definitions. + * + * @return string|null + * Returns the local task ID of the given route or NULL if none is found. + */ + protected function getPluginIdFromRoute($route_name, &$local_tasks) { + $local_task_id = NULL; + foreach ($local_tasks as $plugin_id => $local_task) { + if ($local_task['route_name'] == $route_name) { + $local_task_id = $plugin_id; + break; + } + } + + return $local_task_id; + } + + /** + * 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); + } + +} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php index ff49c18..048630a 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php @@ -8,7 +8,10 @@ namespace Drupal\field_ui\Routing; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Routing\RouteSubscriberBase; +use Drupal\Core\Routing\RoutingEvents; +use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; @@ -25,13 +28,23 @@ class RouteSubscriber extends RouteSubscriberBase { protected $manager; /** + * The route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface + */ + protected $routeProvider; + + /** * Constructs a RouteSubscriber object. * * @param \Drupal\Core\Entity\EntityManagerInterface $manager * The entity type manager. + * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider + * The route provider. */ - public function __construct(EntityManagerInterface $manager) { + public function __construct(EntityManagerInterface $manager, RouteProviderInterface $route_provider) { $this->manager = $manager; + $this->routeProvider = $route_provider; } /** @@ -40,8 +53,20 @@ public function __construct(EntityManagerInterface $manager) { protected function routes(RouteCollection $collection) { foreach ($this->manager->getDefinitions() as $entity_type => $entity_info) { $defaults = array(); - if ($entity_info['fieldable'] && isset($entity_info['route_base_path'])) { - $path = $entity_info['route_base_path']; + if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) { + // First try to get the route from the dynamic_routes collection. + if (!$entity_route = $collection->get($entity_info['links']['admin-form'])) { + // Then try to get the route from the route provider itself, checking + // all previous collections. + try { + $entity_route = $this->routeProvider->getRouteByName($entity_info['links']['admin-form']); + } + // If the route was not found, skip this entity type. + catch (RouteNotFoundException $e) { + continue; + } + } + $path = $entity_route->getPath(); $route = new Route( "$path/fields/{field_instance}", @@ -85,20 +110,19 @@ protected function routes(RouteCollection $collection) { '_form' => '\Drupal\field_ui\FormDisplayOverview', '_title' => 'Manage form display', ) + $defaults, - array('_permission' => 'administer ' . $entity_type . ' form display') + array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display') ); $collection->add("field_ui.form_display_overview_$entity_type", $route); - foreach (entity_get_form_modes($entity_type) as $form_mode => $form_mode_info) { - $route = new Route( - "$path/form-display/$form_mode", - array( - '_form' => '\Drupal\field_ui\FormDisplayOverview', - 'mode' => $form_mode, - ) + $defaults, - array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display')); - $collection->add("field_ui.form_display_overview_$entity_type" . '_'. $form_mode, $route); - } + $route = new Route( + "$path/form-display/{form_mode_name}", + array( + '_form' => '\Drupal\field_ui\FormDisplayOverview', + 'form_mode_name' => NULL, + ) + $defaults, + array('_field_ui_form_mode_access' => 'administer ' . $entity_type . ' form display') + ); + $collection->add("field_ui.form_display_overview_form_mode_$entity_type", $route); $route = new Route( "$path/display", @@ -106,22 +130,30 @@ protected function routes(RouteCollection $collection) { '_form' => '\Drupal\field_ui\DisplayOverview', '_title' => 'Manage display', ) + $defaults, - array('_permission' => 'administer ' . $entity_type . ' display') + array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display') ); $collection->add("field_ui.display_overview_$entity_type", $route); - foreach (entity_get_view_modes($entity_type) as $view_mode => $view_mode_info) { - $route = new Route( - "$path/display/$view_mode", - array( - '_form' => '\Drupal\field_ui\DisplayOverview', - 'mode' => $view_mode, - ) + $defaults, - array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display')); - $collection->add("field_ui.display_overview_$entity_type" . '_' . $view_mode, $route); - } + $route = new Route( + "$path/display/{view_mode_name}", + array( + '_form' => '\Drupal\field_ui\DisplayOverview', + 'view_mode_name' => NULL, + ) + $defaults, + array('_field_ui_view_mode_access' => 'administer ' . $entity_type . ' display') + ); + $collection->add("field_ui.display_overview_view_mode_$entity_type", $route); } } } + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events = parent::getSubscribedEvents(); + $events[RoutingEvents::DYNAMIC] = array('onDynamicRoutes', -100); + return $events; + } + } diff --git a/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php b/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php index e6841c5..ede7799 100644 --- a/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php +++ b/core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Entity/FieldUITestNoBundle.php @@ -20,8 +20,7 @@ * controllers = { * "storage" = "Drupal\Core\Entity\DatabaseStorageController" * }, - * fieldable = TRUE, - * route_base_path = "field-ui-test-no-bundle/manage" + * fieldable = TRUE * ) */ class FieldUITestNoBundle extends Entity { diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php index f67d15b..dddecb3 100644 --- a/core/modules/node/lib/Drupal/node/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php @@ -49,12 +49,13 @@ * bundle_keys = { * "bundle" = "type" * }, - * route_base_path = "admin/structure/types/manage/{bundle}", + * bundle_entity_type = "node_type", * permission_granularity = "bundle", * links = { * "canonical" = "node.view", * "edit-form" = "node.page_edit", - * "version-history" = "node.revision_overview" + * "version-history" = "node.revision_overview", + * "admin-form" = "node.type_edit" * } * ) */ diff --git a/core/modules/node/node.local_tasks.yml b/core/modules/node/node.local_tasks.yml index 24d9ef2..ecf6298 100644 --- a/core/modules/node/node.local_tasks.yml +++ b/core/modules/node/node.local_tasks.yml @@ -16,3 +16,7 @@ node.revision_overview: tab_root_id: node.view title: 'Revisions' weight: 20 +node.type_edit: + title: 'Edit' + route_name: node.type_edit + tab_root_id: node.type_edit diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 79c7b44..a860f3f 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -949,16 +949,6 @@ function node_menu() { 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, ); - $items['admin/structure/types/manage/%node_type'] = array( - 'title' => 'Edit content type', - 'title callback' => 'entity_page_label', - 'title arguments' => array(4), - 'route_name' => 'node.type_edit', - ); - $items['admin/structure/types/manage/%node_type/edit'] = array( - 'title' => 'Edit', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); $items['node/add'] = array( 'title' => 'Add content', 'route_name' => 'node.add_page', diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php index ad22414..a6a42ee 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php @@ -30,4 +30,11 @@ public function testEdit(Request $request) { return entity_test_edit($entity); } + /** + * Returns an empty page. + */ + public function testAdmin() { + return ''; + } + } diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php index 5ba7091..b4ad49b 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php @@ -37,10 +37,10 @@ * "bundle" = "type", * "label" = "name" * }, - * route_base_path = "admin/structure/entity-test/manage/{bundle}", * links = { * "canonical" = "entity_test.render", - * "edit-form" = "entity_test.edit_entity_test" + * "edit-form" = "entity_test.edit_entity_test", + * "admin-form" = "entity_test.admin_entity_test" * } * ) */ diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php index c3f9212..e73ff5e 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php @@ -36,10 +36,10 @@ * "bundle" = "type", * "label" = "name" * }, - * route_base_path = "entity_test_mul/structure/{bundle}", * links = { * "canonical" = "entity_test.edit_entity_test_mul", - * "edit-form" = "entity_test.edit_entity_test_mul" + * "edit-form" = "entity_test.edit_entity_test_mul", + * "admin-form" = "entity_test.admin_entity_test_mul" * } * ) */ diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php index 0379d37..91a43cd 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php @@ -39,6 +39,13 @@ protected function routes(RouteCollection $collection) { )) ); $collection->add("entity_test.edit_$entity_type", $route); + + $route = new Route( + "$entity_type/structure/{bundle}", + array('_content' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'), + array('_permission' => 'administer entity_test content') + ); + $collection->add("entity_test.admin_$entity_type", $route); } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php index 0b7c3d6..a091d1a 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php @@ -44,11 +44,12 @@ * bundle_keys = { * "bundle" = "vid" * }, + * bundle_entity_type = "taxonomy_vocabulary", * links = { * "canonical" = "taxonomy.term_page", - * "edit-form" = "taxonomy.term_edit" + * "edit-form" = "taxonomy.term_edit", + * "admin-form" = "taxonomy.overview_terms" * }, - * route_base_path = "admin/structure/taxonomy/manage/{bundle}", * permission_granularity = "bundle" * ) */ diff --git a/core/modules/taxonomy/taxonomy.local_tasks.yml b/core/modules/taxonomy/taxonomy.local_tasks.yml index a04379e..cdcb4ea 100644 --- a/core/modules/taxonomy/taxonomy.local_tasks.yml +++ b/core/modules/taxonomy/taxonomy.local_tasks.yml @@ -7,3 +7,12 @@ taxonomy.term_edit: title: 'Edit' route_name: taxonomy.term_edit tab_root_id: taxonomy.term_page + +taxonomy.overview_terms: + title: 'List' + route_name: taxonomy.overview_terms + tab_root_id: taxonomy.overview_terms +taxonomy.vocabulary_edit: + title: 'Edit' + route_name: taxonomy.vocabulary_edit + tab_root_id: taxonomy.overview_terms diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 72222aa..c5f6153 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -240,14 +240,6 @@ function taxonomy_menu() { 'description' => 'Manage tagging, categorization, and classification of your content.', 'route_name' => 'taxonomy.vocabulary_list', ); - $items['admin/structure/taxonomy/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['admin/structure/taxonomy/add'] = array( - 'route_name' => 'taxonomy.vocabulary_add', - 'type' => MENU_SIBLING_LOCAL_TASK, - ); $items['taxonomy/term/%taxonomy_term'] = array( 'title' => 'Taxonomy term', diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php index fe90bfc..c667d20 100644 --- a/core/modules/user/lib/Drupal/user/Entity/User.php +++ b/core/modules/user/lib/Drupal/user/Entity/User.php @@ -32,7 +32,6 @@ * admin_permission = "administer user", * base_table = "users", * uri_callback = "user_uri", - * route_base_path = "admin/config/people/accounts", * label_callback = "user_label", * fieldable = TRUE, * translatable = TRUE, @@ -42,7 +41,8 @@ * }, * links = { * "canonical" = "user.view", - * "edit-form" = "user.edit" + * "edit-form" = "user.edit", + * "admin-form" = "user.account_settings" * } * ) */