Commit 3c7447f8 authored by webchick's avatar webchick

Issue #880940 by realityloop, xjm, Jody Lynn, tim.plunkett, smortimore,...

Issue #880940 by realityloop, xjm, Jody Lynn, tim.plunkett, smortimore, dagmar: Added view/revert/delete revisions per content type.
parent 63619f5c
......@@ -16,9 +16,16 @@ class NodeRevisionPermissionsTest extends NodeTestBase {
// Map revision permission names to node revision access ops.
protected $map = array(
'view' => 'view revisions',
'update' => 'revert revisions',
'delete' => 'delete revisions',
'view' => 'view all revisions',
'update' => 'revert all revisions',
'delete' => 'delete all revisions',
);
// Map revision permission names to node type revision access ops.
protected $type_map = array(
'view' => 'view page revisions',
'update' => 'revert page revisions',
'delete' => 'delete page revisions',
);
public static function getInfo() {
......@@ -32,19 +39,28 @@ public static function getInfo() {
function setUp() {
parent::setUp();
// Create a node with several revisions.
$node = $this->drupalCreateNode();
$this->node_revisions[] = $node;
for ($i = 0; $i < 3; $i++) {
// Create a revision for the same nid and settings with a random log.
$revision = clone $node;
$revision->setNewRevision();
$revision->log = $this->randomName(32);
node_save($revision);
$this->node_revisions[] = $revision;
$types = array('page', 'article');
foreach ($types as $type) {
// Create a node with several revisions.
$nodes[$type] = $this->drupalCreateNode(array('type' => $type));
$this->node_revisions[$type][] = $nodes[$type];
for ($i = 0; $i < 3; $i++) {
// Create a revision for the same nid and settings with a random log.
$revision = clone $nodes[$type];
$revision->revision = 1;
$revision->log = $this->randomName(32);
node_save($revision);
$this->node_revisions[$type][] = $revision;
}
}
}
/**
* Tests general revision access permissions.
*/
function testNodeRevisionAccessAnyType() {
// Create three users, one with each revision permission.
foreach ($this->map as $op => $permission) {
// Create the user.
......@@ -64,18 +80,14 @@ function setUp() {
$admin_account = $this->drupalCreateUser(array('access content', 'administer nodes'));
$admin_account->is_admin = TRUE;
$this->accounts['admin'] = $admin_account;
$accounts['admin'] = $admin_account;
// Create a normal account (returns FALSE for all revision permissions).
$normal_account = $this->drupalCreateUser();
$normal_account->op = FALSE;
$this->accounts[] = $normal_account;
}
/**
* Tests the _node_revision_access() function.
*/
function testNodeRevisionAccess() {
$revision = node_revision_load($this->node_revisions[1]->vid);
$accounts[] = $normal_account;
$revision = $this->node_revisions['page'][1];
$parameters = array(
'op' => array_keys($this->map),
......@@ -83,24 +95,70 @@ function testNodeRevisionAccess() {
);
$permutations = $this->generatePermutations($parameters);
foreach ($permutations as $case) {
if (!empty($case['account']->is_admin) || $case['op'] == $case['account']->op) {
$this->assertTrue(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} granted.");
}
else {
$this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} not granted.");
// Skip this test if there are no revisions for the node.
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $revision->nid))->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
if (!empty($case['account']->is_admin) || user_access($this->map[$case['op']], $case['account'])) {
$this->assertTrue(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} granted.");
}
else {
$this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->map[$case['op']]} not granted.");
}
}
}
// Test that access is FALSE for a node administrator with an invalid $node
// or $op parameters.
$admin_account = $this->accounts['admin'];
$admin_account = $accounts['admin'];
$this->assertFalse(_node_revision_access($revision, 'invalid-op', $admin_account), '_node_revision_access() returns FALSE with an invalid op.');
}
/**
* Tests revision access permissions for a specific content type.
*/
function testNodeRevisionAccessPerType() {
// Create three users, one with each revision permission.
foreach ($this->type_map as $op => $permission) {
// Create the user.
$account = $this->drupalCreateUser(
array(
'access content',
'edit any page content',
'delete any page content',
$permission,
)
);
$account->op = $op;
$accounts[] = $account;
}
// Test that the $account parameter defaults to the "logged in" user.
$original_user = $GLOBALS['user'];
$GLOBALS['user'] = $admin_account;
$this->assertTrue(_node_revision_access($revision, 'view'), '_node_revision_access() returns TRUE when used with global user.');
$GLOBALS['user'] = $original_user;
$parameters = array(
'op' => array_keys($this->type_map),
'account' => $accounts,
);
// Test that the accounts have access to the correspoding page revision permissions.
$revision = $this->node_revisions['page'][1];
$permutations = $this->generatePermutations($parameters);
foreach ($permutations as $case) {
// Skip this test if there are no revisions for the node.
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $revision->nid))->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
if (!empty($case['account']->is_admin) || user_access($this->type_map[$case['op']], $case['account'])) {
$this->assertTrue(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} granted.");
}
else {
$this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} not granted.");
}
}
}
// Test that the accounts have no access to the article revisions.
$revision = $this->node_revisions['article'][1];
foreach ($permutations as $case) {
$this->assertFalse(_node_revision_access($revision, $case['op'], $case['account']), "{$this->type_map[$case['op']]} did not grant revision permission for articles.");
}
}
}
......@@ -16,8 +16,8 @@ class NodeRevisionsTest extends NodeTestBase {
public static function getInfo() {
return array(
'name' => 'Node revisions',
'description' => 'Create a node with revisions and test viewing, saving, reverting, and deleting revisions.',
'name' => 'Node revisions by type',
'description' => 'Create a node with revisions and test viewing, saving, reverting, and deleting revisions for users with access for this content type.',
'group' => 'Node',
);
}
......@@ -25,9 +25,17 @@ public static function getInfo() {
function setUp() {
parent::setUp();
// Create and login user.
$web_user = $this->drupalCreateUser(array('view revisions', 'revert revisions', 'edit any page content',
'delete revisions', 'delete any page content', 'administer nodes'));
// Create and log in user.
$web_user = $this->drupalCreateUser(
array(
'view page revisions',
'revert page revisions',
'delete page revisions',
'edit any page content',
'delete any page content'
)
);
$this->drupalLogin($web_user);
// Create initial node.
......@@ -190,3 +198,144 @@ function testNodeRevisionWithoutLogMessage() {
$this->assertTrue(empty($node_revision->log), 'After a new node revision is saved with an empty log message, the log message for the node is empty.');
}
}
/**
* Tests actions against revisions for user with access to all revisions.
*/
class NodeRevisionsAllTestCase extends NodeTestBase {
protected $nodes;
protected $logs;
protected $profile = "standard";
public static function getInfo() {
return array(
'name' => 'Node revisions all',
'description' => 'Create a node with revisions and test viewing, saving, reverting, and deleting revisions for user with access to all.',
'group' => 'Node',
);
}
function setUp() {
parent::setUp();
// Create and log in user.
$web_user = $this->drupalCreateUser(
array(
'view page revisions',
'revert page revisions',
'delete page revisions',
'edit any page content',
'delete any page content'
)
);
$this->drupalLogin($web_user);
// Create an initial node.
$node = $this->drupalCreateNode();
$settings = get_object_vars($node);
$settings['revision'] = 1;
$nodes = array();
$logs = array();
// Get the original node.
$nodes[] = $node;
// Create three revisions.
$revision_count = 3;
for ($i = 0; $i < $revision_count; $i++) {
$logs[] = $settings['log'] = $this->randomName(32);
// Create revision with a random title and body and update variables.
$this->drupalCreateNode($settings);
$node = node_load($node->nid); // Make sure we get revision information.
$settings = get_object_vars($node);
$nodes[] = $node;
}
$this->nodes = $nodes;
$this->logs = $logs;
}
/**
* Checks node revision operations.
*/
function testRevisions() {
$nodes = $this->nodes;
$logs = $this->logs;
// Get last node for simple checks.
$node = $nodes[3];
// Create and login user.
$content_admin = $this->drupalCreateUser(
array(
'view all revisions',
'revert all revisions',
'delete all revisions',
'edit any page content',
'delete any page content'
)
);
$this->drupalLogin($content_admin);
// Confirm the correct revision text appears on "view revisions" page.
$this->drupalGet("node/$node->nid/revisions/$node->vid/view");
$this->assertText($node->body[LANGUAGE_NOT_SPECIFIED][0]['value'], t('Correct text displays for version.'));
// Confirm the correct log message appears on "revisions overview" page.
$this->drupalGet("node/$node->nid/revisions");
foreach ($logs as $log) {
$this->assertText($log, t('Log message found.'));
}
// Confirm that this is the current revision.
$this->assertTrue($node->isCurrentRevision(), 'Third node revision is the current one.');
// Confirm that revisions revert properly.
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
$this->assertRaw(t('@type %title has been reverted back to the revision from %revision-date.',
array(
'@type' => 'Basic page',
'%title' => $nodes[1]->title,
'%revision-date' => format_date($nodes[1]->revision_timestamp)
)),
'Revision reverted.');
$reverted_node = node_load($node->nid);
$this->assertTrue(($nodes[1]->body[LANGUAGE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[LANGUAGE_NOT_SPECIFIED][0]['value']), t('Node reverted correctly.'));
// Confirm that this is not the current version.
$node = node_load($node->nid, $node->vid);
$this->assertFalse($node->isCurrentRevision(), 'Third node revision is not the current one.');
// Confirm revisions delete properly.
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete'));
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.',
array(
'%revision-date' => format_date($nodes[1]->revision_timestamp),
'@type' => 'Basic page',
'%title' => $nodes[1]->title,
)),
'Revision deleted.');
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid',
array(':nid' => $node->nid, ':vid' => $nodes[1]->vid))->fetchField() == 0,
'Revision not found.');
// Set the revision timestamp to an older date to make sure that the
// confirmation message correctly displays the stored revision date.
$old_revision_date = REQUEST_TIME - 86400;
db_update('node_revision')
->condition('vid', $nodes[2]->vid)
->fields(array(
'timestamp' => $old_revision_date,
))
->execute();
$this->drupalPost("node/$node->nid/revisions/{$nodes[2]->vid}/revert", array(), t('Revert'));
$this->assertRaw(t('@type %title has been reverted back to the revision from %revision-date.', array(
'@type' => 'Basic page',
'%title' => $nodes[2]->title,
'%revision-date' => format_date($old_revision_date),
)));
}
}
......@@ -718,6 +718,21 @@ function node_update_8012() {
}
/**
* Renames global revision permissions to use the word 'all'.
*/
function node_update_8013() {
$actions = array('view', 'delete', 'revert');
foreach ($actions as $action) {
db_update('role_permission')
->fields(array('permission' => $action . ' all revisions'))
->condition('permission', $action . ' revisions')
->condition('module', 'node')
->execute();
}
}
/**
* @} End of "addtogroup updates-7.x-to-8.x"
* The next series of updates should start at 9000.
......
......@@ -1219,14 +1219,16 @@ function node_permission() {
'view own unpublished content' => array(
'title' => t('View own unpublished content'),
),
'view revisions' => array(
'title' => t('View content revisions'),
'view all revisions' => array(
'title' => t('View all revisions'),
),
'revert revisions' => array(
'title' => t('Revert content revisions'),
'revert all revisions' => array(
'title' => t('Revert all revisions'),
'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
),
'delete revisions' => array(
'title' => t('Delete content revisions'),
'delete all revisions' => array(
'title' => t('Delete all revisions'),
'description' => t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
),
);
......@@ -1531,12 +1533,17 @@ function _node_revision_access(Node $node, $op = 'view', $account = NULL, $langc
$access = &drupal_static(__FUNCTION__, array());
$map = array(
'view' => 'view revisions',
'update' => 'revert revisions',
'delete' => 'delete revisions',
'view' => 'view all revisions',
'update' => 'revert all revisions',
'delete' => 'delete all revisions',
);
$type_map = array(
'view' => "view $node->type revisions",
'update' => "revert $node->type revisions",
'delete' => "delete $node->type revisions",
);
if (!$node || !isset($map[$op])) {
if (!$node || !isset($map[$op]) || !isset($type_map[$op])) {
// If there was no node to check against, or the $op was not one of the
// supported ones, we return access denied.
return FALSE;
......@@ -1557,7 +1564,7 @@ function _node_revision_access(Node $node, $op = 'view', $account = NULL, $langc
if (!isset($access[$cid])) {
// Perform basic permission checks first.
if (!user_access($map[$op], $account) && !user_access('administer nodes', $account)) {
if (!user_access($map[$op], $account) && !user_access($type_map[$op], $account) && !user_access('administer nodes', $account)) {
return $access[$cid] = FALSE;
}
......@@ -2861,6 +2868,17 @@ function node_list_permissions($type) {
"delete any $type->type content" => array(
'title' => t('%type_name: Delete any content', array('%type_name' => $type->name)),
),
"view $type->type revisions" => array(
'title' => t('%type_name: View revisions', array('%type_name' => $type->name)),
),
"revert $type->type revisions" => array(
'title' => t('%type_name: Revert revisions', array('%type_name' => $type->name)),
'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
),
"delete $type->type revisions" => array(
'title' => t('%type_name: Delete revisions', array('%type_name' => $type->name)),
'description' => t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
),
);
return $perms;
}
......
......@@ -255,16 +255,19 @@ function node_revision_overview($node) {
$revisions = node_revision_list($node);
$rows = array();
$type = $node->type;
$revert_permission = FALSE;
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
if ((user_access("revert $type revisions") || user_access('revert all revisions') || user_access('administer nodes')) && node_access('update', $node)) {
$revert_permission = TRUE;
}
$delete_permission = FALSE;
if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
if ((user_access("delete $type revisions") || user_access('delete all revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
$delete_permission = TRUE;
}
foreach ($revisions as $revision) {
$row = array();
$type = $node->type;
if ($revision->current_vid > 0) {
$row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision))))
. (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
......
......@@ -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 revisions', 'administer content types'));
$admin_user = $this->drupalCreateUser(array('edit own article content', 'revert article revisions', 'administer content types'));
$this->drupalLogin($admin_user);
$langcode = LANGUAGE_NOT_SPECIFIED;
......
......@@ -34,9 +34,9 @@ public function setUp() {
// Create and login user.
$this->web_user = $this->drupalCreateUser(array(
'view revisions',
'revert revisions',
'delete revisions',
'view all revisions',
'revert all revisions',
'delete all revisions',
'administer entity_test content',
));
$this->drupalLogin($this->web_user);
......
......@@ -28,7 +28,7 @@ protected function setUp() {
$this->adminUser = $this->drupalCreateUser(array('administer views'));
$views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view revisions'));
$views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
$this->drupalLogin($views_admin);
}
......
......@@ -25,7 +25,7 @@ function setUp() {
parent::setUp();
// 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 revisions'));
$views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
$this->drupalLogin($views_admin);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment