Passed
Push — 3.x ( 959eab...e2655b )
by Jeroen
56:13 queued 10s
created

_elgg_init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2.0054

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 20
ccs 8
cts 9
cp 0.8889
crap 2.0054
rs 9.9666
1
<?php
2
3
use Elgg\Http\ResponseBuilder;
4
5
/**
6
 * Bootstrapping and helper procedural code available for use in Elgg core and plugins.
7
 *
8
 * @package Elgg.Core
9
 * @todo These functions can't be subpackaged because they cover a wide mix of
10
 * purposes and subsystems.  Many of them should be moved to more relevant files.
11
 */
12
13
/**
14
 * Get a reference to the global Application object
15
 *
16
 * @return \Elgg\Di\PublicContainer
17
 * @since 2.0.0
18
 */
19
function elgg() {
20 6080
	return _elgg_services()->dic;
21
}
22
23
/**
24
 * Forward to $location.
25
 *
26
 * Sends a 'Location: $location' header and exits.  If headers have already been sent, throws an exception.
27
 *
28
 * @param string $location URL to forward to browser to. This can be a path
29
 *                         relative to the network's URL.
30
 * @param string $reason   Short explanation for why we're forwarding. Set to
31
 *                         '404' to forward to error page. Default message is
32
 *                         'system'.
33
 *
34
 * @return void
35
 * @throws SecurityException|InvalidParameterException
36
 */
37
function forward($location = "", $reason = 'system') {
38
	if (headers_sent($file, $line)) {
39
		throw new \SecurityException("Redirect could not be issued due to headers already being sent. Halting execution for security. "
40
			. "Output started in file $file at line $line. Search http://learn.elgg.org/ for more information.");
41
	}
42
43
	_elgg_services()->responseFactory->redirect($location, $reason);
44
	exit;
45
}
46
47
/**
48
 * Set a response HTTP header
49
 *
50
 * @see header()
51
 *
52
 * @param string $header  Header
53
 * @param bool   $replace Replace existing header
54
 * @return void
55
 * @since 2.3
56
 */
57
function elgg_set_http_header($header, $replace = true) {
58 35
	if (!preg_match('~^HTTP/\\d\\.\\d~', $header)) {
59 35
		list($name, $value) = explode(':', $header, 2);
60 35
		_elgg_services()->responseFactory->setHeader($name, ltrim($value), $replace);
61
	}
62 35
}
63
64
/**
65
 * Defines a JS lib as an AMD module. This is useful for shimming
66
 * traditional JS or for setting the paths of AMD modules.
67
 *
68
 * Calling multiple times for the same name will:
69
 *     * set the preferred path to the last call setting a path
70
 *     * overwrite the shimmed AMD modules with the last call setting a shimmed module
71
 *
72
 * Use elgg_require_js($name) to load on the current page.
73
 *
74
 * Calling this function is not needed if your JS are in views named like `module/name.js`
75
 * Instead, simply call elgg_require_js("module/name").
76
 *
77
 * @note The configuration is cached in simplecache, so logic should not depend on user-
78
 *       specific values like get_language().
79
 *
80
 * @param string $name   The module name
81
 * @param array  $config An array like the following:
82
 *                       array  'deps'    An array of AMD module dependencies
83
 *                       string 'exports' The name of the exported module
84
 *                       string 'src'     The URL to the JS. Can be relative.
85
 *
86
 * @return void
87
 */
88
function elgg_define_js($name, $config) {
89 81
	$src = elgg_extract('src', $config);
90
91 81
	if ($src) {
92 81
		$url = elgg_normalize_url($src);
93 81
		_elgg_services()->amdConfig->addPath($name, $url);
94
	}
95
96
	// shimmed module
97 81
	if (isset($config['deps']) || isset($config['exports'])) {
98 68
		_elgg_services()->amdConfig->addShim($name, $config);
99
	}
100 81
}
101
102
/**
103
 * Request that Elgg load an AMD module onto the page.
104
 *
105
 * @param string $name The AMD module name.
106
 * @return void
107
 * @since 1.9.0
108
 */
109
function elgg_require_js($name) {
110 16
	_elgg_services()->amdConfig->addDependency($name);
111 16
}
112
113
/**
114
 * Cancel a request to load an AMD module onto the page.
115
 *
116
 * @note The elgg, jquery, and jquery-ui modules cannot be cancelled.
117
 *
118
 * @param string $name The AMD module name.
119
 * @return void
120
 * @since 2.1.0
121
 */
122
function elgg_unrequire_js($name) {
123
	_elgg_services()->amdConfig->removeDependency($name);
124
}
125
126
/**
127
 * Get the JavaScript URLs that are loaded
128
 *
129
 * @param string $location 'head' or 'footer'
130
 *
131
 * @return array
132
 * @since 1.8.0
133
 */
134
function elgg_get_loaded_js($location = 'head') {
135 8
	return elgg_get_loaded_external_files('js', $location);
136
}
137
138
/**
139
 * Register a CSS view name to be included in the HTML head
140
 *
141
 * @param string $view The css view name
142
 *
143
 * @return void
144
 *
145
 * @since 3.1
146
 */
147
function elgg_require_css(string $view) {
148 68
	$view_name = "{$view}.css";
149 68
	if (!elgg_view_exists($view_name)) {
150 5
		$view_name = $view;
151
	}
152
	
153 68
	elgg_register_external_file('css', $view, elgg_get_simplecache_url($view_name));
154 68
	elgg_load_external_file('css', $view);
155 68
}
156
157
/**
158
 * Unregister a CSS view name to be included in the HTML head
159
 *
160
 * @param string $view The css view name
161
 *
162
 * @return void
163
 *
164
 * @since 3.1
165
 */
166
function elgg_unrequire_css(string $view) {
167
	elgg_unregister_css($view);
0 ignored issues
show
Deprecated Code introduced by
The function elgg_unregister_css() has been deprecated: 3.1 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

167
	/** @scrutinizer ignore-deprecated */ elgg_unregister_css($view);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
168
}
169
170
/**
171
 * Get the loaded CSS URLs
172
 *
173
 * @return array
174
 * @since 1.8.0
175
 */
176
function elgg_get_loaded_css() {
177 6
	return elgg_get_loaded_external_files('css', 'head');
178
}
179
180
/**
181
 * Core registration function for external files
182
 *
183
 * @param string $type     Type of external resource (js or css)
184
 * @param string $name     Identifier used as key
185
 * @param string $url      URL
186
 * @param string $location Location in the page to include the file (default = 'head')
187
 * @param int    $priority Loading priority of the file
188
 *
189
 * @return bool
190
 * @since 1.8.0
191
 */
192
function elgg_register_external_file($type, $name, $url, $location = 'head', $priority = 500) {
193 86
	return _elgg_services()->externalFiles->register($type, $name, $url, $location, $priority);
194
}
195
196
/**
197
 * Unregister an external file
198
 *
199
 * @param string $type Type of file: js or css
200
 * @param string $name The identifier of the file
201
 *
202
 * @return bool
203
 * @since 1.8.0
204
 */
205
function elgg_unregister_external_file($type, $name) {
206 3
	return _elgg_services()->externalFiles->unregister($type, $name);
207
}
208
209
/**
210
 * Load an external resource for use on this page
211
 *
212
 * @param string $type Type of file: js or css
213
 * @param string $name The identifier for the file
214
 *
215
 * @return void
216
 * @since 1.8.0
217
 */
218
function elgg_load_external_file($type, $name) {
219 80
	_elgg_services()->externalFiles->load($type, $name);
220 80
}
221
222
/**
223
 * Get external resource descriptors
224
 *
225
 * @param string $type     Type of file: js or css
226
 * @param string $location Page location
227
 *
228
 * @return array
229
 * @since 1.8.0
230
 */
231
function elgg_get_loaded_external_files($type, $location) {
232 10
	return _elgg_services()->externalFiles->getLoadedFiles($type, $location);
233
}
234
235
/**
236
 * Returns a list of files in $directory.
237
 *
238
 * Only returns files.  Does not recurse into subdirs.
239
 *
240
 * @param string $directory  Directory to look in
241
 * @param array  $exceptions Array of filenames to ignore
242
 * @param array  $list       Array of files to append to
243
 * @param mixed  $extensions Array of extensions to allow, null for all. Use a dot: array('.php').
244
 *
245
 * @return array Filenames in $directory, in the form $directory/filename.
246
 */
247
function elgg_get_file_list($directory, $exceptions = [], $list = [], $extensions = null) {
248
249
	$directory = \Elgg\Project\Paths::sanitize($directory);
250
	if ($handle = opendir($directory)) {
251
		while (($file = readdir($handle)) !== false) {
252
			if (!is_file($directory . $file) || in_array($file, $exceptions)) {
253
				continue;
254
			}
255
256
			if (is_array($extensions)) {
257
				if (in_array(strrchr($file, '.'), $extensions)) {
258
					$list[] = $directory . $file;
259
				}
260
			} else {
261
				$list[] = $directory . $file;
262
			}
263
		}
264
		closedir($handle);
265
	}
266
267
	return $list;
268
}
269
270
/**
271
 * Counts the number of messages, either globally or in a particular register
272
 *
273
 * @param string $register Optionally, the register
274
 *
275
 * @return integer The number of messages
276
 */
277
function count_messages($register = "") {
278
	return elgg()->system_messages->count($register);
279
}
280
281
/**
282
 * Display a system message on next page load.
283
 *
284
 * @param string|array $message Message or messages to add
285
 *
286
 * @return bool
287
 */
288
function system_message($message) {
289 32
	elgg()->system_messages->addSuccessMessage($message);
290 32
	return true;
291
}
292
293
/**
294
 * Display an error on next page load.
295
 *
296
 * @param string|array $error Error or errors to add
297
 *
298
 * @return bool
299
 */
300
function register_error($error) {
301 63
	elgg()->system_messages->addErrorMessage($error);
302 63
	return true;
303
}
304
305
/**
306
 * Get a copy of the current system messages.
307
 *
308
 * @return \Elgg\SystemMessages\RegisterSet
309
 * @since 2.1
310
 */
311
function elgg_get_system_messages() {
312
	return elgg()->system_messages->loadRegisters();
313
}
314
315
/**
316
 * Set the system messages. This will overwrite the state of all messages and errors!
317
 *
318
 * @param \Elgg\SystemMessages\RegisterSet $set Set of messages
319
 * @return void
320
 * @since 2.1
321
 */
322
function elgg_set_system_messages(\Elgg\SystemMessages\RegisterSet $set) {
323
	elgg()->system_messages->saveRegisters($set);
324
}
325
326
/**
327
 * Register a callback as an Elgg event handler.
328
 *
329
 * Events are emitted by Elgg when certain actions occur.  Plugins
330
 * can respond to these events or halt them completely by registering a handler
331
 * as a callback to an event.  Multiple handlers can be registered for
332
 * the same event and will be executed in order of $priority.
333
 *
334
 * For most events, any handler returning false will halt the execution chain and
335
 * cause the event to be "cancelled". For After Events, the return values of the
336
 * handlers will be ignored and all handlers will be called.
337
 *
338
 * This function is called with the event name, event type, and handler callback name.
339
 * Setting the optional $priority allows plugin authors to specify when the
340
 * callback should be run.  Priorities for plugins should be 1-1000.
341
 *
342
 * The callback is passed 3 arguments when called: $event, $type, and optional $params.
343
 *
344
 * $event is the name of event being emitted.
345
 * $type is the type of event or object concerned.
346
 * $params is an optional parameter passed that can include a related object.  See
347
 * specific event documentation for details on which events pass what parameteres.
348
 *
349
 * @tip If a priority isn't specified it is determined by the order the handler was
350
 * registered relative to the event and type.  For plugins, this generally means
351
 * the earlier the plugin is in the load order, the earlier the priorities are for
352
 * any event handlers.
353
 *
354
 * @tip $event and $object_type can use the special keyword 'all'.  Handler callbacks registered
355
 * with $event = all will be called for all events of type $object_type.  Similarly,
356
 * callbacks registered with $object_type = all will be called for all events of type
357
 * $event, regardless of $object_type.  If $event and $object_type both are 'all', the
358
 * handler callback will be called for all events.
359
 *
360
 * @tip Event handler callbacks are considered in the follow order:
361
 *  - Specific registration where 'all' isn't used.
362
 *  - Registration where 'all' is used for $event only.
363
 *  - Registration where 'all' is used for $type only.
364
 *  - Registration where 'all' is used for both.
365
 *
366
 * @warning If you use the 'all' keyword, you must have logic in the handler callback to
367
 * test the passed parameters before taking an action.
368
 *
369
 * @tip When referring to events, the preferred syntax is "event, type".
370
 *
371
 * @param string   $event       The event type
372
 * @param string   $object_type The object type
373
 * @param callable $callback    The handler callback
374
 * @param int      $priority    The priority - 0 is default, negative before, positive after
375
 *
376
 * @return bool
377
 * @example documentation/events/basic.php
378
 * @example documentation/events/advanced.php
379
 * @example documentation/events/all.php
380
 */
381
function elgg_register_event_handler($event, $object_type, $callback, $priority = 500) {
382 337
	return _elgg_services()->events->registerHandler($event, $object_type, $callback, $priority);
383
}
384
385
/**
386
 * Unregisters a callback for an event.
387
 *
388
 * @param string   $event       The event type
389
 * @param string   $object_type The object type
390
 * @param callable $callback    The callback. Since 1.11, static method callbacks will match dynamic methods
391
 *
392
 * @return bool true if a handler was found and removed
393
 * @since 1.7
394
 */
395
function elgg_unregister_event_handler($event, $object_type, $callback) {
396 16
	return _elgg_services()->events->unregisterHandler($event, $object_type, $callback);
397
}
398
399
/**
400
 * Clears all callback registrations for a event.
401
 *
402
 * @param string $event       The name of the event
403
 * @param string $object_type The objecttype of the event
404
 *
405
 * @return void
406
 * @since 2.3
407
 */
408
function elgg_clear_event_handlers($event, $object_type) {
409
	_elgg_services()->events->clearHandlers($event, $object_type);
410
}
411
412
/**
413
 * Trigger an Elgg Event and attempt to run all handler callbacks registered to that
414
 * event, type.
415
 *
416
 * This function attempts to run all handlers registered to $event, $object_type or
417
 * the special keyword 'all' for either or both. If a handler returns false, the
418
 * event will be cancelled (no further handlers will be called, and this function
419
 * will return false).
420
 *
421
 * $event is usually a verb: create, update, delete, annotation.
422
 *
423
 * $object_type is usually a noun: object, group, user, annotation, relationship, metadata.
424
 *
425
 * $object is usually an Elgg* object associated with the event.
426
 *
427
 * @warning Elgg events should only be triggered by core.  Plugin authors should use
428
 * {@link trigger_elgg_plugin_hook()} instead.
429
 *
430
 * @tip When referring to events, the preferred syntax is "event, type".
431
 *
432
 * @note Internal: Only rarely should events be changed, added, or removed in core.
433
 * When making changes to events, be sure to first create a ticket on Github.
434
 *
435
 * @note Internal: @tip Think of $object_type as the primary namespace element, and
436
 * $event as the secondary namespace.
437
 *
438
 * @param string $event       The event type
439
 * @param string $object_type The object type
440
 * @param mixed  $object      The object involved in the event
441
 *
442
 * @return bool False if any handler returned false, otherwise true.
443
 * @example documentation/examples/events/trigger.php
444
 */
445
function elgg_trigger_event($event, $object_type, $object = null) {
446 64
	return elgg()->events->trigger($event, $object_type, $object);
447
}
448
449
/**
450
 * Trigger a "Before event" indicating a process is about to begin.
451
 *
452
 * Like regular events, a handler returning false will cancel the process and false
453
 * will be returned.
454
 *
455
 * To register for a before event, append ":before" to the event name when registering.
456
 *
457
 * @param string $event       The event type. The fired event type will be appended with ":before".
458
 * @param string $object_type The object type
459
 * @param mixed  $object      The object involved in the event
460
 *
461
 * @return bool False if any handler returned false, otherwise true
462
 *
463
 * @see elgg_trigger_event()
464
 * @see elgg_trigger_after_event()
465
 */
466
function elgg_trigger_before_event($event, $object_type, $object = null) {
467 10
	return elgg()->events->triggerBefore($event, $object_type, $object);
468
}
469
470
/**
471
 * Trigger an "After event" indicating a process has finished.
472
 *
473
 * Unlike regular events, all the handlers will be called, their return values ignored.
474
 *
475
 * To register for an after event, append ":after" to the event name when registering.
476
 *
477
 * @param string $event       The event type. The fired event type will be appended with ":after".
478
 * @param string $object_type The object type
479
 * @param string $object      The object involved in the event
480
 *
481
 * @return true
482
 *
483
 * @see elgg_trigger_before_event()
484
 */
485
function elgg_trigger_after_event($event, $object_type, $object = null) {
486 9
	return elgg()->events->triggerAfter($event, $object_type, $object);
487
}
488
489
/**
490
 * Trigger an event normally, but send a notice about deprecated use if any handlers are registered.
491
 *
492
 * @param string $event       The event type
493
 * @param string $object_type The object type
494
 * @param string $object      The object involved in the event
495
 * @param string $message     The deprecation message
496
 * @param string $version     Human-readable *release* version: 1.9, 1.10, ...
497
 *
498
 * @return bool
499
 *
500
 * @see elgg_trigger_event()
501
 */
502
function elgg_trigger_deprecated_event($event, $object_type, $object = null, $message = null, $version = null) {
503
	return elgg()->events->triggerDeprecated($event, $object_type, $object, $message, $version);
504
}
505
506
/**
507
 * Register a callback as a plugin hook handler.
508
 *
509
 * Plugin hooks allow developers to losely couple plugins and features by
510
 * responding to and emitting {@link elgg_trigger_plugin_hook()} customizable hooks.
511
 * Handler callbacks can respond to the hook, change the details of the hook, or
512
 * ignore it.
513
 *
514
 * Multiple handlers can be registered for a plugin hook, and each callback
515
 * is called in order of priority.  If the return value of a handler is not
516
 * null, that value is passed to the next callback in the call stack.  When all
517
 * callbacks have been run, the final value is passed back to the caller
518
 * via {@link elgg_trigger_plugin_hook()}.
519
 *
520
 * Similar to Elgg Events, plugin hook handler callbacks are registered by passing
521
 * a hook, a type, and a priority.
522
 *
523
 * The callback is passed 4 arguments when called: $hook, $type, $value, and $params.
524
 *
525
 *  - str $hook The name of the hook.
526
 *  - str $type The type of hook.
527
 *  - mixed $value The return value of the last handler or the default
528
 *  value if no other handlers have been called.
529
 *  - mixed $params An optional array of parameters.  Used to provide additional
530
 *  information to plugins.
531
 *
532
 * @tip Plugin hooks are similar to Elgg Events in that Elgg emits
533
 * a plugin hook when certain actions occur, but a plugin hook allows you to alter the
534
 * parameters, as well as halt execution.
535
 *
536
 * @tip If a priority isn't specified it is determined by the order the handler was
537
 * registered relative to the event and type.  For plugins, this generally means
538
 * the earlier the plugin is in the load order, the earlier the priorities are for
539
 * any event handlers.
540
 *
541
 * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
542
 * Handler callbacks registered with $hook = all will be called for all hooks
543
 * of type $type.  Similarly, handlers registered with $type = all will be
544
 * called for all hooks of type $event, regardless of $object_type.  If $hook
545
 * and $type both are 'all', the handler will be called for all hooks.
546
 *
547
 * @tip Plugin hooks are sometimes used to gather lists from plugins.  This is
548
 * usually done by pushing elements into an array passed in $params.  Be sure
549
 * to append to and then return $value so you don't overwrite other plugin's
550
 * values.
551
 *
552
 * @warning Unlike Elgg Events, a handler that returns false will NOT halt the
553
 * execution chain.
554
 *
555
 * @param string   $hook     The name of the hook
556
 * @param string   $type     The type of the hook
557
 * @param callable $callback The name of a valid function or an array with object and method
558
 * @param int      $priority The priority - 500 is default, lower numbers called first
559
 *
560
 * @return bool
561
 *
562
 * @example hooks/register/basic.php Registering for a plugin hook and examining the variables.
563
 * @example hooks/register/advanced.php Registering for a plugin hook and changing the params.
564
 * @since 1.8.0
565
 */
566
function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) {
567 480
	return elgg()->hooks->registerHandler($hook, $type, $callback, $priority);
568
}
569
570
/**
571
 * Unregister a callback as a plugin hook.
572
 *
573
 * @param string   $hook        The name of the hook
574
 * @param string   $entity_type The name of the type of entity (eg "user", "object" etc)
575
 * @param callable $callback    The PHP callback to be removed. Since 1.11, static method
576
 *                              callbacks will match dynamic methods
577
 *
578
 * @return void
579
 * @since 1.8.0
580
 */
581
function elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback) {
582 286
	elgg()->hooks->unregisterHandler($hook, $entity_type, $callback);
583 286
}
584
585
/**
586
 * Clears all callback registrations for a plugin hook.
587
 *
588
 * @param string $hook The name of the hook
589
 * @param string $type The type of the hook
590
 *
591
 * @return void
592
 * @since 2.0
593
 */
594
function elgg_clear_plugin_hook_handlers($hook, $type) {
595
	elgg()->hooks->clearHandlers($hook, $type);
596
}
597
598
/**
599
 * Trigger a Plugin Hook and run all handler callbacks registered to that hook:type.
600
 *
601
 * This function runs all handlers registered to $hook, $type or
602
 * the special keyword 'all' for either or both.
603
 *
604
 * Use $params to send additional information to the handler callbacks.
605
 *
606
 * $returnvalue is the initial value to pass to the handlers, which can
607
 * change it by returning non-null values. It is useful to use $returnvalue
608
 * to set defaults. If no handlers are registered, $returnvalue is immediately
609
 * returned.
610
 *
611
 * Handlers that return null (or with no explicit return or return value) will
612
 * not change the value of $returnvalue.
613
 *
614
 * $hook is usually a verb: import, get_views, output.
615
 *
616
 * $type is usually a noun: user, ecml, page.
617
 *
618
 * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
619
 * Handler callbacks registered with $hook = all will be called for all hooks
620
 * of type $type.  Similarly, handlers registered with $type = all will be
621
 * called for all hooks of type $event, regardless of $object_type.  If $hook
622
 * and $type both are 'all', the handler will be called for all hooks.
623
 *
624
 * @tip It's not possible for a plugin hook to change a non-null $returnvalue
625
 * to null.
626
 *
627
 * @note Internal: The checks for $hook and/or $type not being equal to 'all' is to
628
 * prevent a plugin hook being registered with an 'all' being called more than
629
 * once if the trigger occurs with an 'all'. An example in core of this is in
630
 * actions.php:
631
 * elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', ...)
632
 *
633
 * @see elgg_register_plugin_hook_handler()
634
 *
635
 * @param string $hook        The name of the hook to trigger ("all" will
636
 *                            trigger for all $types regardless of $hook value)
637
 * @param string $type        The type of the hook to trigger ("all" will
638
 *                            trigger for all $hooks regardless of $type value)
639
 * @param mixed  $params      Additional parameters to pass to the handlers
640
 * @param mixed  $returnvalue An initial return value
641
 *
642
 * @return mixed|null The return value of the last handler callback called
643
 *
644
 * @example hooks/trigger/basic.php    Trigger a hook that determines if execution
645
 *                                     should continue.
646
 * @example hooks/trigger/advanced.php Trigger a hook with a default value and use
647
 *                                     the results to populate a menu.
648
 * @example hooks/basic.php            Trigger and respond to a basic plugin hook.
649
 *
650
 * @since 1.8.0
651
 */
652
function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) {
653 5128
	return elgg()->hooks->trigger($hook, $type, $params, $returnvalue);
654
}
655
656
/**
657
 * Trigger an plugin hook normally, but send a notice about deprecated use if any handlers are registered.
658
 *
659
 * @param string $hook        The name of the plugin hook
660
 * @param string $type        The type of the plugin hook
661
 * @param mixed  $params      Supplied params for the hook
662
 * @param mixed  $returnvalue The value of the hook, this can be altered by registered callbacks
663
 * @param string $message     The deprecation message
664
 * @param string $version     Human-readable *release* version: 1.9, 1.10, ...
665
 *
666
 * @return mixed
667
 *
668
 * @see elgg_trigger_plugin_hook()
669
 * @since 3.0
670
 */
671
function elgg_trigger_deprecated_plugin_hook($hook, $type, $params = null, $returnvalue = null, $message = null, $version = null) {
672
	return elgg()->hooks->triggerDeprecated($hook, $type, $params, $returnvalue, $message, $version);
673
}
674
675
/**
676
 * Returns an ordered array of hook handlers registered for $hook and $type.
677
 *
678
 * @param string $hook Hook name
679
 * @param string $type Hook type
680
 *
681
 * @return array
682
 *
683
 * @since 2.0.0
684
 */
685
function elgg_get_ordered_hook_handlers($hook, $type) {
686
	return elgg()->hooks->getOrderedHandlers($hook, $type);
687
}
688
689
/**
690
 * Returns an ordered array of event handlers registered for $event and $type.
691
 *
692
 * @param string $event Event name
693
 * @param string $type  Object type
694
 *
695
 * @return array
696
 *
697
 * @since 2.0.0
698
 */
699
function elgg_get_ordered_event_handlers($event, $type) {
700
	return elgg()->events->getOrderedHandlers($event, $type);
701
}
702
703
/**
704
 * Log a message.
705
 *
706
 * If $level is >= to the debug setting in {@link $CONFIG->debug}, the
707
 * message will be sent to {@link elgg_dump()}.  Messages with lower
708
 * priority than {@link $CONFIG->debug} are ignored.
709
 *
710
 * @note Use the developers plugin to display logs
711
 *
712
 * @param string $message User message
713
 * @param string $level   NOTICE | WARNING | ERROR
714
 *
715
 * @return bool
716
 * @since 1.7.0
717
 */
718
function elgg_log($message, $level = \Psr\Log\LogLevel::NOTICE) {
719 1362
	return _elgg_services()->logger->log($level, $message);
720
}
721
722
/**
723
 * Logs $value to PHP's {@link error_log()}
724
 *
725
 * A {@elgg_plugin_hook debug log} is called.  If a handler returns
726
 * false, it will stop the default logging method.
727
 *
728
 * @note Use the developers plugin to display logs
729
 *
730
 * @param mixed $value The value
731
 * @return void
732
 * @since 1.7.0
733
 */
734
function elgg_dump($value) {
735
	_elgg_services()->logger->dump($value);
736
}
737
738
/**
739
 * Get the current Elgg version information
740
 *
741
 * @param bool $human_readable Whether to return a human readable version (default: false)
742
 *
743
 * @return string|false Depending on success
744
 * @since 1.9
745
 */
746
function elgg_get_version($human_readable = false) {
747 89
	static $version, $release;
748
	
749 89
	if (!isset($version) || !isset($release)) {
750
		$path = \Elgg\Application::elggDir()->getPath('version.php');
751
		if (!is_file($path)) {
752
			return false;
753
		}
754
		include $path;
755
	}
756
	
757 89
	return $human_readable ? $release : $version;
758
}
759
760
/**
761
 * Log a notice about deprecated use of a function, view, etc.
762
 *
763
 * @param string $msg             Message to log
764
 * @param string $dep_version     Human-readable *release* version: 1.7, 1.8, ...
765
 * @param int    $backtrace_level How many levels back to display the backtrace.
766
 *                                Useful if calling from functions that are called
767
 *                                from other places (like elgg_view()). Set to -1
768
 *                                for a full backtrace.
769
 *
770
 * @return bool
771
 * @since 1.7.0
772
 */
773
function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) {
774 399
	$backtrace_level += 1;
775 399
	return _elgg_services()->deprecation->sendNotice($msg, $dep_version, $backtrace_level);
776
}
777
778
/**
779
 * Builds a URL from the a parts array like one returned by {@link parse_url()}.
780
 *
781
 * @note If only partial information is passed, a partial URL will be returned.
782
 *
783
 * @param array $parts       Associative array of URL components like parse_url() returns
784
 *                           'user' and 'pass' parts are ignored because of security reasons
785
 * @param bool  $html_encode HTML Encode the url?
786
 *
787
 * @see https://github.com/Elgg/Elgg/pull/8146#issuecomment-91544585
788
 * @return string Full URL
789
 * @since 1.7.0
790
 */
791
function elgg_http_build_url(array $parts, $html_encode = true) {
792
	// build only what's given to us.
793 141
	$scheme = isset($parts['scheme']) ? "{$parts['scheme']}://" : '';
794 141
	$host = isset($parts['host']) ? "{$parts['host']}" : '';
795 141
	$port = isset($parts['port']) ? ":{$parts['port']}" : '';
796 141
	$path = isset($parts['path']) ? "{$parts['path']}" : '';
797 141
	$query = isset($parts['query']) ? "?{$parts['query']}" : '';
798 141
	$fragment = isset($parts['fragment']) ? "#{$parts['fragment']}" : '';
799
800 141
	$string = $scheme . $host . $port . $path . $query . $fragment;
801
802 141
	if ($html_encode) {
803 21
		return htmlspecialchars($string, ENT_QUOTES, 'UTF-8', false);
804
	} else {
805 120
		return $string;
806
	}
807
}
808
809
/**
810
 * Adds action tokens to URL
811
 *
812
 * As of 1.7.0 action tokens are required on all actions.
813
 * Use this function to append action tokens to a URL's GET parameters.
814
 * This will preserve any existing GET parameters.
815
 *
816
 * @note If you are using {@elgg_view input/form} you don't need to
817
 * add tokens to the action.  The form view automatically handles
818
 * tokens.
819
 *
820
 * @param string $url         Full action URL
821
 * @param bool   $html_encode HTML encode the url? (default: false)
822
 *
823
 * @return string URL with action tokens
824
 * @since 1.7.0
825
 */
826
function elgg_add_action_tokens_to_url($url, $html_encode = false) {
827 5
	$url = elgg_normalize_url($url);
828 5
	$components = parse_url($url);
829
830 5
	if (isset($components['query'])) {
831 3
		$query = elgg_parse_str($components['query']);
832
	} else {
833 2
		$query = [];
834
	}
835
836 5
	if (isset($query['__elgg_ts']) && isset($query['__elgg_token'])) {
837
		return $url;
838
	}
839
840
	// append action tokens to the existing query
841 5
	$query['__elgg_ts'] = time();
842 5
	$query['__elgg_token'] = generate_action_token($query['__elgg_ts']);
843 5
	$components['query'] = http_build_query($query);
844
845
	// rebuild the full url
846 5
	return elgg_http_build_url($components, $html_encode);
847
}
848
849
/**
850
 * Removes an element from a URL's query string.
851
 *
852
 * @note You can send a partial URL string.
853
 *
854
 * @param string $url     Full URL
855
 * @param string $element The element to remove
856
 *
857
 * @return string The new URL with the query element removed.
858
 * @since 1.7.0
859
 */
860
function elgg_http_remove_url_query_element($url, $element) {
861 26
	return elgg_http_add_url_query_elements($url, [$element => null]);
862
}
863
864
/**
865
 * Sets elements in a URL's query string.
866
 *
867
 * @param string $url      The URL
868
 * @param array  $elements Key/value pairs to set in the URL. If the value is null, the
869
 *                         element is removed from the URL.
870
 *
871
 * @return string The new URL with the query strings added
872
 * @since 1.7.0
873
 */
874
function elgg_http_add_url_query_elements($url, array $elements) {
875 120
	$url_array = parse_url($url);
876
877 120
	if (isset($url_array['query'])) {
878 35
		$query = elgg_parse_str($url_array['query']);
879
	} else {
880 85
		$query = [];
881
	}
882
883 120
	foreach ($elements as $k => $v) {
884 72
		if ($v === null) {
885 32
			unset($query[$k]);
886
		} else {
887 72
			$query[$k] = $v;
888
		}
889
	}
890
891
	// why check path? A: if no path, this may be a relative URL like "?foo=1". In this case,
892
	// the output "" would be interpreted the current URL, so in this case we *must* set
893
	// a query to make sure elements are removed.
894 120
	if ($query || empty($url_array['path'])) {
895 67
		$url_array['query'] = http_build_query($query);
896
	} else {
897 57
		unset($url_array['query']);
898
	}
899 120
	$string = elgg_http_build_url($url_array, false);
900
901
	// Restore relative protocol to url if missing and is provided as part of the initial url (see #9874)
902 120
	if (!isset($url['scheme']) && (substr($url, 0, 2) == '//')) {
903 3
		$string = "//{$string}";
904
	}
905
	
906 120
	return $string;
907
}
908
909
/**
910
 * Test if two URLs are functionally identical.
911
 *
912
 * @tip If $ignore_params is used, neither the name nor its value will be considered when comparing.
913
 *
914
 * @tip The order of GET params doesn't matter.
915
 *
916
 * @param string $url1          First URL
917
 * @param string $url2          Second URL
918
 * @param array  $ignore_params GET params to ignore in the comparison
919
 *
920
 * @return bool
921
 * @since 1.8.0
922
 */
923
function elgg_http_url_is_identical($url1, $url2, $ignore_params = ['offset', 'limit']) {
924 67
	if (!is_string($url1) || !is_string($url2)) {
925 6
		return false;
926
	}
927
	
928 63
	$url1 = elgg_normalize_url($url1);
929 63
	$url2 = elgg_normalize_url($url2);
930
931 63
	if ($url1 == $url2) {
932 27
		return true;
933
	}
934
935 37
	$url1_info = parse_url($url1);
936 37
	$url2_info = parse_url($url2);
937
938 37
	if (isset($url1_info['path'])) {
939 10
		$url1_info['path'] = trim($url1_info['path'], '/');
940
	}
941 37
	if (isset($url2_info['path'])) {
942 33
		$url2_info['path'] = trim($url2_info['path'], '/');
943
	}
944
945
	// compare basic bits
946 37
	$parts = ['scheme', 'host', 'path'];
947
948 37
	foreach ($parts as $part) {
949 37
		if ((isset($url1_info[$part]) && isset($url2_info[$part]))
950 37
		&& $url1_info[$part] != $url2_info[$part]) {
951 9
			return false;
952 36
		} elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) {
953 1
			return false;
954 36
		} elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) {
955 36
			return false;
956
		}
957
	}
958
959
	// quick compare of get params
960 6
	if (isset($url1_info['query']) && isset($url2_info['query'])
961 6
	&& $url1_info['query'] == $url2_info['query']) {
962
		return true;
963
	}
964
965
	// compare get params that might be out of order
966 6
	$url1_params = [];
967 6
	$url2_params = [];
968
969 6
	if (isset($url1_info['query'])) {
970 5
		if ($url1_info['query'] = html_entity_decode($url1_info['query'])) {
971 5
			$url1_params = elgg_parse_str($url1_info['query']);
972
		}
973
	}
974
975 6
	if (isset($url2_info['query'])) {
976 5
		if ($url2_info['query'] = html_entity_decode($url2_info['query'])) {
977 5
			$url2_params = elgg_parse_str($url2_info['query']);
978
		}
979
	}
980
981
	// drop ignored params
982 6
	foreach ($ignore_params as $param) {
983 5
		if (isset($url1_params[$param])) {
984 3
			unset($url1_params[$param]);
985
		}
986 5
		if (isset($url2_params[$param])) {
987 5
			unset($url2_params[$param]);
988
		}
989
	}
990
991
	// array_diff_assoc only returns the items in arr1 that aren't in arrN
992
	// but not the items that ARE in arrN but NOT in arr1
993
	// if arr1 is an empty array, this function will return 0 no matter what.
994
	// since we only care if they're different and not how different,
995
	// add the results together to get a non-zero (ie, different) result
996 6
	$diff_count = count(array_diff_assoc($url1_params, $url2_params));
997 6
	$diff_count += count(array_diff_assoc($url2_params, $url1_params));
998 6
	if ($diff_count > 0) {
999 2
		return false;
1000
	}
1001
1002 4
	return true;
1003
}
1004
1005
/**
1006
 * Signs provided URL with a SHA256 HMAC key
1007
 *
1008
 * @note Signed URLs do not offer CSRF protection and should not be used instead of action tokens.
1009
 *
1010
 * @param string $url     URL to sign
1011
 * @param string $expires Expiration time
1012
 *                        A string suitable for strtotime()
1013
 *                        Falsey values indicate non-expiring URL
1014
 * @return string
1015
 */
1016
function elgg_http_get_signed_url($url, $expires = false) {
1017
	return _elgg_services()->urlSigner->sign($url, $expires);
1018
}
1019
1020
/**
1021
 * Validates if the HMAC signature of the URL is valid
1022
 *
1023
 * @param string $url URL to validate
1024
 * @return bool
1025
 */
1026
function elgg_http_validate_signed_url($url) {
1027
	return _elgg_services()->urlSigner->isValid($url);
1028
}
1029
1030
/**
1031
 * Validates if the HMAC signature of the current request is valid
1032
 * Issues 403 response if signature is invalid
1033
 *
1034
 * @return void
1035
 * @throws \Elgg\HttpException
1036
 */
1037
function elgg_signed_request_gatekeeper() {
1038
1039 2
	if (\Elgg\Application::isCli()) {
1040 2
		return;
1041
	}
1042
1043
	if (!elgg_http_validate_signed_url(current_page_url())) {
1044
		throw new \Elgg\HttpException(elgg_echo('invalid_request_signature'), ELGG_HTTP_FORBIDDEN);
1045
	}
1046
}
1047
1048
/**
1049
 * Checks for $array[$key] and returns its value if it exists, else
1050
 * returns $default.
1051
 *
1052
 * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default';
1053
 *
1054
 * @param string $key     Key to check in the source array
1055
 * @param array  $array   Source array
1056
 * @param mixed  $default Value to return if key is not found
1057
 * @param bool   $strict  Return array key if it's set, even if empty. If false,
1058
 *                        return $default if the array key is unset or empty.
1059
 *
1060
 * @return mixed
1061
 * @since 1.8.0
1062
 */
1063
function elgg_extract($key, $array, $default = null, $strict = true) {
1064 6080
	if (!is_array($array) && !$array instanceof ArrayAccess) {
1065 38
		return $default;
1066
	}
1067
1068 6080
	if ($strict) {
1069 6080
		return (isset($array[$key])) ? $array[$key] : $default;
1070
	} else {
1071 288
		return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default;
1072
	}
1073
}
1074
1075
/**
1076
 * Extract class names from an array, optionally merging into a preexisting set.
1077
 *
1078
 * @param array           $array       Source array
1079
 * @param string|string[] $existing    Existing name(s)
1080
 * @param string          $extract_key Key to extract new classes from
1081
 * @return string[]
1082
 *
1083
 * @since 2.3.0
1084
 */
1085
function elgg_extract_class(array $array, $existing = [], $extract_key = 'class') {
1086 85
	$existing = empty($existing) ? [] : (array) $existing;
1087
1088 85
	$merge = (array) elgg_extract($extract_key, $array, []);
1089
1090 85
	array_splice($existing, count($existing), 0, $merge);
1091
1092 85
	return array_values(array_unique($existing));
1093
}
1094
1095
/**
1096
 * Calls a callable autowiring the arguments using public DI services
1097
 * and applying logic based on flags
1098
 *
1099
 * @param int     $flags   Bitwise flags
1100
 *                         ELGG_IGNORE_ACCESS
1101
 *                         ELGG_ENFORCE_ACCESS
1102
 *                         ELGG_SHOW_DISABLED_ENTITIES
1103
 *                         ELGG_HIDE_DISABLED_ENTITIES
1104
 * @param Closure $closure Callable to call
1105
 *
1106
 * @return mixed
1107
 */
1108
function elgg_call(int $flags, Closure $closure) {
1109 653
	return _elgg_services()->invoker->call($flags, $closure);
1110
}
1111
1112
/**
1113
 * Sorts a 3d array by specific element.
1114
 *
1115
 * @warning Will re-index numeric indexes.
1116
 *
1117
 * @note This operates the same as the built-in sort functions.
1118
 * It sorts the array and returns a bool for success.
1119
 *
1120
 * Do this: elgg_sort_3d_array_by_value($my_array);
1121
 * Not this: $my_array = elgg_sort_3d_array_by_value($my_array);
1122
 *
1123
 * @param array  $array      Array to sort
1124
 * @param string $element    Element to sort by
1125
 * @param int    $sort_order PHP sort order {@link http://us2.php.net/array_multisort}
1126
 * @param int    $sort_type  PHP sort type {@link http://us2.php.net/sort}
1127
 *
1128
 * @return bool
1129
 */
1130
function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC, $sort_type = SORT_LOCALE_STRING) {
1131
1132
	$sort = [];
1133
1134
	foreach ($array as $v) {
1135
		if (isset($v[$element])) {
1136
			$sort[] = strtolower($v[$element]);
1137
		} else {
1138
			$sort[] = null;
1139
		}
1140
	};
1141
1142
	return array_multisort($sort, $sort_order, $sort_type, $array);
1143
}
1144
1145
/**
1146
 * Return the state of a php.ini setting as a bool
1147
 *
1148
 * @warning Using this on ini settings that are not boolean
1149
 * will be inaccurate!
1150
 *
1151
 * @param string $ini_get_arg The INI setting
1152
 *
1153
 * @return bool Depending on whether it's on or off
1154
 */
1155
function ini_get_bool($ini_get_arg) {
1156
	$temp = strtolower(ini_get($ini_get_arg));
1157
1158
	if ($temp == '1' || $temp == 'on' || $temp == 'true') {
1159
		return true;
1160
	}
1161
	return false;
1162
}
1163
1164
/**
1165
 * Returns a PHP INI setting in bytes.
1166
 *
1167
 * @tip Use this for arithmetic when determining if a file can be uploaded.
1168
 *
1169
 * @param string $setting The php.ini setting
1170
 *
1171
 * @return int
1172
 * @since 1.7.0
1173
 * @link http://www.php.net/manual/en/function.ini-get.php
1174
 */
1175
function elgg_get_ini_setting_in_bytes($setting) {
1176
	// retrieve INI setting
1177
	$val = ini_get($setting);
1178
1179
	// convert INI setting when shorthand notation is used
1180
	$last = strtolower($val[strlen($val) - 1]);
1181
	if (in_array($last, ['g', 'm', 'k'])) {
1182
		$val = substr($val, 0, -1);
1183
	}
1184
	$val = (int) $val;
1185
	switch ($last) {
1186
		case 'g':
1187
			$val *= 1024;
1188
			// fallthrough intentional
1189
		case 'm':
1190
			$val *= 1024;
1191
			// fallthrough intentional
1192
		case 'k':
1193
			$val *= 1024;
1194
	}
1195
1196
	// return byte value
1197
	return $val;
1198
}
1199
1200
/**
1201
 * Returns true is string is not empty, false, or null.
1202
 *
1203
 * Function to be used in array_filter which returns true if $string is not null.
1204
 *
1205
 * @param string $string The string to test
1206
 *
1207
 * @return bool
1208
 * @todo This is used once in metadata.php.  Use a lambda function instead.
1209
 */
1210
function is_not_null($string) {
1211 2
	if (($string === '') || ($string === false) || ($string === null)) {
1212
		return false;
1213
	}
1214
1215 2
	return true;
1216
}
1217
1218
/**
1219
 * Get the global service provider
1220
 *
1221
 * @return \Elgg\Di\ServiceProvider
1222
 * @access private
1223
 */
1224
function _elgg_services() {
1225
	// This yields a more shallow stack depth in recursive APIs like views. This aids in debugging and
1226
	// reduces false positives in xdebug's infinite recursion protection.
1227 6081
	return Elgg\Application::$_instance->_services;
1228
}
1229
1230
/**
1231
 * Serve individual views for Ajax.
1232
 *
1233
 * /ajax/view/<view_name>?<key/value params>
1234
 * /ajax/form/<action_name>?<key/value params>
1235
 *
1236
 * @param string[] $segments URL segments (not including "ajax")
1237
 * @return false|ResponseBuilder
1238
 *
1239
 * @see elgg_register_ajax_view()
1240
 * @elgg_pagehandler ajax
1241
 * @access private
1242
 */
1243
function _elgg_ajax_page_handler($segments) {
1244 17
	elgg_ajax_gatekeeper();
1245
1246 17
	if (count($segments) < 2) {
1247
		return elgg_error_response("Ajax pagehandler called with invalid segments", REFERRER, ELGG_HTTP_BAD_REQUEST);
1248
	}
1249
1250 17
	if ($segments[0] === 'view' || $segments[0] === 'form') {
1251 17
		if ($segments[0] === 'view') {
1252 13
			if ($segments[1] === 'admin') {
1253
				// protect admin views similar to all admin pages that are protected automatically in the admin_page_handler
1254
				elgg_admin_gatekeeper();
1255
			}
1256
			// ignore 'view/'
1257 13
			$view = implode('/', array_slice($segments, 1));
1258
		} else {
1259
			// form views start with "forms", not "form"
1260 4
			$view = 'forms/' . implode('/', array_slice($segments, 1));
1261
		}
1262
1263 17
		$ajax_api = _elgg_services()->ajax;
1264 17
		$allowed_views = $ajax_api->getViews();
1265
1266
		// cacheable views are always allowed
1267 17
		if (!in_array($view, $allowed_views) && !_elgg_services()->views->isCacheableView($view)) {
1268 4
			return elgg_error_response("Ajax view '$view' was not registered", REFERRER, ELGG_HTTP_FORBIDDEN);
1269
		}
1270
1271 13
		if (!elgg_view_exists($view)) {
1272
			return elgg_error_response("Ajax view '$view' was not found", REFERRER, ELGG_HTTP_NOT_FOUND);
1273
		}
1274
1275
		// pull out GET parameters through filter
1276 13
		$vars = [];
1277 13
		foreach (_elgg_services()->request->query->keys() as $name) {
1278 7
			$vars[$name] = get_input($name);
1279
		}
1280
1281 13
		if (isset($vars['guid'])) {
1282
			$vars['entity'] = get_entity($vars['guid']);
1283
		}
1284
1285 13
		if (isset($vars['river_id'])) {
1286
			$vars['item'] = elgg_get_river_item_from_id($vars['river_id']);
1287
		}
1288
1289 13
		$content_type = '';
1290 13
		if ($segments[0] === 'view') {
1291 11
			$output = elgg_view($view, $vars);
1292
1293
			// Try to guess the mime-type
1294 11
			switch ($segments[1]) {
1295 11
				case "js":
1296 2
					$content_type = 'text/javascript;charset=utf-8';
1297 2
					break;
1298 9
				case "css":
1299 2
					$content_type = 'text/css;charset=utf-8';
1300 2
					break;
1301
				default :
1302 7
					if (_elgg_services()->views->isCacheableView($view)) {
1303 2
						$file = _elgg_services()->views->findViewFile($view, elgg_get_viewtype());
1304 2
						$content_type = (new \Elgg\Filesystem\MimeTypeDetector())->getType($file, 'text/html');
1305
					}
1306 11
					break;
1307
			}
1308
		} else {
1309 2
			$action = implode('/', array_slice($segments, 1));
1310 2
			$output = elgg_view_form($action, [], $vars);
1311
		}
1312
1313 13
		if ($content_type) {
1314 6
			elgg_set_http_header("Content-Type: $content_type");
1315
		}
1316
1317 13
		return elgg_ok_response($output);
1318
	}
1319
1320
	return false;
1321
}
1322
1323
/**
1324
 * Checks if there are some constraints on the options array for
1325
 * potentially dangerous operations.
1326
 *
1327
 * @param array  $options Options array
1328
 * @param string $type    Options type: metadata, annotation or river
1329
 *
1330
 * @return bool
1331
 * @access private
1332
 */
1333
function _elgg_is_valid_options_for_batch_operation($options, $type) {
1334 323
	if (empty($options) || !is_array($options)) {
1335 1
		return false;
1336
	}
1337
1338
	// at least one of these is required.
1339
	$required = [
1340
		// generic restraints
1341 323
		'guid', 'guids'
1342
	];
1343
1344 323
	switch ($type) {
1345 323
		case 'metadata':
1346
			$metadata_required = [
1347 320
				'metadata_name', 'metadata_names',
1348
				'metadata_value', 'metadata_values'
1349
			];
1350
1351 320
			$required = array_merge($required, $metadata_required);
1352 320
			break;
1353
1354 254
		case 'annotations':
1355 254
		case 'annotation':
1356
			$annotations_required = [
1357 253
				'annotation_owner_guid', 'annotation_owner_guids',
1358
				'annotation_name', 'annotation_names',
1359
				'annotation_value', 'annotation_values'
1360
			];
1361
1362 253
			$required = array_merge($required, $annotations_required);
1363 253
			break;
1364
1365 253
		case 'river':
1366
			// overriding generic restraints as guids isn't supported in river
1367
			$required = [
1368 253
				'id', 'ids',
1369
				'subject_guid', 'subject_guids',
1370
				'object_guid', 'object_guids',
1371
				'target_guid', 'target_guids',
1372
				'annotation_id', 'annotation_ids',
1373
				'view', 'views',
1374
			];
1375 253
			break;
1376
		
1377
		default:
1378
			return false;
1379
	}
1380
1381 323
	foreach ($required as $key) {
1382
		// check that it exists and is something.
1383 323
		if (isset($options[$key]) && $options[$key]) {
1384 323
			return true;
1385
		}
1386
	}
1387
1388 1
	return false;
1389
}
1390
1391
/**
1392
 * Checks the status of the Walled Garden and forwards to a login page
1393
 * if required.
1394
 *
1395
 * If the site is in Walled Garden mode, all page except those registered as
1396
 * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to
1397
 * a login page.
1398
 *
1399
 * @since 1.8.0
1400
 * @elgg_event_handler init system
1401
 * @return void
1402
 * @access private
1403
 */
1404
function _elgg_walled_garden_init() {
1405 80
	if (!_elgg_config()->walled_garden) {
1406 80
		return;
1407
	}
1408
1409
	elgg_register_external_file('css', 'elgg.walled_garden', elgg_get_simplecache_url('walled_garden.css'));
1410
1411
	elgg_register_plugin_hook_handler('register', 'menu:walled_garden', '_elgg_walled_garden_menu');
1412
1413
	if (_elgg_config()->default_access == ACCESS_PUBLIC) {
1414
		elgg_set_config('default_access', ACCESS_LOGGED_IN);
1415
	}
1416
1417
	elgg_register_plugin_hook_handler('access:collections:write', 'all', '_elgg_walled_garden_remove_public_access', 9999);
1418
1419
	if (!elgg_is_logged_in()) {
1420
		// override the front page
1421
		elgg_register_route('index', [
1422
			'path' => '/',
1423
			'resource' => 'walled_garden',
1424
		]);
1425
	}
1426
}
1427
1428
/**
1429
 * Adds home link to walled garden menu
1430
 *
1431
 * @param string $hook         'register'
1432
 * @param string $type         'menu:walled_garden'
1433
 * @param array  $return_value Current menu items
1434
 * @param array  $params       Optional menu parameters
1435
 *
1436
 * @return array
1437
 *
1438
 * @access private
1439
 */
1440
function _elgg_walled_garden_menu($hook, $type, $return_value, $params) {
1441
	
1442
	if (current_page_url() === elgg_get_site_url()) {
1443
		return;
1444
	}
1445
	
1446
	$return_value[] = \ElggMenuItem::factory([
1447
		'name' => 'home',
1448
		'href' => '/',
1449
		'text' => elgg_echo('walled_garden:home'),
1450
		'priority' => 10,
1451
	]);
1452
1453
	return $return_value;
1454
}
1455
1456
/**
1457
 * Remove public access for walled gardens
1458
 *
1459
 * @param string $hook     'access:collections:write'
1460
 * @param string $type     'all'
1461
 * @param array  $accesses current return value
1462
 *
1463
 * @return array
1464
 *
1465
 * @access private
1466
 */
1467
function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) {
1468
	if (isset($accesses[ACCESS_PUBLIC])) {
1469
		unset($accesses[ACCESS_PUBLIC]);
1470
	}
1471
	return $accesses;
1472
}
1473
1474
/**
1475
 * Elgg's main init.
1476
 *
1477
 * Handles core actions, the JS pagehandler, and the shutdown function.
1478
 *
1479
 * @elgg_event_handler init system
1480
 * @return void
1481
 * @access private
1482
 */
1483
function _elgg_init() {
1484 80
	elgg_register_simplecache_view('resources/manifest.json');
1485
	
1486
	elgg_register_plugin_hook_handler('head', 'page', function($hook, $type, array $result) {
1487 6
		$result['links']['manifest'] = [
1488 6
			'rel' => 'manifest',
1489 6
			'href' => elgg_get_simplecache_url('resources/manifest.json'),
1490
		];
1491
1492 6
		return $result;
1493 80
	});
1494
1495 80
	if (_elgg_config()->enable_profiling) {
1496
		/**
1497
		 * @see \Elgg\Profiler::handlePageOutput
1498
		 */
1499
		elgg_register_plugin_hook_handler('output', 'page', [\Elgg\Profiler::class, 'handlePageOutput'], 999);
1500
	}
1501
1502 80
	elgg_register_plugin_hook_handler('commands', 'cli', '_elgg_init_cli_commands');
1503 80
}
1504
1505
/**
1506
 * Initialize Cli commands
1507
 *
1508
 * @elgg_plugin_hook commands cli
1509
 *
1510
 * @param \Elgg\Hook $hook Hook
1511
 *
1512
 * @return \Elgg\Cli\Command[]
1513
 * @access private
1514
 */
1515
function _elgg_init_cli_commands(\Elgg\Hook $hook) {
1516
	$defaults = [
1517
		\Elgg\Cli\SimpletestCommand::class,
1518
		\Elgg\Cli\DatabaseSeedCommand::class,
1519
		\Elgg\Cli\DatabaseUnseedCommand::class,
1520
		\Elgg\Cli\CronCommand::class,
1521
		\Elgg\Cli\FlushCommand::class,
1522
		\Elgg\Cli\PluginsListCommand::class,
1523
		\Elgg\Cli\PluginsActivateCommand::class,
1524
		\Elgg\Cli\PluginsDeactivateCommand::class,
1525
	];
1526
1527
	return array_merge($defaults, (array) $hook->getValue());
1528
}
1529
1530
/**
1531
 * Register core routes
1532
 * @return void
1533
 * @internal
1534
 */
1535
function _elgg_register_routes() {
1536 190
	$conf = \Elgg\Project\Paths::elgg() . 'engine/routes.php';
1537 190
	$routes = \Elgg\Includer::includeFile($conf);
1538
1539 190
	foreach ($routes as $name => $def) {
1540 190
		elgg_register_route($name, $def);
1541
	}
1542 190
}
1543
1544
/**
1545
 * Register core actions
1546
 * @return void
1547
 * @internal
1548
 */
1549
function _elgg_register_actions() {
1550 68
	$conf = \Elgg\Project\Paths::elgg() . 'engine/actions.php';
1551 68
	$actions = \Elgg\Includer::includeFile($conf);
1552
	
1553 68
	$root_path = \Elgg\Project\Paths::elgg();
1554
1555 68
	foreach ($actions as $action => $action_spec) {
1556 68
		if (!is_array($action_spec)) {
1557
			continue;
1558
		}
1559
		
1560 68
		$access = elgg_extract('access', $action_spec, 'logged_in');
1561 68
		$handler = elgg_extract('controller', $action_spec);
1562 68
		if (!$handler) {
1563 68
			$handler = elgg_extract('filename', $action_spec);
1564 68
			if (!$handler) {
1565 68
				$handler = "$root_path/actions/{$action}.php";
1566
			}
1567
		}
1568
		
1569 68
		elgg_register_action($action, $handler, $access);
1570
	}
1571 68
}
1572
1573
/**
1574
 * Adds unit tests for the general API.
1575
 *
1576
 * @param string $hook   unit_test
1577
 * @param string $type   system
1578
 * @param array  $value  array of test files
1579
 * @param array  $params empty
1580
 *
1581
 * @elgg_plugin_hook unit_tests system
1582
 * @return array
1583
 * @access private
1584
 * @codeCoverageIgnore
1585
 */
1586
function _elgg_api_test($hook, $type, $value, $params) {
1587
	$value[] = ElggTravisInstallTest::class;
1588
	$value[] = ElggCoreHelpersTest::class;
1589
	$value[] = ElggCoreRegressionBugsTest::class;
1590
	$value[] = ElggBatchTest::class;
1591
	return $value;
1592
}
1593
1594
/**
1595
 * @see \Elgg\Application::loadCore Do not do work here. Just register for events.
1596
 */
1597
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
1598 69
	$events->registerHandler('init', 'system', '_elgg_init');
1599 69
	$events->registerHandler('init', 'system', '_elgg_walled_garden_init', 1000);
1600
1601 69
	$hooks->registerHandler('unit_test', 'system', '_elgg_api_test');
1602
};
1603