diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 51472cf8c5..6928f17efd 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -7,11 +7,14 @@ use Drupal\comment\CommentInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\taxonomy\TermInterface; use Drupal\taxonomy\VocabularyInterface; use Drupal\user\Entity\User; @@ -349,6 +352,22 @@ function forum_form_node_form_alter(&$form, FormStateInterface $form_state, $for } /** + * Implements hook_ENTITY_TYPE_access() for taxonomy_term. + */ +function forum_taxonomy_term_access(TermInterface $term, $operation, AccountInterface $account) { + // Allow update access to terms in the forum vocabulary with the + // administer forums permission. + $forum_vid = \Drupal::config('forum.settings')->get('vocabulary'); + if ($operation ==='update' && $term->bundle() === $forum_vid) { + return AccessResult::allowedIfHasPermission($account, 'administer forums'); + } + + // No opinion. + return AccessResult::neutral(); +} + + +/** * Implements hook_preprocess_HOOK() for block templates. */ function forum_preprocess_block(&$variables) { diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php index 08d37787bd..8c374b5842 100644 --- a/core/modules/forum/src/Form/Overview.php +++ b/core/modules/forum/src/Form/Overview.php @@ -58,13 +58,14 @@ public function buildForm(array $form, FormStateInterface $form_state) { foreach (Element::children($form['terms']) as $key) { if (isset($form['terms'][$key]['#term'])) { + /** @var \Drupal\taxonomy\TermInterface $term */ $term = $form['terms'][$key]['#term']; $form['terms'][$key]['term']['#url'] = Url::fromRoute('forum.page', ['taxonomy_term' => $term->id()]); - if ($form['terms'][$key]['#term']->access('delete')) { + if ($term->access('delete')) { unset($form['terms'][$key]['operations']['#links']['delete']); } - if ($form['terms'][$key]['#term']->access('update')) { + if ($term->access('update')) { $route_parameters = $form['terms'][$key]['operations']['#links']['edit']['url']->getRouteParameters(); if (!empty($term->forum_container->value)) { $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit container'); diff --git a/core/modules/forum/tests/src/Functional/ForumIndexTest.php b/core/modules/forum/tests/src/Functional/ForumIndexTest.php index 38adb72ea8..4b048e2647 100644 --- a/core/modules/forum/tests/src/Functional/ForumIndexTest.php +++ b/core/modules/forum/tests/src/Functional/ForumIndexTest.php @@ -57,6 +57,8 @@ public function testForumIndexStatus() { 'parent[0]' => $tid, ]; $this->drupalPostForm('admin/structure/forum/add/forum', $edit, t('Save')); + $this->assertSession()->linkExists(t('edit forum'), 0, 'edit forum link is not accessible to user with administer forums permission'); + $tid_child = $tid + 1; // Verify that the node appears on the index. diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php index 1a4d4498a5..b13250faaa 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php @@ -45,14 +45,14 @@ protected function setUpAuthorization($method) { $this->grantPermissionsToTestedRole(['create terms in camelids']); break; case 'PATCH': - $this->grantPermissionsToTestedRole(['edit terms in camelids', 'create url aliases']); - break; - case 'DELETE': // Grant the 'create url aliases' permission to test the case when // the path field is accessible, see // \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase // for a negative test. - $this->grantPermissionsToTestedRole(['delete terms in camelids', 'create url aliases']); + $this->grantPermissionsToTestedRole(['edit terms in camelids', 'create url aliases']); + break; + case 'DELETE': + $this->grantPermissionsToTestedRole(['delete terms in camelids']); break; } } diff --git a/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php b/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php deleted file mode 100644 index cd9065bb6f..0000000000 --- a/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php +++ /dev/null @@ -1,25 +0,0 @@ -hasPermission('administer taxonomy') || $account->hasPermission('access taxonomy overview'); - return AccessResult::allowedIf($access); - } - -} diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php index 03491ab962..c28a379d09 100644 --- a/core/modules/taxonomy/src/Entity/Term.php +++ b/core/modules/taxonomy/src/Entity/Term.php @@ -20,6 +20,7 @@ * "storage" = "Drupal\taxonomy\TermStorage", * "storage_schema" = "Drupal\taxonomy\TermStorageSchema", * "view_builder" = "Drupal\taxonomy\TermViewBuilder", + * "list_builder" = "Drupal\taxonomy\TermListBuilder", * "access" = "Drupal\taxonomy\TermAccessControlHandler", * "views_data" = "Drupal\taxonomy\TermViewsData", * "form" = { diff --git a/core/modules/taxonomy/src/Entity/Vocabulary.php b/core/modules/taxonomy/src/Entity/Vocabulary.php index b0d1ac13d2..d61294b4e3 100644 --- a/core/modules/taxonomy/src/Entity/Vocabulary.php +++ b/core/modules/taxonomy/src/Entity/Vocabulary.php @@ -15,6 +15,7 @@ * handlers = { * "storage" = "Drupal\taxonomy\VocabularyStorage", * "list_builder" = "Drupal\taxonomy\VocabularyListBuilder", + * "access" = "Drupal\taxonomy\VocabularyAccessControlHandler", * "form" = { * "default" = "Drupal\taxonomy\VocabularyForm", * "reset" = "Drupal\taxonomy\Form\VocabularyResetForm", diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php index 8c4b64c178..e4209ebffd 100644 --- a/core/modules/taxonomy/src/Form/OverviewTerms.php +++ b/core/modules/taxonomy/src/Form/OverviewTerms.php @@ -6,6 +6,7 @@ use Drupal\Core\Form\FormBase; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; use Drupal\taxonomy\VocabularyInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -36,6 +37,13 @@ class OverviewTerms extends FormBase { protected $storageController; /** + * The term list builder. + * + * @var \Drupal\Core\Entity\EntityListBuilderInterface + */ + protected $termListBuilder; + + /** * Constructs an OverviewTerms object. * * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler @@ -47,6 +55,7 @@ public function __construct(ModuleHandlerInterface $module_handler, EntityManage $this->moduleHandler = $module_handler; $this->entityManager = $entity_manager; $this->storageController = $entity_manager->getStorage('taxonomy_term'); + $this->termListBuilder = $entity_manager->getListBuilder('taxonomy_term'); } /** @@ -204,12 +213,11 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular } $errors = $form_state->getErrors(); - $destination = $this->getDestinationArray(); $row_position = 0; // Build the actual form. $access_control_handler = $this->entityManager->getAccessControlHandler('taxonomy_term'); if ($access_control_handler->createAccess($taxonomy_vocabulary->id())) { - $empty = $this->t('No terms available. Add term.', ['@link' => $this->url('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $taxonomy_vocabulary->id()])]); + $empty = $this->t('No terms available. Add term.', ['@link' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $taxonomy_vocabulary->id()])->toString()]); } else { $empty = $this->t('No terms available.'); @@ -222,7 +230,7 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular ], ]; $change_weight_access = TRUE; - $operations_access = TRUE; + $operations_access = FALSE; foreach ($current_page as $key => $term) { /** @var $term \Drupal\Core\Entity\EntityInterface */ $term = $this->entityManager->getTranslationFromContext($term); @@ -282,35 +290,14 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular '#access' => $edit_access, ]; } - $operations = []; - if ($edit_access) { - $operations['edit'] = [ - 'title' => $this->t('Edit'), - 'query' => $destination, - 'url' => $term->urlInfo('edit-form'), - ]; - } - if ($term->access('delete')) { - $operations['delete'] = [ - 'title' => $this->t('Delete'), - 'query' => $destination, - 'url' => $term->urlInfo('delete-form'), - ]; - } - if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($term)->isAllowed()) { - $operations['translate'] = [ - 'title' => $this->t('Translate'), - 'query' => $destination, - 'url' => $term->urlInfo('drupal:content-translation-overview'), - ]; - } - - $operations_access &= count($operations) > 0; - if (count($operations)) { + if ($operations = $this->termListBuilder->getOperations($term)) { + // Allow access to operations if there is at least one term with + // operations. + $operations_access = TRUE; $form['terms'][$key]['operations'] = [ '#type' => 'operations', - '#links' => $operations, + '#links' => $operations ]; } diff --git a/core/modules/taxonomy/src/TermListBuilder.php b/core/modules/taxonomy/src/TermListBuilder.php new file mode 100644 index 0000000000..0d691e194b --- /dev/null +++ b/core/modules/taxonomy/src/TermListBuilder.php @@ -0,0 +1,64 @@ +get('entity_type.manager')->getStorage($entity_type->id()), + $container->get('redirect.destination') + ); + } + + /** + * Constructs a new instance. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type definition. + * @param \Drupal\Core\Entity\EntityStorageInterface $storage + * The entity storage class. + * @param \Drupal\Core\Routing\RedirectDestinationInterface + * The redirect destination. + */ + public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, RedirectDestinationInterface $redirect_destination) { + parent::__construct($entity_type, $storage); + + $this->redirectDestination = $redirect_destination; + } + + /** + * {@inheritdoc} + */ + public function getDefaultOperations(EntityInterface $entity) { + $operations = parent::getDefaultOperations($entity); + + $destination = $this->redirectDestination->get(); + foreach ($operations as $key => $operation) { + $operations[$key]['query']['destination'] = $destination; + } + + return $operations; + } +} diff --git a/core/modules/taxonomy/src/VocabularyAccessControlHandler.php b/core/modules/taxonomy/src/VocabularyAccessControlHandler.php new file mode 100644 index 0000000000..befc8a5fdf --- /dev/null +++ b/core/modules/taxonomy/src/VocabularyAccessControlHandler.php @@ -0,0 +1,30 @@ +getStorage($entity_type->id())); - $this->current_user = $current_user; - $this->entityManager = $entity_manager; - $this->access_view = $access_view; + $this->currentUser = $current_user; + $this->entityTypeManager = $entity_type_manager; } /** @@ -67,10 +60,8 @@ public function __construct(EntityTypeInterface $entity_type, EntityStorageInter public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { return new static( $entity_type, - $container->get('entity.manager')->getStorage($entity_type->id()), $container->get('current_user'), - $container->get('entity.manager'), - $container->get('access_check.taxonomy.taxonomy_overview') + $container->get('entity_type.manager') ); } @@ -91,15 +82,15 @@ public function getDefaultOperations(EntityInterface $entity) { $operations['edit']['title'] = t('Edit vocabulary'); } - if ($this->access_view->access($this->current_user)) { + if ($entity->access('access taxonomy overview')) { $operations['list'] = [ 'title' => t('List terms'), 'weight' => 0, - 'url' => $entity->urlInfo('overview-form'), + 'url' => $entity->toUrl('overview-form'), ]; } - $taxonomy_term_access_control_handler = $this->entityManager->getAccessControlHandler('taxonomy_term'); + $taxonomy_term_access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term'); if ($taxonomy_term_access_control_handler->createAccess($entity->id())) { $operations['add'] = [ 'title' => t('Add terms'), @@ -120,7 +111,7 @@ public function buildHeader() { $header['label'] = t('Vocabulary name'); $header['description'] = t('Description'); - if ($this->current_user->hasPermission('administer vocabularies')) { + if ($this->currentUser->hasPermission('administer vocabularies')) { $header['weight'] = t('Weight'); } @@ -147,8 +138,9 @@ public function render() { unset($this->weightKey); } $build = parent::render(); - if ($this->current_user->hasPermission('administer vocabularies')) { - $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', ['@link' => \Drupal::url('entity.taxonomy_vocabulary.add_form')]); + $create_access = $this->entityTypeManager->getAccessControlHandler('taxonomy_vocabulary')->createAccess(); + if ($create_access) { + $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', ['@link' => Url::fromRoute('entity.taxonomy_vocabulary.add_form')->toString()]); } else { $build['table']['#empty'] = t('No vocabularies available.'); diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml index 94c8727768..19989241e4 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -74,7 +74,7 @@ entity.taxonomy_vocabulary.overview_form: _form: 'Drupal\taxonomy\Form\OverviewTerms' _title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle' requirements: - _permission: 'access taxonomy overview+administer taxonomy' + _entity_access: 'taxonomy_vocabulary.access taxonomy overview' entity.taxonomy_term.canonical: path: '/taxonomy/term/{taxonomy_term}' diff --git a/core/modules/taxonomy/taxonomy.services.yml b/core/modules/taxonomy/taxonomy.services.yml index b521c7f596..a8153ed117 100644 --- a/core/modules/taxonomy/taxonomy.services.yml +++ b/core/modules/taxonomy/taxonomy.services.yml @@ -4,7 +4,3 @@ services: arguments: ['@entity.manager'] tags: - { name: breadcrumb_builder, priority: 1002 } - access_check.taxonomy.taxonomy_overview: - class: Drupal\taxonomy\Access\TaxonomyOverviewAccessCheck - tags: - - { name: access_check, applies_to: _access_taxonomy_overview }