These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | use Elgg\Filesystem\Directory; |
||
4 | use Elgg\Http\ResponseBuilder; |
||
5 | |||
6 | /** |
||
7 | * Bootstrapping and helper procedural code available for use in Elgg core and plugins. |
||
8 | * |
||
9 | * @package Elgg.Core |
||
10 | * @todo These functions can't be subpackaged because they cover a wide mix of |
||
11 | * purposes and subsystems. Many of them should be moved to more relevant files. |
||
12 | */ |
||
13 | |||
14 | /** |
||
15 | * Get a reference to the global Application object |
||
16 | * |
||
17 | * @return Elgg\Application |
||
18 | * @since 2.0.0 |
||
19 | */ |
||
20 | function elgg() { |
||
21 | 15 | return Elgg\Application::$_instance; |
|
22 | } |
||
23 | |||
24 | /** |
||
25 | * Register a PHP file as a library. |
||
26 | * |
||
27 | * @see elgg_load_library |
||
28 | * |
||
29 | * @param string $name The name of the library |
||
30 | * @param string $location The location of the file |
||
31 | * |
||
32 | * @return void |
||
33 | * @since 1.8.0 |
||
34 | */ |
||
35 | function elgg_register_library($name, $location) { |
||
36 | $libraries = _elgg_config()->libraries; |
||
37 | if ($libraries === null) { |
||
38 | $libraries = []; |
||
39 | } |
||
40 | $libraries[$name] = $location; |
||
41 | _elgg_config()->libraries = $libraries; |
||
42 | } |
||
43 | |||
44 | /** |
||
45 | * Load a PHP library. |
||
46 | * |
||
47 | * @see elgg_register_library |
||
48 | * |
||
49 | * @param string $name The name of the library |
||
50 | * |
||
51 | * @return void |
||
52 | * @throws InvalidParameterException |
||
53 | * @since 1.8.0 |
||
54 | */ |
||
55 | function elgg_load_library($name) { |
||
56 | static $loaded_libraries = []; |
||
57 | |||
58 | if (in_array($name, $loaded_libraries)) { |
||
59 | return; |
||
60 | } |
||
61 | |||
62 | $libraries = _elgg_config()->libraries; |
||
63 | |||
64 | if (!isset($libraries[$name])) { |
||
65 | $error = "$name is not a registered library"; |
||
66 | throw new \InvalidParameterException($error); |
||
67 | } |
||
68 | |||
69 | if (!include_once($libraries[$name])) { |
||
70 | $error = "Could not load the $name library from {$libraries[$name]}"; |
||
71 | throw new \InvalidParameterException($error); |
||
72 | } |
||
73 | |||
74 | $loaded_libraries[] = $name; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Forward to $location. |
||
79 | * |
||
80 | * Sends a 'Location: $location' header and exits. If headers have already been sent, throws an exception. |
||
81 | * |
||
82 | * @param string $location URL to forward to browser to. This can be a path |
||
83 | * relative to the network's URL. |
||
84 | * @param string $reason Short explanation for why we're forwarding. Set to |
||
85 | * '404' to forward to error page. Default message is |
||
86 | * 'system'. |
||
87 | * |
||
88 | * @return void |
||
89 | * @throws SecurityException|InvalidParameterException |
||
90 | */ |
||
91 | function forward($location = "", $reason = 'system') { |
||
92 | if (headers_sent($file, $line)) { |
||
93 | throw new \SecurityException("Redirect could not be issued due to headers already being sent. Halting execution for security. " |
||
94 | . "Output started in file $file at line $line. Search http://learn.elgg.org/ for more information."); |
||
95 | } |
||
96 | |||
97 | _elgg_services()->responseFactory->redirect($location, $reason); |
||
98 | exit; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Set a response HTTP header |
||
103 | * |
||
104 | * @see header |
||
105 | * |
||
106 | * @param string $header Header |
||
107 | * @param bool $replace Replace existing header |
||
108 | * @return void |
||
109 | * @since 2.3 |
||
110 | */ |
||
111 | function elgg_set_http_header($header, $replace = true) { |
||
112 | if (headers_sent($file, $line)) { |
||
113 | _elgg_services()->logger->error("Cannot modify header information - headers already sent by |
||
114 | 17 | (output started at $file:$line)"); |
|
115 | 17 | } else { |
|
116 | 17 | header($header, $replace); |
|
117 | } |
||
118 | |||
119 | if (!preg_match('~^HTTP/\\d\\.\\d~', $header)) { |
||
120 | list($name, $value) = explode(':', $header, 2); |
||
121 | 17 | _elgg_services()->responseFactory->setHeader($name, ltrim($value), $replace); |
|
122 | 17 | } |
|
123 | 17 | } |
|
124 | |||
125 | 17 | /** |
|
126 | * Register a JavaScript file for inclusion |
||
127 | * |
||
128 | * This function handles adding JavaScript to a web page. If multiple |
||
129 | * calls are made to register the same JavaScript file based on the $id |
||
130 | * variable, only the last file is included. This allows a plugin to add |
||
131 | * JavaScript from a view that may be called more than once. It also handles |
||
132 | * more than one plugin adding the same JavaScript. |
||
133 | * |
||
134 | * jQuery plugins often have filenames such as jquery.rating.js. A best practice |
||
135 | * is to base $name on the filename: "jquery.rating". It is recommended to not |
||
136 | * use version numbers in the name. |
||
137 | * |
||
138 | * The JavaScript files can be local to the server or remote (such as |
||
139 | * Google's CDN). |
||
140 | * |
||
141 | * @note Since 2.0, scripts with location "head" will also be output in the footer, but before |
||
142 | * those with location "footer". |
||
143 | * |
||
144 | * @param string $name An identifier for the JavaScript library |
||
145 | * @param string $url URL of the JavaScript file |
||
146 | * @param string $location Page location: head or footer. (default: head) |
||
147 | * @param int $priority Priority of the JS file (lower numbers load earlier) |
||
148 | * |
||
149 | * @return bool |
||
150 | * @since 1.8.0 |
||
151 | */ |
||
152 | function elgg_register_js($name, $url, $location = 'head', $priority = null) { |
||
153 | return elgg_register_external_file('js', $name, $url, $location, $priority); |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Defines a JS lib as an AMD module. This is useful for shimming |
||
158 | * traditional JS or for setting the paths of AMD modules. |
||
159 | * |
||
160 | * Calling multiple times for the same name will: |
||
161 | * * set the preferred path to the last call setting a path |
||
162 | * * overwrite the shimmed AMD modules with the last call setting a shimmed module |
||
163 | * |
||
164 | * Use elgg_require_js($name) to load on the current page. |
||
165 | * |
||
166 | * Calling this function is not needed if your JS are in views named like `module/name.js` |
||
167 | * Instead, simply call elgg_require_js("module/name"). |
||
168 | * |
||
169 | * @note The configuration is cached in simplecache, so logic should not depend on user- |
||
170 | * specific values like get_language(). |
||
171 | * |
||
172 | * @param string $name The module name |
||
173 | * @param array $config An array like the following: |
||
174 | * array 'deps' An array of AMD module dependencies |
||
175 | * string 'exports' The name of the exported module |
||
176 | * string 'src' The URL to the JS. Can be relative. |
||
177 | * |
||
178 | * @return void |
||
179 | */ |
||
180 | function elgg_define_js($name, $config) { |
||
181 | $src = elgg_extract('src', $config); |
||
182 | |||
183 | if ($src) { |
||
184 | $url = elgg_normalize_url($src); |
||
185 | _elgg_services()->amdConfig->addPath($name, $url); |
||
186 | } |
||
187 | |||
188 | // shimmed module |
||
189 | if (isset($config['deps']) || isset($config['exports'])) { |
||
190 | _elgg_services()->amdConfig->addShim($name, $config); |
||
191 | } |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Unregister a JavaScript file |
||
196 | * |
||
197 | * @param string $name The identifier for the JavaScript library |
||
198 | * |
||
199 | * @return bool |
||
200 | * @since 1.8.0 |
||
201 | */ |
||
202 | function elgg_unregister_js($name) { |
||
203 | return elgg_unregister_external_file('js', $name); |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Load a JavaScript resource on this page |
||
208 | * |
||
209 | * This must be called before elgg_view_page(). It can be called before the |
||
210 | * script is registered. If you do not want a script loaded, unregister it. |
||
211 | * |
||
212 | * @param string $name Identifier of the JavaScript resource |
||
213 | * |
||
214 | * @return void |
||
215 | * @since 1.8.0 |
||
216 | */ |
||
217 | function elgg_load_js($name) { |
||
218 | elgg_load_external_file('js', $name); |
||
219 | } |
||
220 | |||
221 | |||
222 | /** |
||
223 | * Request that Elgg load an AMD module onto the page. |
||
224 | * |
||
225 | * @param string $name The AMD module name. |
||
226 | * @return void |
||
227 | * @since 1.9.0 |
||
228 | */ |
||
229 | function elgg_require_js($name) { |
||
230 | _elgg_services()->amdConfig->addDependency($name); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Cancel a request to load an AMD module onto the page. |
||
235 | * |
||
236 | * @note The elgg, jquery, and jquery-ui modules cannot be cancelled. |
||
237 | * |
||
238 | * @param string $name The AMD module name. |
||
239 | * @return void |
||
240 | * @since 2.1.0 |
||
241 | */ |
||
242 | function elgg_unrequire_js($name) { |
||
243 | _elgg_services()->amdConfig->removeDependency($name); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Get the JavaScript URLs that are loaded |
||
248 | * |
||
249 | * @param string $location 'head' or 'footer' |
||
250 | * |
||
251 | * @return array |
||
252 | * @since 1.8.0 |
||
253 | */ |
||
254 | function elgg_get_loaded_js($location = 'head') { |
||
255 | return elgg_get_loaded_external_files('js', $location); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Register a CSS file for inclusion in the HTML head |
||
260 | * |
||
261 | * @param string $name An identifier for the CSS file |
||
262 | * @param string $url URL of the CSS file |
||
263 | * @param int $priority Priority of the CSS file (lower numbers load earlier) |
||
264 | * |
||
265 | * @return bool |
||
266 | * @since 1.8.0 |
||
267 | */ |
||
268 | function elgg_register_css($name, $url, $priority = null) { |
||
269 | return elgg_register_external_file('css', $name, $url, 'head', $priority); |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Unregister a CSS file |
||
274 | * |
||
275 | * @param string $name The identifier for the CSS file |
||
276 | * |
||
277 | * @return bool |
||
278 | * @since 1.8.0 |
||
279 | */ |
||
280 | function elgg_unregister_css($name) { |
||
281 | return elgg_unregister_external_file('css', $name); |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Load a CSS file for this page |
||
286 | * |
||
287 | * This must be called before elgg_view_page(). It can be called before the |
||
288 | * CSS file is registered. If you do not want a CSS file loaded, unregister it. |
||
289 | * |
||
290 | * @param string $name Identifier of the CSS file |
||
291 | * |
||
292 | * @return void |
||
293 | * @since 1.8.0 |
||
294 | */ |
||
295 | function elgg_load_css($name) { |
||
296 | elgg_load_external_file('css', $name); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Get the loaded CSS URLs |
||
301 | * |
||
302 | * @return array |
||
303 | * @since 1.8.0 |
||
304 | */ |
||
305 | function elgg_get_loaded_css() { |
||
306 | return elgg_get_loaded_external_files('css', 'head'); |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Core registration function for external files |
||
311 | * |
||
312 | * @param string $type Type of external resource (js or css) |
||
313 | * @param string $name Identifier used as key |
||
314 | * @param string $url URL |
||
315 | * @param string $location Location in the page to include the file |
||
316 | * @param int $priority Loading priority of the file |
||
317 | * |
||
318 | * @return bool |
||
319 | * @since 1.8.0 |
||
320 | */ |
||
321 | function elgg_register_external_file($type, $name, $url, $location, $priority = 500) { |
||
322 | return _elgg_services()->externalFiles->register($type, $name, $url, $location, $priority); |
||
323 | } |
||
324 | |||
325 | /** |
||
326 | * Unregister an external file |
||
327 | * |
||
328 | * @param string $type Type of file: js or css |
||
329 | * @param string $name The identifier of the file |
||
330 | * |
||
331 | * @return bool |
||
332 | * @since 1.8.0 |
||
333 | */ |
||
334 | function elgg_unregister_external_file($type, $name) { |
||
335 | return _elgg_services()->externalFiles->unregister($type, $name); |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * Load an external resource for use on this page |
||
340 | * |
||
341 | * @param string $type Type of file: js or css |
||
342 | * @param string $name The identifier for the file |
||
343 | * |
||
344 | * @return void |
||
345 | * @since 1.8.0 |
||
346 | */ |
||
347 | function elgg_load_external_file($type, $name) { |
||
348 | _elgg_services()->externalFiles->load($type, $name); |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * Get external resource descriptors |
||
353 | * |
||
354 | * @param string $type Type of file: js or css |
||
355 | * @param string $location Page location |
||
356 | * |
||
357 | * @return array |
||
358 | * @since 1.8.0 |
||
359 | */ |
||
360 | function elgg_get_loaded_external_files($type, $location) { |
||
361 | return _elgg_services()->externalFiles->getLoadedFiles($type, $location); |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Returns a list of files in $directory. |
||
366 | * |
||
367 | * Only returns files. Does not recurse into subdirs. |
||
368 | * |
||
369 | * @param string $directory Directory to look in |
||
370 | * @param array $exceptions Array of filenames to ignore |
||
371 | * @param array $list Array of files to append to |
||
372 | * @param mixed $extensions Array of extensions to allow, null for all. Use a dot: array('.php'). |
||
373 | * |
||
374 | * @return array Filenames in $directory, in the form $directory/filename. |
||
375 | */ |
||
376 | View Code Duplication | function elgg_get_file_list($directory, $exceptions = [], $list = [], $extensions = null) { |
|
377 | |||
378 | $directory = sanitise_filepath($directory); |
||
379 | if ($handle = opendir($directory)) { |
||
380 | while (($file = readdir($handle)) !== false) { |
||
381 | if (!is_file($directory . $file) || in_array($file, $exceptions)) { |
||
382 | continue; |
||
383 | } |
||
384 | |||
385 | if (is_array($extensions)) { |
||
386 | if (in_array(strrchr($file, '.'), $extensions)) { |
||
387 | $list[] = $directory . $file; |
||
388 | } |
||
389 | } else { |
||
390 | $list[] = $directory . $file; |
||
391 | } |
||
392 | } |
||
393 | closedir($handle); |
||
394 | } |
||
395 | |||
396 | return $list; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * Sanitise file paths ensuring that they begin and end with slashes etc. |
||
401 | * |
||
402 | * @param string $path The path |
||
403 | * @param bool $append_slash Add tailing slash |
||
404 | * |
||
405 | * @return string |
||
406 | */ |
||
407 | View Code Duplication | function sanitise_filepath($path, $append_slash = true) { |
|
408 | // Convert to correct UNIX paths |
||
409 | $path = str_replace('\\', '/', $path); |
||
410 | $path = str_replace('../', '/', $path); |
||
411 | // replace // with / except when preceeded by : |
||
412 | $path = preg_replace("/([^:])\/\//", "$1/", $path); |
||
413 | |||
414 | // Sort trailing slash |
||
415 | $path = trim($path); |
||
416 | // rtrim defaults plus / |
||
417 | $path = rtrim($path, " \n\t\0\x0B/"); |
||
418 | |||
419 | if ($append_slash) { |
||
420 | $path = $path . '/'; |
||
421 | } |
||
422 | |||
423 | return $path; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Counts the number of messages, either globally or in a particular register |
||
428 | * |
||
429 | * @param string $register Optionally, the register |
||
430 | * |
||
431 | * @return integer The number of messages |
||
432 | */ |
||
433 | function count_messages($register = "") { |
||
434 | return _elgg_services()->systemMessages->count($register); |
||
435 | } |
||
436 | |||
437 | /** |
||
438 | * Display a system message on next page load. |
||
439 | * |
||
440 | * @param string|array $message Message or messages to add |
||
441 | * |
||
442 | * @return bool |
||
443 | */ |
||
444 | function system_message($message) { |
||
445 | _elgg_services()->systemMessages->addSuccessMessage($message); |
||
446 | return true; |
||
447 | 9 | } |
|
448 | 9 | ||
449 | /** |
||
450 | * Display an error on next page load. |
||
451 | * |
||
452 | * @param string|array $error Error or errors to add |
||
453 | * |
||
454 | * @return bool |
||
455 | */ |
||
456 | function register_error($error) { |
||
457 | _elgg_services()->systemMessages->addErrorMessage($error); |
||
458 | return true; |
||
459 | 17 | } |
|
460 | 17 | ||
461 | /** |
||
462 | * Get a copy of the current system messages. |
||
463 | * |
||
464 | * @return \Elgg\SystemMessages\RegisterSet |
||
465 | * @since 2.1 |
||
466 | */ |
||
467 | function elgg_get_system_messages() { |
||
468 | return _elgg_services()->systemMessages->loadRegisters(); |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Set the system messages. This will overwrite the state of all messages and errors! |
||
473 | * |
||
474 | * @param \Elgg\SystemMessages\RegisterSet $set Set of messages |
||
475 | * @return void |
||
476 | * @since 2.1 |
||
477 | */ |
||
478 | function elgg_set_system_messages(\Elgg\SystemMessages\RegisterSet $set) { |
||
479 | _elgg_services()->systemMessages->saveRegisters($set); |
||
480 | } |
||
481 | |||
482 | /** |
||
483 | * Register a callback as an Elgg event handler. |
||
484 | * |
||
485 | * Events are emitted by Elgg when certain actions occur. Plugins |
||
486 | * can respond to these events or halt them completely by registering a handler |
||
487 | * as a callback to an event. Multiple handlers can be registered for |
||
488 | * the same event and will be executed in order of $priority. |
||
489 | * |
||
490 | * For most events, any handler returning false will halt the execution chain and |
||
491 | * cause the event to be "cancelled". For After Events, the return values of the |
||
492 | * handlers will be ignored and all handlers will be called. |
||
493 | * |
||
494 | * This function is called with the event name, event type, and handler callback name. |
||
495 | * Setting the optional $priority allows plugin authors to specify when the |
||
496 | * callback should be run. Priorities for plugins should be 1-1000. |
||
497 | * |
||
498 | * The callback is passed 3 arguments when called: $event, $type, and optional $params. |
||
499 | * |
||
500 | * $event is the name of event being emitted. |
||
501 | * $type is the type of event or object concerned. |
||
502 | * $params is an optional parameter passed that can include a related object. See |
||
503 | * specific event documentation for details on which events pass what parameteres. |
||
504 | * |
||
505 | * @tip If a priority isn't specified it is determined by the order the handler was |
||
506 | * registered relative to the event and type. For plugins, this generally means |
||
507 | * the earlier the plugin is in the load order, the earlier the priorities are for |
||
508 | * any event handlers. |
||
509 | * |
||
510 | * @tip $event and $object_type can use the special keyword 'all'. Handler callbacks registered |
||
511 | * with $event = all will be called for all events of type $object_type. Similarly, |
||
512 | * callbacks registered with $object_type = all will be called for all events of type |
||
513 | * $event, regardless of $object_type. If $event and $object_type both are 'all', the |
||
514 | * handler callback will be called for all events. |
||
515 | * |
||
516 | * @tip Event handler callbacks are considered in the follow order: |
||
517 | * - Specific registration where 'all' isn't used. |
||
518 | * - Registration where 'all' is used for $event only. |
||
519 | * - Registration where 'all' is used for $type only. |
||
520 | * - Registration where 'all' is used for both. |
||
521 | * |
||
522 | * @warning If you use the 'all' keyword, you must have logic in the handler callback to |
||
523 | * test the passed parameters before taking an action. |
||
524 | * |
||
525 | * @tip When referring to events, the preferred syntax is "event, type". |
||
526 | * |
||
527 | * Internal note: Events are stored in $CONFIG->events as: |
||
528 | * <code> |
||
529 | * $CONFIG->events[$event][$type][$priority] = $callback; |
||
530 | * </code> |
||
531 | * |
||
532 | * @param string $event The event type |
||
533 | * @param string $object_type The object type |
||
534 | * @param string $callback The handler callback |
||
535 | * @param int $priority The priority - 0 is default, negative before, positive after |
||
536 | * |
||
537 | * @return bool |
||
538 | * @example documentation/events/basic.php |
||
539 | * @example documentation/events/advanced.php |
||
540 | * @example documentation/events/all.php |
||
541 | */ |
||
542 | function elgg_register_event_handler($event, $object_type, $callback, $priority = 500) { |
||
543 | return _elgg_services()->events->registerHandler($event, $object_type, $callback, $priority); |
||
544 | } |
||
545 | 93 | ||
546 | /** |
||
547 | * Unregisters a callback for an event. |
||
548 | * |
||
549 | * @param string $event The event type |
||
550 | * @param string $object_type The object type |
||
551 | * @param string $callback The callback. Since 1.11, static method callbacks will match dynamic methods |
||
552 | * |
||
553 | * @return bool true if a handler was found and removed |
||
554 | * @since 1.7 |
||
555 | */ |
||
556 | function elgg_unregister_event_handler($event, $object_type, $callback) { |
||
557 | return _elgg_services()->events->unregisterHandler($event, $object_type, $callback); |
||
558 | } |
||
559 | |||
560 | /** |
||
561 | * Clears all callback registrations for a event. |
||
562 | * |
||
563 | * @param string $event The name of the event |
||
564 | * @param string $object_type The objecttype of the event |
||
565 | * |
||
566 | * @return void |
||
567 | * @since 2.3 |
||
568 | */ |
||
569 | function elgg_clear_event_handlers($event, $object_type) { |
||
570 | _elgg_services()->events->clearHandlers($event, $object_type); |
||
571 | } |
||
572 | |||
573 | /** |
||
574 | * Trigger an Elgg Event and attempt to run all handler callbacks registered to that |
||
575 | * event, type. |
||
576 | * |
||
577 | * This function attempts to run all handlers registered to $event, $object_type or |
||
578 | * the special keyword 'all' for either or both. If a handler returns false, the |
||
579 | * event will be cancelled (no further handlers will be called, and this function |
||
580 | * will return false). |
||
581 | * |
||
582 | * $event is usually a verb: create, update, delete, annotation. |
||
583 | * |
||
584 | * $object_type is usually a noun: object, group, user, annotation, relationship, metadata. |
||
585 | * |
||
586 | * $object is usually an Elgg* object associated with the event. |
||
587 | * |
||
588 | * @warning Elgg events should only be triggered by core. Plugin authors should use |
||
589 | * {@link trigger_elgg_plugin_hook()} instead. |
||
590 | * |
||
591 | * @tip When referring to events, the preferred syntax is "event, type". |
||
592 | * |
||
593 | * @note Internal: Only rarely should events be changed, added, or removed in core. |
||
594 | * When making changes to events, be sure to first create a ticket on Github. |
||
595 | * |
||
596 | * @note Internal: @tip Think of $object_type as the primary namespace element, and |
||
597 | * $event as the secondary namespace. |
||
598 | * |
||
599 | * @param string $event The event type |
||
600 | * @param string $object_type The object type |
||
601 | * @param string $object The object involved in the event |
||
602 | * |
||
603 | * @return bool False if any handler returned false, otherwise true. |
||
604 | * @example documentation/examples/events/trigger.php |
||
605 | */ |
||
606 | function elgg_trigger_event($event, $object_type, $object = null) { |
||
607 | return _elgg_services()->events->trigger($event, $object_type, $object); |
||
608 | } |
||
609 | 2 | ||
610 | /** |
||
611 | * Trigger a "Before event" indicating a process is about to begin. |
||
612 | * |
||
613 | * Like regular events, a handler returning false will cancel the process and false |
||
614 | * will be returned. |
||
615 | * |
||
616 | * To register for a before event, append ":before" to the event name when registering. |
||
617 | * |
||
618 | * @param string $event The event type. The fired event type will be appended with ":before". |
||
619 | * @param string $object_type The object type |
||
620 | * @param string $object The object involved in the event |
||
621 | * |
||
622 | * @return bool False if any handler returned false, otherwise true |
||
623 | * |
||
624 | * @see elgg_trigger_event |
||
625 | * @see elgg_trigger_after_event |
||
626 | */ |
||
627 | function elgg_trigger_before_event($event, $object_type, $object = null) { |
||
628 | return _elgg_services()->events->triggerBefore($event, $object_type, $object); |
||
629 | } |
||
630 | 85 | ||
631 | /** |
||
632 | * Trigger an "After event" indicating a process has finished. |
||
633 | * |
||
634 | * Unlike regular events, all the handlers will be called, their return values ignored. |
||
635 | * |
||
636 | * To register for an after event, append ":after" to the event name when registering. |
||
637 | * |
||
638 | * @param string $event The event type. The fired event type will be appended with ":after". |
||
639 | * @param string $object_type The object type |
||
640 | * @param string $object The object involved in the event |
||
641 | * |
||
642 | * @return true |
||
643 | * |
||
644 | * @see elgg_trigger_before_event |
||
645 | */ |
||
646 | function elgg_trigger_after_event($event, $object_type, $object = null) { |
||
647 | return _elgg_services()->events->triggerAfter($event, $object_type, $object); |
||
648 | } |
||
649 | 86 | ||
650 | /** |
||
651 | * Trigger an event normally, but send a notice about deprecated use if any handlers are registered. |
||
652 | * |
||
653 | * @param string $event The event type |
||
654 | * @param string $object_type The object type |
||
655 | * @param string $object The object involved in the event |
||
656 | * @param string $message The deprecation message |
||
657 | * @param string $version Human-readable *release* version: 1.9, 1.10, ... |
||
658 | * |
||
659 | * @return bool |
||
660 | * |
||
661 | * @see elgg_trigger_event |
||
662 | */ |
||
663 | function elgg_trigger_deprecated_event($event, $object_type, $object = null, $message = null, $version = null) { |
||
664 | return _elgg_services()->events->triggerDeprecated($event, $object_type, $object, $message, $version); |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * Register a callback as a plugin hook handler. |
||
669 | * |
||
670 | * Plugin hooks allow developers to losely couple plugins and features by |
||
671 | * responding to and emitting {@link elgg_trigger_plugin_hook()} customizable hooks. |
||
672 | * Handler callbacks can respond to the hook, change the details of the hook, or |
||
673 | * ignore it. |
||
674 | * |
||
675 | * Multiple handlers can be registered for a plugin hook, and each callback |
||
676 | * is called in order of priority. If the return value of a handler is not |
||
677 | * null, that value is passed to the next callback in the call stack. When all |
||
678 | * callbacks have been run, the final value is passed back to the caller |
||
679 | * via {@link elgg_trigger_plugin_hook()}. |
||
680 | * |
||
681 | * Similar to Elgg Events, plugin hook handler callbacks are registered by passing |
||
682 | * a hook, a type, and a priority. |
||
683 | * |
||
684 | * The callback is passed 4 arguments when called: $hook, $type, $value, and $params. |
||
685 | * |
||
686 | * - str $hook The name of the hook. |
||
687 | * - str $type The type of hook. |
||
688 | * - mixed $value The return value of the last handler or the default |
||
689 | * value if no other handlers have been called. |
||
690 | * - mixed $params An optional array of parameters. Used to provide additional |
||
691 | * information to plugins. |
||
692 | * |
||
693 | * @note Internal: Plugin hooks are stored in $CONFIG->hooks as: |
||
694 | * <code> |
||
695 | * $CONFIG->hooks[$hook][$type][$priority] = $callback; |
||
696 | * </code> |
||
697 | * |
||
698 | * @tip Plugin hooks are similar to Elgg Events in that Elgg emits |
||
699 | * a plugin hook when certain actions occur, but a plugin hook allows you to alter the |
||
700 | * parameters, as well as halt execution. |
||
701 | * |
||
702 | * @tip If a priority isn't specified it is determined by the order the handler was |
||
703 | * registered relative to the event and type. For plugins, this generally means |
||
704 | * the earlier the plugin is in the load order, the earlier the priorities are for |
||
705 | * any event handlers. |
||
706 | * |
||
707 | * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'. |
||
708 | * Handler callbacks registered with $hook = all will be called for all hooks |
||
709 | * of type $type. Similarly, handlers registered with $type = all will be |
||
710 | * called for all hooks of type $event, regardless of $object_type. If $hook |
||
711 | * and $type both are 'all', the handler will be called for all hooks. |
||
712 | * |
||
713 | * @tip Plugin hooks are sometimes used to gather lists from plugins. This is |
||
714 | * usually done by pushing elements into an array passed in $params. Be sure |
||
715 | * to append to and then return $value so you don't overwrite other plugin's |
||
716 | * values. |
||
717 | * |
||
718 | * @warning Unlike Elgg Events, a handler that returns false will NOT halt the |
||
719 | * execution chain. |
||
720 | * |
||
721 | * @param string $hook The name of the hook |
||
722 | * @param string $type The type of the hook |
||
723 | * @param callable $callback The name of a valid function or an array with object and method |
||
724 | * @param int $priority The priority - 500 is default, lower numbers called first |
||
725 | * |
||
726 | * @return bool |
||
727 | * |
||
728 | * @example hooks/register/basic.php Registering for a plugin hook and examining the variables. |
||
729 | * @example hooks/register/advanced.php Registering for a plugin hook and changing the params. |
||
730 | * @since 1.8.0 |
||
731 | */ |
||
732 | function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) { |
||
733 | return _elgg_services()->hooks->registerHandler($hook, $type, $callback, $priority); |
||
734 | } |
||
735 | 94 | ||
736 | /** |
||
737 | * Unregister a callback as a plugin hook. |
||
738 | * |
||
739 | * @param string $hook The name of the hook |
||
740 | * @param string $entity_type The name of the type of entity (eg "user", "object" etc) |
||
741 | * @param callable $callback The PHP callback to be removed. Since 1.11, static method |
||
742 | * callbacks will match dynamic methods |
||
743 | * |
||
744 | * @return void |
||
745 | * @since 1.8.0 |
||
746 | */ |
||
747 | function elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback) { |
||
748 | _elgg_services()->hooks->unregisterHandler($hook, $entity_type, $callback); |
||
749 | } |
||
750 | 1 | ||
751 | 1 | /** |
|
752 | * Clears all callback registrations for a plugin hook. |
||
753 | * |
||
754 | * @param string $hook The name of the hook |
||
755 | * @param string $type The type of the hook |
||
756 | * |
||
757 | * @return void |
||
758 | * @since 2.0 |
||
759 | */ |
||
760 | function elgg_clear_plugin_hook_handlers($hook, $type) { |
||
761 | _elgg_services()->hooks->clearHandlers($hook, $type); |
||
762 | } |
||
763 | |||
764 | /** |
||
765 | * Trigger a Plugin Hook and run all handler callbacks registered to that hook:type. |
||
766 | * |
||
767 | * This function runs all handlers registered to $hook, $type or |
||
768 | * the special keyword 'all' for either or both. |
||
769 | * |
||
770 | * Use $params to send additional information to the handler callbacks. |
||
771 | * |
||
772 | * $returnvalue is the initial value to pass to the handlers, which can |
||
773 | * change it by returning non-null values. It is useful to use $returnvalue |
||
774 | * to set defaults. If no handlers are registered, $returnvalue is immediately |
||
775 | * returned. |
||
776 | * |
||
777 | * Handlers that return null (or with no explicit return or return value) will |
||
778 | * not change the value of $returnvalue. |
||
779 | * |
||
780 | * $hook is usually a verb: import, get_views, output. |
||
781 | * |
||
782 | * $type is usually a noun: user, ecml, page. |
||
783 | * |
||
784 | * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'. |
||
785 | * Handler callbacks registered with $hook = all will be called for all hooks |
||
786 | * of type $type. Similarly, handlers registered with $type = all will be |
||
787 | * called for all hooks of type $event, regardless of $object_type. If $hook |
||
788 | * and $type both are 'all', the handler will be called for all hooks. |
||
789 | * |
||
790 | * @tip It's not possible for a plugin hook to change a non-null $returnvalue |
||
791 | * to null. |
||
792 | * |
||
793 | * @note Internal: The checks for $hook and/or $type not being equal to 'all' is to |
||
794 | * prevent a plugin hook being registered with an 'all' being called more than |
||
795 | * once if the trigger occurs with an 'all'. An example in core of this is in |
||
796 | * actions.php: |
||
797 | * elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', ...) |
||
798 | * |
||
799 | * @see elgg_register_plugin_hook_handler() |
||
800 | * |
||
801 | * @param string $hook The name of the hook to trigger ("all" will |
||
802 | * trigger for all $types regardless of $hook value) |
||
803 | * @param string $type The type of the hook to trigger ("all" will |
||
804 | * trigger for all $hooks regardless of $type value) |
||
805 | * @param mixed $params Additional parameters to pass to the handlers |
||
806 | * @param mixed $returnvalue An initial return value |
||
807 | * |
||
808 | * @return mixed|null The return value of the last handler callback called |
||
809 | * |
||
810 | * @example hooks/trigger/basic.php Trigger a hook that determines if execution |
||
811 | * should continue. |
||
812 | * @example hooks/trigger/advanced.php Trigger a hook with a default value and use |
||
813 | * the results to populate a menu. |
||
814 | * @example hooks/basic.php Trigger and respond to a basic plugin hook. |
||
815 | * |
||
816 | * @since 1.8.0 |
||
817 | */ |
||
818 | function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) { |
||
819 | return _elgg_services()->hooks->trigger($hook, $type, $params, $returnvalue); |
||
820 | } |
||
821 | 119 | ||
822 | /** |
||
823 | * Returns an ordered array of hook handlers registered for $hook and $type. |
||
824 | * |
||
825 | * @param string $hook Hook name |
||
826 | * @param string $type Hook type |
||
827 | * |
||
828 | * @return array |
||
829 | * |
||
830 | * @since 2.0.0 |
||
831 | */ |
||
832 | function elgg_get_ordered_hook_handlers($hook, $type) { |
||
833 | return _elgg_services()->hooks->getOrderedHandlers($hook, $type); |
||
834 | } |
||
835 | |||
836 | /** |
||
837 | * Returns an ordered array of event handlers registered for $event and $type. |
||
838 | * |
||
839 | * @param string $event Event name |
||
840 | * @param string $type Object type |
||
841 | * |
||
842 | * @return array |
||
843 | * |
||
844 | * @since 2.0.0 |
||
845 | */ |
||
846 | function elgg_get_ordered_event_handlers($event, $type) { |
||
847 | return _elgg_services()->events->getOrderedHandlers($event, $type); |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * Intercepts, logs, and displays uncaught exceptions. |
||
852 | * |
||
853 | * To use a viewtype other than failsafe, create the views: |
||
854 | * <viewtype>/messages/exceptions/admin_exception |
||
855 | * <viewtype>/messages/exceptions/exception |
||
856 | * See the json viewtype for an example. |
||
857 | * |
||
858 | * @warning This function should never be called directly. |
||
859 | * |
||
860 | * @see http://www.php.net/set-exception-handler |
||
861 | * |
||
862 | * @param Exception $exception The exception being handled |
||
863 | * |
||
864 | * @return void |
||
865 | * @access private |
||
866 | */ |
||
867 | function _elgg_php_exception_handler($exception) { |
||
868 | $timestamp = time(); |
||
869 | error_log("Exception at time $timestamp: $exception"); |
||
870 | |||
871 | // Wipe any existing output buffer |
||
872 | ob_end_clean(); |
||
873 | |||
874 | // make sure the error isn't cached |
||
875 | header("Cache-Control: no-cache, must-revalidate", true); |
||
876 | header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true); |
||
877 | |||
878 | $CONFIG = _elgg_services()->config->getStorageObject(); |
||
879 | |||
880 | try { |
||
881 | // allow custom scripts to trigger on exception |
||
882 | // $CONFIG->exception_include can be set locally in settings.php |
||
883 | // value should be a system path to a file to include |
||
884 | if (!empty($CONFIG->exception_include) && is_file($CONFIG->exception_include)) { |
||
885 | ob_start(); |
||
886 | |||
887 | // don't isolate, these scripts may use the local $exception var. |
||
888 | include $CONFIG->exception_include; |
||
889 | |||
890 | $exception_output = ob_get_clean(); |
||
891 | |||
892 | // if content is returned from the custom handler we will output |
||
893 | // that instead of our default failsafe view |
||
894 | if (!empty($exception_output)) { |
||
895 | echo $exception_output; |
||
896 | exit; |
||
897 | } |
||
898 | } |
||
899 | |||
900 | if (elgg_is_xhr()) { |
||
901 | elgg_set_viewtype('json'); |
||
902 | $response = new \Symfony\Component\HttpFoundation\JsonResponse(null, 500); |
||
903 | } else { |
||
904 | elgg_set_viewtype('failsafe'); |
||
905 | $response = new \Symfony\Component\HttpFoundation\Response('', 500); |
||
906 | } |
||
907 | |||
908 | if (elgg_is_admin_logged_in()) { |
||
909 | $body = elgg_view("messages/exceptions/admin_exception", [ |
||
910 | 'object' => $exception, |
||
911 | 'ts' => $timestamp |
||
912 | ]); |
||
913 | } else { |
||
914 | $body = elgg_view("messages/exceptions/exception", [ |
||
915 | 'object' => $exception, |
||
916 | 'ts' => $timestamp |
||
917 | ]); |
||
918 | } |
||
919 | |||
920 | $response->setContent(elgg_view_page(elgg_echo('exception:title'), $body)); |
||
921 | $response->send(); |
||
922 | } catch (Exception $e) { |
||
923 | $timestamp = time(); |
||
924 | $message = $e->getMessage(); |
||
925 | http_response_code(500); |
||
926 | echo "Fatal error in exception handler. Check log for Exception at time $timestamp"; |
||
927 | error_log("Exception at time $timestamp : fatal error in exception handler : $message"); |
||
928 | } |
||
929 | } |
||
930 | |||
931 | /** |
||
932 | * Intercepts catchable PHP errors. |
||
933 | * |
||
934 | * @warning This function should never be called directly. |
||
935 | * |
||
936 | * @internal |
||
937 | * For catchable fatal errors, throws an Exception with the error. |
||
938 | * |
||
939 | * For non-fatal errors, depending upon the debug settings, either |
||
940 | * log the error or ignore it. |
||
941 | * |
||
942 | * @see http://www.php.net/set-error-handler |
||
943 | * |
||
944 | * @param int $errno The level of the error raised |
||
945 | * @param string $errmsg The error message |
||
946 | * @param string $filename The filename the error was raised in |
||
947 | * @param int $linenum The line number the error was raised at |
||
948 | * @param array $vars An array that points to the active symbol table where error occurred |
||
949 | * |
||
950 | * @return true |
||
951 | * @throws Exception |
||
952 | * @access private |
||
953 | */ |
||
954 | function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) { |
||
0 ignored issues
–
show
|
|||
955 | |||
956 | $error = date("Y-m-d H:i:s (T)") . ": \"$errmsg\" in file $filename (line $linenum)"; |
||
957 | |||
958 | switch ($errno) { |
||
959 | case E_USER_ERROR: |
||
960 | if (!elgg_log("PHP: $error", 'ERROR')) { |
||
961 | error_log("PHP ERROR: $error"); |
||
962 | } |
||
963 | register_error("ERROR: $error"); |
||
964 | |||
965 | // Since this is a fatal error, we want to stop any further execution but do so gracefully. |
||
966 | throw new \Exception($error); |
||
967 | break; |
||
968 | |||
969 | case E_WARNING : |
||
970 | case E_USER_WARNING : |
||
971 | case E_RECOVERABLE_ERROR: // (e.g. type hint violation) |
||
972 | |||
973 | // check if the error wasn't suppressed by the error control operator (@) |
||
974 | if (error_reporting() && !elgg_log("PHP: $error", 'WARNING')) { |
||
975 | error_log("PHP WARNING: $error"); |
||
976 | } |
||
977 | break; |
||
978 | |||
979 | default: |
||
980 | $CONFIG = _elgg_services()->config->getStorageObject(); |
||
981 | if (isset($CONFIG->debug) && $CONFIG->debug === 'NOTICE') { |
||
982 | if (!elgg_log("PHP (errno $errno): $error", 'NOTICE')) { |
||
983 | error_log("PHP NOTICE: $error"); |
||
984 | } |
||
985 | } |
||
986 | } |
||
987 | |||
988 | return true; |
||
989 | } |
||
990 | |||
991 | |||
992 | /** |
||
993 | * Display or log a message. |
||
994 | * |
||
995 | * If $level is >= to the debug setting in {@link $CONFIG->debug}, the |
||
996 | * message will be sent to {@link elgg_dump()}. Messages with lower |
||
997 | * priority than {@link $CONFIG->debug} are ignored. |
||
998 | * |
||
999 | * Outputs all levels but NOTICE to screen by default. |
||
1000 | * |
||
1001 | * @note No messages will be displayed unless debugging has been enabled. |
||
1002 | * |
||
1003 | * @param string $message User message |
||
1004 | * @param string $level NOTICE | WARNING | ERROR |
||
1005 | * |
||
1006 | * @return bool |
||
1007 | * @since 1.7.0 |
||
1008 | */ |
||
1009 | function elgg_log($message, $level = 'NOTICE') { |
||
1010 | static $levels = [ |
||
1011 | 'INFO' => 200, |
||
1012 | 2 | 'NOTICE' => 250, |
|
1013 | 'WARNING' => 300, |
||
1014 | 'ERROR' => 400, |
||
1015 | ]; |
||
1016 | |||
1017 | if (!isset($levels[$level])) { |
||
1018 | throw new \InvalidArgumentException("Invalid \$level value"); |
||
1019 | 2 | } |
|
1020 | |||
1021 | $level = $levels[$level]; |
||
1022 | return _elgg_services()->logger->log($message, $level); |
||
1023 | 2 | } |
|
1024 | 2 | ||
1025 | /** |
||
1026 | * Logs or displays $value. |
||
1027 | * |
||
1028 | * If $to_screen is true, $value is displayed to screen. Else, |
||
1029 | * it is handled by PHP's {@link error_log()} function. |
||
1030 | * |
||
1031 | * A {@elgg_plugin_hook debug log} is called. If a handler returns |
||
1032 | * false, it will stop the default logging method. |
||
1033 | * |
||
1034 | * @param mixed $value The value |
||
1035 | * @param bool $to_screen Display to screen? |
||
1036 | * @return void |
||
1037 | * @since 1.7.0 |
||
1038 | */ |
||
1039 | function elgg_dump($value, $to_screen = true) { |
||
1040 | _elgg_services()->logger->dump($value, $to_screen); |
||
1041 | } |
||
1042 | |||
1043 | /** |
||
1044 | * Get the current Elgg version information |
||
1045 | * |
||
1046 | * @param bool $human_readable Whether to return a human readable version (default: false) |
||
1047 | * |
||
1048 | * @return string|false Depending on success |
||
1049 | * @since 1.9 |
||
1050 | */ |
||
1051 | function elgg_get_version($human_readable = false) { |
||
1052 | static $version, $release; |
||
1053 | |||
1054 | if (!isset($version) || !isset($release)) { |
||
1055 | $path = \Elgg\Application::elggDir()->getPath('version.php'); |
||
1056 | if (!is_file($path)) { |
||
1057 | return false; |
||
1058 | } |
||
1059 | include $path; |
||
1060 | } |
||
1061 | |||
1062 | return $human_readable ? $release : $version; |
||
1063 | } |
||
1064 | |||
1065 | /** |
||
1066 | * Log a notice about deprecated use of a function, view, etc. |
||
1067 | * |
||
1068 | * @param string $msg Message to log |
||
1069 | * @param string $dep_version Human-readable *release* version: 1.7, 1.8, ... |
||
1070 | * @param int $backtrace_level How many levels back to display the backtrace. |
||
1071 | * Useful if calling from functions that are called |
||
1072 | * from other places (like elgg_view()). Set to -1 |
||
1073 | * for a full backtrace. |
||
1074 | * |
||
1075 | * @return bool |
||
1076 | * @since 1.7.0 |
||
1077 | */ |
||
1078 | function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) { |
||
1079 | $backtrace_level += 1; |
||
1080 | return _elgg_services()->deprecation->sendNotice($msg, $dep_version, $backtrace_level); |
||
1081 | 3 | } |
|
1082 | 3 | ||
1083 | /** |
||
1084 | * Builds a URL from the a parts array like one returned by {@link parse_url()}. |
||
1085 | * |
||
1086 | * @note If only partial information is passed, a partial URL will be returned. |
||
1087 | * |
||
1088 | * @param array $parts Associative array of URL components like parse_url() returns |
||
1089 | * 'user' and 'pass' parts are ignored because of security reasons |
||
1090 | * @param bool $html_encode HTML Encode the url? |
||
1091 | * |
||
1092 | * @see https://github.com/Elgg/Elgg/pull/8146#issuecomment-91544585 |
||
1093 | * @return string Full URL |
||
1094 | * @since 1.7.0 |
||
1095 | */ |
||
1096 | function elgg_http_build_url(array $parts, $html_encode = true) { |
||
1097 | // build only what's given to us. |
||
1098 | $scheme = isset($parts['scheme']) ? "{$parts['scheme']}://" : ''; |
||
1099 | $host = isset($parts['host']) ? "{$parts['host']}" : ''; |
||
1100 | 94 | $port = isset($parts['port']) ? ":{$parts['port']}" : ''; |
|
1101 | 94 | $path = isset($parts['path']) ? "{$parts['path']}" : ''; |
|
1102 | 94 | $query = isset($parts['query']) ? "?{$parts['query']}" : ''; |
|
1103 | 94 | $fragment = isset($parts['fragment']) ? "#{$parts['fragment']}" : ''; |
|
1104 | 94 | ||
1105 | 94 | $string = $scheme . $host . $port . $path . $query . $fragment; |
|
1106 | |||
1107 | 94 | if ($html_encode) { |
|
1108 | return htmlspecialchars($string, ENT_QUOTES, 'UTF-8', false); |
||
1109 | 94 | } else { |
|
1110 | 22 | return $string; |
|
1111 | } |
||
1112 | 72 | } |
|
1113 | |||
1114 | /** |
||
1115 | * Adds action tokens to URL |
||
1116 | * |
||
1117 | * As of 1.7.0 action tokens are required on all actions. |
||
1118 | * Use this function to append action tokens to a URL's GET parameters. |
||
1119 | * This will preserve any existing GET parameters. |
||
1120 | * |
||
1121 | * @note If you are using {@elgg_view input/form} you don't need to |
||
1122 | * add tokens to the action. The form view automatically handles |
||
1123 | * tokens. |
||
1124 | * |
||
1125 | * @param string $url Full action URL |
||
1126 | * @param bool $html_encode HTML encode the url? (default: false) |
||
1127 | * |
||
1128 | * @return string URL with action tokens |
||
1129 | * @since 1.7.0 |
||
1130 | */ |
||
1131 | function elgg_add_action_tokens_to_url($url, $html_encode = false) { |
||
1132 | $url = elgg_normalize_url($url); |
||
1133 | $components = parse_url($url); |
||
1134 | |||
1135 | if (isset($components['query'])) { |
||
1136 | $query = elgg_parse_str($components['query']); |
||
1137 | } else { |
||
1138 | $query = []; |
||
1139 | } |
||
1140 | |||
1141 | if (isset($query['__elgg_ts']) && isset($query['__elgg_token'])) { |
||
1142 | return $url; |
||
1143 | } |
||
1144 | |||
1145 | // append action tokens to the existing query |
||
1146 | $query['__elgg_ts'] = time(); |
||
1147 | $query['__elgg_token'] = generate_action_token($query['__elgg_ts']); |
||
1148 | $components['query'] = http_build_query($query); |
||
1149 | |||
1150 | // rebuild the full url |
||
1151 | return elgg_http_build_url($components, $html_encode); |
||
1152 | } |
||
1153 | |||
1154 | /** |
||
1155 | * Removes an element from a URL's query string. |
||
1156 | * |
||
1157 | * @note You can send a partial URL string. |
||
1158 | * |
||
1159 | * @param string $url Full URL |
||
1160 | * @param string $element The element to remove |
||
1161 | * |
||
1162 | * @return string The new URL with the query element removed. |
||
1163 | * @since 1.7.0 |
||
1164 | */ |
||
1165 | function elgg_http_remove_url_query_element($url, $element) { |
||
1166 | return elgg_http_add_url_query_elements($url, [$element => null]); |
||
1167 | } |
||
1168 | 20 | ||
1169 | /** |
||
1170 | * Sets elements in a URL's query string. |
||
1171 | * |
||
1172 | * @param string $url The URL |
||
1173 | * @param array $elements Key/value pairs to set in the URL. If the value is null, the |
||
1174 | * element is removed from the URL. |
||
1175 | * |
||
1176 | * @return string The new URL with the query strings added |
||
1177 | * @since 1.7.0 |
||
1178 | */ |
||
1179 | function elgg_http_add_url_query_elements($url, array $elements) { |
||
1180 | $url_array = parse_url($url); |
||
1181 | |||
1182 | 72 | if (isset($url_array['query'])) { |
|
1183 | $query = elgg_parse_str($url_array['query']); |
||
1184 | 72 | } else { |
|
1185 | 35 | $query = []; |
|
1186 | } |
||
1187 | 37 | ||
1188 | foreach ($elements as $k => $v) { |
||
1189 | if ($v === null) { |
||
1190 | 72 | unset($query[$k]); |
|
1191 | 52 | } else { |
|
1192 | 24 | $query[$k] = $v; |
|
1193 | } |
||
1194 | 52 | } |
|
1195 | |||
1196 | // why check path? A: if no path, this may be a relative URL like "?foo=1". In this case, |
||
1197 | // the output "" would be interpreted the current URL, so in this case we *must* set |
||
1198 | // a query to make sure elements are removed. |
||
1199 | if ($query || empty($url_array['path'])) { |
||
1200 | $url_array['query'] = http_build_query($query); |
||
1201 | 72 | } else { |
|
1202 | 47 | unset($url_array['query']); |
|
1203 | } |
||
1204 | 25 | $string = elgg_http_build_url($url_array, false); |
|
1205 | |||
1206 | 72 | // Restore relative protocol to url if missing and is provided as part of the initial url (see #9874) |
|
1207 | if (!isset($url['scheme']) && (substr($url, 0, 2) == '//')) { |
||
1208 | $string = "//{$string}"; |
||
1209 | 72 | } |
|
1210 | 3 | ||
1211 | return $string; |
||
1212 | } |
||
1213 | 72 | ||
1214 | /** |
||
1215 | * Test if two URLs are functionally identical. |
||
1216 | * |
||
1217 | * @tip If $ignore_params is used, neither the name nor its value will be considered when comparing. |
||
1218 | * |
||
1219 | * @tip The order of GET params doesn't matter. |
||
1220 | * |
||
1221 | * @param string $url1 First URL |
||
1222 | * @param string $url2 Second URL |
||
1223 | * @param array $ignore_params GET params to ignore in the comparison |
||
1224 | * |
||
1225 | * @return bool |
||
1226 | * @since 1.8.0 |
||
1227 | */ |
||
1228 | function elgg_http_url_is_identical($url1, $url2, $ignore_params = ['offset', 'limit']) { |
||
1229 | $url1 = elgg_normalize_url($url1); |
||
1230 | $url2 = elgg_normalize_url($url2); |
||
1231 | 29 | ||
1232 | 29 | // @todo - should probably do something with relative URLs |
|
1233 | |||
1234 | if ($url1 == $url2) { |
||
1235 | return true; |
||
1236 | 29 | } |
|
1237 | 24 | ||
1238 | $url1_info = parse_url($url1); |
||
1239 | $url2_info = parse_url($url2); |
||
1240 | 5 | ||
1241 | 5 | if (isset($url1_info['path'])) { |
|
1242 | $url1_info['path'] = trim($url1_info['path'], '/'); |
||
1243 | 5 | } |
|
1244 | 5 | if (isset($url2_info['path'])) { |
|
1245 | $url2_info['path'] = trim($url2_info['path'], '/'); |
||
1246 | 5 | } |
|
1247 | 5 | ||
1248 | // compare basic bits |
||
1249 | $parts = ['scheme', 'host', 'path']; |
||
1250 | |||
1251 | 5 | foreach ($parts as $part) { |
|
1252 | if ((isset($url1_info[$part]) && isset($url2_info[$part])) |
||
1253 | 5 | && $url1_info[$part] != $url2_info[$part]) { |
|
1254 | 5 | return false; |
|
1255 | 5 | } elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) { |
|
1256 | return false; |
||
1257 | 5 | } elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) { |
|
1258 | return false; |
||
1259 | 5 | } |
|
1260 | 5 | } |
|
1261 | |||
1262 | // quick compare of get params |
||
1263 | if (isset($url1_info['query']) && isset($url2_info['query']) |
||
1264 | && $url1_info['query'] == $url2_info['query']) { |
||
1265 | 5 | return true; |
|
1266 | 5 | } |
|
1267 | |||
1268 | // compare get params that might be out of order |
||
1269 | $url1_params = []; |
||
1270 | $url2_params = []; |
||
1271 | 5 | ||
1272 | 5 | if (isset($url1_info['query'])) { |
|
1273 | if ($url1_info['query'] = html_entity_decode($url1_info['query'])) { |
||
1274 | 5 | $url1_params = elgg_parse_str($url1_info['query']); |
|
1275 | 5 | } |
|
1276 | 5 | } |
|
1277 | |||
1278 | if (isset($url2_info['query'])) { |
||
1279 | if ($url2_info['query'] = html_entity_decode($url2_info['query'])) { |
||
1280 | 5 | $url2_params = elgg_parse_str($url2_info['query']); |
|
1281 | 5 | } |
|
1282 | 5 | } |
|
1283 | |||
1284 | // drop ignored params |
||
1285 | foreach ($ignore_params as $param) { |
||
1286 | if (isset($url1_params[$param])) { |
||
1287 | 5 | unset($url1_params[$param]); |
|
1288 | 4 | } |
|
1289 | 3 | if (isset($url2_params[$param])) { |
|
1290 | unset($url2_params[$param]); |
||
1291 | 4 | } |
|
1292 | 4 | } |
|
1293 | |||
1294 | // array_diff_assoc only returns the items in arr1 that aren't in arrN |
||
1295 | // but not the items that ARE in arrN but NOT in arr1 |
||
1296 | // if arr1 is an empty array, this function will return 0 no matter what. |
||
1297 | // since we only care if they're different and not how different, |
||
1298 | // add the results together to get a non-zero (ie, different) result |
||
1299 | $diff_count = count(array_diff_assoc($url1_params, $url2_params)); |
||
1300 | $diff_count += count(array_diff_assoc($url2_params, $url1_params)); |
||
1301 | 5 | if ($diff_count > 0) { |
|
1302 | 5 | return false; |
|
1303 | 5 | } |
|
1304 | 2 | ||
1305 | return true; |
||
1306 | } |
||
1307 | 3 | ||
1308 | /** |
||
1309 | * Signs provided URL with a SHA256 HMAC key |
||
1310 | * |
||
1311 | * @note Signed URLs do not offer CSRF protection and should not be used instead of action tokens. |
||
1312 | * |
||
1313 | * @param string $url URL to sign |
||
1314 | * @param string $expires Expiration time |
||
1315 | * A string suitable for strtotime() |
||
1316 | * Falsey values indicate non-expiring URL |
||
1317 | * @return string |
||
1318 | */ |
||
1319 | function elgg_http_get_signed_url($url, $expires = false) { |
||
1320 | return _elgg_services()->urlSigner->sign($url, $expires); |
||
1321 | } |
||
1322 | |||
1323 | /** |
||
1324 | * Validates if the HMAC signature of the URL is valid |
||
1325 | * |
||
1326 | * @param string $url URL to validate |
||
1327 | * @return bool |
||
1328 | */ |
||
1329 | function elgg_http_validate_signed_url($url) { |
||
1330 | return _elgg_services()->urlSigner->isValid($url); |
||
1331 | } |
||
1332 | |||
1333 | /** |
||
1334 | * Validates if the HMAC signature of the current request is valid |
||
1335 | * Issues 403 response if signature is inalid |
||
1336 | * @return void |
||
1337 | */ |
||
1338 | function elgg_signed_request_gatekeeper() { |
||
1339 | if (!elgg_http_validate_signed_url(current_page_url())) { |
||
1340 | register_error(elgg_echo('invalid_request_signature')); |
||
1341 | forward('', '403'); |
||
1342 | } |
||
1343 | } |
||
1344 | |||
1345 | /** |
||
1346 | * Checks for $array[$key] and returns its value if it exists, else |
||
1347 | * returns $default. |
||
1348 | * |
||
1349 | * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default'; |
||
1350 | * |
||
1351 | * @param string $key Key to check in the source array |
||
1352 | * @param array $array Source array |
||
1353 | * @param mixed $default Value to return if key is not found |
||
1354 | * @param bool $strict Return array key if it's set, even if empty. If false, |
||
1355 | * return $default if the array key is unset or empty. |
||
1356 | * |
||
1357 | * @return mixed |
||
1358 | * @since 1.8.0 |
||
1359 | */ |
||
1360 | function elgg_extract($key, $array, $default = null, $strict = true) { |
||
1361 | if (!is_array($array)) { |
||
1362 | return $default; |
||
1363 | 213 | } |
|
1364 | |||
1365 | if ($strict) { |
||
1366 | return (isset($array[$key])) ? $array[$key] : $default; |
||
1367 | 213 | } else { |
|
1368 | 213 | return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default; |
|
1369 | } |
||
1370 | 48 | } |
|
1371 | |||
1372 | /** |
||
1373 | * Extract class names from an array with key "class", optionally merging into a preexisting set. |
||
1374 | * |
||
1375 | * @param array $array Source array |
||
1376 | * @param string|string[] $existing Existing name(s) |
||
1377 | * @return string[] |
||
1378 | * |
||
1379 | * @since 2.3.0 |
||
1380 | */ |
||
1381 | function elgg_extract_class(array $array, $existing = []) { |
||
1382 | $existing = empty($existing) ? [] : (array) $existing; |
||
1383 | |||
1384 | 3 | $merge = (array) elgg_extract('class', $array, []); |
|
1385 | |||
1386 | 3 | array_splice($existing, count($existing), 0, $merge); |
|
1387 | |||
1388 | 3 | return array_values(array_unique($existing)); |
|
1389 | } |
||
1390 | 3 | ||
1391 | /** |
||
1392 | * Sorts a 3d array by specific element. |
||
1393 | * |
||
1394 | * @warning Will re-index numeric indexes. |
||
1395 | * |
||
1396 | * @note This operates the same as the built-in sort functions. |
||
1397 | * It sorts the array and returns a bool for success. |
||
1398 | * |
||
1399 | * Do this: elgg_sort_3d_array_by_value($my_array); |
||
1400 | * Not this: $my_array = elgg_sort_3d_array_by_value($my_array); |
||
1401 | * |
||
1402 | * @param array &$array Array to sort |
||
1403 | * @param string $element Element to sort by |
||
1404 | * @param int $sort_order PHP sort order |
||
1405 | * {@link http://us2.php.net/array_multisort} |
||
1406 | * @param int $sort_type PHP sort type |
||
1407 | * {@link http://us2.php.net/sort} |
||
1408 | * |
||
1409 | * @return bool |
||
1410 | */ |
||
1411 | function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC, $sort_type = SORT_LOCALE_STRING) { |
||
1412 | |||
1413 | $sort = []; |
||
1414 | |||
1415 | foreach ($array as $v) { |
||
1416 | if (isset($v[$element])) { |
||
1417 | $sort[] = strtolower($v[$element]); |
||
1418 | } else { |
||
1419 | $sort[] = null; |
||
1420 | } |
||
1421 | }; |
||
1422 | |||
1423 | return array_multisort($sort, $sort_order, $sort_type, $array); |
||
1424 | } |
||
1425 | |||
1426 | /** |
||
1427 | * Return the state of a php.ini setting as a bool |
||
1428 | * |
||
1429 | * @warning Using this on ini settings that are not boolean |
||
1430 | * will be inaccurate! |
||
1431 | * |
||
1432 | * @param string $ini_get_arg The INI setting |
||
1433 | * |
||
1434 | * @return bool Depending on whether it's on or off |
||
1435 | */ |
||
1436 | function ini_get_bool($ini_get_arg) { |
||
1437 | $temp = strtolower(ini_get($ini_get_arg)); |
||
1438 | |||
1439 | if ($temp == '1' || $temp == 'on' || $temp == 'true') { |
||
1440 | return true; |
||
1441 | } |
||
1442 | return false; |
||
1443 | } |
||
1444 | |||
1445 | /** |
||
1446 | * Returns a PHP INI setting in bytes. |
||
1447 | * |
||
1448 | * @tip Use this for arithmetic when determining if a file can be uploaded. |
||
1449 | * |
||
1450 | * @param string $setting The php.ini setting |
||
1451 | * |
||
1452 | * @return int |
||
1453 | * @since 1.7.0 |
||
1454 | * @link http://www.php.net/manual/en/function.ini-get.php |
||
1455 | */ |
||
1456 | function elgg_get_ini_setting_in_bytes($setting) { |
||
1457 | // retrieve INI setting |
||
1458 | $val = ini_get($setting); |
||
1459 | |||
1460 | // convert INI setting when shorthand notation is used |
||
1461 | $last = strtolower($val[strlen($val) - 1]); |
||
1462 | if (in_array($last, ['g', 'm', 'k'])) { |
||
1463 | $val = substr($val, 0, -1); |
||
1464 | } |
||
1465 | $val = (int) $val; |
||
1466 | switch ($last) { |
||
1467 | case 'g': |
||
1468 | $val *= 1024; |
||
1469 | // fallthrough intentional |
||
1470 | case 'm': |
||
1471 | $val *= 1024; |
||
1472 | // fallthrough intentional |
||
1473 | case 'k': |
||
1474 | $val *= 1024; |
||
1475 | } |
||
1476 | |||
1477 | // return byte value |
||
1478 | return $val; |
||
1479 | } |
||
1480 | |||
1481 | /** |
||
1482 | * Returns true is string is not empty, false, or null. |
||
1483 | * |
||
1484 | * Function to be used in array_filter which returns true if $string is not null. |
||
1485 | * |
||
1486 | * @param string $string The string to test |
||
1487 | * |
||
1488 | * @return bool |
||
1489 | * @todo This is used once in metadata.php. Use a lambda function instead. |
||
1490 | */ |
||
1491 | function is_not_null($string) { |
||
1492 | if (($string === '') || ($string === false) || ($string === null)) { |
||
1493 | return false; |
||
1494 | 1 | } |
|
1495 | |||
1496 | return true; |
||
1497 | } |
||
1498 | 1 | ||
1499 | /** |
||
1500 | * Normalise the singular keys in an options array to plural keys. |
||
1501 | * |
||
1502 | * Used in elgg_get_entities*() functions to support shortcutting plural |
||
1503 | * names by singular names. |
||
1504 | * |
||
1505 | * @param array $options The options array. $options['keys'] = 'values'; |
||
1506 | * @param array $singulars A list of singular words to pluralize by adding 's'. |
||
1507 | * |
||
1508 | * @return array |
||
1509 | * @since 1.7.0 |
||
1510 | * @access private |
||
1511 | */ |
||
1512 | function _elgg_normalize_plural_options_array($options, $singulars) { |
||
1513 | foreach ($singulars as $singular) { |
||
1514 | $plural = $singular . 's'; |
||
1515 | 99 | ||
1516 | 99 | if (array_key_exists($singular, $options)) { |
|
1517 | if ($options[$singular] === ELGG_ENTITIES_ANY_VALUE) { |
||
1518 | 99 | $options[$plural] = $options[$singular]; |
|
1519 | 99 | } else { |
|
1520 | // Test for array refs #2641 |
||
1521 | if (!is_array($options[$singular])) { |
||
1522 | $options[$plural] = [$options[$singular]]; |
||
1523 | 99 | } else { |
|
1524 | 99 | $options[$plural] = $options[$singular]; |
|
1525 | } |
||
1526 | } |
||
1527 | } |
||
1528 | |||
1529 | unset($options[$singular]); |
||
1530 | } |
||
1531 | 99 | ||
1532 | return $options; |
||
1533 | } |
||
1534 | 99 | ||
1535 | /** |
||
1536 | * Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped. |
||
1537 | * |
||
1538 | * @tip Register for the shutdown:system event to perform functions at the end of page loads. |
||
1539 | * |
||
1540 | * @warning Using this event to perform long-running functions is not very |
||
1541 | * useful. Servers will hold pages until processing is done before sending |
||
1542 | * them out to the browser. |
||
1543 | * |
||
1544 | * @see http://www.php.net/register-shutdown-function |
||
1545 | * |
||
1546 | * @internal This is registered in \Elgg\Application::create() |
||
1547 | * |
||
1548 | * @return void |
||
1549 | * @see register_shutdown_hook() |
||
1550 | * @access private |
||
1551 | */ |
||
1552 | function _elgg_shutdown_hook() { |
||
1553 | try { |
||
1554 | _elgg_services()->logger->setDisplay(false); |
||
1555 | elgg_trigger_event('shutdown', 'system'); |
||
1556 | |||
1557 | $time = (float) (microtime(true) - $GLOBALS['START_MICROTIME']); |
||
1558 | $uri = _elgg_services()->request->server->get('REQUEST_URI', 'CLI'); |
||
1559 | // demoted to NOTICE from DEBUG so javascript is not corrupted |
||
1560 | elgg_log("Page {$uri} generated in $time seconds", 'INFO'); |
||
1561 | } catch (Exception $e) { |
||
1562 | $message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. '; |
||
1563 | $message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})"; |
||
1564 | error_log($message); |
||
1565 | error_log("Exception trace stack: {$e->getTraceAsString()}"); |
||
1566 | } |
||
1567 | |||
1568 | // Prevent an APC session bug: https://bugs.php.net/bug.php?id=60657 |
||
1569 | session_write_close(); |
||
1570 | } |
||
1571 | |||
1572 | /** |
||
1573 | * Serve individual views for Ajax. |
||
1574 | * |
||
1575 | * /ajax/view/<view_name>?<key/value params> |
||
1576 | * /ajax/form/<action_name>?<key/value params> |
||
1577 | * |
||
1578 | * @param string[] $segments URL segments (not including "ajax") |
||
1579 | * @return ResponseBuilder |
||
1580 | * |
||
1581 | * @see elgg_register_ajax_view() |
||
1582 | * @elgg_pagehandler ajax |
||
1583 | * @access private |
||
1584 | */ |
||
1585 | function _elgg_ajax_page_handler($segments) { |
||
1586 | elgg_ajax_gatekeeper(); |
||
1587 | |||
1588 | 17 | if (count($segments) < 2) { |
|
1589 | return false; |
||
1590 | 17 | } |
|
1591 | |||
1592 | if ($segments[0] === 'view' || $segments[0] === 'form') { |
||
1593 | if ($segments[0] === 'view') { |
||
1594 | 17 | // ignore 'view/' |
|
1595 | 17 | $view = implode('/', array_slice($segments, 1)); |
|
1596 | } else { |
||
1597 | 13 | // form views start with "forms", not "form" |
|
1598 | $view = 'forms/' . implode('/', array_slice($segments, 1)); |
||
1599 | } |
||
1600 | 4 | ||
1601 | $ajax_api = _elgg_services()->ajax; |
||
1602 | $allowed_views = $ajax_api->getViews(); |
||
1603 | 17 | ||
1604 | 17 | // cacheable views are always allowed |
|
1605 | if (!in_array($view, $allowed_views) && !_elgg_services()->views->isCacheableView($view)) { |
||
1606 | return elgg_error_response("Ajax view '$view' was not registered", REFERRER, ELGG_HTTP_FORBIDDEN); |
||
1607 | 17 | } |
|
1608 | 4 | ||
1609 | // pull out GET parameters through filter |
||
1610 | $vars = []; |
||
1611 | foreach (_elgg_services()->request->query->keys() as $name) { |
||
1612 | 13 | $vars[$name] = get_input($name); |
|
1613 | 13 | } |
|
1614 | 7 | ||
1615 | if (isset($vars['guid'])) { |
||
1616 | $vars['entity'] = get_entity($vars['guid']); |
||
1617 | 13 | } |
|
1618 | |||
1619 | $content_type = ''; |
||
1620 | if ($segments[0] === 'view') { |
||
1621 | 13 | $output = elgg_view($view, $vars); |
|
1622 | 13 | ||
1623 | 11 | // Try to guess the mime-type |
|
1624 | switch ($segments[1]) { |
||
1625 | case "js": |
||
1626 | 11 | $content_type = 'text/javascript;charset=utf-8'; |
|
1627 | 11 | break; |
|
1628 | 2 | case "css": |
|
1629 | 2 | $content_type = 'text/css;charset=utf-8'; |
|
1630 | 9 | break; |
|
1631 | 2 | default : |
|
1632 | 2 | if (_elgg_services()->views->isCacheableView($view)) { |
|
1633 | $file = _elgg_services()->views->findViewFile($view, elgg_get_viewtype()); |
||
1634 | 7 | $content_type = (new \Elgg\Filesystem\MimeTypeDetector())->getType($file, 'text/html'); |
|
1635 | 2 | } |
|
1636 | 2 | break; |
|
1637 | } |
||
1638 | 11 | } else { |
|
1639 | $action = implode('/', array_slice($segments, 1)); |
||
1640 | $output = elgg_view_form($action, [], $vars); |
||
1641 | 2 | } |
|
1642 | 2 | ||
1643 | if ($content_type) { |
||
1644 | elgg_set_http_header("Content-Type: $content_type"); |
||
1645 | 13 | } |
|
1646 | 6 | ||
1647 | return elgg_ok_response($output); |
||
1648 | } |
||
1649 | 13 | ||
1650 | return false; |
||
1651 | } |
||
1652 | |||
1653 | /** |
||
1654 | * Handle requests for /favicon.ico |
||
1655 | * |
||
1656 | * @param string[] $segments The URL segments |
||
1657 | * @return bool |
||
1658 | * @access private |
||
1659 | * @since 1.10 |
||
1660 | */ |
||
1661 | function _elgg_favicon_page_handler($segments) { |
||
1662 | header("HTTP/1.1 404 Not Found", true, 404); |
||
1663 | |||
1664 | header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+1 week")), true); |
||
1665 | header("Pragma: public", true); |
||
1666 | header("Cache-Control: public", true); |
||
1667 | |||
1668 | header('Content-Type: image/x-icon'); |
||
1669 | echo elgg_view('graphics/favicon.ico'); |
||
1670 | |||
1671 | return true; |
||
1672 | } |
||
1673 | |||
1674 | /** |
||
1675 | * Reverses the ordering in an ORDER BY clause. This is achived by replacing |
||
1676 | * asc with desc, or appending desc to the end of the clause. |
||
1677 | * |
||
1678 | * This is used mostly for elgg_get_entities() and other similar functions. |
||
1679 | * |
||
1680 | * @param string $order_by An order by clause |
||
1681 | * @access private |
||
1682 | * @return string |
||
1683 | * @access private |
||
1684 | */ |
||
1685 | function _elgg_sql_reverse_order_by_clause($order_by) { |
||
1686 | $order_by = strtolower($order_by); |
||
1687 | |||
1688 | if (strpos($order_by, ' asc') !== false) { |
||
1689 | $return = str_replace(' asc', ' desc', $order_by); |
||
1690 | } elseif (strpos($order_by, ' desc') !== false) { |
||
1691 | $return = str_replace(' desc', ' asc', $order_by); |
||
1692 | } else { |
||
1693 | // no order specified, so default to desc since mysql defaults to asc |
||
1694 | $return = $order_by . ' desc'; |
||
1695 | } |
||
1696 | |||
1697 | return $return; |
||
1698 | } |
||
1699 | |||
1700 | /** |
||
1701 | * Enable objects with an enable() method. |
||
1702 | * |
||
1703 | * Used as a callback for \ElggBatch. |
||
1704 | * |
||
1705 | * @todo why aren't these static methods on \ElggBatch? |
||
1706 | * |
||
1707 | * @param object $object The object to enable |
||
1708 | * @return bool |
||
1709 | * @access private |
||
1710 | */ |
||
1711 | function elgg_batch_enable_callback($object) { |
||
1712 | // our db functions return the number of rows affected... |
||
1713 | return $object->enable() ? true : false; |
||
1714 | } |
||
1715 | |||
1716 | /** |
||
1717 | * Disable objects with a disable() method. |
||
1718 | * |
||
1719 | * Used as a callback for \ElggBatch. |
||
1720 | * |
||
1721 | * @param object $object The object to disable |
||
1722 | * @return bool |
||
1723 | * @access private |
||
1724 | */ |
||
1725 | function elgg_batch_disable_callback($object) { |
||
1726 | // our db functions return the number of rows affected... |
||
1727 | return $object->disable() ? true : false; |
||
1728 | } |
||
1729 | |||
1730 | /** |
||
1731 | * Delete objects with a delete() method. |
||
1732 | * |
||
1733 | * Used as a callback for \ElggBatch. |
||
1734 | * |
||
1735 | * @param object $object The object to disable |
||
1736 | * @return bool |
||
1737 | * @access private |
||
1738 | */ |
||
1739 | function elgg_batch_delete_callback($object) { |
||
1740 | // our db functions return the number of rows affected... |
||
1741 | return $object->delete() ? true : false; |
||
1742 | } |
||
1743 | |||
1744 | /** |
||
1745 | * Checks if there are some constraints on the options array for |
||
1746 | * potentially dangerous operations. |
||
1747 | * |
||
1748 | * @param array $options Options array |
||
1749 | * @param string $type Options type: metadata, annotation or river |
||
1750 | * @return bool |
||
1751 | * @access private |
||
1752 | */ |
||
1753 | function _elgg_is_valid_options_for_batch_operation($options, $type) { |
||
1754 | if (!$options || !is_array($options)) { |
||
1755 | return false; |
||
1756 | 2 | } |
|
1757 | |||
1758 | // at least one of these is required. |
||
1759 | $required = [ |
||
1760 | // generic restraints |
||
1761 | 'guid', 'guids' |
||
1762 | ]; |
||
1763 | 2 | ||
1764 | switch ($type) { |
||
1765 | case 'metadata': |
||
1766 | $metadata_required = [ |
||
1767 | 2 | 'metadata_owner_guid', 'metadata_owner_guids', |
|
1768 | 'metadata_name', 'metadata_names', |
||
1769 | 1 | 'metadata_value', 'metadata_values' |
|
1770 | ]; |
||
1771 | |||
1772 | $required = array_merge($required, $metadata_required); |
||
1773 | break; |
||
1774 | 1 | ||
1775 | 1 | case 'annotations': |
|
1776 | case 'annotation': |
||
1777 | 2 | $annotations_required = [ |
|
1778 | 2 | 'annotation_owner_guid', 'annotation_owner_guids', |
|
1779 | 'annotation_name', 'annotation_names', |
||
1780 | 2 | 'annotation_value', 'annotation_values' |
|
1781 | ]; |
||
1782 | |||
1783 | $required = array_merge($required, $annotations_required); |
||
1784 | break; |
||
1785 | 2 | ||
1786 | 2 | case 'river': |
|
1787 | // overriding generic restraints as guids isn't supported in river |
||
1788 | 1 | $required = [ |
|
1789 | 'id', 'ids', |
||
1790 | 'subject_guid', 'subject_guids', |
||
1791 | 1 | 'object_guid', 'object_guids', |
|
1792 | 'target_guid', 'target_guids', |
||
1793 | 'annotation_id', 'annotation_ids', |
||
1794 | 'view', 'views', |
||
1795 | ]; |
||
1796 | break; |
||
1797 | |||
1798 | 1 | default: |
|
1799 | return false; |
||
1800 | } |
||
1801 | |||
1802 | foreach ($required as $key) { |
||
1803 | // check that it exists and is something. |
||
1804 | 2 | if (isset($options[$key]) && $options[$key]) { |
|
1805 | return true; |
||
1806 | 2 | } |
|
1807 | 2 | } |
|
1808 | |||
1809 | return false; |
||
1810 | } |
||
1811 | |||
1812 | /** |
||
1813 | * Intercepts the index page when Walled Garden mode is enabled. |
||
1814 | * |
||
1815 | * @return ResponseBuilder |
||
1816 | * @access private |
||
1817 | */ |
||
1818 | function _elgg_walled_garden_index() { |
||
1819 | return elgg_ok_response(elgg_view_resource('walled_garden')); |
||
1820 | } |
||
1821 | |||
1822 | /** |
||
1823 | * Checks the status of the Walled Garden and forwards to a login page |
||
1824 | * if required. |
||
1825 | * |
||
1826 | * If the site is in Walled Garden mode, all page except those registered as |
||
1827 | * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to |
||
1828 | * a login page. |
||
1829 | * |
||
1830 | * @since 1.8.0 |
||
1831 | * @elgg_event_handler init system |
||
1832 | * @return void |
||
1833 | * @access private |
||
1834 | */ |
||
1835 | function _elgg_walled_garden_init() { |
||
1836 | if (!elgg_get_config('walled_garden')) { |
||
1837 | return; |
||
1838 | } |
||
1839 | |||
1840 | elgg_register_css('elgg.walled_garden', elgg_get_simplecache_url('walled_garden.css')); |
||
1841 | |||
1842 | elgg_register_plugin_hook_handler('register', 'menu:walled_garden', '_elgg_walled_garden_menu'); |
||
1843 | |||
1844 | elgg_register_page_handler('walled_garden', '_elgg_walled_garden_ajax_handler'); |
||
1845 | |||
1846 | if (elgg_get_config('default_access') == ACCESS_PUBLIC) { |
||
1847 | elgg_set_config('default_access', ACCESS_LOGGED_IN); |
||
1848 | } |
||
1849 | |||
1850 | elgg_register_plugin_hook_handler('access:collections:write', 'all', '_elgg_walled_garden_remove_public_access', 9999); |
||
1851 | |||
1852 | if (!elgg_is_logged_in()) { |
||
1853 | // override the front page |
||
1854 | elgg_register_page_handler('', '_elgg_walled_garden_index'); |
||
1855 | } |
||
1856 | } |
||
1857 | |||
1858 | /** |
||
1859 | * Adds home link to walled garden menu |
||
1860 | * |
||
1861 | * @param string $hook 'register' |
||
1862 | * @param string $type 'menu:walled_garden' |
||
1863 | * @param array $return_value Current menu items |
||
1864 | * @param array $params Optional menu parameters |
||
1865 | * |
||
1866 | * @return array |
||
1867 | * |
||
1868 | * @access private |
||
1869 | */ |
||
1870 | function _elgg_walled_garden_menu($hook, $type, $return_value, $params) { |
||
1871 | |||
1872 | if (current_page_url() === elgg_get_site_url()) { |
||
1873 | return; |
||
1874 | } |
||
1875 | |||
1876 | $return_value[] = \ElggMenuItem::factory([ |
||
1877 | 'name' => 'home', |
||
1878 | 'href' => '/', |
||
1879 | 'text' => elgg_echo('walled_garden:home'), |
||
1880 | 'priority' => 10, |
||
1881 | ]); |
||
1882 | |||
1883 | return $return_value; |
||
1884 | } |
||
1885 | |||
1886 | /** |
||
1887 | * Remove public access for walled gardens |
||
1888 | * |
||
1889 | * @param string $hook |
||
1890 | * @param string $type |
||
1891 | * @param array $accesses |
||
1892 | * @return array |
||
1893 | * @access private |
||
1894 | */ |
||
1895 | function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) { |
||
1896 | if (isset($accesses[ACCESS_PUBLIC])) { |
||
1897 | unset($accesses[ACCESS_PUBLIC]); |
||
1898 | } |
||
1899 | return $accesses; |
||
1900 | } |
||
1901 | |||
1902 | /** |
||
1903 | * Elgg's main init. |
||
1904 | * |
||
1905 | * Handles core actions for comments, the JS pagehandler, and the shutdown function. |
||
1906 | * |
||
1907 | * @elgg_event_handler init system |
||
1908 | * @return void |
||
1909 | * @access private |
||
1910 | */ |
||
1911 | function _elgg_init() { |
||
1912 | elgg_register_action('entity/delete'); |
||
1913 | elgg_register_action('comment/save'); |
||
1914 | elgg_register_action('comment/delete'); |
||
1915 | |||
1916 | elgg_register_page_handler('ajax', '_elgg_ajax_page_handler'); |
||
1917 | elgg_register_page_handler('favicon.ico', '_elgg_favicon_page_handler'); |
||
1918 | |||
1919 | elgg_register_page_handler('manifest.json', function() { |
||
1920 | $site = elgg_get_site_entity(); |
||
1921 | $resource = new \Elgg\Http\WebAppManifestResource($site); |
||
1922 | header('Content-Type: application/json;charset=utf-8'); |
||
1923 | echo json_encode($resource->get()); |
||
1924 | return true; |
||
1925 | }); |
||
1926 | |||
1927 | elgg_register_plugin_hook_handler('head', 'page', function($hook, $type, array $result) { |
||
1928 | $result['links']['manifest'] = [ |
||
1929 | 'rel' => 'manifest', |
||
1930 | 'href' => elgg_normalize_url('/manifest.json'), |
||
1931 | ]; |
||
1932 | |||
1933 | return $result; |
||
1934 | }); |
||
1935 | |||
1936 | if (_elgg_services()->config->getVolatile('enable_profiling')) { |
||
1937 | /** |
||
1938 | * @see \Elgg\Profiler::handlePageOutput |
||
1939 | */ |
||
1940 | elgg_register_plugin_hook_handler('output', 'page', [\Elgg\Profiler::class, 'handlePageOutput'], 999); |
||
1941 | } |
||
1942 | } |
||
1943 | |||
1944 | /** |
||
1945 | * Adds unit tests for the general API. |
||
1946 | * |
||
1947 | * @param string $hook unit_test |
||
1948 | * @param string $type system |
||
1949 | * @param array $value array of test files |
||
1950 | * @param array $params empty |
||
1951 | * |
||
1952 | * @elgg_plugin_hook unit_tests system |
||
1953 | * @return array |
||
1954 | * @access private |
||
1955 | */ |
||
1956 | function _elgg_api_test($hook, $type, $value, $params) { |
||
1957 | global $CONFIG; |
||
1958 | $value[] = $CONFIG->path . 'engine/tests/ElggTravisInstallTest.php'; |
||
1959 | $value[] = $CONFIG->path . 'engine/tests/ElggCoreHelpersTest.php'; |
||
1960 | $value[] = $CONFIG->path . 'engine/tests/ElggCoreRegressionBugsTest.php'; |
||
1961 | $value[] = $CONFIG->path . 'engine/tests/ElggBatchTest.php'; |
||
1962 | return $value; |
||
1963 | } |
||
1964 | |||
1965 | /**#@+ |
||
1966 | * Controls access levels on \ElggEntity entities, metadata, and annotations. |
||
1967 | * |
||
1968 | * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not |
||
1969 | * use it when saving an entity. |
||
1970 | * |
||
1971 | * @var int |
||
1972 | */ |
||
1973 | define('ACCESS_DEFAULT', -1); |
||
1974 | define('ACCESS_PRIVATE', 0); |
||
1975 | define('ACCESS_LOGGED_IN', 1); |
||
1976 | define('ACCESS_PUBLIC', 2); |
||
1977 | define('ACCESS_FRIENDS', -2); |
||
1978 | /**#@-*/ |
||
1979 | |||
1980 | /** |
||
1981 | * Constant to request the value of a parameter be ignored in elgg_get_*() functions |
||
1982 | * |
||
1983 | * @see elgg_get_entities() |
||
1984 | * @var null |
||
1985 | * @since 1.7 |
||
1986 | */ |
||
1987 | define('ELGG_ENTITIES_ANY_VALUE', null); |
||
1988 | |||
1989 | /** |
||
1990 | * Constant to request the value of a parameter be nothing in elgg_get_*() functions. |
||
1991 | * |
||
1992 | * @see elgg_get_entities() |
||
1993 | * @var int 0 |
||
1994 | * @since 1.7 |
||
1995 | */ |
||
1996 | define('ELGG_ENTITIES_NO_VALUE', 0); |
||
1997 | |||
1998 | /** |
||
1999 | * Used in calls to forward() to specify the browser should be redirected to the |
||
2000 | * referring page. |
||
2001 | * |
||
2002 | * @see forward |
||
2003 | * @var int -1 |
||
2004 | */ |
||
2005 | define('REFERRER', -1); |
||
2006 | |||
2007 | /** |
||
2008 | * Alternate spelling for REFERRER. Included because of some bad documentation |
||
2009 | * in the original HTTP spec. |
||
2010 | * |
||
2011 | * @see forward() |
||
2012 | * @link http://en.wikipedia.org/wiki/HTTP_referrer#Origin_of_the_term_referer |
||
2013 | * @var int -1 |
||
2014 | */ |
||
2015 | define('REFERER', -1); |
||
2016 | |||
2017 | /** |
||
2018 | * HTTP Response codes |
||
2019 | */ |
||
2020 | define('ELGG_HTTP_CONTINUE', 100); |
||
2021 | define('ELGG_HTTP_SWITCHING_PROTOCOLS', 101); |
||
2022 | define('ELGG_HTTP_PROCESSING', 102);// RFC2518 |
||
2023 | define('ELGG_HTTP_OK', 200); |
||
2024 | define('ELGG_HTTP_CREATED', 201); |
||
2025 | define('ELGG_HTTP_ACCEPTED', 202); |
||
2026 | define('ELGG_HTTP_NON_AUTHORITATIVE_INFORMATION', 203); |
||
2027 | define('ELGG_HTTP_NO_CONTENT', 204); |
||
2028 | define('ELGG_HTTP_RESET_CONTENT', 205); |
||
2029 | define('ELGG_HTTP_PARTIAL_CONTENT', 206); |
||
2030 | define('ELGG_HTTP_MULTI_STATUS', 207); // RFC4918 |
||
2031 | define('ELGG_HTTP_ALREADY_REPORTED', 208); // RFC5842 |
||
2032 | define('ELGG_HTTP_IM_USED', 226); // RFC3229 |
||
2033 | define('ELGG_HTTP_MULTIPLE_CHOICES', 300); |
||
2034 | define('ELGG_HTTP_MOVED_PERMANENTLY', 301); |
||
2035 | define('ELGG_HTTP_FOUND', 302); |
||
2036 | define('ELGG_HTTP_SEE_OTHER', 303); |
||
2037 | define('ELGG_HTTP_NOT_MODIFIED', 304); |
||
2038 | define('ELGG_HTTP_USE_PROXY', 305); |
||
2039 | define('ELGG_HTTP_RESERVED', 306); |
||
2040 | define('ELGG_HTTP_TEMPORARY_REDIRECT', 307); |
||
2041 | define('ELGG_HTTP_PERMANENTLY_REDIRECT', 308); // RFC7238 |
||
2042 | define('ELGG_HTTP_BAD_REQUEST', 400); |
||
2043 | define('ELGG_HTTP_UNAUTHORIZED', 401); |
||
2044 | define('ELGG_HTTP_PAYMENT_REQUIRED', 402); |
||
2045 | define('ELGG_HTTP_FORBIDDEN', 403); |
||
2046 | define('ELGG_HTTP_NOT_FOUND', 404); |
||
2047 | define('ELGG_HTTP_METHOD_NOT_ALLOWED', 405); |
||
2048 | define('ELGG_HTTP_NOT_ACCEPTABLE', 406); |
||
2049 | define('ELGG_HTTP_PROXY_AUTHENTICATION_REQUIRED', 407); |
||
2050 | define('ELGG_HTTP_REQUEST_TIMEOUT', 408); |
||
2051 | define('ELGG_HTTP_CONFLICT', 409); |
||
2052 | define('ELGG_HTTP_GONE', 410); |
||
2053 | define('ELGG_HTTP_LENGTH_REQUIRED', 411); |
||
2054 | define('ELGG_HTTP_PRECONDITION_FAILED', 412); |
||
2055 | define('ELGG_HTTP_REQUEST_ENTITY_TOO_LARGE', 413); |
||
2056 | define('ELGG_HTTP_REQUEST_URI_TOO_LONG', 414); |
||
2057 | define('ELGG_HTTP_UNSUPPORTED_MEDIA_TYPE', 415); |
||
2058 | define('ELGG_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE', 416); |
||
2059 | define('ELGG_HTTP_EXPECTATION_FAILED', 417); |
||
2060 | define('ELGG_HTTP_I_AM_A_TEAPOT', 418); // RFC2324 |
||
2061 | define('ELGG_HTTP_UNPROCESSABLE_ENTITY', 422);// RFC4918 |
||
2062 | define('ELGG_HTTP_LOCKED', 423); // RFC4918 |
||
2063 | define('ELGG_HTTP_FAILED_DEPENDENCY', 424); // RFC4918 |
||
2064 | define('ELGG_HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL', 425); // RFC2817 |
||
2065 | define('ELGG_HTTP_UPGRADE_REQUIRED', 426);// RFC2817 |
||
2066 | define('ELGG_HTTP_PRECONDITION_REQUIRED', 428); // RFC6585 |
||
2067 | define('ELGG_HTTP_TOO_MANY_REQUESTS', 429); // RFC6585 |
||
2068 | define('ELGG_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE', 431); // RFC6585 |
||
2069 | define('ELGG_HTTP_INTERNAL_SERVER_ERROR', 500); |
||
2070 | define('ELGG_HTTP_NOT_IMPLEMENTED', 501); |
||
2071 | define('ELGG_HTTP_BAD_GATEWAY', 502); |
||
2072 | define('ELGG_HTTP_SERVICE_UNAVAILABLE', 503); |
||
2073 | define('ELGG_HTTP_GATEWAY_TIMEOUT', 504); |
||
2074 | define('ELGG_HTTP_VERSION_NOT_SUPPORTED', 505); |
||
2075 | define('ELGG_HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL', 506);// RFC2295 |
||
2076 | define('ELGG_HTTP_INSUFFICIENT_STORAGE', 507);// RFC4918 |
||
2077 | define('ELGG_HTTP_LOOP_DETECTED', 508); // RFC5842 |
||
2078 | define('ELGG_HTTP_NOT_EXTENDED', 510);// RFC2774 |
||
2079 | define('ELGG_HTTP_NETWORK_AUTHENTICATION_REQUIRED', 511); // RFC6585 |
||
2080 | |||
2081 | /** |
||
2082 | * Default JSON encoding |
||
2083 | */ |
||
2084 | define('ELGG_JSON_ENCODING', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); |
||
2085 | |||
2086 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
||
2087 | $events->registerHandler('cache:flush', 'system', function () { |
||
2088 | _elgg_services()->boot->invalidateCache(); |
||
2089 | }); |
||
2090 | |||
2091 | $events->registerHandler('init', 'system', '_elgg_init'); |
||
2092 | $events->registerHandler('init', 'system', '_elgg_walled_garden_init', 1000); |
||
2093 | |||
2094 | $hooks->registerHandler('unit_test', 'system', '_elgg_api_test'); |
||
2095 | }; |
||
2096 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.