/[drupal]/contributions/modules/hosting/hosting.module
ViewVC logotype

Contents of /contributions/modules/hosting/hosting.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.124 - (show annotations) (download) (as text)
Thu Nov 5 16:18:05 2009 UTC (2 weeks, 6 days ago) by adrian
Branch: MAIN
Changes since 1.123: +27 -35 lines
File MIME type: text/x-php
Replace crontab entry so that it _will_ work on Ubuntu and other systems using Dash with the new drush 2.1
1 <?php
2 // $Id: hosting.module,v 1.123 2009/10/25 01:05:30 mig5 Exp $
3
4 /**
5 * @file Hosting module
6 *
7 * Contains just about all the interface magic of hostmaster.
8 */
9
10 /**
11 * Not split for performance reasons. Just to keep code together.
12 */
13
14 include_once('hosting.inc');
15 include_once('hosting_help.inc');
16 include_once('hosting.queues.inc');
17 include_once('hosting.features.inc');
18
19 /**
20 * Implementation of hook_menu()
21 */
22 function hosting_menu() {
23 global $user;
24 $items = array();
25
26
27 $items['hosting/disabled'] = array(
28 'title' => 'Site disabled',
29 'page callback' => 'hosting_disabled_site',
30 'access arguments' => array('access content'),
31 'type' => MENU_CALLBACK
32 );
33
34 $items['hosting/js'] = array(
35 'title' => t('ahah callback'),
36 'page callback' => 'hosting_js_page',
37 'access arguments' => array('access content'),
38 'type' => MENU_CALLBACK
39 );
40
41 $items['hosting/maintenance'] = array(
42 'title' => 'Site maintenance',
43 'page callback' => 'hosting_site_maintenance',
44 'access arguments' => array('access content'),
45 'type' => MENU_CALLBACK
46 );
47
48
49 $items['admin/help/provision/requirements'] = array(
50 'title' => 'Provisioning requirements',
51 'description' => "Information of how to configure the provisioning system.",
52 'page callback' => 'hosting_help_requirements',
53 'type' => MENU_CALLBACK
54 );
55
56
57 $items['admin/hosting'] = array(
58 'title' => 'Hosting',
59 'description' => 'Configure and manage the hosting system',
60 'page callback' => 'drupal_get_form',
61 'page arguments' => array('hosting_features_form'),
62 'access arguments' => array('administer hosting'),
63 'type' => MENU_NORMAL_ITEM
64 );
65
66 $items['admin/hosting/features'] = array(
67 'title' => 'Features',
68 'description' => 'Configure the exposed functionality of the Hosting system',
69 'weight' => -100,
70 'page callback' => 'drupal_get_form',
71 'page arguments' => array('hosting_features_form'),
72 'type' => MENU_DEFAULT_LOCAL_TASK,
73 'access arguments' => array('administer hosting features'),
74 );
75
76 $items['admin/hosting/queues'] = array(
77 'title' => 'Queues',
78 'description' => 'Configure the frequency that cron, backup and task events are process',
79 'page callback' => 'drupal_get_form',
80 'page arguments' => array('hosting_queues_configure'),
81 'type' => MENU_LOCAL_TASK,
82 'access arguments' => array('administer hosting queues'),
83 );
84
85
86 $items['hosting/queues'] = array(
87 'page callback' => 'hosting_queues',
88 'type' => MENU_CALLBACK,
89 'access arguments' => array('access task logs')
90 );
91
92 return $items;
93 }
94
95 function hosting_js_page() {
96 modalframe_child_js();
97
98 $args = func_get_args();
99 $path = implode('/', $args);
100
101 menu_set_active_item($path);
102 # $_SERVER['REQUEST_URI'] = str_replace('/hosting/js', '', $_SERVER['REQUEST_URI']);
103
104 if ($router_item = menu_get_item($path)) {
105 if ($router_item['access']) {
106 if ($router_item['file']) {
107 require_once($router_item['file']);
108 }
109 $return = call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
110 }
111 }
112 // Menu status constants are integers; page content is a string.
113 if (is_int($return)) {
114 modalframe_close_dialog();
115 }
116 return $return;
117 }
118
119 function hosting_menu_alter(&$items) {
120 $items['node/add']['page callback'] = '_hosting_node_add';
121
122 $types = hosting_feature_node_types(TRUE);
123 foreach ($types as $feature => $type) {
124 $path = sprintf('node/add/%s', str_replace('_', '-', $type));
125 $items[$path]['access callback'] = 'hosting_menu_access';
126 $items[$path]['access arguments'] = array($type, $feature);
127 }
128
129 // These node types should remain hidden, and provide no user interface.
130 unset($items['node/add/package']);
131 unset($items['node/add/task']);
132 }
133
134 function hosting_menu_access($type, $feature) {
135 global $user;
136 return (($user->uid == 1) || user_access('create ' . $type)) && (hosting_feature($feature) != HOSTING_FEATURE_DISABLED);
137 }
138
139 function hosting_disabled_site() {
140 drupal_set_breadcrumb(array());
141 return t("This site has been disabled by the site administrators");
142 }
143
144 function hosting_site_maintenance() {
145 drupal_set_breadcrumb(array());
146 return t("This site is currently in maintenance. Check back later.");
147 }
148
149 /**
150 * Implementation of hook_nodeapi
151 *
152 * This function redirects to hosting_nodeapi_$nodetype_$op calls, to save ourselves
153 * from an incessant amount of intricately nested code, and allow easier extension / maintenance.
154 */
155 function hosting_nodeapi(&$node, $op, $teaser) {
156 $func = "hosting_nodeapi_" . $node->type . "_" . str_replace(" ", "_", $op);
157 if (function_exists($func)) {
158 $func($node, $op, $teaser);
159 }
160 }
161 /**
162 * Implementation of hook_perm
163 */
164 function hosting_perm() {
165 return array('access hosting wizard', 'administer hosting queues', 'administer hosting features', 'administer hosting');
166 }
167
168 /**
169 * Implementation of hook_init
170 */
171 function hosting_init() {
172 // Definitions for the default platforms, clients etc.
173 // Done to avoid using 'magic numbers'
174 define('HOSTING_DEFAULT_CLIENT', variable_get('hosting_default_client', 1));
175 define('HOSTING_DEFAULT_DB_SERVER', variable_get('hosting_default_db_server', 2));
176 define('HOSTING_DEFAULT_WEB_SERVER', variable_get('hosting_default_web_server', 3));
177 define('HOSTING_DEFAULT_PLATFORM', variable_get('hosting_default_platform', 6));
178
179 define('HOSTING_OWN_DB_SERVER', variable_get('hosting_own_db_server', 2));
180 define('HOSTING_OWN_WEB_SERVER', variable_get('hosting_own_web_server', 3));
181 define('HOSTING_OWN_PLATFORM', variable_get('hosting_own_platform', 6));
182
183
184 // These defaults could be temporary.
185 $info = posix_getgrgid(posix_getgid());
186 define('HOSTING_DEFAULT_WEB_GROUP', $info['name']);
187 $user = get_current_user();
188 if ($user == 'root') {
189 $user = 'aegir'; # a better default than root
190 }
191 define('HOSTING_DEFAULT_SCRIPT_USER', $user);
192
193 define('HOSTING_DEFAULT_RESTART_CMD', _hosting_default_restart_cmd());
194
195 $path = ($_SERVER['PWD']) ? $_SERVER['PWD'] : $_SERVER['DOCUMENT_ROOT'];
196 define('HOSTING_DEFAULT_DOCROOT_PATH',rtrim($path, '/'));
197
198 $parts = explode("/", rtrim($path, '/'));
199 array_pop($parts);
200 define('HOSTING_DEFAULT_PARENT_PATH', rtrim(implode("/" , $parts), '/'));
201 define('HOSTING_DEFAULT_BACKUP_PATH', HOSTING_DEFAULT_PARENT_PATH . '/backups');
202 define('HOSTING_DEFAULT_CONFIG_PATH', HOSTING_DEFAULT_PARENT_PATH .'/config');
203 define('HOSTING_DEFAULT_VHOST_PATH', HOSTING_DEFAULT_CONFIG_PATH .'/vhost.d');
204
205 /**
206 * Find the base URL, this is used by the initial 'hosting setup' drush command
207 * This gets defined in the bootstrap, so just using the global definition.
208 */
209 define('HOSTING_DEFAULT_BASE_URL', $GLOBALS['base_url']);
210
211 // moved from hook_menu()
212 drupal_add_css(drupal_get_path('module', 'hosting') . '/hosting.css');
213 }
214
215
216 /**
217 * This function should not have to be duplicated between hosting/provision
218 */
219 function _hosting_default_restart_cmd() {
220 $command = '/usr/sbin/apachectl'; # a proper default for most of the world
221 foreach (explode(':', $_SERVER['PATH']) as $path) {
222 $options[] = "$path/apache2ctl";
223 $options[] = "$path/apachectl";
224 }
225 # try to detect the apache restart command
226 $options[] = '/usr/local/sbin/apachectl'; # freebsd
227 $options[] = '/usr/sbin/apache2ctl'; # debian + apache2
228 $options[] = $command;
229
230 foreach ($options as $test) {
231 if (is_executable($test)) {
232 $command = $test;
233 break;
234 }
235 }
236
237 return "sudo $command graceful";
238 }
239
240 /**
241 * Implementation of hook_theme().
242 */
243 function hosting_theme() {
244 return array(
245 'hosting_summary_block' => array(
246 'file' => 'hosting.module',
247 'arguments' => array(
248 'components' => NULL,
249 ),
250 ),
251 'hosting_queues_configure' => array(
252 'file' => 'hosting.module',
253 'arguments' => array(
254 'form' => NULL,
255 ),
256 ),
257 'requirement_help' => array(
258 'file' => 'hosting_help.inc',
259 'arguments' => array(
260 'form' => NULL,
261 ),
262 ),
263 );
264 }
265
266 function _hosting_node_link($nid, $title = null) {
267 if (is_null($nid)) {
268 return t("None");
269 }
270 $node = node_load($nid);
271 $title = (!is_null($title)) ? $title : filter_xss($node->title);
272 if ($node->nid) {
273 return node_access('view', $node) ? l($title, "node/" . $node->nid) : filter_xss($node->title);
274 }
275 }
276
277
278 function hosting_block($op = 'list', $delta = 0, $edit = array()) {
279 switch ($op) {
280 case 'list' :
281 $blocks['hosting_summary'] = array('info' => t('Hosting summary'),
282 'enabled' => 1, 'region' => 'left', 'weight' => 10);
283 $blocks['hosting_queues'] = array('info' => t('Hosting queues'),
284 'enabled' => 1, 'region' => 'right', 'weight' => 0);
285 $blocks['hosting_queues_summary'] = array('info' => t('Hosting queues summary'),
286 'enabled' => 1, 'region' => 'right', 'weight' => 1);
287 return $blocks;
288 case 'view' :
289 switch ($delta) {
290 case 'hosting_summary':
291 return array(
292 'title' => t('Summary'),
293 'content' => hosting_summary_block());
294 break;
295 case 'hosting_queues':
296 return array('title' => t('Queues'),
297 'content' => hosting_queue_block());
298 break;
299 case 'hosting_queues_summary':
300 return array('title' => t('Queues summary'),
301 'content' => hosting_queue_summary_block());
302 break;
303 }
304 }
305 }
306
307 function hosting_queue_summary_block() {
308 if (user_access('administer hosting queues')) {
309 $queues = hosting_get_queues();
310 $output = '';
311 foreach ($queues as $queue => $info) {
312 $disp = array();
313 # special case
314 if (!$info['enabled']) {
315 $disp[] = t('Status: disabled');
316 continue;
317 }
318 $disp[] = t('Status: enabled');
319 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) {
320 if ($key == 'last_run') {
321 $info[$key] = hosting_format_interval($info[$key]);
322 } elseif ($key == 'frequency') {
323 $info[$key] = t('every @interval', array('@interval' => format_interval($info[$key])));
324 }
325 $disp[] = $title . ": " . $info[$key];
326 }
327 $output .= theme('item_list', $disp, $info['name']);
328 }
329 return $output;
330 }
331 }
332
333 function hosting_queue_block() {
334 if (user_access('access task logs')) {
335 $queues = hosting_get_queues();
336 $output = '';
337 foreach ($queues as $queue => $info) {
338 $func = 'hosting_'.$info['singular'].'_summary';
339 if (function_exists($func)) {
340 $output .= $func();
341 }
342 }
343 return $output;
344 }
345 }
346
347 function hosting_summary_block() {
348 if (user_access('administer hosting')) {
349 return theme('hosting_summary_block', module_invoke_all('hosting_summary'));
350 }
351 }
352
353 function theme_hosting_summary_block($components) {
354 foreach ($components as $component) {
355 $output .= $component;
356 }
357 return $output;
358 }
359
360 /**
361 * Check site URL is allowed
362 *
363 * This function hooks into hook_allow_domain to let contrib modules
364 * weigh in on whether the site should be created.
365 *
366 * All the hooks must return true for the domain to be allowed.
367 */
368 function hosting_domain_allowed($url, $params = array()) {
369 $results = module_invoke_all('allow_domain', $url, $params);
370 $return = !in_array(FALSE, $results);
371 return $return;
372 }
373
374 /**
375 * Initial hosting setup
376 * Runs the 'hosting dispatch' command, and
377 * move on to setting up the crontab
378 */
379 function hosting_setup() {
380 if (drush_confirm("This command will replace your crontab for this user. continue?")) {
381 variable_set('hosting_dispatch_enabled', FALSE);
382 // attempt to run the dispatch command, to make sure it runs without the queue being enabled.
383 variable_set('hosting_dispatch_enabled', TRUE);
384 drush_print(_hosting_dispatch_cmd());
385 exec(_hosting_dispatch_cmd(), $return, $code);
386 variable_set('hosting_dispatch_enabled', FALSE);
387 $return = join("\n", $return);
388 $data = unserialize($return);
389 if ($code == DRUSH_SUCCESS) {
390 variable_set('hosting_dispatch_enabled', TRUE);
391 drush_log(t("Dispatch command was run successfully"), 'success');
392 _hosting_setup_cron();
393 }
394 else {
395 drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("Dispatch command could not be run. Returned: \n@return", array('@return' => $return)));
396 }
397 if (drush_get_error()) {
398 drush_log(t("The command did not complete successfully, please fix the issues and re-run this script"), 'error');
399 }
400 }
401 }
402
403 /**
404 * Set up the hosting dispatch command in the aegir user's crontab
405 * Replace the crontab entry if it exists, else create it from scratch
406 */
407 function _hosting_setup_cron() {
408 $existing = FALSE;
409 exec('crontab -l 2> /dev/null', $cron);
410 variable_set('hosting_cron_backup', $cron);
411 if (sizeof($cron)) {
412 drush_log("Your existing cron entry will be replaced.", 'warning');
413 exec('crontab -r 2> /dev/null');
414 $cron = array();
415 }
416 else {
417 drush_log("message", t("No existing crontab was found"));
418 }
419
420 $cron[] = hosting_queues_cron_cmd();
421
422 $tmpnam = tempnam('hostmaster', 'hm.cron');
423 $fp = fopen($tmpnam, "w");
424 foreach ($cron as $line) {
425 fwrite($fp, $line . "\n");
426 }
427 fclose($fp);
428 system(sprintf('crontab %s', escapeshellarg($tmpnam)));
429 unlink($tmpnam);
430 drush_log("Notice", t("Installed hosting dispatch cron entry to run every minute"));
431 return null;
432 }
433
434
435 /**
436 * Replacement node/add page.
437 *
438 * Major kludge to remove the hidden node types from node/add page
439 *
440 * Copied from node.module
441 */
442 function _hosting_node_add($type = '') {
443 global $user;
444
445 $types = node_get_types();
446 $type = ($type) ? str_replace('-', '_', $type) : NULL;
447 // If a node type has been specified, validate its existence.
448 if (isset($types[$type]) && user_access('create ' . $type) && (hosting_feature($type) !== HOSTING_FEATURE_DISABLED)) {
449 // Initialize settings:
450 $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type);
451
452 drupal_set_title(t('Submit @name', array('@name' => $types[$type]->name)));
453 $output = drupal_get_form($type .'_node_form', $node);
454 }
455 else {
456 // If no (valid) node type has been provided, display a node type overview.
457 foreach ($types as $type) {
458 if (function_exists($type->module .'_form') && user_access('create ' . $type->type) && (hosting_feature($type->type) !== HOSTING_FEATURE_DISABLED)) {
459 $type_url_str = str_replace('_', '-', $type->type);
460 $title = t('Add a new @s.', array('@s' => $type->name));
461 $out = '<dt>'. l(drupal_ucfirst($type->name), "node/add/$type_url_str", array('attributes' => array('title' => $title))) .'</dt>';
462 $out .= '<dd>'. filter_xss_admin($type->description) .'</dd>';
463 $item[$type->name] = $out;
464 }
465 }
466
467 if (isset($item)) {
468 uksort($item, 'strnatcasecmp');
469 $output = t('Choose the appropriate item from the list:') .'<dl>'. implode('', $item) .'</dl>';
470 }
471 else {
472 $output = t('No content types available.');
473 }
474 }
475
476 return $output;
477 }
478
479 /**
480 * List queues or tasks in a queue if a key is provided
481 */
482
483 function hosting_queues($key='') {
484 $queues = hosting_get_queues();
485
486 if ($queues[$key]) {
487 if ($queues[$key]['name'])
488 {
489 $output .= "<h1>".$queues[$key]['name']."</h1>";
490 }
491
492 $func = 'hosting_'.$queues[$key]['singular'].'_list';
493 if (function_exists($func)) {
494 $output .= $func();
495 }
496 }
497 else
498 {
499 foreach($queues as $key => $queue) {
500 $item[] = l($queue['name'], 'hosting/queues/'.$key);
501 }
502 $output .= theme('item_list', $item, t('Queues'));
503 }
504
505 return $output;
506 }
507
508 /**
509 * Generate context sensitive breadcrumbs
510 */
511 function hosting_set_breadcrumb($node) {
512 $breadcrumbs[] = l(t('Home'), NULL);
513 switch ($node->type) {
514 case 'task':
515 $breadcrumbs[] = _hosting_node_link($node->rid);
516 break;
517 case 'platform' :
518 $breadcrumbs[] = _hosting_node_link($node->web_server);
519 break;
520 case 'site' :
521 $breadcrumbs[] = _hosting_node_link($node->platform);
522 break;
523 }
524 drupal_set_breadcrumb($breadcrumbs);
525 }
526
527 /**
528 * Page callback
529 *
530 * Configure the frequency of tasks.
531 */
532 function hosting_queues_configure() {
533 drupal_add_css(drupal_get_path('module', 'hosting') . '/hosting.css');
534 $units = array(
535 strtotime("1 second", 0) => t("Seconds"),
536 strtotime("1 minute", 0) => t("Minutes"),
537 strtotime("1 hour", 0) => t("Hours"),
538 strtotime("1 day", 0) => t("Days"),
539 strtotime("1 week", 0) => t("Weeks"),
540 );
541
542 $queues = hosting_get_queues();
543 $form['#tree'] = TRUE;
544
545 foreach ($queues as $queue => $info) {
546 $form[$queue]['description'] = array(
547 '#type' => 'item',
548 '#value' => $info['name'],
549 '#description' => $info['description']
550 );
551
552 $form[$queue]["enabled"] = array(
553 '#type' => 'checkbox',
554 '#default_value' => $info['enabled']
555 );
556
557 $form[$queue]["last_run"] = array(
558 '#value' => hosting_format_interval(variable_get('hosting_queue_' . $queue . '_last_run', false))
559 );
560 $form[$queue]['frequency']['#prefix'] = "<div class='hosting-queue-frequency'>";
561 $form[$queue]['frequency']['#suffix'] = "</div>";
562
563 if ($info['type'] == 'batch') {
564 $form[$queue]['frequency']['items'] = array(
565 '#value' => t('%count %items every ', array("%count" => $info['total_items'], "%items" => format_plural($info['total_items'], $info['singular'], $info['plural']))),
566 );
567 }
568 else {
569 $form[$queue]['frequency']['items'] = array(
570 '#type' => 'textfield',
571 '#size' => 3,
572 '#maxlength' => 3,
573 '#default_value' => $info['items'],
574 '#suffix' => t(' %items every ', array('%items' => $info['plural'])),
575 );
576 }
577 foreach (array_reverse(array_keys($units)) as $length) {
578 $unit = $units[$length];
579
580 if (!($info['frequency'] % $length)) {
581 $frequency_ticks = $info['frequency'] / $length;
582 $frequency_length = $length;
583 break;
584 }
585 }
586 $form[$queue]['frequency']["ticks"] = array(
587 '#type' => 'textfield',
588 '#default_value' => $frequency_ticks,
589 '#maxlength' => 5,
590 '#size' => 5
591 );
592 $form[$queue]['frequency']["unit"] = array(
593 '#type' => 'select',
594 '#options' => $units,
595 '#default_value' => $frequency_length,
596 );
597 }
598 $form['submit'] = array('#type' => 'submit', '#value' => t('Save changes'));
599 return $form;
600 }
601
602 function theme_hosting_queues_configure($form) {
603 $queues = hosting_get_queues();
604
605 $rows = array();
606 $header = array('', t('Description'),
607 array('data' => t('Frequency'), 'class' => 'hosting-queue-frequency-head'),
608 t('Last run'),);
609 foreach ($queues as $key => $info) {
610 $row = array();
611 $row[] = drupal_render($form[$key]['enabled']);
612 $row[] = drupal_render($form[$key]['description']);
613 $row[] = drupal_render($form[$key]['frequency']);
614 $row[] = drupal_render($form[$key]['last_run']);
615 $rows[] = $row;
616 }
617 $output = theme('table', $header, $rows);
618 $output .= drupal_render($form['submit']);
619 $output .= drupal_render($form);
620 return $output;
621 }
622
623 /**
624 * Implementation of hook_validate()
625 */
626 function hosting_queues_configure_validate($form, &$form_state) {
627 foreach (hosting_get_queues() as $queue => $info) {
628 if ($form_state['values'][$queue]) {
629 if ($form_state['values'][$queue]['frequency']['ticks'] && !is_numeric($form_state['values'][$queue]['frequency']['ticks'])) {
630 form_set_error($queue, t('Please enter a valid frequency.'));
631 }
632 if ($form_state['values'][$queue]['frequency']['items'] && !is_numeric($form_state['values'][$queue]['frequency']['items'])) {
633 form_set_error($queue, t('Please enter a valid amount of items.'));
634 }
635 }
636 }
637 }
638
639 /**
640 * Implementation of hook_submit()
641 */
642 function hosting_queues_configure_submit($form, &$form_state) {
643 foreach (hosting_get_queues() as $queue => $info) {
644 if ($form_state['values'][$queue]) {
645 variable_set("hosting_queue_" . $queue . "_enabled", $form_state['values'][$queue]['enabled']);
646 variable_set("hosting_queue_" . $queue . "_frequency", $form_state['values'][$queue]['frequency']['ticks'] * $form_state['values'][$queue]['frequency']['unit']);
647 if ($info['type'] == 'serial') {
648 variable_set("hosting_queue_" . $queue . "_items", $form_state['values'][$queue]['frequency']['items']);
649 }
650 }
651 }
652 drupal_set_message(t('The queue settings have been updated.'));
653 }
654
655 /**
656 * Implementation of hook_form_alter()
657 */
658 function hosting_form_alter(&$form, &$form_state, $form_id) {
659 // Alter the 'Add User' form to remind users that this is not the New Client form
660 if ($form_id == 'user_register') {
661 $form[user_registration_help] = array(
662 '#type' => 'item',
663 '#description' => t('<strong>Adding a system user account does not make the user a Client that can add sites.</strong><br />
664 To add a Client, enable the Client feature and then add a new Client node.<br />
665 If you wish, you may then assign this system user to the Client as an \'Allowed user\' to inherit the permissions to add sites.'),
666 '#weight' => '-10'
667 );
668 }
669 }
670

  ViewVC Help
Powered by ViewVC 1.1.2