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); }