workbench_scheduler.module in Workbench Scheduler 7
Same filename and directory in other branches
Content scheduling for Workbench.
File
workbench_scheduler.moduleView source
<?php
/**
* @file
* Content scheduling for Workbench.
*/
/**
* Implements hook_menu().
*/
function workbench_scheduler_menu() {
$items = array();
// Schedules.
$items['admin/config/workbench/scheduler'] = array(
'title' => 'Workbench Schedules',
'description' => 'Manage content workbench schedules.',
'page callback' => 'workbench_scheduler_admin_page',
'access arguments' => array(
'administer workbench schedules',
),
'file' => 'workbench_scheduler.admin.inc',
);
// Schedules.
$items['admin/config/workbench/scheduler/schedules'] = array(
'title' => 'Schedules',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -1,
);
// Add schedule.
$items['admin/config/workbench/scheduler/schedules/add'] = array(
'title' => 'Add Schedule',
'description' => 'Create a new schedule.',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_scheduler_admin_edit_schedule',
),
'access arguments' => array(
'administer workbench schedules',
),
'file' => 'workbench_scheduler.admin.inc',
);
// Edit schedule.
$items['admin/config/workbench/scheduler/schedules/%/edit'] = array(
'title' => 'Edit Schedule',
'type' => MENU_CALLBACK,
'description' => 'Edit a workbench schedule.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_scheduler_admin_edit_schedule',
5,
),
'access arguments' => array(
'administer workbench schedules',
),
'file' => 'workbench_scheduler.admin.inc',
);
// Delete schedule.
$items['admin/config/workbench/scheduler/schedules/%/delete'] = array(
'title' => 'Delete Schedule',
'description' => 'Delete a workbench schedule.',
'type' => MENU_CALLBACK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_scheduler_admin_delete_schedule',
5,
),
'access arguments' => array(
'administer workbench schedules',
),
'file' => 'workbench_scheduler.admin.inc',
);
// Manage node schedules.
$items['node/%node/manage_schedules'] = array(
'title' => 'Manage Schedules',
'description' => 'Manage the schedules set for this node.',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_scheduler_admin_manage_node_schedules',
1,
),
'access callback' => 'workbench_scheduler_manage_schedules_access_check',
'file' => 'workbench_scheduler.admin.inc',
);
// Edit existing node schedule for a revision.
$items['node/%node/manage_schedules/%/edit'] = array(
'title' => 'Edit Revision Schedule',
'description' => 'Edit the schedule for this revision',
'type' => MENU_CALLBACK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_scheduler_admin_edit_revision_schedule',
1,
3,
),
'access callback' => 'workbench_scheduler_manage_schedules_access_check',
'file' => 'workbench_scheduler.admin.inc',
);
return $items;
}
/**
* Access callback to see if current node type allows schedules.
*
* @return bool
* Return boolean value.
*/
function workbench_scheduler_manage_schedules_access_check() {
// First make sure user has access.
if (user_access('set workbench schedule')) {
// Attempt to load schedule types and node.
if (($types = workbench_scheduler_get_types()) && ($node = node_load(arg(1)))) {
// Check if node is a schedule type.
return !empty($types) && in_array($node->type, $types);
}
}
return FALSE;
}
/**
* Implements hook_admin_paths().
*/
function workbench_scheduler_admin_paths() {
if (variable_get('node_admin_theme', FALSE)) {
$paths = array(
'node/*/manage_schedules' => TRUE,
'node/*/manage_schedules/*/edit' => TRUE,
);
return $paths;
}
}
/**
* Implements hook_permission().
*
* Provides permissions for workbench schedules.
*/
function workbench_scheduler_permission() {
$permissions = array();
$permissions['administer workbench schedules'] = array(
'title' => t('Administer workbench schedules'),
);
// Expand for each content type.
$permissions['set workbench schedule'] = array(
'title' => t('Set workbench schedule'),
);
$permissions['set any workbench schedule'] = array(
'title' => t('Set any workbench schedule'),
);
if ($schedules = workbench_scheduler_load_schedules()) {
foreach ($schedules as $schedule) {
$permissions['set workbench schedule for ' . $schedule->name] = array(
'title' => t('Set workbench schedule for "@label"', array(
'@label' => $schedule->label,
)),
);
}
}
return $permissions;
}
/**
* Implements hook_action_info().
*/
function workbench_scheduler_action_info() {
module_load_include('inc', 'workbench_scheduler', 'actions/workbench_scheduler.action');
return array(
'workbench_scheduler_schedules_action' => array(
'type' => 'node',
'label' => t('Set Workbench schedule to a node'),
'behavior' => array(
'changes_property',
),
'configurable' => FALSE,
'vbo_configurable' => TRUE,
'triggers' => array(
'any',
),
),
);
}
/**
* Implements hook_node_load().
*/
function workbench_scheduler_node_load($nodes, $types) {
// Are there any node types that have schedules setup?
if ($scheduled_types = workbench_scheduler_get_types()) {
// Any of these nodes match those types?
if (count(array_intersect($scheduled_types, $types))) {
// Fetch schedule data for each node based on nid & vid.
foreach ($nodes as $nid => &$node) {
// Is this node one of the scheduled types?
if (in_array($node->type, $scheduled_types)) {
// Fetch the scheduled data for this node.
if ($schedule = workbench_scheduler_load_node_schedule($nid, $node->vid)) {
$node->workbench_schedule = $schedule;
}
}
}
}
}
}
/**
* Implements hook_node_insert().
*/
function workbench_scheduler_node_insert($node) {
if (isset($node->workbench_schedule)) {
// Save the schedule.
workbench_scheduler_save_node_schedule($node->nid, $node->vid, (array) $node->workbench_schedule);
}
}
/**
* Implements hook_node_update().
*/
function workbench_scheduler_node_update($node) {
if (isset($node->workbench_schedule)) {
// Update the schedule.
workbench_scheduler_save_node_schedule($node->nid, $node->vid, (array) $node->workbench_schedule);
}
}
/**
* Implements hook_node_delete().
*/
function workbench_scheduler_node_delete($node) {
// Delete any schedule data for this node.
workbench_scheduler_delete_node_schedule($node->nid);
}
/**
* Implements hook_node_type_delete().
*/
function workbench_scheduler_node_type_delete($info) {
$delete = db_delete('workbench_scheduler_types');
$delete
->condition('type', $info->type);
$delete
->execute();
}
/**
* Implements hook_node_revision_delete().
*/
function workbench_scheduler_node_revision_delete($node) {
// Delete the schedule for this revision.
workbench_scheduler_delete_node_schedule($node->nid, $node->vid);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function workbench_scheduler_form_node_form_alter(&$form, &$form_state, $form_id) {
// Does the user have permission to set schedules?
$user_permission = user_access('set workbench schedule');
// And are there any schedules for this node type?
$type_schedules = workbench_scheduler_load_type_schedules($form['#node']->type);
if ($user_permission && $type_schedules) {
// Add a scheduler section.
$form['workbench_scheduler'] = array(
'#type' => 'fieldset',
'#title' => t('Workbench Schedule'),
'#description' => t('Select a schedule for changing moderation states.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#access' => user_access('set workbench schedule'),
'#attached' => array(
'js' => array(
drupal_get_path('module', 'workbench_scheduler') . '/js/workbench_scheduler.js',
),
),
);
// Build an options array for which schedules to choose.
$schedule_options = array();
// Add default option of no schedule.
$schedule_options[] = array(
'label' => t('No Schedule'),
'start_state' => '',
'end_state' => '',
);
// Retrieve a list of human safe moderation state names.
$moderation_states = workbench_scheduler_state_labels();
// Add each type schedule to the options array.
foreach ($type_schedules as $schedule) {
if (user_access('set any workbench schedule') || user_access('set workbench schedule for ' . $schedule->name)) {
$schedule_options[$schedule->sid] = array(
'label' => $schedule->label,
'start_state' => !empty($schedule->start_state) ? $moderation_states[$schedule->start_state] : '',
'end_state' => !empty($schedule->end_state) ? $moderation_states[$schedule->end_state] : '',
);
}
}
// Add schedules to JavaScript settings.
$form['workbench_scheduler']['#attached']['js'][] = array(
'data' => array(
'workbench_scheduler' => array(
'schedules' => $schedule_options,
),
),
'type' => 'setting',
);
// Create table select for selecting schedule.
$form['workbench_scheduler']['workbench_scheduler_sid'] = array(
'#type' => 'tableselect',
'#title' => t('Select Schedule'),
'#description' => t('Select the schedule to use for this node.'),
'#options' => $schedule_options,
'#multiple' => FALSE,
'#header' => array(
'label' => t('Name'),
'start_state' => t('Start State'),
'end_state' => t('End State'),
),
);
// Fieldset for collecting schedule dates.
$form['workbench_scheduler']['dates'] = array(
'#type' => 'fieldset',
'#title' => t('Schedule Dates'),
'#description' => t('Select the start and/or end dates to trigger states changes for this node.'),
'#states' => array(
'invisible' => array(
':input[name=workbench_scheduler_sid]' => array(
'value' => '0',
),
),
),
);
// Start date.
$form['workbench_scheduler']['dates']['workbench_scheduler_start_date'] = array(
'#type' => 'date_popup',
'#date_format' => 'Y-m-d H:i',
'#title' => t('Start date'),
'#description' => t('Select the date to switch this node to the scheduled "start state"'),
);
// End date.
$form['workbench_scheduler']['dates']['workbench_scheduler_end_date'] = array(
'#type' => 'date_popup',
'#date_format' => 'Y-m-d H:i',
'#title' => t('End date'),
'#description' => t('Select the date to switch this node to the scheduled "end state"'),
);
$node_schedule = isset($form['#node']->workbench_schedule) ? $form['#node']->workbench_schedule : FALSE;
// Have a node schedule(editing a node)? Use for defaults.
if ($node_schedule) {
$form['workbench_scheduler']['workbench_scheduler_sid']['#default_value'] = $node_schedule->sid;
if ($node_schedule->start_date) {
// Format date for field.
$start_date = format_date($node_schedule->start_date, 'custom', 'Y-m-d H:i:s');
$form['workbench_scheduler']['dates']['workbench_scheduler_start_date']['#default_value'] = $start_date;
}
if ($node_schedule->end_date) {
// Format date for field.
$end_date = format_date($node_schedule->end_date, 'custom', 'Y-m-d H:i:s');
$form['workbench_scheduler']['dates']['workbench_scheduler_end_date']['#default_value'] = $end_date;
}
}
// Display message depending on schedule settings.
$msg = '';
$type_settings = variable_get('workbench_scheduler_' . $form['#node']->type, array());
// Check if only process latest revision.
if (in_array('workbench_scheduler_limit_current_revision', $type_settings)) {
$msg = t('Only the schedule for the most recent revision will be run.');
}
elseif (isset($form['#node']->nid)) {
// Check if there are existing active schedules.
$active_schedules = workbench_scheduler_count_node_schedules($form['#node']->nid, 'active');
// Schedules set?
if ($active_schedules > 0) {
// Display message about active schedules.
$msg = format_plural($active_schedules, 'There is still 1 active schedule for a previous revision. !link', 'There are still @count active schedules for previous revisions. !link', array(
'!link' => l(t('Review Schedules'), 'node/' . $form['#node']->nid . '/manage_schedules'),
));
}
}
if ($msg) {
$form['workbench_scheduler']['note'] = array(
'#type' => 'item',
'#title' => t('Note'),
'#markup' => $msg,
);
}
// Add custom validation and submission hooks.
$form['#validate'][] = 'workbench_scheduler_node_form_validate';
// Add to submit button action so that will have access to the new,
// Nid & vid values.
$form['actions']['submit']['#submit'][] = 'workbench_scheduler_node_form_submit';
}
}
/**
* Validation function for editing a scheduled node.
*
* @param array $form
* The form array.
* @param array $form_state
* The form state array.
*/
function workbench_scheduler_node_form_validate($form, &$form_state) {
if (!empty($form_state['values']['workbench_scheduler_start_date'])) {
$start_date = $form_state['values']['workbench_scheduler_start_date'];
}
else {
$start_date = '';
}
if (!empty($form_state['values']['workbench_scheduler_end_date'])) {
$end_date = $form_state['values']['workbench_scheduler_end_date'];
}
else {
$end_date = '';
}
// Retrieve schedule data.
$sid = $form_state['values']['workbench_scheduler_sid'];
// Was a schedule selected?
if ($sid && $sid > 0) {
// Both a start date and an end date, start should be before end.
if ($start_date && $end_date && $start_date >= $end_date) {
form_set_error('workbench_scheduler_end_date', t('End date must be after start date.'));
}
elseif ($start_date || $end_date) {
// All good.
}
else {
// Only other option, are no dates. throw an error.
form_set_error('workbench_scheduler_start_date', t('Must provide either a start date or end date for workbench schedule'));
}
}
elseif ($start_date || $end_date) {
if ($form_state['values']['workbench_scheduler_sid'] != 0) {
// Need to select a schedule for the dates unless none were selected.
form_set_error('workbench_scheduler_sid', t('Must select a workbench schedule to apply to the provided date(s)'));
}
}
}
/**
* Submit function for editing a scheduled node.
*
* @param array $form
* The form array.
* @param array $form_state
* The form state array.
*/
function workbench_scheduler_node_form_submit($form, &$form_state) {
if (!empty($form_state['values']['workbench_scheduler_start_date'])) {
$start_date = $form_state['values']['workbench_scheduler_start_date'];
}
else {
$start_date = '';
}
if (!empty($form_state['values']['workbench_scheduler_end_date'])) {
$end_date = $form_state['values']['workbench_scheduler_end_date'];
}
else {
$end_date = '';
}
// Retrieve schedule data.
$schedule_data = array(
'sid' => $form_state['values']['workbench_scheduler_sid'],
// Format to timestamp for storage.
'start_date' => strtotime($start_date),
// Format to timestamp for storage.
'end_date' => strtotime($end_date),
);
// Get the node data from the form state.
$nid = $form_state['node']->nid;
$vid = $form_state['node']->vid;
// Have a schedule selected? sid of 0 for removing a schedule.
if ($schedule_data['sid'] || $schedule_data['sid'] == 0) {
// Attempt to save the schedule for this node.
if (workbench_scheduler_save_node_schedule($nid, $vid, $schedule_data)) {
// Success.
}
else {
drupal_set_message(t('Error saving workbench schedule for node'), 'error', FALSE);
}
}
}
/**
* Implements hook_cron().
*/
function workbench_scheduler_cron() {
// Call the run function to process schedules.
if ($count = _workbench_scheduler_run()) {
watchdog('workbench_scheduler', '@count Workbench Schedules were processed.', array(
'@count' => $count,
));
}
}
/**
* Process the different workbench scheduler schedules.
*/
function _workbench_scheduler_run() {
$count = 0;
// Only run if there are schedules.
if (workbench_scheduler_schedules_exist()) {
// Run scheduler.
$curr_time = time();
// Process the start date schedules.
$count += workbench_scheduler_process_start_dates($curr_time);
// Process the end date schedules.
$count += workbench_scheduler_process_end_dates($curr_time);
}
return $count;
}
/**
* Implements hook_views_api().
*/
function workbench_scheduler_views_api() {
return array(
'api' => 2.0,
);
}
/**
* Retrieve a list of all the content types associated to schedules.
*
* @return mixed
* Returns a boolean FALSE or array of associated types.
*/
function workbench_scheduler_get_types() {
$types =& drupal_static(__FUNCTION__);
if (!isset($types)) {
// Build select query to retrieve all distinct content types,
// In the workbench_scheduler_types table.
$types_query = db_select('workbench_scheduler_types', 'wst')
->fields('wst', array(
'type',
))
->distinct()
->execute();
// If query returns results, build an array.
if ($types_query
->rowCount()) {
foreach ($types_query as $type) {
$types[] = $type->type;
}
}
}
return $types;
}
/**
* Returns the number of results for schedules with given machine name(s).
*
* @param mixed $names
* Machine name of schedule(s).
*
* @return int
* The number of results.
*/
function _workbench_schedule_check_machine_name_exists($names) {
if (!is_array($names)) {
$names = array(
$names,
);
}
// Turn into an associate array.
$names = array_flip($names);
if ($schedule_names = workbench_scheduler_schedule_names()) {
// Find the intersection.
$intersect = array_intersect_key($names, $schedule_names);
return count($intersect);
}
// No schedules, so return 0.
return 0;
}
/**
* Generate an array of moderation states.
*
* @return array
* Array of moderation states.
*/
function workbench_scheduler_state_labels() {
$states =& drupal_static(__FUNCTION__);
if (!isset($states)) {
// Retrieve the moderation states from the workbench_moderation module.
module_load_include('module', 'workbench_moderation');
$states = workbench_moderation_state_labels();
// Add unpublished to list since it is not actually a moderation state.
$states['unpublished'] = t('Unpublished');
}
return $states;
}
/**
* Generate an array of schedule machine names.
*
* @return array
* An array of schedule machine names.
*/
function workbench_scheduler_schedule_names() {
$names =& drupal_static(__FUNCTION__);
if (!isset($names)) {
// Fetch list of all machine_names for schedules from the DB.
$names = db_select('workbench_scheduler_schedules', 'wss')
->fields('wss', array(
'name',
'label',
))
->execute()
->fetchAllAssoc('name');
// Simplify the assoc array.
foreach ($names as &$name) {
$name = $name->label;
}
}
return $names;
}
/**
* Return an array of counts of schedules for a specific node.
*
* @param int $nid
* Node id.
* @param bool|string $status
* Which status to return counts.
*
* @return int
* Return count of schedules.
*/
function workbench_scheduler_count_node_schedules($nid, $status = FALSE) {
$counts = array(
'active' => 0,
'completed' => 0,
);
// Select from the workbench_scheduler_nodes table.
$node_schedules = db_select('workbench_scheduler_nodes', 'wsn')
->fields('wsn', array(
'nid',
));
// Retrieve count of completed.
$node_schedules
->addExpression('SUM(wsn.completed)', 'completed_schedules');
// Retrieve count of active (count of completed - sum).
$node_schedules
->addExpression('(COUNT(wsn.completed) - SUM(wsn.completed))', 'active_schedules');
// WHERE node id matches this node id.
$node_schedules
->condition('wsn.nid', $nid)
->range(0, 1);
// Execute.
$node_schedules = $node_schedules
->execute();
// Returned results?
if ($node_schedules
->rowCount()) {
// Gather data from first result.
$result = $node_schedules
->fetchObject();
$counts['completed'] = $result->completed_schedules;
$counts['active'] = $result->active_schedules;
}
// Only want count for a specific status?
if ($status) {
if (isset($counts[$status])) {
return $counts[$status];
}
else {
return 0;
}
}
return array_sum($counts);
}
/**
* Retrieves schedule data for a given node revision.
*
* @param int $nid
* Node id.
* @param int $vid
* Revision id.
*
* @return mixed
* Return an array of scheduler data for the node, or boolean TRUE
* if there is no schedule for this node.
*/
function workbench_scheduler_load_node_schedule($nid, $vid) {
// Select from the workbench_scheduler_nodes table.
$node_schedule_query = db_select('workbench_scheduler_nodes', 'wsn');
// Join the workbench_scheduler_schedule table on the schedule id.
$node_schedule_query
->innerjoin('workbench_scheduler_schedules', 'wss', 'wsn.sid = wss.sid');
// Retrieve the node start and end date.
$node_schedule_query
->fields('wsn', array(
'start_date',
'end_date',
'completed',
))
->fields('wss');
// WHERE node id matches this node id.
$node_schedule_query
->condition('wsn.nid', $nid)
->condition('wsn.vid', $vid)
->range(0, 1);
$node_schedule = $node_schedule_query
->execute();
// Was data successfully retrieve?
if ($node_schedule
->rowCount()) {
return $node_schedule
->fetchObject();
}
// Return a boolean false if nothing is retrieved.
return FALSE;
}
/**
* Saves the schedule data for a given node revision.
*
* @param int $nid
* The node id.
* @param int $vid
* The node revision id.
* @param array $schedule_data
* Array of schedule data to set/update.
*
* @return bool
* Boolean TRUE/TRUE on success of query.
*/
function workbench_scheduler_save_node_schedule($nid, $vid, $schedule_data) {
// Need to have the sid and start date or end date data.
if (isset($schedule_data['sid']) && ($schedule_data['sid'] == 0 || $schedule_data['start_date'] || $schedule_data['end_date'])) {
if ($schedule_data['sid'] != 0) {
// Run a merge query to insert or update the row.
$merge = db_merge('workbench_scheduler_nodes')
->key(array(
'nid' => $nid,
'vid' => $vid,
))
->fields(array(
'sid' => $schedule_data['sid'],
// Start date if it was passed.
'start_date' => $schedule_data['start_date'] ? $schedule_data['start_date'] : 0,
// End if it was passed.
'end_date' => $schedule_data['end_date'] ? $schedule_data['end_date'] : 0,
))
->execute();
// Was the merge successful?
if ($merge) {
// Apply update hooks.
module_invoke_all('workbench_scheduler_post_save_node_schedule', $nid, $vid, $schedule_data);
return TRUE;
}
}
elseif ($schedule_data['sid'] == 0) {
// Make sure the this revision has no schedule.
workbench_scheduler_delete_node_schedule($nid, $vid);
// Schedule_data removed.
return TRUE;
}
}
// Return boolean false if missing fields or failed query.
return FALSE;
}
/**
* Delete the schedule(s) for a node.
*
* @param int $nid
* The node_id.
* @param int $vid
* Optional node revision id.
* @param int $sid
* Optional schedule id.
*
* @return bool
* Boolean result of delete query.
*/
function workbench_scheduler_delete_node_schedule($nid, $vid = 0, $sid = 0) {
// Build the delete query.
$delete = db_delete('workbench_scheduler_nodes')
->condition('nid', $nid);
// Deleting for a specific revision?
if ($vid) {
$delete
->condition('vid', $vid);
}
// Deleting for a specific schedule.
if ($sid) {
$delete
->condition('sid', $sid);
}
$delete = $delete
->execute();
if ($delete) {
module_invoke_all('workbench_scheduler_post_delete_node_schedule', $nid, $vid, $sid);
return $delete;
}
}
/**
* Mark that a node schedule has been completed.
*
* @param int $nid
* The node id.
* @param int $vid
* The node revision id.
* @param int $sid
* The schedule id.
*/
function workbench_scheduler_node_set_complete($nid, $vid, $sid) {
db_update('workbench_scheduler_nodes')
->fields(array(
'completed' => 1,
))
->condition('nid', $nid)
->condition('vid', $vid)
->condition('sid', $sid)
->execute();
module_invoke_all('workbench_scheduler_post_node_set_complete', $nid, $vid, $sid);
}
/**
* Return a list of the schedules for a given content type.
*
* @param mixed $types
* A string value of a content type or an array of content types.
*
* @return mixed
* An array of schedule objects or boolean false.
*/
function workbench_scheduler_load_type_schedules($types) {
$schedules =& drupal_static(__FUNCTION__);
// Check to see if only retrieving for a single type.
$get_single = FALSE;
if ($schedules) {
// Create an array of the type schedules already retrieved.
$retrieved_types = array_keys($schedules);
}
// Passed a single content type?
if (!is_array($types)) {
// Retrieving a single.
$get_single = $types;
// Make it an array.
$types = array(
$types,
);
}
// Find out which of the types have not already been retrieved.
if (isset($retrieved_types)) {
$missing_types = array_diff($types, $retrieved_types);
}
else {
$missing_types = $types;
}
// Only need to perform query if no schedules or missing types.
if (!$schedules || count($schedules) < 1 || count($missing_types) > 0) {
// Retrieve machine name of schedules for types.
$schedules_query = db_select('workbench_scheduler_types', 'wst')
->fields('wst')
->condition('wst.type', $missing_types, 'IN')
->execute();
// Retrieved results?
if ($schedules_query
->rowCount()) {
// Add the schedules to the array.
foreach ($schedules_query as $type_schedule) {
$schedule = workbench_scheduler_load_schedules($type_schedule->name);
$schedules[$type_schedule->type][$schedule->sid] = $schedule;
}
}
}
if ($schedules && count($schedules) > 0) {
// Flip types to make it assoc.
$types = array_flip($types);
// Take only a subsection of what was retrieved.
$return_schedules = array_intersect_key($schedules, $types);
// Able to get they types?
if (count($return_schedules) > 0) {
// Only want a single one?
if ($get_single) {
// And that type exists in the array?
if (isset($return_schedules[$get_single])) {
// Only return schedules for that type.
$return_schedules = $return_schedules[$get_single];
}
}
return $return_schedules;
}
}
return FALSE;
}
/**
* Retrieve an array of schedule data.
*
* @param mixed $names
* Machine name of a schedule or array of machine names (optional).
*
* @return mixed
* An array of schedule data or boolean FALSE.
*/
function workbench_scheduler_load_schedules($names = FALSE) {
$schedules =& drupal_static(__FUNCTION__);
// Boolean to check if only retrieving a single schedule.
$get_single = FALSE;
if ($schedules) {
// Create an array of the schedules names already retrieved.
$retrieved_names = array_keys($schedules);
}
// Passed machine name(s) of schedules to retrieve?
if ($names) {
// Passed a single schedule name?
if (!is_array($names)) {
// Retrieving a single.
$get_single = $names;
// Make it an array.
$names = array(
$names,
);
}
// Find out which of the schedules have not already been retrieved.
if (isset($retrieved_names)) {
$missing_names = array_diff($names, $retrieved_names);
}
else {
$missing_names = $names;
}
}
// Only need to query the DB if noe schedules, no names or missing names.
if (!$schedules || count($schedules) < 1 || !isset($missing_names) || count($missing_names) > 0) {
// Build query to retrieve schedules.
$schedules_query = db_select('workbench_scheduler_schedules', 'wss');
// Left join the types table to get related content types based on.
// Schedule machine names.
$schedules_query
->leftjoin('workbench_scheduler_types', 'wst', 'wss.name = wst.name');
// Retrieve all the fields for a schedule.
$schedules_query
->fields('wss')
->fields('wst', array(
'type',
));
// Have missing names we want to only retrieve?
if (isset($missing_names)) {
// Add condition to only retrieve these schedules.
$schedules_query
->condition('wss.name', $missing_names, 'IN');
}
// Have previously retrieved schedules?
if (isset($retrieved_names)) {
// Add condition not to retrieve these.
$schedules_query
->condition('wss.name', $retrieved_names, 'NOT IN');
}
// Perform the query.
$retrieved_schedules = $schedules_query
->execute();
// Were results returned?
if ($retrieved_schedules
->rowCount()) {
// Loop through the results.
foreach ($retrieved_schedules as $schedule) {
// Already retrieved the schedule?
if (isset($schedules[$schedule->name])) {
// Yes, then just add this content type to it.
$schedules[$schedule->name]->types[] = $schedule->type;
}
else {
// No, add to schedules array.
$schedules[$schedule->name] = $schedule;
// Add an array for the content types.
$schedules[$schedule->name]->types = array(
$schedule->type,
);
// Do not need the type attribute.
unset($schedules[$schedule->name]->type);
}
}
}
}
// Have schedules to return?
if ($schedules && count($schedules) > 0) {
// Have a specific sub set of schedules ?
if ($names) {
// Flip names to make it assoc.
$names = array_flip($names);
// Take only a sub section of the returned schedules.
$return_schedules = array_intersect_key($schedules, $names);
// Only want a single schedule?
if ($get_single) {
// Is it present in the array?
if (isset($return_schedules[$get_single])) {
// Return just that schedule.
return $return_schedules[$get_single];
}
}
elseif (count($return_schedules) > 0) {
return $return_schedules;
}
}
else {
// Return all of the retrieved schedules.
return $schedules;
}
}
// No schedules fetched, return boolean FALSE.
return FALSE;
}
/**
* Inserts/updates a schedule.
*
* @param string $name
* The machine name of the schedule.
* @param array $schedule_data
* An array of other schedule data.
*
* @return bool
* boolean TRUE/FALSE if the insert/update was a success
*/
function workbench_scheduler_save_schedule($name, $schedule_data) {
// Need to have the label, start_state and end_state.
if (isset($schedule_data['label']) && isset($schedule_data['start_state']) && isset($schedule_data['end_state'])) {
// Run a merge query to insert or update the row.
$merge = db_merge('workbench_scheduler_schedules')
->key(array(
'name' => $name,
))
->fields(array(
'label' => trim($schedule_data['label']),
'start_state' => trim($schedule_data['start_state']),
'end_state' => trim($schedule_data['end_state']),
))
->execute();
if ($merge) {
// Core schedule data was updated, need to update types?
if (isset($schedule_data['types'])) {
// Delete the old types set.
db_delete('workbench_scheduler_types')
->condition('name', $name)
->execute();
// Build insert query for new types.
$insert_query = db_insert('workbench_scheduler_types')
->fields(array(
'name',
'type',
));
foreach ($schedule_data['types'] as $content_type) {
$insert_query
->values(array(
'name' => $name,
'type' => $content_type,
));
}
$insert_query
->execute();
}
// Reset the statics.
drupal_static_reset('workbench_scheduler_load_schedules');
drupal_static_reset('workbench_scheduler_load_type_schedules');
return $merge;
}
}
// Return boolean FALSE if missing fields or failed query.
return FALSE;
}
/**
* Delete schedule(s) add associated data from the database.
*
* @param mixed $names
* Machine name(s) of schedule(s) to be deleted.
*
* @return bool
* True or false is successful.
*/
function workbench_scheduler_delete_schedules($names) {
// If was not passed an array of machine names.
if (!is_array($names)) {
// Make machine name an array with one element.
$names = array(
$names,
);
}
// Get all sid's for the schedules going to be delete.
$sids_query = db_select('workbench_scheduler_schedules', 'wss')
->fields('wss', array(
'sid',
))
->condition('wss.name', $names, 'IN')
->execute();
$sids = array();
if ($sids_query
->rowCount()) {
foreach ($sids_query as $sid) {
$sids[] = $sid->sid;
}
}
// Schedules nodes to delete?
if (count($sids) > 0) {
// Delete all nodes for these schedules.
db_delete('workbench_scheduler_nodes')
->condition('sid', $sids, 'IN')
->execute();
}
// Delete all types associated to these schedules.
db_delete('workbench_scheduler_types')
->condition('name', $names, 'IN')
->execute();
// Delete the schedules themselves.
db_delete('workbench_scheduler_schedules')
->condition('name', $names, 'IN')
->execute();
// Check to see if machine name still exists as validation.
if (_workbench_schedule_check_machine_name_exists($names)) {
// Failed to delete, return false.
return FALSE;
}
else {
// Reset the statics.
drupal_static_reset('workbench_scheduler_load_schedules');
drupal_static_reset('workbench_scheduler_load_type_schedules');
return TRUE;
}
}
/**
* Check to see if schedules exist in the db.
*
* @return bool
* Boolean TRUE/FALSE if at least one schedule exists.
*/
function workbench_scheduler_schedules_exist() {
// Run query to see if an sid can be turned.
// Should return a boolean TRUE/FALSE.
$count = db_select('workbench_scheduler_schedules', 'wss')
->fields('wss', array(
'sid',
))
->execute()
->rowCount();
return $count > 0;
}
/**
* Run schedules for start times / states.
*
* @param int $timestamp
* Timestamp to check against.
*
* @return int|bool
* Count of nodes that were processed or boolean FALSE.
*/
function workbench_scheduler_process_start_dates($timestamp) {
// Fetch all nodes that need to have their states changed to the 'start_date'.
// State before NOW, and have not already done so.
// Instantiate a counter for number of nodes processed.
$count = 0;
// Select from the workbench_scheduler_nodes table.
$schedule_query = db_select('workbench_scheduler_nodes', 'wsn');
// Join node table to get 'type'.
$schedule_query
->innerjoin('node', 'node', 'node.nid = wsn.nid');
// Inner join on workbench_scheduler_schedules table on schedule id.
$schedule_query
->innerjoin('workbench_scheduler_schedules', 'wss', 'wsn.sid = wss.sid');
// Inner join on workbench_moderation_node_history table on nid, revision id.
// And where the current state does not equal the start state.
$schedule_query
->innerjoin('workbench_moderation_node_history', 'wmnh', 'wsn.nid = wmnh.nid AND wsn.vid = wmnh.vid AND wss.start_state != wmnh.state');
// Join the node history table and grab highest vid of the highest hid.
$schedule_query
->leftJoin('workbench_moderation_node_history', 'wmnh2', 'wmnh.nid = wmnh2.nid AND wmnh.vid = wmnh2.vid AND wmnh.hid < wmnh2.hid');
// Retrieve the nid, vid from the workbench_scheduler_nodes table.
$schedule_query
->fields('wsn')
->fields('wss', array(
'start_state',
))
->fields('node', array(
'type',
))
->condition('wsn.completed', 0)
->condition('wsn.start_date', 0, '!=')
->condition('wsn.start_date', $timestamp, '<=');
// Only grab vid with highest hid.
$schedule_query
->isNull('wmnh2.hid');
// Create an or condition.
$db_or = db_or();
// Where no end date.
$db_or
->condition('wsn.end_date', 0);
// Or end date is after now.
$db_or
->condition('wsn.end_date', $timestamp, '>');
// Add or condition to the query.
$schedule_query
->condition($db_or);
// Order by vid to return the highest vid of each node.
$schedule_query
->orderBy('wmnh.vid', 'DESC');
// Execute the query.
$schedule_nodes = $schedule_query
->execute();
// If scheduled nodes are returned from the query.
if ($schedule_nodes
->rowCount()) {
// Include workbench moderation.
module_load_include('module', 'workbench_moderation');
// Loop through each scheduled node.
foreach ($schedule_nodes as $node_schedule) {
$do_process = FALSE;
$type_settings = variable_get('workbench_scheduler_' . $node_schedule->type, array());
// Only process latest revision.
if (in_array('workbench_scheduler_limit_current_revision', $type_settings)) {
// Getting the latest revision.
$rev_list = node_revision_list($node_schedule);
$latest_vid = max(array_keys($rev_list));
if ($node_schedule->vid == $latest_vid) {
$do_process = TRUE;
}
}
else {
$do_process = TRUE;
}
// Load and process the node.
if ($do_process && ($node = node_load($node_schedule->nid, $node_schedule->vid))) {
// If moderation state is 'unpublished' follow a different workflow.
if ($node_schedule->start_state == 'unpublished' && $node->status) {
workbench_scheduler_moderate_unpublish($node);
}
else {
workbench_moderation_moderate($node, $node_schedule->start_state);
}
// Does this schedule have an end date as well?
if ($node_schedule->end_date) {
}
else {
// No end state so mark schedule as complete.
workbench_scheduler_node_set_complete($node_schedule->nid, $node_schedule->vid, $node_schedule->sid);
}
// Update count of run schedules.
$count++;
module_invoke_all('workbench_scheduler_post_process_start_dates', $node_schedule);
}
}
}
// Return int if nodes that were moderated, if none boolean FALSE.
return $count ?: FALSE;
}
/**
* Run schedules for end times / states.
*
* @param int $timestamp
* Timestamp to check against.
*
* @return int|bool
* Count of nodes that were processed or boolean FALSE.
*/
function workbench_scheduler_process_end_dates($timestamp) {
// Fetch all nodes that need to have their states changed to the 'end_date'.
// State before NOW, and have not already done so.
// Instantiate a counter for number of nodes processed.
$count = 0;
// Select from the workbench_scheduler_nodes table.
$schedule_query = db_select('workbench_scheduler_nodes', 'wsn');
// Join node table to get 'type'.
$schedule_query
->innerjoin('node', 'node', 'node.nid = wsn.nid');
// Inner join on workbench_scheduler_schedules table on schedule id.
$schedule_query
->innerjoin('workbench_scheduler_schedules', 'wss', 'wsn.sid = wss.sid');
// Inner join on workbench_moderation_node_history table on nid, revision id.
// And where the current state does not equal the end state.
$schedule_query
->innerjoin('workbench_moderation_node_history', 'wmnh', 'wsn.nid = wmnh.nid AND wsn.vid = wmnh.vid AND wss.end_state != wmnh.state');
// Join the node history table and grab highest vid of the highest hid.
$schedule_query
->leftJoin('workbench_moderation_node_history', 'wmnh2', 'wmnh.nid = wmnh2.nid AND wmnh.vid = wmnh2.vid AND wmnh.hid < wmnh2.hid');
// Retrieve the nid, vid from the workbench_scheduler_nodes table.
$schedule_query
->fields('wsn')
->fields('wss', array(
'end_state',
))
->fields('node', array(
'type',
))
->condition('wsn.completed', 0)
->condition('wsn.end_date', 0, '!=')
->condition('wsn.end_date', $timestamp, '<=');
$schedule_query
->isNull('wmnh2.hid');
// Order by vid to return the highest vid of each node.
$schedule_query
->orderBy('wmnh.vid', 'DESC');
// Execute the query.
$scheduled_nodes = $schedule_query
->execute();
// If scheduled nodes are returned from the query.
if ($scheduled_nodes
->rowCount()) {
// Include workbench moderation.
module_load_include('module', 'workbench_moderation');
// Loop through each scheduled node.
foreach ($scheduled_nodes as $node_schedule) {
$do_process = FALSE;
$type_settings = variable_get('workbench_scheduler_' . $node_schedule->type, array());
// Only process latest revision.
if (in_array('workbench_scheduler_limit_current_revision', $type_settings)) {
// Getting the latest revision.
$rev_list = node_revision_list($node_schedule);
$latest_vid = max(array_keys($rev_list));
if ($node_schedule->vid == $latest_vid) {
$do_process = TRUE;
}
}
else {
$do_process = TRUE;
}
// Need to load the node.
if ($do_process && ($node = node_load($node_schedule->nid, $node_schedule->vid))) {
// Only moderate if state is not 'unpublish'.
// If moderation state is 'unpublished' follow a different workflow.
if ($node_schedule->end_state == 'unpublished' && $node->status) {
workbench_scheduler_moderate_unpublish($node);
}
else {
workbench_moderation_moderate($node, $node_schedule->end_state);
}
// Mark schedule as completed.
workbench_scheduler_node_set_complete($node_schedule->nid, $node_schedule->vid, $node_schedule->sid);
// Update count of run schedules.
$count++;
module_invoke_all('workbench_scheduler_post_process_end_dates', $node_schedule);
}
}
}
// Return int if nodes that were moderated, if none boolean FALSE.
return $count ?: FALSE;
}
/**
* Unpublish a node following proper moderation workflow.
*
* @param object $node
* The node to unpublish.
*/
function workbench_scheduler_moderate_unpublish($node) {
module_load_include('module', 'workbench_moderation');
module_load_include('inc', 'workbench_moderation', 'workbench_moderation.node');
// Copied from workbench_moderation_node_unpublish_form_submit.
// In workbench_moderation.node.inc.
// Remove the moderation record's "published" flag.
db_update('workbench_moderation_node_history')
->condition('hid', $node->workbench_moderation['published']->hid)
->fields(array(
'published' => 0,
))
->execute();
// Moderate the revision.
workbench_moderation_moderate($node, workbench_moderation_state_none());
// Make sure the 'current' revision is the 'live' revision.
// I.e, the revision in {node}.
$live_revision = workbench_moderation_node_current_load($node);
$live_revision->status = 0;
$live_revision->revision = 0;
$live_revision->workbench_moderation['updating_live_revision'] = TRUE;
// @TODO: do we trust node_save() here?
node_save($live_revision);
}
/**
* Content Type Edit Form.
*/
function workbench_scheduler_form_node_type_form_alter(&$form, &$form_state, $form_id) {
// Add Workbench Scheduler options.
$form['workflow']['workbench_scheduler'] = array(
'#type' => 'checkboxes',
'#title' => t('Workbench Scheduler'),
'#options' => array(
'workbench_scheduler_limit_current_revision' => t('Allow active schedule for latest revisions only'),
),
'#states' => array(
'visible' => array(
':input[name="node_options[moderation]"]' => array(
'checked' => TRUE,
),
),
),
'#weight' => 1,
'#default_value' => variable_get('workbench_scheduler_' . $form['#node_type']->type, array()),
);
}