Commit 1e63dd1a authored by Gábor Hojtsy's avatar Gábor Hojtsy

#201536 by chx: centralizing permission checking code in node revision...

#201536 by chx: centralizing permission checking code in node revision handling, removing lots of duplicate code
parent f761b874
......@@ -360,9 +360,12 @@ function menu_execute_active_handler($path = NULL) {
* @return
* Returns TRUE for success, FALSE if an object cannot be loaded
*/
function _menu_load_objects($item, &$map) {
if ($item['load_functions']) {
$load_functions = unserialize($item['load_functions']);
function _menu_load_objects(&$item, &$map) {
if ($load_functions = $item['load_functions']) {
// If someone calls this function twice, then unserialize will fail.
if ($load_functions_unserialized = unserialize($load_functions)) {
$load_functions = $load_functions_unserialized;
}
$path_map = $map;
foreach ($load_functions as $index => $function) {
if ($function) {
......@@ -373,6 +376,7 @@ function _menu_load_objects($item, &$map) {
// some processing. In this case the $function is the key to the
// load_function array, and the value is the list of arguments.
list($function, $args) = each($function);
$load_functions[$index] = $function;
// Some arguments are placeholders for dynamic items to process.
foreach ($args as $i => $arg) {
......@@ -387,6 +391,9 @@ function _menu_load_objects($item, &$map) {
// the map.
$args[$i] = &$map;
}
if (is_int($arg)) {
$args[$i] = isset($path_map[$arg]) ? $path_map[$arg] : '';
}
}
array_unshift($args, $value);
$return = call_user_func_array($function, $args);
......@@ -403,6 +410,7 @@ function _menu_load_objects($item, &$map) {
$map[$index] = $return;
}
}
$item['load_functions'] = $load_functions;
}
return TRUE;
}
......@@ -609,6 +617,31 @@ function _menu_link_translate(&$item) {
return $map;
}
/**
* Get a loaded object from a router item.
*
* menu_get_object() will provide you the current node on paths like node/5,
* node/5/revisions/48 etc. menu_get_object('user') will give you the user
* account on user/5 etc.
*
* @param $type
* Type of the object. These appear in hook_menu definitons as %type. Core
* provides aggregator_feed, aggregator_category, contact, filter_format,
* forum_term, menu, menu_link, node, taxonomy_vocabulary, user. See the
* relevant {$type}_load function for more on each. Defaults to node.
* @param $position
* The expected position for $type object. For node/%node this is 1, for
* comment/reply/%node this is 2. Defaults to 1.
* @param $path
* See @menu_get_item for more on this. Defaults to the current path.
*/
function menu_get_object($type = 'node', $position = 1, $path = NULL) {
$router_item = menu_get_item($path);
if (isset($router_item['load_functions'][$position]) && !empty($router_item['map'][$position]) && $router_item['load_functions'][$position] == $type .'_load') {
return $router_item['map'][$position];
}
}
/**
* Render a menu tree based on the current path.
*
......
......@@ -1736,8 +1736,8 @@ function template_preprocess_page(&$variables) {
// Closure should be filled last.
$variables['closure'] = theme('closure');
if ((arg(0) == 'node') && is_numeric(arg(1))) {
$variables['node'] = node_load(arg(1));
if ($node = menu_get_object()) {
$variables['node'] = $node;
}
// Compile a list of classes that are going to be applied to the body element.
......
......@@ -1043,7 +1043,10 @@ function node_build_content($node, $teaser = FALSE, $page = FALSE) {
/**
* Generate a page displaying a single node, along with its comments.
*/
function node_show($node, $cid) {
function node_show($node, $cid, $message = FALSE) {
if ($message) {
drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))));
}
$output = node_view($node, FALSE, TRUE);
if (function_exists('comment_render') && $node->comment) {
......@@ -1069,7 +1072,7 @@ function theme_node_log_message($log) {
* Implementation of hook_perm().
*/
function node_perm() {
$perms = array('administer content types', 'administer nodes', 'access content', 'view revisions', 'revert revisions');
$perms = array('administer content types', 'administer nodes', 'access content', 'view revisions', 'revert revisions', 'delete revisions');
foreach (node_get_types() as $type) {
if ($type->module == 'node') {
......@@ -1304,8 +1307,31 @@ function node_link($type, $node = NULL, $teaser = FALSE) {
return $links;
}
function _node_revision_access($node) {
return (user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node) && db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $node->nid)) > 1;
function _node_revision_access($node, $op = 'view') {
static $access = array();
if (!isset($access[$node->vid])) {
$node_current_revision = node_load($node->nid);
$is_current_revision = $node_current_revision->vid == $node->vid;
// There should be at least two revisions. If the vid of the given node
// and the vid of the current revision differs, then we already have two
// different revisions so there is no need for a separate database check.
// Also, if you try to revert to or delete the current revision, that's
// not good.
if ($is_current_revision && (db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $node->nid)) == 1 || $op == 'update' || $op == 'delete')) {
$access[$node->vid] = FALSE;
}
elseif (user_access('administer nodes')) {
$access[$node->vid] = TRUE;
}
else {
$map = array('view' => 'view revisions', 'update' => 'revert revisions', 'delete' => 'delete revisions');
// First check the user permission, second check the access to the
// current revision and finally, if the node passed in is not the current
// revision then access to that, too.
$access[$node->vid] = isset($map[$op]) && user_access($map[$op]) && node_access($op, $node_current_revision) && ($is_current_revision || node_access($op, $node));
}
}
return $access[$node->vid];
}
function _node_add_access() {
......@@ -1450,28 +1476,37 @@ function node_menu() {
'type' => MENU_CALLBACK);
$items['node/%node/revisions'] = array(
'title' => 'Revisions',
'page callback' => 'node_revisions',
'page callback' => 'node_revision_overview',
'page arguments' => array(1),
'access callback' => '_node_revision_access',
'access arguments' => array(1),
'weight' => 2,
'file' => 'node.pages.inc',
'type' => MENU_LOCAL_TASK,
);
$items['node/%node/revisions/%/view'] = array(
'title' => 'Revisions',
'page callback' => 'node_show',
'page arguments' => array(1, NULL, TRUE),
'type' => MENU_CALLBACK,
);
$items['node/%node/revisions/%/revert'] = array(
'title' => 'Revert to earlier revision',
'page callback' => 'node_revision_revert',
'page arguments' => array(1, 3),
'load arguments' => array(3),
'page callback' => 'drupal_get_form',
'page arguments' => array('node_revision_revert_confirm', 1),
'access callback' => '_node_revision_access',
'access arguments' => array(1, 3),
'access arguments' => array(1, 'update'),
'file' => 'node.pages.inc',
'type' => MENU_CALLBACK,
);
$items['node/%node/revisions/%/delete'] = array(
'title' => 'Delete earlier revision',
'page callback' => 'node_revision_delete',
'page arguments' => array(1, 3),
'load arguments' => array(3),
'page callback' => 'drupal_get_form',
'page arguments' => array('node_revision_delete_confirm', 1),
'access callback' => '_node_revision_access',
'access arguments' => array(1, 3),
'access arguments' => array(1, 'delete'),
'file' => 'node.pages.inc',
'type' => MENU_CALLBACK,
);
......
......@@ -505,38 +505,6 @@ function node_delete_confirm_submit($form, &$form_state) {
return;
}
/**
* Menu callback for revisions related activities.
*/
function node_revisions() {
if (is_numeric(arg(1)) && arg(2) == 'revisions') {
$op = arg(4) ? arg(4) : 'overview';
switch ($op) {
case 'overview':
$node = node_load(arg(1));
if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
return node_revision_overview($node);
}
drupal_access_denied();
return;
case 'view':
if (is_numeric(arg(3))) {
$node = node_load(arg(1), arg(3));
if ($node->nid) {
if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) {
drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))));
return node_show($node, arg(2));
}
drupal_access_denied();
return;
}
}
break;
}
}
drupal_not_found();
}
/**
* Generate an overview table of older revisions of a node.
*/
......@@ -553,7 +521,7 @@ function node_revision_overview($node) {
$revert_permission = TRUE;
}
$delete_permission = FALSE;
if (user_access('administer nodes')) {
if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
$delete_permission = TRUE;
}
foreach ($revisions as $revision) {
......@@ -582,25 +550,6 @@ function node_revision_overview($node) {
return theme('table', $header, $rows);
}
/**
* Revert to the revision with the specified revision number. A node and nodeapi "update" event is triggered
* (via the node_save() call) when a revision is reverted.
*/
function node_revision_revert($node, $revision) {
global $user;
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
$node_revision = node_load($node->nid, $revision);
if ($node_revision->vid) {
return drupal_get_form('node_revision_revert_confirm', $node_revision);
}
else {
drupal_set_message(t('You tried to revert to an invalid revision.'), 'error');
drupal_goto('node/'. $node->nid .'/revisions');
}
}
drupal_access_denied();
}
/**
* Ask for confirmation of the reversion to prevent against CSRF attacks.
*/
......@@ -624,28 +573,6 @@ function node_revision_revert_confirm_submit($form, &$form_state) {
$form_state['redirect'] = 'node/'. $node_revision->nid .'/revisions';
}
/**
* Delete the revision with specified revision number. A "delete revision" nodeapi event is invoked when a
* revision is deleted.
*/
function node_revision_delete($node, $revision) {
if (user_access('administer nodes')) {
if (node_access('delete', $node)) {
// Don't allow deleting the current revision.
if ($revision != $node->vid) {
// Load the specific revision instead of the current one.
$node_revision = node_load($node->nid, $revision);
return drupal_get_form('node_revision_delete_confirm', $node_revision);
}
else {
drupal_set_message(t('Deletion failed. You tried to delete the current revision.'));
drupal_goto('node/'. $node->nid .'/revisions');
}
}
}
drupal_access_denied();
}
function node_revision_delete_confirm($form_state, $node_revision) {
$form['#node_revision'] = $node_revision;
return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/'. $node_revision->nid .'/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
......
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