[177a560] | 1 | <?php |
---|
| 2 | |
---|
| 3 | /** |
---|
| 4 | * @file |
---|
| 5 | * Allows the creation and modification of pre-published as well as live |
---|
| 6 | * content while the current revision remains unchanged and publicly visible |
---|
| 7 | * until the changes have been reviewed by a moderator. |
---|
| 8 | */ |
---|
| 9 | |
---|
| 10 | define('REVISIONING_LOAD_CURRENT', 0); // node/%node/view, node/%node/edit opens current revision |
---|
| 11 | define('REVISIONING_LOAD_LATEST', 1); // node/%node/view, node/%node/edit opens latest revison |
---|
| 12 | |
---|
| 13 | define('NEW_REVISION_WHEN_NOT_PENDING', 0); |
---|
| 14 | define('NEW_REVISION_EVERY_SAVE', 1); |
---|
| 15 | |
---|
| 16 | define('REVISIONS_BLOCK_OLDEST_AT_TOP', 0); |
---|
| 17 | define('REVISIONS_BLOCK_NEWEST_AT_TOP', 1); |
---|
| 18 | |
---|
| 19 | require_once drupal_get_path('module', 'revisioning') .'/revisioning_api.inc'; |
---|
| 20 | require_once drupal_get_path('module', 'revisioning') .'/revisioning.pages.inc'; |
---|
| 21 | require_once drupal_get_path('module', 'revisioning') .'/revisioning_theme.inc'; |
---|
| 22 | require_once drupal_get_path('module', 'revisioning') .'/revisioning_tokens.inc'; |
---|
| 23 | require_once drupal_get_path('module', 'revisioning') .'/revisioning_triggers_actions.inc'; |
---|
| 24 | // No need to include Rules integration file - Rules module does it for us automatically. |
---|
| 25 | |
---|
| 26 | /** |
---|
| 27 | * Implementation of hook_help(). |
---|
| 28 | */ |
---|
| 29 | function revisioning_help($path, $arg) { |
---|
| 30 | switch ($path) { |
---|
| 31 | case 'admin/help#revisioning': |
---|
| 32 | $s = t('For documentation and tutorials see the <a href="@revisioning">Revisioning project page</a>', |
---|
| 33 | array('@revisioning' => url('http://drupal.org/project/revisioning'))); |
---|
| 34 | break; |
---|
| 35 | case 'node/%/revisions': |
---|
| 36 | $s = t('To edit, publish or delete one of the revisions below, click on its saved date.'); |
---|
| 37 | break; |
---|
| 38 | case 'admin/build/trigger/revisioning': |
---|
| 39 | $s = t("Below you can assign actions to run when certain publication-related events happen. For example, you could send an e-mail to an author when their content is pubished."); |
---|
| 40 | break; |
---|
| 41 | case 'accessible-content/i-created/pending': |
---|
| 42 | $s = t('Showing all <em>pending</em> content <em>you created</em> and still have at least view access to.'); |
---|
| 43 | break; |
---|
| 44 | case 'accessible-content/i-last-modified/pending': |
---|
| 45 | $s = t('Showing all <em>pending</em> content <em>you last modified</em> and still have at least view access to.'); |
---|
| 46 | break; |
---|
| 47 | case 'accessible-content/i-can-edit/pending': |
---|
| 48 | $s = t('Showing all <em>pending</em> content you can <em>edit</em>.'); |
---|
| 49 | break; |
---|
| 50 | case 'accessible-content/i-can-view/pending': |
---|
| 51 | $s = t('Showing all <em>pending</em> content you have at least <em>view</em> access to.'); |
---|
| 52 | break; |
---|
| 53 | } |
---|
| 54 | return empty($s) ? '' : '<p>'. $s .'</p>'; |
---|
| 55 | } |
---|
| 56 | |
---|
| 57 | /** |
---|
| 58 | * Implementation of hook_perm(). |
---|
| 59 | * |
---|
| 60 | * Revisioning permissions. Note that permissions to view, revert and delete |
---|
| 61 | * revisions already exist in node.module. |
---|
| 62 | */ |
---|
| 63 | function revisioning_perm() { |
---|
| 64 | $perms = module_exists('module_grants_monitor') ? array('access Pending tab') : array(); |
---|
| 65 | $perms = array_merge($perms, array('view revision status messages', |
---|
| 66 | 'edit revisions', 'publish revisions', 'unpublish current revision')); |
---|
| 67 | // Add per node-type view perms in same way as edit perms of node module. |
---|
| 68 | // TOOD only do this for content types that have the "Create new revision" ticked |
---|
| 69 | foreach (node_get_types() as $type) { |
---|
| 70 | $name = check_plain($type->type); |
---|
| 71 | $perms[] = 'view revisions of own '. $name .' content'; |
---|
| 72 | $perms[] = 'view revisions of any '. $name .' content'; |
---|
| 73 | $perms[] = 'publish revisions of own '. $name .' content'; |
---|
| 74 | $perms[] = 'publish revisions of any '. $name .' content'; |
---|
| 75 | } |
---|
| 76 | return $perms; |
---|
| 77 | } |
---|
| 78 | |
---|
| 79 | /** |
---|
| 80 | * Implementation of hook_menu(). |
---|
| 81 | * |
---|
| 82 | * Define new menu items. |
---|
| 83 | * Existing menu items are modified through hook_menu_alter(). |
---|
| 84 | */ |
---|
| 85 | function revisioning_menu() { |
---|
| 86 | $items = array(); |
---|
| 87 | |
---|
| 88 | if (module_exists('module_grants_monitor')) { |
---|
| 89 | // Add a tab to the 'I created' tab (defined in module_grants_monitor.module) |
---|
| 90 | $items['accessible-content/i-created/pending'] = array( |
---|
| 91 | 'title' => 'In draft/Pending publication', |
---|
| 92 | 'page callback' => '_revisioning_show_pending_nodes', |
---|
| 93 | 'page arguments' => array('view', I_CREATED), |
---|
| 94 | 'access callback' => 'revisioning_user_all_access', |
---|
| 95 | 'access arguments' => array(array('access I Created tab', 'access Pending tab')), |
---|
| 96 | 'type' => MENU_LOCAL_TASK, |
---|
| 97 | 'weight' => -1 |
---|
| 98 | ); |
---|
| 99 | // Add a tab to the 'I last modified' tab |
---|
| 100 | $items['accessible-content/i-last-modified/pending'] = array( |
---|
| 101 | 'title' => 'In draft/Pending publication', |
---|
| 102 | 'page callback' => '_revisioning_show_pending_nodes', |
---|
| 103 | 'page arguments' => array('view', I_LAST_MODIFIED), |
---|
| 104 | 'access callback' => 'revisioning_user_all_access', |
---|
| 105 | 'access arguments' => array(array('access I Last Modified tab', 'access Pending tab')), |
---|
| 106 | 'type' => MENU_LOCAL_TASK, |
---|
| 107 | 'weight' => -1 |
---|
| 108 | ); |
---|
| 109 | // Add a tab to the 'I can edit' tab |
---|
| 110 | $items['accessible-content/i-can-edit/pending'] = array( |
---|
| 111 | 'title' => 'In draft/Pending publication', |
---|
| 112 | 'page callback' => '_revisioning_show_pending_nodes', |
---|
| 113 | 'page arguments' => array('update'), |
---|
| 114 | 'access callback' => 'revisioning_user_all_access', |
---|
| 115 | 'access arguments' => array(array('access I Can Edit tab', 'access Pending tab')), |
---|
| 116 | 'type' => MENU_LOCAL_TASK, |
---|
| 117 | 'weight' => -1 |
---|
| 118 | ); |
---|
| 119 | // Add a tab to the 'I can view' tab (defined in module_grants.module) |
---|
| 120 | $items['accessible-content/i-can-view/pending'] = array( |
---|
| 121 | 'title' => 'In draft/Pending publication', |
---|
| 122 | 'page callback' => '_revisioning_show_pending_nodes', |
---|
| 123 | 'page arguments' => array('view'), |
---|
| 124 | 'access callback' => 'revisioning_user_all_access', |
---|
| 125 | 'access arguments' => array(array('access I Can View tab', 'access Pending tab')), |
---|
| 126 | 'type' => MENU_LOCAL_TASK, |
---|
| 127 | 'weight' => -1 |
---|
| 128 | ); |
---|
| 129 | } |
---|
| 130 | |
---|
| 131 | // Callback (not a tab) to allow users to unpublish a node. |
---|
| 132 | // Note that is a node operation more so than a revision operation, but |
---|
| 133 | // we let _revisioning_node_revision_access() handle access anyway. |
---|
| 134 | $items['node/%node/unpublish'] = array( |
---|
| 135 | //'title' => t(Unpublish current revision'), |
---|
| 136 | 'page callback' => 'drupal_get_form', |
---|
| 137 | 'page arguments' => array('revisioning_unpublish_confirm', 1), |
---|
| 138 | 'access callback' => '_revisioning_node_revision_access', // _revisioning_node_access ? |
---|
| 139 | 'access arguments' => array('unpublish current revision', 1), |
---|
| 140 | 'type' => MENU_CALLBACK, |
---|
| 141 | ); |
---|
| 142 | |
---|
| 143 | // Revision tab local subtasks (i.e. secondary tabs), up to 7 of them: |
---|
| 144 | // view, edit, publish, unpublish, revert, delete and compare. |
---|
| 145 | // All revision operations 'node/%node/revisions/%vid/<op>' are defined as |
---|
| 146 | // local tasks (tabs) secondary to the primary 'node/%node/revisions' local |
---|
| 147 | // task (tab). |
---|
| 148 | // The tricky part is to always set "tab_parent", core does NOT figure this |
---|
| 149 | // out based on the URL. %vid is optional, see vid_to_arg(). |
---|
| 150 | // Note: the MENU_DEFAULT_LOCAL_TASK for 'node/%node/revisions' is defined in |
---|
| 151 | // function revisioning_menu_alter() |
---|
| 152 | |
---|
| 153 | // View revision local subtask |
---|
| 154 | $items['node/%node/revisions/%vid/view'] = array( |
---|
| 155 | 'title' => 'View', |
---|
| 156 | 'load arguments' => array(3), |
---|
| 157 | 'page callback' => '_revisioning_view_revision', |
---|
| 158 | 'page arguments' => array(1), |
---|
| 159 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 160 | 'access arguments' => array('view revisions', 1), |
---|
| 161 | 'type' => MENU_LOCAL_TASK, |
---|
| 162 | 'weight' => -10, |
---|
| 163 | 'tab_parent' => 'node/%/revisions', |
---|
| 164 | ); |
---|
| 165 | // Edit revision local subtask |
---|
| 166 | $items['node/%node/revisions/%vid/edit'] = array( |
---|
| 167 | 'title' => 'Edit', |
---|
| 168 | 'load arguments' => array(3), |
---|
| 169 | 'page callback' => '_revisioning_edit_revision', |
---|
| 170 | 'page arguments' => array(1), |
---|
| 171 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 172 | 'access arguments' => array('edit revisions', 1), |
---|
| 173 | 'file' => 'node.pages.inc', |
---|
| 174 | 'file path' => drupal_get_path('module', 'node'), |
---|
| 175 | 'type' => MENU_LOCAL_TASK, |
---|
| 176 | 'weight' => -7, |
---|
| 177 | 'tab_parent' => 'node/%/revisions', |
---|
| 178 | ); |
---|
| 179 | // Publish revision local subtask |
---|
| 180 | $items['node/%node/revisions/%vid/publish'] = array( |
---|
| 181 | 'title' => 'Publish this', |
---|
| 182 | 'load arguments' => array(3), |
---|
| 183 | 'page callback' => 'drupal_get_form', |
---|
| 184 | 'page arguments' => array('revisioning_publish_confirm', 1), |
---|
| 185 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 186 | 'access arguments' => array('publish revisions', 1), |
---|
| 187 | 'type' => MENU_LOCAL_TASK, |
---|
| 188 | 'weight' => -4, |
---|
| 189 | 'tab_parent' => 'node/%/revisions', |
---|
| 190 | ); |
---|
| 191 | // Unpublish node local subtask |
---|
| 192 | $items['node/%node/revisions/%vid/unpublish'] = array( |
---|
| 193 | 'title' => 'Unpublish this', |
---|
| 194 | 'load arguments' => array(3), |
---|
| 195 | 'page callback' => 'drupal_get_form', |
---|
| 196 | 'page arguments' => array('revisioning_unpublish_confirm', 1), |
---|
| 197 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 198 | 'access arguments' => array('unpublish current revision', 1), |
---|
| 199 | 'type' => MENU_LOCAL_TASK, |
---|
| 200 | 'weight' => -3, |
---|
| 201 | 'tab_parent' => 'node/%/revisions', |
---|
| 202 | ); |
---|
| 203 | // Revert to revision local subtask. |
---|
| 204 | // Difference from core version is %vid that's served by vid_to_arg() function. |
---|
| 205 | $items['node/%node/revisions/%vid/revert'] = array( |
---|
| 206 | 'title' => 'Revert to this', |
---|
| 207 | 'load arguments' => array(3), |
---|
| 208 | 'page callback' => 'drupal_get_form', |
---|
| 209 | 'page arguments' => array('node_revision_revert_confirm', 1), |
---|
| 210 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 211 | 'access arguments' => array('revert revisions', 1), |
---|
| 212 | 'file' => 'node.pages.inc', |
---|
| 213 | 'file path' => drupal_get_path('module', 'node'), |
---|
| 214 | 'type' => MENU_LOCAL_TASK, |
---|
| 215 | 'weight' => -2, |
---|
| 216 | 'tab_parent' => 'node/%/revisions', |
---|
| 217 | ); |
---|
| 218 | // Delete revision local subtask. |
---|
| 219 | // Difference from core version is %vid that's served by vid_to_arg() function. |
---|
| 220 | $items['node/%node/revisions/%vid/delete'] = array( |
---|
| 221 | 'title' => 'Delete', |
---|
| 222 | 'load arguments' => array(3), |
---|
| 223 | 'page callback' => 'drupal_get_form', |
---|
| 224 | 'page arguments' => array('node_revision_delete_confirm', 1), |
---|
| 225 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 226 | 'access arguments' => array('delete revisions', 1), |
---|
| 227 | 'file' => 'node.pages.inc', |
---|
| 228 | 'file path' => drupal_get_path('module', 'node'), |
---|
| 229 | 'type' => MENU_LOCAL_TASK, |
---|
| 230 | 'weight' => 10, |
---|
| 231 | 'tab_parent' => 'node/%/revisions', |
---|
| 232 | ); |
---|
| 233 | |
---|
| 234 | // If Diff module is enabled, provide a compare local subtask |
---|
| 235 | if (module_exists('diff')) { |
---|
| 236 | $items['node/%node/revisions/%vid/compare'] = array( |
---|
| 237 | 'title' => 'Compare to current', |
---|
| 238 | 'load arguments' => array(3), |
---|
| 239 | 'page callback' => '_revisioning_compare_to_current_revision', |
---|
| 240 | 'page arguments' => array(1), |
---|
| 241 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 242 | 'access arguments' => array('compare to current', 1), |
---|
| 243 | 'type' => MENU_LOCAL_TASK, |
---|
| 244 | 'weight' => 0, |
---|
| 245 | 'tab_parent' => 'node/%/revisions', |
---|
| 246 | ); |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | // Finally, the Revisioning configuration menu item |
---|
| 250 | $items['admin/settings/revisioning'] = array( |
---|
| 251 | 'title' => 'Revisioning', |
---|
| 252 | 'description' => 'Configure how links to view and edit content behave.', |
---|
| 253 | 'page callback' => 'drupal_get_form', |
---|
| 254 | 'page arguments' => array('revisioning_admin_configure'), |
---|
| 255 | 'access arguments' => array('administer site configuration'), |
---|
| 256 | 'file' => 'revisioning.admin.inc' |
---|
| 257 | ); |
---|
| 258 | |
---|
| 259 | return $items; |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | /** |
---|
| 263 | * Implementation of hook_menu_alter(). |
---|
| 264 | * |
---|
| 265 | * Modify menu items defined in other modules (in particular the Node and |
---|
| 266 | * Module Grants modules). |
---|
| 267 | */ |
---|
| 268 | function revisioning_menu_alter(&$items) { |
---|
| 269 | |
---|
| 270 | // Primary tabs for 'node/%node': View tab, Edit tab, Revisions tab ... |
---|
| 271 | |
---|
| 272 | // View tab can be either 'View current' or 'View latest'. |
---|
| 273 | // It should be suppressed when the 'Revisions' tab shows the same revision, |
---|
| 274 | // so we need a special access callback for this, which expands on the |
---|
| 275 | // callback defined in Module Grants. |
---|
| 276 | $items['node/%node']['access callback'] = $items['node/%node/view']['access callback'] = '_revisioning_view_edit_access_callback'; |
---|
| 277 | $items['node/%node']['access arguments']= $items['node/%node/view']['access arguments']= array('view', 1); |
---|
| 278 | $items['node/%node']['page callback'] = $items['node/%node/view']['page callback'] = '_revisioning_view'; |
---|
| 279 | $items['node/%node']['page arguments'] = $items['node/%node/view']['page arguments'] = array(1); |
---|
| 280 | // Not applying title callback to 'node/%node', see #782316 |
---|
| 281 | $items['node/%node/view']['title callback'] = '_revisioning_title_for_tab'; |
---|
| 282 | $items['node/%node/view']['title arguments'] = array(1, FALSE); |
---|
| 283 | |
---|
| 284 | // Edit tab can be either 'Edit current' or 'Edit latest'. |
---|
| 285 | // It should be suppressed when the 'Revisions' tab shows the same revision, |
---|
| 286 | // so we need a special access callback for this, which expands on the |
---|
| 287 | // callback defined in Module Grants. |
---|
| 288 | $items['node/%node/edit']['access callback'] = '_revisioning_view_edit_access_callback'; |
---|
| 289 | $items['node/%node/edit']['access arguments']= array('edit', 1); |
---|
| 290 | $items['node/%node/edit']['page callback'] = '_revisioning_edit'; |
---|
| 291 | $items['node/%node/edit']['title callback'] = '_revisioning_title_for_tab'; |
---|
| 292 | $items['node/%node/edit']['title arguments'] = array(1, TRUE); |
---|
| 293 | |
---|
| 294 | // 'Revisions' tab remains but points to new page callback, allowing users to |
---|
| 295 | // pick the revision to view, edit, publish, revert, unpublish, delete. |
---|
| 296 | // Need to override _node_revision_access() call back as it disallows access |
---|
| 297 | // to the 'Revisions' tab when there's only one revision, which will prevent |
---|
| 298 | // users from getting to the publish/unpublish links. |
---|
| 299 | $items['node/%node/revisions']['access callback'] = '_revisioning_node_revision_access'; |
---|
| 300 | $items['node/%node/revisions']['access arguments'] = array('view revision list', 1); |
---|
| 301 | $items['node/%node/revisions']['page callback'] = '_revisioning_present_node'; |
---|
| 302 | $items['node/%node/revisions']['page arguments'] = array(1); |
---|
| 303 | |
---|
| 304 | // Unset old menu items defined in node.module (or module_grants.module), as |
---|
| 305 | // these are replaced by ones that use the %vid wildcard instead of % and |
---|
| 306 | // come with the appropriate callbacks. |
---|
| 307 | unset($items['node/%node/revisions/%/view']); |
---|
| 308 | unset($items['node/%node/revisions/%/revert']); |
---|
| 309 | unset($items['node/%node/revisions/%/delete']); |
---|
| 310 | |
---|
| 311 | if (module_exists('diff')) { |
---|
| 312 | // If Diff module is enabled, make sure it uses correct access callback |
---|
| 313 | $items['node/%node/revisions/view/%/%']['access callback'] = '_revisioning_node_revision_access'; |
---|
| 314 | $items['node/%node/revisions/view/%/%']['access arguments'] = array('view revisions', 1); |
---|
| 315 | } |
---|
| 316 | // This is here rather than in revisioning_menu() as Diff may redefine |
---|
| 317 | // the node/%node/revisions/list item. |
---|
| 318 | $items['node/%node/revisions/list'] = array( |
---|
| 319 | 'title' => t('List all revisions'), |
---|
| 320 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 321 | 'access arguments' => array('view revision list', 1), |
---|
| 322 | 'file' => 'node.pages.inc', |
---|
| 323 | 'module' => 'node', |
---|
| 324 | //'file path' => drupal_get_path('module', 'node'), |
---|
| 325 | 'type' => MENU_LOCAL_TASK, // was: MENU_DEFAULT_LOCAL_TASK; changed for Smart tabs |
---|
| 326 | 'weight' => -20, |
---|
| 327 | ); |
---|
| 328 | |
---|
| 329 | $items['node/%node/revisions/delete-archived'] = array( |
---|
| 330 | 'title' => t('Delete archived revisions'), |
---|
| 331 | 'page callback' => 'drupal_get_form', |
---|
| 332 | 'page arguments' => array('revisioning_delete_archived_confirm', 1), |
---|
| 333 | 'access callback' => '_revisioning_node_revision_access', |
---|
| 334 | 'access arguments' => array('delete archived revisions', 1), |
---|
| 335 | 'type' => MENU_CALLBACK, |
---|
| 336 | ); |
---|
| 337 | |
---|
| 338 | // Apart from administrators, allow those that pass the 'trigger_access_check' |
---|
| 339 | // to configure the revisioning triggers. This means that users must have at |
---|
| 340 | // least 'administer actions' and 'access administration pages' (the latter is |
---|
| 341 | // to allow them to navigate to the trigger page via the menu). |
---|
| 342 | if (module_exists('trigger')) { |
---|
| 343 | $items['admin/build/trigger/revisioning']['access callback'] = 'trigger_access_check'; |
---|
| 344 | } |
---|
| 345 | |
---|
| 346 | // [#1024864]: Allow other modules to make further alterations |
---|
| 347 | drupal_alter('revisioning_menu', $items); |
---|
| 348 | } |
---|
| 349 | |
---|
| 350 | /** |
---|
| 351 | * Implementation of hook_nodeapi(). |
---|
| 352 | * |
---|
| 353 | * This function is called serveral times during the node's life cycle, with |
---|
| 354 | * different node operations passed in. |
---|
| 355 | * |
---|
| 356 | * Typically when loading a node for viewing, the order is: |
---|
| 357 | * 'load', 'view', 'alter' |
---|
| 358 | * |
---|
| 359 | * When creating new content: |
---|
| 360 | * Before displaying the creation form: 'prepare' |
---|
| 361 | * When saving: 'validate', 'presave', 'insert' |
---|
| 362 | * |
---|
| 363 | * When editing an existing node: |
---|
| 364 | * Before displaying the edit form: 'load', 'prepare' |
---|
| 365 | * When saving: 'load', 'validate', 'presave', 'update' |
---|
| 366 | * |
---|
| 367 | * The same $op may be requested multiple times during the same HTTP request, |
---|
| 368 | * especially 'load'. |
---|
| 369 | */ |
---|
| 370 | function revisioning_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { |
---|
| 371 | if (!empty($node->bypass_nodeapi)) { |
---|
| 372 | return; |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | switch ($op) { |
---|
| 376 | case 'load': // called at the end of node_load() |
---|
| 377 | // The revision_moderation flag may be overridden on the node edit form |
---|
| 378 | // by users with the "administer nodes" permission |
---|
| 379 | $node->revision_moderation = node_tools_content_is_moderated($node->type); |
---|
| 380 | $node->is_pending = _revisioning_node_is_pending($node); |
---|
| 381 | |
---|
| 382 | // Could use following, but this seems old style, i.e. when $node is not a &ref |
---|
| 383 | //$node_extras['revision_moderation'] = $node->revision_moderation; |
---|
| 384 | //$node_extras['is_pending'] = $node->is_pending; |
---|
| 385 | break; |
---|
| 386 | |
---|
| 387 | case 'view': // called from called from node_view() before $node is fully built |
---|
| 388 | break; |
---|
| 389 | |
---|
| 390 | case 'alter': // called from node_view() after $node is fully built for display |
---|
| 391 | if (!$teaser && $node->nid == arg(1) && // don't show msg on page with many nodes |
---|
| 392 | !empty($node->revision_moderation) && user_access('view revision status messages')) { |
---|
| 393 | drupal_set_message(_revisioning_node_info_msg($node)); |
---|
| 394 | } |
---|
| 395 | break; |
---|
| 396 | |
---|
| 397 | case 'prepare': // presenting edit form |
---|
| 398 | // Check that we're dealing with the current node, not its translation source |
---|
| 399 | $current_nid = arg(1); |
---|
| 400 | if (is_numeric($current_nid) && $current_nid == $node->nid) { |
---|
| 401 | _revisioning_prepare_msg($node); |
---|
| 402 | } |
---|
| 403 | break; |
---|
| 404 | |
---|
| 405 | case 'presave': // for edits, called from node_save(), prior to _node_save_revision() |
---|
| 406 | if (!empty($node->revision_moderation)) { // Tick-box on edit form |
---|
| 407 | $node->is_pending = _revisioning_node_is_pending($node); |
---|
| 408 | if ($node->revision && $node->is_pending && variable_get('new_revisions_'. $node->type, NEW_REVISION_WHEN_NOT_PENDING) == NEW_REVISION_WHEN_NOT_PENDING) { |
---|
| 409 | drupal_set_message(t('Updating existing copy, not creating new revision as this one is still pending.')); |
---|
| 410 | // Update the $node object just before it is saved to the db |
---|
| 411 | $node->revision = FALSE; |
---|
| 412 | } |
---|
| 413 | // Save title of current revision to restore at $op=='update' time |
---|
| 414 | $node->current_title = db_result(db_query('SELECT title FROM {node} WHERE nid=%d', $node->nid)); |
---|
| 415 | |
---|
| 416 | if (variable_get('revisioning_auto_publish_'. $node->type, FALSE)) { |
---|
| 417 | if (user_access('publish revisions') || |
---|
| 418 | user_access("publish revisions of any $node->type content") || |
---|
| 419 | (user_access("publish revisions of own $node->type content") && $node->revision_uid == $user->uid)) { |
---|
| 420 | if (!$node->status) { |
---|
| 421 | // Fix for [#751092] (thanks naquah and mirgorod_a). |
---|
| 422 | // If the Publish box has just been unticked, do not auto-publish. |
---|
| 423 | if (isset($node->nid)) { |
---|
| 424 | // Existing published node for which Publish box was unticked. |
---|
| 425 | if (db_result(db_query("SELECT status FROM {node} WHERE nid = %d", $node->nid))) { |
---|
| 426 | break; |
---|
| 427 | } |
---|
| 428 | } |
---|
| 429 | else { |
---|
| 430 | $node_options = variable_get('node_options_'. $node->type, array()); |
---|
| 431 | if (in_array('status', $node_options)) { |
---|
| 432 | // New node for which Publish box is ticked by default but was |
---|
| 433 | // unticked on the edit form. |
---|
| 434 | break; |
---|
| 435 | } |
---|
| 436 | } |
---|
| 437 | } |
---|
| 438 | drupal_set_message(t('Auto-publishing this revision.')); |
---|
| 439 | $node->status = TRUE; |
---|
| 440 | // Make sure the 'update' does NOT reset vid, so that new revision becomes current |
---|
| 441 | unset($node->current_revision_id); |
---|
| 442 | } |
---|
| 443 | } |
---|
| 444 | } |
---|
| 445 | break; |
---|
| 446 | |
---|
| 447 | case 'insert': // new node, called from node_save(), after _node_save_revision() |
---|
| 448 | if (!empty($node->revision_moderation)) { |
---|
| 449 | _revisioning_insert_msg($node); |
---|
| 450 | } |
---|
| 451 | break; |
---|
| 452 | |
---|
| 453 | case 'update': // edited node, called from node_save(), after _node_save_revision() |
---|
| 454 | if (!empty($node->revision_moderation) && $node->current_revision_id // i.e. set and not 0 |
---|
| 455 | && $node->current_revision_id != $node->vid) { |
---|
| 456 | // Resetting title and vid back to their originial values, thus creating pending revision. |
---|
| 457 | db_query("UPDATE {node} SET vid=%d, title='%s' WHERE nid=%d", $node->current_revision_id, $node->current_title, $node->nid); |
---|
| 458 | //$node->is_pending = TRUE; |
---|
| 459 | } |
---|
| 460 | break; |
---|
| 461 | |
---|
| 462 | case 'delete revision': |
---|
| 463 | module_invoke_all('revisionapi', 'post delete', $node); |
---|
| 464 | break; |
---|
| 465 | } |
---|
| 466 | } |
---|
| 467 | |
---|
| 468 | /** |
---|
| 469 | * Implementation of hook_block(). |
---|
| 470 | * |
---|
| 471 | * A block that may be placed on all or selected pages, alerting the user |
---|
| 472 | * (moderator) when new content has been submitted for review. Shows titles |
---|
| 473 | * of pending revisions as a series of links (max. number configurable). |
---|
| 474 | * Clicking a link takes the moderator straight to the revision in question. |
---|
| 475 | */ |
---|
| 476 | function revisioning_block($op = 'list', $delta = 0, $edit = array()) { |
---|
| 477 | switch ($op) { |
---|
| 478 | case 'list': |
---|
| 479 | // Set up the defaults for the Site configuration>>Blocks page |
---|
| 480 | // Return a list of (1) block(s) and the default values |
---|
| 481 | $blocks[0]['info'] = t('Pending revisions'); |
---|
| 482 | $blocks[0]['cache'] = BLOCK_NO_CACHE; |
---|
| 483 | $blocks[0]['weight'] = -10; // top of whatever region is chosen |
---|
| 484 | $blocks[0]['custom'] = FALSE; // block is implemented by this module; |
---|
| 485 | return $blocks; |
---|
| 486 | |
---|
| 487 | case 'configure': |
---|
| 488 | $form['revisioning_block_num_pending'] = array( |
---|
| 489 | '#type' => 'textfield', |
---|
| 490 | '#title' => t('Maximum number of pending revisions displayed'), |
---|
| 491 | '#default_value' => variable_get('revisioning_block_num_pending', 5), |
---|
| 492 | '#description' => t('Note: the title of this block mentions the total number of revisions pending, which may be greater than the number of revisions displayed.') |
---|
| 493 | ); |
---|
| 494 | $form['revisioning_block_order'] = array( |
---|
| 495 | '#type' => 'radios', |
---|
| 496 | '#title' => t('Order in which pending revisions are displayed'), |
---|
| 497 | '#options' => array( |
---|
| 498 | REVISIONS_BLOCK_OLDEST_AT_TOP => t('Oldest at top'), |
---|
| 499 | REVISIONS_BLOCK_NEWEST_AT_TOP => t('Newest at top')), |
---|
| 500 | '#default_value' => variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP), |
---|
| 501 | '#description' => t('Note: order is based on revision timestamps.') |
---|
| 502 | ); |
---|
| 503 | $form['revisioning_content_summary_page'] = array( |
---|
| 504 | '#type' => 'textfield', |
---|
| 505 | '#title' => t('Page to go to when the block title is clicked'), |
---|
| 506 | '#default_value' => variable_get('revisioning_content_summary_page', ''), |
---|
| 507 | '#description' => t('When left blank this will default to %accessible_content, provided Module Grants Montior is enabled and the user has sufficient permissions. Otherwise %admin_content is used, subject to permissions. For any of this to work the above <strong>Block title</strong> field must be left blank.', |
---|
| 508 | array('%accessible_content' => 'accessible-content', '%admin_content' => 'admin/content/node')) |
---|
| 509 | ); |
---|
| 510 | return $form; |
---|
| 511 | |
---|
| 512 | case 'save': |
---|
| 513 | variable_set('revisioning_block_num_pending', (int)$edit['revisioning_block_num_pending']); |
---|
| 514 | variable_set('revisioning_block_order', (int)$edit['revisioning_block_order']); |
---|
| 515 | variable_set('revisioning_content_summary_page', $edit['revisioning_content_summary_page']); |
---|
| 516 | break; |
---|
| 517 | |
---|
| 518 | case 'view': |
---|
| 519 | $max_nodes = variable_get('revisioning_block_num_pending', 100); |
---|
| 520 | $order = variable_get('revisioning_block_order', REVISIONS_BLOCK_OLDEST_AT_TOP) == REVISIONS_BLOCK_OLDEST_AT_TOP ? 'ASC' : 'DESC'; |
---|
| 521 | $nodes = node_tools_get_nodes('update', NO_FILTER, NO_FILTER, NO_FILTER, TRUE, TRUE, $max_nodes, 'timestamp '. $order); |
---|
| 522 | if (!empty($nodes)) { |
---|
| 523 | return _theme_revisions_pending_block($nodes); |
---|
| 524 | } |
---|
| 525 | } |
---|
| 526 | } |
---|
| 527 | |
---|
| 528 | /** |
---|
| 529 | * Implementation of hook_views_api(). |
---|
| 530 | */ |
---|
| 531 | function revisioning_views_api() { |
---|
| 532 | return array( |
---|
| 533 | 'api' => views_api_version(), |
---|
| 534 | 'path' => drupal_get_path('module', 'revisioning') .'/views' |
---|
| 535 | ); |
---|
| 536 | } |
---|
| 537 | |
---|
| 538 | /** |
---|
| 539 | * Perform path manipulations for menu items containing %vid wildcard. $map |
---|
| 540 | * contains what arg() function returns, eg. $map[0]=='node', $map[1]=='123'. |
---|
| 541 | * |
---|
| 542 | * When vid is absent, return $map as empty array. This seems to disable menu |
---|
| 543 | * items which require a vid context to work. So on the page |
---|
| 544 | * "node/123/revisions" we won't see tasks like "node/123/revisions/456/edit". |
---|
| 545 | * |
---|
| 546 | * An alternative implementation would be to substitute an empty vid with |
---|
| 547 | * current revision id. In that case we should also change the tab titles |
---|
| 548 | * (via title callbacks) for an enhanced user experience. For example: we'd |
---|
| 549 | * change "Edit" to "Edit current". |
---|
| 550 | * |
---|
| 551 | * See http://drupal.org/node/500864 |
---|
| 552 | */ |
---|
| 553 | function vid_to_arg($arg, &$map, $index) { |
---|
| 554 | if (empty($arg)) { |
---|
| 555 | //return node_tools_get_current_node_revision_id($nid = $map[1]); |
---|
| 556 | $map = array(); |
---|
| 557 | return ''; |
---|
| 558 | } |
---|
| 559 | return $arg; |
---|
| 560 | } |
---|
| 561 | |
---|
| 562 | /** |
---|
| 563 | * Implementation of hook_user_node_access(). |
---|
| 564 | * |
---|
| 565 | * @see module_grants_node_revision_access() |
---|
| 566 | * |
---|
| 567 | * @param $revision_op |
---|
| 568 | * node or revision operation e.g. 'view revisions' |
---|
| 569 | * @param $node |
---|
| 570 | * @return the associated node operation required for this revision_op, or |
---|
| 571 | * FALSE if access to the node is to be denied. |
---|
| 572 | * Valid node operations to return are 'view', 'update', 'delete'. |
---|
| 573 | */ |
---|
| 574 | function revisioning_user_node_access($revision_op, $node) { |
---|
| 575 | global $user; |
---|
| 576 | |
---|
| 577 | $type = check_plain($node->type); |
---|
| 578 | |
---|
| 579 | switch ($revision_op) { |
---|
| 580 | case 'view current': |
---|
| 581 | break; |
---|
| 582 | case 'compare to current': |
---|
| 583 | case 'view revisions': |
---|
| 584 | case 'view revision list': |
---|
| 585 | if (user_access('view revisions', $user)) { // node.module |
---|
| 586 | break; |
---|
| 587 | } |
---|
| 588 | if (user_access('view revisions of any '. $type .' content', $user)) { |
---|
| 589 | break; |
---|
| 590 | } |
---|
| 591 | if (($node->uid == $user->uid) && user_access('view revisions of own '. $type .' content', $user)) { |
---|
| 592 | break; |
---|
| 593 | } |
---|
| 594 | return FALSE; |
---|
| 595 | |
---|
| 596 | case 'edit current': |
---|
| 597 | return 'update'; |
---|
| 598 | |
---|
| 599 | case 'edit revisions': |
---|
| 600 | case 'revert revisions': |
---|
| 601 | return user_access($revision_op, $user) ? 'update' : FALSE; |
---|
| 602 | |
---|
| 603 | case 'publish revisions': |
---|
| 604 | if (user_access('publish revisions of any '. $type .' content', $user)) { |
---|
| 605 | break; |
---|
| 606 | } |
---|
| 607 | if (($node->uid == $user->uid) && user_access('publish revisions of own '. $type .' content', $user)) { |
---|
| 608 | break; |
---|
| 609 | } |
---|
| 610 | case 'unpublish current revision': |
---|
| 611 | return user_access($revision_op, $user) ? 'view' : FALSE; |
---|
| 612 | |
---|
| 613 | case 'delete revisions': |
---|
| 614 | case 'delete archived revisions': |
---|
| 615 | if (!user_access('delete revisions', $user)) { |
---|
| 616 | return FALSE; |
---|
| 617 | } |
---|
| 618 | case 'delete node': |
---|
| 619 | return 'delete'; |
---|
| 620 | |
---|
| 621 | default: |
---|
| 622 | drupal_set_message(t("Unknown Revisioning operation '%op'. Treating as 'view'.", array('%op' => $revision_op)), 'warning', FALSE); |
---|
| 623 | } |
---|
| 624 | return 'view'; |
---|
| 625 | } |
---|
| 626 | |
---|
| 627 | /** |
---|
| 628 | * Test whether the supplied revision operation is appropriate for the node. |
---|
| 629 | * This is irrespective of user permissions, e.g. even for an administrator it |
---|
| 630 | * doesn't make sense to publish a node that is already published or to |
---|
| 631 | * "revert" to the current revision. |
---|
| 632 | * |
---|
| 633 | * @param $revision_op |
---|
| 634 | * @param $node |
---|
| 635 | * @return TRUE if the operation is appropriate for this node at this point |
---|
| 636 | */ |
---|
| 637 | function _revisioning_operation_appropriate($revision_op, $node) { |
---|
| 638 | switch ($revision_op) { |
---|
| 639 | |
---|
| 640 | case 'compare to current': |
---|
| 641 | // Can't compare against itself |
---|
| 642 | case 'delete revisions': |
---|
| 643 | // If the revision is the current one, suppress the delete operation |
---|
| 644 | // @TODO ...unless it's the only revision, in which case delete the |
---|
| 645 | // entire node; however this requires a different URL. |
---|
| 646 | return !$node->is_current; |
---|
| 647 | |
---|
| 648 | case 'delete archived revisions': |
---|
| 649 | break; |
---|
| 650 | |
---|
| 651 | case 'view revision list': // i.e. node revisions summary |
---|
| 652 | if ($node->num_revisions == 1 && !$node->revision_moderation |
---|
| 653 | /* && (module_exists('module_grants') ? !module_grants_node_access('delete', $node) : !node_access('delete', $node))*/) { |
---|
| 654 | // Suppress Revisions tab when when there's only 1 revision -- consistent with core. |
---|
| 655 | // However, when content is moderated (i.e. "New revision in draft, |
---|
| 656 | // pending moderation" is ticked) we want to be able to get to the |
---|
| 657 | // 'Unpublish current' link on this page and the 'Publish this' tab on |
---|
| 658 | // the next. Also when user has permission to delete node, we need to |
---|
| 659 | // present the Delete link, unless we assume that this privilege |
---|
| 660 | // assumes the 'edit' permission. |
---|
| 661 | return FALSE; |
---|
| 662 | } |
---|
| 663 | break; |
---|
| 664 | |
---|
| 665 | case 'publish revisions': |
---|
| 666 | // If the node isn't meant to be moderated and the user is not an admin, |
---|
| 667 | // or the revision is not either pending or current but not published, |
---|
| 668 | // then disallow publication. |
---|
| 669 | if ((!$node->revision_moderation && !user_access('administer nodes')) |
---|
| 670 | || !($node->is_pending || ($node->is_current && !$node->status))) { |
---|
| 671 | return FALSE; |
---|
| 672 | } |
---|
| 673 | break; |
---|
| 674 | |
---|
| 675 | case 'unpublish current revision': |
---|
| 676 | // If the node isn't meant to be moderated and the user is not an admin, |
---|
| 677 | // or it is unpublished already or we're not looking at the current |
---|
| 678 | // revision, then unpublication is not an option. |
---|
| 679 | if ((!$node->revision_moderation && !user_access('administer nodes')) |
---|
| 680 | || !$node->status || !$node->is_current) { |
---|
| 681 | return FALSE; |
---|
| 682 | } |
---|
| 683 | break; |
---|
| 684 | |
---|
| 685 | case 'revert revisions': |
---|
| 686 | // If this revision is pending or current, suppress the reversion |
---|
| 687 | if ($node->is_pending || $node->is_current) { |
---|
| 688 | return FALSE; |
---|
| 689 | } |
---|
| 690 | break; |
---|
| 691 | } |
---|
| 692 | return TRUE; |
---|
| 693 | } |
---|
| 694 | |
---|
| 695 | /** |
---|
| 696 | * Determine whether the supplied revision operation is permitted on the node. |
---|
| 697 | * This requires getting through three levels of defence: |
---|
| 698 | * o Is the operation appropriate for this node at this time, e.g. a node must |
---|
| 699 | * not be published if it already is or if it isn't under moderation control |
---|
| 700 | * o Does the user have permissions to operations of this kind in general? |
---|
| 701 | * o Does the user have the node access rights (view/update/delete) required |
---|
| 702 | * for this operation? |
---|
| 703 | * |
---|
| 704 | * @param $revision_op |
---|
| 705 | * For instance 'publish revisions', 'delete revisions' |
---|
| 706 | * @param $node |
---|
| 707 | * @return bool |
---|
| 708 | */ |
---|
| 709 | function _revisioning_node_revision_access($revision_op, $node) { |
---|
| 710 | |
---|
| 711 | if (!isset($node->num_revisions) || !isset($node->is_current)) { |
---|
| 712 | drupal_set_message(t('Node object data incomplete -- have you enabled the Node Tools submodule?'), 'warning', FALSE); |
---|
| 713 | } |
---|
| 714 | if (!_revisioning_operation_appropriate($revision_op, $node)) { |
---|
| 715 | return FALSE; |
---|
| 716 | } |
---|
| 717 | if (module_exists('module_grants')) { |
---|
| 718 | $access = module_grants_node_revision_access($revision_op, $node); |
---|
| 719 | } |
---|
| 720 | else { |
---|
| 721 | // Fall back to core to assess permissions (even though they suck) |
---|
| 722 | $access = ($node_op = revisioning_user_node_access($revision_op, $node)) && |
---|
| 723 | node_access($node_op, $node); |
---|
| 724 | } |
---|
| 725 | return $access; |
---|
| 726 | } |
---|
| 727 | |
---|
| 728 | /** |
---|
| 729 | * Access callback for 'node/%', 'node/%/view' and 'node/%/edit' links that |
---|
| 730 | * may appear anywhere on the site. |
---|
| 731 | * At the time that this function is called the CURRENT revision will already |
---|
| 732 | * have been loaded by the system. However depending on the value of the |
---|
| 733 | * 'revisioning_view_callback' and 'revisioning_edit_callback' variables (as |
---|
| 734 | * set on the admin/settings/revisioning page), this may not be the desired |
---|
| 735 | * revision. |
---|
| 736 | * If these variables state that the LATEST revision should be loaded, we need |
---|
| 737 | * to check at this point whether the user has permission to view this revision. |
---|
| 738 | * |
---|
| 739 | * The 'View current' and/or 'Edit current' tabs are suppressed when the current |
---|
| 740 | * revision is already displayed via one of the Revisions subtabs. |
---|
| 741 | * The 'View latest' and/or 'Edit latest' tabs are suppressed when the latest |
---|
| 742 | * revision is already displayed via one of the Revisions subtabs. |
---|
| 743 | * |
---|
| 744 | * @param op, must be one of 'view' or 'edit' |
---|
| 745 | * @param $node |
---|
| 746 | * @return FALSE if access to the desired revision is denied |
---|
| 747 | * |
---|
| 748 | */ |
---|
| 749 | function _revisioning_view_edit_access_callback($op, $node) { |
---|
| 750 | |
---|
| 751 | $load_op = _revisioning_load_op($node, $op); |
---|
| 752 | |
---|
| 753 | $vid = arg(3); |
---|
| 754 | if (/*!empty($node->revision_moderation) && */is_numeric($vid)) { |
---|
| 755 | // The View, Edit primary tabs are requested indirectly, in the context of |
---|
| 756 | // the secondary tabs under Revisions, e.g. node/%/revisions/% |
---|
| 757 | if ($load_op == REVISIONING_LOAD_CURRENT && $vid == $node->current_revision_id) { |
---|
| 758 | // Suppress 'View current' and 'Edit current' primary tabs when viewing current |
---|
| 759 | return FALSE; |
---|
| 760 | } |
---|
| 761 | if ($load_op == REVISIONING_LOAD_LATEST && $vid == revisioning_get_latest_revision_id($node->nid)) { |
---|
| 762 | // Suppress 'View latest' and 'Edit latest' primary tabs when viewing latest |
---|
| 763 | return FALSE; |
---|
| 764 | } |
---|
| 765 | } |
---|
| 766 | if ($load_op == REVISIONING_LOAD_LATEST) { |
---|
| 767 | // _revisioning_load_op has already checked permission to view latest |
---|
| 768 | return TRUE; |
---|
| 769 | } |
---|
| 770 | $revision_op = ($op == 'view') ? 'view current' : 'edit current'; |
---|
| 771 | return _revisioning_node_revision_access($revision_op, $node); |
---|
| 772 | } |
---|
| 773 | |
---|
| 774 | function _revisioning_load_op($node, $op, $check_access = TRUE) { |
---|
| 775 | if ($node->revision_moderation) { |
---|
| 776 | $view_mode = (int)variable_get('revisioning_view_callback', REVISIONING_LOAD_CURRENT); |
---|
| 777 | $edit_mode = (int)variable_get('revisioning_edit_callback', REVISIONING_LOAD_CURRENT); |
---|
| 778 | $load_op = ($op == 'edit') ? $edit_mode : $view_mode; |
---|
| 779 | if ($load_op == REVISIONING_LOAD_LATEST) { |
---|
| 780 | // Site is configured to load latest revision, but we'll only do this if |
---|
| 781 | // the latest isn't loaded already and the user has the permission to do so. |
---|
| 782 | $latest_vid = revisioning_get_latest_revision_id($node->nid); |
---|
| 783 | if ($latest_vid != $node->current_revision_id) { |
---|
| 784 | if (!$check_access) { |
---|
| 785 | return REVISIONING_LOAD_LATEST; |
---|
| 786 | } |
---|
| 787 | $original_vid = $node->vid; |
---|
| 788 | $node->vid = $latest_vid; |
---|
| 789 | $node->is_current = node_tools_revision_is_current($node); |
---|
| 790 | $revision_op = ($op == 'view') ? 'view revisions' : 'edit revisions'; |
---|
| 791 | $access = _revisioning_node_revision_access($revision_op, $node); |
---|
| 792 | // Restore $node (even though called by value), so that it remains consistent |
---|
| 793 | $node->vid = $original_vid; |
---|
| 794 | $node->is_current = node_tools_revision_is_current($node); |
---|
| 795 | if ($access) { |
---|
| 796 | return REVISIONING_LOAD_LATEST; |
---|
| 797 | } |
---|
| 798 | } |
---|
| 799 | } |
---|
| 800 | } |
---|
| 801 | return REVISIONING_LOAD_CURRENT; |
---|
| 802 | } |
---|
| 803 | |
---|
| 804 | function _revisioning_prepare_msg($node) { |
---|
| 805 | if (!$node->nid) { // new node |
---|
| 806 | return; |
---|
| 807 | } |
---|
| 808 | $count = _revisioning_get_number_of_revisions_newer_than($node->vid, $node->nid); |
---|
| 809 | if ($count == 1) { |
---|
| 810 | drupal_set_message(t('Please note there is one revision more recent than the one you are about to edit.'), 'warning'); |
---|
| 811 | } |
---|
| 812 | elseif ($count > 1) { |
---|
| 813 | drupal_set_message(t('Please note there are !count revisions more recent than the one you are about to edit.', array('!count' => $count)), 'warning'); |
---|
| 814 | } |
---|
| 815 | } |
---|
| 816 | |
---|
| 817 | function _revisioning_insert_msg($node) { |
---|
| 818 | if ($node->status) { |
---|
| 819 | drupal_set_message(t('Initial revision created and published.')); |
---|
| 820 | } |
---|
| 821 | else { |
---|
| 822 | drupal_set_message(t('Initial draft created, pending publication.')); |
---|
| 823 | } |
---|
| 824 | } |
---|
| 825 | |
---|
| 826 | /** |
---|
| 827 | * Display all revisions of the supplied node in a themed table with links for |
---|
| 828 | * the permitted operations above it. |
---|
| 829 | */ |
---|
| 830 | function _revisioning_present_node($node, $op = 'any') { |
---|
| 831 | return ($op == 'edit' && !$node->revision_moderation) ? node_page_edit($node) :_theme_revisions_summary($node); |
---|
| 832 | } |
---|
| 833 | |
---|
| 834 | /** |
---|
| 835 | * Menu callback for the primary View tab. |
---|
| 836 | */ |
---|
| 837 | function _revisioning_view($node) { |
---|
| 838 | if (_revisioning_load_op($node, 'view') == REVISIONING_LOAD_LATEST) { |
---|
| 839 | $vid_to_load = revisioning_get_latest_revision_id($node->nid); |
---|
| 840 | $node = node_load($node->nid, $vid_to_load); |
---|
| 841 | } |
---|
| 842 | // In node.module, node_page_view() is used to display the current, while |
---|
| 843 | // node_show() is used for any other revision. The difference between the |
---|
| 844 | // two is that node_page_view() surpresses the message that tells us we're |
---|
| 845 | // viewing a revision. That's what we use here because we have our own |
---|
| 846 | // configurable message. |
---|
| 847 | return node_page_view($node); |
---|
| 848 | } |
---|
| 849 | |
---|
| 850 | /** |
---|
| 851 | * Callback for the primary Edit tab. |
---|
| 852 | */ |
---|
| 853 | function _revisioning_edit($node) { |
---|
| 854 | // Use the admin theme if the user specified this for node edit pages |
---|
| 855 | if (variable_get('node_admin_theme', FALSE)) { |
---|
| 856 | global $theme, $custom_theme; |
---|
| 857 | $custom_theme = variable_get('admin_theme', $theme); |
---|
| 858 | } |
---|
| 859 | if (_revisioning_load_op($node, 'edit') == REVISIONING_LOAD_LATEST) { |
---|
| 860 | $vid_to_load = revisioning_get_latest_revision_id($node->nid); |
---|
| 861 | $node = node_load($node->nid, $vid_to_load); |
---|
| 862 | } |
---|
| 863 | // Following is the same as node_page_edit(). |
---|
| 864 | drupal_set_title(check_plain($node->title)); |
---|
| 865 | return drupal_get_form($node->type .'_node_form', $node); |
---|
| 866 | } |
---|
| 867 | |
---|
| 868 | /** |
---|
| 869 | * Callback for the primary View and Edit tabs. |
---|
| 870 | * @param $node |
---|
| 871 | * @param $edit, bool |
---|
| 872 | * @return string |
---|
| 873 | */ |
---|
| 874 | function _revisioning_title_for_tab($node, $edit = FALSE) { |
---|
| 875 | if ($node->num_revisions <= 1 || !$node->revision_moderation) { |
---|
| 876 | return $edit ? t('Edit') : t('View'); |
---|
| 877 | } |
---|
| 878 | if (_revisioning_load_op($node, $edit ? 'edit' : 'view') == REVISIONING_LOAD_LATEST) { |
---|
| 879 | return $edit ? t('Edit latest') : t('View latest'); |
---|
| 880 | } |
---|
| 881 | return $edit ? t('Edit current') : t('View current'); |
---|
| 882 | } |
---|
| 883 | |
---|
| 884 | /** |
---|
| 885 | * Callback to view a particular revision. |
---|
| 886 | */ |
---|
| 887 | function _revisioning_view_revision($node) { |
---|
| 888 | if (isset($node->nid)) { |
---|
| 889 | $router_item = menu_get_item('node/' . $node->nid); |
---|
| 890 | if (!empty($router_item['file'])) { |
---|
| 891 | $path = $_SERVER['DOCUMENT_ROOT'] . base_path(); |
---|
| 892 | require_once $path . $router_item['file']; |
---|
| 893 | } |
---|
| 894 | // Call whatever function is assigned to the main node path but pass the |
---|
| 895 | // current node as an argument. This approach allows for the reuse of Panel |
---|
| 896 | // definition acting on node/%node. See [#1567880]. |
---|
| 897 | if (isset($router_item['page_callback'])) { |
---|
| 898 | return $router_item['page_callback']($node); |
---|
| 899 | } |
---|
| 900 | } |
---|
| 901 | return node_page_view($node); |
---|
| 902 | } |
---|
| 903 | |
---|
| 904 | /** |
---|
| 905 | * Callback to edit a particular revision. |
---|
| 906 | */ |
---|
| 907 | function _revisioning_edit_revision($node) { |
---|
| 908 | // Use the admin theme if the user specified this for node edit pages |
---|
| 909 | if (variable_get('node_admin_theme', FALSE)) { |
---|
| 910 | global $theme, $custom_theme; |
---|
| 911 | $custom_theme = variable_get('admin_theme', $theme); |
---|
| 912 | } |
---|
| 913 | drupal_set_title(check_plain($node->title)); |
---|
| 914 | return drupal_get_form($node->type .'_node_form', $node); |
---|
| 915 | } |
---|
| 916 | |
---|
| 917 | /** |
---|
| 918 | * Use diff's compare callback to compare specific revision to the current one |
---|
| 919 | */ |
---|
| 920 | if (module_exists('diff')) { |
---|
| 921 | function _revisioning_compare_to_current_revision($node) { |
---|
| 922 | module_load_include('inc', 'diff', 'diff.pages'); // for diff_diffs_show() |
---|
| 923 | // Make sure that latest of the two revisions is on the right |
---|
| 924 | if ($node->is_pending) { |
---|
| 925 | return diff_diffs_show($node, $node->current_revision_id, $node->vid); |
---|
| 926 | } |
---|
| 927 | return diff_diffs_show($node, $node->vid, $node->current_revision_id); |
---|
| 928 | } |
---|
| 929 | } |
---|