4 * This site is deleted from the backend.
6 define('HOSTING_SITE_DELETED', -2);
9 * This site is disabled.
11 define('HOSTING_SITE_DISABLED', -1);
14 * This site is queued for creation.
16 define('HOSTING_SITE_QUEUED', 0);
19 * This site is created and enabled in the backend.
21 define('HOSTING_SITE_ENABLED', 1);
23 include_once('hosting_site.nodeapi.inc');
24 include_once('hosting_site.form.inc');
25 include_once('hosting_site.backups.inc');
27 //Conditonally include site quotas
28 if (module_exists('hosting_quota')) {
29 include_once('hosting_site.quota.inc');
33 * Implementation of hook_menu()
35 function hosting_site_menu() {
38 $items['hosting/sites'] = array(
40 'description' => 'Display a list of sites',
41 'page callback' => 'hosting_sites',
42 'access arguments' => array('view site'),
43 'type' => MENU_NORMAL_ITEM
,
46 $items['hosting/sites/list'] = array(
48 'type' => MENU_DEFAULT_LOCAL_TASK
,
52 $items['hosting/sites/add'] = array(
53 'title' => 'Add site',
54 'type' => MENU_LOCAL_TASK
,
55 'page callback' => 'drupal_goto',
56 'page arguments' => array('node/add/site'),
57 'access callback' => 'node_access',
58 'access arguments' => array('create', 'site'),
61 $items['hosting/hosting_site_form_check'] = array(
62 'page callback' =>'_hosting_site_form_check',
63 'type' => MENU_CALLBACK
,
64 'access arguments' => array('access content'),
68 $items['node/%hosting_site_node/goto_site'] = array(
69 'page callback' => 'hosting_site_goto',
70 'page arguments' => array(1),
71 'access callback' => 'node_access',
72 'access arguments' => array('view', 1),
73 'type' => MENU_CALLBACK
,
79 function _hosting_site_form_check() {
81 hosting_site_available_options($_POST)
87 * Implementation of hook_load().
92 function hosting_site_node_load($arg) {
93 if (!is_numeric($arg)) {
96 if ($node = node_load($arg)) {
97 if ($node->type
== 'site') {
105 * Page handler for displaying list of hosted sites on front page
107 function hosting_sites() {
108 if ($list = drupal_get_form('hosting_site_list_form')) {
111 $create_site_link = l(t('Create a site now?'), 'node/add/site');
112 return t("No sites have been created yet. !link", array(
113 '!link' => $create_site_link));
117 * Generate a link allowing the user to log into their new site, or simply
118 * go to the site front page if the link has expired.
120 function _hosting_site_goto_link($node) {
121 $cache = cache_get("hosting:site:" .
$node->nid .
":login_link");
122 if (!is_null($cache) && (time() < $cache->data
['expire'])) {
123 $title = t("Log in to !url" , array('!url' => $node->title
));
126 $title = t("Go to !url" , array('!url' => $node->title
));
128 $options['attributes']['class'] = 'hosting-goto-site-link';
129 return l($title, "node/" .
$node->nid .
"/goto_site", $options);
133 * Menu callback to go to your site.
135 * This needs to be a callback because the link is only going to work once,
136 * so this will remove the link from the cache and just redirect to the site
137 * not the login page.
139 function hosting_site_goto($node) {
140 $cid = "hosting:site:" .
$node->nid .
":login_link";
141 $cache = cache_get($cid);
142 if (!is_null($cache) && (time() < $cache->data
['expire'])) {
143 $theurl = $cache->data
['link'];
144 cache_clear_all($cid, 'cache');
147 $theurl = _hosting_site_url($node);
150 drupal_goto($theurl);
155 * Retrieve the port the site will be available on, so we can redirect.
157 function hosting_site_get_port($node) {
158 $platform = node_load($node->platform
);
159 $server = node_load($platform->web_server
);
160 return $server->services
['http']->port
;
166 function _hosting_site_url($node) {
170 $url = strtolower(trim($node->title
));
172 $platform = node_load($node->platform
);
173 $server = node_load($platform->web_server
);
176 if ($server->services
['http']->has_port()) {
177 $port = $server->services
['http']->port
;
184 * This is part of the ssl feature, but is better to implement here.
186 if (isset($node->ssl_enabled
) && ($node->ssl_enabled
== 2)) {
187 // this is a bit of a magic number, because we cant rely on the constant being available yet.
188 // 2 == Only SSL is enabled.
191 if ($server->services
['http']->has_port()) {
192 $port = $server->services
['http']->ssl_port
;
199 if (is_numeric($port)) {
200 return "{$schema}://{$url}:{$port}";
203 return "{$schema}://{$url}";
207 * Implementation of hook_hosting_tasks
209 function hosting_site_hosting_tasks() {
212 $tasks['site']['backup'] = array(
213 'title' => t('Backup'),
214 'description' => t('Generate a backup of this site that can be restored to at any time'),
218 $tasks['site']['restore'] = array(
219 'title' => t('Restore'),
220 'description' => t('Restore this site to a previous backup. A new backup will be created before this is attempted.'),
224 $tasks['site']['verify'] = array(
225 'title' => t('Verify'),
226 'description' => t('Confirm that the site has been correctly installed and regenerate all configuration files to match the hosting front end.'),
227 'provision_save' => TRUE
,
230 $tasks['site']['disable'] = array(
231 'title' => t('Disable'),
232 'description' => t('Disabling this site will stop it from being accessible.
233 It can be enabled again later.'),
236 $tasks['site']['enable'] = array(
237 'title' => t('Enable'),
238 'description' => t('Enabling this site will allow it to be accesible again.
239 It may be disabled again if needed.'),
241 $tasks['site']['delete'] = array(
242 'title' => t('Delete'),
243 'description' => t('Deleting this site will completely remove it from the hosting system,
244 but will keep the last backup available. This process can not be undone.
245 Are you really sure you want to delete this site?'),
249 $tasks['site']['login-reset'] = array(
250 'title' => t('Reset password'),
251 'description' => t('Generate a one-time login reset url for this site.'),
254 $tasks['site']['backup-delete'] = array(
255 'title' => t('Delete backups'),
256 'description' => t('Delete one or more backup files of a site.'),
260 $tasks['site']['install'] = array(
261 'title' => t('Install'),
262 'description' => t('Install a site'),
264 'provision_save' => TRUE
,
267 $tasks['site']['import'] = array(
268 'title' => t('Import'),
269 'description' => t('Import an existing site into Aegir'),
271 'provision_save' => TRUE
,
278 * Implementation of hook_perm
280 function hosting_site_perm() {
281 return array('create site', 'view site', 'edit site', 'delete site', 'administer sites');
285 * Implementation of hook_access
287 function hosting_site_access($op, $node, $account) {
290 return user_access('create site', $account);
293 return user_access('edit site', $account);
296 return user_access('delete site', $account);
299 return user_access('view site', $account);
305 * Returns a count of sites.
307 * This is used by cron and statistics to batch the number of sites that are
308 * processed with each call. It is also used to generate the 'site count' per
309 * platform at /hosting/platforms
311 * By default it only counts enabled sites.
313 * Optionally can include an array of site statuses by which to consider in the
317 * (optional) A platform nid to only count sites on that specific platform.
319 * (optional) An array of site statuses (defined by their constants) by which
320 * to include such sites in the site count.
323 * The number of sites matching the specified criteria.
325 function hosting_site_count($platform = NULL
, $statuses = NULL
) {
326 if (is_null($statuses)) {
327 $statuses = array(HOSTING_SITE_ENABLED
);
330 $query = "SELECT count(nid) FROM {hosting_site}";
333 if (count($statuses)) {
334 $where[] = '(status IN (' .
db_placeholders($statuses) .
'))';
335 $args = array_merge($args, $statuses);
338 if (!is_null($platform)) {
339 $where[] = "(platform = %d)";
343 if (!empty($where)) {
344 $query .
= ' WHERE ' .
implode(' AND ', $where);
347 return db_result(db_query($query, $args));
351 * Retrieve sites on a specific platform, with a specific status
353 function hosting_get_sites_by_status($platform, $status) {
355 $result = db_query("SELECT nid FROM {hosting_site} WHERE platform=%d AND status = %d", $platform, $status);
356 while ($nid = db_fetch_object($result)) {
357 $nodes[$nid->nid
] = node_load($nid->nid
);
364 * Retrieve a node based on the url
366 function hosting_get_site_by_url($url) {
367 // If the Aliases feature is enabled, try and get the site by its alias too
368 if (hosting_feature('alias')) {
369 $nid = db_result(db_query("SELECT n.nid FROM {node} n JOIN {hosting_site} h ON n.nid = h.nid LEFT JOIN {hosting_site_alias} ha ON h.vid = ha.vid WHERE (n.title = '%s' OR ha.alias = '%s') AND n.type = 'site' AND NOT (h.status = %d)", $url, $url, HOSTING_SITE_DELETED
));
372 $nid = db_result(db_query("SELECT n.nid FROM {node} n JOIN {hosting_site} h ON n.nid = h.nid WHERE n.title='%s' AND n.type = 'site' AND NOT (h.status=%d)", $url, HOSTING_SITE_DELETED
));
375 return node_load($nid);
381 * Helper function to generate update a site node during import.
384 * The node ID of the existing site node to update.
386 * An associative array of data to add to the site node. Keys should correspond
387 * to properties on the node object to overwrite.
389 * The node ID of the platform that the site is deployed on.
391 function hosting_import_site($site_id, $data, $platform) {
393 $client = node_load(HOSTING_DEFAULT_CLIENT
);
395 if ($data['client_name']) {
396 $client = hosting_import_client($data['client_name']);
399 $site = node_load($site_id);
400 $site->nid
= $site_id;
401 $site->uid
= $client->uid
;
403 $site->site_status
= 1;
404 $site->platform
= $platform;
405 $site->no_verify
= TRUE
;
406 $site->verified
= time();
407 $site->client
= $client->nid ?
$client->nid
: HOSTING_DEFAULT_CLIENT
;
408 $site->ip_addresses
= array_values($data['site_ip_addresses']);
409 $site->cron_key
= ($data['cron_key']) ?
$data['cron_key'] : '';
411 $site->aliases
= ($data['aliases']) ?
$data['aliases'] : array();
412 $site->db_server
= ($site->db_server
) ?
$site->db_server
: HOSTING_DEFAULT_DB_SERVER
;
413 $site->site_language
= $data['language'] ?
$data['language'] : 'en';
415 // Drupal 6 introduced a language field on nodes
416 unset($data['language']);
419 $profile = hosting_package_instance_load(array(
420 'i.rid' => $platform,
421 'p.short_name' => $data['profile']
424 $profile = hosting_package_instance_load(array(
425 'i.rid' => $platform,
426 'p.short_name' => 'default'));
428 $site->profile
= $profile->package_id
;
430 // cast site object to array, will be using various array_* functions on the data.
431 $site = (array) $site;
433 // Protect the fields already in the site object.
434 foreach (array_keys($site) as
$key) {
438 // Load all the original node fields.
439 $site = array_merge( (array) node_load($site_id), $site);
441 // Map the imported fields onto the node object.
442 $site = array_merge($site, $data);
444 // Cast back to object.
445 $site = (object) $site;
450 * Define the status types of a site
452 function _hosting_site_status($node) {
453 $status = (is_numeric($node)) ?
$node : $node->site_status
;
454 static
$labels = array(
455 HOSTING_SITE_QUEUED
=> "Queued",
456 HOSTING_SITE_ENABLED
=> "Enabled",
457 HOSTING_SITE_DELETED
=> "Deleted",
458 HOSTING_SITE_DISABLED
=> "Disabled",
460 return $labels[$status];
464 * Implementation of hook_allow_domain().
466 * @see hosting_domain_allowed()
468 function hosting_site_allow_domain($url, $params = array()) {
469 $query = "SELECT COUNT(n.nid) FROM {node} n
470 JOIN {hosting_site} h ON n.nid = h.nid
471 WHERE type='site' AND title='%s' AND h.status <> %d ";
473 $args[] = HOSTING_SITE_DELETED
;
475 if (isset($params['nid'])) {
476 $query .
= ' AND n.nid <> %d';
477 $args[] = $params['nid'];
479 $result = !db_result(db_query($query, $args));
483 function hosting_site_task_status($nid) {
484 return hosting_task_status_output('nid', $nid, 'install');
488 * Create a form for building a list of sites.
489 * @TODO Add ability to filter by additional fields
491 function hosting_site_list_form($form_state, $filter_by = NULL
, $filter_value = NULL
) {
492 $step = isset($form_state['storage']['step']) ?
$form_state['storage']['step'] : 1;
494 // Step 1 - select sites
499 if ($filter_by && $filter_value) {
500 if ($filter_by == 'status') {
501 $cond = ' AND s.' .
$filter_by .
' & %d';
503 $cond = ' AND s.' .
$filter_by .
' = %d';
505 $args[] = $filter_value;
509 array('data' => t('Site'), 'field' => 'title'),
510 array('data' => t('Profile'), 'field' => 'profile'),
511 array('data' => t('Language'), 'field' => 'site_language'),
512 array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),
514 $platforms = _hosting_get_platforms();
515 if (sizeof($platforms) > 1) {
516 $header[] = array('data' => t('Platform'), 'field' => 'platform');
519 $sql = "SELECT n.nid, n.title, n.created, s.status as site_status, profile, s.language as site_language, platform, verified FROM {node} n left join {hosting_site} s ON n.vid=s.vid WHERE type='%s' AND s.status != -2 " .
$cond;
520 $sql .
= tablesort_sql($header);
522 // @TODO hide deleted sites
523 $result = pager_query(db_rewrite_sql($sql), 25, 1, null
, $args);
525 $form['options'] = array(
526 '#type' => 'fieldset',
527 '#title' => t('Site tasks'),
528 '#prefix' => '<div class="container-inline">',
529 '#suffix' => '</div>',
533 foreach (hosting_available_tasks('site') as
$task => $array) {
534 // At this stage we only want to handle simple tasks, the presense of a
535 // specific task form means there are other options for this tasks.
536 $func = 'hosting_task_' .
$task .
'_form';
537 if (!function_exists($func) && user_access('create '.
$task .
' task')) {
538 $options[$task] = $array['title'];
541 $form['options']['task'] = array(
543 '#options' => $options,
544 '#default_value' => 'backup',
546 $form['options']['submit'] = array(
548 '#value' => t('Add to queue'),
549 '#submit' => array('hosting_site_list_form_submit'),
553 while ($node = db_fetch_object($result)) {
554 $sites[$node->nid
] = '';
555 $form['site'][$node->nid
] = array('#value' => l($node->title
, 'node/' .
$node->nid
));
556 $form['profile'][$node->nid
] = array('#value' => ($node->profile
) ?
_hosting_node_link($node->profile
) : t('n/a'));
557 $form['language'][$node->nid
] = array('#value' => ($node->site_language
) ?
_hosting_language_name($node->site_language
) : t('n/a'));
558 $form['created'][$node->nid
] = array('#value' => t("@interval ago", array('@interval' => format_interval(time() - $node->created
, 1))));
559 if (sizeof($platforms) > 1) {
560 $form['platform'][$node->nid
] = array('#value' => ($node->platform
) ?
_hosting_node_link($node->platform
) : t('n/a'));
562 $form['site_class'][$node->nid
] = array('#value' => _hosting_site_list_class($node, $node->verified
));
564 $form['sites'] = array('#type' => 'checkboxes', '#options' => $sites);
565 $form['pager'] = array('#value' => theme('pager', NULL
, 25, 1));
566 $form['#theme'] = 'hosting_site_list';
567 $form['#action'] = url('hosting/sites');
570 elseif ($step == 2) {
571 $task = $form_state['values']['task'];
572 $tasks = hosting_available_tasks('site');
575 'passed' => t("The following sites will be processed"),
576 'failed' => t("The following sites failed validation checks and will not be processed")
578 foreach (array('passed', 'failed') as
$type) {
579 if (sizeof($form_state['storage'][$type])) {
580 foreach ($form_state['storage'][$type] as
$site_id => $url) {
583 array('data' => l($url, 'node/'.
$site_id), 'class' => 'hosting-status'),
585 'class' => ($type == 'passed' ?
'hosting-success' : 'hosting-error'),
592 $header = array(t('Site'));
593 $form['sites_test'] = array(
594 '#value' => theme('table', $header, $rows)
597 if (sizeof($form_state['storage']['failed']) && sizeof($form_state['storage']['passed'])) {
598 drupal_set_message(t('The task @task is not able to be performed on all the sites selected, the sites below that failed will not be added to the queue.', array('@task' => $task)), 'warning');
600 elseif (sizeof($form_state['storage']['failed'])) {
601 drupal_set_message(t('The task @task is not able to be performed on any of the selected sites.', array('@task' => $task)), 'error');
602 $form['return_link'] = array('#value' => l('Return to site listing', 'hosting/sites'));
606 $form['help'] = array('#value' => $tasks[$task]['description']);
608 $question = t('Are you sure you want to perform the "@task" task on each of the following sites?', array('@task' => $tasks[$task]['title']));
609 return confirm_form($form, $question, 'hosting/sites', '', $tasks[$task]['title']);
613 * Validate hosting_site_list form submissions.
615 function hosting_site_list_form_validate($form, &$form_state) {
616 if (isset($form_state['values']['sites'])) {
617 $sites = array_filter($form_state['values']['sites']);
618 if (count($sites) == 0) {
619 form_set_error('', t('No sites selected.'));
625 * Process hosting_site_list form submissions.
627 function hosting_site_list_form_submit($form, &$form_state) {
628 $step = isset($form_state['storage']['step']) ?
$form_state['storage']['step'] : 1;
630 $task = $form_state['values']['task'];
633 $form_state['storage']['task'] = $task;
634 // Verify tasks can be performed on each site.
635 $tasks = hosting_available_tasks('site');
637 // Filter out unchecked sites
638 $sites = array_filter($form_state['values']['sites']);
640 $operations = array();
641 foreach ($sites as
$site) {
642 $operations[] = array('hosting_sites_batch_process',
643 array($site, $task));
645 if (sizeof($operations)) {
647 'operations' => $operations,
648 'title' => t('Processing'),
649 'progress_message' => t('Evaluated @current out of @total sites.'),
650 'error_message' => t('The update has encountered an error.'),
656 $values = $form_state['values'];
657 foreach ($form_state['storage']['passed'] as
$site_id => $url) {
658 hosting_add_task($site_id, $form_state['storage']['task'], $values['parameters']);
660 unset($form_state['storage']);
662 drupal_set_message(t('The task @task will be applied to the selected sites and have been added to the task queue.', array('@task' => $form_state['storage']['task'])));
665 $form_state['storage']['step'] = $step + 1;
668 function hosting_sites_batch_process($site_id, $task, &$context) {
669 if (!isset($context['sandbox']['progress'])) {
670 $context['sandbox']['progress'] = 0;
673 $site = node_load($site_id);
674 $batch =& batch_get();
676 if (hosting_task_menu_access($site, $task)) {
677 $batch['form_state']['storage']['passed'][$site->nid
] = $site->title
;
680 $batch['form_state']['storage']['failed'][$site->nid
] = $site->title
;
685 * Implementation of hook_theme().
687 function hosting_site_theme() {
689 'hosting_site_list' => array(
690 'arguments' => array('form' => NULL
),
696 * Build the site list form.
698 function theme_hosting_site_list($form) {
699 // If there are rows in this form, then $form['site'] contains a list of
700 // the title form elements.
701 $has_posts = isset($form['site']) && is_array($form['site']);
702 $select_header = $has_posts ?
theme('table_select_header_cell') : '';
705 array('data' => t('Site'), 'field' => 'title'),
706 array('data' => t('Profile'), 'field' => 'profile'),
707 array('data' => t('Language'), 'field' => 'site_language'),
708 array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),
710 if (isset($form['platform'])) {
711 $header[] = array('data' => t('Platform'), 'field' => 'platform');
715 $output .
= drupal_render($form['options']);
717 foreach (element_children($form['site']) as
$key) {
719 $row[] = drupal_render($form['sites'][$key]);
720 $row[] = array('data' => drupal_render($form['site'][$key]), 'class'=> 'hosting-status');
721 $row[] = drupal_render($form['profile'][$key]);
722 $row[] = drupal_render($form['language'][$key]);
723 $row[] = drupal_render($form['created'][$key]);
724 if (isset($form['platform'])) {
725 $row[] = drupal_render($form['platform'][$key]);
727 $rows[] = array('data' => $row, 'class' => drupal_render($form['site_class'][$key]));
732 $rows[] = array(array('data' => t('No sites available.'), 'colspan' => sizeof($header)));
735 $output .
= theme('table', $header, $rows, array('class' => 'hosting-table'));
736 if ($form['pager']['#value']) {
737 $output .
= drupal_render($form['pager']);
740 $output .
= drupal_render($form);
747 * Define the classes that correspond to the site status.
748 * @see _hosting_site_status()
750 function _hosting_site_list_class($node, $verified = null
) {
751 $status = (is_numeric($node)) ?
$node : $node->site_status
;
752 static
$classes = array(
753 HOSTING_SITE_QUEUED
=> "hosting-queue",
754 HOSTING_SITE_ENABLED
=> "hosting-success",
755 HOSTING_SITE_DELETED
=> "hosting-error",
756 HOSTING_SITE_DISABLED
=> "hosting-error",
758 if (($status == HOSTING_SITE_ENABLED
) && (!$verified)) {
759 return 'hosting-warning';
762 return $classes[$status];
768 function hosting_site_views_api() {
771 'path' => drupal_get_path('module', 'hosting_site'),
776 function hosting_site_preprocess_views_view_table(&$variables) {
777 $view = $variables['view'];
778 if ($view->plugin_name
== 'hosting_site_list') {
779 foreach ($view->result as
$num => $result) {
780 if (isset($result->hosting_site_status
) && isset($result->hosting_site_verified
)) {
781 $variables['row_classes'][$num][] = _hosting_site_list_class($result->hosting_site_status
, $result->hosting_site_verified
);
785 $variables['class'] .
= " hosting-table";