| 1 |
<?php |
<?php |
| 2 |
// $Id: revisioning.module,v 1.10 2009/05/12 07:08:07 rdeboer Exp $ |
// $Id: revisioning.module,v 1.22 2009/05/28 07:33:45 rdeboer Exp $ |
| 3 |
|
|
| 4 |
/** |
/** |
| 5 |
* @file |
* @file |
| 40 |
* |
* |
| 41 |
* Define new menu items. |
* Define new menu items. |
| 42 |
* Existing menu items are modified through hook_menu_alter(). |
* Existing menu items are modified through hook_menu_alter(). |
| 43 |
|
* @todo add "In draft" tab |
| 44 |
*/ |
*/ |
| 45 |
function revisioning_menu() { |
function revisioning_menu() { |
| 46 |
$items = array(); |
$items = array(); |
| 47 |
/* |
|
| 48 |
// Add a tab to the 'I can access' tab (defined in module_grants.module) |
// Add a tab to the 'I created' tab (defined in module_grants.module) |
| 49 |
$items['content-admin/accessible/in-draft'] = array( |
$items['accessible-content/i-created/pending'] = array( |
| 50 |
'title' => 'In draft', |
'title' => 'In draft/Pending publication', |
| 51 |
'page callback' => _show_nodes_with_drafts, |
'page callback' => '_show_pending_nodes', |
| 52 |
'page arguments' => array(FALSE), |
'page arguments' => array('view', I_CREATED), |
| 53 |
'access arguments' => array('access content summary'), |
'access arguments' => array('access content summary'), |
| 54 |
'type' => MENU_LOCAL_TASK, |
'type' => MENU_LOCAL_TASK, |
| 55 |
'weight' => -10 |
'weight' => -1 |
| 56 |
); |
); |
| 57 |
|
// Add a tab to the 'I last modified' tab (defined in module_grants.module) |
| 58 |
// Add a tab to the 'I created' tab (defined in module_grants.module) |
$items['accessible-content/i-last-modified/pending'] = array( |
| 59 |
$items['content-admin/i-created/in-draft'] = array( |
'title' => 'In draft/Pending publication', |
| 60 |
'title' => 'In draft', |
'page callback' => '_show_pending_nodes', |
| 61 |
'page callback' => _show_nodes_with_drafts, |
'page arguments' => array('view', I_LAST_MODIFIED), |
|
'page arguments' => array(TRUE), |
|
| 62 |
'access arguments' => array('access content summary'), |
'access arguments' => array('access content summary'), |
| 63 |
'type' => MENU_LOCAL_TASK, |
'type' => MENU_LOCAL_TASK, |
| 64 |
'weight' => -10 |
'weight' => -1 |
| 65 |
); |
); |
| 66 |
*/ |
// Add a tab to the 'I can edit' tab (defined in module_grants.module) |
| 67 |
// Add a tab to the 'I can access' tab (defined in module_grants.module) |
$items['accessible-content/i-can-edit/pending'] = array( |
| 68 |
$items['content-admin/accessible/pending'] = array( |
'title' => 'In draft/Pending publication', |
|
'title' => 'Pending', |
|
| 69 |
'page callback' => '_show_pending_nodes', |
'page callback' => '_show_pending_nodes', |
| 70 |
'page arguments' => array(FALSE), |
'page arguments' => array('update'), |
| 71 |
'access arguments' => array('access content summary'), |
'access arguments' => array('access content summary'), |
| 72 |
'type' => MENU_LOCAL_TASK, |
'type' => MENU_LOCAL_TASK, |
| 73 |
'weight' => -9 |
'weight' => -1 |
| 74 |
); |
); |
| 75 |
// Add a tab to the 'I created' tab (defined in module_grants.module) |
// Add a tab to the 'I can view' tab (defined in module_grants.module) |
| 76 |
$items['content-admin/i-created/pending'] = array( |
$items['accessible-content/i-can-view/pending'] = array( |
| 77 |
'title' => 'Pending', |
'title' => 'In draft/Pending publication', |
| 78 |
'page callback' => '_show_pending_nodes', |
'page callback' => '_show_pending_nodes', |
| 79 |
'page arguments' => array(TRUE), |
'page arguments' => array('view'), |
| 80 |
'access arguments' => array('access content summary'), |
'access arguments' => array('access content summary'), |
| 81 |
'type' => MENU_LOCAL_TASK, |
'type' => MENU_LOCAL_TASK, |
| 82 |
'weight' => -9 |
'weight' => -1 |
| 83 |
); |
); |
| 84 |
|
|
| 85 |
// Callback (not a menu item) to allow users to edit specified revision |
// Callback (not a menu item) to allow users to edit specified revision |
| 86 |
$items['node/%node/revisions/%/edit'] = array( |
$items['node/%node/revisions/%/edit'] = array( |
| 87 |
//'title' => t('Edit revision'), |
//'title' => t('Edit revision'), |
| 131 |
*/ |
*/ |
| 132 |
function revisioning_menu_alter(&$items) { |
function revisioning_menu_alter(&$items) { |
| 133 |
|
|
| 134 |
// Change 'I created' default tab from Editable to Pending |
// Change secondary filter tab default from Published' to 'In draft/Pending publication' |
| 135 |
$items['content-admin']['page callback'] = '_show_pending_nodes'; |
$items['accessible-content']['page callback'] = '_show_pending_nodes'; |
| 136 |
$items['content-admin']['page arguments'] = array(TRUE); |
$items['accessible-content']['page arguments'] = array('view', I_LAST_MODIFIED); |
| 137 |
$items['content-admin/i-created']['page callback'] = '_show_pending_nodes'; |
|
| 138 |
$items['content-admin/i-created']['page arguments'] = array(TRUE); |
$items['accessible-content/i-created']['page callback'] = '_show_pending_nodes'; |
| 139 |
$items['content-admin/i-created/editable']['type'] = MENU_LOCAL_TASK; |
$items['accessible-content/i-created']['page arguments'] = array('view', I_CREATED); |
| 140 |
$items['content-admin/i-created/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
$items['accessible-content/i-created/published']['type'] = MENU_LOCAL_TASK; |
| 141 |
|
$items['accessible-content/i-created/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
| 142 |
// Change 'I can access' default tab from Editable to Pending |
|
| 143 |
$items['content-admin/accessible']['page callback'] = '_show_pending_nodes'; |
$items['accessible-content/i-last-modified']['page callback'] = '_show_pending_nodes'; |
| 144 |
$items['content-admin/accessible']['page arguments'] = array(FALSE); |
$items['accessible-content/i-last-modified']['page arguments'] = array('view', I_LAST_MODIFIED); |
| 145 |
$items['content-admin/accessible/editable']['type'] = MENU_LOCAL_TASK; |
$items['accessible-content/i-last-modified/published']['type'] = MENU_LOCAL_TASK; |
| 146 |
$items['content-admin/accessible/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
$items['accessible-content/i-last-modified/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
| 147 |
|
|
| 148 |
|
$items['accessible-content/i-can-edit']['page callback'] = '_show_pending_nodes'; |
| 149 |
|
$items['accessible-content/i-can-edit']['page arguments'] = array('update'); |
| 150 |
|
$items['accessible-content/i-can-edit/published']['type'] = MENU_LOCAL_TASK; |
| 151 |
|
$items['accessible-content/i-can-edit/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
| 152 |
|
|
| 153 |
|
$items['accessible-content/i-can-view']['page callback'] = '_show_pending_nodes'; |
| 154 |
|
$items['accessible-content/i-can-view']['page arguments'] = array('view'); |
| 155 |
|
$items['accessible-content/i-can-view/published']['type'] = MENU_LOCAL_TASK; |
| 156 |
|
$items['accessible-content/i-can-view/pending']['type'] = MENU_DEFAULT_LOCAL_TASK; |
| 157 |
|
|
| 158 |
// Rename "View" tab |
// Rename "View" tab |
| 159 |
$items['node/%node/view']['title'] = 'View current'; |
$items['node/%node/view']['title'] = 'View current'; |
| 160 |
|
|
| 186 |
} |
} |
| 187 |
|
|
| 188 |
/** |
/** |
|
* Return as a themed table a list of nodes that are currently in draft. |
|
|
* |
|
|
*/ |
|
|
function _show_nodes_with_drafts($created_by_user = TRUE) { |
|
|
global $user; |
|
|
return $created_by_user |
|
|
? '<p>Imagine here a list of all drafts created by user '. theme('username', user_load(array('uid' => $user->uid))) |
|
|
: '<p>One day this will show all nodes accessible to this user that have outstanding drafts.</p>'; |
|
|
} |
|
|
/** |
|
| 189 |
* Return as a themed table a list of nodes that have pending revisions. |
* Return as a themed table a list of nodes that have pending revisions. |
| 190 |
* access rights of the logged-in user. |
* access rights of the logged-in user. |
| 191 |
* |
* |
| 192 |
* @param $created_by_user |
* @param $op |
| 193 |
* TRUE to filter by logged-in user |
* Operation, one of 'view', 'update' or 'delete'. |
| 194 |
|
* @param $user_filter |
| 195 |
|
* One of NO_FILTER, I_CREATED or I_LAST_MODIFIED. |
| 196 |
* @return |
* @return |
| 197 |
* themed HTML |
* themed HTML |
| 198 |
*/ |
*/ |
| 199 |
function _show_pending_nodes($created_by_user = FALSE) { |
function _show_pending_nodes($op = 'view', $user_filter = -1) { |
| 200 |
global $user; |
global $user; |
| 201 |
$nodes = get_nodes('update', NO_FILTER, $created_by_user ? $user->uid : NO_FILTER, TRUE); |
$nodes = get_nodes($op, -1, $user_filter == I_CREATED ? $user->uid : -1, $user_filter == I_LAST_MODIFIED ? $user->uid : -1, TRUE); |
| 202 |
return theme('nodes_summary', $nodes); |
return theme('nodes_summary', $nodes); |
| 203 |
} |
} |
| 204 |
|
|
| 214 |
* Menu callback for admin settings. |
* Menu callback for admin settings. |
| 215 |
*/ |
*/ |
| 216 |
function revisioning_admin_settings() { |
function revisioning_admin_settings() { |
|
$form['revisioning_publication_options'] = array( |
|
|
'#type' => 'fieldset', |
|
|
'#title' => t('Publication options'), |
|
|
); |
|
|
$form['revisioning_publication_options']['revisioning_auto_publish'] = array( |
|
|
'#type' => 'checkbox', |
|
|
'#title' => t('Auto-publish content upon saving (for moderators).'), |
|
|
'#default_value' => variable_get('revisioning_auto_publish', FALSE), |
|
|
'#description' => t('If this box is ticked and the logged-in user has the "publish revisions" permission, then any content is published immeditaley upon saving, without further review.') |
|
|
); |
|
|
return system_settings_form($form); |
|
| 217 |
} |
} |
| 218 |
|
|
| 219 |
/** |
/** |
| 220 |
* Implementation of hook_form_alter(). |
* Implementation of hook_form_alter(). |
| 221 |
* |
* |
| 222 |
* On node edit form, add the "New revisions in moderation" tick-box. |
* On node edit form, add the "New revision in moderation" tick-box. |
| 223 |
*/ |
*/ |
| 224 |
function revisioning_form_alter(&$form, &$form_state, $form_id) { |
function revisioning_form_alter(&$form, &$form_state, $form_id) { |
| 225 |
// Note that $form_id = <content_type>_$form['#id'] |
// Note that $form_id = <content_type>_$form['#id'] |
| 226 |
if (isset($form['#id']) && $form['#id'] == 'node-form') { |
if (isset($form['#id']) && $form['#id'] == 'node-form') { |
| 227 |
|
$form['options']['#collapsed'] = FALSE; |
| 228 |
|
|
| 229 |
$default_value = in_array('revision_moderation', variable_get("node_options_{$form['type']['#value']}", array('status', 'promote'))); |
$default_value = in_array('revision_moderation', variable_get("node_options_{$form['type']['#value']}", array('status', 'promote'))); |
| 230 |
// Only show the checkbox if user has the 'administer nodes' permission |
// Only show this option if user has the 'administer nodes' permission |
| 231 |
if (user_access('administer nodes')) { |
if (user_access('administer nodes')) { |
| 232 |
$form['revision_information']['revision_moderation'] = array( |
$form['revision_information']['revision_moderation'] = array( |
| 233 |
|
'#title' => t('New revision in draft, pending moderation'), |
| 234 |
'#type' => 'checkbox', |
'#type' => 'checkbox', |
|
'#title' => t('New revisions in moderation'), |
|
| 235 |
'#default_value' => $default_value |
'#default_value' => $default_value |
| 236 |
); |
); |
| 237 |
} |
} |
| 238 |
else { |
else { |
| 239 |
// Don't show checkbox, set default on form |
// Don't show option, just set default on form |
| 240 |
$form['revision_moderation'] = array( |
$form['revision_moderation'] = array( |
| 241 |
'#type' => 'value', |
'#type' => 'value', |
| 242 |
'#value' => $default_value |
'#value' => $default_value |
| 249 |
* Implementation of hook_node_type_form_alter(). |
* Implementation of hook_node_type_form_alter(). |
| 250 |
* |
* |
| 251 |
* On content type edit form, add the "New revisions in moderation" tick-box |
* On content type edit form, add the "New revisions in moderation" tick-box |
| 252 |
* and a couple of radio-boxes to select the new revision policy. |
* and a couple of radio-boxes to select the new revision and auto-publish |
| 253 |
|
* policies. |
| 254 |
*/ |
*/ |
| 255 |
function revisioning_form_node_type_form_alter(&$form, &$form_state) { |
function revisioning_form_node_type_form_alter(&$form, &$form_state) { |
| 256 |
$form['workflow']['node_options']['#options']['revision_moderation'] = t('New revisions in moderation'); |
$form['workflow']['#collapsed'] = FALSE; |
| 257 |
$form['workflow']['new_revisions'] = array( |
$form['workflow']['node_options']['#options']['revision_moderation'] = t('New revision in draft, pending moderation (requires "Create new revision")'); |
| 258 |
|
|
| 259 |
|
$content_type = $form['#node_type']->type; |
| 260 |
|
$form['workflow']['revisioning'] = array( |
| 261 |
|
'#type' => 'fieldset', |
| 262 |
|
'#title' => t('New revision in draft'), |
| 263 |
|
'#collapsible' => TRUE, |
| 264 |
|
'#collapsed' => FALSE |
| 265 |
|
); |
| 266 |
|
$form['workflow']['revisioning']['new_revisions'] = array( |
| 267 |
'#title' => t('Create new revision'), |
'#title' => t('Create new revision'), |
| 268 |
'#type' => 'radios', |
'#type' => 'radios', |
| 269 |
'#options' => array( |
'#options' => array( |
| 270 |
NEW_REVISION_WHEN_NOT_PENDING => t('Only when saving content that is <em>not</em> pending'), |
NEW_REVISION_WHEN_NOT_PENDING => t('Only when saving %type content that is not already in draft/pending moderation', array('%type' => $content_type)), |
| 271 |
NEW_REVISION_EVERY_SAVE => t('Every time content is saved')), |
NEW_REVISION_EVERY_SAVE => t('Every time %type content is updated, even when saving content in draft/pending moderation', array('%type' => $content_type))), |
| 272 |
'#default_value' => variable_get('new_revisions_'. $form['#node_type']->type, NEW_REVISION_WHEN_NOT_PENDING), |
'#default_value' => (int)variable_get('new_revisions_'. $content_type, NEW_REVISION_WHEN_NOT_PENDING), |
| 273 |
'#description' => t('Use less disk space and avoid cluttering your revisions list. With the first option ticked, modifications are saved to the same copy (i.e. no additional revisions are created) until the content is published. This selection applies only when "Create new revision" and "New revisions in moderation" are both ticked.') |
'#description' => t('Use less disk space and avoid cluttering your revisions list. With the first option ticked, modifications are saved to the same copy (i.e. no additional revisions are created) until the content is published.') |
| 274 |
); |
); |
| 275 |
|
$form['workflow']['revisioning']['revisioning_auto_publish'] = array( |
| 276 |
|
'#title' => t('Auto-publish drafts of type %type (for moderators)', array('%type' => $content_type)), |
| 277 |
|
'#type' => 'checkbox', |
| 278 |
|
'#default_value' => (int)variable_get('revisioning_auto_publish_'. $content_type, 1), |
| 279 |
|
'#description' => t('If this box is ticked and the logged-in user has the "publish revisions" permission, then any draft of type %type is published immeditaley upon saving, without further review.', array('%type' => $content_type)) |
| 280 |
|
); |
| 281 |
} |
} |
| 282 |
|
|
| 283 |
/** |
/** |
| 301 |
if ($op == 'alter') { // called just after $node object is fully built for display |
if ($op == 'alter') { // called just after $node object is fully built for display |
| 302 |
if (module_grants_node_revision_access('view revisions', $node)) { |
if (module_grants_node_revision_access('view revisions', $node)) { |
| 303 |
if (!$teaser && $node->nid == $args[1]) { // don't show msg on page with many nodes |
if (!$teaser && $node->nid == $args[1]) { // don't show msg on page with many nodes |
| 304 |
drupal_set_message(_get_node_details($node)); |
drupal_set_message(_get_node_info_msg($node)); |
| 305 |
} |
} |
| 306 |
global $theme_engine; |
global $theme_engine; |
| 307 |
if (!isset($theme_engine)) { |
if (!isset($theme_engine)) { |
| 317 |
* @param $op, only listening to 'presave' and 'insert' |
* @param $op, only listening to 'presave' and 'insert' |
| 318 |
* @return nothing |
* @return nothing |
| 319 |
*/ |
*/ |
| 320 |
function _handle_add($node, $op) { |
function _handle_add(&$node, $op) { |
| 321 |
switch ($op) { |
switch ($op) { |
| 322 |
case 'presave': |
case 'presave': |
| 323 |
if ($node->revision_moderation && !$node->status && variable_get('revisioning_auto_publish', FALSE) && user_access('publish revisions')) { |
if ($node->revision_moderation && !$node->status && variable_get('revisioning_auto_publish_'. $node->type, FALSE) && (user_access('publish revisions'))) { |
| 324 |
drupal_set_message("Auto-publishing initial revision."); |
drupal_set_message(t('Auto-publishing initial draft as the first revision.')); |
| 325 |
$node->status = TRUE; |
$node->status = TRUE; |
| 326 |
} |
} |
| 327 |
break; |
break; |
| 331 |
drupal_set_message(t('Initial revision created and published.')); |
drupal_set_message(t('Initial revision created and published.')); |
| 332 |
} |
} |
| 333 |
else { |
else { |
| 334 |
drupal_set_message(t('Initial revision created, pending publication.')); |
drupal_set_message(t('Initial draft created, pending publication.')); |
| 335 |
} |
} |
| 336 |
break; |
break; |
| 337 |
} |
} |
| 343 |
* @param $op, only listening to 'prepare', 'presave' and 'update' |
* @param $op, only listening to 'prepare', 'presave' and 'update' |
| 344 |
* @return nothing |
* @return nothing |
| 345 |
*/ |
*/ |
| 346 |
function _handle_edit($node, $op) { |
function _handle_edit(&$node, $op) { |
| 347 |
if ($op == 'prepare') { |
if ($op == 'prepare') { |
| 348 |
$count = _number_of_revisions_newer_than($node->vid, $node->nid); |
$count = _number_of_revisions_newer_than($node->vid, $node->nid); |
| 349 |
if ($count == 1) { |
if ($count == 1) { |
| 364 |
$node->revision = FALSE; |
$node->revision = FALSE; |
| 365 |
} |
} |
| 366 |
} |
} |
| 367 |
if (variable_get('revisioning_auto_publish', FALSE) && user_access('publish revisions')) { |
if (variable_get('revisioning_auto_publish_'. $node->type, FALSE) && user_access('publish revisions')) { |
| 368 |
// By-pass moderation and make sure node is published |
// By-pass moderation and make sure node is published |
| 369 |
drupal_set_message("Auto-publishing this revision."); |
drupal_set_message(t('Auto-publishing this revision.')); |
| 370 |
$node->status = TRUE; |
$node->status = TRUE; |
| 371 |
} |
} |
| 372 |
else { |
else { |
| 635 |
* @return |
* @return |
| 636 |
* A translatable message containing details about the node |
* A translatable message containing details about the node |
| 637 |
*/ |
*/ |
| 638 |
function _get_node_details($node) { |
function _get_node_info_msg($node) { |
| 639 |
// Get username for the revision rather than the original node. |
// Get username for the revision rather than the original node. |
| 640 |
$revision_author = user_load($node->revision_uid); |
$revision_author = user_load($node->revision_uid); |
| 641 |
$published = $node->status ? t('current, published') : t('current, unpublished'); |
$published = $node->status ? t('current, published') : t('current, unpublished'); |
| 765 |
function _is_pending($node) { |
function _is_pending($node) { |
| 766 |
return |
return |
| 767 |
($node->vid > _get_current_revision_id($node->nid)) || |
($node->vid > _get_current_revision_id($node->nid)) || |
| 768 |
(!$node->status && db_result(db_query("SELECT COUNT(vid) node_revisions WHERE nid=%d", $node->nid)) == 1); |
(!$node->status && db_result(db_query("SELECT COUNT(vid) FROM {node_revisions} WHERE nid=%d", $node->nid)) == 1); |
| 769 |
} |
} |
| 770 |
|
|
| 771 |
/** |
/** |
| 846 |
break; |
break; |
| 847 |
|
|
| 848 |
case 'view': |
case 'view': |
| 849 |
if (user_access("view revisions")) { |
if (user_access('view revisions') && (user_access('publish revisions') || user_access('administer nodes'))) { |
| 850 |
$order = variable_get('revisioning_block_order', OLDEST_AT_TOP) == OLDEST_AT_TOP ? 'ASC' : 'DESC'; |
$order = variable_get('revisioning_block_order', OLDEST_AT_TOP) == OLDEST_AT_TOP ? 'ASC' : 'DESC'; |
| 851 |
$nodes = get_nodes('update', NO_FILTER, NO_FILTER, TRUE, "timestamp $order"); |
$nodes = get_nodes('update', NO_FILTER, NO_FILTER, NO_FILTER, TRUE, "timestamp $order", 100, FALSE); |
| 852 |
if (!empty($nodes)) { |
if (!empty($nodes)) { |
| 853 |
return _theme_revisions_pending_block($nodes); |
return _theme_revisions_pending_block($nodes); |
| 854 |
} |
} |