Commit d246cd37 authored by DamienMcKenna's avatar DamienMcKenna Committed by das-peter

Issue #2376839 by DamienMcKenna, MiroslavBanov, gnucifer, nicrodgers,...

Issue #2376839 by DamienMcKenna, MiroslavBanov, gnucifer, nicrodgers, dgtlmoon, agentrickard, joelpittet, catch, Dave Reid, das-peter, colan, Fabianx, et al: Rewrite to use the new Drafty module
parent ef46517e
......@@ -31,7 +31,7 @@ CONTENTS
----
1. Introduction
Workbench Moderation
Workbench Moderation
----
1.1 Concepts
......@@ -129,6 +129,13 @@ them where your users can find them.
Using the "Workbench" module with Workbench Moderation enables the display of
moderation status information and a mini moderation form on node viewing pages.
There is one dependency:
https://www.drupal.org/project/drafty
The Drafty module is used for managing changes to the node's state and must also
be installed.
----
3. Configuration
......@@ -176,7 +183,7 @@ perform a particular moderation task.
----
3.3.1 Recommended permissions
For reference, these are the permission sets recommended by the "Check
For reference, these are the permission sets recommended by the "Check
Permissions" tab:
Author:
......@@ -189,7 +196,7 @@ Permissions" tab:
Workbench Moderation:
view moderation messages
use workbench_moderation my drafts tab
Editor:
Node:
access content
......@@ -203,7 +210,7 @@ Permissions" tab:
view moderation history
use workbench_moderation my drafts tab
use workbench_moderation needs review tab
Moderator:
Node:
access content
......
......@@ -178,12 +178,13 @@ class WorkbenchModerationExternalNodeUpdateTestCase extends WorkbenchModerationT
* The node's record(s) from the {workbench_moderation_node_history} table.
*/
protected function getModerationRecord($status = 'is_current') {
return db_select('workbench_moderation_node_history', 'nh')
$data = db_select('workbench_moderation_node_history', 'nh')
->fields('nh', array('from_state', 'state', 'published', 'is_current'))
->condition('nid', $this->node->nid, '=')
->condition($status, 1)
->execute()
->fetchAssoc();
return $data;
}
}
......@@ -14,7 +14,7 @@ class WorkbenchModerationFilesTestCase extends FileFieldTestCase {
function setUp() {
parent::setUp();
module_enable(array('workbench_moderation'));
module_enable(array('drafty', 'workbench_moderation'));
// Create a new content type and enable moderation on it.
$type = $this->drupalCreateContentType();
......
<?php
/**
* @file
* Node Access tests for workbench_moderation.module.
*/
class WorkbenchModerationNodeAccessTestCase extends WorkbenchModerationTestCase {
protected $test_user;
public static function getInfo() {
return array(
'name' => 'Node Access test',
'description' => 'Ensure that node access rules are enforced during moderation.',
'group' => 'Workbench Moderation',
);
}
function setUp($modules = array()) {
$modules = array_merge($modules, array('workbench_moderation_node_access_test'));
parent::setUp($modules);
}
/**
* Creates a node and tests the creation of node access rules.
*/
function testNodeAccessRecords() {
// Make sure this node type is moderated.
$is_moderated = workbench_moderation_node_type_moderated($this->content_type);
$this->assertTrue($is_moderated, t('The content type is moderated.'));
// Create an published node that has access records.
$node1 = $this->drupalCreateNode(array('type' => $this->content_type, 'title' => 'Published node', 'status' => 1));
$this->assertTrue(node_load($node1->nid), 'Test published node created.');
// Assert grants were added to node_access on creation.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node1->nid))->fetchAll();
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
$this->assertEqual($records[0]->realm, 'test_wm_realm', 'Grant with test_wm_realm acquired for node without alteration.');
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create an unpublished node that has no access records.
$node2 = $this->drupalCreateNode(array('type' => $this->content_type, 'title' => 'Unpublished node', 'status' => 0));
$this->assertTrue(node_load($node2->nid), 'Test unpublished node created.');
// Check that no access records are present for the node, since it is not published.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node2->nid))->fetchAll();
$this->assertEqual(count($records), 0, 'Returned no records for unpublished node.');
// Login for form testing.
$this->drupalLogin($this->moderator_user);
// Create a new draft of the published node.
$edit = array('title' => 'Draft node revision1');
$this->drupalPost("node/{$node1->nid}/edit", $edit, t('Save'));
// Assert grants still exist on node_access after draft created.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node1->nid))->fetchAll();
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
$this->assertEqual($records[0]->realm, 'test_wm_realm', 'Grant with article_realm acquired for node without alteration.');
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create an published node that has access records.
$node3 = $this->drupalCreateNode(array('type' => $this->content_type, 'title' => 'Published node', 'status' => 1));
$this->assertTrue(node_load($node3->nid), 'Test published node created.');
// Make sure the "Moderate" tab is accessible.
$this->drupalGet("node/{$node3->nid}/moderation");
// Attempt to change the moderation state without a token in the link.
$this->drupalGet("node/{$node3->nid}/moderation/{$node3->vid}/change-state/needs_review");
$this->assertResponse(403);
// Run the same state change with a valid token, which should succeed.
$this->drupalGet("node/{$node3->nid}/moderation/{$node3->vid}/change-state/needs_review", array(
'query' => array('token' => $this->drupalGetToken("{$node3->nid}:{$node3->vid}:needs_review"))
));
// Test that we have unpublished the node.
$this->assertResponse(200);
$node = node_load($node3->nid, NULL, TRUE);
$this->assertEqual($node->workbench_moderation['current']->state, 'needs_review', 'Node state changed to Needs Review via callback.');
$this->assertFalse($node->status, 'Node is no longer published after moderation.');
// Check that no access records are present for the node, since it is not published.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node3->nid))->fetchAll();
$this->assertEqual(count($records), 0, 'Returned no records for unpublished node.');
// Publish the node via the moderation form.
$moderate = array('state' => workbench_moderation_state_published());
$this->drupalPost("node/{$node3->nid}/moderation", $moderate, t('Apply'));
// Ensure published status.
$node = node_load($node3->nid, NULL, TRUE);
$this->assertTrue(isset($node->workbench_moderation['published']), t('Workbench moderation has a published revision'));
// Check to see if grants are still present.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node3->nid))->fetchAll();
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
$this->assertEqual($records[0]->realm, 'test_wm_realm', 'Grant with article_realm acquired for node without alteration.');
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create a new draft.
$edit = array('title' => $this->randomName(10) . '_revision1');
$this->drupalPost("node/{$node3->nid}/edit", $edit, t('Save'));
// Check that grants are not changed.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node3->nid))->fetchAll();
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
$this->assertEqual($records[0]->realm, 'test_wm_realm', 'Grant with article_realm acquired for node without alteration.');
$this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Ensure that the published version is still present.
$node = node_load($node3->nid, NULL, TRUE);
debug($node->title);
$this->assertTrue($node->status, t('Content is published'));
$this->assertEqual($node->title, 'Published node', t('Published version is loaded.'));
// Load the published and draft revisions.
$draft = clone $node;
$draft = workbench_moderation_node_current_load($draft);
// Assert the two revisions are unique.
$this->assertEqual($node->vid, $node->workbench_moderation['published']->vid, t('Published revision is loaded by default'));
$this->assertTrue($node->status, t('Published revision has status = 1'));
$this->assertNotEqual($node->vid, $draft->vid, t('Draft revision is different from the published revision'));
// Test the node for an anon user.
$this->drupalLogout();
$this->drupalGet('node/' . $node->nid);
$this->assertRaw('Published node', t('Page title is "Published node"'));
}
}
<?php
/**
* @file
* Permissions-related tests for workbench_moderation.module.
*/
class WorkbenchModerationPermsTestCase extends DrupalWebTestCase {
protected $content_type;
protected $editor_user;
protected $author_user;
function setUp($modules = array()) {
$modules = array_merge($modules, array('drafty', 'workbench_moderation'));
parent::setUp($modules);
// Create a new content type and enable moderation on it.
$type = $this->drupalCreateContentType();
$this->content_type = $type->name;
variable_set('node_options_' . $this->content_type, array('revision', 'moderation'));
// The editor should be able to view all unpublished content, even without authoring perms.
$editor_permissions = array(
0 => 'view all unpublished content',
);
$this->editor_user = $this->drupalCreateUser($editor_permissions);
// The Author will create the content.
$author_permissions = array(
0 => 'create ' . $type->name . ' content',
);
$this->author_user = $this->drupalCreateUser($author_permissions);
}
}
class WorkbenchModerationViewUnpublishedTestCase extends WorkbenchModerationPermsTestCase {
public static function getInfo() {
return array(
'name' => 'View all unpublished content',
'description' => 'Create a user who can view unpublished content. Create a node and leave it unpublished. Try to view it.',
'group' => 'Workbench Moderation',
);
}
function setUp($modules = array()) {
parent::setUp($modules);
$this->drupalLogin($this->author_user);
}
function testViewUnpublished() {
$is_moderated = workbench_moderation_node_type_moderated($this->content_type);
$this->assertTrue($is_moderated, t('The content type is moderated.'));
// Create a new node and make sure it is unpublished.
$body_name = 'body[' . LANGUAGE_NONE . '][0]';
$edit = array(
'title' => $this->randomName(),
"{$body_name}[value]" => $this->randomString(128),
"{$body_name}[format]" => filter_default_format(),
);
$this->drupalPost("node/add/{$this->content_type}", $edit, t('Save'));
// Get the new node.
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->assertFalse($node->status, t('New node is unpublished'));
$this->assertTrue(isset($node->workbench_moderation), t('Workbench moderation information is present on the node object'));
$this->assertFalse(isset($node->workbench_moderation['published']), t('Workbench moderation has no published revision'));
$this->assertEqual($node->uid, $this->author_user->uid, 'This node was authored by the author user.');
$this->verbose(print_r($this->loggedInUser, TRUE));
$this->drupalLogin($this->editor_user);
global $user;
$user = user_load($this->loggedInUser->uid);
$this->drupalGet($node->path['source']);
$this->assertFalse($node->status, t('This node is unpublished.'));
$this->assertResponse(200);
$this->assertFalse($node->uid == $this->loggedInUser->uid, t('The current user is not the author of this node.'));
$this->assertEqual($user->uid, $this->loggedInUser->uid, 'The current global user is the same as the logged in user.');
$this->assertEqual($user->uid, $this->editor_user->uid, 'The current user is the editor user.');
$this->assertTrue(user_access('view all unpublished content'), 'Current user has permission to view all unpublished content');
}
}
......@@ -10,7 +10,7 @@ class WorkbenchModerationTestCase extends DrupalWebTestCase {
protected $moderator_user;
function setUp($modules = array()) {
$modules = array_merge($modules, array('workbench_moderation', 'workbench_moderation_test'));
$modules = array_merge($modules, array('drafty', 'workbench_moderation', 'workbench_moderation_test'));
parent::setUp($modules);
// Create a new content type and enable moderation on it.
......@@ -41,6 +41,17 @@ class WorkbenchModerationTestCase extends DrupalWebTestCase {
));
}
/**
* Override DrupalWebTestCase::drupalGetToken() as it does not return the
* correct token for the currently logged-in testing user.
*/
protected function drupalGetToken($value = '') {
$session_id = $this->session_id;
if (empty($session_id) && !empty($this->loggedInUser)) {
$session_id = db_query("SELECT sid FROM {sessions} WHERE uid = :uid ORDER BY timestamp DESC", array(':uid' => $this->loggedInUser->uid))->fetchField();
}
return drupal_hmac_base64($value, $session_id . drupal_get_private_key() . drupal_get_hash_salt());
}
}
class WorkbenchModerationModerateTabTestCase extends WorkbenchModerationTestCase {
......@@ -58,18 +69,6 @@ class WorkbenchModerationModerateTabTestCase extends WorkbenchModerationTestCase
$this->drupalLogin($this->moderator_user);
}
/**
* Override DrupalWebTestCase::drupalGetToken() as it does not return the
* correct token for the currently logged-in testing user.
*/
protected function drupalGetToken($value = '') {
$session_id = $this->session_id;
if (empty($session_id) && !empty($this->loggedInUser)) {
$session_id = db_query("SELECT sid FROM {sessions} WHERE uid = :uid ORDER BY timestamp DESC", array(':uid' => $this->loggedInUser->uid))->fetchField();
}
return drupal_hmac_base64($value, $session_id . drupal_get_private_key() . drupal_get_hash_salt());
}
function testModerateTab() {
$is_moderated = workbench_moderation_node_type_moderated($this->content_type);
$this->assertTrue($is_moderated, t('The content type is moderated.'));
......
<?php
/**
* @file
* Tests for node transition hooks with workbench_moderation.module.
*/
class WorkbenchModerationTransitionTestCase extends WorkbenchModerationTestCase {
protected $properties;
public static function getInfo() {
return array(
'name' => 'Transition hook test',
'description' => 'Tests the usage of hook_workbench_moderation_transition().',
'group' => 'Workbench Moderation',
);
}
function setUp($modules = array()) {
// Enable a test module that will publish and unpublish nodes for us and
// provide hook implementations.
parent::setUp(array_merge($modules, array('workbench_moderation_test')));
$this->drupalLogin($this->moderator_user);
$this->properties = array('title', 'nid', 'vid', 'previous_state', 'new_state');
}
/**
* Asserts that transition values are as expected.
*
* @param array $expected
* An array of values to be set by the transition.
*/
private function assertTransition($expected = array()) {
foreach ($this->properties as $name) {
$value = workbench_moderation_test_get($name);
$this->assertEqual($value, $expected[$name], "Transition success: $name set to $value");
}
}
function testTransitionFromNodeForm() {
// Create a new node and publish it immediately.
$body_name = 'body[' . LANGUAGE_NONE . '][0]';
$edit = array(
'title' => $this->randomName(),
"{$body_name}[value]" => $this->randomString(128),
"{$body_name}[format]" => filter_default_format(),
'workbench_moderation_state_new' => workbench_moderation_state_published(),
);
$this->drupalPost("node/add/{$this->content_type}", $edit, t('Save'));
// Test the published state transition.
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $edit['title'],
'previous_state' => 'draft',
'new_state' => 'published',
'status' => 1,
);
$this->assertTransition($expected);
// Create a new draft of the published node.
$node = $this->drupalGetNodeByTitle($edit['title']);
$edit = array('title' => 'Draft node revision1');
$this->drupalPost("node/{$node->nid}/edit", $edit, t('Save'));
$expected = array(
'nid' => 1,
'vid' => 2,
'title' => $edit['title'],
'previous_state' => 'published',
'new_state' => 'draft',
'status' => 0,
);
$this->assertTransition($expected);
// Moderate to needs review and check transition.
$this->drupalGet("node/1/moderation/2/change-state/needs_review", array(
'query' => array('token' => $this->drupalGetToken("1:2:needs_review"))
));
$expected = array(
'nid' => 1,
'vid' => 2,
'title' => $edit['title'],
'previous_state' => 'draft',
'new_state' => 'needs_review',
'status' => 0,
);
$this->assertTransition($expected);
// Publish the revision and check transition.
$this->drupalGet("node/1/moderation/2/change-state/published", array(
'query' => array('token' => $this->drupalGetToken("1:2:published"))
));
$expected = array(
'nid' => 1,
'vid' => 2,
'title' => $edit['title'],
'previous_state' => 'needs_review',
'new_state' => 'published',
'status' => 1,
);
$this->assertTransition($expected);
// Create a new node and make sure it is unpublished.
$body_name = 'body[' . LANGUAGE_NONE . '][0]';
$edit = array(
'title' => $this->randomName(),
"{$body_name}[value]" => $this->randomString(128),
"{$body_name}[format]" => filter_default_format(),
);
$this->drupalPost("node/add/{$this->content_type}", $edit, t('Save'));
// Get the new node.
$node = $this->drupalGetNodeByTitle($edit['title']);
$expected = array(
'nid' => $node->nid,
'vid' => $node->vid,
'title' => $edit['title'],
'previous_state' => 'draft',
'new_state' => 'draft',
'status' => 0,
);
$this->assertTransition($expected);
}
function testUnpublishTransition() {
// Create a new node and publish it immediately. Assumes that
// WorkbenchModerationPublishFromNodeFormTestCase passes.
$body_name = 'body[' . LANGUAGE_NONE . '][0]';
$edit = array(
'title' => $this->randomName(),
"{$body_name}[value]" => $this->randomString(128),
"{$body_name}[format]" => filter_default_format(),
'workbench_moderation_state_new' => workbench_moderation_state_published(),
);
$this->drupalPost("node/add/{$this->content_type}", $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title']);
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $edit['title'],
'previous_state' => 'draft',
'new_state' => 'published',
'status' => 1,
);
$this->assertTransition($expected);
// Unpublish the node via the unpublish confirmation form.
$this->drupalPost("node/{$node->nid}/moderation/{$node->vid}/unpublish", array(), t('Unpublish'));
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $edit['title'],
'previous_state' => 'published',
'new_state' => 'draft',
'status' => 0,
);
$this->assertTransition($expected);
}
public function testTransitionFromNodeSave() {
// Create a brand new unpublished node programmatically.
$edit = array(
'title' => $this->randomName(),
'type' => $this->content_type,
'status' => NODE_NOT_PUBLISHED,
);
$this->node = $this->drupalCreateNode($edit);
// Get the new node.
$node = $this->drupalGetNodeByTitle($edit['title']);
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $edit['title'],
'previous_state' => 'draft',
'new_state' => 'draft',
'status' => 0,
);
$this->assertTransition($expected);
$node = $this->drupalGetNodeByTitle($edit['title']);
$node = node_load($node->nid, NULL, TRUE);
// Update the status external to our processes.
$node->status = 1;
$node->title = 'New title';
node_save($node);
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $node->title,
'previous_state' => 'draft',
'new_state' => 'published',
'status' => 1,
);
$this->assertTransition($expected);
// Ensure that multiple saves that do not spawn a new PHP request are
// handled correctly to protect against stale static cache.
$node = $this->drupalGetNodeByTitle($node->title);
$node = node_load($node->nid, NULL, TRUE);
$node->status = 1;
$node->title = 'Newer title';
node_save($node);
$expected = array(
'nid' => 1,
'vid' => 1,
'title' => $node->title,
'previous_state' => 'published',
'new_state' => 'published',
'status' => 1,
);
$this->assertTransition($expected);
}
}
name = Workbench Moderation Node Access Test
description = Node Access test module for Workbench Moderation.
package = Workbench
core = 7.x
hidden = TRUE
<?php
/**
* @file
* Test module for node access control on moderated nodes.
*/
/**
* Implements hook_node_grants().
*/
function workbench_moderation_node_access_test_node_grants($account, $op) {
// Give everyone full grants so we don't break other node tests.
return array(
'test_wm_realm' => array(1),
);
}
/**
* Implements hook_node_access_records().
*/
function workbench_moderation_node_access_test_node_access_records($node) {
// Return nothing for unpublished nodes.
if (!$node->status) {
return array();
}
$grants[] = array(
'realm' => 'test_wm_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
return $grants;
}
name = Workbench Moderation Test
description = Test module for Workbench Moderation.
package = Workbench
core = 7.x
hidden = TRUE
<?php
/**
* @file
* Test module for Workbench Moderation.
*/
/**
* Implements hook_menu().
*/