diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteTagsWidget.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteTagsWidget.php
index 6d46c2f..b7dc3ac 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteTagsWidget.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteTagsWidget.php
@@ -57,15 +57,33 @@ public function elementValidate($element, &$form_state, $form) {
$match = $handler->validateAutocompleteInput($input, $element, $form_state, $form, !$auto_create);
}
+ $target_bundles = $this->getSelectionHandlerSetting('target_bundles');
+
if ($match) {
$value[] = array('target_id' => $match);
}
- elseif ($auto_create && (count($this->getSelectionHandlerSetting('target_bundles')) == 1 || count($bundles) == 1)) {
- // Auto-create item. see entity_reference_field_presave().
- $value[] = array(
- 'target_id' => 0,
- 'entity' => $this->createNewEntity($input, $element['#autocreate_uid']),
- );
+ elseif ($auto_create && (count($target_bundles) == 1 || count($bundles) == 1)) {
+ $entity_manager = \Drupal::entityManager();
+ $target_type = $this->getFieldSetting('target_type');
+
+ // Get the bundle.
+ if (!empty($target_bundles)) {
+ $bundle = reset($target_bundles);
+ }
+ else {
+ $bundle = reset($bundles);
+ }
+
+ if ($entity_manager->getAccessController($target_type)->createAccess($bundle)) {
+ // Auto-create item. see entity_reference_field_presave().
+ $value[] = array(
+ 'target_id' => 0,
+ 'entity' => $this->createNewEntity($input, $element['#autocreate_uid']),
+ );
+ }
+ else {
+ form_set_error($element['#field_name'], t('You do not have permission to create term: %match', array('%match' => $input)));
+ }
}
}
};
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteWidget.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteWidget.php
index fb2b575..a9ff84f 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteWidget.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/widget/AutocompleteWidget.php
@@ -73,17 +73,27 @@ public function elementValidate($element, &$form_state, $form) {
$value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form, !$auto_create);
}
- if (!$value && $auto_create && (count($this->getSelectionHandlerSetting('target_bundles')) == 1)) {
- // Auto-create item. see entity_reference_field_presave().
- $value = array(
- 'target_id' => 0,
- 'entity' => $this->createNewEntity($element['#value'], $element['#autocreate_uid']),
- // Keep the weight property.
- '_weight' => $element['#weight'],
- );
- // Change the element['#parents'], so in form_set_value() we
- // populate the correct key.
- array_pop($element['#parents']);
+ $target_bundles = $this->getSelectionHandlerSetting('target_bundles');
+ if (!$value && $auto_create && (count($target_bundles) == 1)) {
+ $entity_manager = \Drupal::entityManager();
+ $target_type = $this->getFieldSetting('target_type');
+ $bundle = reset($target_bundles);
+
+ if ($entity_manager->getAccessController($target_type)->createAccess($bundle)) {
+ // Auto-create item. see entity_reference_field_presave().
+ $value = array(
+ 'target_id' => 0,
+ 'entity' => $this->createNewEntity($element['#value'], $element['#autocreate_uid']),
+ // Keep the weight property.
+ '_weight' => $element['#weight'],
+ );
+ // Change the element['#parents'], so in form_set_value() we
+ // populate the correct key.
+ array_pop($element['#parents']);
+ }
+ else {
+ form_set_error($element['#field_name'], t('You do not have permission to create term: %match', array('%match' => $element['#value'])));
+ }
}
}
form_set_value($element, $value, $form_state);
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
index ee55eb9..e6a4e31 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
@@ -66,7 +66,7 @@ function testNodeAccessBasic() {
$titles = array(); // Titles keyed by nid
$private_nodes = array(); // Array of nids marked private.
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/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php
new file mode 100644
index 0000000..db73156
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php
@@ -0,0 +1,36 @@
+hasPermission('administer taxonomy') || $account->hasPermission('access taxonomy overview');
+
+ return $access ? static::ALLOW : static::DENY;
+ }
+
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
index d79af40..79a0c20 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
@@ -202,14 +202,22 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
$destination = drupal_get_destination();
$row_position = 0;
// Build the actual form.
+ $access_controller = \Drupal::entityManager()->getAccessController('taxonomy_term');
+ if ($access_controller->createAccess($taxonomy_vocabulary->id())) {
+ $empty = $this->t('No terms available. Add term.', array('@link' => url('admin/structure/taxonomy/manage/' . $taxonomy_vocabulary->id() . '/add')));
+ }
+ 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' => url('admin/structure/taxonomy/manage/' . $taxonomy_vocabulary->id() . '/add'))),
+ '#empty' => $empty,
'#attributes' => array(
'id' => 'taxonomy',
),
);
+ $change_weight_access = TRUE;
+ $operations_access = TRUE;
foreach ($current_page as $key => $term) {
$uri = $term->uri();
$edit_uri = $term->uri('edit-form');
@@ -255,28 +263,38 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
),
);
}
- $form['terms'][$key]['weight'] = array(
- '#type' => 'weight',
- '#delta' => $delta,
- '#title_display' => 'invisible',
- '#title' => $this->t('Weight for added term'),
- '#default_value' => $term->weight->value,
- '#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'] = array(
+ '#type' => 'weight',
+ '#delta' => $delta,
+ '#title_display' => 'invisible',
+ '#title' => $this->t('Weight for added term'),
+ '#default_value' => $term->weight->value,
+ '#attributes' => array(
+ 'class' => array('term-weight'),
+ ),
+ '#access' => $edit_access,
+ );
+ }
+ $operations = array();
+
+ if ($edit_access) {
+ $operations['edit'] = array(
'title' => $this->t('edit'),
'href' => $edit_uri['path'],
'query' => $destination,
- ),
- 'delete' => array(
+ );
+ }
+ if ($term->access('delete')) {
+ $operations['delete'] = array(
'title' => $this->t('delete'),
'href' => $uri['path'] . '/delete',
'query' => $destination,
- ),
- );
+ );
+ }
if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($term)) {
$operations['translate'] = array(
'title' => $this->t('translate'),
@@ -284,10 +302,14 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
'query' => $destination,
);
}
- $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) {
@@ -317,30 +339,38 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
$row_position++;
}
- if ($parent_fields) {
- $form['terms']['#tabledrag'][] = array(
- 'match',
- 'parent',
- 'term-parent',
- 'term-parent',
- 'term-id',
- FALSE,
- );
- $form['terms']['#tabledrag'][] = array(
- 'depth',
- 'group',
- 'term-depth',
- NULL,
- NULL,
- FALSE
- );
- $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
- $form['terms']['#attached']['js'][] = array(
- 'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
- 'type' => 'setting',
- );
+ $form['terms']['#header'] = array($this->t('Name'));
+ if ($change_weight_access) {
+ $form['terms']['#header'][] = $this->t('Weight');
+ if ($parent_fields) {
+ $form['terms']['#tabledrag'][] = array(
+ 'match',
+ 'parent',
+ 'term-parent',
+ 'term-parent',
+ 'term-id',
+ FALSE,
+ );
+ $form['terms']['#tabledrag'][] = array(
+ 'depth',
+ 'group',
+ 'term-depth',
+ NULL,
+ NULL,
+ FALSE
+ );
+ $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
+ $form['terms']['#attached']['js'][] = array(
+ 'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
+ 'type' => 'setting',
+ );
+ }
+ $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
+ }
+
+ if ($operations_access) {
+ $form['terms']['#header'][] = $this->t('Operations');
}
- $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
@@ -348,11 +378,13 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
+ '#access' => $change_weight_access,
);
$form['actions']['reset_alphabetical'] = array(
'#type' => 'submit',
'#submit' => array(array($this, 'submitReset')),
'#value' => $this->t('Reset to alphabetical'),
+ '#access' => $change_weight_access,
);
$form_state['redirect'] = array(current_path(), ($page ? array('query' => array('page' => $page)) : array()));
}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
index ca98dbb..e644fce 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
@@ -41,7 +41,7 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
- return $account->hasPermission('administer taxonomy');
+ return $account->hasPermission("create terms in {$entity_bundle}") || $account->hasPermission('administer taxonomy');
}
}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
index e127dbb..dd873af 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
@@ -21,6 +21,33 @@ public static function getInfo() {
}
/**
+ * 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() {
@@ -66,8 +93,35 @@ function testVocabularyPermissionsTaxonomyTerm() {
$this->drupalPost(NULL, NULL, t('Delete'));
$this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name'])), '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.
@@ -91,7 +145,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/lib/Drupal/taxonomy/VocabularyAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
index 56b92ed..55a47eb 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
@@ -22,7 +22,10 @@ class VocabularyAccessController extends EntityAccessController {
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
- return $account->hasPermission('administer taxonomy');
+ switch ($operation) {
+ case 'view': return $account->hasPermission('administer taxonomy') || $account->hasPermission('access taxonomy overview');
+ default: return $account->hasPermission('administer taxonomy');
+ }
}
/**
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
index 3ce091b..5f32e57 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
@@ -45,12 +45,15 @@ public function getOperations(EntityInterface $entity) {
'options' => $uri['options'],
'weight' => 0,
);
- $operations['add'] = array(
- 'title' => t('add terms'),
- 'href' => $uri['path'] . '/add',
- 'options' => $uri['options'],
- 'weight' => 10,
- );
+ $access_controller = \Drupal::entityManager()->getAccessController('taxonomy_term');
+ if ($access_controller->createAccess($entity->bundle())) {
+ $operations['add'] = array(
+ 'title' => t('add terms'),
+ 'href' => $uri['path'] . '/add',
+ 'options' => $uri['options'],
+ 'weight' => 10,
+ );
+ }
unset($operations['delete']);
return $operations;
@@ -61,7 +64,11 @@ public function getOperations(EntityInterface $entity) {
*/
public function buildHeader() {
$header['label'] = t('Vocabulary name');
- $header['weight'] = t('Weight');
+
+ if (user_access('administer vocabularies')) {
+ $header['weight'] = t('Weight');
+ }
+
return $header + parent::buildHeader();
}
@@ -84,7 +91,13 @@ public function render() {
unset($this->weightKey);
}
$build = parent::render();
- $build['#empty'] = t('No vocabularies available. Add vocabulary.', array('@link' => url('admin/structure/taxonomy/add')));
+
+ if (user_access('administer vocabularies')) {
+ $build['#empty'] = t('No vocabularies available. Add vocabulary.', array('@link' => url('admin/structure/taxonomy/add')));
+ }
+ else {
+ $build['#empty'] = t('No vocabularies available.');
+ }
return $build;
}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 0827bef..e91c474 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -92,9 +92,20 @@ function taxonomy_permission() {
'administer taxonomy' => array(
'title' => t('Administer vocabularies and terms'),
),
+ 'access taxonomy overview' => array(
+ 'title' => t('Access the taxonomy overview page'),
+ 'description' => t('Get an overview of terms at admin/structure/taxonomy.', array(
+ '@url' => url('admin/structure/taxonomy'),
+ )),
+ ),
);
foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) {
$permissions += array(
+ 'create terms in ' . $vocabulary->id() => array(
+ 'title' => t('Create terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
+ ),
+ );
+ $permissions += array(
'edit terms in ' . $vocabulary->id() => array(
'title' => t('Edit terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
),
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index 817c6f6..b735b8c 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -3,7 +3,7 @@ taxonomy_vocabulary_list:
defaults:
_entity_list: 'taxonomy_vocabulary'
requirements:
- _permission: 'administer taxonomy'
+ _access_taxonomy_overview: 'TRUE'
taxonomy_term_add:
pattern: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add'
diff --git a/core/modules/taxonomy/taxonomy.services.yml b/core/modules/taxonomy/taxonomy.services.yml
new file mode 100644
index 0000000..ed754a6
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.services.yml
@@ -0,0 +1,5 @@
+services:
+ access_check.taxonomy.taxonomy_overview:
+ class: Drupal\taxonomy\Access\TaxonomyOverviewAccessCheck
+ tags:
+ - { name: access_check }
\ No newline at end of file
diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
index c7ec4e0..1e31af2 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
@@ -40,6 +40,8 @@ public static function getInfo() {
}
function setUp() {
+ $this->permissions[] = 'administer taxonomy';
+
parent::setUp();
// Create two content types. One will have an autocomplete tagging field,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php
index 0abb524..ede9897 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php
+++ b/core/modules/views/lib/Drupal/views/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 @@
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);
}