hosting.module in Hosting 7.4
Same filename and directory in other branches
Hosting module.
Contains just about all the interface magic of hostmaster.
File
hosting.moduleView source
<?php
/**
* @file
* Hosting module.
*
* Contains just about all the interface magic of hostmaster.
*/
/**
* The maximum length for a site alias.
* Leaving room to append '.alias.drushrc.php' and keep filenames below 255.
*/
define('HOSTING_MAX_ALIAS_LENGTH', 235);
/**
* A serial queue type.
*
* This queue will be processed at a specified interval. When the interval has
* elapsed a configurable number of the items will be processed.
*/
define('HOSTING_QUEUE_TYPE_SERIAL', 'serial');
/**
* A batch queue type.
*
* This queue type aims to process repeatable tasks spread out over a
* configurable interval.
*
* See the processing code in hosting_get_queues() for the exact
* implementation.
*
* This queue type will:
* - Process items in the queue at some small number of specific points, evenly spaced throughout
* the configurable interval.
* - Process as many items at those intervals so that all the items are
* processed at least once over the configurable interval.
*
* @see HOSTING_QUEUE_TYPE_SPREAD
*/
define('HOSTING_QUEUE_TYPE_BATCH', 'batch');
/**
* A spread queue type.
*
* This queue type aims to process repeatable tasks spread out over a
* configurable interval.
*
* See the processing code in hosting_get_queues() for the exact
* implementation.
*
* This queue type will attempt to:
* - Process items at as many points as possible evenly spread over the
* configurable interval.
* - Points will be at least 1 minute apart to allow for the default dispatcher to execute them.
* - At each point process the minimum number of items needed to result in all
* items being processed at least once over the configurable interval.
*
* @see HOSTING_QUEUE_TYPE_BATCH
*/
define('HOSTING_QUEUE_TYPE_SPREAD', 'spread');
/**
* Not split for performance reasons. Just to keep code together.
*/
include_once dirname(__FILE__) . '/hosting.inc';
include_once dirname(__FILE__) . '/hosting.queues.inc';
include_once dirname(__FILE__) . '/hosting.features.inc';
/**
* Implements hook_menu().
*/
function hosting_menu() {
global $user;
$items = array();
$items['hosting/disabled'] = array(
'title' => 'Site disabled',
'page callback' => 'hosting_disabled_site',
'access arguments' => array(
'access disabled sites',
),
'type' => MENU_CALLBACK,
);
$items['hosting/js'] = array(
'title' => 'ahah callback',
'page callback' => 'hosting_js_page',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items['hosting/maintenance'] = array(
'title' => 'Site maintenance',
'page callback' => 'hosting_site_maintenance',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items['admin/hosting'] = array(
'title' => 'Hosting',
'description' => 'Configure and manage the hosting system',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hosting_features_form',
),
'access arguments' => array(
'administer hosting',
),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/hosting/features'] = array(
'title' => 'Features',
'description' => 'Configure the exposed functionality of the Hosting system',
'weight' => -100,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hosting_features_form',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'access arguments' => array(
'administer hosting features',
),
);
$items['admin/hosting/queues'] = array(
'title' => 'Queues',
'description' => 'Configure the frequency that cron, backup and task events are process',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hosting_queues_configure',
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer hosting queues',
),
);
$items['admin/hosting/settings'] = array(
'title' => 'Settings',
'description' => 'Configure general Hosting settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hosting_settings',
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer hosting settings',
),
);
$items['hosting/queues'] = array(
'page callback' => 'hosting_queues',
'type' => MENU_CALLBACK,
'access arguments' => array(
'access task logs',
),
);
$items['hostmaster'] = array(
'title' => 'Hostmaster',
'description' => 'View and configure the Hostmaster site.',
'page callback' => 'hosting_menu_hostmaster_redirect',
'access callback' => 'hosting_menu_hostmaster_access',
'menu_name' => 'main-menu',
'type' => MENU_NORMAL_ITEM,
'weight' => 100,
);
return $items;
}
/**
* Access hook for /hostmaster callback page. Checks the access for hostmaster node page.
* @return bool
*/
function hosting_menu_hostmaster_access() {
return drupal_valid_path('node/' . hosting_get_hostmaster_nid());
}
/**
* Send the user from /hostmaster to /hosting/c/hostmaster
*/
function hosting_menu_hostmaster_redirect() {
drupal_goto('hosting/c/hostmaster');
}
/**
* Page callback for a page to be rendered in modal frame.
*/
function hosting_js_page() {
modalframe_child_js();
$args = func_get_args();
$path = implode('/', $args);
// In case Aegir is running in a sub-directory, the base_path should be
// stripped away here. Remove the leading slash from base_path as $path
// doesn't have one either.
$path = str_replace(ltrim(base_path(), '/'), '', $path);
menu_set_active_item($path);
if ($router_item = menu_get_item($path)) {
if ($router_item['access']) {
if ($router_item['file']) {
require_once $router_item['file'];
}
$return = call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
}
else {
$return = drupal_access_denied();
}
}
// Menu status constants are integers; page content is a string.
if (is_int($return) && module_exists('overlay')) {
overlay_close_dialog();
}
return $return;
}
/**
* Implements hook_menu_alter().
*/
function hosting_menu_alter(&$items) {
$items['node/add']['page callback'] = '_hosting_node_add';
$types = hosting_feature_node_types(TRUE);
foreach ($types as $feature => $type) {
$path = sprintf('node/add/%s', str_replace('_', '-', $type));
$items[$path]['access callback'] = 'hosting_menu_access';
$items[$path]['access arguments'] = array(
str_replace('_', ' ', $type),
$feature,
);
}
// These node types should remain hidden, and provide no user interface.
$items['node/add/package']['page callback'] = 'drupal_access_denied';
$items['node/add/task']['page callback'] = 'drupal_access_denied';
$items['node/add/package']['type'] = MENU_CALLBACK;
$items['node/add/task']['type'] = MENU_CALLBACK;
// Make drupal node pages more user-friendly.
$items['node/%node/view']['title callback'] = 'hosting_node_tab_title';
$items['node/%node/view']['title arguments'] = array(
'View',
1,
);
$items['node/%node/view']['weight'] = -100;
$items['node/%node/edit']['title callback'] = 'hosting_node_tab_title';
$items['node/%node/edit']['title arguments'] = array(
'Edit',
1,
);
$items['node/%node/edit']['weight'] = -99;
}
/**
* Replace the text used on node page "tabs".
*/
function hosting_node_tab_title($default, $node) {
// Use "X Dashboard" and "X Settings" for nodes that are hosting contexts.
$type_names = node_type_get_names();
if (!empty($node->hosting_name)) {
switch ($default) {
case "View":
return t('@type Dashboard', [
'@type' => $type_names[$node->type],
]);
case "Edit":
return t('@type Settings', [
'@type' => $type_names[$node->type],
]);
}
}
// Use "Task Log" for the "View" tab of task nodes.
if ($default == 'View' && $node->type == 'task') {
return t('Task Logs');
}
// Otherwise, just return the page text
return t($default);
}
/**
* Menu access callback for creating a node provided by a Hosting feature.
*
* @param string $type
* The node type that the user wants to create.
* @param string $feature
* The machine name of the host feature that should be additionally checked to
* see if it's enabled.
*
* @return bool
* TRUE if the user can access, FALSE otherwise.
*/
function hosting_menu_access($type, $feature) {
global $user;
return ($user->uid == 1 || user_access('create ' . $type)) && hosting_feature($feature) != HOSTING_FEATURE_DISABLED;
}
/**
* Page callback for a site that has been disabled.
*/
function hosting_disabled_site() {
drupal_set_breadcrumb(array());
return t("This site has been disabled by the site administrators");
}
/**
* Page callback for a site that is undergoing maintenance.
*/
function hosting_site_maintenance() {
drupal_set_breadcrumb(array());
return t("This site is currently in maintenance. Check back later.");
}
/**
* Implements hook_node_delete().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_delete($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'delete');
}
/**
* Implements hook_node_insert().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_insert($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'insert');
}
/**
* Implements hook_node_load().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_load($nodes, $types) {
// Decide whether any of $types are relevant to our purposes.
// Gather our extra data for each of these nodes.
$result = db_query("SELECT nid, name AS hosting_name FROM {hosting_context} WHERE nid IN(:nids)", array(
':nids' => array_keys($nodes),
))
->fetchAllKeyed();
// Add our extra data to the node objects.
foreach ($result as $nid => $hosting_name) {
$nodes[$nid]->hosting_name = $hosting_name;
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($nodes[$nid], 'load');
}
}
/**
* Implements hook_node_prepare().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_prepare($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'prepare');
}
/**
* Implements hook_node_presave().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_presave($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'presave');
}
/**
* Implements hook_node_revision_delete().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_revision_delete($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'delete revision');
}
/**
* Implements hook_node_search_result().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_search_result($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'search result');
}
/**
* Implements hook_node_update().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_update($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'update');
}
/**
* Implements hook_node_update_index().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_update_index($node) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'update index');
}
/**
* Implements hook_node_validate().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_validate($node, $form, &$form_state) {
// Redispatch through our custom nodeapi implementation.
hosting_nodeapi($node, 'validate', $form);
}
/**
* Implements hook_node_view().
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_node_view($node, $view_mode, $langcode) {
// Redispatch through our custom nodeapi implementation.
if ($view_mode == 'teaser') {
hosting_nodeapi($node, 'view', TRUE, FALSE);
}
elseif ($view_mode == 'full') {
hosting_nodeapi($node, 'view', FALSE, TRUE);
}
}
/**
* Redirects to hosting_nodeapi_$nodetype_$op calls.
*
* To save ourselves from an incessant amount of intricately nested code,
* and allow easier extension and maintenance.
*
* @see hook_nodeapi_TYPE_OP()
*/
function hosting_nodeapi(&$node, $op, $arg1 = NULL, $arg2 = NULL) {
// We need to practically re-invent module_invoke_all here because
// we have to pass the node as a reference.
$return = array();
$hook = "nodeapi_" . $node->type . "_" . str_replace(" ", "_", $op);
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
$result = $function($node, $arg1, $arg2);
if (isset($result) && is_array($result)) {
$return = array_merge_recursive($return, $result);
}
elseif (isset($result)) {
$return[] = $result;
}
}
return $return;
}
/**
* Implements hook_permission().
*/
function hosting_permission() {
return array(
'access hosting wizard' => array(
'title' => t('access hosting wizard'),
'description' => t("TODO Remove 'access hosting wizard', as it is not checked anywhere."),
),
'administer hosting queues' => array(
'title' => t('administer hosting queues'),
'description' => t('Configure the frequency that cron, backup and task events are process.'),
),
'administer hosting features' => array(
'title' => t('administer hosting features'),
'description' => t('Configure the exposed functionality of the hosting system.'),
),
'administer hosting' => array(
'title' => t('administer hosting'),
'description' => t('Configure and manage the hosting system.'),
),
'access disabled sites' => array(
'title' => t('access disabled sites'),
'description' => t('Access disabled sites.'),
),
'administer hosting settings' => array(
'title' => t('administer hosting settings'),
'description' => t('Configure general hosting settings.'),
),
);
}
/**
* Per node type description text. To be stored in the node_type table.
*
* @param string $type
* The node type.
*
* @return string
* Description text for the node type.
*/
function hosting_node_help($type) {
switch ($type) {
case 'site':
return t("<strong>An instance of a hosted site.</strong>\nIt contains information relating to the site, most notably the domain name, database server and platform it is being published on. A site may also have several aliases for additional domains the site needs to be accessible on.");
case 'platform':
return t("<strong>The file system location on a specific web server on which to publish sites.</strong>\nMultiple platforms can co-exist on the same web server, and need to do so for upgrades to be managed, as this is accomplished by migrating the site between platforms. Platforms are most commonly built for specific releases of Drupal.");
case 'client':
return t("<strong>The person or group that runs the site.</strong>\nThis information is usually required for billing and access purposes, to ensure that only certain people are able to view the information for sites they run. If you do not intend on having more than one client access the system, you will not need to create any additional clients for your purposes.");
case 'server':
return t("<strong>The physical machine which will provide various services to your site.</strong>\n Each server can have multiple services associated to it. To host a site you require a database service and a web service, which can either be provided by a single or multiple servers.");
case 'task':
return t("<strong>The mechanism whereby Hostmaster keeps track of all changes that occur to the system.</strong>\nEach task acts as a command for the back-end, and contains a full log of all changes that have occurred. If a task should fail, the administrator will be notified with an explanation of exactly what went wrong, and how to fix it.");
case 'package':
return t("PLACEHOLDER");
}
}
/**
* Implements hook_init().
*/
function hosting_init() {
static $already_called = FALSE;
if ($already_called) {
return;
}
$already_called = TRUE;
// Definitions for the default platforms, clients etc.
// Done to avoid using 'magic numbers'
define('HOSTING_DEFAULT_CLIENT', variable_get('hosting_default_client', 1));
define('HOSTING_DEFAULT_DB_SERVER', variable_get('hosting_default_db_server', 4));
define('HOSTING_DEFAULT_WEB_SERVER', variable_get('hosting_default_web_server', 3));
// This client has access to everything, see hosting_client.access.inc
define('HOSTING_ADMIN_CLIENT', variable_get('hosting_admin_client', 1));
define('HOSTING_OWN_DB_SERVER', variable_get('hosting_own_db_server', 2));
define('HOSTING_OWN_WEB_SERVER', variable_get('hosting_own_web_server', 3));
define('HOSTING_OWN_PLATFORM', variable_get('hosting_own_platform', 6));
// Find the base URL, this is used by the initial 'hosting-setup' Drush
// command. This gets defined in the bootstrap, so just using the global
// definition.
define('HOSTING_DEFAULT_BASE_URL', $GLOBALS['base_url']);
// Moved from hook_menu()
drupal_add_css(drupal_get_path('module', 'hosting') . '/hosting.css');
}
/**
* Implements hook_theme().
*/
function hosting_theme() {
return array(
'hosting_queues_configure' => array(
'file' => 'hosting.module',
'render element' => 'form',
),
'hosting_feature_dependencies' => array(
'file' => 'hosting.features.inc',
'arguments' => array(
'dependencies' => NULL,
'prefix' => NULL,
'features' => NULL,
),
),
);
}
/**
* Generates a link to a node.
*/
function _hosting_node_link($nid, $title = NULL) {
if (is_null($nid)) {
return t("None");
}
$node = node_load($nid);
if (!isset($title)) {
$title = isset($node->title) ? $node->title : NULL;
// Override with the redirect target, when available.
$title = $node->type == 'site' ? hosting_site_canonical_url($node) : $title;
// Override with the human-readable name, when available.
$title = isset($node->human_name) && strlen($node->human_name) ? $node->human_name : $title;
}
$title = filter_xss($title);
if (isset($node->nid)) {
return node_access('view', $node) ? l($title, "node/" . $node->nid) : $title;
}
}
/**
* Implements hook_block_info().
*/
function hosting_block_info() {
$blocks['hosting_summary'] = array(
'info' => t('Hosting summary'),
'enabled' => 1,
'status' => 1,
'region' => 'sidebar_second',
'weight' => 10,
);
$blocks['hosting_queues'] = array(
'info' => t('Hosting queues'),
'enabled' => 1,
'status' => 1,
'region' => 'sidebar_first',
'weight' => 0,
);
$blocks['hosting_queues_summary'] = array(
'info' => t('Hosting queues summary'),
'enabled' => 1,
'status' => 1,
'region' => 'sidebar_first',
'weight' => 1,
'cache' => 'BLOCK_NO_CACHE',
);
$blocks['hosting_supporting_aegir'] = array(
'info' => t('Support Aegir'),
'enabled' => 1,
'status' => 1,
'region' => 'sidebar_second',
'weight' => 1,
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function hosting_block_view($delta) {
$out = NULL;
switch ($delta) {
case 'hosting_queues':
$out = array(
'title' => t('Queues'),
'content' => hosting_queue_block(),
);
break;
case 'hosting_queues_summary':
$out = array(
'title' => t('Queues summary'),
'content' => hosting_queue_summary_block(),
);
break;
case 'hosting_supporting_aegir':
$out = array(
'title' => t('Support Aegir'),
'content' => hosting_supporting_aegir_block(),
);
break;
}
return $out;
// @todo where is 'hosting_summary'?
}
/**
* Build a block summarising the hosting queues.
*/
function hosting_queue_summary_block() {
if (user_access('administer hosting queues')) {
$queues = hosting_get_queues();
$output = '';
foreach ($queues as $queue => $info) {
$disp = array();
// Special case.
if (!$info['enabled']) {
$disp[] = t('Status: disabled');
continue;
}
$disp[] = t('Status: enabled');
foreach (array(
'description' => t('Description'),
'frequency' => t('Frequency'),
'items' => t('Items per run'),
'total_items' => t('Items in queue'),
'last_run' => t('Last run'),
) as $key => $title) {
if ($key == 'last_run') {
$info[$key] = hosting_format_interval($info[$key]);
}
elseif ($key == 'frequency') {
$info[$key] = t('every @interval', array(
'@interval' => format_interval($info[$key]),
));
}
$disp[] = $title . ": " . $info[$key];
}
$output .= theme('item_list', array(
'items' => $disp,
'title' => $info['name'],
));
}
return $output;
}
}
/**
* Build a block of the hosting queues.
*
* @see hosting_TASK_SINGULAR_summary()
*/
function hosting_queue_block() {
if (user_access('access task logs')) {
$queues = hosting_get_queues();
$output = '';
foreach ($queues as $queue => $info) {
// Invoke hosting_TASK_SINGULAR_summary().
$func = 'hosting_' . $info['singular'] . '_summary';
if (function_exists($func)) {
$output .= $func();
}
}
return $output;
}
}
/**
* Build a 'Support Aegir' block.
*/
function hosting_supporting_aegir_block() {
if (user_access('administer hosting')) {
$output = "\n\n <p>Aegir will always cost you nothing to use, but that doesn't mean it costs us nothing to make.</p>\n\n <p><strong>Aegir needs your continued support to grow and thrive.</strong></p>\n\n <p>If you feel that Aegir has added significant value to your business or endeavour, please consider donating!</p>\n <p><strong>How much is Aegir worth to you?</strong></p>\n <a href=\"http://www.aegirproject.org/#support\" target=_blank>Donate Now ... </a>\n <a href=\"http://www.aegirproject.org/#support\" target=_blank>or get more info.</a>\n\n <footer style=\"font-size: smaller;\">This block is only displayed to you, the superuser.</footer>\n ";
return $output;
}
}
/**
* Check site URL is allowed.
*
* This function hooks into hook_allow_domain to let contrib modules
* weigh in on whether the site should be created.
*
* All the hooks must return true for the domain to be allowed.
*
* @see hook_allow_domain()
*/
function hosting_domain_allowed($url, $params = array()) {
$results = module_invoke_all('allow_domain', $url, $params);
$return = !in_array(FALSE, $results);
return $return;
}
/**
* Initial hosting setup drush command.
*
* Runs the 'hosting-dispatch' command, and sets up the crontab.
*/
function drush_hosting_setup() {
if (drush_confirm("This command will replace your crontab for this user. continue?")) {
variable_set('hosting_dispatch_enabled', FALSE);
// Attempt to run the dispatch command, to make sure it runs without the
// queue being enabled.
variable_set('hosting_dispatch_enabled', TRUE);
drush_print(_hosting_dispatch_cmd());
exec(_hosting_dispatch_cmd(), $return, $code);
variable_set('hosting_dispatch_enabled', FALSE);
$return = implode("\n", $return);
$data = unserialize($return);
if ($code == DRUSH_SUCCESS) {
variable_set('hosting_dispatch_enabled', TRUE);
drush_log(t("Dispatch command was run successfully"), 'success');
_hosting_setup_cron();
}
else {
drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("Dispatch command could not be run. Returned: \n@return", array(
'@return' => $return,
)));
}
if (drush_get_error()) {
drush_log(t("The command did not complete successfully, please fix the issues and re-run this script"), 'error');
}
}
}
/**
* Set up the hosting-dispatch command in the aegir user's crontab.
*
* Replace the crontab entry if it exists, else create it from scratch.
*/
function _hosting_setup_cron($add = TRUE) {
$existing = FALSE;
$cron = '';
$cron_lines = array();
// Load any existing crontab.
exec('crontab -l 2> /dev/null', $cron_lines);
variable_set('hosting_cron_backup', $cron_lines);
if (count($cron_lines)) {
drush_log(dt("Replacing existing crontab"), 'notice');
$cron_lines = hosting_queues_cron_removal($cron_lines);
$cron = implode("\n", $cron_lines);
}
else {
drush_log(dt("No existing crontab was found"), 'message');
}
if ($add) {
// Append the fresh Aegir specific lines.
$cron .= "\n" . hosting_queues_cron_cmd();
drush_log(dt("Adding hosting-dispatch cron entry to run every minute"), 'ok');
}
// Write a new crontab.
$fp = popen('crontab -', 'w');
$success = TRUE;
if ($fp) {
if (fwrite($fp, $cron) && fwrite($fp, "\n")) {
$success = TRUE;
}
else {
$success = FALSE;
}
// 'pclose' returns shell exit codes (ie. 0 is success).
if (pclose($fp)) {
$success = FALSE;
}
}
else {
$success = FALSE;
}
if ($success) {
drush_log(dt("Installed a new crontab entry."), 'success');
}
else {
drush_set_error('PROVISION_CRON_FAILED', dt('Failed to install cron job'));
}
}
/**
* Replacement node/add page.
*
* Major kludge to remove the hidden node types from node/add page.
*
* Copied from node.module.
*/
function _hosting_node_add($type = '') {
global $user;
$types = node_type_get_types();
$type = $type ? str_replace('-', '_', $type) : NULL;
// If a node type has been specified, validate its existence.
if (isset($types[$type]) && user_access('create ' . $type) && hosting_feature($type) !== HOSTING_FEATURE_DISABLED) {
// Initialize settings:
$node = array(
'uid' => $user->uid,
'name' => $user->name,
'type' => $type,
);
drupal_set_title(t('Submit @name', array(
'@name' => $types[$type]->name,
)), PASS_THROUGH);
// TODO $node needs to have $form as its first parameter.
$output = drupal_get_form($type . '_node_form', $node);
}
else {
// If no (valid) node type has been provided, display a node type overview.
foreach ($types as $type) {
if (function_exists($type->module . '_form') && user_access('create ' . $type->type) && hosting_feature($type->type) !== HOSTING_FEATURE_DISABLED) {
$type_url_str = str_replace('_', '-', $type->type);
$title = t('Add a new @s.', array(
'@s' => $type->name,
));
$out = '<dt>' . l(drupal_ucfirst($type->name), "node/add/{$type_url_str}", array(
'attributes' => array(
'title' => $title,
),
)) . '</dt>';
$out .= '<dd>' . filter_xss_admin($type->description) . '</dd>';
$item[$type->name] = $out;
}
}
if (isset($item)) {
uksort($item, 'strnatcasecmp');
$output = t('Choose the appropriate item from the list:') . '<dl>' . implode('', $item) . '</dl>';
}
else {
$output = t('No content types available.');
}
}
return $output;
}
/**
* List queues or tasks in a queue if a key is provided.
*
* @see hosting_TASK_SINGULAR_list()
*/
function hosting_queues($key = '') {
$queues = hosting_get_queues();
$output = '';
if ($queues[$key]) {
if ($queues[$key]['name']) {
$output .= "<h1>" . $queues[$key]['name'] . "</h1>";
}
// Invoke hosting_TASK_SINGULAR_list().
$func = 'hosting_' . $queues[$key]['singular'] . '_list';
if (function_exists($func)) {
$output .= $func();
}
}
else {
foreach ($queues as $key => $queue) {
$item[] = l($queue['name'], 'hosting/queues/' . $key);
}
$output .= theme('item_list', array(
'items' => $item,
'title' => t('Queues'),
));
}
return $output;
}
/**
* Generate context sensitive breadcrumbs.
*
* @param object $node
* A node object to use for context of the breadcrumbs.
*/
function hosting_set_breadcrumb($node) {
$breadcrumbs[] = l(t('Home'), NULL);
switch ($node->type) {
case 'task':
$breadcrumbs[] = _hosting_node_link($node->rid);
break;
case 'platform':
$breadcrumbs[] = _hosting_node_link($node->web_server);
break;
case 'site':
$breadcrumbs[] = _hosting_node_link($node->platform);
break;
case 'server':
$breadcrumbs[] = l(t('Servers'), 'hosting/servers');
break;
}
$breadcrumbs[] = _hosting_node_link($node->nid);
drupal_set_breadcrumb($breadcrumbs);
}
/**
* Form to configure the frequency of queue execution.
*/
function hosting_queues_configure($form, &$form_state) {
drupal_add_css(drupal_get_path('module', 'hosting') . '/hosting.css');
$units = array(
strtotime("1 second", 0) => t("Seconds"),
strtotime("1 minute", 0) => t("Minutes"),
strtotime("1 hour", 0) => t("Hours"),
strtotime("1 day", 0) => t("Days"),
strtotime("1 week", 0) => t("Weeks"),
);
$queues = hosting_get_queues();
$form['#tree'] = TRUE;
foreach ($queues as $queue => $info) {
$form[$queue]["enabled"] = array(
'#type' => 'checkbox',
'#default_value' => $info['enabled'],
'#title' => $info['name'],
'#description' => $info['description'],
);
$form[$queue]["last_run"] = array(
'#markup' => hosting_format_interval(variable_get('hosting_queue_' . $queue . '_last_run', FALSE)),
);
$form[$queue]['frequency']['#prefix'] = "<div class='hosting-queue-frequency'>";
$form[$queue]['frequency']['#suffix'] = "</div>";
if ($info['type'] == HOSTING_QUEUE_TYPE_BATCH) {
$form[$queue]['frequency']['items'] = array(
'#markup' => t('%count %items every', array(
"%count" => $info['total_items'],
"%items" => format_plural($info['total_items'], $info['singular'], $info['plural']),
)),
'#prefix' => "<div class='hosting-queue-frequency-items'>",
'#suffix' => "</div>",
);
}
elseif ($info['type'] == HOSTING_QUEUE_TYPE_SPREAD) {
$form[$queue]['frequency']['items'] = array(
'#markup' => t('%count %items every', array(
"%count" => $info['total_items'],
"%items" => format_plural($info['total_items'], $info['singular'], $info['plural']),
)),
'#prefix' => "<div class='hosting-queue-frequency-items'>",
'#suffix' => "</div>",
);
}
else {
$form[$queue]['frequency']['items'] = array(
'#type' => 'textfield',
'#size' => 3,
'#maxlength' => 3,
'#default_value' => $info['items'],
// @ignore i18n_11
'#suffix' => ' ' . t('%items every', array(
'%items' => $info['plural'],
)) . ' ',
'#attributes' => array(
'class' => array(
'hosting-select-frequency-items',
),
),
);
}
foreach (array_reverse(array_keys($units)) as $length) {
$unit = $units[$length];
if (!($info['frequency'] % $length)) {
$frequency_ticks = $info['frequency'] / $length;
$frequency_length = $length;
break;
}
}
$form[$queue]['frequency']["ticks"] = array(
'#type' => 'textfield',
'#default_value' => $frequency_ticks,
'#maxlength' => 5,
'#size' => 5,
'#attributes' => array(
'class' => array(
'hosting-select-frequency-ticks',
),
),
);
$form[$queue]['frequency']["unit"] = array(
'#type' => 'select',
'#options' => $units,
'#default_value' => $frequency_length,
'#attributes' => array(
'class' => array(
'hosting-select-frequency-unit',
),
),
);
}
$form['help'] = array(
'#type' => 'item',
'#description' => t("Increasing the queue frequency to every 1 second will not cause them to be dispatched every second, as the dispatcher can only run once per minute via cron. However, with such a short frequency, executing the hosting-dispatch command manually from the CLI will allow you to 'force' the queues to be dispatched between cron runs. This may be useful for development purposes."),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save changes'),
);
return $form;
}
/**
* Theme function to render the queue configuration form.
*
* @see hosting_queues_configure()
*/
function theme_hosting_queues_configure($variables) {
$form = $variables['form'];
$queues = hosting_get_queues();
$rows = array();
$header = array(
t('Queue'),
array(
'data' => t('Frequency'),
'class' => array(
'hosting-queue-frequency-head',
),
),
t('Last run'),
);
foreach ($queues as $key => $info) {
$row = array();
$row[] = drupal_render($form[$key]['enabled']);
$row[] = drupal_render($form[$key]['frequency']);
$row[] = drupal_render($form[$key]['last_run']);
$rows[] = $row;
}
$output = theme('table', array(
'header' => $header,
'rows' => $rows,
));
$output .= drupal_render($form['help']);
$output .= drupal_render($form['submit']);
$output .= drupal_render_children($form);
return $output;
}
/**
* Validation callback for the the queue execution frequency form.
*
* @see hosting_queues_configure()
*/
function hosting_queues_configure_validate($form, &$form_state) {
foreach (hosting_get_queues() as $queue => $info) {
if ($form_state['values'][$queue]) {
if ($form_state['values'][$queue]['frequency']['ticks'] && !is_numeric($form_state['values'][$queue]['frequency']['ticks'])) {
form_set_error($queue, t('Please enter a valid frequency.'));
}
if (isset($form_state['values'][$queue]['frequency']['items']) && !is_numeric($form_state['values'][$queue]['frequency']['items'])) {
form_set_error($queue, t('Please enter a valid amount of items.'));
}
}
}
}
/**
* Submit callback for the the queue execution frequency form.
*
* @see hosting_queues_configure()
*/
function hosting_queues_configure_submit($form, &$form_state) {
foreach (hosting_get_queues() as $queue => $info) {
if ($form_state['values'][$queue]) {
variable_set("hosting_queue_" . $queue . "_enabled", $form_state['values'][$queue]['enabled']);
variable_set("hosting_queue_" . $queue . "_frequency", $form_state['values'][$queue]['frequency']['ticks'] * $form_state['values'][$queue]['frequency']['unit']);
if ($info['type'] == HOSTING_QUEUE_TYPE_SERIAL) {
variable_set("hosting_queue_" . $queue . "_items", $form_state['values'][$queue]['frequency']['items']);
}
}
}
drupal_set_message(t('The queue settings have been updated.'));
}
/**
* Implements hook_form_alter().
*/
function hosting_form_alter(&$form, &$form_state, $form_id) {
// Do not allow package or task nodes to be edited through the interface.
if ($form_id == 'package_node_form' || $form_id == 'task_node_form') {
drupal_access_denied();
exit;
}
// Alter the 'Add User' form to remind users that this is not the New Client
// form.
if ($form_id == 'user_register') {
$form['user_registration_help'] = array(
'#type' => 'item',
'#description' => t('<strong>Adding a system user account does not make the user a Client that can add sites.</strong><br />
To add a Client, enable the Client feature and then add a new Client node.<br />
If you wish, you may then assign this system user to the Client as an \'Allowed user\' to inherit the permissions to add sites.'),
'#weight' => '-10',
);
}
// Remove additional UI added by core modules, it conflicts with the our UI.
$node_types = array(
'site',
'platform',
'server',
'client',
);
foreach ($node_types as $type) {
if ($form_id == $type . '_node_form') {
$form['options']['#access'] = FALSE;
$form['revision_information']['#access'] = FALSE;
$form['author']['#access'] = FALSE;
// Because these aren't really posts, 'preview' doesn't make sense in
// this context.
$form['actions']['preview']['#access'] = FALSE;
// Add our own helpful messages.
$form['#submit'][] = 'hosting_form_submit_messages';
}
}
}
/**
* Submit handler for hosting node forms: Display helpful messages.
*/
function hosting_form_submit_messages($form, $form_state) {
// Only act on sites, platforms, & servers.
if (in_array($form_state['node']->type, array(
'site',
'platform',
'server',
))) {
// Translation array.
$t = array(
'@type' => $form_state['node']->type,
'%title' => $form_state['values']['title'],
'%task' => t('Verify'),
);
// If this is an add form...
if (!isset($form_state['node']->nid)) {
$t['!message'] = t('It will be available once the task is completed.');
// If node is a site, task is "install".
if ($form_state['node']->type == 'site') {
$t['%task'] = t('Install');
}
}
else {
$t['!message'] = t('Changes will be applied once the task is completed.');
}
// Display the message.
drupal_set_message(t('%task task has been queued for @type %title. !message', $t));
// If using the hosting queue, mention the scheduled time.
$queues = hosting_get_queues();
if ($queues['tasks']['enabled']) {
$until = _hosting_queue_next_run('tasks') - REQUEST_TIME;
drupal_set_message(t('Task is scheduled to start in approximately %until.', array(
'%until' => format_interval($until),
)));
}
}
}
/**
* Get the node associated with a given provision context.
*
* @param string $name
* The name of the context to load.
*
* @return bool|object
* Either the node object associated with the context, or FALSE if no
*/
function hosting_context_load($name) {
$name = ltrim($name, '@');
// TODO: introduce static caching ?
$result = db_query("SELECT nid FROM {hosting_context} WHERE name = :name", array(
':name' => $name,
));
if ($obj = $result
->fetch()) {
return node_load($obj->nid);
}
return FALSE;
}
/**
* Add a node to the context lookup db.
*
* @param int $nid
* The nid of the node to associate to the given provision context name.
* @param string $name
* The name of the provision context.
*/
function hosting_context_register($nid, $name) {
// Check first to see if this nid exists in the system. If so, update its
// name.
if ($result = db_query("SELECT nid FROM {hosting_context} WHERE nid = :nid", array(
':nid' => $nid,
))
->fetch()) {
db_update('hosting_context')
->fields(array(
'name' => $name,
))
->condition('nid', $nid)
->execute();
}
else {
// It's a new item.
$id = db_insert('hosting_context')
->fields(array(
'nid' => $nid,
'name' => $name,
))
->execute();
}
// We include the file instead of enabling the module,
// because we do not want the overhead of having all the
// path UI stuff on nodes.
require_once DRUPAL_ROOT . '/modules/path/path.module';
// Delete an existing alias,
// could be left behind after migrating a site from this name.
path_delete(array(
'alias' => "hosting/c/{$name}",
));
// Add the new alias.
$path = array(
'source' => "node/{$nid}",
'alias' => "hosting/c/{$name}",
);
path_save($path);
}
/**
* Delete an alias from the context lookup table.
*
* @param int $nid
* The nid of the node to remove the association from.
*/
function hosting_context_delete($nid) {
path_delete(array(
'source' => 'node/' . $nid,
));
db_delete('hosting_context')
->condition('nid', $nid)
->execute();
}
/**
* Return the hosting context name for a node.
*
* @param int $nid
* The nid of the node to get the provision context name for.
*
* @return string|bool
* The provision context name associated with the specified node, will be
* prefixed with '@' or FALSE if node doesnt exist.
*/
function hosting_context_name($nid) {
$node = node_load($nid);
if (!$node) {
drupal_set_message(t('Error: cannot load node id %nid to find its context', array(
'%nid' => $nid,
)), 'error');
return FALSE;
}
return '@' . $node->hosting_name;
}
/**
* Lookup the node ID for a hosting context name.
*
* @param object|string $alias
* The name of the provision context to import.
*
* @return int
* The node ID associated with the alias.
*/
function hosting_context_nid($alias) {
$name = is_object($alias) ? $alias->name : $alias;
$name = ltrim($name, '@');
$result = db_query("SELECT nid FROM {hosting_context} WHERE name = :name", array(
':name' => $name,
));
if ($obj = $result
->fetch()) {
return $obj->nid;
}
else {
return hosting_drush_import($alias);
}
}
/**
* Define the node types that have associated provision contexts.
*
* @deprecated See issue #2763509. If you really need this, you can check
* if a nid is in the hosting_context table.
*/
function hosting_context_node_types() {
return array(
'site',
'platform',
'server',
);
}
/**
* Implements hook_views_api().
*/
function hosting_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'hosting') . '/includes/views',
);
}
/**
* Return a list of log entry types.
*/
function hosting_log_types() {
$known_types = [
'error',
'info',
'message',
'notice',
'ok',
'status',
'success',
'warning',
];
$types_from_logs = db_query('SELECT DISTINCT type FROM {hosting_task_log}')
->fetchCol();
$types = array_unique(array_merge($known_types, $types_from_logs));
return array_combine($types, $types);
}
/**
* General settings form.
*/
function hosting_settings($form, &$form_state) {
$form['settings'] = array(
'#type' => 'item',
'#title' => t('General Hosting settings'),
'#value' => t('Here you may set various general settings that apply to site management or to the frontend system.'),
'#weight' => 0,
);
$profiles = array();
foreach (hosting_get_profiles() as $id => $name) {
$profile = hosting_package_instance_load(array(
'p.nid' => $id,
));
$profiles[$profile->short_name] = theme('hosting_site_profile', array(
'profile' => $profile,
'html' => '',
));
// Don't allow a site to be provisioned a restricted profile.
if (in_array($name, array(
'Hostslave',
'Hostmaster',
))) {
unset($profiles[$profile->short_name]);
}
}
natcasesort($profiles);
$default_value = hosting_package_instance_load(array(
'p.nid' => hosting_get_default_profile(),
))->short_name;
$form['hosting_default_profile'] = array(
'#type' => 'select',
'#title' => t('Default profile'),
'#options' => $profiles,
'#description' => t('The default install profile to use when creating new sites.'),
'#default_value' => $default_value,
);
$form['hosting_ignore_default_profiles'] = array(
'#type' => 'checkbox',
'#title' => t('Hide platforms with non-default profiles'),
'#description' => t('When selecting a Drupal core profile, hide platforms that also contain other profiles (i.e intended as distributions). Warning: requires a platform other than the Hostmaster platform.'),
'#default_value' => variable_get('hosting_ignore_default_profiles', FALSE),
);
$form['hosting_lock_platforms_by_default'] = array(
'#type' => 'checkbox',
'#title' => t('Lock platforms by default on initial creation'),
'#description' => t("When verifying a new platform, set its initial state to 'Locked'. This can avoid having sites created on a new platform before there has been a chance to test it. Those users with the 'create sites on locked platforms' permission (e.g., with the 'platform manager' role) will still be able to build sites."),
'#default_value' => variable_get('hosting_lock_platforms_by_default', FALSE),
);
$form['hosting_platform_automatic_site_import'] = array(
'#type' => 'checkbox',
'#title' => t('Automatically import sites when verifying platforms'),
'#description' => t("When verifying a platform, if sites are found that do not yet exist in the front-end, automatically import them."),
'#default_value' => variable_get('hosting_platform_automatic_site_import', TRUE),
);
$form['hosting_require_disable_before_delete'] = array(
'#type' => 'checkbox',
'#title' => t('Require site to be disabled before deletion'),
'#description' => t('Check if you wish for users to run the Disable task on a site prior to running the Delete task.'),
'#default_value' => variable_get('hosting_require_disable_before_delete', TRUE),
);
$form['hosting_delete_force'] = array(
'#type' => 'checkbox',
'#title' => t('Force sites, platforms, and servers to be removed when running the Delete task.'),
'#description' => t('If something goes wrong when deleting a site, platform, or server, the task will fail. Check this box to force contexts to be removed when Delete tasks are run.'),
'#default_value' => variable_get('hosting_delete_force', FALSE),
);
$form['hosting_site_allow_custom_file_paths'] = array(
'#type' => 'checkbox',
'#title' => t('Allow sites to have custom file paths for public, private, and temporary files.'),
'#description' => t('When creating or editing a site, show a field for changing the default files paths. <em>CAUTION:</em> The web server user will be granted write access permission to these folders so that the Drupal site can receive uploaded files. If this is checked, Aegir users can enter any path, and the <code>aegir</code> system user will attempt to make it writable for the web server user, typically <code>www-data</code>, <code>apache</code> or <code>nginx</code>. '),
'#default_value' => variable_get('hosting_site_allow_custom_file_paths', FALSE),
);
if (hosting_feature('cron')) {
$form['hosting_cron_use_backend'] = array(
'#type' => 'radios',
'#title' => t('Cron method'),
'#description' => t('For running cron on a site. You can use the drush cron implementation or the web-based cron.php method using builtin HTTP requests. The drush implementation is more reliable but will be slower than the web-based approach if the webserver has an opcode cache (like APC) configured.'),
'#options' => array(
'Web-based',
'Drush',
),
'#default_value' => variable_get('hosting_cron_use_backend', TRUE),
);
}
$types = hosting_log_types();
$form['hosting_task_logs_types_display'] = array(
'#type' => 'checkboxes',
'#title' => t('Task Logs to Display'),
'#description' => t('Select the types of logs you wish to show to the user.'),
'#options' => $types,
'#default_value' => variable_get('hosting_task_logs_types_display', $types),
);
return system_settings_form($form);
}
/**
* Create the default hosting roles.
*/
function hosting_create_roles() {
// Only allow access to our splash pages for anyone without a specific role.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
'access disabled sites',
));
user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array(
'access disabled sites',
));
// Create a default role for Aegir clients.
$client_role = new stdClass();
$client_role->name = 'aegir client';
$client_role->weight = 4;
user_role_save($client_role);
// Create a default role for Aegir account managers.
$acct_mgr_role = new stdClass();
$acct_mgr_role->name = 'aegir account manager';
$acct_mgr_role->weight = 6;
user_role_save($acct_mgr_role);
// Create a default role for Aegir platform managers.
$plat_mgr_role = new stdClass();
$plat_mgr_role->name = 'aegir platform manager';
$plat_mgr_role->weight = 8;
user_role_save($plat_mgr_role);
// Create a default role for Aegir administrators.
$aegir_admin_role = new stdClass();
$aegir_admin_role->name = 'aegir administrator';
$aegir_admin_role->weight = 10;
user_role_save($aegir_admin_role);
// Create a default role for site administrators,
// with all available permissions assigned.
$admin_role = new stdClass();
$admin_role->name = 'administrator';
$admin_role->weight = 20;
user_role_save($admin_role);
user_role_grant_permissions($admin_role->rid, array_keys(module_invoke_all('permission')));
// Set this as the administrator role.
variable_set('user_admin_role', $admin_role->rid);
// Assign user 1 the "administrator" role.
db_insert('users_roles')
->fields(array(
'uid' => 1,
'rid' => $admin_role->rid,
))
->execute();
node_access_rebuild();
}
/**
* Implements hook_modules_enabled().
*/
function hosting_modules_enabled($modules) {
// List any features whose module was just enabled.
$features = hosting_get_features();
$enable = array();
foreach ($features as $feature => $info) {
if (in_array($info['module'], $modules) && !variable_get('hosting_feature_' . $feature, FALSE)) {
$enable[$info['module']] = $feature;
}
}
hosting_features_enable($enable, TRUE, FALSE);
}
/**
* Implements hook_modules_disabled().
*/
function hosting_modules_disabled($modules) {
// List any features whose module was just disabled.
$features = hosting_get_features();
$disable = array();
foreach ($features as $feature => $info) {
if (in_array($info['module'], $modules) && variable_get('hosting_feature_' . $feature, TRUE)) {
$disable[$info['module']] = $feature;
}
}
hosting_features_disable($disable, $rebuild = TRUE, $disable_module = FALSE);
}
Functions
Name | Description |
---|---|
drush_hosting_setup | Initial hosting setup drush command. |
hosting_block_info | Implements hook_block_info(). |
hosting_block_view | Implements hook_block_view(). |
hosting_context_delete | Delete an alias from the context lookup table. |
hosting_context_load | Get the node associated with a given provision context. |
hosting_context_name | Return the hosting context name for a node. |
hosting_context_nid | Lookup the node ID for a hosting context name. |
hosting_context_node_types Deprecated | Define the node types that have associated provision contexts. |
hosting_context_register | Add a node to the context lookup db. |
hosting_create_roles | Create the default hosting roles. |
hosting_disabled_site | Page callback for a site that has been disabled. |
hosting_domain_allowed | Check site URL is allowed. |
hosting_form_alter | Implements hook_form_alter(). |
hosting_form_submit_messages | Submit handler for hosting node forms: Display helpful messages. |
hosting_init | Implements hook_init(). |
hosting_js_page | Page callback for a page to be rendered in modal frame. |
hosting_log_types | Return a list of log entry types. |
hosting_menu | Implements hook_menu(). |
hosting_menu_access | Menu access callback for creating a node provided by a Hosting feature. |
hosting_menu_alter | Implements hook_menu_alter(). |
hosting_menu_hostmaster_access | Access hook for /hostmaster callback page. Checks the access for hostmaster node page. |
hosting_menu_hostmaster_redirect | Send the user from /hostmaster to /hosting/c/hostmaster |
hosting_modules_disabled | Implements hook_modules_disabled(). |
hosting_modules_enabled | Implements hook_modules_enabled(). |
hosting_nodeapi | Redirects to hosting_nodeapi_$nodetype_$op calls. |
hosting_node_delete | Implements hook_node_delete(). |
hosting_node_help | Per node type description text. To be stored in the node_type table. |
hosting_node_insert | Implements hook_node_insert(). |
hosting_node_load | Implements hook_node_load(). |
hosting_node_prepare | Implements hook_node_prepare(). |
hosting_node_presave | Implements hook_node_presave(). |
hosting_node_revision_delete | Implements hook_node_revision_delete(). |
hosting_node_search_result | Implements hook_node_search_result(). |
hosting_node_tab_title | Replace the text used on node page "tabs". |
hosting_node_update | Implements hook_node_update(). |
hosting_node_update_index | Implements hook_node_update_index(). |
hosting_node_validate | Implements hook_node_validate(). |
hosting_node_view | Implements hook_node_view(). |
hosting_permission | Implements hook_permission(). |
hosting_queues | List queues or tasks in a queue if a key is provided. |
hosting_queues_configure | Form to configure the frequency of queue execution. |
hosting_queues_configure_submit | Submit callback for the the queue execution frequency form. |
hosting_queues_configure_validate | Validation callback for the the queue execution frequency form. |
hosting_queue_block | Build a block of the hosting queues. |
hosting_queue_summary_block | Build a block summarising the hosting queues. |
hosting_settings | General settings form. |
hosting_set_breadcrumb | Generate context sensitive breadcrumbs. |
hosting_site_maintenance | Page callback for a site that is undergoing maintenance. |
hosting_supporting_aegir_block | Build a 'Support Aegir' block. |
hosting_theme | Implements hook_theme(). |
hosting_views_api | Implements hook_views_api(). |
theme_hosting_queues_configure | Theme function to render the queue configuration form. |
_hosting_node_add | Replacement node/add page. |
_hosting_node_link | Generates a link to a node. |
_hosting_setup_cron | Set up the hosting-dispatch command in the aegir user's crontab. |
Constants
Name | Description |
---|---|
HOSTING_MAX_ALIAS_LENGTH | The maximum length for a site alias. Leaving room to append '.alias.drushrc.php' and keep filenames below 255. |
HOSTING_QUEUE_TYPE_BATCH | A batch queue type. |
HOSTING_QUEUE_TYPE_SERIAL | A serial queue type. |
HOSTING_QUEUE_TYPE_SPREAD | A spread queue type. |