hosting_task.module in Hosting 5
Same filename and directory in other branches
Web server node type is defined here.
File
task/hosting_task.moduleView source
<?php
/**
* @file Web server node type is defined here.
*/
/**
* Implementation of hook_menu().
*/
function hosting_task_menu($may_cache) {
$items = array();
if (!$may_cache) {
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
foreach (hosting_available_tasks($node) as $type => $task) {
$items[] = array(
'path' => 'node/' . arg(1) . '/task_' . $type,
'title' => $task['title'],
'description' => $task['description'],
'callback' => 'drupal_get_form',
'callback arguments' => array(
'hosting_task_confirm_form',
$node,
$type,
),
'access' => user_access('create ' . $type . ' task'),
'type' => MENU_LOCAL_TASK,
'weight' => $task['weight'] ? $task['weight'] : 0,
);
}
}
}
return $items;
}
function hosting_task_node_info() {
#management
$types["task"] = array(
"type" => 'task',
"name" => 'Task',
"module" => 'hosting_task',
"has_title" => FALSE,
"title_label" => '',
# "description" => hosting_node_help("task"),
"has_body" => 0,
"body_label" => '',
"min_word_count" => 0,
);
return $types;
}
function hosting_task_access($op, $node) {
if (user_access('administer tasks')) {
return TRUE;
}
}
function hosting_task_perm() {
return array(
'administer tasks',
'create backup task',
'create restore task',
'create disable task',
'create enable task',
'create delete task',
'create verify task',
'view own tasks',
'view task',
'access task logs',
'retry failed tasks',
);
}
function hosting_task_log($vid, $type, $message, $error = '', $timestamp = null) {
$timestamp = $timestamp ? $timestamp : mktime();
db_query("INSERT INTO {hosting_task_log} (vid, type, message, error, timestamp) VALUES (%d, '%s', '%s', '%s', %d)", $vid, $type, $message, $error, $timestamp);
}
function hosting_task_retry($task_id) {
$node = node_load($task_id);
if (!$node->queued) {
drupal_set_message(t("The task is being retried and has been added to the hosting queue again"));
hosting_task_log($node->vid, 'queue', t("The task is being retried and has been added to the hosting queue again"));
$node->revision = FALSE;
$node->queued = 1;
node_save($node);
}
}
/**
* Helper function to generate new task node
*/
function hosting_add_task($nid, $type, $args = null) {
global $user;
$node = node_load($nid);
$task = new stdClass();
$task->type = 'task';
$task->uid = $user->uid;
$task->status = 1;
#todo: make this pretty
$task->title = t("!type !title", array(
'!type' => $type,
'!title' => $node->title,
));
$task->task_type = $type;
$task->task_status = HOSTING_TASK_QUEUED;
$task->rid = $node->nid;
$task->queued = 1;
#arguments, such as which backup to restore.
if (is_array($args)) {
$task->task_args = $args;
}
node_save($task);
}
/**
* Implementation of hook_form().
*/
function hosting_task_confirm_form(&$node, $task) {
$tasks = hosting_available_tasks($node);
$form['help'] = array(
'#value' => $tasks[$task]['description'],
);
$form['nid'] = array(
'#type' => 'value',
'#value' => $node->nid,
);
$form['task'] = array(
'#type' => 'value',
'#value' => $task,
);
$form['parameters'] = array(
'#tree' => TRUE,
);
$func = 'hosting_task_' . $task . '_form';
if (function_exists($func)) {
$form['parameters'] += $func($node);
}
$func = $func . '_validate';
if (function_exists($func)) {
$form['#validate'][$func] = array(
$node,
$task,
);
}
$question = t("Are you sure you want to @task @object?", array(
'@task' => $task,
'@object' => $node->title,
));
return confirm_form($form, $question, 'node/' . $node->nid, '', $tasks[$task]['title']);
}
function hosting_task_restore_form($node) {
$list = hosting_site_backup_list($node->nid);
if (sizeof($list)) {
$form['bid'] = array(
'#type' => 'radios',
'#title' => t('Backups'),
'#options' => $list,
'#required' => TRUE,
);
}
else {
$form['no_backups'] = array(
'#type' => 'item',
'#title' => t('Backups'),
'#value' => t('There are no valid backups available.'),
);
}
return $form;
}
function hosting_task_confirm_form_submit($form_id, $values) {
hosting_add_task($values['nid'], $values['task'], $values['parameters']);
return 'node/' . $values['nid'];
}
function hosting_task_set_title(&$node) {
$ref = node_load($node->rid);
$node->title = drupal_ucfirst($node->task_type) . ' ' . $ref->title;
db_query("UPDATE {node} SET title='%s' WHERE nid=%d", $node->title, $node->nid);
db_query("UPDATE {node_revisions} SET title='%s' WHERE vid=%d", $node->title, $node->vid);
}
/**
* Determine whether there is an outstanding task of a specific type.
*
* This is used to ensure that there are not multiple tasks of the same type queued.
*/
function hosting_task_outstanding($nid, $type) {
$return = db_result(db_query("\n SELECT q.nid FROM \n {hosting_task_queue} q LEFT JOIN {hosting_task} a ON a.nid=q.nid \n WHERE \n a.rid = %d \n AND q.status = 1 \n AND a.task_type = '%s' \n LIMIT 1", $nid, $type));
return $return;
}
/**
* Return the amount of items still in the queue
*/
function hosting_task_count() {
return db_result(db_query("SELECT COUNT(nid) FROM {hosting_task_queue} q WHERE q.status = 1"));
}
function hosting_available_tasks($node) {
return module_invoke_all('hosting_tasks', $node);
}
/**
* Implementation of hook_insert().
*/
function hosting_task_insert($node) {
db_query("INSERT INTO {hosting_task} (vid, nid, task_type, rid, executed, task_status) VALUES (%d, %d, '%s', %d, %d, %d)", $node->vid, $node->nid, $node->task_type, $node->rid, $node->executed, $node->task_status);
if (is_array($node->task_args)) {
foreach ($node->task_args as $key => $value) {
db_query("INSERT INTO {hosting_task_arguments} (vid, nid, name, value) VALUES (%d, %d, '%s', '%s')", $node->vid, $node->nid, $key, $value);
}
}
hosting_task_set_title($node);
if ($node->is_new) {
db_query("INSERT INTO {hosting_task_queue} (nid, timestamp, status) VALUES (%d, %d, %d)", $node->nid, mktime(), 1);
}
else {
db_query("UPDATE {hosting_task_queue} SET status = %d WHERE nid=%d", $node->queued, $node->nid);
}
}
/**
* Implementation of hook_update().
*
* As an existing node is being updated in the database, we need to do our own
* database updates.
*/
function hosting_task_update($node) {
// if this is a new node or we're adding a new revision,
if ($node->revision) {
hosting_task_insert($node);
}
else {
hosting_task_set_title($node);
db_query("UPDATE {hosting_task} SET nid=%d, task_type = '%s', rid = %d, executed=%d, task_status=%d WHERE vid=%d", $node->nid, $node->task_type, $node->rid, $node->executed, $node->task_status, $node->vid);
db_query("UPDATE {hosting_task_queue} SET status=%d WHERE nid=%d", $node->queued, $node->nid);
if (is_array($node->task_args)) {
# Wipe out old arguments first, since arguments could theoretically be removed.
db_query("DELETE FROM {hosting_task_arguments} WHERE vid=%d", $node->vid);
foreach ($node->task_args as $key => $value) {
db_query("INSERT INTO {hosting_task_arguments} (vid, nid, name, value) VALUES (%d, %d, '%s', '%s')", $node->vid, $node->nid, $key, $value);
}
}
}
}
function hosting_nodeapi_task_delete_revision(&$node) {
db_query('DELETE FROM {hosting_task} WHERE vid = %d', $node->vid);
db_query('DELETE FROM {hosting_task_arguments} WHERE vid = %d', $node->vid);
}
/**
* Implementation of hook_delete().
*/
function hosting_task_delete($node) {
db_query('DELETE FROM {hosting_task} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {hosting_task_arguments} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {hosting_task_queue} WHERE nid = %d', $node->nid);
}
/**
* Implementation of hook_load().
*/
function hosting_task_load($node) {
$additions = db_fetch_object(db_query('SELECT task_type, executed, task_status, rid, q.status as queued FROM {hosting_task} t LEFT JOIN {hosting_task_queue} q ON t.nid=q.nid WHERE vid = %d', $node->vid));
$result = db_query("SELECT name, value FROM {hosting_task_arguments} WHERE vid=%d", $node->vid);
if (db_num_rows($result)) {
while ($arg = db_fetch_object($result)) {
$additions->task_args[$arg->name] = $arg->value;
}
}
return $additions;
}
function hosting_task_retry_form($nid) {
$form['#prefix'] = '<div class="hosting-task-retry">';
$form['task'] = array(
'#type' => 'hidden',
'#default_value' => $nid,
);
$form['retry'] = array(
'#type' => 'submit',
'#value' => t('Retry failed task'),
);
$form['#suffix'] = '</div>';
return $form;
}
function hosting_task_retry_form_submit($form_id, $edit) {
hosting_task_retry($edit['task']);
}
/**
* Implementation of hook_view().
*/
function hosting_task_view($node, $teaser = FALSE, $page = FALSE) {
drupal_add_js(drupal_get_path('module', 'hosting') . '/hosting.js');
$node = node_prepare($node, $teaser);
$ref = node_load($node->rid);
hosting_set_breadcrumb($node);
$node->content['info']['#prefix'] = '<div id="hosting-task-info" class="clear-block">';
$node->content['info']['reference'] = array(
'#type' => 'item',
'#title' => drupal_ucfirst($ref->type),
'#value' => _hosting_node_link($node->rid),
);
if (!$node->queued) {
$node->content['info']['executed'] = array(
'#type' => 'item',
'#title' => t('Executed'),
'#value' => format_date($node->executed),
);
}
else {
$queues = hosting_get_queues();
$queue = $queues['tasks'];
$freq = $queue['frequency'];
$last = $queue['last_run'];
$now = time();
# the first part is the regular case: the task was never run, compute the next time
# the second part is the case where the task wasn't run in the last queue run even though it was scheduled, so we compute the next iteration
$next = max($last + $freq, $now - ($now - $last) % $freq + $freq);
if ($freq < 60 * 60 * 12) {
# 12h
# display only the time if we have short iterations
$date = format_date($next, 'custom', 'H:i:sO');
}
else {
$date = format_date($next, 'medium');
}
$items = $queue['items'];
$node->content['info']['notexecuted'] = array(
'#type' => 'item',
'#title' => t('This task has not been processed yet'),
'#value' => t('It will be processed around %date, if the queue is not too crowded. The queue is currently ran every %freq, was last ran %last and processes %items items at a time. Server time is %time.', array(
'%freq' => format_interval($freq),
'%date' => $date,
'%last' => hosting_format_interval($last),
'%items' => $items,
'%time' => format_date($now, 'custom', 'H:i:sO'),
)),
);
}
if ($node->task_status) {
$node->content['info']['status'] = array(
'#type' => 'item',
'#title' => t('Status'),
'#value' => _hosting_parse_error_code($node->task_status),
);
}
$node->content['info']['#suffix'] = '</div>';
if (user_access('retry failed tasks') && !$node->queued && $node->task_status == HOSTING_TASK_ERROR) {
$node->content['retry'] = array(
'#type' => 'markup',
'#value' => drupal_get_form('hosting_task_retry_form', $node->nid),
'#weight' => 5,
);
}
if (user_access('access task logs')) {
if ($table = _hosting_task_log_table($node->vid)) {
$node->content['hosting_log'] = array(
'#weight' => 10,
'#type' => 'item',
'#value' => $table,
);
}
}
return $node;
}
/**
* Display table containing the logged information for this task
*/
function _hosting_task_log_table($vid) {
$result = db_query("SELECT * FROM {hosting_task_log} WHERE vid=%d", $vid);
if (db_num_rows($result)) {
$header = array(
'data' => 'Log message',
);
while ($entry = db_fetch_object($result)) {
if (strlen($entry->message) > 300) {
$summary = "<span class='hosting-task-summary'>" . filter_xss(substr($entry->message, 0, 75), array()) . "... <a href='javascript:void(0)' class='hosting-summary-expand'>(" . t('Expand') . ')</a></span>';
$message = $summary . "<span class='hosting-task-full'>" . filter_xss($entry->message) . '</span>';
}
else {
$message = $entry->message;
}
$row = array(
array(
'data' => $message,
'class' => 'hosting-status',
),
);
$rows[] = array(
'data' => $row,
'class' => _hosting_task_log_class($entry->type),
);
}
return theme("table", $header, (array) $rows, array(
'id' => 'hosting-task-log',
'class' => 'hosting-table',
));
}
return false;
}
/**
* Map entry statuses to coincide.
*
* @todo make this irrelevant.
*/
function _hosting_task_log_class($type) {
switch (strtolower($type)) {
case "warning":
$type = "warning";
break;
case "error":
$type = "error";
break;
case "queue":
$type = "queue";
break;
case "command":
case "notice":
$type = "info";
break;
default:
$type = 'success';
break;
}
return 'hosting-' . $type;
}
/**
* Retrieve the latest task related to the specified platform, of a specific type
*
* This is used for documenting issues with verification.
*/
function hosting_get_most_recent_task($rid, $type) {
$nid = db_result(db_query("SELECT nid FROM {hosting_task} t WHERE task_type='%s' and t.rid=%d ORDER BY t.executed DESC limit 1", $type, $rid));
if ($nid) {
return node_load($nid);
}
return false;
}
/**
* Retrieve tasks with specified criterias
*/
function hosting_get_tasks($filter_by = null, $filter_value = null, $count = 5, $element = 0) {
$node = array();
$args[] = 'task';
$cond = '';
if ($filter_by && $filter_value) {
$cond = ' AND t.' . $filter_by . ' = %d';
$args[] = $filter_value;
}
$result = pager_query(db_rewrite_sql("SELECT *, t.task_status,q.status as queued FROM {node} n left join {hosting_task} t on n.vid=t.vid LEFT JOIN {hosting_task_queue} q ON t.nid=q.nid WHERE type='%s'" . $cond . " ORDER BY n.nid DESC"), $count, $element, NULL, $args);
while ($row = db_fetch_object($result)) {
$nodes[] = $row;
}
return $nodes;
}
/**
* @name Error status definitions
* @{
* Bitmask values used to generate the error code to return.
* @see drush_set_error(), drush_get_error(), drush_cmp_error()
*/
/** The task is queued **/
define('HOSTING_TASK_QUEUED', 0);
/** The command completed succesfully. */
define('HOSTING_TASK_SUCCESS', 1);
/** The command was not successfully completed. This is the default error status. */
define('HOSTING_TASK_ERROR', 2);
/**
* @} End of "name Error status defintions".
*/
/**
* Turn bitmask integer error code into associative array
*/
function _hosting_parse_error_code($code) {
$messages = array(
HOSTING_TASK_SUCCESS => t('Succesful'),
HOSTING_TASK_QUEUED => t('Queued'),
HOSTING_TASK_ERROR => t('Failed'),
);
return $messages[$code];
}
function hosting_task_status($filter_by, $filter_value, $type = 'install') {
$args[] = 'task';
$args[] = $type;
$cond = '';
if ($filter_by && $filter_value) {
$cond = ' AND t.' . $filter_by . ' = %d';
$args[] = $filter_value;
}
$result = db_fetch_array(db_query("SELECT q.status AS status FROM {node} n left join {hosting_task_queue} q on n.nid=q.nid left join {hosting_task} t on n.vid=t.vid WHERE n.type='%s' AND t.task_type='%s' " . $cond, $args));
return $result['status'];
}
function hosting_task_status_output($filter_by, $filter_value, $type = 'install') {
$status = hosting_task_status($filter_by, $filter_value, $type);
if (is_int($status)) {
return _hosting_parse_error_code($status);
}
else {
return $status;
# should be NULL
}
}
/**
* Display list of tasks
*/
function hosting_task_list($filter_by = null, $filter_value = null) {
return _hosting_task_list($filter_by, $filter_value, 25, 12, 'title');
}
function hosting_task_summary($filter_by = null, $filter_value = null) {
return _hosting_task_list($filter_by, $filter_value, 5, 11, 'title', array(
'created',
'actions',
), l(t('More tasks'), 'hosting/queues/tasks'));
}
function hosting_task_list_embedded($filter_by = null, $filter_value = null) {
return _hosting_task_list($filter_by, $filter_value, 5, 10, 'task_type', array(
'created',
));
}
/**
* Theme functions
*/
function _hosting_task_list($filter_by, $filter_value, $count = 5, $element = 0, $field = 'title', $skip = array(), $pager = TRUE) {
$nodes = hosting_get_tasks($filter_by, $filter_value, $count, $element);
if (!$nodes) {
return t('No tasks available');
}
else {
$headers[t('Task')] = '';
foreach ($nodes as $node) {
$row = array();
$row['type'] = array(
'data' => l(drupal_ucfirst($node->{$field}), 'node/' . $node->nid),
'class' => 'hosting-status',
);
if (!in_array('created', $skip)) {
$row['created'] = t("@interval ago", array(
'@interval' => format_interval(mktime() - $node->created, 1),
));
$headers[t('Created')] = '';
}
if (!in_array('actions', $skip) && user_access('retry failed tasks')) {
$headers[t('Actions')] = '';
if ($node->task_status == HOSTING_TASK_ERROR && !$node->queued) {
$row['actions'] = array(
'data' => drupal_get_form('hosting_task_retry_form', $node->nid),
'class' => 'hosting-task-retry',
);
}
else {
$row['actions'] = '';
}
}
if ($node->task_status == HOSTING_TASK_SUCCESS) {
$class = 'hosting-success';
}
elseif ($node->task_status == HOSTING_TASK_ERROR) {
$class = 'hosting-error';
}
else {
$class = 'hosting-queue';
}
$rows[] = array(
'data' => $row,
'class' => $class,
);
}
$output = theme('table', array_keys($headers), $rows, array(
'class' => 'hosting-table',
));
if ($pager === TRUE) {
$output .= theme('pager', NULL, $count, $element);
}
elseif (is_string($pager)) {
$output .= $pager;
}
return $output;
}
}
Functions
Constants
Name | Description |
---|---|
HOSTING_TASK_ERROR | The command was not successfully completed. This is the default error status. |
HOSTING_TASK_QUEUED | The task is queued * |
HOSTING_TASK_SUCCESS | The command completed succesfully. |