hosting_server.module

Hook implementations for the Hosting server module.

Functions

Namesort descending Description
hosting_get_servers Get servers providing a service.
hosting_nodeapi_server_delete_revision Implements hook_delete_revision().
hosting_nodeapi_server_presave Implements hook_presave().
hosting_server_delete Implements hook_delete().
hosting_server_form Implements hook_form().
hosting_server_form_alter Hide the delete button on server nodes.
hosting_server_form_hosting_settings_alter Implements hook_form_FORM_ID_alter().
hosting_server_hosting_tasks Implements hook_hosting_tasks().
hosting_server_init_services Initializes the service objects associated with a server node object.
hosting_server_insert Implements hook_insert().
hosting_server_invoke_services Invoke a method on all enabled services.
hosting_server_load Implements hook_load().
hosting_server_menu Implements hook_menu().
hosting_server_node_access Implements hook_node_access().
hosting_server_node_info Implements hook_node_info().
hosting_server_permission Implements hook_permission().
hosting_server_preprocess Implements hook_preprocess().
hosting_server_services Return an associative array of services enabled on this system.
hosting_server_services_from_post Translate a server form submission into populated server objects on the node.
hosting_server_update Implements hook_update().
hosting_server_validate Implements hook_validate().
hosting_server_view Implements hook_view().
hosting_server_views_api Views integration.
hosting_server_wildcard_load Menu wildcard loader callback.
hosting_services_add Add a service to an existing server node.
hosting_services_new_object Factory method for generating new instance of a service class.
_hosting_server_list_class Define the classes that correspond to the platform status.
_hosting_server_preprocess_classes Add our service availability classes.
_hosting_server_preprocess_expand Since we're adding headers and fields to our table render array, we need to adjust other $variables as their passed to the template.
_hosting_server_preprocess_explode Add fields to table headers or rows.
_hosting_server_status Return the appropriate status label.
_hosting_server_status_codes Helper function to map status codes to labels and classes.

Constants

Namesort descending Description
HOSTING_SERVER_DELETED This server has been deleted.
HOSTING_SERVER_ENABLED This server is created and enabled. Its services will be available for use.
HOSTING_SERVER_LOCKED This server has been locked. Its services will not be available for use.
HOSTING_SERVER_QUEUED This platform has been queued for registration (and eventually creation).

File

server/hosting_server.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * Hook implementations for the Hosting server module.
  5. */
  6. include_once "hosting_server.service.inc";
  7. include_once "hosting.ip.inc";
  8. /**
  9. * This server has been deleted.
  10. */
  11. define('HOSTING_SERVER_DELETED', -2);
  12. /**
  13. * This server has been locked. Its services will not be available for use.
  14. */
  15. define('HOSTING_SERVER_LOCKED', -1);
  16. /**
  17. * This platform has been queued for registration (and eventually creation).
  18. */
  19. define('HOSTING_SERVER_QUEUED', 0);
  20. /**
  21. * This server is created and enabled. Its services will be available for use.
  22. */
  23. define('HOSTING_SERVER_ENABLED', 1);
  24. /**
  25. * Helper function to map status codes to labels and classes.
  26. */
  27. function _hosting_server_status_codes() {
  28. $codes = array(
  29. HOSTING_SERVER_QUEUED => array(
  30. 'label' => 'Queued',
  31. 'class' => 'hosting-queue',
  32. ),
  33. HOSTING_SERVER_ENABLED => array(
  34. 'label' => 'Enabled',
  35. 'class' => 'hosting-success',
  36. ),
  37. HOSTING_SERVER_DELETED => array(
  38. 'label' => 'Deleted',
  39. 'class' => 'hosting-error',
  40. ),
  41. HOSTING_SERVER_LOCKED => array(
  42. 'label' => 'Locked',
  43. 'class' => 'hosting-disable',
  44. ),
  45. );
  46. return $codes;
  47. }
  48. /**
  49. * Return the appropriate status label.
  50. */
  51. function _hosting_server_status($status) {
  52. static $labels;
  53. $labels = _hosting_server_status_codes();
  54. return is_object($status) ? $labels[$status->server_status]['label'] : $labels[$status]['label'];
  55. }
  56. /**
  57. * Define the classes that correspond to the platform status.
  58. */
  59. function _hosting_server_list_class($status) {
  60. static $labels;
  61. $labels = _hosting_server_status_codes();
  62. return is_object($status) ? $labels[$status->server_status]['class'] : $labels[$status]['class'];
  63. }
  64. /**
  65. * Implements hook_node_info().
  66. */
  67. function hosting_server_node_info() {
  68. $types["server"] = array(
  69. "type" => 'server',
  70. "name" => t('Server'),
  71. 'base' => 'hosting_server',
  72. "has_title" => TRUE,
  73. "title_label" => t('Hostname'),
  74. "description" => hosting_node_help("server"),
  75. "has_body" => 0,
  76. "body_label" => '',
  77. "min_word_count" => 0,
  78. );
  79. return $types;
  80. }
  81. /**
  82. * Implements hook_menu().
  83. */
  84. function hosting_server_menu() {
  85. $items = array();
  86. $items['hosting/servers/list'] = array(
  87. 'title' => 'List',
  88. 'type' => MENU_DEFAULT_LOCAL_TASK,
  89. 'weight' => -10,
  90. );
  91. $items['hosting/servers/add'] = array(
  92. 'title' => 'Add server',
  93. 'type' => MENU_LOCAL_TASK,
  94. 'page callback' => 'drupal_goto',
  95. 'page arguments' => array('node/add/server'),
  96. 'access callback' => 'node_access',
  97. 'access arguments' => array('create', 'server'),
  98. );
  99. return $items;
  100. }
  101. /**
  102. * Implements hook_hosting_tasks().
  103. */
  104. function hosting_server_hosting_tasks() {
  105. $tasks = array();
  106. $tasks['server']['verify'] = array(
  107. 'title' => t('Verify'),
  108. 'description' => t('Verify that the server is correctly installed and working.'),
  109. 'weight' => 10,
  110. 'provision_save' => TRUE,
  111. );
  112. $tasks['server']['delete'] = array(
  113. 'title' => t('Delete'),
  114. 'description' => t('Delete the server.'),
  115. );
  116. return $tasks;
  117. }
  118. /**
  119. * Implements hook_permission().
  120. */
  121. function hosting_server_permission() {
  122. return array(
  123. 'administer server' => array(
  124. 'title' => t('administer server'),
  125. ),
  126. 'create server' => array(
  127. 'title' => t('create server'),
  128. ),
  129. 'view server' => array(
  130. 'title' => t('view server'),
  131. ),
  132. 'edit server' => array(
  133. 'title' => t('edit server'),
  134. ),
  135. 'delete server' => array(
  136. 'title' => t('delete server'),
  137. ),
  138. );
  139. }
  140. /**
  141. * Implements hook_node_access().
  142. */
  143. function hosting_server_node_access($node, $op, $account) {
  144. // @todo Verify that this ought not be going through node_grants()
  145. if (!hosting_feature('server')) {
  146. return FALSE;
  147. }
  148. switch ($op) {
  149. case 'create':
  150. return user_access('create server', $account);
  151. case 'view':
  152. return user_access('view server', $account);
  153. case 'update':
  154. return user_access('edit server', $account);
  155. case 'delete':
  156. return user_access('delete server', $account);
  157. }
  158. }
  159. /**
  160. * Return an associative array of services enabled on this system.
  161. */
  162. function hosting_server_services() {
  163. static $services;
  164. if (!isset($services)) {
  165. foreach (module_implements('hosting_service_type') as $module) {
  166. // Load all service_type classes.
  167. module_load_include('service.inc', $module);
  168. }
  169. $services = module_invoke_all('hosting_service_type');
  170. foreach (module_implements('hosting_service') as $module) {
  171. foreach (module_invoke($module, 'hosting_service') as $service => $service_type) {
  172. // Load all service classes.
  173. module_load_include('service.inc', $module);
  174. $services[$service_type]['types'][$service] = 'hostingService_' . $service_type . '_' . $service;
  175. }
  176. }
  177. }
  178. return $services;
  179. }
  180. /**
  181. * Factory method for generating new instance of a service class.
  182. */
  183. function hosting_services_new_object($name, $type, $node, $values = NULL) {
  184. $services = hosting_server_services();
  185. $class = $services[$name]['types'][$type];
  186. if (!$class) {
  187. return;
  188. }
  189. return new $class($node, $values);
  190. }
  191. /**
  192. * Add a service to an existing server node.
  193. */
  194. function hosting_services_add(&$node, $name, $type, $values = array()) {
  195. $values['available'] = (isset($values['available'])) ? $values['available'] : 1;
  196. if ($service_object = hosting_services_new_object($name, $type, $node, $values)) {
  197. $node->services[$name] = $service_object;
  198. }
  199. }
  200. /**
  201. * Initializes the service objects associated with a server node object.
  202. *
  203. * @paramc $node
  204. * The server node object.
  205. *
  206. * @return Object
  207. * The server node object with an additional 'services' property. The
  208. * services property is an associative array with the name of the service
  209. * as key, and the service object as value.
  210. */
  211. function hosting_server_init_services(&$node) {
  212. $node->services = array();
  213. $result = db_query("SELECT service, type
  214. FROM {hosting_service}
  215. WHERE vid = :vid
  216. AND available = :available",
  217. array(
  218. ':vid' => $node->vid,
  219. ':available' => 1,
  220. )
  221. );
  222. foreach ($result as $record) {
  223. $name = $record->service;
  224. if ($service_object = hosting_services_new_object($name, $record->type, $node)) {
  225. $node->services[$name] = $service_object;
  226. }
  227. }
  228. return $node;
  229. }
  230. /**
  231. * Translate a server form submission into populated server objects on the node.
  232. */
  233. function hosting_server_services_from_post(&$node) {
  234. if (isset($node->services) && is_array($node->services)) {
  235. foreach (hosting_server_services() as $name => $data) {
  236. if (isset($node->services[$name]) && is_array($node->services[$name])) {
  237. if ($node->services[$name]['type'] !== 'null') {
  238. if ($service_object = hosting_services_new_object($name, $node->services[$name]['type'], $node, $node->services[$name][$node->services[$name]['type']])) {
  239. $node->services[$name] = hosting_services_new_object($name, $node->services[$name]['type'], $node, $node->services[$name][$node->services[$name]['type']]);
  240. $node->services[$name]->available = TRUE;
  241. }
  242. }
  243. else {
  244. unset($node->services[$name]);
  245. }
  246. }
  247. }
  248. }
  249. }
  250. /**
  251. * Hide the delete button on server nodes.
  252. */
  253. function hosting_server_form_alter(&$form, &$form_state, $form_id) {
  254. // Remove delete button from server edit form,
  255. // unless the server's already been deleted via the Delete task.
  256. if ($form_id == 'server_node_form') {
  257. $node = $form['#node'];
  258. if (isset($node->server_status) && $node->server_status !== HOSTING_SERVER_DELETED) {
  259. $form['actions']['delete']['#type'] = 'hidden';
  260. }
  261. }
  262. }
  263. /**
  264. * Invoke a method on all enabled services.
  265. */
  266. function hosting_server_invoke_services(&$node, $method, &$arg1 = NULL, &$arg2 = NULL, &$arg3 = NULL, &$arg4 = NULL) {
  267. $return = array();
  268. foreach ($node->services as $name => $service) {
  269. $result = $service->$method($arg1, $arg2, $arg3, $arg4);
  270. if (isset($result) && is_array($result)) {
  271. $return = array_merge_recursive($return, $result);
  272. }
  273. elseif (isset($result)) {
  274. $return[] = $result;
  275. }
  276. }
  277. return $return;
  278. }
  279. /**
  280. * Implements hook_form().
  281. */
  282. function hosting_server_form($node, &$form_state) {
  283. drupal_add_js(drupal_get_path('module', 'hosting_server') . '/hosting_server.js');
  284. $form['title'] = array(
  285. '#type' => 'textfield',
  286. '#title' => t('Server hostname'),
  287. '#required' => TRUE,
  288. '#default_value' => $node->title,
  289. '#description' => t('The host name of the server. This is the address that will be used by Hostmaster to connect to the server to issue commands. It is to resolve to the internal network, if you have such a separation.<br /><strong>Be careful when changing the node title, it is used to create the SQL users if the IP address, below, is not set.</strong>'),
  290. '#weight' => 0,
  291. );
  292. $form['human_name'] = array(
  293. '#type' => 'textfield',
  294. '#title' => t('Human-readable name'),
  295. '#default_value' => isset($node->human_name) ? $node->human_name : '',
  296. '#description' => t('<strong>Optional, but recommended.</strong> Internally, Aegir refers to servers by their hostname. The value of this field will be used throughout the UI, and allows for more meaningful names. While not enforced by the system, it is highly recommended that the name assigned to a server be unique.'),
  297. '#weight' => 1,
  298. );
  299. $form['services'] = array(
  300. '#weight' => 5,
  301. '#tree' => TRUE,
  302. );
  303. if (!isset($node->nid)) {
  304. $node->services = array();
  305. $node->ip_addresses = array();
  306. }
  307. // Taken mostly from the hosting_site alias stuff.
  308. $form['ips_wrapper'] = array(
  309. '#title' => t('IP addresses'),
  310. '#description' => t('A list of IP addresses this server is publicly available under, one per line. If none is specified, X509 certificates will be offered through the SNI mechanism by TLS-enabled webservers. <br /><strong>This should point to the public network, if you have such a separation.</strong>'),
  311. '#type' => 'fieldset',
  312. '#tree' => FALSE,
  313. '#weight' => 2,
  314. '#prefix' => '<div class="clear-block" id="hosting-ips-wrapper">',
  315. '#suffix' => '</div>',
  316. );
  317. $form['ips_wrapper']['ip_addresses'] = array(
  318. '#prefix' => '<div id="hosting-ip-addresses">',
  319. '#suffix' => '</div>',
  320. '#tree' => TRUE,
  321. // '#theme' => 'hosting_alias_form',
  322. );
  323. // Get the list of existing IPs.
  324. $ips = $node->ip_addresses;
  325. $ips_count = max(3, empty($ips) ? 3 : count($ips) + 1);
  326. // Add alias textfields.
  327. foreach ($ips as $id => $ip) {
  328. $form['ips_wrapper']['ip_addresses'][$id] = array(
  329. '#type' => 'textfield',
  330. '#default_value' => $ip,
  331. );
  332. $ips_count--;
  333. }
  334. $form['ips_wrapper']['new_ip_addresses'] = array(
  335. '#prefix' => '<div id="hosting-ip-addresses">',
  336. '#suffix' => '</div>',
  337. '#tree' => TRUE,
  338. // '#theme' => 'hosting_alias_form',
  339. );
  340. for ($delta = 0; $delta < $ips_count; $delta++) {
  341. $form['ips_wrapper']['new_ip_addresses'][$delta] = array(
  342. '#type' => 'textfield',
  343. );
  344. }
  345. foreach (hosting_server_services() as $name => $service) {
  346. $form['services'][$name] = array(
  347. '#type' => 'fieldset',
  348. '#title' => check_plain($service['title']),
  349. '#prefix' => '<div class="hosting-service-form">',
  350. '#suffix' => '</div>',
  351. '#weight' => isset($service['weight']) ? $service['weight'] : 0,
  352. );
  353. $form['services'][$name]['type'] = array(
  354. '#type' => 'radios',
  355. '#weight' => -99,
  356. '#options' => array_merge(array('null' => 'None'), drupal_map_assoc(array_keys($service['types']))),
  357. '#default_value' => isset($node->services[$name]->available) && $node->services[$name]->available ? $node->services[$name]->type : 'null',
  358. );
  359. // Service-specific configuration.
  360. foreach ($service['types'] as $type => $class) {
  361. $form['services'][$name][$type] = array(
  362. '#prefix' => '<div id="provision-service-settings-' . $name . '-' . $type . '" class="provision-service-settings-' . $name . '">',
  363. '#suffix' => '</div>',
  364. );
  365. if (isset($node->services[$name]) && $node->services[$name]->type === $type) {
  366. $node->services[$name]->form($form['services'][$name][$type]);
  367. }
  368. else {
  369. $service_object = hosting_services_new_object($name, $type, $node);
  370. $service_object->form($form['services'][$name][$type]);
  371. }
  372. }
  373. }
  374. foreach (array('verified', 'platform_status') as $extra_attribute) {
  375. $form["$extra_attribute"] = array(
  376. '#type' => 'value',
  377. '#value' => isset($node->$extra_attribute) ? $node->$extra_attribute : NULL,
  378. );
  379. }
  380. return $form;
  381. }
  382. /**
  383. * Implements hook_presave().
  384. *
  385. * We notice the operator if no IP was given. We do not set any to allow
  386. * operators to use SNI. We also fire up the regular services hooks.
  387. */
  388. function hosting_nodeapi_server_presave(&$node) {
  389. if (empty($node->ip_addresses)) {
  390. // This returns an array or FALSE.
  391. $ips = gethostbynamel($node->title);
  392. if ($ips) {
  393. drupal_set_message(t('No IP addresses provided for server. We guess the IP is %ip based on hostname %name. You should set IP addreses in the server node unless you are ready to use SNI (Server Name Indication) which is incompatible with IE and Safari on Windows XP.', array('%ip' => join(',', $ips), '%name' => $node->title)), 'message');
  394. }
  395. }
  396. hosting_server_services_from_post($node);
  397. }
  398. /**
  399. * Implements hook_validate().
  400. */
  401. function hosting_server_validate(&$node, &$form) {
  402. if ($node->op != t('Delete')) {
  403. // Make sure the server name is unique, to avoid context clashes.
  404. $result = db_query("SELECT n.title AS name
  405. FROM {hosting_server} AS h
  406. INNER JOIN {node} AS n ON n.nid = h.nid
  407. WHERE n.title = :title
  408. AND n.nid <> :nid
  409. AND h.status >= :status",
  410. array(
  411. ':title' => $node->title,
  412. ':nid' => empty($node->nid) ? '' : $node->nid,
  413. ':status' => HOSTING_SERVER_LOCKED,
  414. )
  415. )->fetchField();
  416. if ($result) {
  417. form_set_error('title',
  418. t('The hostname `%name` is already in use. Server names must be unique.',
  419. array('%name' => $result))
  420. );
  421. }
  422. _hosting_ip_validate($node);
  423. if (!_hosting_valid_fqdn($node->title)) {
  424. form_set_error('title', t('Invalid hostname. Use a proper hostname or an IP address.'));
  425. }
  426. hosting_server_services_from_post($node);
  427. hosting_server_invoke_services($node, 'validate', $node, $form);
  428. }
  429. }
  430. /**
  431. * Implements hook_update().
  432. *
  433. * As an existing node is being updated in the database, we need to do our own
  434. * database updates.
  435. */
  436. function hosting_server_update($node) {
  437. // If this is a new node or we're adding a new revision.
  438. if (!empty($node->revision)) {
  439. hosting_server_insert($node);
  440. }
  441. else {
  442. hosting_server_invoke_services($node, 'save', $node);
  443. hosting_server_invoke_services($node, 'update', $node);
  444. // Remove disabled services.
  445. foreach (array_diff(array_keys(hosting_server_services()), array_keys($node->services)) as $name) {
  446. db_delete('hosting_service')
  447. ->condition('service', $name)
  448. ->condition('nid', $node->nid)
  449. ->execute();
  450. }
  451. }
  452. if ($node->server_status == HOSTING_SERVER_DELETED) {
  453. $node->no_verify = TRUE;
  454. }
  455. $id = db_update('hosting_server')
  456. ->fields(array(
  457. 'human_name' => $node->human_name,
  458. 'verified' => $node->verified,
  459. 'status' => $node->server_status,
  460. ))
  461. ->condition('nid', $node->nid)
  462. ->execute();
  463. _hosting_ip_save($node, TRUE);
  464. if (!isset($node->no_verify) || !$node->no_verify) {
  465. hosting_add_task($node->nid, 'verify');
  466. }
  467. }
  468. /**
  469. * Implements hook_insert().
  470. */
  471. function hosting_server_insert($node) {
  472. // Always generate a new context.
  473. $hosting_name = isset($node->hosting_name) ? $node->hosting_name : 'server_' . preg_replace("/[!\W\.\-]/", "", $node->title);
  474. hosting_context_register($node->nid, $hosting_name);
  475. $id = db_insert('hosting_server')
  476. ->fields(array(
  477. 'vid' => $node->vid,
  478. 'nid' => $node->nid,
  479. 'human_name' => isset($node->human_name) ? $node->human_name : '',
  480. 'verified' => isset($node->verified) ? $node->verified : 0,
  481. 'status' => isset($node->platform_status) ? $node->platform_status : 0,
  482. ))
  483. ->execute();
  484. _hosting_ip_save($node, FALSE);
  485. hosting_server_invoke_services($node, 'save', $node);
  486. hosting_server_invoke_services($node, 'insert', $node);
  487. if (!isset($node->no_verify) || !$node->no_verify) {
  488. hosting_add_task($node->nid, 'verify');
  489. }
  490. }
  491. /**
  492. * Implements hook_delete_revision().
  493. */
  494. function hosting_nodeapi_server_delete_revision(&$node) {
  495. db_delete('hosting_server')
  496. ->condition('vid', $node->vid)
  497. ->execute();
  498. hosting_ip_delete_revision($node);
  499. hosting_server_invoke_services($node, 'delete_revision');
  500. }
  501. /**
  502. * Implements hook_delete().
  503. */
  504. function hosting_server_delete($node) {
  505. db_delete('hosting_server')
  506. ->condition('nid', $node->nid)
  507. ->execute();
  508. _hosting_ip_delete($node);
  509. hosting_context_delete($node->nid);
  510. hosting_task_delete_related_tasks($node->nid);
  511. hosting_server_invoke_services($node, 'delete_revision');
  512. }
  513. /**
  514. * Implements hook_load().
  515. */
  516. function hosting_server_load($nodes) {
  517. foreach ($nodes as $nid => &$node) {
  518. $additions = db_query('SELECT human_name, verified, status AS server_status FROM {hosting_server} WHERE vid = :vid', array(':vid' => $node->vid))->fetch();
  519. // Avoid PHP 5.4 warning when platform doesn't exist yet.
  520. // See: https://drupal.org/node/1940378
  521. $additions = $additions ? $additions : new stdClass();
  522. hosting_server_init_services($node);
  523. hosting_server_invoke_services($node, 'load');
  524. $additions->services = $node->services;
  525. $additions->ip_addresses = _hosting_ip_load($node);
  526. foreach ($additions as $property => &$value) {
  527. $node->$property = $value;
  528. }
  529. }
  530. }
  531. /**
  532. * Menu wildcard loader callback.
  533. *
  534. * Loads a hosting_server node.
  535. * @see hosting_task_menu()
  536. *
  537. * @arg $arg a numeric nid
  538. */
  539. function hosting_server_wildcard_load($arg) {
  540. if (!is_numeric($arg)) {
  541. return FALSE;
  542. }
  543. if ($node = node_load($arg)) {
  544. if ($node->type === 'server') {
  545. return $node;
  546. }
  547. }
  548. return FALSE;
  549. }
  550. /**
  551. * Implements hook_view().
  552. */
  553. function hosting_server_view(&$node, $teaser = FALSE, $page = FALSE) {
  554. hosting_set_breadcrumb($node);
  555. $services = hosting_server_services();
  556. $node->content['info'] = array(
  557. '#prefix' => '<div id="hosting-server-info" class="hosting-info-list">',
  558. '#suffix' => '</div>',
  559. );
  560. $node->content['info']['status'] = array(
  561. '#type' => 'item',
  562. '#title' => t('Status'),
  563. '#markup' => _hosting_server_status($node->server_status),
  564. );
  565. $node->content['info']['verified'] = array(
  566. '#type' => 'item',
  567. '#title' => t('Verified'),
  568. '#markup' => hosting_format_interval($node->verified),
  569. '#weight' => -10,
  570. );
  571. $node->content['info']['hostname'] = array(
  572. '#title' => 'hostname',
  573. '#type' => 'item',
  574. '#markup' => $node->title,
  575. '#weight' => -50,
  576. );
  577. _hosting_ip_view($node);
  578. if (isset($node->content->info->ip_addresses['#weight'])) {
  579. $node->content->info->ip_addresses['#weight'] = -30;
  580. }
  581. if (isset($node->services)) {
  582. foreach ($node->services as $name => $service) {
  583. $node->content['info'][$name] = array(
  584. '#prefix' => '<div class="hosting-service-info hosting-service-' . $name . '-info">',
  585. '#suffix' => '</div>',
  586. );
  587. if (isset($services[$name]['weight'])) {
  588. $node->content['info'][$name]['#weight'] = $services[$name]['weight'];
  589. }
  590. $node->content['info'][$name]['title'] = array(
  591. '#title' => $services[$name]['title'],
  592. '#markup' => $service->type,
  593. '#type' => 'item',
  594. '#weight' => -20,
  595. );
  596. $service->view($node->content['info'][$name]);
  597. }
  598. }
  599. if ($page) {
  600. // Task list.
  601. if (isset($node->nid)) {
  602. $node->content['tasks_view'] = array(
  603. '#type' => 'item',
  604. '#markup' => hosting_task_table($node),
  605. '#prefix' => '<div id="hosting-task-list">',
  606. '#suffix' => '</div>',
  607. '#weight' => 10,
  608. );
  609. $settings['hostingTaskRefresh'] = array(
  610. 'nid' => $node->nid,
  611. 'changed' => $node->changed,
  612. );
  613. drupal_add_js($settings, array('type' => 'setting', 'scope' => JS_DEFAULT));
  614. drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
  615. }
  616. }
  617. if ($node->human_name) {
  618. $node->title = $node->human_name;
  619. }
  620. return $node;
  621. }
  622. /**
  623. * Get servers providing a service.
  624. *
  625. * @param string $service
  626. * Service type string, like 'http' or 'db'
  627. *
  628. * @return array
  629. * An array of enabled servers, keys are the nid's of the nodes representing
  630. * them, values are the titles of the servers.
  631. *
  632. * @see hook_hosting_servers_titles_alter()
  633. */
  634. function hosting_get_servers($service) {
  635. $return = array();
  636. $result = db_query("SELECT n.nid, n.title
  637. FROM {node} n
  638. INNER JOIN {hosting_service} s
  639. ON n.vid = s.vid
  640. WHERE s.available = 1
  641. AND s.service = :service",
  642. array(':service' => $service)
  643. );
  644. foreach ($result as $server) {
  645. $return[$server->nid] = $server->title;
  646. }
  647. drupal_alter('hosting_servers_titles', $return, $service);
  648. return $return;
  649. }
  650. /**
  651. * Views integration.
  652. */
  653. function hosting_server_views_api() {
  654. return array(
  655. 'api' => 3,
  656. 'path' => drupal_get_path('module', 'hosting_server') . '/includes/views',
  657. );
  658. }
  659. /**
  660. * Implements hook_preprocess().
  661. */
  662. function hosting_server_preprocess(&$variables) {
  663. if (isset($variables['view']) && array_key_exists('services', $variables['view']->field)) {
  664. if ($variables['header']) {
  665. $services = explode(',', $variables['header']['services']);
  666. // Split our headers and fields.
  667. _hosting_server_preprocess_explode($variables['header']);
  668. _hosting_server_preprocess_explode($variables['rows'], 1);
  669. // Adjust the rest of the $variables accordingly.
  670. _hosting_server_preprocess_expand($variables['header_classes'], $services);
  671. _hosting_server_preprocess_expand($variables['field_attributes'], $services);
  672. _hosting_server_preprocess_expand($variables['field_classes'], $services);
  673. _hosting_server_preprocess_expand($variables['fields'], $services);
  674. // Add service availability classes.
  675. _hosting_server_preprocess_classes($variables['field_classes'], $variables['rows']);
  676. }
  677. }
  678. }
  679. /**
  680. * Add fields to table headers or rows.
  681. *
  682. * In order to allow the our server list view to dynamically respond to the
  683. * number of enabled services, we define the field as a comma-separated list
  684. * in our field handler. Here we explode that text back into an array, and add
  685. * it back to the table render array.
  686. *
  687. * @see: hosting_server_handler_field_services::label()
  688. * @see: hosting_server_handler_field_services::render()
  689. */
  690. function _hosting_server_preprocess_explode(&$element, $depth = 0) {
  691. if ($depth) {
  692. foreach ($element as $i => $sub_element) {
  693. _hosting_server_preprocess_explode($sub_element, $depth - 1);
  694. $element[$i] = $sub_element;
  695. }
  696. }
  697. else {
  698. $index = array_search('services', array_keys($element));
  699. $services = explode(',', $element['services']);
  700. array_splice($element, $index, 1, $services);
  701. }
  702. }
  703. /**
  704. * Since we're adding headers and fields to our table render array, we need to
  705. * adjust other $variables as their passed to the template.
  706. */
  707. function _hosting_server_preprocess_expand(&$element, $services) {
  708. foreach ($services as $i => $name) {
  709. $element[$i] = $element['services'];
  710. }
  711. unset($element['services']);
  712. }
  713. /**
  714. * Add our service availability classes.
  715. */
  716. function _hosting_server_preprocess_classes(&$classes, $rows) {
  717. foreach ($classes as $field_label => $field_row) {
  718. if (is_numeric($field_label)) {
  719. foreach ($field_row as $index => $field_classes) {
  720. $classes[$field_label][$index] .= $rows[$index][$field_label] == 'no' ? ' hosting-service-unavailable' : ' hosting-service-available';
  721. }
  722. }
  723. }
  724. }
  725. /**
  726. * Implements hook_form_FORM_ID_alter().
  727. */
  728. function hosting_server_form_hosting_settings_alter(&$form, $form_state) {
  729. $form['hosting_default_db_server'] = array(
  730. '#title' => 'Default database server',
  731. '#description' => 'When importing sites, and during some other operations, Aegir will use this database, if none was specified.',
  732. '#type' => 'select',
  733. '#options' => hosting_get_servers('db'),
  734. );
  735. }