You are here

hosting_platform.module in Hosting 7.4

Platform node type definition.

File

platform/hosting_platform.module
View source
<?php

/**
 * @file
 * Platform node type definition.
 */

/**
 * This platform has been deleted.
 */
define('HOSTING_PLATFORM_DELETED', -2);

/**
 * This platform has been locked, new sites will not be allowed to use it.
 */
define('HOSTING_PLATFORM_LOCKED', -1);

/**
 * This platform has been queued for creation.
 */
define('HOSTING_PLATFORM_QUEUED', 0);

/**
 * This platform is created and enabled, sites may be deployed on it.
 */
define('HOSTING_PLATFORM_ENABLED', 1);

/**
 * Implements hook_menu().
 */
function hosting_platform_menu() {
  $items = array();
  $items['hosting/platforms/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['hosting/platforms/add'] = array(
    'title' => 'Add platform',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_goto',
    'page arguments' => array(
      'node/add/platform',
    ),
    'access callback' => 'node_access',
    'access arguments' => array(
      'create',
      'platform',
    ),
  );
  $items['node/%node/platform-edit'] = array(
    'title' => 'Codebase Settings',
    'page callback' => 'hosting_platform_edit_form_on_site_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'hosting_platform_edit_form_on_site_page_access',
    'access arguments' => array(
      'update',
      1,
    ),
    'weight' => 0,
    'type' => MENU_LOCAL_TASK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'file' => 'node.pages.inc',
    'file path' => drupal_get_path('module', 'node'),
    'weight' => -98,
  );
  return $items;
}

/**
 * Implements hook_node_info().
 */
function hosting_platform_node_info() {
  $types["platform"] = array(
    "type" => 'platform',
    "name" => 'Platform',
    'base' => 'hosting_platform',
    "has_title" => TRUE,
    "title_label" => t('Name'),
    "description" => hosting_node_help("platform"),
    "has_body" => 0,
    "body_label" => '',
    "min_word_count" => 0,
  );
  return $types;
}

/**
 * Page callback for node/%node/platform-edit
 *
 * Wrapper for node_page_edit, for the "Edit Platform" tab.
 */
function hosting_platform_edit_form_on_site_page($site) {
  $platform = node_load($site->platform);
  return node_page_edit($platform);
}

/**
 * Wrapper for node_page_edit, for the "Edit Platform" tab.
 */
function hosting_platform_edit_form_on_site_page_access($op, $site) {
  if ($site->type == 'site') {
    $platform = node_load($site->platform);
    return node_access($op, $platform);
  }
}

/**
 * Implements hook_hosting_tasks().
 */
function hosting_platform_hosting_tasks() {
  $tasks = array();
  $tasks['platform']['verify'] = array(
    'title' => t('Verify Platform'),
    'description' => t('Verify that the platform is correctly installed and working.'),
    'weight' => 10,
    'provision_save' => TRUE,
  );
  $tasks['platform']['delete'] = array(
    'title' => t('Delete Platform'),
    'description' => t('Deleting this platform will completely remove it from the hosting system.
        This process can not be undone. It can not be performed if you have sites currently
        running on this platform.
        Are you really sure you want to delete this platform?'),
    'weight' => 10,
    'dialog' => TRUE,
  );
  $tasks['platform']['lock'] = array(
    'title' => t('Lock'),
    'description' => t('Locking this platform will not delete or disable it or its sites, but will
        prevent any new sites from being created on it. This may be useful when you have sites that
        cannot be migrated onto a newer platform, but you wish to prevent other users or clients
        from continuing to provision on this platform. The platform can be unlocked later.
        Are you really sure you want to lock this platform?'),
    'weight' => 10,
  );
  $tasks['platform']['unlock'] = array(
    'title' => t('Unlock'),
    'description' => t('Unlocking this platform will allow sites to be provisioned on it.
        Are you really sure you want to unlock this platform?'),
    'weight' => 10,
  );
  return $tasks;
}

/**
 * Implements hook_permission().
 */
function hosting_platform_permission() {
  return array(
    'administer platforms' => array(
      'title' => t('administer platforms'),
    ),
    'create platform' => array(
      'title' => t('create platform'),
    ),
    'view platform' => array(
      'title' => t('view platform'),
    ),
    'edit platform' => array(
      'title' => t('edit platform'),
    ),
    'delete platform' => array(
      'title' => t('delete platform'),
    ),
    'view locked platforms' => array(
      'title' => t('view locked platforms'),
    ),
    'create sites on locked platforms' => array(
      'title' => t('create sites on locked platforms'),
    ),
  );
}

/**
 * Implements hook_node_access().
 */
function hosting_platform_node_access($node, $op, $account) {
  if (hosting_feature('client') && $op != 'create') {

    // We rely on hosting_client_node_grants() instead of global configuration.
    return NODE_ACCESS_IGNORE;
  }
  $type = is_string($node) ? $node : $node->type;
  if ($type != 'platform') {
    return NODE_ACCESS_IGNORE;
  }
  switch ($op) {
    case 'create':
      return user_access('create platform', $account);
    case 'update':
      return user_access('edit platform', $account);
    case 'delete':
      return user_access('delete platform', $account);
    case 'view':
      return user_access('view platform', $account);
  }
}

/**
 * Callback for platform verify action.
 *
 * @see: hosting_task_action_info().
 */
function hosting_platform_verify_action($node) {
  hosting_add_task($node->nid, 'verify');
}

/**
 * Callback for platform lock action.
 *
 * @see: hosting_task_action_info().
 */
function hosting_platform_lock_action($node) {
  hosting_add_task($node->nid, 'lock');
}

/**
 * Callback for platform unlock action.
 *
 * @see: hosting_task_action_info().
 */
function hosting_platform_unlock_action($node) {
  hosting_add_task($node->nid, 'unlock');
}

/**
 * Callback for platform delete action.
 *
 * @see: hosting_task_action_info().
 */
function hosting_platform_delete_action($node) {
  hosting_add_task($node->nid, 'delete');
}

/**
 * Helper function to get platforms that haven't been deleted.
 *
 * @param int $web_server
 *   Node ID of the server hosting this platform.
 *   Default 0 indicates any web_server.
 * @param boolean $enabled_only
 *   Set to TRUE to return only enabled platforms. Defaults to FALSE.
 * @return
 *   The list of platforms.
 * @todo
 *   Convert this to an EntityFieldQuery with conditions & tags (for JOINs).
 *   Tags will be unnecessary in D8 as JOINs are natively supported. See
 *   hosting_get_site_by_url() for an example of how to do this.
 */
function _hosting_get_platforms($web_server = 0, $enabled_only = FALSE) {
  $return = array();

  // Set the query parameters.
  $parameters = array(
    ':type' => 'platform',
    ':nstatus' => 1,
    //@todo: remove magic number?
    ':hstatus' => HOSTING_PLATFORM_DELETED,
    ':web_server' => $web_server,
  );

  // Add another filter to return only enabled sites is requested.
  $enabled_text = $enabled_only ? "AND h.status <> :disabled" : '';
  if ($enabled_only) {
    $parameters[':disabled'] = HOSTING_PLATFORM_LOCKED;
  }
  $result = db_query("SELECT n.nid, n.title\n                      FROM {node} n\n                      LEFT JOIN {hosting_platform} h\n                      ON n.nid = h.nid\n                      WHERE n.type = :type\n                      AND n.status = :nstatus\n                      AND h.status <> :hstatus\n                      AND (h.web_server = :web_server OR :web_server = 0)\n                      {$enabled_text}\n                      ORDER BY n.title\n                     ", $parameters);
  while ($platform = $result
    ->fetch()) {
    $return[$platform->nid] = $platform->title;
  }
  return $return;
}

/**
 * Helper function to get platforms that haven't been deleted or locked.
 *
 * @deprecated
 *   _hosting_get_platforms() now has an $enabled_only parameter.
 */
function _hosting_get_enabled_platforms() {
  $return = array();
  $result = db_query("SELECT n.nid, n.title\n                      FROM {node} n\n                      LEFT JOIN {hosting_platform} h\n                      ON n.nid = h.nid\n                      WHERE n.type = :n.type\n                      AND n.status = :n.status\n                      AND h.status <> :h.status\n                      ORDER BY n.title\n                     ", array(
    ':n.type' => 'platform',
    ':n.status' => 1,
    //@todo: remove magic number?
    ':h.status' => HOSTING_PLATFORM_LOCKED,
  ));
  while ($platform = $result
    ->fetch()) {
    $return[$platform->nid] = $platform->title;
  }
  return $return;
}

/**
 * Helper function to get platforms that have been locked.
 */
function _hosting_get_locked_platforms() {
  $return = array();
  $result = db_query("SELECT n.nid, n.title\n                      FROM {node} n\n                      LEFT JOIN {hosting_platform} h\n                      ON n.nid = h.nid\n                      WHERE n.type = :type\n                      AND n.status = :nstatus\n                      AND h.status = :hstatus\n                      ORDER BY n.title\n                     ", array(
    ':type' => 'platform',
    ':nstatus' => NODE_PUBLISHED,
    ':hstatus' => HOSTING_PLATFORM_LOCKED,
  ));
  while ($platform = $result
    ->fetch()) {
    $return[$platform->nid] = $platform->title;
  }
  return $return;
}

/**
 * Helper function to check if a platform is locked.
 */
function _hosting_platform_is_locked($node) {
  if (in_array($node, array_keys(_hosting_get_locked_platforms()))) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Implements hook_form().
 */
function hosting_platform_form(&$node) {
  $type = node_type_get_type($node);
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#required' => TRUE,
    '#description' => t('Choose a unique descriptive name for your platform. You very likely want this to be something like "Drupal 7.21".'),
    '#size' => 40,
    '#default_value' => $node->title,
    '#maxlength' => 255,
  );

  // Allow editing the "publish path" so that users can change their platforms if they need to.
  //  $base_path = variable_get('hosting_platform_base_path', '/var/aegir/platforms/');
  //  $default_path = '';
  //  $field_prefix = $base_path;
  //  if (isset($node->publish_path)) {
  //    // Strip the $base_path, since we prepend it in a validation handler.
  //    // This enforces platform publishing under the defined base path, even if
  //    // an error allows the path to be edited again.
  //    if (strpos($node->publish_path, $base_path) === 0) {
  //      $default_path = substr($node->publish_path, strlen($base_path));
  //    }
  //    else {
  //      // If publish path is not inside /var/aegir/platforms, use that as the default path and remove the field prefix.
  //      $default_path = $node->publish_path;
  //      $field_prefix = '';
  //    }
  //  }
  $form['git'] = array(
    '#type' => 'fieldset',
    '#title' => t('Git Information'),
    '#collapsible' => false,
    '#description' => $node->nid ? t('Change the git configuration of the codebase checked out to &path.', array(
      '&path' => $node->git_root,
    )) : t('Create this platform using a git repository.'),
    '#weight' => -1,
  );
  $form['git']['git_remote'] = array(
    '#type' => 'textfield',
    '#title' => t('Git Repository URL'),
    '#description' => t('The URL or path of the git repository to clone for this site.'),
    '#default_value' => $node->git_remote,
    '#required' => TRUE,
  );

  // @TODO: Detect clone state instead of just saying "it worked!".
  $git_root_description = $node->nid ? '<div class="alert alert-info"><i class="fa fa-info-circle"></i> ' . t('The repository was cloned to: &path', array(
    '&path' => $node->git_root,
  )) : t('The full path on the server to clone the git repository to.') . "</div>";
  $form['git']['git_root'] = array(
    '#type' => $node->nid ? 'value' : 'textfield',
    '#title' => t('Path to Clone'),
    '#default_value' => $node->git_root,
    '#weight' => 10,
    '#description' => $git_root_description,
    '#suffix' => $git_root_description,
  );
  $form['git']['git_reference'] = array(
    '#type' => 'textfield',
    '#title' => t('Git Reference'),
    '#description' => $node->nid ? t('Enter a git reference to checkout. If left blank, no changes will be made.') : t('The desired git reference. Can be a branch, tag or SHA. If left blank, the default branch will be checked out.'),
    '#default_value' => $node->git_reference,
  );
  $form['git']['git_docroot'] = array(
    '#type' => 'textfield',
    '#title' => t('Relative Docroot'),
    '#description' => t('The relative path within the git repository to publish to the web.'),
    '#default_value' => $node->git_docroot,
  );
  $form['git']['git_reset'] = array(
    '#type' => 'checkbox',
    '#title' => t('Reset working copy changes.'),
    '#description' => t('If checked, any changes to the cloned git code will be reset and lost.'),
    '#default_value' => $node->git_reset,
  );

  // @TODO: Decide to remove makefile support?
  //  $form['frommakefile'] = array(
  //    '#type' => 'fieldset',
  //    '#tree' => TRUE,
  //    '#title' => t('Deploy from makefile'),
  //    '#collapsible' => TRUE,
  //    '#description' => t('You may deploy this platform from a makefile.'),
  //    '#weight' => -1,
  //    '#access' =>
  //  );
  //
  //  $form['frommakefile']['makefile'] = array(
  //    '#type' => 'textfield',
  //    '#title' => t('Makefile'),
  //    '#description' => t('The absolute path on the filesystem or public URL of a makefile that will be used to create the platform in the directory specified above. If the directory already exists, this file will be ignored.'),
  //    '#size' => 40,
  //    '#default_value' => isset($node->makefile) ? $node->makefile : NULL,
  //    '#maxlength' => 255,
  //  );
  //
  //  $form['frommakefile']['make_working_copy'] = array(
  //    '#type' => 'radios',
  //    '#title' => t('Drush make option'),
  //    '#default_value' => isset($node->make_working_copy) ? $node->make_working_copy : 0,
  //    '#options' => array(
  //      0 => t('Normal - Discards SCM files'),
  //      1 => t('Working copy - preserves SCM files'),
  //    ),
  //  );
  $form['#after_build'][] = 'hosting_platform_form_platform_after_build';
  $servers = hosting_get_servers('http');
  if (count($servers) > 1) {
    $form['web_server'] = array(
      '#type' => 'radios',
      '#title' => t('Web server'),
      '#description' => t('The web server the sites will be hosted on.'),
      '#options' => $servers,
      '#default_value' => isset($node->web_server) ? $node->web_server : HOSTING_DEFAULT_WEB_SERVER,
    );
  }
  else {
    reset($servers);
    $form['web_server'] = array(
      '#type' => 'hidden',
      '#value' => key($servers),
    );
  }
  foreach (array(
    'verified',
    'platform_status',
  ) as $extra_attribute) {
    $form["{$extra_attribute}"] = array(
      '#type' => 'value',
      '#value' => isset($node->{$extra_attribute}) ? $node->{$extra_attribute} : NULL,
    );
  }
  return $form;
}

/**
 * After build callback for the hosting_platform_form.
 */
function hosting_platform_form_platform_after_build($form) {
  drupal_add_js(drupal_get_path('module', 'hosting_platform') . '/hosting_platform.js');
  $form['frommakefile']['makefile']['#attributes']['class'][] = ' hosting-platform-working-copy-source';
  $form['frommakefile']['make_working_copy']['#attributes']['class'][] = ' hosting-platform-working-copy-target';
  return $form;
}

/**
 * Hide the delete button on platform nodes.
 */
function hosting_platform_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'platform_node_form':

      // Remove delete button from platform edit form,
      // unless the platform's already been deleted via the Delete task.
      $node = $form['#node'];
      if (isset($node->platform_status) && $node->platform_status !== HOSTING_PLATFORM_DELETED) {
        $form['actions']['delete']['#type'] = 'hidden';
      }
      break;
    case 'hosting_settings':
      $form['hosting_platform_base_path'] = array(
        '#type' => 'textfield',
        '#title' => t('Platform base path'),
        '#description' => t('The base path for your platforms, i.e. /var/aegir/platforms/'),
        '#default_value' => variable_get('hosting_platform_base_path', '/var/aegir/platforms/'),
      );
      break;
  }
}

/**
 * Implements hook_node_presave()
 */
function hosting_platform_node_presave($node) {

  // Generate publish path from git_root and git_docroot if it is not already set.
  if ($node->type == 'platform' && !empty($node->git_root)) {

    // If no git_docroot, just set git_root.
    if (empty($node->git_docroot)) {
      $node->publish_path = $node->git_root;
    }
    else {
      $node->publish_path = $node->git_root . DIRECTORY_SEPARATOR . $node->git_docroot;
    }
  }
}

/**
 * Implements hook_insert().
 */
function hosting_platform_insert($node) {
  if (empty($node->no_verify)) {
    hosting_add_task($node->nid, 'verify');
  }
  $id = db_insert('hosting_platform')
    ->fields(array(
    'vid' => $node->vid,
    'nid' => $node->nid,
    'git_remote' => $node->git_remote,
    'git_reference' => $node->git_reference,
    'git_reset' => (int) $node->git_reset,
    'git_root' => hosting_path_normalize($node->git_root),
    'git_docroot' => $node->git_docroot,
    'publish_path' => hosting_path_normalize($node->publish_path),
    'makefile' => isset($node->makefile) ? $node->makefile : (isset($node->frommakefile['makefile']) ? $node->frommakefile['makefile'] : ''),
    'verified' => isset($node->verified) ? $node->verified : 0,
    'web_server' => $node->web_server,
    'status' => isset($node->platform_status) ? $node->platform_status : 0,
    'make_working_copy' => isset($node->frommakefile['make_working_copy']) ? $node->frommakefile['make_working_copy'] : 0,
  ))
    ->execute();
  if (!isset($node->old_vid)) {
    hosting_context_register($node->nid, 'platform_' . preg_replace("/[!\\W]/", "", $node->title));
  }
}

/**
 * Implements hook_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function hosting_platform_update($node) {

  // If this is a new node or we're adding a new revision.
  if (!empty($node->revision)) {
    hosting_platform_insert($node);
  }
  else {
    if ($node->platform_status == HOSTING_PLATFORM_DELETED) {
      $node->no_verify = TRUE;
    }
    db_update('hosting_platform')
      ->fields(array(
      'git_remote' => $node->git_remote,
      'git_reference' => $node->git_reference,
      'git_reset' => (int) $node->git_reset,
      'git_root' => hosting_path_normalize($node->git_root),
      'git_docroot' => $node->git_docroot,
      'publish_path' => hosting_path_normalize($node->publish_path),
      'makefile' => isset($node->makefile) ? $node->makefile : (isset($node->frommakefile['makefile']) ? $node->frommakefile['makefile'] : ''),
      'web_server' => $node->web_server,
      'verified' => $node->verified,
      'status' => $node->platform_status,
      'make_working_copy' => isset($node->frommakefile['make_working_copy']) ? $node->frommakefile['make_working_copy'] : $node->make_working_copy,
    ))
      ->condition('nid', $node->nid)
      ->execute();
  }

  // Trigger verify tasks for each enabled site.
  if (empty($node->no_verify)) {
    $sites = hosting_get_sites_by_status($node->nid, HOSTING_SITE_ENABLED);
    foreach ($sites as $site_nid => $site) {
      hosting_add_task($site->nid, 'verify');
    }

    // If no sites, just verify the platform.
    if (empty($sites)) {
      hosting_add_task($node->nid, 'verify');
    }
  }
}

/**
 * Implements hook_nodeapi_TYPE_OP().
 */
function hosting_nodeapi_platform_delete_revision(&$node) {
  db_delete('hosting_platform')
    ->condition('vid', $node->vid)
    ->execute();
}

/**
 * Implements hook_delete().
 */
function hosting_platform_delete($node) {
  db_delete('hosting_platform')
    ->condition('nid', $node->nid)
    ->execute();
  db_delete('hosting_package_instance')
    ->condition('rid', $node->nid)
    ->execute();
  hosting_context_delete($node->nid);
  hosting_task_delete_related_tasks($node->nid);
  $result = db_query("SELECT distinct nid FROM {hosting_site} WHERE platform = :platform", array(
    ':platform' => $node->nid,
  ));
  while ($nid = $result
    ->fetch()) {
    node_delete($nid->nid);
  }
}

/**
 * Implements hook_validate().
 */
function hosting_platform_validate($node, &$form) {
  if ($node->op != t('Delete')) {

    // Make sure the platform name is unique, to avoid context clashes.
    $sql = "SELECT n.title as name\n              FROM {hosting_platform} h\n        INNER JOIN {node} n ON n.nid = h.nid\n             WHERE n.title = :title\n               AND h.status >= :status";
    $args = array(
      ':title' => $node->title,
      ':status' => HOSTING_PLATFORM_QUEUED,
    );
    if (!is_null($node->nid)) {
      $sql .= " AND n.nid <> :nid";
      $args[':nid'] = $node->nid;
    }
    $result = db_query($sql, $args)
      ->fetch();
    if ($result) {
      form_set_error('title', t('Platform name %name is already defined. Platform names must be unique across all servers.', array(
        '%name' => $result->name,
      )));
    }

    // Make sure the path is unique. Remote servers can't have the same path to a platform that is in use by another server.
    $exists = hosting_platform_path_exists($node->publish_path);
    if ($exists) {
      form_set_error('publish_path', t('Path is already in use by platform %name. Platform paths must be unique across all servers.', array(
        '%name' => $result->name,
      )));
    }

    // Make sure the git_root is unique. Can't clone two repos to the same folder.
    $exists = hosting_platform_path_exists($node->git_root);
    if ($exists) {
      form_set_error('git_root', t('Path is already in use by the platform %name. Platform paths must be unique across all servers.', array(
        '%name' => l($result->name, "node/{$result->name}/nid"),
      )));
    }
    if (is_null($node->web_server)) {
      form_set_error('web_server', t('Platform needs to be associated with a webserver. Make sure you have a verified webserver on this Aegir install!'));
    }
  }
}

/**
 * Determine whether a given path has already been used with an existing
 * platform.
 */
function hosting_platform_path_exists($path) {
  $result = db_query("SELECT\n        n.title AS name,\n        h.nid\n    FROM {hosting_platform} AS h\n      INNER JOIN {node} AS n ON n.nid = h.nid\n    WHERE publish_path = !publish_path\n      OR git_root = !publish_path\n      AND h.status >= !h.status", array(
    '!publish_path' => hosting_path_normalize($path),
    '!h.status' => HOSTING_PLATFORM_QUEUED,
  ))
    ->fetch();
  return $result;
}

/**
 * Implements hook_load().
 */
function hosting_platform_load($nodes) {
  foreach ($nodes as $nid => &$node) {
    $additions = db_query('
      SELECT
        p.*,
        c.name as web_server_name,
        status AS platform_status
      FROM {hosting_platform} p
      LEFT JOIN {hosting_context} c ON p.web_server = c.nid
      WHERE vid = :vid

    ', array(
      ':vid' => $node->vid,
    ))
      ->fetch();

    // Avoid PHP 5.4 warning when platform doesn't exist yet.
    // See: https://drupal.org/node/1940378
    $additions = $additions ? $additions : new stdClass();
    $iid = db_query("SELECT iid\n                     FROM {hosting_package_instance} i\n                     LEFT JOIN {hosting_package} p\n                     ON p.nid=i.package_id\n                     WHERE p.package_type = :package_type\n                     AND i.rid = :rid", array(
      ':package_type' => 'platform',
      ':rid' => $node->nid,
    ))
      ->fetchField();
    if (empty($node->frommakefile)) {
      $node->frommakefile = array(
        'makefile' => '',
        'make_working_copy' => 0,
      );
    }
    if (!empty($additions->makefile)) {
      $node->frommakefile['makefile'] = $additions->makefile;
    }
    if (!empty($additions->make_working_copy)) {
      $node->frommakefile['make_working_copy'] = $additions->make_working_copy;
    }
    if (module_exists('hosting_package')) {
      $additions->release = hosting_package_instance_load($iid);
      $additions->profiles = hosting_get_profiles($node->nid, 'short_name');
    }
    if (hosting_feature('client')) {
      $result = db_query("SELECT cid FROM {hosting_platform_client_access} WHERE pid = :pid", array(
        ':pid' => $node->nid,
      ));
      foreach ($result as $record) {
        $additions->clients[$record->cid] = $record->cid;
      }
    }

    // @TODO: Legacy Refactor
    // Load service_subscriptions.
    $additions->service_subscriptions['http'] = array(
      'server' => $additions->web_server_name,
    );
    foreach ($additions as $property => &$value) {
      $node->{$property} = is_numeric($value) ? (int) $value : $value;
    }

    // Load commands for this platform.
    $node->commands = hosting_find_deploy_commands($node);
  }
}

/**
 * Menu wildcard loader callback.
 *
 * Loads a hosting_platform node.
 * @see hosting_task_menu()
 *
 * @param int $arg
 *   Node's numeric nid
 *
 * @return bool|object
 *   A platforn node object or FALSE.
 */
function hosting_platform_wildcard_load($arg) {
  if (!is_numeric($arg)) {
    return FALSE;
  }
  if ($node = node_load($arg)) {
    if ($node->type == 'platform') {
      return $node;
    }
  }
  return FALSE;
}

/**
 * Implements hook_view().
 */
function hosting_platform_view($node, $view_mode, $langcode = NULL) {
  hosting_set_breadcrumb($node);
  $node->content['info'] = array(
    '#type' => 'fieldset',
  );
  $node->content['info']['git_remote'] = array(
    '#type' => 'item',
    '#title' => t('Git Remote'),
    '#markup' => $node->git_remote ?: t('None'),
  );
  $node->content['info']['git_root'] = array(
    '#type' => 'item',
    '#title' => t('Repository Path'),
    '#markup' => $node->git_root ?: t('Unknown'),
  );
  $node->content['info']['git_reference'] = array(
    '#type' => 'item',
    '#title' => t('Git Reference'),
    '#markup' => $node->git_reference ?: t('Unknown'),
  );
  $node->content['info']['git_docroot'] = array(
    '#type' => 'item',
    '#title' => t('Relative Docroot'),
    '#markup' => $node->git_docroot ?: '',
    '#access' => !empty($node->git_docroot),
  );

  // Show an Edit Platform button.
  $platform_nid = $node->type == 'platform' ? $node->nid : $node->platform;
  $platform_edit_url = "node/{$platform_nid}/edit";
  $page_is_platform_page = arg(1) == $platform_nid;
  $platform_edit_url_query = $page_is_platform_page ? array() : drupal_get_destination();
  $node->content['info']['verified'] = array(
    '#type' => 'item',
    '#title' => t('Verified'),
    '#markup' => hosting_format_interval($node->verified),
    '#weight' => -10,
  );
  $node->content['info']['publish_path'] = array(
    '#type' => 'item',
    '#title' => t('Publish path'),
    '#markup' => filter_xss($node->publish_path),
    '#weight' => -8,
  );
  $node->content['info']['web_server'] = array(
    '#type' => 'item',
    '#title' => t('Web server'),
    '#markup' => _hosting_node_link($node->web_server),
    '#weight' => -7,
  );
  $node->content['info']['status'] = array(
    '#type' => 'item',
    '#title' => t('Status'),
    '#markup' => _hosting_platform_status($node->platform_status),
  );
  if ($node->makefile) {
    $node->content['info']['makefile'] = array(
      '#type' => 'item',
      '#title' => t('Drush makefile'),
      '#markup' => preg_match('/^http|^ftp/', $node->makefile) ? l(t('makefile'), $node->makefile) : filter_xss($node->makefile),
    );
  }
  if ($node->release) {
    $release = sprintf("%s %s", $node->release->title, $node->release->version);
    $node->content['info']['release'] = array(
      '#type' => 'item',
      '#title' => t('Release'),
      '#markup' => _hosting_node_link($node->release->nid, $release),
      '#weight' => -6,
    );
  }

  // Task list.
  if ($view_mode == 'full' && $node->nid) {
    $node->content['tasks_view'] = array(
      '#type' => 'item',
      '#markup' => hosting_task_table($node),
      '#prefix' => '<div id="hosting-task-list">',
      '#suffix' => '</div>',
      '#weight' => 9,
    );
    $settings['hostingTaskRefresh'] = array(
      'nid' => $node->nid,
      'changed' => $node->changed,
    );
    drupal_add_js($settings, array(
      'type' => 'setting',
      'scope' => JS_DEFAULT,
    ));
    drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
  }
  return $node;
}

/**
 * Implements hook_field_extra_fields()
 */
function hosting_platform_field_extra_fields() {
  $return['node']['platform'] = array(
    'display' => array(
      'info' => array(
        'label' => t('Aegir Platform Information'),
        'description' => t('Detailed information about this platform.'),
        'weight' => 0,
      ),
      'tasks_view' => array(
        'label' => t('Aegir Platform Tasks'),
        'description' => t('List of available tasks.'),
        'weight' => 1,
      ),
    ),
  );
  return $return;
}

/**
 * Implements hook_entity_property_info().
 */
function hosting_platform_entity_property_info() {
  $info['node']['bundles']['platform']['properties'] = array(
    'publish_path' => array(
      'label' => t('Publish path'),
      'description' => t('The location on disk of the platform.'),
      'type' => 'text',
    ),
    'makefile' => array(
      'label' => t('Makefile'),
      'description' => t('The makefile used to generate this platform (if any).'),
      'type' => 'text',
    ),
    'verified' => array(
      'label' => t('Last verification time'),
      'description' => t('The date and time of the last verification of the platform.'),
      'type' => 'date',
    ),
    'web_server' => array(
      'label' => t('Web server'),
      'description' => t('The server the platform is on.'),
      'type' => 'node',
      'bundle' => 'server',
    ),
    'platform_status' => array(
      'label' => t('Platform status'),
      'description' => t('The status of the platform. E.g. enabled, deleted, etc.'),
      'type' => 'integer',
      'options list' => '_hosting_platform_status_codes_labels',
    ),
    'make_working_copy' => array(
      'label' => t('Drush make - Working copy'),
      'description' => t('Determines if the working-copy option is used when using Drush make to generate the codebase for the platform.'),
      'type' => 'boolean',
    ),
  );
  if (hosting_feature('client')) {
    $info['node']['bundles']['platform']['properties']['clients'] = array(
      'label' => t('Clients with access'),
      'description' => t('An array of clients with access to this platform.'),
      'type' => 'list<node>',
      'bundle' => 'client',
    );
  }
  return $info;
}

/**
 * Implements hook_hosting_site_site_list_filters().
 */
function hosting_platform_hosting_site_site_list_filters() {
  return array(
    'platform',
  );
}

/**
 * Helper function to map status codes to labels and classes.
 */
function _hosting_platform_status_codes() {
  $codes = array(
    HOSTING_PLATFORM_QUEUED => array(
      'label' => 'Queued',
      'class' => 'hosting-queue',
    ),
    HOSTING_PLATFORM_ENABLED => array(
      'label' => 'Enabled',
      'class' => 'hosting-success',
    ),
    HOSTING_PLATFORM_DELETED => array(
      'label' => 'Deleted',
      'class' => 'hosting-error',
    ),
    HOSTING_PLATFORM_LOCKED => array(
      'label' => 'Locked',
      'class' => 'hosting-disable',
    ),
  );
  return $codes;
}

/**
 * Platform status codes to human-readable label map.
 */
function _hosting_platform_status_codes_labels() {
  $labels = array();
  foreach (_hosting_platform_status_codes() as $code => $info) {
    $labels[$code] = $info['label'];
  }
  return $labels;
}

/**
 * Return the appropriate status label.
 */
function _hosting_platform_status($status) {
  static $labels;
  $labels = _hosting_platform_status_codes();
  return is_object($status) ? $labels[$status->platform_status]['label'] : $labels[$status]['label'];
}

/**
 * Implements hook_hosting_summary().
 */
function hosting_platform_hosting_summary() {
  $summary = array();
  if (user_access('view locked platforms')) {
    $platforms = _hosting_get_platforms();
    $summary['platforms'] = theme('item_list', array(
      'items' => array_map('_hosting_node_link', array_keys($platforms)),
      'title' => t('Platforms'),
    ));
  }
  elseif (user_access('view platform')) {
    $platforms = _hosting_get_enabled_platforms();
    $summary['platforms'] = theme('item_list', array(
      'items' => array_map('_hosting_node_link', array_keys($platforms)),
      'title' => t('Platforms'),
    ));
  }
  return $summary;
}

/**
 * Implements hook_views_api().
 */
function hosting_platform_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'hosting_platform') . '/includes/views',
  );
}

/**
 * Define the classes that correspond to the platform status.
 */
function _hosting_platform_list_class($status) {
  static $labels;
  $labels = _hosting_platform_status_codes();
  return is_object($status) ? $labels[$status->platform_status]['class'] : $labels[$status]['class'];
}

/**
 * Implements hook_hosting_task_guarded_nodes().
 */
function hosting_platform_hosting_task_guarded_nodes() {
  return array(
    hosting_get_hostmaster_platform_nid(),
  );
}

Functions

Namesort descending Description
hosting_nodeapi_platform_delete_revision Implements hook_nodeapi_TYPE_OP().
hosting_platform_delete Implements hook_delete().
hosting_platform_delete_action Callback for platform delete action.
hosting_platform_edit_form_on_site_page Page callback for node/%node/platform-edit
hosting_platform_edit_form_on_site_page_access Wrapper for node_page_edit, for the "Edit Platform" tab.
hosting_platform_entity_property_info Implements hook_entity_property_info().
hosting_platform_field_extra_fields Implements hook_field_extra_fields()
hosting_platform_form Implements hook_form().
hosting_platform_form_alter Hide the delete button on platform nodes.
hosting_platform_form_platform_after_build After build callback for the hosting_platform_form.
hosting_platform_hosting_site_site_list_filters Implements hook_hosting_site_site_list_filters().
hosting_platform_hosting_summary Implements hook_hosting_summary().
hosting_platform_hosting_tasks Implements hook_hosting_tasks().
hosting_platform_hosting_task_guarded_nodes Implements hook_hosting_task_guarded_nodes().
hosting_platform_insert Implements hook_insert().
hosting_platform_load Implements hook_load().
hosting_platform_lock_action Callback for platform lock action.
hosting_platform_menu Implements hook_menu().
hosting_platform_node_access Implements hook_node_access().
hosting_platform_node_info Implements hook_node_info().
hosting_platform_node_presave Implements hook_node_presave()
hosting_platform_path_exists Determine whether a given path has already been used with an existing platform.
hosting_platform_permission Implements hook_permission().
hosting_platform_unlock_action Callback for platform unlock action.
hosting_platform_update Implements hook_update().
hosting_platform_validate Implements hook_validate().
hosting_platform_verify_action Callback for platform verify action.
hosting_platform_view Implements hook_view().
hosting_platform_views_api Implements hook_views_api().
hosting_platform_wildcard_load Menu wildcard loader callback.
_hosting_get_enabled_platforms Deprecated Helper function to get platforms that haven't been deleted or locked.
_hosting_get_locked_platforms Helper function to get platforms that have been locked.
_hosting_get_platforms Helper function to get platforms that haven't been deleted.
_hosting_platform_is_locked Helper function to check if a platform is locked.
_hosting_platform_list_class Define the classes that correspond to the platform status.
_hosting_platform_status Return the appropriate status label.
_hosting_platform_status_codes Helper function to map status codes to labels and classes.
_hosting_platform_status_codes_labels Platform status codes to human-readable label map.

Constants

Namesort descending Description
HOSTING_PLATFORM_DELETED This platform has been deleted.
HOSTING_PLATFORM_ENABLED This platform is created and enabled, sites may be deployed on it.
HOSTING_PLATFORM_LOCKED This platform has been locked, new sites will not be allowed to use it.
HOSTING_PLATFORM_QUEUED This platform has been queued for creation.