diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
index 6b7d00d..f5ded7f 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', 'administer taxonomy'));
}
foreach ($simple_users as $this->webUser) {
$this->drupalLogin($this->webUser);
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
index 6f99dae..e0e432e 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaMarkupTest.php
@@ -99,7 +99,7 @@ function testDrupalRdfaAttributes() {
*/
function testAttributesInMarkupFile() {
// Create a user to post the image.
- $admin_user = $this->drupalCreateUser(array('edit own article content', 'revert article revisions', 'administer content types'));
+ $admin_user = $this->drupalCreateUser(array('edit own article content', 'revert article revisions', 'administer content types', 'administer taxonomy'));
$this->drupalLogin($admin_user);
$langcode = LANGUAGE_NOT_SPECIFIED;
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
new file mode 100644
index 0000000..019a4b4
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
@@ -0,0 +1,202 @@
+ 'Taxonomy vocabulary permissions',
+ 'description' => 'Test the taxonomy vocabulary permissions.',
+ 'group' => 'Taxonomy',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ /**
+ * Create, edit and delete a vocabulary via the user interface.
+ */
+ function testVocabularyPermissionsTaxonomyOverview() {
+ // Test as admin user.
+ $admin_user = $this->drupalCreateUser(array('administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Visit the main taxonomy administration page.
+ $this->drupalGet('admin/structure/taxonomy');
+ $this->assertResponse(200, 'Vocabulary overview opened successfully.');
+ $this->assertText(t('Vocabulary name'), 'Vocabulary overview opened successfully.');
+
+ // Create a new vocabulary.
+ $this->clickLink(t('Add vocabulary'));
+
+ $edit = array();
+ $edit['name'] = $this->randomName();
+ $edit['description'] = $this->randomName();
+ $vid = drupal_strtolower($this->randomName());
+ $edit['vid'] = $vid;
+
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.');
+
+ // Edit the vocabulary.
+ $this->drupalGet('admin/structure/taxonomy');
+ $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
+ $this->clickLink(t('edit vocabulary'));
+
+ $edit = array();
+ $edit['name'] = $this->randomName();
+
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->drupalGet('admin/structure/taxonomy');
+ $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
+
+ $vocabulary = taxonomy_vocabulary_load($vid);
+
+ // Delete the vocabulary.
+ $edit = array();
+ $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->id() . '/edit', $edit, t('Delete'));
+ $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), '[confirm deletion] Asks for confirmation.');
+
+ // Confirm deletion.
+ $this->drupalPost(NULL, NULL, t('Delete'));
+ $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), 'Vocabulary deleted.');
+ entity_get_controller('taxonomy_vocabulary')->resetCache();
+ $this->assertFalse(taxonomy_vocabulary_load($vocabulary->id()), t('Vocabulary not found.'));
+
+ // 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 proper 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->clickLink(t('Add vocabulary'));
+ $this->assertResponse(403, 'Add vocabulary form open failed.');
+ }
+
+ /**
+ * Create, edit and delete a taxonomy term via the user interface.
+ */
+ function testVocabularyPermissionsTaxonomyTerm() {
+ // Vocabulary used for creating, removing and editing terms.
+ $vocabulary = $this->createVocabulary();
+
+ // Reset to get proper permissions.
+ drupal_static_reset();
+
+ // Test as admin user.
+ $admin_user = $this->drupalCreateUser(array('administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Visit the main taxonomy administration page.
+ $this->drupalGet('admin/structure/taxonomy/' . $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(200);
+ $this->assertText($edit['name'], 'Edit taxonomy term form opened successfully.');
+
+ $edit['name'] = $this->randomName();
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('Updated term %name.', array('%name' => $edit['name'])), 'Term updated successfully.');
+
+ // Delete the vocabulary.
+ $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
+ $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $edit['name'])), 'Delete taxonomy term form opened successfully.');
+
+ // Confirm deletion.
+ $this->drupalPost(NULL, NULL, t('Delete'));
+ $this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name'])), 'Term deleted.');
+
+ // Test as user with proper permissions.
+ $user = $this->drupalCreateUser(array("administer taxonomy", "edit terms in {$vocabulary->id()}", "delete terms in {$vocabulary->id()}"));
+ $this->drupalLogin($user);
+
+ // Visit the main taxonomy administration page.
+ $this->drupalGet('admin/structure/taxonomy/' . $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(200);
+ $this->assertText($edit['name'], 'Edit taxonomy term form opened successfully.');
+
+ $edit = array();
+ $edit['name'] = $this->randomName();
+
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('Updated term %name.', array('%name' => $edit['name'])), 'Term updated successfully.');
+
+ // Delete the vocabulary.
+ $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
+ $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $edit['name'])), 'Delete taxonomy term form opened successfully.');
+
+ // Confirm deletion.
+ $this->drupalPost(NULL, NULL, t('Delete'));
+ $this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name'])), 'Term deleted.');
+
+ // Test as user without proper permissions.
+ $user = $this->drupalCreateUser();
+ $this->drupalLogin($user);
+
+ // Visit the main taxonomy administration page.
+ $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->id() . '/add');
+ $this->assertResponse(403, 'Add taxonomy term form open failed.');
+
+ // Create a test term.
+ $term = $this->createTerm($vocabulary);
+
+ // 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->assertNoRaw(t('Are you sure you want to delete the term %name?', array('%name' => $term->name)), 'Delete taxonomy term form opened successfully.');
+ }
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
index b1ec119..bc0c1a1 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
@@ -22,7 +22,7 @@ class VocabularyAccessController extends EntityAccessController {
* Implements \Drupal\Core\Entity\EntityAccessControllerInterface::viewAccess().
*/
public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer taxonomy', $account);
+ return taxonomy_overview_access($account);
}
/**
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index 6305e48..fa49b28 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -30,18 +30,23 @@ function taxonomy_overview_vocabularies($form) {
'#default_value' => $vocabulary->weight,
);
$links = array();
- $links['edit'] = array(
- 'title' => t('edit vocabulary'),
- 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/edit",
- );
+ if ($vocabulary->access('update')) {
+ $links['edit'] = array(
+ 'title' => t('edit vocabulary'),
+ 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/edit",
+ );
+ }
$links['list'] = array(
'title' => t('list terms'),
'href' => "admin/structure/taxonomy/{$vocabulary->id()}",
);
- $links['add'] = array(
- 'title' => t('add terms'),
- 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/add",
- );
+ // @TODO: Should be replaced with new create term permission.
+ if (entity_page_create_access('taxonomy_term')) {
+ $links['add'] = array(
+ 'title' => t('add terms'),
+ 'href' => "admin/structure/taxonomy/{$vocabulary->id()}/add",
+ );
+ }
$form[$vocabulary->id()]['operations'] = array(
'#type' => 'operations',
'#links' => $links,
@@ -285,10 +290,14 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) {
'#default_value' => $term->weight,
);
}
- $operations = array(
- 'edit' => array('title' => t('edit'), 'href' => 'taxonomy/term/' . $term->tid . '/edit', 'query' => $destination),
- 'delete' => array('title' => t('delete'), 'href' => 'taxonomy/term/' . $term->tid . '/delete', 'query' => $destination),
- );
+
+ $operations = array();
+ if ($term->access('update')) {
+ $operations['edit'] = array('title' => t('edit'), 'href' => 'taxonomy/term/' . $term->tid . '/edit', 'query' => $destination);
+ }
+ if ($term->access('delete')) {
+ $operations['delete'] = array('title' => t('delete'), 'href' => 'taxonomy/term/' . $term->tid . '/delete', 'query' => $destination);
+ }
if (module_invoke('translation_entity', 'translate_access', $term)) {
$operations['translate'] = array(
'title' => t('translate'),
@@ -307,7 +316,12 @@ function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) {
$form['#page_entries'] = $page_entries;
$form['#back_step'] = $back_step;
$form['#forward_step'] = $forward_step;
- $form['#empty_text'] = t('No terms available. Add term.', array('@link' => url('admin/structure/taxonomy/' . $vocabulary->id() . '/add')));
+
+ // @TODO: Should be replaced with new create term permission.
+ $form['#empty_text'] = t('No terms available.');
+ if (entity_page_create_access('taxonomy_term')) {
+ $form['#empty_text'] .= ' ' . l(t('Add term'), 'admin/structure/taxonomy/' . $vocabulary->id() . '/add');
+ }
if ($vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 43d2ac8..8e60c5e 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -89,8 +89,15 @@ 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('Access the taxonomy overview page at admin/structure/taxonomy.', array(
+ '@url' => url('admin/structure/taxonomy'),
+ )),
+ ),
);
foreach (taxonomy_vocabulary_load_multiple() as $vocabulary) {
+ // @TODO: Should be extended with new create term permission.
$permissions += array(
'edit terms in ' . $vocabulary->id() => array(
'title' => t('Edit terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
@@ -260,7 +267,7 @@ function taxonomy_menu() {
'description' => 'Manage tagging, categorization, and classification of your content.',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_overview_vocabularies'),
- 'access arguments' => array('administer taxonomy'),
+ 'access callback' => 'taxonomy_overview_access',
'file' => 'taxonomy.admin.inc',
);
$items['admin/structure/taxonomy/list'] = array(
@@ -349,7 +356,7 @@ function taxonomy_menu() {
'title' => 'Edit',
'page callback' => 'entity_get_form',
'page arguments' => array(3),
- 'access callback' => 'entity_page_access',
+ 'access callback' => 'entity_page_access', // @TODO: Should be replaced with new create term permission.
'access arguments' => array(3, 'update'),
'type' => MENU_LOCAL_TASK,
'file' => 'taxonomy.admin.inc',
@@ -369,6 +376,21 @@ function taxonomy_menu() {
}
/**
+ * Access callback: Checks a user's permission for accessing the taxonomy
+ * overview page.
+ *
+ * @param $account
+ * (optional) The account to check, if not given use currently logged in user.
+ * @return
+ * TRUE if the operation may be performed, FALSE otherwise.
+ *
+ * @see taxonomy_menu()
+ */
+function taxonomy_overview_access($account = NULL) {
+ return user_access("access taxonomy overview", $account) || user_access('administer taxonomy', $account);
+}
+
+/**
* Implements hook_admin_paths().
*/
function taxonomy_admin_paths() {
@@ -1022,6 +1044,15 @@ function taxonomy_field_validate(EntityInterface $entity = NULL, $field, $instan
if (!empty($item['tid']) && $item['tid'] != 'autocreate') {
$tids[] = $item['tid'];
}
+ // Check if the current user has permissions to create new terms.
+ if (empty($item['tid'])) {
+ if (!$item['entity']->access('create')) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'taxonomy_term_reference_illegal_value',
+ 'message' => t('You do not have permission to create new term: %term in field %name.', array('%term' => $item['entity']->name, '%name' => $instance['label'])),
+ );
+ }
+ }
}
if (!empty($tids)) {
$terms = taxonomy_term_load_multiple($tids);
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 6416193..ac20a3a 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
@@ -38,6 +38,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);
}