You are here

hosting_site.module in Hosting 7.4

Contains hook implementations for Hosting site module.

File

site/hosting_site.module
View source
<?php

/**
 * @file
 * Contains hook implementations for Hosting site module.
 */
use TQ\Git\Repository\Repository;
use GitWrapper\GitWrapper;
use GitWrapper\GitWorkingCopy;
use GitWrapper\GitException;
use DevShop\Component\PowerProcess\PowerProcess;

/**
 * This site is deleted from the backend.
 */
define('HOSTING_SITE_DELETED', -2);

/**
 * This site is disabled.
 */
define('HOSTING_SITE_DISABLED', -1);

/**
 * This site is queued for creation.
 */
define('HOSTING_SITE_QUEUED', 0);

/**
 * This site is created and enabled in the backend.
 */
define('HOSTING_SITE_ENABLED', 1);
include_once 'hosting_site.nodeapi.inc';
include_once 'hosting_site.form.inc';
include_once 'hosting_site.backups.inc';

//Conditonally include site quotas
if (module_exists('hosting_quota')) {
  include_once 'hosting_site.quota.inc';
}

/**
 * Implements hook_menu().
 */
function hosting_site_menu() {
  $items = array();
  $items['hosting/sites/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['hosting/sites/add'] = array(
    'title' => 'Add site',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_goto',
    'page arguments' => array(
      'node/add/site',
    ),
    'access callback' => 'node_access',
    'access arguments' => array(
      'create',
      'site',
    ),
  );
  $items['node/%node/platform-add-site'] = array(
    'title' => 'Add site',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'hosting_site_add_by_platform',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'hosting_site_add_by_platform_access',
    'access arguments' => array(
      1,
    ),
  );
  $items['hosting/hosting_site_form_check'] = array(
    'page callback' => '_hosting_site_form_check',
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'access content',
    ),
  );

  // Provide autocomplete functionality.
  $items['hosting/sites/autocomplete'] = array(
    'title' => 'Autocomplete for sites',
    'page callback' => 'hosting_site_autocomplete_sites',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['node/%node/goto_site'] = array(
    'page callback' => 'hosting_site_goto',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'node_access',
    'access arguments' => array(
      'view',
      1,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function hosting_site_theme($existing, $type, $theme, $path) {
  $items = array();
  $items['hosting_site_profile'] = array(
    'arguments' => array(
      'profile' => NULL,
      'html' => FALSE,
    ),
  );
  $items['hosting_site_goto_link'] = array(
    'arguments' => array(
      'title' => NULL,
      'url' => NULL,
    ),
  );
  return $items;
}
function _hosting_site_form_check() {
  $platform = NULL;
  if (isset($_GET['platform'])) {
    $platform = $_GET['platform'];
  }
  drupal_json_output(hosting_site_available_options($_POST, $platform));
  exit;
}

/**
 * Retrieve autocomplete suggestions for site names.
 *
 * @param $string
 *   The typed string so far, as typed in by the user.
 *
 * @return
 *   A list of matching sites in JSON format.
 */
function hosting_site_autocomplete_sites($string) {

  // Fetch the list of matching active sites. Don't use EntityFieldQuery here
  // as we'd have to load the nodes afterwards to get the field data. db_query()
  // is much faster as we can get away with only one DB query overall.
  $query = db_select('node', 'n');
  $query
    ->join('hosting_site', 's', 's.nid = n.nid');
  $results = $query
    ->fields('n', array(
    'nid',
    'title',
  ))
    ->condition('n.type', 'site')
    ->condition('n.title', '%' . db_like($string) . '%', 'LIKE')
    ->condition('s.status', HOSTING_SITE_DELETED, '!=')
    ->execute();

  // Save each result to the list.
  $matches = array();
  foreach ($results as $row) {
    $matches[$row->title] = check_plain($row->title);
  }

  // Return the results as a JSON list.
  drupal_json_output($matches);
}

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

/**
 * Generate a link allowing the user to log into their new site, or simply
 * go to the site front page if the link has expired.
 */
function _hosting_site_goto_link($node) {
  $cache = cache_get("hosting:site:" . $node->nid . ":login_link");
  if (user_access('create login-reset task') && $cache && REQUEST_TIME < $cache->data['expire']) {
    $title = t("Log in to !url", array(
      '!url' => hosting_site_canonical_url($node),
    ));
  }
  else {
    $title = t("Go to !url", array(
      '!url' => hosting_site_canonical_url($node),
    ));
  }
  $url = "node/" . $node->nid . "/goto_site";
  return theme('hosting_site_goto_link', array(
    'title' => $title,
    'url' => $url,
  ));
}

/**
 * Theme function for site link.
 */
function theme_hosting_site_goto_link($variables) {
  $options['attributes']['class'] = 'hosting-goto-site-link';
  $options['attributes']['target'] = '_blank';
  return l($variables['title'], $variables['url'], $options);
}

/**
 * Menu callback to go to your site.
 *
 * This needs to be a callback because the link is only going to work once,
 * so this will remove the link from the cache and just redirect to the site
 * not the login page.
 */
function hosting_site_goto($node) {
  $cid = "hosting:site:" . $node->nid . ":login_link";
  $cache = cache_get($cid);
  if (user_access('create login-reset task') && is_object($cache) && REQUEST_TIME < $cache->data['expire']) {
    $theurl = $cache->data['link'];
    cache_clear_all($cid, 'cache');
  }
  else {
    $theurl = _hosting_site_url($node);
  }
  drupal_goto($theurl);
  exit;
}

/**
 * Retrieve the port the site will be available on, so we can redirect.
 */
function hosting_site_get_port($node) {
  $platform = node_load($node->platform);
  $server = node_load($platform->web_server);
  return $server->services['http']->port;
}

/**
 * Get site's URL.
 */
function _hosting_site_url($node) {
  $schema = 'http';
  $port = NULL;
  $url = hosting_site_canonical_url($node);
  $platform = node_load($node->platform);
  $server = node_load($platform->web_server);
  if ($server->services['http']
    ->has_port()) {
    $port = $server->services['http']->port;
    if ($port == 80) {
      $port = NULL;
    }
  }

  // This is part of the ssl feature, but is better to implement here.
  // The 2 is a bit of a magic number, because we cant rely on the constants being available yet.
  // 2 == Only SSL is required, corresponding to HOSTING_SSL_REQUIRED and HOSTING_HTTPS_REQUIRED.
  $hosting_ssl_required = isset($node->ssl_enabled) && $node->ssl_enabled == 2;
  $hosting_https_required = isset($node->https_enabled) && $node->https_enabled == 2;
  if ($hosting_ssl_required || $hosting_https_required) {
    $schema = 'https';
    if ($server->services['http']
      ->has_port()) {
      $port = $server->services['http']->ssl_port;
      if ($port == 443) {
        $port = NULL;
      }
    }
  }
  if (is_numeric($port)) {
    return "{$schema}://{$url}:{$port}";
  }
  return "{$schema}://{$url}";
}

/**
 * Get a site's canonical URL.
 */
function hosting_site_canonical_url($node) {

  # Use the redirection target, if it's available.

  # Default to the site title, otherwise.
  $url = isset($node->redirection) && $node->redirection ? $node->redirection : $node->title;
  return hosting_site_clean_domain($url);
}

/**
 * Implements hook_hosting_tasks().
 */
function hosting_site_hosting_tasks() {
  $tasks = array();
  $tasks['site']['backup'] = array(
    'title' => t('Backup'),
    'description' => t('Generate a backup of this site that can be restored to at any time'),
    'dialog' => TRUE,
  );
  $tasks['site']['restore'] = array(
    'title' => t('Restore'),
    'description' => t('Restore this site to a previous backup. A new backup will be created before this is attempted.'),
    'dialog' => TRUE,
  );
  $tasks['site']['verify'] = array(
    'title' => t('Verify Site'),
    'description' => t('Confirm that the site has been correctly installed and regenerate all configuration files to match the hosting front end.'),
    'provision_save' => TRUE,
  );
  $tasks['site']['disable'] = array(
    'title' => t('Disable'),
    'description' => t('Disabling this site will stop it from being accessible.
         It can be enabled again later.'),
    'dialog' => TRUE,
  );
  $tasks['site']['enable'] = array(
    'title' => t('Enable'),
    'description' => t('Enabling this site will allow it to be accesible again.
         It may be disabled again if needed.'),
  );
  $tasks['site']['delete'] = array(
    'title' => t('Delete Site'),
    'description' => t('Deleting this site will completely remove it from the hosting system,
        but will keep the last backup available. This process can not be undone.
        Are you really sure you want to delete this site?'),
    'dialog' => TRUE,
  );
  $tasks['site']['login-reset'] = array(
    'title' => t('Reset password'),
    'description' => t('Generate a one-time login reset url for this site.'),
  );
  $tasks['site']['backup-delete'] = array(
    'title' => t('Delete backups'),
    'description' => t('Delete one or more backup files of a site.'),
    'dialog' => TRUE,
  );
  $tasks['site']['install'] = array(
    'title' => t('Install'),
    'description' => t('Install a site'),
    'provision_save' => TRUE,
    'dialog' => TRUE,
  );
  $tasks['site']['import'] = array(
    'title' => t('Import'),
    'description' => t('Import an existing site into Aegir'),
    'hidden' => TRUE,
  );
  $tasks['site']['deploy'] = array(
    'title' => t('Deploy'),
    'description' => t('Deploy new code and run pre- and post- deployment scripts.'),
    'dialog' => TRUE,
    'icon' => 'truck',
    'provision_save' => TRUE,
  );
  return $tasks;
}

/**
 * Implements hook_permission().
 */
function hosting_site_permission() {
  return array(
    'create site' => array(
      'title' => t('create site'),
    ),
    'view site' => array(
      'title' => t('view site'),
    ),
    'edit site' => array(
      'title' => t('edit site'),
    ),
    'delete site' => array(
      'title' => t('delete site'),
    ),
    'administer sites' => array(
      'title' => t('administer sites'),
    ),
  );
}

/**
 * Implements hook_node_access().
 */
function hosting_site_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 != 'site') {
    return NODE_ACCESS_IGNORE;
  }
  if ($op == 'create' && user_access('create site', $account)) {
    return NODE_ACCESS_ALLOW;
  }
  if ($op == 'update' && user_access('edit site', $account)) {
    return NODE_ACCESS_ALLOW;
  }
  if ($op == 'delete' && user_access('delete site', $account)) {
    return NODE_ACCESS_ALLOW;
  }
  if ($op == 'view' && user_access('view site', $account)) {
    return NODE_ACCESS_ALLOW;
  }
}

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

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

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

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

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

/**
 * Callback for site login reset action.
 *
 * @see: hosting_task_action_info().
 */
function hosting_site_op_login_reset($node) {
  hosting_add_task($node->nid, 'login-reset');
}

/**
 * Access callback for Site add by platform.
 */
function hosting_site_add_by_platform_access($node) {
  return $node->type == 'platform' && $node->platform_status == HOSTING_PLATFORM_ENABLED && node_access('create', 'site');
}

/**
 * Page callback for Site add by platform.
 */
function hosting_site_add_by_platform($node) {
  drupal_goto('node/add/site', array(
    'query' => array(
      'platform' => $node->nid,
    ),
  ));
}

/**
 * Returns a count of sites.
 *
 * This is used by cron and statistics to batch the number of sites that are
 * processed with each call. It is also used to generate the 'site count' per
 * platform at /hosting/platforms
 *
 * By default it only counts enabled sites.
 *
 * Optionally can include an array of site statuses by which to consider in the
 * count.
 *
 * @param $platform
 *   (optional) A platform nid to only count sites on that specific platform.
 * @param $statuses
 *   (optional) An array of site statuses (defined by their constants) by which
 *   to include such sites in the site count.
 *
 * @return
 *   The number of sites matching the specified criteria.
 */
function hosting_site_count($platform = NULL, $statuses = NULL) {
  if (is_null($statuses)) {
    $statuses = array(
      HOSTING_SITE_ENABLED,
    );
  }
  $query = db_select('hosting_site')
    ->addTag('hosting_site_count')
    ->fields('hosting_site', array(
    'nid',
  ));
  if (count($statuses)) {
    $query
      ->condition('status', $statuses);
  }
  if (!is_null($platform)) {
    $query
      ->condition('platform', $platform);
  }
  return $query
    ->countQuery()
    ->execute()
    ->fetchField();
}

/**
 * Retrieve sites on a specific platform, with a specific status
 */
function hosting_get_sites_by_status($platform, $status) {
  $nodes = array();
  $result = db_query("SELECT nid FROM {hosting_site} WHERE platform = :platform AND status = :status", array(
    ':platform' => $platform,
    ':status' => $status,
  ));
  while ($nid = $result
    ->fetch()) {
    $nodes[$nid->nid] = node_load($nid->nid);
  }
  return $nodes;
}

/**
 * Retrieves a site node based on the URL.
 *
 * @param string $url
 *   The URL of the site.
 * @param boolean $loaded_object
 *   Determines if a loaded site should be returned, or just the node ID.
 *   Defaults to TRUE.
 * @return
 *   If $loaded_object is TRUE, a fully loaded site node object.
 *   If $loaded_object is FALSE, the node ID of the site.
 *   In either case, if the site can't be found, FALSE is returned.
 * @todo
 *   Remove the db_query() and have only one EntityFieldQuery. The two
 *   different cases will be handled by adding different conditions/tags to it.
 *   A new tag will have to be added to match the title for aliases. These tags
 *   won't be necessary in Drupal 8 as JOINs are natively supported. See
 *   https://www.drupal.org/node/1882418 for details.
 */
function hosting_get_site_by_url($url, $loaded_object = TRUE) {

  // If the Aliases feature is enabled, try and get the site by its alias too
  if (hosting_feature('alias')) {
    $nid = db_query("SELECT n.nid\n                       FROM {node} n\n                       JOIN {hosting_site} h\n                         ON n.nid = h.nid\n                  LEFT JOIN {hosting_site_alias} ha\n                         ON h.vid = ha.vid\n                      WHERE (n.title = :title OR ha.alias = :alias)\n                        AND n.type = :type\n                        AND NOT (h.status = :status)", array(
      ':title' => $url,
      ':alias' => $url,
      ':type' => 'site',
      ':status' => HOSTING_SITE_DELETED,
    ))
      ->fetchField();
  }
  else {

    // Get the list of node IDs whose title matches the name.
    $query = new EntityFieldQuery();
    $entities = $query
      ->entityCondition('entity_type', 'node')
      ->propertyCondition('type', 'site')
      ->propertyCondition('title', $url)
      ->addTag('site_not_deleted')
      ->range(0, 1)
      ->execute();

    // Get the ID of the single node, if there is one.
    if (!empty($entities['node'])) {

      // Fetch the node ID from the list and return it.
      $nids = array_keys($entities['node']);
      $nid = array_shift($nids);
    }
    else {
      $nid = FALSE;
    }
  }
  if ($nid) {
    return $loaded_object ? node_load($nid) : $nid;
  }
  return FALSE;
}

/**
 * Implements hook_query_TAG_alter() for the 'site_not_deleted' tag.
 *
 * Ensures that only non-deleted sites are returned in the query.
 *
 * @see hosting_get_site_by_url()
 */
function hosting_site_query_site_not_deleted_alter(QueryAlterableInterface $query) {
  $query
    ->join('hosting_site', 'site', 'site.nid = node.nid');
  $query
    ->condition('site.status', HOSTING_SITE_DELETED, '!=');
}

/**
 * Define the status types of a site
 */
function _hosting_site_status($node) {
  $status = is_numeric($node) ? $node : $node->site_status;
  $labels = hosting_site_status_codes('label');
  return $labels[$status];
}

/**
 * Site status codes to human readable label mappings.
 *
 * @see hosting_site_status_codes
 */
function hosting_site_status_codes_labels() {
  return hosting_site_status_codes('label');
}

/**
 * Define the status type labels and classes.
 *
 * @param $type
 *   The type of status to return ('label' or 'class').
 *
 * @return array
 */
function hosting_site_status_codes($type = NULL) {
  static $codes = array(
    HOSTING_SITE_QUEUED => array(
      'label' => 'Queued',
      'class' => 'hosting-queue',
    ),
    HOSTING_SITE_ENABLED => array(
      'label' => 'Enabled',
      'class' => 'hosting-success',
    ),
    HOSTING_SITE_DELETED => array(
      'label' => 'Deleted',
      'class' => 'hosting-error',
    ),
    HOSTING_SITE_DISABLED => array(
      'label' => 'Disabled',
      'class' => 'hosting-disable',
    ),
  );
  if (!is_null($type)) {

    // Return just the $type
    $return = array();
    foreach ($codes as $code => $types) {
      $return[$code] = $types[$type];
    }
    return $return;
  }
  else {

    // Return everything
    return $codes;
  }
}

/**
 * Implements hook_allow_domain().
 *
 * Disallow domains already used as any site's title/url, unless the site has
 * been deleted.
 *
 * @see hosting_domain_allowed()
 */
function hosting_site_allow_domain($url, $params = array()) {
  $query = "SELECT COUNT(n.nid) FROM {node} n\n    JOIN {hosting_site} h ON n.nid = h.nid\n    WHERE type = 'site' AND n.title = :title AND h.status <> :status";
  $args[':title'] = $url;
  $args[':status'] = HOSTING_SITE_DELETED;
  if (isset($params['nid'])) {
    $query .= " AND n.nid <> :nid";
    $args[':nid'] = $params['nid'];
  }
  $result = !db_query($query, $args)
    ->fetchField();
  return $result;
}

/**
 * @todo Please document this function.
 * @see http://drupal.org/node/1354
 */
function hosting_site_task_status($nid) {
  return hosting_task_status_output('nid', $nid, 'install');
}

/**
 * Define the classes that correspond to the site status.
 * @see _hosting_site_status()
 */
function _hosting_site_list_class($node, $verified = NULL) {
  $status = is_numeric($node) ? $node : $node->site_status;
  $classes = hosting_site_status_codes('class');
  if ($status == HOSTING_SITE_ENABLED && $verified <= 1) {
    return 'hosting-warning';
  }
  return $classes[$status];
}

/**
 * Views integration.
 */
function hosting_site_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'hosting_site') . '/includes/views',
  );
}

/**
 * Theme function for install profile item.
 */
function theme_hosting_site_profile($variables) {
  if ($variables['html']) {
    return "{$variables['profile']->title} <em>({$variables['profile']->short_name})</em>";
  }
  return "{$variables['profile']->title} ({$variables['profile']->short_name})";
}

/**
 * Theme function for site titles.
 */
function hosting_site_preprocess(&$variables) {
  if (isset($variables['node'])) {
    $variables['title'] = hosting_site_canonical_url($variables['node']);
  }
}

/**
 * Control block visibility.
 */
function hosting_site_client_list_block_visibility() {
  global $user;
  $node = menu_get_object();
  $menu_item = menu_get_item();
  if (!empty($node) && $menu_item['number_parts'] == 2) {
    return $node->type == 'client' && !user_access('administer clients', $user);
  }
  return FALSE;
}

/**
 * Control block visibility.
 */
function hosting_site_client_admin_block_visibility() {
  global $user;
  $node = menu_get_object();
  $menu_item = menu_get_item();
  if (!empty($node) && $menu_item['number_parts'] == 2) {
    return $node->type == 'client' && user_access('administer clients', $user);
  }
  return FALSE;
}

/**
 * Control block visibility.
 */
function hosting_site_profile_block_visibility() {
  $node = menu_get_object();
  $menu_item = menu_get_item();
  if (!empty($node) && $menu_item['number_parts'] == 2) {
    return $node->type == 'package' && $node->package_type == 'profile';
  }
  return FALSE;
}

/**
 * Implements hook_block_view_alter().
 *
 * @see: hosting_site_views_pre_build().
 */
function hosting_site_block_view_alter(&$data, $block) {
  if ($block->delta == 'hosting_site_list-block_sites') {
    if ($node = menu_get_object()) {

      // We only want to show a list of sites on a server with a database,
      // or on install profile package pages.
      if ($node->type == 'server' && !array_key_exists('db', $node->services) || $node->type == 'package' && $node->package_type != 'profile') {
        unset($data['subject']);
        unset($data['content']);
      }
    }
  }
}

/**
 * Implements hook_action_info_alter().
 */
function hosting_site_action_info_alter(&$actions) {
  if (variable_get('hosting_require_disable_before_delete', TRUE)) {
    unset($actions['hosting_site_delete_action']);
  }
}

/**
 * Implements hook_hosting_task_guarded_nodes().
 */
function hosting_site_hosting_task_guarded_nodes() {
  return array(
    hosting_get_hostmaster_site_nid(),
  );
}

/**
 * Confirmation form for site deploy.
 *
 * @see hosting_task_confirm_form().
 */
function hosting_task_deploy_form($node) {
  $form = array();
  if ($node->type == 'site') {
    try {
      $wrapper = new GitWrapper();
      $git = $wrapper
        ->workingCopy($node->git_root);
      $diff = $git
        ->diff()
        ->getOutput();
      $status = $git
        ->getStatus();
    } catch (GitException $e) {
      drupal_set_message($e
        ->getMessage(), 'error');
    }
    $two_col_classes = array(
      'col-sm-6',
      'col-md-6',
      'col-lg-6',
    );
    $form['info'] = array(
      '#type' => 'container',
      '#attributes' => array(
        'class' => array(
          'container',
        ),
      ),
      'current' => array(
        '#type' => 'container',
        '#attributes' => array(
          'class' => $two_col_classes,
        ),
      ),
      'target' => array(
        '#type' => 'container',
        '#attributes' => array(
          'class' => $two_col_classes,
        ),
      ),
    );
    $form['info']['current']['current_git_reference'] = array(
      '#title' => t('Current Git Reference'),
      '#type' => 'item',
      '#markup' => $node->git_reference,
      '#description' => t('The current branch, tag, or SHA of the site code at &path.', array(
        '&path' => $node->git_root,
      )),
      '#field_prefix' => '<div class="label label-default git-ref-label">',
      '#field_suffix' => '</div>',
    );

    // Allow the user to change git_reference here.
    $form['info']['target']['target_git_reference'] = array(
      '#title' => t('Target Git Reference'),
      '#type' => 'textfield',
      '#default_value' => $_GET['git_reference'] ?: $node->git_reference,
      '#description' => t('Enter the desired git branch, tag, or SHA. If a branch is entered, the git repository will pulled and reset to match the remote repository. If a tag or SHA is entered, it will be checked out, resulting in a "DETACHED HEAD" state.'),
      '#parents' => array(
        'parameters',
        'target_git_reference',
      ),
      '#attributes' => array(
        'class' => array(
          'col-md-6',
        ),
      ),
    );

    /**
     * @TODO:
    // Offer users ability to control commands that will run during this deployment.
    $form['hooks'] = array(
      '#type' => 'container',
      'title' => array(
        '#prefix' => '<h4>',
        '#markup' => t('Deployment Phases'),
        '#suffix' => '</h4>',
      ),
      'description' => array(
        '#markup' => t('Choose what commands will be run during this deploy.'),
      ),
      '#attributes' => array(
        'class' => array(
          'alert alert-info'
        ),
      ),
    );
    $form['hooks']['checkout'] = array(
      '#title' => _hosting_task_deploy_command_form_title('git', $node->commands),
      '#type' => 'checkbox',
      '#default_value' => TRUE,
      '#disabled' => TRUE,
      '#parents' => array(
        'parameters', 'build',
      ),
    );
    $form['hooks']['build'] = array(
      '#title' => _hosting_task_deploy_command_form_title('build', $node->commands),
      '#type' => 'checkbox',
      // @TODO: Use site default.
      '#default_value' => TRUE,
      '#parents' => array(
        'parameters', 'build',
      ),
    );
    $form['hooks']['install'] = array(
      '#title' => _hosting_task_deploy_command_form_title('install', $node->commands),
      '#type' => 'checkbox',
      '#parents' => array(
        'parameters', 'install',
      ),
    );
    $form['hooks']['deploy'] = array(
      '#title' => _hosting_task_deploy_command_form_title('deploy', $node->commands),
      '#type' => 'checkbox',
      '#default_value' => TRUE,
      '#parents' => array(
        'parameters', 'deploy',
      ),
    );
    $form['hooks']['test'] = array(
      '#title' => _hosting_task_deploy_command_form_title('test', $node->commands),
      '#type' => 'checkbox',
      '#parents' => array(
        'parameters', 'test',
      ),
    );

    // Check for working copy changes and offer to reset them for the user.
    $form['danger_zone'] = array(
      '#type' => 'container',
      'title' => array(
        '#prefix' => '<h4>',
        '#markup' => t('DANGER ZONE'),
        '#suffix' => '</h4>',
      ),
      '#attributes' => array(
        'class' => array(
          'alert alert-danger'
        ),
      ),
      '#states' => array(
        'visible' => array(
          ':input[name="parameters[install]"]' => array('checked' => TRUE),
        ),
      ),
    );
    $form['danger_zone']['git_reset'] = array(
      '#title' => t('Discard working copy changes.'),
      '#type' => 'checkbox',
      '#default_value' => FALSE,
      '#description' => t('Uncommitted changes were detected in the codebase. Check this box to discard changes to the code before attempting to deploy. If you do not, a "git checkout" command will still be attempted, and any errors will be logged.'),
      '#parents' => array(
        'parameters', 'git_reset'
      ),
      '#access' => !empty($diff) || !empty($status),
    );
    /**
     * @TODO: This was built with assumption deploy stages was available.
    $form['danger_zone']['confirm_destroy'] = array(
      '#title' => t('I understand that all data in the site !link will be destroyed.', array(
        '!link' => l($node->title, $node->title),
      )),
      '#type' => 'checkbox',
      '#default_value' => FALSE,
      '#description' => t('The site will be destroyed before the <code>install</code> command is run.'),
      '#parents' => array(
        'confirm_destroy'
      ),
      '#states' => array(
        'visible' => array(
          ':input[name="parameters[install]"]' => array('checked' => TRUE),
        ),
      ),
    );
    */
  }
  $form['git_info'] = array(
    '#title' => t('View git status in &path', array(
      '&path' => $node->git_root,
    )),
    '#type' => 'fieldset',
    '#collapsed' => true,
    '#collapsible' => true,
    'status' => array(
      '#prefix' => '<h4>' . t('Git Status') . '</h4><pre>',
      '#markup' => $status,
      '#suffix' => '</pre>',
      '#access' => !empty($status),
    ),
    'diff' => array(
      '#prefix' => '<h4>' . t('Git diff') . '</h4><pre>',
      '#markup' => $diff,
      '#suffix' => '</pre>',
      '#access' => !empty($diff),
    ),
    'clean' => array(
      '#prefix' => '<h4>',
      '#markup' => t('Git working copy is clean. No changes.'),
      '#suffix' => '</h4>',
      '#access' => empty($diff) && empty($status),
    ),
  );
  return $form;
}

/**
 * Output friendly looking HTML after a deploy checkbox.
 * @param $command_name
 * @param $commands array The full array of commands.
 */
function _hosting_task_deploy_command_form_title($command_name, $commands) {
  $command = isset($commands[$command_name]) ? $commands[$command_name] : '';
  $command_title_text = '<span class="command-label"><code>!command_name</code></span> !command';
  $missing_prefix = '<span class="alert alert-warning alert-inline"><i class="fa fa-warning"></i> ';
  $missing_text = 'MISSING. Add desired command to <code>composer.json:extra.devshop.commands.!name</code>';
  $missing_suffix = '</span>';
  return t($command_title_text, array(
    '!command_name' => $command_name,
    '!command' => $command ? '<kbd>' . $command . '</kbd>' : $missing_prefix . t($missing_text, array(
      '!name' => $command_name,
    )) . $missing_suffix,
  ));
}

/**
 * Confirmation form for site backups.
 *
 * @see hosting_task_confirm_form().
 */
function hosting_task_delete_form($node) {
  $form = array();
  if ($node->type == 'site') {
    $form['confirm'] = array(
      '#title' => t('Confirm: All data in this site will be destroyed. Check this box to confirm your intentions to do this.'),
      '#type' => 'checkbox',
      '#required' => TRUE,
      '#description' => t('WARNING: Taking this action will destroy all data in the site !link.', array(
        '!link' => l($node->title, $node->title, array(
          'options' => array(
            'attributes' => array(
              'target' => '_blank',
            ),
          ),
        )),
      )),
    );
  }
  return $form;
}

/**
 * Allow other modules to alter the domain.
 */
function hosting_site_get_domain($domain) {
  drupal_alter('hosting_site_domain', $domain);
  return $domain;
}

/**
 * Implements hook_hosting_site_domain_alter().
 */
function hosting_site_hosting_site_domain_alter(&$domain) {
  $domain = hosting_site_clean_domain($domain);
}

/**
 * Ensure domains are properly formatted.
 */
function hosting_site_clean_domain($domain) {

  // Domain names are case-insensitive.
  return strtolower(trim($domain));
}

Functions

Namesort descending Description
hosting_get_sites_by_status Retrieve sites on a specific platform, with a specific status
hosting_get_site_by_url Retrieves a site node based on the URL.
hosting_site_action_info_alter Implements hook_action_info_alter().
hosting_site_add_by_platform Page callback for Site add by platform.
hosting_site_add_by_platform_access Access callback for Site add by platform.
hosting_site_allow_domain Implements hook_allow_domain().
hosting_site_autocomplete_sites Retrieve autocomplete suggestions for site names.
hosting_site_backup_action Callback for site backup action.
hosting_site_block_view_alter Implements hook_block_view_alter().
hosting_site_canonical_url Get a site's canonical URL.
hosting_site_clean_domain Ensure domains are properly formatted.
hosting_site_client_admin_block_visibility Control block visibility.
hosting_site_client_list_block_visibility Control block visibility.
hosting_site_count Returns a count of sites.
hosting_site_delete_action Callback for site delete action.
hosting_site_disable_action Callback for site disable action.
hosting_site_enable_action Callback for site enable action.
hosting_site_get_domain Allow other modules to alter the domain.
hosting_site_get_port Retrieve the port the site will be available on, so we can redirect.
hosting_site_goto Menu callback to go to your site.
hosting_site_hosting_site_domain_alter Implements hook_hosting_site_domain_alter().
hosting_site_hosting_tasks Implements hook_hosting_tasks().
hosting_site_hosting_task_guarded_nodes Implements hook_hosting_task_guarded_nodes().
hosting_site_menu Implements hook_menu().
hosting_site_node_access Implements hook_node_access().
hosting_site_op_login_reset Callback for site login reset action.
hosting_site_permission Implements hook_permission().
hosting_site_preprocess Theme function for site titles.
hosting_site_profile_block_visibility Control block visibility.
hosting_site_query_site_not_deleted_alter Implements hook_query_TAG_alter() for the 'site_not_deleted' tag.
hosting_site_status_codes Define the status type labels and classes.
hosting_site_status_codes_labels Site status codes to human readable label mappings.
hosting_site_task_status @todo Please document this function.
hosting_site_theme Implements hook_theme().
hosting_site_verify_action Callback for site verify action.
hosting_site_views_api Views integration.
hosting_site_wildcard_load Menu wildcard loader callback.
hosting_task_delete_form Confirmation form for site backups.
hosting_task_deploy_form Confirmation form for site deploy.
theme_hosting_site_goto_link Theme function for site link.
theme_hosting_site_profile Theme function for install profile item.
_hosting_site_form_check
_hosting_site_goto_link Generate a link allowing the user to log into their new site, or simply go to the site front page if the link has expired.
_hosting_site_list_class Define the classes that correspond to the site status.
_hosting_site_status Define the status types of a site
_hosting_site_url Get site's URL.
_hosting_task_deploy_command_form_title Output friendly looking HTML after a deploy checkbox.

Constants

Namesort descending Description
HOSTING_SITE_DELETED This site is deleted from the backend.
HOSTING_SITE_DISABLED This site is disabled.
HOSTING_SITE_ENABLED This site is created and enabled in the backend.
HOSTING_SITE_QUEUED This site is queued for creation.