View source
<?php
function revision_scheduler_permission() {
$permission['schedule revisions'] = array(
'title' => t('Schedule revisions'),
);
return $permission;
}
function revision_scheduler_menu() {
if (!module_exists('diff') && !module_exists('workbench_moderation')) {
$items['node/%node/revisions/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'file path' => drupal_get_path('module', 'node'),
);
}
$items['node/%node/revisions/schedule'] = array(
'title' => 'Schedule',
'page callback' => 'revision_scheduler_list_page',
'page arguments' => array(
'node',
1,
),
'access arguments' => array(
'schedule revisions',
),
'file' => 'revision_scheduler.pages.inc',
'type' => MENU_LOCAL_TASK,
);
$items['node/%node/revisions/schedule/add'] = array(
'title' => 'Add scheduled revision',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revision_scheduler_add_form',
'node',
1,
),
'access callback' => 'revision_scheduler_operation_create_access',
'access arguments' => array(
'node',
1,
NULL,
TRUE,
),
'file' => 'revision_scheduler.pages.inc',
'type' => MENU_LOCAL_ACTION,
);
if (module_exists('workbench_moderation')) {
foreach ($items as $path => $item) {
$moderation_path = str_replace('/revisions/', '/moderation/', $path);
$items[$moderation_path] = $item;
}
}
$items['revision-scheduler/%revision_scheduler_operation/run'] = array(
'title' => 'Edit scheduled revision',
'page callback' => 'revision_scheduler_operation_run',
'page arguments' => array(
1,
),
'access callback' => 'revision_scheduler_operation_access',
'access arguments' => array(
'run',
1,
),
'type' => MENU_CALLBACK,
);
$items['revision-scheduler/%revision_scheduler_operation/edit'] = array(
'title' => 'Edit scheduled revision',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revision_scheduler_edit_form',
1,
),
'access callback' => 'revision_scheduler_operation_access',
'access arguments' => array(
'update',
1,
),
'file' => 'revision_scheduler.pages.inc',
);
$items['revision-scheduler/%revision_scheduler_operation/delete'] = array(
'title' => 'Cancel scheduled revision',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'revision_scheduler_delete_form',
1,
),
'access callback' => 'revision_scheduler_operation_access',
'access arguments' => array(
'delete',
1,
),
'file' => 'revision_scheduler.pages.inc',
);
return $items;
}
function revision_scheduler_menu_alter(array &$items) {
$paths = array(
'node/%node/revisions',
'node/%node/revisions/list',
);
foreach ($paths as $path) {
if (isset($items[$path]['access callback'])) {
$override = '_revision_scheduler_override_' . $items[$path]['access callback'];
if (function_exists($override)) {
$items[$path]['access callback'] = $override;
}
}
}
}
function _revision_scheduler_override__node_revision_access($node, $op = 'view', $account = NULL) {
$access =& drupal_static(__FUNCTION__, array());
$map = array(
'view' => 'view revisions',
'update' => 'revert revisions',
'delete' => 'delete revisions',
);
if (!$node || !isset($map[$op])) {
return FALSE;
}
if (!isset($account)) {
$account = $GLOBALS['user'];
}
$cid = $node->vid . ':' . $account->uid . ':' . $op;
if (!isset($access[$cid])) {
if (!user_access($map[$op], $account) && !user_access('administer nodes', $account)) {
return $access[$cid] = FALSE;
}
$node_current_revision = node_load($node->nid);
$is_current_revision = $node_current_revision->vid == $node->vid;
if ($is_current_revision && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(
':nid' => $node->nid,
))
->fetchField() == 1 && !db_query("SELECT COUNT(revision_id) FROM {revision_scheduler} WHERE entity_type = 'node' AND entity_id = :nid", array(
':nid' => $node->nid,
))
->fetchField() || $op == 'update' || $op == 'delete')) {
$access[$cid] = FALSE;
}
elseif (user_access('administer nodes', $account)) {
$access[$cid] = TRUE;
}
else {
$access[$cid] = node_access($op, $node_current_revision, $account) && ($is_current_revision || node_access($op, $node, $account));
}
}
return $access[$cid];
}
function _revision_scheduler_override_diff_node_revision_access($node, $op = 'view') {
$may_revision_this_type = variable_get('diff_enable_revisions_page_node_' . $node->type, TRUE) || variable_get('enable_revisions_page_' . $node->type, TRUE) || user_access('administer nodes');
return $may_revision_this_type && _revision_scheduler_override__node_revision_access($node, $op);
}
function revision_scheduler_preprocess_menu_local_action(&$variables) {
$link =& $variables['element']['#link'];
if (isset($link['href']) && !isset($link['localized_options']['query']['destination']) && preg_match('#node/\\d+/(revisions|moderation)/schedule/add#', $link['href'])) {
$link += array(
'localized_options' => array(),
);
$link['localized_options'] += array(
'query' => array(),
);
$link['localized_options']['query'] += drupal_get_destination();
}
}
function revision_scheduler_admin_paths() {
$paths = array();
if (variable_get('node_admin_theme')) {
$paths['node/*/revisions/list'] = TRUE;
$paths['node/*/revisions/schedule'] = TRUE;
$paths['node/*/revisions/schedule/add'] = TRUE;
if (module_exists('workbench_moderation')) {
$paths['node/*/moderation/schedule'] = TRUE;
$paths['node/*/moderation/schedule/add'] = TRUE;
}
}
$paths['revision-scheduler/*'] = TRUE;
return $paths;
}
function revision_scheduler_entity_delete($entity, $entity_type) {
list($entity_id) = entity_extract_ids($entity_type, $entity);
db_delete('revision_scheduler')
->condition('entity_type', $entity_type)
->condition('entity_id', $entity_id)
->execute();
}
function revision_scheduler_field_attach_delete_revision($entity_type, $entity) {
list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
db_delete('revision_scheduler')
->condition('entity_type', $entity_type)
->condition('entity_id', $entity_id)
->condition('revision_id', $revision_id)
->condition('time_executed', 0)
->execute();
}
function revision_scheduler_user_cancel($edit, $account, $method) {
switch ($method) {
case 'user_cancel_reassign':
db_update('revision_scheduler')
->fields(array(
'uid' => 0,
))
->condition('uid', $account->uid)
->execute();
break;
}
}
function revision_scheduler_user_delete($account) {
db_delete('revision_scheduler')
->condition('uid', $account->uid)
->execute();
}
function revision_scheduler_entity_type_has_revisions($entity_type) {
$info = entity_get_info($entity_type);
return !empty($info['entity keys']['id']) && !empty($info['entity keys']['revision']) && !empty($info['revision table']);
}
function revision_scheduler_get_all_entity_revision_ids($entity_type, $entity_id) {
if (!revision_scheduler_entity_type_has_revisions($entity_type)) {
return array();
}
$info = entity_get_info($entity_type);
$id_key = $info['entity keys']['id'];
$revision_key = $info['entity keys']['revision'];
$query = db_select($info['revision table'], 'revision');
$query
->addField('revision', $revision_key);
$query
->condition('revision.' . $id_key, $entity_id);
return $query
->execute()
->fetchCol();
}
function revision_scheduler_get_entity_revision_key($entity_type) {
$info = entity_get_info($entity_type);
return !empty($info['entity keys']['revision']) ? $info['entity keys']['revision'] : FALSE;
}
function revision_scheduler_entity_revision_load($entity_type, $entity_id, $revision_id = NULL) {
if (empty($revision_id)) {
$revisions = entity_load($entity_type, array(
$entity_id,
));
}
else {
$revisions = revision_scheduler_entity_revision_load_multiple($entity_type, $entity_id, array(
$revision_id,
));
}
return !empty($revisions) ? reset($revisions) : FALSE;
}
function revision_scheduler_entity_revision_load_multiple($entity_type, $entity_id, array $revision_ids) {
$revisions =& drupal_static(__FUNCTION__, array());
if (!isset($revisions[$entity_type . ':' . $entity_id])) {
$revisions[$entity_type . ':' . $entity_id] = array();
}
$entity_revisions =& $revisions[$entity_type . ':' . $entity_id];
if ($revision_ids_to_load = array_diff($revision_ids, array_keys($entity_revisions))) {
$revision_key = revision_scheduler_get_entity_revision_key($entity_type);
foreach ($revision_ids_to_load as $revision_id) {
if ($entities = entity_load($entity_type, array(
$entity_id,
), array(
$revision_key => $revision_id,
))) {
$entity_revisions[$revision_id] = reset($entities);
}
}
}
return array_intersect_key($entity_revisions, array_flip($revision_ids));
}
function revision_scheduler_cron_queue_info() {
$info['revision_scheduler'] = array(
'worker callback' => 'revision_scheduler_queue_process_item',
'time' => variable_get('revision_scheduler_cron_time', 30),
'skip on cron' => variable_get('revision_scheduler_cron_disable', FALSE),
);
return $info;
}
function revision_scheduler_cron() {
if ($ids = db_query("SELECT * FROM {revision_scheduler} WHERE time_scheduled <= :now AND time_queued = 0 AND time_executed = 0 ORDER BY time_scheduled ASC, id ASC", array(
':now' => REQUEST_TIME,
))
->fetchCol()) {
$queue = DrupalQueue::get('revision_scheduler');
foreach ($ids as $id) {
if ($queue
->createItem($id)) {
db_update('revision_scheduler')
->fields(array(
'time_queued' => time(),
))
->condition('id', $id)
->execute();
}
}
}
}
function revision_scheduler_queue_process_item($operation_id) {
$operation = is_object($operation_id) ? $operation_id : revision_scheduler_operation_load($operation_id);
if ($operation) {
revision_scheduler_operation_process($operation);
}
}
function revision_scheduler_operation_process($operation) {
$transaction = db_transaction();
$args = array(
'@entity-type' => $operation->entity_type,
'@entity-id' => $operation->entity_id,
'@operation' => $operation->operation,
'@revision-id' => $operation->revision_id,
);
try {
$entity = revision_scheduler_entity_revision_load($operation->entity_type, $operation->entity_id, $operation->revision_id);
$operation_info = revision_scheduler_entity_revision_operation_get_info($operation->entity_type, $operation->operation);
$entity_info = entity_get_info($operation->entity_type);
if (empty($entity)) {
throw new Exception(t('Failed to load entity @entity-type @entity-id.', $args));
}
elseif (empty($operation_info)) {
throw new Exception(t('Failed to load revision_scheduler_entity_revision_operation_get_info(@entity-type, @operation).', $args));
}
elseif (empty($entity_info)) {
throw new Exception(t('Failed to load entity_get_info(@entity-type).', $args));
}
module_invoke_all('revision_scheduler_operation_preprocess', $entity, $operation);
$callback = $operation_info['callback'];
if (isset($operation_info['file'])) {
include_once $operation_info['file'];
}
if (!is_callable($callback)) {
throw new Exception(t('Revision operation @operation callback @callback does not exist.', $args + array(
'@callback' => is_array($callback) ? implode('::', $callback) : $callback,
)));
}
$original_session_saving = drupal_save_session();
$original_user = $GLOBALS['user'];
if ($operation->uid != $GLOBALS['user']->uid) {
drupal_save_session(FALSE);
if ($operation->uid && ($account = user_load($operation->uid))) {
$GLOBALS['user'] = $account;
}
else {
$GLOBALS['user'] = drupal_anonymous_user();
}
}
$operation->time_executed = time();
revision_scheduler_operation_save($operation);
call_user_func($callback, $entity, $operation);
$GLOBALS['user'] = $original_user;
drupal_save_session($original_session_saving);
module_invoke_all('revision_scheduler_operation_postprocess', $entity, $operation);
watchdog('revision_scheduler', 'Performed scheduled revision operation @operation on @entity-type @entity-id revision @revision-id.', $args);
} catch (Exception $e) {
$transaction
->rollback();
watchdog_exception('revision_scheduler', $e);
}
}
function revision_scheduler_operation_run($operation) {
$value = implode('-', array(
'run',
$operation->id,
$operation->operation,
$operation->time_scheduled,
));
if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $value)) {
return MENU_ACCESS_DENIED;
}
revision_scheduler_operation_process($operation);
drupal_goto();
}
function revision_scheduler_operation_load($id) {
$operations = revision_scheduler_operation_load_multiple(array(
$id,
));
return !empty($operations) ? reset($operations) : FALSE;
}
function revision_scheduler_operation_load_multiple(array $ids) {
if (empty($ids)) {
return array();
}
$operations = db_query("SELECT * FROM {revision_scheduler} WHERE id IN (:ids)", array(
':ids' => $ids,
))
->fetchAllAssoc('id');
return $operations;
}
function revision_scheduler_operation_save($operation) {
$operation->is_new = empty($operation->id);
if (!isset($operation->uid)) {
$operation->uid = $GLOBALS['user']->uid;
}
if (!empty($operation->time_scheduled) && !is_numeric($operation->time_scheduled)) {
$operation->time_scheduled = strtotime($operation->time_scheduled);
}
if ($operation->is_new) {
drupal_write_record('revision_scheduler', $operation);
}
else {
drupal_write_record('revision_scheduler', $operation, array(
'id',
));
}
unset($operation->is_new);
}
function revision_scheduler_operation_delete($id) {
return revision_scheduler_operation_delete_multiple(array(
$id,
));
}
function revision_scheduler_operation_delete_multiple(array $ids) {
if ($operations = revision_scheduler_operation_load_multiple($ids)) {
db_delete('revision_scheduler')
->condition('id', $ids, 'IN')
->execute();
}
}
function revision_scheduler_operation_uasort($a, $b) {
if ($a->time_scheduled != $b->time_scheduled) {
return $a->time_scheduled < $b->time_scheduled ? -1 : 1;
}
else {
return $a->id < $b->id ? -1 : 1;
}
}
function revision_scheduler_entity_revision_operation_get_info($entity_type = NULL, $operation = NULL) {
$operations =& drupal_static(__FUNCTION__);
if (!isset($operations)) {
$cid = 'revision:operations:info:' . $GLOBALS['language']->language;
if ($cache = cache_get($cid)) {
$operations = $cache->data;
}
else {
$operations = module_invoke_all('entity_revision_operation_info');
drupal_alter('entity_revision_operation_info', $operations);
foreach ($operations as &$entity_operations) {
foreach (array_keys($entity_operations) as $key) {
$entity_operations[$key]['operation'] = $key;
}
}
cache_set($cid, $operations);
}
}
if (isset($entity_type) && isset($operation)) {
return isset($operations[$entity_type][$operation]) ? $operations[$entity_type][$operation] : FALSE;
}
if (isset($entity_type)) {
return isset($operations[$entity_type]) ? $operations[$entity_type] : array();
}
return $operations;
}
function _revision_scheduler_operation_access($operation, $entity_type, $entity = NULL, $account = NULL) {
$access = FALSE;
if (isset($operation['access callback']) || isset($operation['access arguments'])) {
$operation += array(
'access callback' => 'user_access',
'access arguments' => array(),
);
$access_callback = isset($operation['access callback']) ? $operation['access callback'] : 'user_access';
if (is_bool($access_callback)) {
$access = $access_callback;
}
else {
$access_arguments = isset($operation['access arguments']) ? $operation['access arguments'] : array();
if ($access_callback === 'user_access' && count($access_arguments) === 1) {
$access_arguments[] = $account;
}
$access = call_user_func_array($access_callback, $access_arguments);
}
}
$access_checks = module_invoke_all('entity_revision_operation_access', $operation, $entity_type, $entity, $account);
if (in_array(FALSE, $access_checks, TRUE)) {
return FALSE;
}
elseif (in_array(TRUE, $access_checks, TRUE)) {
return TRUE;
}
return $access;
}
function revision_scheduler_entity_revision_operation_get_options($entity_type, $entity = NULL, $account = NULL) {
$options =& drupal_static(__FUNCTION__, array());
if (!isset($account)) {
$account = $GLOBALS['user'];
}
$cid = md5(serialize(array(
$entity_type,
$entity,
$account,
)));
if (!isset($options[$cid])) {
$options[$cid] = array();
$operations = revision_scheduler_entity_revision_operation_get_info($entity_type);
foreach ($operations as $key => $operation) {
if (_revision_scheduler_operation_access($operation, $entity_type, $entity, $account)) {
$options[$cid][$key] = $operation['label'];
}
}
}
return $options[$cid];
}
function revision_scheduler_operation_create_access($entity_type, $entity = NULL, $account = NULL, $check_all_revisions = FALSE) {
if (!revision_scheduler_entity_type_has_revisions($entity_type)) {
return FALSE;
}
if (!isset($account)) {
$account = $GLOBALS['user'];
}
if (!user_access('schedule revisions', $account)) {
return FALSE;
}
$access =& drupal_static(__FUNCTION__, array());
$cid = md5(serialize(array(
$entity_type,
$entity,
$account,
$check_all_revisions,
)));
if (!isset($access[$cid])) {
$access[$cid] = (bool) revision_scheduler_entity_revision_operation_get_options($entity_type, $entity, $account);
if ($check_all_revisions && !$access[$cid] && !empty($entity)) {
list($entity_id, $entity_vid) = entity_extract_ids($entity_type, $entity);
if (!empty($entity_id)) {
$revision_ids = revision_scheduler_get_all_entity_revision_ids($entity_type, $entity_id);
foreach ($revision_ids as $revision_id) {
if ($revision_id != $entity_vid && ($revision = revision_scheduler_entity_revision_load($entity_type, $entity_id, $revision_id))) {
if (revision_scheduler_entity_revision_operation_get_options($entity_type, $revision, $account)) {
$access[$cid] = TRUE;
break;
}
}
}
}
}
}
return $access[$cid];
}
function revision_scheduler_operation_access($op, $operation, $account = NULL) {
if (!revision_scheduler_entity_type_has_revisions($operation->entity_type)) {
return FALSE;
}
if (!user_access('schedule revisions', $account)) {
return FALSE;
}
$entity = revision_scheduler_entity_revision_load($operation->entity_type, $operation->entity_id, $operation->revision_id);
if ($op == 'run' || $op == 'update') {
if (empty($entity)) {
return FALSE;
}
}
if ($op == 'update') {
if (!revision_scheduler_entity_revision_operation_get_options($operation->entity_type, $entity, $account)) {
return FALSE;
}
}
if ($op == 'run' || $op == 'update' || $op == 'delete') {
return empty($operation->time_executed) && empty($operation->time_queued);
}
return FALSE;
}
function revision_scheduler_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
$form_entity_type = $entity_type;
if ($form_entity_type == 'taxonomy_term') {
$form_entity_type = 'term';
}
if (!isset($form_state[$form_entity_type])) {
return;
}
list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity);
$form['revision_scheduler'] = array(
'#type' => 'fieldset',
'#title' => t('Revision scheduler'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array(
'entity-form-revision-scheduler',
),
),
'#weight' => 21,
'#access' => revision_scheduler_operation_create_access($entity_type, $entity, NULL),
'#tree' => TRUE,
'#parents' => array_merge($form['#parents'], array(
'revision_scheduler',
)),
);
$operation = new stdClass();
$operation->id = NULL;
$operation->entity_type = $entity_type;
$operation->entity_id = $entity_id;
$operation->revision_id = $revision_id;
$operation->operation = NULL;
$operation->uid = $GLOBALS['user']->uid;
form_load_include($form_state, 'inc', 'revision_scheduler', 'revision_scheduler.pages');
$form_state_clone = $form_state;
$form_state_clone['build_info']['args'] = array(
$operation,
$entity,
);
$form['revision_scheduler'] = revision_scheduler_edit_form($form['revision_scheduler'], $form_state_clone, $operation, $entity);
$form['revision_scheduler']['revision_id']['#access'] = FALSE;
$form['revision_scheduler']['operation']['#required'] = FALSE;
$form['revision_scheduler']['operation']['#empty_option'] = t('- None -');
unset($form['revision_scheduler']['actions']);
drupal_alter('form_revision_scheduler_edit_form', $form['revision_scheduler'], $form_state_clone);
}
function revision_scheduler_entity_insert($entity, $entity_type) {
if (!empty($entity->revision_scheduler['operation'])) {
$operation = (object) $entity->revision_scheduler;
list($operation->entity_id, $operation->revision_id) = entity_extract_ids($entity_type, $entity);
revision_scheduler_operation_save($operation);
$entity->revision_scheduler = (array) $operation;
}
}
function revision_scheduler_entity_update($entity, $entity_type) {
if (!empty($entity->revision_scheduler['operation'])) {
$operation = (object) $entity->revision_scheduler;
list(, $operation->revision_id) = entity_extract_ids($entity_type, $entity);
revision_scheduler_operation_save($operation);
$entity->revision_scheduler = (array) $operation;
}
drupal_static_reset('revision_scheduler_entity_revision_load_multiple');
}
function node_entity_revision_operation_info() {
$operations['node']['revert'] = array(
'label' => t('Revert'),
'access arguments' => array(
'revert revisions',
),
'callback' => 'node_node_revision_operation_revert',
);
$operations['node']['delete'] = array(
'label' => t('Delete'),
'access arguments' => array(
'delete revisions',
),
'callback' => 'node_node_revision_operation_delete',
);
return $operations;
}
function node_node_revision_operation_revert($node) {
$node->revision = 1;
$node->log = t('Copy of the revision from %date.', array(
'%date' => format_date($node->revision_timestamp),
));
node_save($node);
drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array(
'@type' => node_type_get_name($node),
'%title' => $node->title,
'%revision-date' => format_date($node->revision_timestamp),
)));
}
function node_node_revision_operation_delete($node) {
if (node_revision_delete($node->vid)) {
drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array(
'%revision-date' => format_date($node->revision_timestamp),
'@type' => node_type_get_name($node),
'%title' => $node->title,
)));
}
else {
throw new Exception(t('Unable to delete node @nid revision @vid.', array(
'@nid' => $node->nid,
'@vid' => $node->vid,
)));
}
}
function node_entity_revision_operation_access($operation, $entity_type, $entity, $account) {
if ($entity_type == 'node') {
switch ($operation['operation']) {
case 'delete':
case 'revert':
if (empty($entity->nid)) {
return FALSE;
}
else {
$node = node_load($entity->nid);
if ($entity->vid == $node->vid) {
return FALSE;
}
}
break;
}
}
}
function workbench_moderation_entity_revision_operation_info() {
$info = array();
$states = workbench_moderation_state_labels();
foreach ($states as $state => $label) {
$info['node']['workbench_moderation_' . $state] = array(
'label' => t('Moderate to @label', array(
'@label' => $label,
)),
'access callback' => TRUE,
'callback' => 'workbench_moderation_revision_operation_process',
);
}
return $info;
}
function workbench_moderation_entity_revision_operation_access($operation, $entity_type, $entity, $account) {
if (strpos($operation['operation'], 'workbench_moderation_') !== FALSE && !empty($entity) && $entity_type == 'node') {
if (!workbench_moderation_node_type_moderated($entity->type)) {
return FALSE;
}
}
}
function workbench_moderation_revision_operation_process($entity, $operation) {
$state = substr($operation->operation, 21);
workbench_moderation_moderate($entity, $state);
}