-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathjs.php
295 lines (252 loc) · 9.88 KB
/
js.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
<?php
/**
* @file
* Callback page that serves custom JavaScript requests on a Drupal installation.
*/
/**
* @var The Drupal root.
*/
define('DRUPAL_ROOT', getcwd());
/**
* @name Return constants, copies from menu.inc.
* @{
* The constants are copied to be able to drop the menu.inc depdendency.
*/
define('JS_MENU_NOT_FOUND', 2);
define('JS_MENU_ACCESS_DENIED', 3);
/**
* @} End of "Required core files".
*/
/**
* @name Required core files
* @{
* The minimal core files required to be able to run a js request
*/
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/module.inc';
require_once DRUPAL_ROOT . '/includes/unicode.inc';
require_once DRUPAL_ROOT . '/includes/file.inc';
/**
* @} End of "Required core files".
*/
// Do basic bootstrap to make sure the database can be accessed.
drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
// Prevent Devel from hi-jacking our output in any case.
$GLOBALS['devel_shutdown'] = FALSE;
$return = js_execute_callback($delivery_callback);
// Menu status constants are integers; page content is a string. If the delivery
// callback is not custom, then do a full bootstrap and let the normal menu
// system handle delivery of the page.
if (is_int($return) && $delivery_callback === 'drupal_json_output') {
// Make sure the full bootstrap has ran.
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Deliver error page.
drupal_deliver_page($return);
}
elseif (isset($return)) {
// If JavaScript callback did not exit, print any value (including an empty
// string) except NULL or undefined.
call_user_func_array($delivery_callback, array($return));
}
/**
* Loads the requested module and executes the requested callback.
*
* @param string $delivery_callback
* The delivery callback function to use. Defaults to drupal_json_output().
*
* @return mixed
* The callback function's return value or one of the JS_* constants.
*/
function js_execute_callback(&$delivery_callback = 'drupal_json_output') {
$args = explode('/', $_GET['q']);
// If i18n is enabled and therefore the js module should boot
// to DRUPAL_BOOTSTRAP_LANGUAGE.
$i18n = FALSE;
// Validate if there is a language prefix in the path.
if (!empty($args[0]) && !empty($args[1]) && $args[1] == 'js') {
// Language string detected, strip off the language code.
$language_code = array_shift($args);
// Enable language detection to make sure i18n is enabled.
$i18n = TRUE;
}
// Strip first argument 'js'.
if (!empty($args[0]) && $args[0] == 'js') {
array_shift($args);
}
// Determine module to load.
$module = check_plain(array_shift($args));
if (!$module || !drupal_load('module', $module)) {
return JS_MENU_ACCESS_DENIED;
}
// Get info hook function name.
$function = $module . '_js';
if (!function_exists($function)) {
return JS_MENU_NOT_FOUND;
}
// Get valid callbacks.
$valid_callbacks = $function();
// Get the callback.
$callback = check_plain(array_shift($args));
// Validate the callback.
if (!isset($valid_callbacks[$callback])) {
return JS_MENU_NOT_FOUND;
}
// If the callback function is located in another file, load that file now.
if (isset($valid_callbacks[$callback]['file']) && ($filepath = drupal_get_path('module', $module) . '/' . $valid_callbacks[$callback]['file']) && file_exists($filepath)) {
require_once $filepath;
}
// Bootstrap to required level.
$full_boostrap = FALSE;
if (!empty($valid_callbacks[$callback]['bootstrap'])) {
drupal_bootstrap($valid_callbacks[$callback]['bootstrap']);
$full_boostrap = ($valid_callbacks[$callback]['bootstrap'] == DRUPAL_BOOTSTRAP_FULL);
}
// Validate if the callback uses i18n.
if (isset($valid_callbacks[$callback]['i18n'])) {
$i18n = $valid_callbacks[$callback]['i18n'];
}
if (!$full_boostrap) {
// The following mimics the behavior of _drupal_bootstrap_full().
// The difference is that not all modules and includes are loaded.
// @see _drupal_bootstrap_full().
// If i18n is enabled, boot to the language phase and make
// sure the required modules are enabled.
if ($i18n) {
// First boot to the variables to make sure drupal_multilingual() works.
drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);
// As the variables bootstrap phase loads all core modules, we have to
// add the user module and the path include as a dependencies because they
// are required by some core modules.
if (empty($valid_callbacks[$callback]['dependencies'])) {
$valid_callbacks[$callback]['dependencies'] = array();
}
if (empty($valid_callbacks[$callback]['includes'])) {
$valid_callbacks[$callback]['includes'] = array();
}
if (!in_array('user', $valid_callbacks[$callback]['dependencies'])) {
$valid_callbacks[$callback]['dependencies'][] = 'user';
}
if (!in_array('path', $valid_callbacks[$callback]['includes'])) {
$valid_callbacks[$callback]['includes'][] = 'path';
}
// Then check if it's a multilingual site. If so, boot to the language
// phase.
if (drupal_multilingual()) {
drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
}
}
// Load required include files based on the callback.
if (isset($valid_callbacks[$callback]['includes']) && is_array($valid_callbacks[$callback]['includes'])) {
foreach ($valid_callbacks[$callback]['includes'] as $include) {
if (file_exists("./includes/$include.inc")) {
require_once "./includes/$include.inc";
}
}
}
// Detect string handling method.
unicode_check();
// Undo magic quotes.
fix_gpc_magic();
// Load required modules.
$modules = array($module => 0);
if (isset($valid_callbacks[$callback]['dependencies']) && is_array($valid_callbacks[$callback]['dependencies'])) {
foreach ($valid_callbacks[$callback]['dependencies'] as $dependency) {
if (!drupal_load('module', $dependency)) {
// Do a boot up till SESSION to be sure the drupal_set_message()
// function works.
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
// Create an error message with information for the user to be able
// to fix the dependency.
$error = t(
'The dependency :dependency for the callback :callback in :module is not installed.',
array(
':dependency' => $dependency,
':callback' => $callback,
':module' => $module,
)
);
// Let the user know what's wrong and throw an exception to stop the
// callback.
drupal_set_message($error, 'error');
throw new Exception($error);
}
$modules[$dependency] = 0;
}
}
// Reset module list.
module_list(FALSE, TRUE, FALSE, $modules);
// Make sure all stream wrappers are registered.
file_get_stream_wrappers();
// Ensure the language variable is set, if not it might cause problems (e.g.
// entity info).
global $language;
if (!isset($language)) {
$language = language_default();
$types = language_types();
foreach ($types as $type) {
$GLOBALS[$type] = $language;
}
}
// If access arguments are passed, boot to SESSION and validate if the user
// has access to this callback.
if (!empty($valid_callbacks[$callback]['access arguments']) || !empty($valid_callbacks[$callback]['access callback'])) {
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
// If no callback is provided, default to user_access.
if (!isset($valid_callbacks[$callback]['access callback'])) {
$valid_callbacks[$callback]['access callback'] = 'user_access';
}
if ($valid_callbacks[$callback]['access callback'] == 'user_access') {
// Ensure the user module is available.
drupal_load('module', 'user');
}
if (!call_user_func_array($valid_callbacks[$callback]['access callback'], js_replace_callback_args(!empty($valid_callbacks[$callback]['access arguments']) ? $valid_callbacks[$callback]['access arguments'] : array()))) {
return JS_MENU_ACCESS_DENIED;
}
}
// Invoke implementations of hook_init() if the callback doesn't indicate it
// should be skipped.
if (!isset($valid_callbacks[$callback]['skip_hook_init']) || $valid_callbacks[$callback]['skip_hook_init'] == FALSE) {
module_invoke_all('init');
}
}
// Validate the existance of the defined callback.
if (!function_exists($valid_callbacks[$callback]['callback'])) {
return JS_MENU_NOT_FOUND;
}
// If there are page arguments defined add them to the callback call.
if (isset($valid_callbacks[$callback]['page arguments'])) {
// Replace the numerical arguments with the current argument values.
$args = js_replace_callback_args($valid_callbacks[$callback]['page arguments']);
}
if (isset($valid_callbacks[$callback]['delivery callback'])) {
$delivery_callback = $valid_callbacks[$callback]['delivery callback'];
}
// Invoke callback function.
return call_user_func_array($valid_callbacks[$callback]['callback'], $args);
}
/**
* Helper function for replacing integer based arguments with path values.
*
* @param array $args
* An array of arguments to walk through and replace values.
*
* @return array
* The arguments array replaced with correct path values.
*/
function js_replace_callback_args(array $args = array()) {
// Retrieve the original arguments again, but strip first and second
// arguments ('js' and 'module').
$original_args = array_slice(explode('/', $_GET['q']), 2);
foreach ($args as $key => $value) {
// Numeric argument exists, replace it.
if (is_int($value) && !empty($original_args[$value])) {
$args[$key] = $original_args[$value];
}
// Numeric argument does not exist, remove it.
elseif (is_int($value)) {
unset($args[$key]);
}
}
return $args;
}