hosting.features.inc

Include for functionality related to Hosting module features.

Functions

Namesort descending Description
hosting_add_permissions Add a module's permissions to the appropriate roles.
hosting_determine_features_status Determine the status and actions to take on Hosting features.
hosting_feature Determine whether a specific feature of the hosting system is turned on.
hosting_features_disable Disable one or more Hosting features.
hosting_features_enable Enable one or more Hosting features.
hosting_features_form The Hosting features form.
hosting_features_form_submit Submit callback for the Hosting features form.
hosting_feature_node_types Determine which node types are provided by Hosting features.
hosting_feature_rebuild_caches Perform necessary task after enabling or disabling Hosting features.
hosting_feature_role_perms_table Helper function to list Hosting feature permissions.
hosting_get_features Get a listing of all known Hosting features.
theme_hosting_feature_dependencies Theme function to display Hosting feature dependencies.

Constants

Namesort descending Description
HOSTING_FEATURE_DISABLED This hosting feature is disabled.
HOSTING_FEATURE_ENABLED This hosting feature is enabled.
HOSTING_FEATURE_REQUIRED This hosting feature is required.

File

hosting.features.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Include for functionality related to Hosting module features.
  5. */
  6. /**
  7. * This hosting feature is disabled.
  8. */
  9. define('HOSTING_FEATURE_DISABLED', 0);
  10. /**
  11. * This hosting feature is enabled.
  12. */
  13. define('HOSTING_FEATURE_ENABLED', 1);
  14. /**
  15. * This hosting feature is required.
  16. */
  17. define('HOSTING_FEATURE_REQUIRED', 2);
  18. /**
  19. * Determine whether a specific feature of the hosting system is turned on.
  20. *
  21. * @param string $feature
  22. * The feature to check the status of, e.g. "client" or "platform".
  23. *
  24. * @return bool
  25. * TRUE if the feature is enabled, FALSE if it is disabled.
  26. */
  27. function hosting_feature($feature) {
  28. static $features = array();
  29. if (!count($features)) {
  30. $features = hosting_get_features();
  31. }
  32. if (isset($features[$feature]['status']) && ($features[$feature]['status'] == HOSTING_FEATURE_REQUIRED)) {
  33. $return = module_exists($features[$feature]['module']) ? HOSTING_FEATURE_REQUIRED : HOSTING_FEATURE_DISABLED;
  34. }
  35. elseif (isset($features[$feature]['module'])) {
  36. $return = (module_exists($features[$feature]['module'])) ? HOSTING_FEATURE_ENABLED : HOSTING_FEATURE_DISABLED;
  37. }
  38. else {
  39. $return = variable_get('hosting_feature_' . $feature, !empty($features[$feature]['status']));
  40. }
  41. return $return;
  42. }
  43. /**
  44. * The Hosting features form.
  45. *
  46. * This returns a form with any known Hosting features grouped and listed. It
  47. * allows administrators to enable or disable the features.
  48. *
  49. * @param array $form
  50. * The built form.
  51. * @param array $form_state
  52. * The current form state.
  53. *
  54. * @return array
  55. * A drupal form.
  56. *
  57. * @see hosting_features_form_submit()
  58. */
  59. function hosting_features_form($form, &$form_state) {
  60. $form = array();
  61. $form['settings'] = array(
  62. '#type' => 'checkboxes',
  63. '#title' => 'Settings',
  64. '#options' => array('dependencies' => 'Display dependencies', 'roles' => 'Display roles and permissions'),
  65. '#default_value' => variable_get('hosting_features_form_settings', array()),
  66. '#weight' => -20,
  67. );
  68. $optional = array(
  69. '#type' => 'fieldset',
  70. '#title' => t('Optional system features'),
  71. '#description' => t('You may choose any of the additional system features from the list below.'),
  72. '#collapsible' => FALSE,
  73. );
  74. $required = array(
  75. '#type' => 'fieldset',
  76. '#title' => t('Required system features'),
  77. '#collapsed' => TRUE,
  78. '#collapsible' => TRUE,
  79. '#weight' => -10,
  80. '#description' => t("These features are central to Aegir's functionality, and thus cannot be disabled."),
  81. );
  82. $experimental = array(
  83. '#type' => 'fieldset',
  84. '#title' => t('Experimental'),
  85. '#collapsed' => TRUE,
  86. '#collapsible' => TRUE,
  87. '#description' => t('Features marked experimental have not been completed to a satisfactory level to be considered production ready, so use at your own risk.'),
  88. );
  89. $advanced = array(
  90. '#type' => 'fieldset',
  91. '#title' => t('Advanced'),
  92. '#collapsed' => TRUE,
  93. '#collapsible' => TRUE,
  94. '#description' => t('Advanced features require a deep knowledge of Aegir and Drupal to be considered safe, so use at your own risk.'),
  95. );
  96. $features = hosting_get_features(TRUE);
  97. foreach ($features as $feature => $info) {
  98. $description = $info['description'];
  99. if ($settings = variable_get('hosting_features_form_settings', FALSE)) {
  100. if ($settings['dependencies']) {
  101. // Gather dependencies and their statuses.
  102. $depends_on = isset($info['dependencies']['features']) ? $info['dependencies']['features'] : FALSE;
  103. if (is_array($depends_on)) {
  104. $description .= theme('hosting_feature_dependencies', array(
  105. 'dependencies' => $depends_on,
  106. 'prefix' => 'Depends on',
  107. 'features' => $features));
  108. }
  109. // Gather relying features and their statuses.
  110. $required_by = isset($info['dependencies']['reverse']) ? $info['dependencies']['reverse'] : FALSE;
  111. if (is_array($required_by)) {
  112. $description .= theme('hosting_feature_dependencies', array(
  113. 'dependencies' => $required_by,
  114. 'prefix' => 'Required by',
  115. 'features' => $features));
  116. }
  117. }
  118. }
  119. if ($settings = variable_get('hosting_features_form_settings', FALSE)) {
  120. if ($settings['roles']) {
  121. // Add collapsed fieldset listing the feature's permissions assigned per role.
  122. $role_perms = isset($info['role_permissions']) ? $info['role_permissions'] : FALSE;
  123. if (is_array($role_perms)) {
  124. $element = drupal_get_form('hosting_feature_role_perms_table', $role_perms);
  125. $description .= drupal_render_children($element);
  126. }
  127. }
  128. }
  129. // Disable checkbox for required features.
  130. $locked = FALSE;
  131. if ($info['status'] == HOSTING_FEATURE_REQUIRED) {
  132. $locked = TRUE;
  133. }
  134. elseif (isset($required_by) && $required_by) {
  135. foreach ($required_by as $mod => $feat) {
  136. $locked = $features[$feat]['enabled'] ? TRUE : $locked;
  137. }
  138. }
  139. $element = array(
  140. '#type' => 'checkbox',
  141. '#title' => check_plain($info['title']),
  142. '#description' => $description,
  143. '#default_value' => $info['status'] == HOSTING_FEATURE_REQUIRED ? 1 : hosting_feature($feature),
  144. '#required' => hosting_feature($feature) == HOSTING_FEATURE_REQUIRED,
  145. '#disabled' => $locked,
  146. );
  147. // Add another fieldset based on contrib module package.
  148. $package = FALSE;
  149. if ($info['package'] != 'Hosting') {
  150. $package = array(
  151. '#type' => 'fieldset',
  152. '#title' => $info['package'],
  153. '#collapsed' => FALSE,
  154. '#collapsible' => TRUE,
  155. );
  156. }
  157. if ($package) {
  158. if ($info['group'] == 'required') {
  159. if (!isset($required[$info['package']])) {
  160. $required[$info['package']] = $package;
  161. }
  162. $required[$info['package']]['hosting_feature_' . $feature] = $element;
  163. }
  164. elseif ($info['group'] == 'optional') {
  165. if (!isset($optional[$info['package']])) {
  166. $optional[$info['package']] = $package;
  167. }
  168. $optional[$info['package']]['hosting_feature_' . $feature] = $element;
  169. }
  170. elseif ($info['group'] == 'advanced') {
  171. if (!isset($advanced[$info['package']])) {
  172. $advanced[$info['package']] = $package;
  173. }
  174. $advanced[$info['package']]['hosting_feature_' . $feature] = $element;
  175. }
  176. else {
  177. if (!isset($experimental[$info['package']])) {
  178. $experimental[$info['package']] = $package;
  179. }
  180. $experimental[$info['package']]['hosting_feature_' . $feature] = $element;
  181. }
  182. }
  183. // This feature is in the 'Hosting' module package.
  184. else {
  185. if (isset($info['group']) && $info['group'] == 'required') {
  186. $required['hosting_feature_' . $feature] = $element;
  187. }
  188. elseif (isset($info['group']) && $info['group'] == 'optional') {
  189. $optional['hosting_feature_' . $feature] = $element;
  190. }
  191. elseif (isset($info['group']) && $info['group'] == 'advanced') {
  192. $advanced['hosting_feature_' . $feature] = $element;
  193. }
  194. else {
  195. $experimental['hosting_feature_' . $feature] = $element;
  196. }
  197. }
  198. }
  199. $form['required'] = $required;
  200. $form['optional'] = $optional;
  201. $form['advanced'] = $advanced;
  202. $form['experimental'] = $experimental;
  203. $form['#submit'][] = 'hosting_features_form_submit';
  204. return system_settings_form($form);
  205. }
  206. /**
  207. * Submit callback for the Hosting features form.
  208. *
  209. * We process the submitted values and enable any features that the user has
  210. * requested. This may involve enabling a module and their dependencies and/or
  211. * calling a specified callback function.
  212. *
  213. * @param array $form
  214. * The built form.
  215. * @param array $form_state
  216. * The current form state.
  217. *
  218. * @see hosting_features_form()
  219. * @see hook_hosting_feature()
  220. */
  221. function hosting_features_form_submit($form, &$form_state) {
  222. variable_set('hosting_features_form_settings', $form_state['values']['settings']);
  223. // Get form values, filtering out irrelevant entries.
  224. $values = array_filter($form_state['values'], 'is_int');
  225. // Figure out which features to enable and/or disable.
  226. $features = hosting_determine_features_status($values);
  227. // Enable the feature(s).
  228. $rebuild_on_enable = count($features['disable']) ? FALSE : TRUE;
  229. hosting_features_enable($features['enable'], $rebuild_on_enable);
  230. // Disable the feature(s).
  231. hosting_features_disable($features['disable']);
  232. }
  233. /**
  234. * Get a listing of all known Hosting features.
  235. *
  236. * @param bool $refresh
  237. * (optional) Pass in TRUE to force the list of features to be rebuilt and not
  238. * returned from the cache.
  239. *
  240. * @return array
  241. * An array of Hosting features.
  242. *
  243. * @see hook_hosting_feature()
  244. */
  245. function hosting_get_features($refresh = FALSE) {
  246. $cache = cache_get('hosting_features');
  247. if (empty($cache->data) || $refresh) {
  248. // Include any optional hosting.feature.*.inc files.
  249. $files = drupal_system_listing("/hosting\.feature\.[a-zA-Z0-9_]*\.inc$/", "modules");
  250. if (count($files)) {
  251. foreach ($files as $name => $info) {
  252. include_once DRUPAL_ROOT . '/' . $info->uri;
  253. }
  254. }
  255. // We need the equivalent of module_invoke_all('hosting_feature'), but
  256. // including disabled features/modules too.
  257. $functions = get_defined_functions();
  258. foreach ($functions['user'] as $function) {
  259. if (preg_match('/_hosting_feature$/', $function)) {
  260. $hooks[] = $function;
  261. }
  262. }
  263. $features = array();
  264. foreach ($hooks as $func) {
  265. $features = array_merge($features, $func());
  266. }
  267. // Add module dependencies, package and enabled status.
  268. $module_cache = system_rebuild_module_data();
  269. $modules = array();
  270. foreach ($features as $feature => $info) {
  271. $features[$feature]['enabled'] = $module_cache[$info['module']]->status;
  272. $features[$feature]['package'] = $module_cache[$info['module']]->info['package'];
  273. $features[$feature]['dependencies']['modules'] = $module_cache[$info['module']]->info['dependencies'];
  274. // Generate a list of features keyed by module name.
  275. $modules[$info['module']] = $feature;
  276. }
  277. // Add Hosting feature dependencies, keyed by module.
  278. foreach ($features as $feature => $info) {
  279. foreach ($info['dependencies']['modules'] as $dependency) {
  280. if (array_key_exists($dependency, $modules) && array_key_exists($modules[$dependency], $features)) {
  281. $features[$feature]['dependencies']['features'][$dependency] = $modules[$dependency];
  282. $features[$modules[$dependency]]['dependencies']['reverse'][$info['module']] = $feature;
  283. }
  284. }
  285. }
  286. cache_set('hosting_features', $features);
  287. return $features;
  288. }
  289. else {
  290. return $cache->data;
  291. }
  292. }
  293. /**
  294. * Determine which node types are provided by Hosting features.
  295. *
  296. * @param bool $refresh
  297. * (optional) Pass in TRUE to force the list of node types to be rebuilt and
  298. * not returned from the cache.
  299. *
  300. * @return true
  301. * An array of node types keyed by the Hosting feature that provides them.
  302. */
  303. function hosting_feature_node_types($refresh = FALSE) {
  304. static $types;
  305. if (!is_array($types) || $refresh) {
  306. $features = hosting_get_features($refresh);
  307. foreach ($features as $feature => $info) {
  308. if (!empty($info['node'])) {
  309. $types[$feature] = $info['node'];
  310. }
  311. }
  312. }
  313. return $types;
  314. }
  315. /**
  316. * Theme function to display Hosting feature dependencies.
  317. */
  318. function theme_hosting_feature_dependencies($vars) {
  319. $return = "<div class=\"admin-required\">{$vars['prefix']}:";
  320. $last = end($vars['dependencies']);
  321. reset($vars['dependencies']);
  322. foreach ($vars['dependencies'] as $module => $require) {
  323. $return .= ' ' . $vars['features'][$require]['title'];
  324. $enabled = $vars['features'][$require]['enabled'] ? 'enabled' : 'disabled';
  325. $return .= " (<span class=\"admin-$enabled\">";
  326. $return .= $enabled;
  327. $return .= '</span>)';
  328. $return .= $require == $last ? '.</div>' : ',';
  329. }
  330. return $return;
  331. }
  332. /**
  333. * Helper function to list Hosting feature permissions.
  334. */
  335. function hosting_feature_role_perms_table($form, &$form_table, $role_perms) {
  336. $form = array();
  337. $form['role_perms'] = array(
  338. '#type' => 'fieldset',
  339. '#title' => 'Roles & permissions',
  340. '#description' => 'Upon enabling this feature, the following roles will be assigned the listed permissions.',
  341. '#collapsible' => TRUE,
  342. '#collapsed' => TRUE,
  343. );
  344. $table = '<table><thead><tr><th>Role</th><th>Permissions</th></tr></thead><tbody>';
  345. foreach ($role_perms as $role => $perms) {
  346. $table .= '<tr><td>' . $role . '</td>';
  347. $table .= '<td>' . implode(', ', $perms) . '</td></tr>';
  348. }
  349. $table .= '</tbody></table>';
  350. $form['role_perms']['table'] = array(
  351. '#markup' => $table,
  352. );
  353. return $form;
  354. }
  355. /**
  356. * Enable one or more Hosting features.
  357. *
  358. * @param bool $rebuild
  359. * Rebuild caches afterwards, default TRUE.
  360. * @param bool $enable
  361. * Also enable the Drupal module, default TRUE.
  362. */
  363. function hosting_features_enable($features, $rebuild = TRUE, $enable = TRUE) {
  364. if (count($features)) {
  365. include_once 'includes/install.inc';
  366. $all_features = hosting_get_features();
  367. $titles = array();
  368. foreach ($features as $feature) {
  369. $titles[] = $all_features[$feature]['title'];
  370. }
  371. drupal_set_message(t("Enabling %feature feature!plural.", array(
  372. '%feature' => implode(", ", $titles),
  373. '!plural' => count($features) > 1 ? 's' : '',
  374. )));
  375. foreach ($features as $module => $feature) {
  376. variable_set('hosting_feature_' . $feature, HOSTING_FEATURE_ENABLED);
  377. if ($enable) {
  378. module_enable(array($module));
  379. }
  380. if (isset($all_features[$feature]['enable']) && function_exists($callback = $all_features[$feature]['enable'])) {
  381. $callback();
  382. }
  383. $role_perms = isset($all_features[$feature]['role_permissions']) ? $all_features[$feature]['role_permissions'] : array();
  384. hosting_add_permissions($module, $role_perms);
  385. }
  386. if ($rebuild) {
  387. hosting_feature_rebuild_caches($features);
  388. }
  389. }
  390. }
  391. /**
  392. * Disable one or more Hosting features.
  393. *
  394. * @param bool $rebuild
  395. * Rebuild caches afterwards, default TRUE.
  396. * @param bool $disable_module
  397. * Also disable the Drupal module, default TRUE.
  398. */
  399. function hosting_features_disable($disable, $rebuild = TRUE, $disable_module = TRUE) {
  400. if (count($disable)) {
  401. drupal_set_message(t("Disabling %feature feature!plural.", array(
  402. '%feature' => implode(", ", $disable),
  403. '!plural' => count($disable) > 1 ? 's' : '',
  404. )));
  405. include_once 'includes/install.inc';
  406. if ($disable_module) {
  407. module_disable(array_keys($disable));
  408. }
  409. $features = hosting_get_features();
  410. foreach ($disable as $module => $feature) {
  411. if (isset($features[$feature]['disable']) && function_exists($callback = $features[$feature]['disable'])) {
  412. $callback();
  413. }
  414. variable_set('hosting_feature_' . $feature, HOSTING_FEATURE_DISABLED);
  415. }
  416. if ($rebuild) {
  417. hosting_feature_rebuild_caches();
  418. }
  419. }
  420. }
  421. /**
  422. * Determine the status and actions to take on Hosting features.
  423. *
  424. * Given an array of Hosting features and their desired statuses, determine
  425. * which are enabled, which should be, and which should be disabled.
  426. */
  427. function hosting_determine_features_status($values) {
  428. $features = hosting_get_features();
  429. // List of features currently enabled.
  430. $enabled = array();
  431. // List of features to enable.
  432. $enable = array();
  433. // List of features to disable.
  434. $disable = array();
  435. foreach ($features as $feature => $info) {
  436. $value = $values['hosting_feature_' . $feature];
  437. $current = $info['enabled'];
  438. if ($current) {
  439. $enabled[$features[$feature]['module']] = $features[$feature]['title'];
  440. if (!$value) {
  441. $disable[$features[$feature]['module']] = $features[$feature]['title'];
  442. }
  443. }
  444. elseif ($value) {
  445. $enable[$features[$feature]['module']] = $feature;
  446. // Add dependencies to enable.
  447. if (isset($info['dependencies']['features']) && count($info['dependencies']['features'])) {
  448. foreach ($info['dependencies']['features'] as $module_dep => $feature_dep) {
  449. $enable[$module_dep] = $features[$feature_dep]['title'];
  450. }
  451. }
  452. }
  453. }
  454. // Remove any features we're about to enable from those to disable.
  455. $disable = array_diff_assoc($disable, $enable);
  456. // Remove any features already enabled from those to enable.
  457. $enable = array_diff_assoc($enable, $enabled);
  458. return array(
  459. 'enabled' => $enabled,
  460. 'enable' => $enable,
  461. 'disable' => $disable,
  462. );
  463. }
  464. /**
  465. * Perform necessary task after enabling or disabling Hosting features.
  466. */
  467. function hosting_feature_rebuild_caches($features = array()) {
  468. // Rebuild schema.
  469. drupal_get_schema(NULL, TRUE);
  470. // Rebuild menu.
  471. menu_rebuild();
  472. // Record enabled features in the Hosting features registry (in
  473. // /var/aegir/.drush/drushrc.php)
  474. $all_features = hosting_get_features();
  475. foreach ($features as $feature) {
  476. // Don't add a verify task during install of hostmaster site.
  477. if ($all_features[$feature]['status'] == HOSTING_FEATURE_REQUIRED) {
  478. return;
  479. }
  480. }
  481. // Allow scripts to skip automatic verify tasks by passing '--no-verify'
  482. if (function_exists('drush_get_option') &&
  483. drush_get_option('no-verify', FALSE)) {
  484. return;
  485. }
  486. if ($nid = hosting_get_hostmaster_site_nid()) {
  487. hosting_add_task($nid, 'verify');
  488. }
  489. }
  490. /**
  491. * Add a module's permissions to the appropriate roles.
  492. *
  493. * @param string $module
  494. * The name of the module whose permissions we're adding.
  495. * @param array $role_perms
  496. * An array of arrays keyed by the role name. The values are the permissions
  497. * to enable.
  498. */
  499. function hosting_add_permissions($module, $role_perms) {
  500. $perm_hook = $module . '_permission';
  501. if (function_exists($perm_hook)) {
  502. $roles = user_roles();
  503. foreach ($roles as $rid => $name) {
  504. // The Aegir admin role gets all hosting module permissions.
  505. if ($name == 'aegir administrator') {
  506. $perms = call_user_func($module . '_permission');
  507. user_role_grant_permissions($rid, array_keys($perms));
  508. }
  509. if (array_key_exists($name, $role_perms)) {
  510. user_role_grant_permissions($rid, $role_perms[$name]);
  511. drupal_set_message(t("The '%role' role was assigned the following permission!plural: '%perms'.", array(
  512. '%role' => $name,
  513. '%perms' => implode('\', \'', $role_perms[$name]),
  514. '!plural' => count($role_perms[$name]) > 1 ? 's' : '',
  515. )));
  516. }
  517. }
  518. }
  519. }