diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php index df6f947..08d3778 100644 --- a/core/modules/forum/src/Form/Overview.php +++ b/core/modules/forum/src/Form/Overview.php @@ -60,19 +60,24 @@ public function buildForm(array $form, FormStateInterface $form_state) { if (isset($form['terms'][$key]['#term'])) { $term = $form['terms'][$key]['#term']; $form['terms'][$key]['term']['#url'] = Url::fromRoute('forum.page', ['taxonomy_term' => $term->id()]); - unset($form['terms'][$key]['operations']['#links']['delete']); - $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'); - $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_container_form', $route_parameters); + if ($form['terms'][$key]['#term']->access('delete')) { + unset($form['terms'][$key]['operations']['#links']['delete']); } - else { - $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit forum'); - $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_form', $route_parameters); + + if ($form['terms'][$key]['#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'); + $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_container_form', $route_parameters); + } + else { + $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit forum'); + $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_form', $route_parameters); + } + // We don't want the redirect from the link so we can redirect the + // delete action. + unset($form['terms'][$key]['operations']['#links']['edit']['query']['destination']); } - // We don't want the redirect from the link so we can redirect the - // delete action. - unset($form['terms'][$key]['operations']['#links']['edit']['query']['destination']); } } diff --git a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php index 8eb9c9e..64f7f59 100644 --- a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php +++ b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php @@ -94,7 +94,7 @@ public function testNodeAccessBasic() { // Array of nids marked private. $private_nodes = []; for ($i = 0; $i < $num_simple_users; $i++) { - $simple_users[$i] = $this->drupalCreateUser(['access content', 'create article content']); + $simple_users[$i] = $this->drupalCreateUser(['access content', 'create article content', 'create terms in tags']); } foreach ($simple_users as $this->webUser) { $this->drupalLogin($this->webUser); 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 698451b..8c00523 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php @@ -39,10 +39,13 @@ protected function setUpAuthorization($method) { $this->grantPermissionsToTestedRole(['access content']); break; case 'POST': + $this->grantPermissionsToTestedRole(['create terms in camelids']); + break; case 'PATCH': + $this->grantPermissionsToTestedRole(['edit terms in camelids']); + break; case 'DELETE': - // @todo Update once https://www.drupal.org/node/2824408 lands. - $this->grantPermissionsToTestedRole(['administer taxonomy']); + $this->grantPermissionsToTestedRole(['delete terms in camelids']); break; } } @@ -149,7 +152,7 @@ protected function getExpectedUnauthorizedAccessMessage($method) { case 'GET': return "The 'access content' permission is required."; case 'POST': - return "The 'administer taxonomy' permission is required."; + return "The following permissions are required: 'create terms in camelids' OR 'administer taxonomy'."; case 'PATCH': return "The following permissions are required: 'edit terms in camelids' OR 'administer taxonomy'."; case 'DELETE': diff --git a/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php b/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php new file mode 100644 index 0000000..cd9065b --- /dev/null +++ b/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php @@ -0,0 +1,25 @@ +hasPermission('administer taxonomy') || $account->hasPermission('access taxonomy overview'); + return AccessResult::allowedIf($access); + } + +} diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php index 0ccb24b..037d517 100644 --- a/core/modules/taxonomy/src/Form/OverviewTerms.php +++ b/core/modules/taxonomy/src/Form/OverviewTerms.php @@ -207,14 +207,22 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular $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()])]); + } + else { + $empty = $this->t('No terms available.'); + } $form['terms'] = [ '#type' => 'table', - '#header' => [$this->t('Name'), $this->t('Weight'), $this->t('Operations')], - '#empty' => $this->t('No terms available. Add term.', [':link' => $this->url('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $taxonomy_vocabulary->id()])]), + '#empty' => $empty, '#attributes' => [ 'id' => 'taxonomy', ], ]; + $change_weight_access = TRUE; + $operations_access = TRUE; foreach ($current_page as $key => $term) { /** @var $term \Drupal\Core\Entity\EntityInterface */ $term = $this->entityManager->getTranslationFromContext($term); @@ -260,28 +268,36 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular ], ]; } - $form['terms'][$key]['weight'] = [ - '#type' => 'weight', - '#delta' => $delta, - '#title' => $this->t('Weight for added term'), - '#title_display' => 'invisible', - '#default_value' => $term->getWeight(), - '#attributes' => [ - 'class' => ['term-weight'], - ], - ]; - $operations = [ - 'edit' => [ + $edit_access = $term->access('update'); + $change_weight_access &= $edit_access; + + if ($edit_access) { + $form['terms'][$key]['weight'] = [ + '#type' => 'weight', + '#delta' => $delta, + '#title' => $this->t('Weight for added term'), + '#title_display' => 'invisible', + '#default_value' => $term->getWeight(), + '#attributes' => ['class' => ['term-weight']], + '#access' => $edit_access, + ]; + } + $operations = []; + + if ($edit_access) { + $operations['edit'] = [ 'title' => $this->t('Edit'), 'query' => $destination, 'url' => $term->urlInfo('edit-form'), - ], - 'delete' => [ + ]; + } + 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'), @@ -289,10 +305,14 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular 'url' => $term->urlInfo('drupal:content-translation-overview'), ]; } - $form['terms'][$key]['operations'] = [ - '#type' => 'operations', - '#links' => $operations, - ]; + + $operations_access &= count($operations) > 0; + if (count($operations)) { + $form['terms'][$key]['operations'] = [ + '#type' => 'operations', + '#links' => $operations, + ]; + } $form['terms'][$key]['#attributes']['class'] = []; if ($parent_fields) { @@ -322,44 +342,54 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular $row_position++; } - if ($parent_fields) { - $form['terms']['#tabledrag'][] = [ - 'action' => 'match', - 'relationship' => 'parent', - 'group' => 'term-parent', - 'subgroup' => 'term-parent', - 'source' => 'term-id', - 'hidden' => FALSE, - ]; + $form['terms']['#header'] = [$this->t('Name')]; + if ($change_weight_access) { + $form['terms']['#header'][] = $this->t('Weight'); + if ($parent_fields) { + $form['terms']['#tabledrag'][] = [ + 'action' => 'match', + 'relationship' => 'parent', + 'group' => 'term-parent', + 'subgroup' => 'term-parent', + 'source' => 'term-id', + 'hidden' => FALSE, + ]; + $form['terms']['#tabledrag'][] = [ + 'action' => 'depth', + 'relationship' => 'group', + 'group' => 'term-depth', + 'hidden' => FALSE, + ]; + $form['terms']['#attached']['library'][] = 'taxonomy/drupal.taxonomy'; + $form['terms']['#attached']['drupalSettings']['taxonomy'] = [ + 'backStep' => $back_step, + 'forwardStep' => $forward_step, + ]; + } $form['terms']['#tabledrag'][] = [ - 'action' => 'depth', - 'relationship' => 'group', - 'group' => 'term-depth', - 'hidden' => FALSE, - ]; - $form['terms']['#attached']['library'][] = 'taxonomy/drupal.taxonomy'; - $form['terms']['#attached']['drupalSettings']['taxonomy'] = [ - 'backStep' => $back_step, - 'forwardStep' => $forward_step, + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'term-weight', ]; } - $form['terms']['#tabledrag'][] = [ - 'action' => 'order', - 'relationship' => 'sibling', - 'group' => 'term-weight', - ]; - if ($taxonomy_vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) { + if ($operations_access) { + $form['terms']['#header'][] = $this->t('Operations'); + } + + if (($taxonomy_vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) && $change_weight_access) { $form['actions'] = ['#type' => 'actions', '#tree' => FALSE]; $form['actions']['submit'] = [ '#type' => 'submit', '#value' => $this->t('Save'), '#button_type' => 'primary', + '#access' => $change_weight_access, ]; $form['actions']['reset_alphabetical'] = [ '#type' => 'submit', '#submit' => ['::submitReset'], '#value' => $this->t('Reset to alphabetical'), + '#access' => $change_weight_access, ]; } diff --git a/core/modules/taxonomy/src/TaxonomyPermissions.php b/core/modules/taxonomy/src/TaxonomyPermissions.php index 196c5a5..5ec9984 100644 --- a/core/modules/taxonomy/src/TaxonomyPermissions.php +++ b/core/modules/taxonomy/src/TaxonomyPermissions.php @@ -5,6 +5,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\taxonomy\Entity\Vocabulary; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -48,19 +49,30 @@ public static function create(ContainerInterface $container) { */ public function permissions() { $permissions = []; - foreach ($this->entityManager->getStorage('taxonomy_vocabulary')->loadMultiple() as $vocabulary) { - $permissions += [ - 'edit terms in ' . $vocabulary->id() => [ - 'title' => $this->t('Edit terms in %vocabulary', ['%vocabulary' => $vocabulary->label()]), - ], - ]; - $permissions += [ - 'delete terms in ' . $vocabulary->id() => [ - 'title' => $this->t('Delete terms from %vocabulary', ['%vocabulary' => $vocabulary->label()]), - ], - ]; + foreach (Vocabulary::loadMultiple() as $vocabulary) { + $permissions += $this->buildPermissions($vocabulary); } return $permissions; } + /** + * Builds a standard list of taxonomy term permissions for a given vocabulary. + * + * @param \Drupal\taxonomy\Entity\Vocabulary + * The vocabulary. + * + * @return array + * An array of permission names and descriptions. + */ + protected function buildPermissions(Vocabulary $vocabulary) { + $id = $vocabulary->id(); + $args = ['%vocabulary' => $vocabulary->label()]; + + return [ + "create terms in $id" => ['title' => $this->t('%vocabulary: Create terms', $args)], + "delete terms in $id" => ['title' => $this->t('%vocabulary: Delete terms', $args)], + "edit terms in $id" => ['title' => $this->t('%vocabulary: Edit terms', $args)], + ]; + } + } diff --git a/core/modules/taxonomy/src/TermAccessControlHandler.php b/core/modules/taxonomy/src/TermAccessControlHandler.php index 04c2c4f..1d48463 100644 --- a/core/modules/taxonomy/src/TermAccessControlHandler.php +++ b/core/modules/taxonomy/src/TermAccessControlHandler.php @@ -38,7 +38,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter * {@inheritdoc} */ protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { - return AccessResult::allowedIfHasPermission($account, 'administer taxonomy'); + return AccessResult::allowedIfHasPermissions($account, ["create terms in $entity_bundle", 'administer taxonomy'], 'OR'); } } diff --git a/core/modules/taxonomy/src/VocabularyListBuilder.php b/core/modules/taxonomy/src/VocabularyListBuilder.php index 8503c28..ad0b050 100644 --- a/core/modules/taxonomy/src/VocabularyListBuilder.php +++ b/core/modules/taxonomy/src/VocabularyListBuilder.php @@ -4,8 +4,14 @@ use Drupal\Core\Config\Entity\DraggableListBuilder; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\taxonomy\Access\TaxonomyOverviewAccessCheck; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Defines a class to build a listing of taxonomy vocabulary entities. @@ -20,6 +26,55 @@ class VocabularyListBuilder extends DraggableListBuilder { protected $entitiesKey = 'vocabularies'; /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * Constructs a new VocabularyListBuilder object. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type definition. + * @param \Drupal\Core\Entity\EntityStorageInterface $storage + * The entity storage class. + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager service. + * @param \Drupal\taxonomy\Access\TaxonomyOverviewAccessCheck $access_view + * The access check service. + */ + public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, AccountInterface $current_user, EntityManagerInterface $entity_manager, TaxonomyOverviewAccessCheck $access_view) { + parent::__construct($entity_type, $storage); + + $this->current_user = $current_user; + $this->entityManager = $entity_manager; + $this->access_view = $access_view; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('entity.manager')->getStorage($entity_type->id()), + $container->get('current_user'), + $container->get('entity.manager'), + $container->get('access_check.taxonomy.taxonomy_overview') + ); + } + + /** * {@inheritdoc} */ public function getFormId() { @@ -36,16 +91,23 @@ public function getDefaultOperations(EntityInterface $entity) { $operations['edit']['title'] = t('Edit vocabulary'); } - $operations['list'] = [ - 'title' => t('List terms'), - 'weight' => 0, - 'url' => $entity->urlInfo('overview-form'), - ]; - $operations['add'] = [ - 'title' => t('Add terms'), - 'weight' => 10, - 'url' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $entity->id()]), - ]; + if ($this->access_view->access($this->current_user)) { + $operations['list'] = [ + 'title' => t('List terms'), + 'weight' => 0, + 'url' => $entity->urlInfo('overview-form'), + ]; + } + + $taxonomy_term_access_control_handler = $this->entityManager->getAccessControlHandler('taxonomy_term'); + if ($taxonomy_term_access_control_handler->createAccess($entity->id())) { + $operations['add'] = [ + 'title' => t('Add terms'), + 'weight' => 10, + 'url' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $entity->id()]), + ]; + } + unset($operations['delete']); return $operations; @@ -66,6 +127,11 @@ public function buildHeader() { public function buildRow(EntityInterface $entity) { $row['label'] = $entity->label(); $row['description']['data'] = ['#markup' => $entity->getDescription()]; + + if ($this->current_user->hasPermission('administer vocabularies')) { + $header['weight'] = t('Weight'); + } + return $row + parent::buildRow($entity); } @@ -80,7 +146,12 @@ public function render() { unset($this->weightKey); } $build = parent::render(); - $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', [':link' => \Drupal::url('entity.taxonomy_vocabulary.add_form')]); + if ($this->current_user->hasPermission('administer vocabularies')) { + $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', ['@link' => \Drupal::url('entity.taxonomy_vocabulary.add_form')]); + } + else { + $build['table']['#empty'] = t('No vocabularies available.'); + } return $build; } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 4a93989..eb88727 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -75,6 +75,17 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) { case 'entity.taxonomy_vocabulary.overview_form': $vocabulary = $route_match->getParameter('taxonomy_vocabulary'); + if (!\Drupal::currentUser()->hasPermission('administer taxonomy')) { + // There is no drag-and-drop handles. + switch ($vocabulary->getHierarchy()) { + case TAXONOMY_HIERARCHY_DISABLED: + return '

' . t('%capital_name contains the following terms.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '

'; + case TAXONOMY_HIERARCHY_SINGLE: + return '

' . t('%capital_name contains terms grouped under parent terms', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '

'; + case TAXONOMY_HIERARCHY_MULTIPLE: + return '

' . t('%capital_name contains terms with multiple parents.', ['%capital_name' => Unicode::ucfirst($vocabulary->label())]) . '

'; + } + } switch ($vocabulary->getHierarchy()) { case VocabularyInterface::HIERARCHY_DISABLED: return '

' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', ['%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label()]) . '

'; diff --git a/core/modules/taxonomy/taxonomy.permissions.yml b/core/modules/taxonomy/taxonomy.permissions.yml index d485949..bb71e93 100644 --- a/core/modules/taxonomy/taxonomy.permissions.yml +++ b/core/modules/taxonomy/taxonomy.permissions.yml @@ -1,5 +1,9 @@ administer taxonomy: title: 'Administer vocabularies and terms' +access taxonomy overview: + title: 'Access the taxonomy vocabulary overview page' + description: 'Get an overview of all taxonomy vocabularies.' + permission_callbacks: - Drupal\taxonomy\TaxonomyPermissions::permissions diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml index 8a3bd1a..94c8727 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -4,7 +4,7 @@ entity.taxonomy_vocabulary.collection: _entity_list: 'taxonomy_vocabulary' _title: 'Taxonomy' requirements: - _permission: 'administer taxonomy' + _permission: 'access taxonomy overview+administer taxonomy' entity.taxonomy_term.add_form: path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add' @@ -74,7 +74,7 @@ entity.taxonomy_vocabulary.overview_form: _form: 'Drupal\taxonomy\Form\OverviewTerms' _title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle' requirements: - _entity_access: 'taxonomy_vocabulary.view' + _permission: 'access taxonomy overview+administer taxonomy' 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 a8153ed..b521c7f 100644 --- a/core/modules/taxonomy/taxonomy.services.yml +++ b/core/modules/taxonomy/taxonomy.services.yml @@ -4,3 +4,7 @@ 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 } diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php index 3ba8868..5059ec8 100644 --- a/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php +++ b/core/modules/taxonomy/tests/src/Functional/VocabularyPermissionsTest.php @@ -16,6 +16,34 @@ protected function setUp() { } /** + * Create, edit and delete a vocabulary via the user interface. + */ + public function testVocabularyPermissionsVocabulary() { + // VocabularyTest.php already tests for user with "administer taxonomy" + // permission. + + // Test as user without proper permissions. + $authenticated_user = $this->drupalCreateUser([]); + $this->drupalLogin($authenticated_user); + + // Visit the main taxonomy administration page. + $this->drupalGet('admin/structure/taxonomy'); + $this->assertResponse(403, 'Vocabulary overview open failed.'); + + // Test as user with "access taxonomy overview" permissions. + $proper_user = $this->drupalCreateUser(['access taxonomy overview']); + $this->drupalLogin($proper_user); + + // Visit the main taxonomy administration page. + $this->drupalGet('admin/structure/taxonomy'); + $this->assertResponse(200); + $this->assertText(t('Vocabulary name'), 'Vocabulary overview opened successfully.'); + + // Create a new vocabulary. + $this->assertNoLink(t('Add vocabulary')); + } + + /** * Create, edit and delete a taxonomy term via the user interface. */ public function testVocabularyPermissionsTaxonomyTerm() { @@ -62,8 +90,35 @@ public function testVocabularyPermissionsTaxonomyTerm() { $this->drupalPostForm(NULL, NULL, t('Delete')); $this->assertRaw(t('Deleted term %name.', ['%name' => $edit['name[0][value]']]), 'Term deleted.'); + // Test as user with "create" permissions. + $user = $this->drupalCreateUser(["access taxonomy overview", "create terms in {$vocabulary->id()}"]); + $this->drupalLogin($user); + + // Visit the main taxonomy administration page. + $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add'); + $this->assertResponse(200); + $this->assertFieldByName('name[0][value]', NULL, 'Add taxonomy term form opened successfully.'); + + // Submit the term. + $edit = []; + $edit['name[0][value]'] = $this->randomMachineName(); + + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertText(t('Created new term @name.', ['@name' => $edit['name[0][value]']]), 'Term created successfully.'); + + $terms = taxonomy_term_load_multiple_by_name($edit['name[0][value]']); + $term = reset($terms); + + // Edit the term. + $this->drupalGet('taxonomy/term/' . $term->id() . '/edit'); + $this->assertResponse(403, 'Edit taxonomy term form open failed.'); + + // Delete the vocabulary. + $this->drupalGet('taxonomy/term/' . $term->id() . '/delete'); + $this->assertResponse(403, 'Delete taxonomy term form open failed.'); + // Test as user with "edit" permissions. - $user = $this->drupalCreateUser(["edit terms in {$vocabulary->id()}"]); + $user = $this->drupalCreateUser(["access taxonomy overview", "edit terms in {$vocabulary->id()}"]); $this->drupalLogin($user); // Visit the main taxonomy administration page. @@ -91,7 +146,7 @@ public function testVocabularyPermissionsTaxonomyTerm() { $this->assertResponse(403, 'Delete taxonomy term form open failed.'); // Test as user with "delete" permissions. - $user = $this->drupalCreateUser(["delete terms in {$vocabulary->id()}"]); + $user = $this->drupalCreateUser(["access taxonomy overview", "delete terms in {$vocabulary->id()}"]); $this->drupalLogin($user); // Visit the main taxonomy administration page.