variable_realm.module 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. /**
  3. * @file
  4. * Variable API module - Realms
  5. *
  6. * Each variable realm can be in one of four states.
  7. * - Undefined, The realm controller has not been created yet.
  8. * - Defined, The realm controller has been created.
  9. * - Enabled, A value has been set for the realm key, though it may be disabled (FALSE)
  10. * - Active, The realm key has been set and is not FALSE.
  11. */
  12. // Prefix for realm keys on query string.
  13. define('VARIABLE_REALM_QUERY_STRING', 'variable_realm_key_');
  14. // Prefix for realm switcher element in forms.
  15. define('VARIABLE_REALM_FORM_SWITCHER', 'variable_realm_selector_');
  16. /**
  17. * Implements hook_boot()
  18. *
  19. * We set current variable realms as early as possible in the page request.
  20. */
  21. function variable_realm_boot() {
  22. variable_realm_status('global', 'default');
  23. variable_realm_rebuild();
  24. }
  25. /**
  26. * Implements hook_init()
  27. *
  28. * Let realms be overriden by query string parameters, but only for:
  29. * - Admin paths (not variable admin pages)
  30. */
  31. function variable_realm_init() {
  32. if (arg(0) == 'admin' && arg(3) != 'variable' && ($params = variable_realm_params()) && user_access('administer site configuration')) {
  33. foreach ($params as $realm_name => $realm_key) {
  34. variable_realm_switch($realm_name, $realm_key, FALSE);
  35. }
  36. variable_realm_rebuild();
  37. }
  38. }
  39. /**
  40. * Initialize realm and set key depending on request.
  41. *
  42. * @param $realm_name
  43. * Variable realm name.
  44. * @param $realm_key
  45. * Optional key to be set when we don't have other key.
  46. */
  47. function variable_realm_initialize($realm_name, $realm_key = NULL) {
  48. $realm_controller = variable_realm_controller($realm_name);
  49. if ($realm_controller && !$realm_controller->isEnabled()) {
  50. $new_key = $realm_controller->enable($realm_key);
  51. _variable_realm_invoke_all('variable_realm_enable', $realm_name, $new_key);
  52. _variable_realm_hook('variableRealmEnable', $realm_name, $new_key);
  53. // If we have already built the configuration, rebuild it.
  54. if ($new_key && drupal_static('variable_realm_rebuild')) {
  55. variable_realm_rebuild();
  56. }
  57. }
  58. }
  59. /**
  60. * Get list of all available realm names ordered by default weight.
  61. */
  62. function variable_realm_list() {
  63. return _variable_realm_invoke(variable_realm_list_all(), 'getTitle');
  64. }
  65. /**
  66. * Get all available realm controllers ordered by default weight.
  67. */
  68. function variable_realm_list_all() {
  69. $list = array();
  70. foreach (array_keys(variable_realm_info()) as $name) {
  71. if ($controller = variable_realm_controller($name)) {
  72. $list[$name] = $controller;
  73. }
  74. uasort($list, '_variable_realm_sort_default');
  75. }
  76. return $list;
  77. }
  78. /**
  79. * Get realm parameters from query string.
  80. */
  81. function variable_realm_params($realm_name = NULL) {
  82. $realm_params = &drupal_static(__FUNCTION__);
  83. if (!isset($realm_params)) {
  84. $realm_params = array();
  85. foreach (variable_realm_info() as $realm => $realm_info) {
  86. if (!empty($realm_info['form switcher'])) {
  87. $param = VARIABLE_REALM_QUERY_STRING . $realm;
  88. if (!empty($_GET[$param]) && array_key_exists($_GET[$param], variable_realm_keys($realm))) {
  89. $realm_params[$realm] = $_GET[$param];
  90. }
  91. }
  92. }
  93. }
  94. if ($realm_name) {
  95. return isset($realm_params[$realm_name]) ? $realm_params[$realm_name] : FALSE;
  96. }
  97. else {
  98. return $realm_params;
  99. }
  100. }
  101. /**
  102. * Get information about variable realms.
  103. */
  104. function variable_realm_info($realm_name = NULL) {
  105. $realm_info_tmp = $realm_info = &drupal_static(__FUNCTION__);
  106. if (!isset($realm_info_tmp)) {
  107. $realm_info_tmp = _variable_realm_invoke_all('variable_realm_info');
  108. // If first param is NULL, module_load_all() only returns a boolean
  109. // indicating whether all modules have been loaded.
  110. if (module_load_all(NULL)) {
  111. // Due to the fact that variable_realm_info() gets called by some
  112. // modules and the menu access callbacks early in the bootstrap,
  113. // we could not cache the realm info for later calls until all
  114. // modules have been loaded.
  115. $realm_info = $realm_info_tmp;
  116. }
  117. }
  118. if ($realm_name) {
  119. return isset($realm_info_tmp[$realm_name]) ? $realm_info_tmp[$realm_name] : array();
  120. }
  121. else {
  122. return $realm_info_tmp;
  123. }
  124. }
  125. /**
  126. * Implements hook_variable_realm_info().
  127. */
  128. function variable_realm_variable_realm_info() {
  129. $realm['global'] = array(
  130. 'title' => t('Global'),
  131. 'weight' => 0,
  132. 'controller class' => 'VariableRealmDefaultController',
  133. 'store class' => 'VariableRealmGlobalStore',
  134. 'keys' => array(
  135. 'default' => t('All variables')
  136. ),
  137. );
  138. return $realm;
  139. }
  140. /**
  141. * Get keys for realm.
  142. */
  143. function variable_realm_keys($realm_name) {
  144. if ($controller = variable_realm_controller($realm_name)) {
  145. return $controller->getAllKeys();
  146. }
  147. }
  148. /**
  149. * Get variable realm store.
  150. *
  151. * The first time this function is invoked we initialize the realm system
  152. * and store global variables in the global/default realm.
  153. *
  154. * @param $realm
  155. * Name of the realm to get / create.
  156. * @param $key
  157. * Realm key to get / create
  158. *
  159. * @return VariableRealmControllerInterface
  160. */
  161. function variable_realm($realm, $key) {
  162. $controller = variable_realm_controller($realm);
  163. return $controller ? $controller->getStore($key) : NULL;
  164. }
  165. /**
  166. * Get variable realm controller or create it if not defined.
  167. */
  168. function variable_realm_controller($realm_name = NULL) {
  169. static $drupal_static_fast;
  170. if (!isset($drupal_static_fast)) {
  171. $drupal_static_fast['realm'] = &drupal_static(__FUNCTION__, array());
  172. if ($global = _variable_realm_controller('global')) {
  173. $global->addStore('default', $GLOBALS['conf']);
  174. $drupal_static_fast['realm']['global'] = $global;
  175. }
  176. }
  177. $variable_realm = &$drupal_static_fast['realm'];
  178. if ($realm_name) {
  179. if (!isset($variable_realm[$realm_name])) {
  180. $variable_realm[$realm_name] = _variable_realm_controller($realm_name);
  181. }
  182. return $variable_realm[$realm_name];
  183. }
  184. else {
  185. // Return only existing realms.
  186. return array_filter($variable_realm);
  187. }
  188. }
  189. /**
  190. * Get value from realm
  191. */
  192. function variable_realm_get($realm, $key, $name = NULL, $default = NULL) {
  193. if ($store = variable_realm($realm, $key)) {
  194. return $store->variable_get($name, $default);
  195. }
  196. }
  197. /**
  198. * Set values for variable realm
  199. *
  200. * @param $realm
  201. * Realm name.
  202. * @param $key
  203. * Realm key.
  204. * @param $values
  205. * Array of runtime variable values to add to the realm.
  206. * @param $weight
  207. * Optional explicit weight for this realm.
  208. * @param $rebuild
  209. * Whether to rebuild domains immediately
  210. */
  211. function variable_realm_add($realm, $key, $values = array(), $weight = NULL, $rebuild = TRUE) {
  212. if ($variable_realm = variable_realm($realm, $key)) {
  213. foreach ($values as $name => $value) {
  214. $variable_realm->variable_add($name, $value);
  215. }
  216. if (isset($weight)) {
  217. variable_realm_weight($realm, $weight);
  218. }
  219. // Rebuild only if this is the current realm
  220. if ($rebuild && variable_realm_status($realm) === $key) {
  221. variable_realm_rebuild();
  222. }
  223. }
  224. }
  225. /**
  226. * Set value for realm variable.
  227. */
  228. function variable_realm_set($realm, $key, $name, $value, $rebuild = TRUE) {
  229. if ($store = variable_realm($realm, $key)) {
  230. $old_value = variable_realm_get($realm, $key, $name);
  231. $store->variable_set($name, $value);
  232. if ($rebuild) {
  233. variable_realm_refresh($realm, $key, $name);
  234. }
  235. $options = array(
  236. 'realm' => $store->realm,
  237. 'key' => $store->key,
  238. );
  239. module_invoke_all('variable_update', $name, $value, $old_value, $options);
  240. }
  241. }
  242. /**
  243. * Delete variable from realm
  244. */
  245. function variable_realm_del($realm, $key, $name, $rebuild = TRUE) {
  246. if ($store = variable_realm($realm, $key)) {
  247. $store->variable_del($name);
  248. if ($rebuild) {
  249. variable_realm_refresh($realm, $key, $name);
  250. }
  251. }
  252. }
  253. /**
  254. * Refresh variable value.
  255. */
  256. function variable_realm_refresh($realm_name, $realm_key, $variable_name) {
  257. $value = NULL;
  258. // Only update value if this is the current realm.
  259. if (variable_realm_status($realm_name) === $realm_key) {
  260. foreach (variable_realm_current() as $realm_controller) {
  261. $value = $realm_controller->getCurrentStore()->variable_get($variable_name, $value);
  262. }
  263. }
  264. if (isset($value)) {
  265. $GLOBALS['conf'][$variable_name] = $value;
  266. }
  267. else {
  268. unset($GLOBALS['conf'][$variable_name]);
  269. }
  270. }
  271. /**
  272. * Get active realm controllers ordered by weight.
  273. */
  274. function variable_realm_current() {
  275. $active = array_filter(variable_realm_controller(), '_variable_realm_active');
  276. uasort($active, '_variable_realm_sort_current');
  277. return $active;
  278. }
  279. /**
  280. * Check whether a realm is defined.
  281. */
  282. function variable_realm_defined($realm_name) {
  283. $controllers = variable_realm_controller();
  284. return !empty($controllers[$realm_name]);
  285. }
  286. /**
  287. * Get current realm values ordered by weights, only realms that are set.
  288. *
  289. * @return array
  290. * Ordered array of name => key pairs.
  291. */
  292. function variable_realm_current_keys() {
  293. return array_map('_variable_realm_status', variable_realm_current());
  294. }
  295. /**
  296. * Get current realm values ordered by weights.
  297. *
  298. * @return array
  299. * Ordered array of name => value pairs, only realms that are set.
  300. */
  301. /**
  302. * Get original global variable
  303. */
  304. function variable_realm_global_get($name, $default = NULL) {
  305. return variable_realm_get('global', 'default', $name, $default);
  306. }
  307. /**
  308. * Switch global variable
  309. *
  310. * @param $name
  311. * Optional global variable name. If not set, it will reset all global variables to its original value.
  312. * @param $value
  313. * Optional new value for global variable. If not set, it will reset the variable to its original value.
  314. * @param $rebuild
  315. * Whether to rebuild the current global $conf
  316. */
  317. function variable_realm_global_set($name, $value = NULL, $rebuild = TRUE) {
  318. variable_realm_set('global', 'default', $name, $value, $rebuild);
  319. }
  320. /**
  321. * Set / get current realm values.
  322. *
  323. * @param $realm
  324. * Optional realm name
  325. * @param $key
  326. * Optional realm value to set a status for this realm.
  327. * FALSE to disable this realm.
  328. */
  329. function variable_realm_status($realm, $key = NULL) {
  330. if ($realm_controller = variable_realm_controller($realm)) {
  331. if (isset($key)) {
  332. $realm_controller->setKey($key);
  333. }
  334. return $realm_controller->getKey();
  335. }
  336. }
  337. /**
  338. * Switch current variable realms.
  339. *
  340. * @see variable_realm_weight()
  341. *
  342. * @param $realm
  343. * Realm name. Example 'language'.
  344. * @param $key
  345. * Realm key. Example, for language will be a language code, 'en
  346. * FALSE to unset the realm.
  347. * @param $rebuild
  348. * Whether we need to rebuild the configuration.
  349. */
  350. function variable_realm_switch($realm, $key, $rebuild = TRUE) {
  351. // Check previous status, if not changed no need to rebuild.
  352. $current = variable_realm_status($realm);
  353. if (!isset($current) || $current !== $key) {
  354. variable_realm_status($realm, $key);
  355. _variable_realm_invoke_all('variable_realm_switch', $realm, $key);
  356. _variable_realm_hook('variableRealmSwitch', $realm, $key);
  357. if ($rebuild) {
  358. variable_realm_rebuild();
  359. }
  360. }
  361. }
  362. /**
  363. * Get / set realm weights.
  364. *
  365. * The default realm will have a weight of 0. Realms with higher weights will override
  366. * global variables.
  367. *
  368. * @param $realm
  369. * Realm name
  370. * @param $weight
  371. * Optional numeric value for realm weight.
  372. * @return integer
  373. * Current realm weight
  374. */
  375. function variable_realm_weight($realm, $weight = NULL) {
  376. if ($realm_controller = variable_realm_controller($realm)) {
  377. if (isset($weight)) {
  378. $realm_controller->setWeight($weight);
  379. }
  380. return $realm_controller->getWeight();
  381. }
  382. }
  383. /**
  384. * Rebuild current variable realm.
  385. */
  386. function variable_realm_rebuild() {
  387. $rebuild_keys = &drupal_static(__FUNCTION__);
  388. _variable_realm_invoke_all('variable_realm_rebuild');
  389. $rebuild_keys = variable_realm_current_keys();
  390. $GLOBALS['conf'] = _variable_realm_build();
  391. }
  392. /**
  393. * Reset realms, deleting currently set ones
  394. *
  395. * If no parameters passed, it will reset global variables to original values.
  396. *
  397. * @param $realm_keys
  398. * Array of realm name => realm key to be set.
  399. */
  400. function variable_realm_reset($realm_keys = array()) {
  401. // We need at least some value for the global realm
  402. $status = $realm_keys + array('global', 'default');
  403. // Disable current active realms not in the list
  404. foreach (variable_realm_current() as $realm_name => $realm_controller) {
  405. if (!isset($status[$realm_name])) {
  406. variable_realm_switch($realm_name, FALSE, FALSE);
  407. }
  408. }
  409. foreach ($status as $realm_name => $realm_key) {
  410. variable_realm_switch($realm_name, $realm_key, FALSE);
  411. }
  412. variable_realm_rebuild();
  413. }
  414. /**
  415. * Implements hook_variable_delete().
  416. */
  417. function variable_realm_variable_delete($variable, $options) {
  418. // If there's a realm option, we are already deleting variable for a realm only.
  419. if (empty($options['realm'])) {
  420. // Delete each variable for each current and existing realm/key
  421. foreach (variable_children($variable['name']) as $variable_name) {
  422. foreach (variable_realm_list_all() as $realm_controller) {
  423. $realm_controller->deleteVariable($variable_name);
  424. }
  425. }
  426. variable_realm_rebuild();
  427. }
  428. }
  429. /**
  430. * Implements hook_features_api().
  431. */
  432. function variable_realm_features_api() {
  433. $components = array(
  434. 'variable_realm' => array(
  435. 'name' => t('Realm variables'),
  436. 'default_hook' => 'variable_realm_default_variables',
  437. 'default_file' => FEATURES_DEFAULTS_CUSTOM,
  438. 'default_filename' => 'variable',
  439. 'features_source' => TRUE,
  440. 'file' => drupal_get_path('module', 'variable_realm') .'/variable_realm.features.inc',
  441. ),
  442. );
  443. return $components;
  444. }
  445. /**
  446. * Check whether realm is active.
  447. */
  448. function _variable_realm_active($realm_controller) {
  449. return $realm_controller && $realm_controller->isActive();
  450. }
  451. /**
  452. * Build current realm.
  453. *
  454. * Buids an array of variables for the current realm with higher weights overriding
  455. * lower weights.
  456. */
  457. function _variable_realm_build() {
  458. $variables = array();
  459. foreach (variable_realm_current() as $realm_controller) {
  460. if ($values = $realm_controller->getCurrentVariables()) {
  461. $variables = array_merge($variables, $values);
  462. }
  463. }
  464. return $variables;
  465. }
  466. /**
  467. * Invoke method on a list of objects.
  468. */
  469. function _variable_realm_invoke($list, $method) {
  470. $result = array();
  471. foreach ($list as $index => $object) {
  472. $result[$index] = $object->$method();
  473. }
  474. return $result;
  475. }
  476. /**
  477. * Invokes all realm controllers that implement a method.
  478. *
  479. * @param $method
  480. * Method name
  481. * @param $arg1, $arg2...
  482. * Variable number of arguments to pass to the method.
  483. */
  484. function _variable_realm_hook() {
  485. $args = func_get_args();
  486. $method = array_shift($args);
  487. $result = array();
  488. foreach (variable_realm_controller() as $realm_name => $realm_controller) {
  489. if (method_exists($realm_controller, $method)) {
  490. $result[$realm_name] = call_user_func_array(array($realm_controller, $method), $args);
  491. }
  492. }
  493. return $result;
  494. }
  495. /**
  496. * Create realm controller object.
  497. *
  498. * This may be invoked really early in the bootstrap so it needs to be safe enough
  499. * for module updates and check whether the class really exists. It returns FALSE if not.
  500. */
  501. function _variable_realm_controller($realm_name) {
  502. $info = variable_realm_info($realm_name);
  503. $class = !empty($info['controller class']) ? $info['controller class'] : 'VariableRealmDefaultController';
  504. return class_exists($class) ? new $class($realm_name) : FALSE;
  505. }
  506. /**
  507. * Get current weight for realm controller.
  508. */
  509. function _variable_realm_weight($realm_controller) {
  510. return $realm_controller->getWeight();
  511. }
  512. /**
  513. * Order realms by default weight.
  514. */
  515. function _variable_realm_sort_default($a, $b) {
  516. return $a->getDefaultWeight() - $b->getDefaultWeight();
  517. }
  518. /**
  519. * Order realms by current weight.
  520. */
  521. function _variable_realm_sort_current($a, $b) {
  522. return $a->getWeight() - $b->getWeight();
  523. }
  524. /**
  525. * Get status (current key) for realm controller.
  526. */
  527. function _variable_realm_status($realm_controller) {
  528. return $realm_controller->getKey();
  529. }
  530. /**
  531. * Invoke variable realm hook on all currently loaded modules.
  532. *
  533. * Variable realm usually starts from bootstrap, on hook_boot() and from here it is not
  534. * safe to user regular hook invokation so we use our own function, similar to
  535. * bootstrap_invoke_all() but returning the values (with deep merge).
  536. *
  537. * @see boostrap_invoke_all()
  538. * @see module_invoke()
  539. *
  540. * @pram $hook
  541. * Hook to invoke in all loaded modules
  542. * @param $arg1, $arg2...
  543. * A variable number of arguments.
  544. */
  545. function _variable_realm_invoke_all() {
  546. $args = func_get_args();
  547. $hook = array_shift($args);
  548. $result = array();
  549. foreach (module_list() as $module) {
  550. if (module_hook($module, $hook) && $merge = call_user_func_array($module . '_' . $hook, $args)) {
  551. $result = drupal_array_merge_deep($result, $merge);
  552. unset($merge);
  553. }
  554. }
  555. return $result;
  556. }
  557. /**
  558. * Implements hook_form_FORM_ID_alter()
  559. */
  560. function variable_realm_form_system_theme_settings_alter(&$form, &$form_state, $form_id) {
  561. module_load_include('form.inc', 'variable_realm');
  562. $theme_variable = $form['var']['#value'];
  563. foreach (_variable_realm_variable_settings_form_list() as $realm_name => $variables) {
  564. if (in_array($theme_variable, variable_children($variables))) {
  565. // Mark theme settings and include other variables in the form.
  566. _variable_realm_variable_settings_form_mark($realm_name, $form['theme_settings']);
  567. $realm_variables = element_children($form);
  568. $realm_variables = array_merge($realm_variables, array('default_logo', 'logo_path', 'default_favicon', 'favicon_path'));
  569. _variable_realm_variable_settings_form_alter($form, $realm_name, $realm_variables);
  570. // Replace variable (theme) name so we use a temporary storage variable
  571. $form['#realm_variables'][$realm_name] = $realm_variables;
  572. // This is a single variable so there can be one realm only.
  573. $form['#realm_theme'] = $realm_name;
  574. break;
  575. }
  576. }
  577. if (!empty($form['#realm_theme'])) {
  578. // Replace callback and user our own realm function.
  579. $form['#submit'] = str_replace('system_theme_settings_submit', 'variable_realm_variable_theme_form_submit', $form['#submit']);
  580. // Add realm switcher/s.
  581. _variable_realm_variable_settings_form_switcher($form);
  582. }
  583. }