js.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. /**
  3. * @file
  4. * Callback page that serves custom JavaScript requests on a Drupal installation.
  5. */
  6. /**
  7. * @var The Drupal root.
  8. */
  9. define('DRUPAL_ROOT', getcwd());
  10. /**
  11. * @name Return constants, copies from menu.inc.
  12. * @{
  13. * The constants are copied to be able to drop the menu.inc depdendency.
  14. */
  15. define('JS_MENU_NOT_FOUND', 2);
  16. define('JS_MENU_ACCESS_DENIED', 3);
  17. /**
  18. * @} End of "Required core files".
  19. */
  20. /**
  21. * @name Required core files
  22. * @{
  23. * The minimal core files required to be able to run a js request
  24. */
  25. require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
  26. require_once DRUPAL_ROOT . '/includes/common.inc';
  27. require_once DRUPAL_ROOT . '/includes/module.inc';
  28. require_once DRUPAL_ROOT . '/includes/unicode.inc';
  29. require_once DRUPAL_ROOT . '/includes/file.inc';
  30. /**
  31. * @} End of "Required core files".
  32. */
  33. // Do basic bootstrap to make sure the database can be accessed.
  34. drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
  35. // Prevent Devel from hi-jacking our output in any case.
  36. $GLOBALS['devel_shutdown'] = FALSE;
  37. $return = js_execute_callback();
  38. // Menu status constants are integers; page content is a string.
  39. if (is_int($return)) {
  40. // Make sure the full bootstrap has ran.
  41. drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
  42. // Deliver error page.
  43. drupal_deliver_page($return);
  44. }
  45. elseif (isset($return)) {
  46. // If JavaScript callback did not exit, print any value (including an empty
  47. // string) except NULL or undefined.
  48. drupal_json_output($return);
  49. }
  50. /**
  51. * Loads the requested module and executes the requested callback.
  52. *
  53. * @return
  54. * The callback function's return value or one of the JS_* constants.
  55. */
  56. function js_execute_callback() {
  57. $args = explode('/', $_GET['q']);
  58. // If i18n is enabled and therefore the js module should boot
  59. // to DRUPAL_BOOTSTRAP_LANGUAGE.
  60. $i18n = FALSE;
  61. // Validate if there is a language prefix in the path.
  62. if (!empty($args[0]) && !empty($args[1]) && $args[1] == 'js') {
  63. // Language string detected, strip off the language code.
  64. $language_code = array_shift($args);
  65. // Enable language detection to make sure i18n is enabled.
  66. $i18n = TRUE;
  67. }
  68. // Strip first argument 'js'.
  69. if (!empty($args[0]) && $args[0] == 'js') {
  70. array_shift($args);
  71. }
  72. // Determine module to load.
  73. $module = check_plain(array_shift($args));
  74. if (!$module || !drupal_load('module', $module)) {
  75. return JS_MENU_ACCESS_DENIED;
  76. }
  77. // Get info hook function name.
  78. $function = $module . '_js';
  79. if (!function_exists($function)) {
  80. return JS_MENU_NOT_FOUND;
  81. }
  82. // Get valid callbacks.
  83. $valid_callbacks = $function();
  84. // Get the callback.
  85. $callback = check_plain(array_shift($args));
  86. // Validate the callback.
  87. if (!isset($valid_callbacks[$callback])) {
  88. return JS_MENU_NOT_FOUND;
  89. }
  90. // If the callback function is located in another file, load that file now.
  91. if (isset($valid_callbacks[$callback]['file']) && ($filepath = drupal_get_path('module', $module) . '/' . $valid_callbacks[$callback]['file']) && file_exists($filepath)) {
  92. require_once $filepath;
  93. }
  94. // Validate the existance of the defined callback.
  95. if (!function_exists($valid_callbacks[$callback]['callback'])) {
  96. return JS_MENU_NOT_FOUND;
  97. }
  98. // Bootstrap to required level.
  99. $full_boostrap = FALSE;
  100. if (!empty($valid_callbacks[$callback]['bootstrap'])) {
  101. drupal_bootstrap($valid_callbacks[$callback]['bootstrap']);
  102. $full_boostrap = ($valid_callbacks[$callback]['bootstrap'] == DRUPAL_BOOTSTRAP_FULL);
  103. }
  104. // Validate if the callback uses i18n.
  105. if (isset($valid_callbacks[$callback]['i18n'])) {
  106. $i18n = $valid_callbacks[$callback]['i18n'];
  107. }
  108. if (!$full_boostrap) {
  109. // The following mimics the behavior of _drupal_bootstrap_full().
  110. // The difference is that not all modules and includes are loaded.
  111. // @see _drupal_bootstrap_full().
  112. // If i18n is enabled, boot to the language phase and make
  113. // sure the required modules are enabled.
  114. if ($i18n) {
  115. // First boot to the variables to make sure drupal_multilingual() works.
  116. drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
  117. // As the variables bootstrap phase loads all core modules, we have to
  118. // add the user module and the path include as a dependencies because they
  119. // are required by some core modules.
  120. if (empty($valid_callbacks[$callback]['dependencies'])) {
  121. $valid_callbacks[$callback]['dependencies'] = array();
  122. }
  123. if (empty($valid_callbacks[$callback]['includes'])) {
  124. $valid_callbacks[$callback]['includes'] = array();
  125. }
  126. if (!in_array('user', $valid_callbacks[$callback]['dependencies'])) {
  127. $valid_callbacks[$callback]['dependencies'][] = 'user';
  128. }
  129. if (!in_array('path', $valid_callbacks[$callback]['includes'])) {
  130. $valid_callbacks[$callback]['includes'][] = 'path';
  131. }
  132. // Then check if it's a multilingual site. If so, boot to the language
  133. // phase.
  134. if (drupal_multilingual()) {
  135. drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
  136. }
  137. }
  138. // Load required include files based on the callback.
  139. if (isset($valid_callbacks[$callback]['includes']) && is_array($valid_callbacks[$callback]['includes'])) {
  140. foreach ($valid_callbacks[$callback]['includes'] as $include) {
  141. if (file_exists("./includes/$include.inc")) {
  142. require_once "./includes/$include.inc";
  143. }
  144. }
  145. }
  146. // Detect string handling method.
  147. unicode_check();
  148. // Undo magic quotes.
  149. fix_gpc_magic();
  150. // Make sure all stream wrappers are registered.
  151. file_get_stream_wrappers();
  152. // Load required modules.
  153. $modules = array($module => 0);
  154. if (isset($valid_callbacks[$callback]['dependencies']) && is_array($valid_callbacks[$callback]['dependencies'])) {
  155. foreach ($valid_callbacks[$callback]['dependencies'] as $dependency) {
  156. if (!drupal_load('module', $dependency)) {
  157. // Do a boot up till SESSION to be sure the drupal_set_message()
  158. // function works.
  159. drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
  160. // Create an error message with information for the user to be able
  161. // to fix the dependency.
  162. $error = t(
  163. 'The dependency :dependency for the callback :callback in :module is not installed.',
  164. array(
  165. ':dependency' => $dependency,
  166. ':callback' => $callback,
  167. ':module' => $module,
  168. )
  169. );
  170. // Let the user know what's wrong and throw an exception to stop the
  171. // callback.
  172. drupal_set_message($error, 'error');
  173. throw new Exception($error);
  174. }
  175. $modules[$dependency] = 0;
  176. }
  177. }
  178. // Reset module list.
  179. module_list(FALSE, TRUE, FALSE, $modules);
  180. // Ensure the language variable is set, if not it might cause problems (e.g.
  181. // entity info).
  182. global $language;
  183. if (!isset($language)) {
  184. $language = language_default();
  185. }
  186. // If access arguments are passed, boot to SESSION and validate if the user
  187. // has access to this callback.
  188. if (!empty($valid_callbacks[$callback]['access arguments']) || !empty($valid_callbacks[$callback]['access callback'])) {
  189. drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
  190. // If no callback is provided, default to user_access.
  191. if (!isset($valid_callbacks[$callback]['access callback'])) {
  192. $valid_callbacks[$callback]['access callback'] = 'user_access';
  193. }
  194. if ($valid_callbacks[$callback]['access callback'] == 'user_access') {
  195. // Ensure the user module is available.
  196. drupal_load('module', 'user');
  197. }
  198. if (!call_user_func_array($valid_callbacks[$callback]['access callback'], !empty($valid_callbacks[$callback]['access arguments']) ? $valid_callbacks[$callback]['access arguments'] : array())) {
  199. return JS_MENU_ACCESS_DENIED;
  200. }
  201. }
  202. // Invoke implementations of hook_init() if the callback doesn't indicate it
  203. // should be skipped.
  204. if (!isset($valid_callbacks[$callback]['skip_hook_init']) || $valid_callbacks[$callback]['skip_hook_init'] == FALSE) {
  205. module_invoke_all('init');
  206. }
  207. }
  208. // If there are page arguments defined add them to the callback call.
  209. if (isset($valid_callbacks[$callback]['page arguments'])) {
  210. // Get the original args again and strip first arguments 'js' and 'module'.
  211. $args = array_slice(explode('/', $_GET['q']), 2);
  212. // Overwrite the arguments
  213. $args = array_intersect_key($args, array_flip($valid_callbacks[$callback]['page arguments']));
  214. }
  215. // Invoke callback function.
  216. return call_user_func_array($valid_callbacks[$callback]['callback'], $args);
  217. }