You are here

hosting_task.module in Hosting 5

Web server node type is defined here.

File

task/hosting_task.module
View 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

Namesort descending Description
hosting_add_task Helper function to generate new task node
hosting_available_tasks
hosting_get_most_recent_task Retrieve the latest task related to the specified platform, of a specific type
hosting_get_tasks Retrieve tasks with specified criterias
hosting_nodeapi_task_delete_revision
hosting_task_access
hosting_task_confirm_form Implementation of hook_form().
hosting_task_confirm_form_submit
hosting_task_count Return the amount of items still in the queue
hosting_task_delete Implementation of hook_delete().
hosting_task_insert Implementation of hook_insert().
hosting_task_list Display list of tasks
hosting_task_list_embedded
hosting_task_load Implementation of hook_load().
hosting_task_log
hosting_task_menu Implementation of hook_menu().
hosting_task_node_info
hosting_task_outstanding Determine whether there is an outstanding task of a specific type.
hosting_task_perm
hosting_task_restore_form
hosting_task_retry
hosting_task_retry_form
hosting_task_retry_form_submit
hosting_task_set_title
hosting_task_status
hosting_task_status_output
hosting_task_summary
hosting_task_update Implementation of hook_update().
hosting_task_view Implementation of hook_view().
_hosting_parse_error_code Turn bitmask integer error code into associative array
_hosting_task_list Theme functions
_hosting_task_log_class Map entry statuses to coincide.
_hosting_task_log_table Display table containing the logged information for this task

Constants

Namesort descending 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.