View source
<?php
function hosting_task_init() {
$settings['hostingTaskRefresh'] = array(
'refreshTimeout' => variable_get('hosting_task_refresh_timeout', 30000),
);
drupal_add_js($settings, 'setting');
}
function hosting_task_menu() {
$items = array();
$tasks = hosting_available_tasks();
foreach ($tasks as $type => $type_tasks) {
if (empty($type_tasks)) {
continue;
}
foreach ($type_tasks as $task => $info) {
if (empty($info['hidden'])) {
$path = sprintf("hosting_confirm/%%hosting_%s_wildcard/%s_%s", $type, $type, $task);
$items[$path] = array(
'title' => $info['title'],
'description' => $info['description'],
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hosting_task_confirm_form',
1,
$task,
),
'access callback' => 'hosting_task_menu_access_csrf',
'access arguments' => array(
1,
$task,
),
'type' => MENU_CALLBACK,
);
$items[$path] = array_merge($items[$path], $info);
}
}
}
$items['hosting/tasks/%node/list'] = array(
'title' => 'Task list',
'description' => 'AJAX callback for refreshing task list',
'page callback' => 'hosting_task_ajax_list',
'page arguments' => array(
2,
),
'access callback' => 'node_access',
'access arguments' => array(
'view',
2,
),
'type' => MENU_CALLBACK,
);
$items['hosting/tasks/%node/cancel'] = array(
'title' => 'Task list',
'description' => 'Callback for stopping tasks',
'page callback' => 'hosting_task_cancel',
'page arguments' => array(
2,
),
'access callback' => 'hosting_task_cancel_access',
'access arguments' => array(
2,
),
'type' => MENU_CALLBACK,
);
$items['hosting/tasks/queue'] = array(
'title' => 'Task list',
'description' => 'AJAX callback for refreshing task queue',
'page callback' => 'hosting_task_ajax_queue',
'access arguments' => array(
'access task logs',
),
'type' => MENU_CALLBACK,
);
$items['hosting/task/%node'] = array(
'page callback' => 'node_page_view',
'page arguments' => array(
2,
),
'access arguments' => array(
'access task logs',
),
);
$items['hosting/task/log/ajax/%node/%/%'] = array(
'page callback' => 'hosting_task_log_ajax',
'page arguments' => array(
4,
5,
6,
),
'access arguments' => array(
'access task logs',
),
'delivery callback' => 'ajax_deliver',
);
return $items;
}
function hosting_task_log_ajax($node, $last_position, $id) {
$commands = array();
$expire = time() + 10;
$table = FALSE;
while ((empty($table) || !count($table['#rows'])) && time() < $expire) {
usleep(200000);
$table = _hosting_task_log_table($node, $last_position);
}
if (isset($table['#refresh_url'])) {
$url = $table['#refresh_url'];
}
else {
$url = url('hosting/task/log/ajax/' . $node->nid . '/' . $last_position);
}
if (!empty($table)) {
unset($table['#header']);
$commands[] = hosting_task_ajax_command_hosting_table_append('#' . $id, drupal_render($table));
}
if (!hosting_task_task_has_finished($node)) {
$commands[] = hosting_task_ajax_command_hosting_table_check('#' . $id, $url);
}
return array(
'#type' => 'ajax',
'#commands' => $commands,
);
}
function hosting_task_task_has_finished($task) {
return !empty($task->delta) && $task->executed + $task->delta + 30 < REQUEST_TIME;
}
function hosting_task_ajax_command_hosting_table_append($selector, $html, $settings = NULL) {
return array(
'command' => 'hosting_table_append',
'selector' => $selector,
'data' => $html,
'settings' => $settings,
);
}
function hosting_task_ajax_command_hosting_table_check($selector, $url, $settings = NULL) {
return array(
'command' => 'hosting_table_check',
'selector' => $selector,
'url' => $url,
'settings' => $settings,
);
}
function hosting_task_ajax_list($node) {
$return['markup'] = hosting_task_table($node);
$return['changed'] = $node->changed;
$return['navigate_url'] = url('node/' . $node->nid);
drupal_json_output($return);
exit;
}
function hosting_task_ajax_queue() {
$view = views_get_view('hosting_task_list');
$view
->set_display('block');
$view
->pre_execute();
$return['markup'] = $view
->render('block');
drupal_json_output($return);
exit;
}
function hosting_task_cancel($node) {
if ($node->type == 'task') {
if ($node->task_status == HOSTING_TASK_QUEUED || $node->task_status == HOSTING_TASK_PROCESSING) {
$node->task_status = HOSTING_TASK_WARNING;
node_save($node);
if (module_exists('hosting_queued')) {
global $locks;
unset($locks['hosting_queue_tasks_running']);
db_delete('semaphore')
->condition('name', 'hosting_queue_tasks_running')
->execute();
}
hosting_task_log($node->vid, 'warning', t("Task was cancelled."));
drupal_goto('node/' . $node->rid);
}
else {
drupal_set_message(t('This task is not running or queued. It cannot be cancelled.'), 'warning');
drupal_goto('node/' . $node->nid);
}
}
}
function hosting_task_cancel_access($node) {
global $user;
if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $user->uid)) {
return FALSE;
}
if (user_access('administer tasks')) {
return TRUE;
}
if (user_access('cancel own tasks') && user_access('create ' . $node->task_type . ' task') && node_access('view', $node)) {
return TRUE;
}
}
function hosting_task_menu_access($node, $task) {
if (user_access("create " . $task . " task")) {
if ($node->type == 'site') {
if (hosting_task_outstanding($node->nid, 'delete') || $node->site_status == HOSTING_SITE_DELETED) {
return FALSE;
}
if ($task == 'login-reset' && $node->site_status != HOSTING_SITE_ENABLED) {
return FALSE;
}
$safe_tasks = array(
'backup',
'backup-delete',
'verify',
'enable',
'deploy',
);
if (!in_array($task, $safe_tasks)) {
if ($node->nid == hosting_get_hostmaster_site_nid()) {
return FALSE;
}
}
$site_enabled = hosting_task_outstanding($node->nid, 'enable') || $node->site_status == HOSTING_SITE_ENABLED;
$deletable = $task == "delete";
$enabable = $task == "enable";
$installable = $task == "install";
$delete_or_enable = $deletable || $enabable || $installable;
if (!$site_enabled) {
return $delete_or_enable;
}
else {
return $installable && variable_get('hosting_allow_reinstall', TRUE) || !variable_get('hosting_require_disable_before_delete', TRUE) && $deletable || !$delete_or_enable;
}
}
if ($node->type == 'platform') {
if (!node_access('update', $node, $GLOBALS['user'])) {
return FALSE;
}
if (hosting_task_outstanding($node->nid, 'delete') || $node->platform_status == HOSTING_PLATFORM_DELETED) {
return FALSE;
}
if ($node->platform_status == HOSTING_PLATFORM_LOCKED) {
$platform_tasks = array(
'verify',
'unlock',
'delete',
'migrate',
);
return in_array($task, $platform_tasks);
}
else {
$platform_tasks = array(
'verify',
'lock',
'delete',
'migrate',
);
}
return in_array($task, $platform_tasks);
}
if ($node->type === 'server') {
if (!node_access('update', $node, $GLOBALS['user'])) {
return FALSE;
}
return TRUE;
}
}
return FALSE;
}
function hosting_task_menu_access_csrf($node, $task) {
global $user;
$interactive_tasks = array(
'migrate',
'clone',
);
if (!in_array($task, $interactive_tasks) && (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $user->uid))) {
return FALSE;
}
return hosting_task_menu_access($node, $task);
}
function hosting_task_node_info() {
$types["task"] = array(
"type" => 'task',
"name" => t('Task'),
'base' => '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_node_access($node, $op, $account) {
if (hosting_feature('client')) {
return NODE_ACCESS_IGNORE;
}
$type = is_string($node) ? $node : $node->type;
if ($type != 'task') {
return NODE_ACCESS_IGNORE;
}
if (user_access('administer tasks', $account)) {
return NODE_ACCESS_ALLOW;
}
}
function hosting_task_permission() {
return array(
'administer tasks' => array(
'title' => t('administer tasks'),
),
'create backup task' => array(
'title' => t('create backup task'),
),
'create restore task' => array(
'title' => t('create restore task'),
),
'create deploy task' => array(
'title' => t('create deploy task'),
),
'create disable task' => array(
'title' => t('create disable task'),
),
'create enable task' => array(
'title' => t('create enable task'),
),
'create delete task' => array(
'title' => t('create delete task'),
),
'create verify task' => array(
'title' => t('create verify task'),
),
'create lock task' => array(
'title' => t('create lock task'),
),
'create unlock task' => array(
'title' => t('create unlock task'),
),
'create login-reset task' => array(
'title' => t('create login-reset task'),
),
'create backup-delete task' => array(
'title' => t('create backup-delete task'),
),
'view own tasks' => array(
'title' => t('view own tasks'),
),
'view task' => array(
'title' => t('view task'),
),
'access task logs' => array(
'title' => t('access task logs'),
),
'retry failed tasks' => array(
'title' => t('retry failed tasks'),
),
'cancel own tasks' => array(
'title' => t('cancel own tasks'),
),
'update status of tasks' => array(
'title' => t('update status of tasks'),
),
);
}
function hosting_task_hosting_queues() {
$queue['tasks'] = array(
'name' => t('Task queue'),
'description' => t('Process the queue of outstanding hosting tasks.'),
'type' => 'serial',
'frequency' => strtotime("1 minute", 0),
'items' => 5,
'total_items' => hosting_task_count(),
'singular' => t('task'),
'plural' => t('tasks'),
'running_items' => hosting_task_count_running(),
);
return $queue;
}
function hosting_task_log($vid, $type, $message, $error = '', $timestamp = NULL) {
static $nids = array();
$timestamp = $timestamp ? $timestamp : REQUEST_TIME;
if (!isset($nids[$vid])) {
$nids[$vid] = (int) db_query('SELECT nid FROM {hosting_task} WHERE vid = :vid', array(
':vid' => $vid,
))
->fetchField();
}
$id = db_insert('hosting_task_log')
->fields(array(
'vid' => $vid,
'nid' => $nids[$vid],
'type' => $type,
'message' => $message,
'error' => isset($error) ? $error : '',
'timestamp' => $timestamp,
))
->execute();
}
function hosting_task_retry($task_id) {
$node = node_load($task_id);
if ($node->task_status != HOSTING_TASK_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->nid = NULL;
$node->vid = NULL;
$node->original = NULL;
$node->revision = TRUE;
$node->created = REQUEST_TIME;
$node->changed = REQUEST_TIME;
$node->task_status = HOSTING_TASK_QUEUED;
node_save($node);
drupal_goto("node/{$node->nid}");
}
}
function hosting_add_task($nid, $type, $args = NULL, $status = HOSTING_TASK_QUEUED) {
global $user;
if (!hosting_task_dangerous_task_is_allowed($type, $nid)) {
return FALSE;
}
$node = db_query("SELECT nid, uid, title FROM {node} WHERE nid = :nid", array(
':nid' => $nid,
))
->fetchObject();
$task = new stdClass();
$task->language = LANGUAGE_NONE;
$task->type = 'task';
$task->title = t("!type !title", array(
'!type' => $type,
'!title' => $node->title,
));
$task->task_type = $type;
$task->rid = $node->nid;
$task->uid = $user->uid ? $user->uid : $node->uid;
$task->status = 1;
$task->task_status = $status;
if ($status == HOSTING_TASK_QUEUED) {
$task->revision = TRUE;
}
if (is_array($args)) {
$task->task_args = $args;
}
node_save($task);
return $task;
}
function hosting_task_confirm_form($form, $form_state, $node, $task) {
drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
$tasks = hosting_available_tasks($node->type);
if (!isset($tasks[$task]['dialog']) || !$tasks[$task]['dialog']) {
hosting_add_task($node->nid, $task);
if ($task == 'delete') {
drupal_set_message(t(':title has been queued for deletion.', array(
':title' => $node->title,
)));
drupal_goto();
}
drupal_goto('node/' . $node->nid);
}
$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_' . str_replace('-', '_', $task) . '_form';
if (function_exists($func)) {
$form['parameters'] += $func($node);
}
$func = $func . '_validate';
if (function_exists($func)) {
$form['#validate'][] = $func;
$form['#func_param_1'] = $node;
$form['#func_param_2'] = $task;
}
$question = t("Are you sure you want to @task @object?", array(
'@task' => $task,
'@object' => $node->title,
));
$path = !empty($_REQUEST['destination']) ? $_REQUEST['destination'] : 'node/' . $node->nid;
$form = confirm_form($form, $question, $path, '', $tasks[$task]['title']);
$form['actions']['#prefix'] = '<div id="hosting-task-confirm-form-actions" class="container-inline">';
$form['actions']['#suffix'] = '</div>';
return $form;
}
function hosting_task_restore_form($node) {
$list = hosting_site_backup_list($node->nid);
if (count($list)) {
$form['bid'] = array(
'#type' => 'radios',
'#title' => t('Backups'),
'#options' => $list,
'#required' => TRUE,
);
}
else {
$form['no_backups'] = array(
'#type' => 'item',
'#title' => t('Backups'),
'#markup' => t('There are no valid backups available.'),
);
}
return $form;
}
function hosting_task_restore_form_validate($form, &$form_state) {
if (isset($form['parameters']['no_backups'])) {
form_set_error('no_backups', t('There are no valid backups available.'));
}
}
function hosting_task_confirm_form_submit($form, &$form_state) {
$values = $form_state['values'];
$parameters = isset($values['parameters']) ? $values['parameters'] : array();
hosting_add_task($values['nid'], $values['task'], $parameters);
$form_state['redirect'] = 'node/' . $values['nid'];
if (module_exists('overlay')) {
overlay_close_dialog();
}
}
function hosting_task_set_title(&$node) {
$ref = node_load($node->rid);
$tasks = hosting_available_tasks($ref->type);
$ref_title = $ref->type == 'site' ? hosting_site_canonical_url($ref) : $ref->title;
$node->title = drupal_ucfirst($tasks[$node->task_type]['title']) . ': ' . $ref_title;
db_update('node')
->fields(array(
'title' => $node->title,
))
->condition('nid', $node->nid)
->execute();
db_update('node_revision')
->fields(array(
'title' => $node->title,
))
->condition('vid', $node->vid)
->execute();
}
function hosting_tasks_queue($count = 20) {
global $provision_errors;
$tasks = hosting_get_new_tasks($count);
if (count($tasks)) {
drush_log(dt("Found @count tasks (max @max) in queue. Running...", array(
'@count' => count($tasks),
'@max' => $count,
)), "ok");
}
else {
drush_log(dt("Found no tasks in queue. Not running."), "ok");
}
foreach ($tasks as $task) {
hosting_task_execute($task, array(
'fork' => drush_get_option('fork', TRUE),
));
}
}
function hosting_task_execute($task, $backend_options = array()) {
$t = array(
'@title' => $task->title,
'@nid' => $task->nid,
'@url' => url("node/{$task->nid}", array(
'absolute' => true,
)),
);
watchdog('hosting_task', 'Starting task "@title": @url', $t, WATCHDOG_NOTICE, url("node/{$task->nid}"));
drush_log(dt('Starting task "@title": @url', $t), 'ok');
drush_invoke_process('@self', "hosting-task", array(
$task->nid,
), array(
'strict' => FALSE,
), $backend_options);
if (isset($backend_options['fork']) && $backend_options['fork']) {
drush_log(dt('Launched task "@title" in a forked process: @url', $t), 'ok');
}
else {
$task = node_load($task->nid, NULL, TRUE);
$t['@status'] = _hosting_parse_error_code($task->task_status);
$t['@duration'] = format_interval($task->delta, 1);
drush_log(dt('Finished task "@title" with status "@status" in @duration: @url', $t), 'ok');
}
}
function hosting_task_outstanding($nid, $type) {
$return = db_query("\n SELECT t.nid FROM {hosting_task} t\n INNER JOIN {node} n ON t.vid = n.vid\n WHERE\n t.rid = :rid\n AND (t.task_status = :status_queued OR t.task_status = :status_processing)\n AND t.task_type = :type\n ORDER BY t.vid DESC\n LIMIT 1", array(
':rid' => $nid,
':status_queued' => HOSTING_TASK_QUEUED,
':status_processing' => HOSTING_TASK_PROCESSING,
':type' => $type,
))
->fetchField();
return $return;
}
function hosting_task_count() {
$result = db_query("SELECT COUNT(t.vid) FROM {hosting_task} t INNER JOIN {node} n ON t.vid = n.vid WHERE t.task_status = :task_status", array(
':task_status' => HOSTING_TASK_QUEUED,
))
->fetchField();
return $result;
}
function hosting_task_count_running() {
return db_query("SELECT COUNT(t.nid) FROM {node} n INNER JOIN {hosting_task} t ON n.vid = t.vid WHERE type = :type AND t.task_status = :task_status AND t.executed > (UNIX_TIMESTAMP() - :timeout)", array(
':type' => 'task',
':task_status' => HOSTING_TASK_PROCESSING,
':timeout' => 28800,
))
->fetchField();
}
function hosting_available_tasks($type = NULL, $reset = FALSE) {
static $cache = array();
if (!count($cache) || $reset) {
$cache = module_invoke_all('hosting_tasks');
drupal_alter('hosting_tasks', $cache);
}
if (isset($type)) {
return $cache[$type];
}
else {
return $cache;
}
}
function hosting_task_hosting_tasks_alter(&$cache) {
foreach ($cache as &$tasks) {
foreach ($tasks as $type => &$task_type) {
if (!isset($task_type['command']) || empty($task_type['command'])) {
$task_type['command'] = 'provision-' . $type;
}
}
}
}
function hosting_task_action_info() {
$available_tasks = hosting_available_tasks();
$actions = array();
foreach ($available_tasks as $module => $tasks) {
foreach ($tasks as $task => $task_info) {
$task = str_replace('-', '_', $task);
$function = 'hosting_' . $module . '_' . $task . '_action';
if (function_exists($function)) {
$label = ucwords($module . ': ' . $task);
$actions[$function] = array(
'type' => 'node',
'label' => t($label),
'configurable' => FALSE,
'behavior' => array(
'none',
),
);
}
}
}
return $actions;
}
function hosting_task_insert($node) {
$node->executed = isset($node->executed) ? $node->executed : NULL;
$node->delta = isset($node->delta) ? $node->delta : NULL;
$id = db_insert('hosting_task')
->fields(array(
'vid' => $node->vid,
'nid' => $node->nid,
'task_type' => $node->task_type,
'task_status' => $node->task_status,
'rid' => $node->rid,
'executed' => $node->executed,
'delta' => $node->delta,
))
->execute();
if ($node->task_type == 'delete' && !empty($node->ref_context)) {
$node->task_args['hosting_context'] = $node->ref_context;
}
if (isset($node->task_args) && is_array($node->task_args)) {
foreach ($node->task_args as $key => $value) {
$id = db_insert('hosting_task_arguments')
->fields(array(
'vid' => $node->vid,
'nid' => $node->nid,
'name' => $key,
'value' => $value,
))
->execute();
}
}
if ($node->task_type == 'delete' && !empty($node->ref_context)) {
hosting_context_delete($node->rid);
$path = array(
'source' => "node/{$node->rid}",
'alias' => "hosting/c/{$node->ref_context}/{$node->rid}",
);
path_save($path);
}
module_invoke_all('hosting_task_update_status', $node, $node->task_status);
hosting_task_set_title($node);
}
function hosting_task_update($node) {
if (!empty($node->revision)) {
hosting_task_insert($node);
}
else {
hosting_task_set_title($node);
db_update('hosting_task')
->fields(array(
'nid' => $node->nid,
'task_type' => $node->task_type,
'task_status' => $node->task_status,
'rid' => $node->rid,
'executed' => $node->executed,
'delta' => $node->delta,
))
->condition('vid', $node->vid)
->execute();
if (isset($node->task_args) && is_array($node->task_args)) {
db_delete('hosting_task_arguments')
->condition('vid', $node->vid)
->execute();
foreach ($node->task_args as $key => $value) {
$id = db_insert('hosting_task_arguments')
->fields(array(
'vid' => $node->vid,
'nid' => $node->nid,
'name' => $key,
'value' => $value,
))
->execute();
}
}
module_invoke_all('hosting_task_update_status', $node, $node->task_status);
}
}
function hosting_nodeapi_task_delete_revision(&$node) {
db_delete('hosting_task')
->condition('vid', $node->vid)
->execute();
db_delete('hosting_task_arguments')
->condition('vid', $node->vid)
->execute();
db_delete('hosting_task_log')
->condition('vid', $node->vid)
->execute();
}
function hosting_task_delete($node) {
db_delete('hosting_task')
->condition('nid', $node->nid)
->execute();
db_delete('hosting_task_arguments')
->condition('nid', $node->nid)
->execute();
db_delete('hosting_task_log')
->condition('nid', $node->nid)
->execute();
}
function hosting_task_delete_related_tasks($nid) {
$result = db_query("SELECT distinct nid FROM {hosting_task} WHERE rid = :rid", array(
':rid' => $nid,
));
foreach ($result as $node) {
node_delete($node->nid);
}
}
function hosting_task_load($nodes) {
$types = module_invoke_all('hosting_tasks');
drupal_alter('hosting_tasks', $types);
foreach ($nodes as $nid => &$node) {
$additions = db_query('SELECT task_type, executed, delta, rid, task_status, r.type as ref_type FROM {hosting_task} t LEFT JOIN {node} r ON t.rid = r.nid WHERE t.vid = :vid', array(
':vid' => $node->vid,
))
->fetch();
$result = db_query("SELECT name, value FROM {hosting_task_arguments} WHERE vid = :vid", array(
':vid' => $node->vid,
));
if ($result) {
$additions->task_args = array();
while ($arg = $result
->fetch()) {
$additions->task_args[$arg->name] = $arg->value;
}
}
foreach ($additions as $property => &$value) {
$node->{$property} = is_numeric($value) ? (int) $value : $value;
}
$node->task_command = $types[$node->ref_type][$node->task_type]['command'];
}
}
function hosting_task_retry_form($form, $form_state, $nid) {
$form['#prefix'] = '<div class="hosting-task-retry">';
$form['task'] = array(
'#type' => 'hidden',
'#default_value' => $nid,
);
$task = node_load($nid);
$button = t('Retry');
if ($task->task_type == 'install' && $task->task_status != HOSTING_TASK_ERROR) {
$button = t('Reinstall');
}
elseif ($task->task_status == HOSTING_TASK_SUCCESS || $task->task_status == HOSTING_TASK_WARNING) {
$button = t('Run Again');
}
$form['retry'] = array(
'#type' => 'submit',
'#value' => $button,
);
$form['#suffix'] = '</div>';
return $form;
}
function hosting_task_retry_form_submit($form, &$form_state) {
hosting_task_retry($form_state['values']['task']);
if (module_exists('overlay')) {
overlay_close_dialog();
}
}
function hosting_task_update_status_form($form, &$form_state, $vid) {
$form['#prefix'] = '<div class="hosting-task-retry">';
$form['task'] = array(
'#type' => 'hidden',
'#default_value' => $vid,
);
$form['update-status'] = array(
'#type' => 'submit',
'#value' => t('Update status'),
);
$form['#suffix'] = '</div>';
return $form;
}
function hosting_task_update_status_form_submit($form, &$form_state) {
hosting_task_update_status($form_state['values']['task']);
if (module_exists('overlay')) {
overlay_close_dialog();
}
if (module_exists('hosting_queued')) {
global $locks;
unset($locks['hosting_queue_tasks_running']);
db_delete('semaphore')
->condition('name', 'hosting_queue_tasks_running')
->execute();
}
}
function hosting_task_view($node, $view_mode, $langcode = NULL) {
drupal_add_js(drupal_get_path('module', 'hosting') . '/hosting.js');
$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),
'#markup' => _hosting_node_link($node->rid),
);
if ($node->task_status != HOSTING_TASK_QUEUED) {
if ($node->task_status == HOSTING_TASK_PROCESSING) {
$node->content['info']['started'] = array(
'#type' => 'item',
'#title' => t('Started'),
'#markup' => format_date($node->executed),
'#weight' => 1,
);
$node->content['info']['delta'] = array(
'#type' => 'item',
'#title' => t('Processing time'),
'#markup' => format_interval(REQUEST_TIME - $node->executed),
'#weight' => 2,
);
}
elseif ($node->executed != NULL) {
$node->content['info']['executed'] = array(
'#type' => 'item',
'#title' => t('Executed'),
'#markup' => format_date($node->executed),
'#weight' => 1,
);
$node->content['info']['delta'] = array(
'#type' => 'item',
'#title' => t('Execution time'),
'#markup' => format_interval($node->delta),
'#weight' => 2,
);
}
}
else {
$queues = hosting_get_queues();
$queue = $queues['tasks'];
$next = _hosting_queue_next_run($queue);
$node->content['info']['notexecuted'] = array(
'#type' => 'item',
'#title' => t('This task has not been processed yet'),
'#markup' => t('It will be processed around %date, if the queue is not too crowded. The queue is currently run every %freq, was last run %last and processes %items items at a time. Server time is %time.', array(
'%freq' => format_interval($queue['frequency']),
'%date' => format_date($next, 'custom', 'H:i:sO'),
'%last' => hosting_format_interval($queue['last_run']),
'%items' => $queue['items'],
'%time' => format_date(REQUEST_TIME, 'custom', 'H:i:sO'),
)),
);
}
if ($node->task_status) {
$node->content['info']['status'] = array(
'#type' => 'item',
'#title' => t('Status'),
'#markup' => _hosting_parse_error_code($node->task_status),
);
}
$node->content['info']['#suffix'] = '</div>';
if (user_access('retry failed tasks') && ($node->task_status == HOSTING_TASK_ERROR || $node->task_status == HOSTING_TASK_WARNING || $node->task_status == HOSTING_TASK_SUCCESS)) {
$node->content['retry'] = array(
'form' => drupal_get_form('hosting_task_retry_form', $node->nid),
'#weight' => 5,
);
if ($node->task_status == HOSTING_TASK_WARNING || $node->task_status == HOSTING_TASK_SUCCESS) {
$node->content['retry']['form']['retry']['#value'] = t('Run Again');
}
}
if (user_access('update status of tasks') && $node->task_status == HOSTING_TASK_PROCESSING) {
$node->content['update-status'] = array(
'form' => drupal_get_form('hosting_task_update_status_form', $node->vid),
'#weight' => 5,
);
}
if (user_access('access task logs')) {
if (in_array($node->task_status, array(
HOSTING_TASK_ERROR,
HOSTING_TASK_WARNING,
))) {
$url_options = array(
'attributes' => array(
'class' => array(
'hosting-button-enabled',
),
'target' => '_self',
),
'fragment' => 'warning',
);
if (module_exists('overlay') && overlay_get_mode() == 'child') {
$url_options['query'] = array(
'render' => 'overlay',
);
}
$node->content['jump-link-warning'] = array(
'#markup' => '<div>' . l(t('Jump to first warning'), request_path(), $url_options) . '</div>',
'#weight' => 8,
);
}
if ($node->task_status == HOSTING_TASK_ERROR) {
$url_options['fragment'] = 'error';
$node->content['jump-link-error'] = array(
'#markup' => '<div>' . l(t('Jump to first error'), request_path(), $url_options) . '</div>',
'#weight' => 9,
);
}
if ($table = _hosting_task_log_table($node)) {
$node->content['hosting_log'] = array(
'#weight' => 10,
'table' => $table,
);
}
}
return $node;
}
function _hosting_task_log_table($node, $last_position = 0) {
$types = variable_get('hosting_task_logs_types_display', hosting_log_types());
$query_args = array(
':vid' => $node->vid,
':lid' => $last_position,
':types' => $types,
);
$result = db_query("SELECT * FROM {hosting_task_log} WHERE vid = :vid AND lid > :lid AND type IN (:types) ORDER BY lid", $query_args);
if ($result) {
$headers = array(
'data' => 'Log message',
'execution_time' => 'Execution time',
'',
);
$rows = array();
$last_lid = $last_position;
$last_timestamp = 0;
$exec_time = '';
$row_count = -1;
foreach ($result as $entry) {
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 modalframe-exclude'>(" . t('Expand') . ')</a></span>';
$message = $summary . "<span class='hosting-task-full'>" . filter_xss($entry->message) . '</span>';
}
else {
$message = filter_xss($entry->message);
}
if ($entry->type == 'error') {
$message = '<a name="error"></a>' . $message;
}
elseif ($entry->type == 'warning') {
$message = '<a name="warning"></a>' . $message;
}
$exec_time = $entry->timestamp - $last_timestamp;
if ($exec_time < 1) {
$exec_time = '<div>-</div>';
}
elseif ($exec_time == 1) {
$exec_time = '<div title="Many tasks take less than 1 second to perform. This duration represents an aggregate of the preceding tasks\' duration."><strong>' . $exec_time . ' s.</strong></div>';
}
else {
$exec_time = '<div><strong>' . $exec_time . ' s.</strong></div>';
}
if ($row_count > -1) {
$rows[$row_count]['data'][1] = array(
'data' => $exec_time,
);
}
$row_count++;
$last_timestamp = $entry->timestamp;
$row = array(
array(
'data' => $message,
'class' => array(
'hosting-status',
),
),
'',
$entry->type,
);
$rows[] = array(
'data' => $row,
'class' => array(
_hosting_task_log_class($entry->type),
),
);
$last_lid = $entry->lid;
}
$table = array(
'#theme' => "table",
'#header' => $headers,
'#rows' => $rows,
'#attributes' => array(
'id' => 'hosting-task-log',
'class' => array(
'hosting-table',
),
),
);
if (!hosting_task_task_has_finished($node)) {
$table += array(
'#refresh_url' => url('hosting/task/log/ajax/' . $node->nid . '/' . $last_lid),
'#attached' => array(
'js' => array(
array(
'data' => array(
'hosting_task' => array(
'refresh_url' => url('hosting/task/log/ajax/' . $node->nid . '/' . $last_lid),
),
),
'type' => 'setting',
),
drupal_get_path('module', 'hosting_task') . '/js/hosting_task.table.js',
),
'library' => array(
array(
'system',
'drupal.ajax',
),
),
),
);
}
return $table;
}
return FALSE;
}
function _hosting_task_log_class($type) {
switch (strtolower($type)) {
case 'warning':
case 'cancel':
case 'rollback':
$type = 'warning';
break;
case 'failed':
case 'error':
$type = 'error';
break;
case 'queue':
$type = 'queue';
break;
case 'ok':
case 'completed':
case 'success':
$type = 'success';
break;
case 'notice':
case 'info':
case 'message':
default:
$type = 'info';
}
return 'hosting-' . $type;
}
function hosting_get_most_recent_task($rid, $type = null) {
$query = db_select('hosting_task', 'ht')
->fields('ht', [
'nid',
])
->condition('rid', $rid)
->orderBy('vid', 'DESC');
if ($type) {
$query
->condition('task_type', $type);
}
$nid = $query
->execute()
->fetchField();
if ($nid) {
return node_load($nid, NULL, TRUE);
}
}
function hosting_get_tasks($filter_by = NULL, $filter_value = NULL, $count = 5, $element = 0) {
$nodes = array();
$query = db_select('node', 'n')
->extend('PagerDefault');
$query
->element($element);
$query
->join('hosting_task', 't', 'n.vid=t.vid');
$query = $query
->fields('n')
->fields('t', array(
'task_status',
'task_type',
'rid',
))
->condition('type', 'task')
->orderBy('n.vid', 'DESC')
->limit($count)
->addTag('node_access');
if (isset($filter_by) && isset($filter_value)) {
$query = $query
->condition($filter_by, $filter_value);
}
$result = $query
->execute();
foreach ($result as $row) {
$nodes[] = $row;
}
return $nodes;
}
function hosting_get_new_tasks($limit = 20, $rid = NULL, $task_type = NULL) {
$return = array();
$query = db_select('hosting_task', 't');
$query
->innerJoin('node', 'n', 't.vid = n.vid');
$query
->fields('t', array(
'nid',
))
->condition('t.task_status', HOSTING_TASK_QUEUED)
->orderBy('n.changed')
->orderBy('n.nid')
->groupBy('t.rid')
->range(0, $limit)
->addTag('hosting_get_new_tasks');
if ($rid) {
$query
->condition('t.rid', $rid);
}
if ($task_type) {
$query
->condition('t.task_type', $task_type);
}
foreach ($query
->execute() as $node) {
$return[$node->nid] = node_load($node->nid);
}
return $return;
}
function _hosting_get_new_tasks($limit = 20) {
return hosting_get_new_tasks($limit);
}
define('HOSTING_TASK_PROCESSING', -1);
define('HOSTING_TASK_QUEUED', 0);
define('HOSTING_TASK_SUCCESS', 1);
define('HOSTING_TASK_ERROR', 2);
define('HOSTING_TASK_WARNING', 3);
function _hosting_task_error_codes() {
$codes = array(
HOSTING_TASK_SUCCESS => t('Successful'),
HOSTING_TASK_QUEUED => t('Queued'),
HOSTING_TASK_ERROR => t('Failed'),
HOSTING_TASK_PROCESSING => t('Processing'),
HOSTING_TASK_WARNING => t('Warning'),
);
return $codes;
}
function _hosting_parse_error_code($code) {
$messages = _hosting_task_error_codes();
return $messages[$code];
}
function hosting_task_status($filter_by, $filter_value, $type = 'install') {
$query = db_select('node', 'n');
$query
->join('hosting_task', 't', 'n.vid = t.vid');
$query
->fields('t', array(
'task_status',
))
->condition('n.type', 'task')
->condition('t.task_type', $type);
if ($filter_by && $filter_value) {
$query
->condition('t.' . $filter_by, $filter_value);
}
$query
->orderBy('t.vid', 'DESC');
return $query
->execute()
->fetchField();
}
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;
}
}
function hosting_task_list($filter_by = NULL, $filter_value = NULL) {
return _hosting_task_list($filter_by, $filter_value, 25, 12, 'title');
}
function hosting_task_table($node) {
$output = '';
$headers[] = t('Task');
$headers[] = array(
'data' => t('Actions'),
'class' => array(
'hosting-actions',
),
);
$tasklist = hosting_task_fetch_tasks($node->nid);
$rows = array();
foreach ($tasklist as $task => $info) {
$row = array();
if (!isset($info['nid']) && !$info['task_permitted']) {
continue;
}
if (empty($info['title'])) {
continue;
}
$row['type'] = array(
'data' => $info['title'],
'class' => array(
'hosting-status',
),
);
$actions = array();
if (isset($info['task_status']) && $info['task_status'] == 0) {
$actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']);
}
else {
$actions['run'] = _hosting_task_button(t('Run'), sprintf("hosting_confirm/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']);
}
$actions['log'] = _hosting_task_button(t('View'), isset($info['nid']) ? 'hosting/task/' . $info['nid'] : '<front>', t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE);
$row['actions'] = array(
'data' => implode('', $actions),
'class' => array(
'hosting-actions',
),
);
$rows[] = array(
'data' => $row,
'class' => array(
$info['class'],
),
);
}
$output .= theme('table', array(
'header' => $headers,
'rows' => $rows,
'attributes' => array(
'class' => array(
'hosting-table',
),
),
));
return $output;
}
function _hosting_task_button($title, $link, $description, $class = '', $status = TRUE, $dialog = FALSE, $add_token = TRUE) {
global $user;
if ($status) {
$classes[] = 'hosting-button-enabled';
if (!empty($class)) {
$classes[] = $class;
}
if ($dialog) {
$classes[] = 'hosting-button-dialog';
}
$options['attributes'] = array(
'title' => $description,
'class' => $classes,
);
if ($add_token) {
$options['query'] = array(
'token' => drupal_get_token($user->uid),
);
}
return l($title, $link, $options);
}
else {
return "<span class='hosting-button-disabled'>" . $title . "</span>";
}
}
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();
if ($field == 'title') {
$data = drupal_ucfirst($node->task_type) . ' ' . _hosting_node_link($node->rid);
}
else {
$data = $node->{$field};
}
$row['type'] = array(
'data' => $data,
'class' => array(
'hosting-status',
),
);
if (!in_array('created', $skip)) {
$row['created'] = t("@interval ago", array(
'@interval' => format_interval(REQUEST_TIME - $node->created, 1),
));
$headers[t('Created')] = '';
}
if ($node->task_status == 0) {
$row['executed'] = '';
}
else {
$row['executed'] = t("@interval ago", array(
'@interval' => format_interval(REQUEST_TIME - $node->changed, 1),
));
}
$headers[t('Executed')] = '';
$headers[t('Actions')] = '';
$actions['log'] = l(t('View'), 'node/' . $node->nid, array(
'attributes' => array(
'class' => array(
'hosting-button-dialog',
'hosting-button-enabled',
'hosting-button-log',
),
),
));
$row['actions'] = array(
'data' => $actions['log'],
'class' => array(
'hosting-actions',
),
);
$class = hosting_task_status_class($node->task_status);
$rows[] = array(
'data' => $row,
'class' => array(
$class,
),
);
}
$output = theme('table', array(
'header' => array_keys($headers),
'rows' => $rows,
'attributes' => array(
'class' => array(
'hosting-table',
),
),
));
if ($pager === TRUE) {
$output .= theme('pager', array(
'tags' => NULL,
'element' => $element,
));
}
elseif (is_string($pager)) {
$output .= $pager;
}
return $output;
}
}
function hosting_task_fetch_tasks($rid) {
$node = node_load($rid);
$result = db_query("SELECT n.nid, t.task_type, t.task_status FROM {node} n INNER JOIN {hosting_task} t ON n.vid = t.vid\n WHERE n.type = :ntype AND t.rid = :trid\n ORDER BY n.changed ASC", array(
':ntype' => 'task',
':trid' => $rid,
));
foreach ($result as $obj) {
$return[$obj->task_type] = array(
'nid' => $obj->nid,
'task_status' => $obj->task_status,
'exists' => TRUE,
);
}
$tasks = hosting_available_tasks($node->type);
ksort($tasks);
foreach ($tasks as $type => $hook_task) {
if (!isset($return[$type])) {
$return[$type] = array();
}
$access_callback = !empty($hook_task['access callback']) ? $hook_task['access callback'] : 'hosting_task_menu_access';
$task = array();
$task = array_merge($return[$type], $hook_task);
$allowed = isset($task['exists']) && !in_array($task['task_status'], array(
HOSTING_TASK_QUEUED,
HOSTING_TASK_PROCESSING,
)) || !isset($task['exists']);
if ($allowed && empty($task['hidden']) && $access_callback($node, $type)) {
$task['task_permitted'] = TRUE;
}
else {
$task['task_permitted'] = FALSE;
}
if (!isset($task['dialog'])) {
$task['dialog'] = FALSE;
}
if (!isset($task['task_status'])) {
$task['task_status'] = NULL;
}
$task['class'] = hosting_task_status_class($task['task_status']);
$return[$type] = $task;
}
return $return;
}
function hosting_task_status_name($hosting_task_status = NULL) {
$codes = array(
HOSTING_TASK_SUCCESS => 'success',
HOSTING_TASK_QUEUED => 'queued',
HOSTING_TASK_ERROR => 'error',
HOSTING_TASK_PROCESSING => 'processing',
HOSTING_TASK_WARNING => 'warning',
);
if (!is_null($hosting_task_status) && isset($codes[$hosting_task_status])) {
return $codes[$hosting_task_status];
}
elseif (!is_null($hosting_task_status) && !isset($codes[$hosting_task_status])) {
return '';
}
else {
return $codes;
}
}
function hosting_task_status_class($status = NULL) {
if (!is_null($status) && ($name = hosting_task_status_name($status))) {
return 'hosting-' . $name;
}
else {
return '';
}
}
function hosting_task_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'hosting_task') . '/includes/views',
);
}
function hosting_task_overlay_paths() {
$paths = array(
'hosting/task/*' => array(
'width' => 600,
),
'hosting_confirm/*' => array(
'width' => 600,
),
);
return $paths;
}
function hosting_task_preprocess_views_view_table(&$vars) {
$id = "{$vars['view']->name}-{$vars['view']->current_display}";
switch ($id) {
case 'hosting_task_list-block':
drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
$settings['hostingTaskRefresh'] = array(
'queueBlock' => 1,
);
drupal_add_js($settings, 'setting');
break;
}
}
function hosting_task_update_status($task) {
if (is_numeric($task)) {
$vid = $task;
$task = node_load(NULL, $task);
}
else {
$vid = $task->vid;
}
$status = hosting_task_parse_log($vid);
db_update('hosting_task')
->fields(array(
'task_status' => $status,
))
->condition('vid', $vid)
->execute();
module_invoke_all('hosting_task_update_status', $task, $status);
return $status;
}
function hosting_task_parse_log($vid) {
$update = HOSTING_TASK_SUCCESS;
$result = db_query('SELECT type FROM {hosting_task_log} WHERE vid = :vid', array(
':vid' => $vid,
));
foreach ($result as $status) {
if ($status->type == 'error' || $status->type == 'failed') {
$update = HOSTING_TASK_ERROR;
}
elseif ($update != HOSTING_TASK_ERROR && $status->type == 'warning') {
$update = HOSTING_TASK_WARNING;
}
}
return $update;
}
function hosting_task_get_guarded_nodes() {
$guarded_nids =& drupal_static(__FUNCTION__);
if (!isset($guarded_nids)) {
$guarded_nids = module_invoke_all('hosting_task_guarded_nodes');
drupal_alter('hosting_task_guarded_nodes', $guarded_nids);
}
return $guarded_nids;
}
function hosting_task_get_dangerous_tasks() {
$dangerous_tasks =& drupal_static(__FUNCTION__);
if (!isset($dangerous_tasks)) {
$dangerous_tasks = module_invoke_all('hosting_task_dangerous_tasks');
drupal_alter('hosting_task_dangerous_tasks', $dangerous_tasks);
}
return $dangerous_tasks;
}
function hosting_task_hosting_task_dangerous_tasks() {
$dangerous_tasks = array(
'disable',
'delete',
);
return $dangerous_tasks;
}
function hosting_task_dangerous_task_is_allowed($task_type, $nid) {
$allowed = TRUE;
$guarded_nids = hosting_task_get_guarded_nodes();
if (in_array($nid, $guarded_nids)) {
$dangerous_tasks = hosting_task_get_dangerous_tasks();
if (in_array($task_type, $dangerous_tasks)) {
$node = node_load($nid);
$map = array(
':task' => $task_type,
':title' => $node->title,
':type' => $node->type,
);
drupal_set_message(t('You cannot run dangerous :task task on guarded :type :title.', $map), 'warning');
watchdog('hostmaster', 'Detected attempt to run dangerous :task task on guarded :type :title.', $map, WATCHDOG_WARNING);
$allowed = FALSE;
}
}
return $allowed;
}
function hosting_task_entity_property_info_alter(&$info) {
$task_properties =& $info['node']['bundles']['task']['properties'];
$task_properties['task_type'] = array(
'label' => t("Task type"),
'description' => t("The type the task node uses."),
'type' => 'text',
'getter callback' => 'entity_property_verbatim_get',
'setter callback' => 'entity_property_verbatim_set',
);
$task_properties['task_status'] = array(
'label' => t('Task status'),
'description' => t('The status of the task. E.g. @TODO, etc.'),
'type' => 'integer',
'options list' => '_hosting_task_error_codes',
'setter callback' => 'entity_property_verbatim_set',
);
$task_properties['rid'] = array(
'label' => t('Relation ID'),
'description' => t('The relation this task belong to: site, platform, etc.'),
'type' => 'integer',
'getter callback' => 'entity_property_verbatim_get',
'setter callback' => 'entity_property_verbatim_set',
);
}