* @date 2006-10-24 * @file */ /** * Show the history overview page. * * @param int $nid The id of the node whose history to show. * @return string The HTML of the history overview page */ function history_page($nid) { $infos = array(); $vids = history_get_vids($nid); $columns = variable_get('history_columns', array()); _history_process_columns($columns); usort($columns, '_history_compare_config_weights'); $head = array(); $data = array(); $classes = array(); foreach ($columns as $key => $column) { $module = $column['module']; $id = $column['id']; $function = $module . '_history'; if (! function_exists($function)) { continue; } if ($column['show'] == 1) { $data[] = call_user_func_array( $function, array('column', $nid, array($id, $vids)) ); if (! isset($infos[$module])) { $infos[$module] = call_user_func_array( $function, array('info', $nid)); } $head[] = $infos[$module][$id]['title']; } if ($column['format'] == 1) { $classes[] = call_user_func_array( $function, array('class', $nid, array($column['id'], $vids)) ); } } if (count($head) == 0) { return history_page_config_needed(); } $data = _history_prepare_rows($data, $classes); $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid)); drupal_set_title(t('History of %title', array('%title' => theme('placeholder', $title)))); return theme('table', $head, $data); } /** * Show the page that instructs the user to configure this module. * * @return string The HTML to instructs the user to configure this module */ function history_page_config_needed() { $output = t('There are currently no columns configured to display in the history list.'); if (user_access('configure history')) { $output .= t('Configure columns on the history configuration page first.', array('%url' => url('admin/settings/history'))); } else { $output .= t('Please tell the system administrator to configure the history module.'); } return $output; } /** * Show a specific revision. * * @param int $nid The node id * @param int $vid The revision id * @return string The result of node_show() */ function history_page_view($nid, $vid) { $node = node_load($nid, $vid); if ($node->nid) { if ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node)) { drupal_set_title(theme('placeholder', $node->title)); return node_show($node, arg(2)); } drupal_access_denied(); return; } } /** * Revert to the revision with the specified vid. A node and nodeapi "update" * event is triggered (via the node_save() call) when a revision is reverted. * * @param int $nid The node id * @param int $vid The vid to revert to * @return void This function does not return, it redirects to node/$nid/history */ function history_page_revert($nid, $vid) { global $user; $node = node_load($nid, $vid); if (! history_authorized('revert', $node)) { drupal_access_denied(); return; } if ($node->vid) { $node->revision = 1; $node->log = t('Copy of the revision from %date.', array('%date' => theme('placeholder', format_date($node->revision_timestamp)))); $node->taxonomy = array_keys($node->taxonomy); node_save($node); drupal_set_message(t('%title has been reverted back to the revision from %revision-date', array('%revision-date' => theme('placeholder', format_date($node->revision_timestamp)), '%title' => theme('placeholder', check_plain($node->title))))); watchdog('content', t('%type: reverted %title revision %revision.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $vid)))); } else { drupal_set_message(t('You tried to revert to an invalid revision.'), 'error'); } drupal_goto('node/'. $nid .'/history'); } /** * Delete the revision with specified revision number. A "delete revision" * nodeapi event is invoked when a revision is deleted. * * @param int $nid The node id * @param int $vid The vid to revert to * @return void This function does not return, it redirects to node/$nid/history */ function history_page_delete($nid, $vid) { $node = node_load($nid, $vid); // Check permissions if (! history_authorized('delete', $node)) { drupal_access_denied(); return; } // Don't delete the current revision if ($vid == history_get_current_vid($nid)) { drupal_set_message(t('Deletion failed. You tried to delete the current revision.')); drupal_goto("node/$nid/history"); return; } db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $nid, $vid); node_invoke_nodeapi($node, 'delete revision'); drupal_set_message(t('Deleted %title revision %revision.', array('%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $vid)))); watchdog('content', t('%type: deleted %title revision %revision.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title), '%revision' => theme('placeholder', $vid)))); drupal_goto("node/$nid/history"); } /** * Retrieves the vids of a specified nid. * * @param int $nid the node identifier * @return array Array of revision identifiers of the specified node */ function history_get_vids($nid) { $result = db_query('SELECT vid FROM {node_revisions} WHERE nid = %d ORDER BY timestamp DESC', $nid); $vids = array(); while ($record = db_fetch_array($result)) { $vids[] = $record['vid']; } return $vids; } /** * Retireves the current vid of the given node. * * @param int $nid * @return array Array of vids of the nid */ function history_get_current_vid($nid) { static $vids = array(); if (! isset($vids[$nid])) { $vids[$nid] = db_result(db_query('SELECT vid FROM {node} WHERE nid = %d', $nid)); } return $vids[$nid]; } /** * Retrieve all available columns. * * Returns a two-dimensional array. The first index is the module, the second * index is the column identifier. This structure was chosen to allow modules * to have the same column identifier as other modules. * * @return array Array of column infomration */ function history_columns() { static $columns = null; if ($columns != null) { return $columns; } $columns = array(); foreach (module_implements('history') as $module) { $function = $module . '_history'; $result = call_user_func_array($function, array('info')); if (count($result != 0)); { $columns[$module] = $result; } } return $columns; } /** * Checks if the current user is allowed to perform a certain operation. * * If the operation is neither 'revert' nor 'delete', permission is denied. * * @param string $op The operation to check * @param mixed &$arg A node or a node id on which to carry out the operation * @param int $vid The vid of the node to load (if a node id was passed as $arg) * @return boolean true: permission granted; false: permission denied */ function history_authorized($op, $arg = null, $vid = null) { if (is_object($arg)) { $node =& $arg; } else { $node =& node_load($arg, $vid); } switch ($op) { case 'revert': return (user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node); case 'delete': return user_access('administer nodes') && node_access('delete', $node); default: return false; } } //////////////////////////////////////////////////////////////////////////////// // ____ _ _ // // | _ \ _ __ (_) __ __ __ _ | |_ ___ // // | |_) | | '__| | | \ \ / / / _` | | __| / _ \ // // | __/ | | | | \ V / | (_| | | |_ | __/ // // |_| |_| |_| \_/ \__,_| \__| \___| // // // // __ _ _ // // / _| _ _ _ __ ___ | |_ (_) ___ _ __ ___ // // | |_ | | | | | '_ \ / __| | __| | | / _ \ | '_ \ / __| // // | _| | |_| | | | | | | (__ | |_ | | | (_) | | | | | \__ \ // // |_| \__,_| |_| |_| \___| \__| |_| \___/ |_| |_| |___/ // // // //////////////////////////////////////////////////////////////////////////////// /** * Brings the coluns of $a into a format suitable for theming as a table while * merging the classes given in $c * * @param array $a The table columns * @param array $c The css classes * @return array The table rows */ function _history_prepare_rows($a, $c) { $t = array(); $d = array(); foreach ($c as $e) { foreach ($e as $key => $val) { // /* This version uses all classes */ // if (isset($d[$key])) { // $d[$key] .= ' ' . $val; // } else { // $d[$key] .= $val; // } /* This version only includes the last class (based on weight) */ if (isset($val) && $val != '') { $d[$key] = $val; } } } foreach ($a as $col => $data) { foreach ($data as $row => $value) { $t[$row][$col] = array('data'=> $value, 'class' => $d[$row]); } } return $t; } /** * Adds module name and column id to each array element. This is done because * they are bevorehand only stored in the key (which is lost during sorting). * * @param array &$columns The columns array */ function _history_process_columns(&$columns) { foreach (array_keys($columns) as $key) { list($module, $id) = explode(':', $key); $columns[$key]['module'] = $module; $columns[$key]['id'] = $id; } } /** * Compares rows of the settings form according to it's weight. * * @param array $a An array such that isset($a['weight']['#default_value']) * @param array $b An array such that isset($b['weight']['#default_value']) * @reutrn int $a['weight']['#default_value'] - $b['weight']['#default_value'] */ function _history_compare_form_weights($a, $b) { $aworld = $a['show']['#default_value'] + $a['format']['#default_value']; $bworld = $b['show']['#default_value'] + $b['format']['#default_value']; if ( ($aworld == 0 && $bworld == 0) || ($aworld != 0 && $bworld != 0) ) { return $a['weight']['#default_value'] - $b['weight']['#default_value']; } else { return $bworld - $aworld; } } /** * Compares rows of the settings configuration according to it's weight. * * @param array $a An array such that isset($a['weight']) * @param array $b An array such that isset($b['weight']) * @return int $a['weight'] - $b['weight'] */ function _history_compare_config_weights($a, $b) { return $a['weight'] - $b['weight']; } /** * Themes the history settings form. * * @param array The form to theme * @return string The HTML of the form */ function theme_history_settings_form($form) { $output = form_render($form['types']); $header = array( array('data' => t('Title')), array('data' => t('Weight')), array('data' => t('Show')), array('data' => t('Format')), array('data' => t('Description')), ); $children = element_children($form['history_columns']); $columns = array(); foreach ($children as $key) { $columns[] =& $form['history_columns'][$key]; } usort($columns, '_history_compare_form_weights'); foreach ($columns as $column) { $rows[] = array( form_render($column['title']), form_render($column['weight']), form_render($column['show']), form_render($column['format']), form_render($column['description']), ); } form_render($form['history_columns']); // marks this element as processed $output .= '
'; $output .= form_render($form); return $output; }