diff --git a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php
index 91547c4..f0e01dd 100644
--- a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php
+++ b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php
@@ -99,7 +99,7 @@ function testNodeAccessBasic() {
// Array of nids marked private.
$private_nodes = [];
for ($i = 0; $i < $num_simple_users; $i++) {
- $simple_users[$i] = $this->drupalCreateUser(array('access content', 'create article content'));
+ $simple_users[$i] = $this->drupalCreateUser(array('access content', 'create article content', 'create terms in tags'));
}
foreach ($simple_users as $this->webUser) {
$this->drupalLogin($this->webUser);
diff --git a/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php b/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php
index e69de29..d529aa5 100644
--- a/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php
+++ b/core/modules/taxonomy/src/Access/TaxonomyOverviewAccessCheck.php
@@ -0,0 +1,28 @@
+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 556e79b..ab98b0b 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -27,6 +27,13 @@ class OverviewTerms extends FormBase {
protected $moduleHandler;
/**
+ * The entity manager.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface
+ */
+ protected $entityManager;
+
+ /**
* The term storage controller.
*
* @var \Drupal\taxonomy\TermStorageInterface
@@ -43,6 +50,7 @@ class OverviewTerms extends FormBase {
*/
public function __construct(ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager) {
$this->moduleHandler = $module_handler;
+ $this->entityManager = $entity_manager;
$this->storageController = $entity_manager->getStorage('taxonomy_term');
}
@@ -204,14 +212,23 @@ 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'] = array(
'#type' => 'table',
'#header' => array($this->t('Name'), $this->t('Weight'), $this->t('Operations')),
- '#empty' => $this->t('No terms available. Add term.', array('@link' => $this->url('entity.taxonomy_term.add_form', array('taxonomy_vocabulary' => $taxonomy_vocabulary->id())))),
+ '#empty' => $empty,
'#attributes' => array(
'id' => 'taxonomy',
),
);
+ $change_weight_access = TRUE;
+ $operations_access = TRUE;
foreach ($current_page as $key => $term) {
/** @var $term \Drupal\Core\Entity\EntityInterface */
$form['terms'][$key]['#term'] = $term;
@@ -256,28 +273,36 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
),
);
}
- $form['terms'][$key]['weight'] = array(
- '#type' => 'weight',
- '#delta' => $delta,
- '#title' => $this->t('Weight for added term'),
- '#title_display' => 'invisible',
- '#default_value' => $term->getWeight(),
- '#attributes' => array(
- 'class' => array('term-weight'),
- ),
- );
- $operations = array(
- 'edit' => array(
+ $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 = array();
+
+ if ($edit_access) {
+ $operations['edit'] = [
'title' => $this->t('Edit'),
'query' => $destination,
'url' => $term->urlInfo('edit-form'),
- ),
- 'delete' => array(
+ ];
+ }
+ 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'] = array(
'title' => $this->t('Translate'),
@@ -285,10 +310,14 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
'url' => $term->urlInfo('drupal:content-translation-overview'),
);
}
- $form['terms'][$key]['operations'] = array(
- '#type' => 'operations',
- '#links' => $operations,
- );
+
+ $operations_access &= count($operations) > 0;
+ if (count($operations)) {
+ $form['terms'][$key]['operations'] = array(
+ '#type' => 'operations',
+ '#links' => $operations,
+ );
+ }
$form['terms'][$key]['#attributes']['class'] = array();
if ($parent_fields) {
@@ -318,32 +347,40 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
$row_position++;
}
- if ($parent_fields) {
- $form['terms']['#tabledrag'][] = array(
- 'action' => 'match',
- 'relationship' => 'parent',
- 'group' => 'term-parent',
- 'subgroup' => 'term-parent',
- 'source' => 'term-id',
- 'hidden' => FALSE,
- );
- $form['terms']['#tabledrag'][] = array(
- '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']['#header'] = array($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' => 'order',
+ 'relationship' => 'sibling',
+ 'group' => 'term-weight',
];
}
- $form['terms']['#tabledrag'][] = array(
- 'action' => 'order',
- 'relationship' => 'sibling',
- 'group' => 'term-weight',
- );
+
+ if ($operations_access) {
+ $form['terms']['#header'][] = $this->t('Operations');
+ }
if ($taxonomy_vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
@@ -351,11 +388,13 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
+ '#access' => $change_weight_access,
);
$form['actions']['reset_alphabetical'] = array(
'#type' => 'submit',
'#submit' => array('::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 6b7b841..24dc3dc 100644
--- a/core/modules/taxonomy/src/TaxonomyPermissions.php
+++ b/core/modules/taxonomy/src/TaxonomyPermissions.php
@@ -10,6 +10,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;
/**
@@ -53,19 +54,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/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
index 00df789..1827cb0 100644
--- a/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
@@ -15,6 +15,33 @@
class VocabularyPermissionsTest extends TaxonomyTestBase {
/**
+ * Create, edit and delete a vocabulary via the user interface.
+ */
+ function testVocabularyPermissionsVocabulary() {
+ // VocabularyTest.php already tests for user with "administer taxonomy" permissions.
+
+ // Test as user without proper permissions.
+ $authenticated_user = $this->drupalCreateUser(array());
+ $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(array('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.
*/
function testVocabularyPermissionsTaxonomyTerm() {
@@ -57,8 +84,35 @@ function testVocabularyPermissionsTaxonomyTerm() {
$this->drupalPostForm(NULL, NULL, t('Delete'));
$this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name[0][value]'])), 'Term deleted.');
+ // Test as user with "create" permissions.
+ $user = $this->drupalCreateUser(array("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->assertField('edit-name', 'Add taxonomy term form opened successfully.');
+
+ // Submit the term.
+ $edit = array();
+ $edit['name'] = $this->randomName();
+
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('Created new term %name.', array('%name' => $edit['name'])), 'Term created successfully.');
+
+ $terms = taxonomy_term_load_multiple_by_name($edit['name']);
+ $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(array("edit terms in {$vocabulary->id()}"));
+ $user = $this->drupalCreateUser(array("access taxonomy overview", "edit terms in {$vocabulary->id()}"));
$this->drupalLogin($user);
// Visit the main taxonomy administration page.
@@ -82,7 +136,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
$this->assertResponse(403, 'Delete taxonomy term form open failed.');
// Test as user with "delete" permissions.
- $user = $this->drupalCreateUser(array("delete terms in {$vocabulary->id()}"));
+ $user = $this->drupalCreateUser(array("access taxonomy overview", "delete terms in {$vocabulary->id()}"));
$this->drupalLogin($user);
// Visit the main taxonomy administration page.
diff --git a/core/modules/taxonomy/src/VocabularyListBuilder.php b/core/modules/taxonomy/src/VocabularyListBuilder.php
index fe196c9..d91df71 100644
--- a/core/modules/taxonomy/src/VocabularyListBuilder.php
+++ b/core/modules/taxonomy/src/VocabularyListBuilder.php
@@ -9,8 +9,13 @@
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 Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a class to build a listing of taxonomy vocabulary entities.
@@ -25,6 +30,51 @@ 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\Entity\EntityManagerInterface $entity_manager
+ * The entity manager service.
+ * @param \Drupal\Core\Session\AccountInterface $current_user
+ * The current user.
+ */
+ public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, AccountInterface $current_user, EntityManagerInterface $entity_manager) {
+ parent::__construct($entity_type, $storage);
+
+ $this->current_user = $current_user;
+ $this->entityManager = $entity_manager;
+ }
+
+ /**
+ * {@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')
+ );
+ }
+
+ /**
* {@inheritdoc}
*/
public function getFormId() {
@@ -46,11 +96,14 @@ public function getDefaultOperations(EntityInterface $entity) {
'weight' => 0,
'url' => $entity->urlInfo('overview-form'),
);
- $operations['add'] = array(
- 'title' => t('Add terms'),
- 'weight' => 10,
- 'url' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $entity->id()]),
- );
+ $taxonomy_term_access_control_handler = $this->entityManager->getAccessControlHandler('taxonomy_term');
+ if ($taxonomy_term_access_control_handler->createAccess($entity->bundle())) {
+ $operations['add'] = array(
+ 'title' => t('Add terms'),
+ 'weight' => 10,
+ 'url' => Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $entity->id()]),
+ );
+ }
unset($operations['delete']);
return $operations;
@@ -61,6 +114,11 @@ public function getDefaultOperations(EntityInterface $entity) {
*/
public function buildHeader() {
$header['label'] = t('Vocabulary name');
+
+ if ($this->current_user->hasPermission('administer vocabularies')) {
+ $header['weight'] = t('Weight');
+ }
+
return $header + parent::buildHeader();
}
@@ -83,7 +141,14 @@ public function render() {
unset($this->weightKey);
}
$build = parent::render();
- $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', array('@link' => \Drupal::url('entity.taxonomy_vocabulary.add_form')));
+
+ if ($this->current_user->hasPermission('administer vocabularies')) {
+ $build['table']['#empty'] = t('No vocabularies available. Add vocabulary.', array('@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.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 a7a75f3..bd00f33 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'
+ _access_taxonomy_overview: 'TRUE'
entity.taxonomy_term.add_form:
path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add'
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/views/src/Tests/Wizard/WizardTestBase.php b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
index f610a5f..686d6cb 100644
--- a/core/modules/views/src/Tests/Wizard/WizardTestBase.php
+++ b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
@@ -15,6 +15,13 @@
abstract class WizardTestBase extends ViewTestBase {
/**
+ * Permissions for this user to test with.
+ *
+ * @var array
+ */
+ protected $permissions = array();
+
+ /**
* Modules to enable.
*
* @var array
@@ -24,8 +31,10 @@
protected function setUp() {
parent::setUp();
+ $this->permissions = array_merge($this->permissions, array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
+
// Create and log in a user with administer views permission.
- $views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
+ $views_admin = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($views_admin);
}