@@ -63,7 +63,7 @@ discard block |
||
63 | 63 | * @since 1.9 |
64 | 64 | */ |
65 | 65 | function elgg_register_notification_event($object_type, $object_subtype, array $actions = []) { |
66 | - _elgg_services()->notifications->registerEvent($object_type, $object_subtype, $actions); |
|
66 | + _elgg_services()->notifications->registerEvent($object_type, $object_subtype, $actions); |
|
67 | 67 | } |
68 | 68 | |
69 | 69 | /** |
@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | * @since 1.9 |
76 | 76 | */ |
77 | 77 | function elgg_unregister_notification_event($object_type, $object_subtype) { |
78 | - return _elgg_services()->notifications->unregisterEvent($object_type, $object_subtype); |
|
78 | + return _elgg_services()->notifications->unregisterEvent($object_type, $object_subtype); |
|
79 | 79 | } |
80 | 80 | |
81 | 81 | /** |
@@ -91,7 +91,7 @@ discard block |
||
91 | 91 | * @since 1.9 |
92 | 92 | */ |
93 | 93 | function elgg_register_notification_method($name) { |
94 | - _elgg_services()->notifications->registerMethod($name); |
|
94 | + _elgg_services()->notifications->registerMethod($name); |
|
95 | 95 | } |
96 | 96 | |
97 | 97 | /** |
@@ -107,7 +107,7 @@ discard block |
||
107 | 107 | * @since 2.3 |
108 | 108 | */ |
109 | 109 | function elgg_get_notification_methods() { |
110 | - return _elgg_services()->notifications->getMethods(); |
|
110 | + return _elgg_services()->notifications->getMethods(); |
|
111 | 111 | } |
112 | 112 | |
113 | 113 | /** |
@@ -119,7 +119,7 @@ discard block |
||
119 | 119 | * @since 1.9 |
120 | 120 | */ |
121 | 121 | function elgg_unregister_notification_method($name) { |
122 | - return _elgg_services()->notifications->unregisterMethod($name); |
|
122 | + return _elgg_services()->notifications->unregisterMethod($name); |
|
123 | 123 | } |
124 | 124 | |
125 | 125 | /** |
@@ -132,10 +132,10 @@ discard block |
||
132 | 132 | * @since 1.9 |
133 | 133 | */ |
134 | 134 | function elgg_add_subscription($user_guid, $method, $target_guid) { |
135 | - $methods = _elgg_services()->notifications->getMethods(); |
|
136 | - $db = _elgg_services()->db; |
|
137 | - $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
138 | - return $subs->addSubscription($user_guid, $method, $target_guid); |
|
135 | + $methods = _elgg_services()->notifications->getMethods(); |
|
136 | + $db = _elgg_services()->db; |
|
137 | + $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
138 | + return $subs->addSubscription($user_guid, $method, $target_guid); |
|
139 | 139 | } |
140 | 140 | |
141 | 141 | /** |
@@ -148,10 +148,10 @@ discard block |
||
148 | 148 | * @since 1.9 |
149 | 149 | */ |
150 | 150 | function elgg_remove_subscription($user_guid, $method, $target_guid) { |
151 | - $methods = _elgg_services()->notifications->getMethods(); |
|
152 | - $db = _elgg_services()->db; |
|
153 | - $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
154 | - return $subs->removeSubscription($user_guid, $method, $target_guid); |
|
151 | + $methods = _elgg_services()->notifications->getMethods(); |
|
152 | + $db = _elgg_services()->db; |
|
153 | + $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
154 | + return $subs->removeSubscription($user_guid, $method, $target_guid); |
|
155 | 155 | } |
156 | 156 | |
157 | 157 | /** |
@@ -169,10 +169,10 @@ discard block |
||
169 | 169 | * @todo deprecate once new subscriptions system has been added |
170 | 170 | */ |
171 | 171 | function elgg_get_subscriptions_for_container($container_guid) { |
172 | - $methods = _elgg_services()->notifications->getMethods(); |
|
173 | - $db = _elgg_services()->db; |
|
174 | - $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
175 | - return $subs->getSubscriptionsForContainer($container_guid); |
|
172 | + $methods = _elgg_services()->notifications->getMethods(); |
|
173 | + $db = _elgg_services()->db; |
|
174 | + $subs = new \Elgg\Notifications\SubscriptionsService($db, $methods); |
|
175 | + return $subs->getSubscriptionsForContainer($container_guid); |
|
176 | 176 | } |
177 | 177 | |
178 | 178 | /** |
@@ -191,17 +191,17 @@ discard block |
||
191 | 191 | * @since 1.9 |
192 | 192 | */ |
193 | 193 | function _elgg_enqueue_notification_event($action, $type, $object) { |
194 | - _elgg_services()->notifications->enqueueEvent($action, $type, $object); |
|
194 | + _elgg_services()->notifications->enqueueEvent($action, $type, $object); |
|
195 | 195 | } |
196 | 196 | |
197 | 197 | /** |
198 | 198 | * @access private |
199 | 199 | */ |
200 | 200 | function _elgg_notifications_cron() { |
201 | - // calculate when we should stop |
|
202 | - // @todo make configurable? |
|
203 | - $stop_time = time() + 45; |
|
204 | - _elgg_services()->notifications->processQueue($stop_time); |
|
201 | + // calculate when we should stop |
|
202 | + // @todo make configurable? |
|
203 | + $stop_time = time() + 45; |
|
204 | + _elgg_services()->notifications->processQueue($stop_time); |
|
205 | 205 | } |
206 | 206 | |
207 | 207 | /** |
@@ -216,38 +216,38 @@ discard block |
||
216 | 216 | */ |
217 | 217 | function _elgg_send_email_notification($hook, $type, $result, $params) { |
218 | 218 | |
219 | - if ($result === true) { |
|
220 | - // assume someone else already sent the message |
|
221 | - return; |
|
222 | - } |
|
219 | + if ($result === true) { |
|
220 | + // assume someone else already sent the message |
|
221 | + return; |
|
222 | + } |
|
223 | 223 | |
224 | - /* @var \Elgg\Notifications\Notification $message */ |
|
225 | - $message = $params['notification']; |
|
224 | + /* @var \Elgg\Notifications\Notification $message */ |
|
225 | + $message = $params['notification']; |
|
226 | 226 | |
227 | - $sender = $message->getSender(); |
|
228 | - $recipient = $message->getRecipient(); |
|
227 | + $sender = $message->getSender(); |
|
228 | + $recipient = $message->getRecipient(); |
|
229 | 229 | |
230 | - if (!$sender) { |
|
231 | - return false; |
|
232 | - } |
|
230 | + if (!$sender) { |
|
231 | + return false; |
|
232 | + } |
|
233 | 233 | |
234 | - if (!$recipient || !$recipient->email) { |
|
235 | - return false; |
|
236 | - } |
|
234 | + if (!$recipient || !$recipient->email) { |
|
235 | + return false; |
|
236 | + } |
|
237 | 237 | |
238 | - $to = Address::getFormattedEmailAddress($recipient->email, $recipient->getDisplayName()); |
|
238 | + $to = Address::getFormattedEmailAddress($recipient->email, $recipient->getDisplayName()); |
|
239 | 239 | |
240 | - // If there's an email address, use it - but only if it's not from a user. |
|
241 | - if (!($sender instanceof \ElggUser) && $sender->email) { |
|
242 | - $from = Address::getFormattedEmailAddress($sender->email, $sender->getDisplayName()); |
|
243 | - } else { |
|
244 | - // get the site email address |
|
245 | - $site = elgg_get_site_entity(); |
|
240 | + // If there's an email address, use it - but only if it's not from a user. |
|
241 | + if (!($sender instanceof \ElggUser) && $sender->email) { |
|
242 | + $from = Address::getFormattedEmailAddress($sender->email, $sender->getDisplayName()); |
|
243 | + } else { |
|
244 | + // get the site email address |
|
245 | + $site = elgg_get_site_entity(); |
|
246 | 246 | |
247 | - $from = Address::getFormattedEmailAddress($site->getEmailAddress(), $site->getDisplayName()); |
|
248 | - } |
|
247 | + $from = Address::getFormattedEmailAddress($site->getEmailAddress(), $site->getDisplayName()); |
|
248 | + } |
|
249 | 249 | |
250 | - return elgg_send_email($from, $to, $message->subject, $message->body, $params); |
|
250 | + return elgg_send_email($from, $to, $message->subject, $message->body, $params); |
|
251 | 251 | } |
252 | 252 | |
253 | 253 | /** |
@@ -266,19 +266,19 @@ discard block |
||
266 | 266 | */ |
267 | 267 | function _elgg_notifications_smtp_default_message_id_header($hook, $type, $returnvalue, $params) { |
268 | 268 | |
269 | - if (!is_array($returnvalue) || !is_array($returnvalue['params'])) { |
|
270 | - // another hook handler returned a non-array, let's not override it |
|
271 | - return; |
|
272 | - } |
|
269 | + if (!is_array($returnvalue) || !is_array($returnvalue['params'])) { |
|
270 | + // another hook handler returned a non-array, let's not override it |
|
271 | + return; |
|
272 | + } |
|
273 | 273 | |
274 | - $hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST); |
|
275 | - $url_path = parse_url(elgg_get_site_url(), PHP_URL_PATH); |
|
274 | + $hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST); |
|
275 | + $url_path = parse_url(elgg_get_site_url(), PHP_URL_PATH); |
|
276 | 276 | |
277 | - $mt = microtime(true); |
|
277 | + $mt = microtime(true); |
|
278 | 278 | |
279 | - $returnvalue['headers']['Message-ID'] = "<{$url_path}.default.{$mt}@{$hostname}>"; |
|
279 | + $returnvalue['headers']['Message-ID'] = "<{$url_path}.default.{$mt}@{$hostname}>"; |
|
280 | 280 | |
281 | - return $returnvalue; |
|
281 | + return $returnvalue; |
|
282 | 282 | } |
283 | 283 | /** |
284 | 284 | * Adds default thread SMTP headers to group messages correctly. |
@@ -293,65 +293,65 @@ discard block |
||
293 | 293 | */ |
294 | 294 | function _elgg_notifications_smtp_thread_headers($hook, $type, $returnvalue, $params) { |
295 | 295 | |
296 | - if (!is_array($returnvalue) || !is_array($returnvalue['params'])) { |
|
297 | - // another hook handler returned a non-array, let's not override it |
|
298 | - return; |
|
299 | - } |
|
300 | - |
|
301 | - $notificationParams = elgg_extract('params', $returnvalue, []); |
|
302 | - /** @var \Elgg\Notifications\Notification */ |
|
303 | - $notification = elgg_extract('notification', $notificationParams); |
|
304 | - |
|
305 | - if (!($notification instanceof \Elgg\Notifications\Notification)) { |
|
306 | - return $returnvalue; |
|
307 | - } |
|
308 | - |
|
309 | - $hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST); |
|
310 | - $urlPath = parse_url(elgg_get_site_url(), PHP_URL_PATH); |
|
311 | - |
|
312 | - $object = elgg_extract('object', $notification->params); |
|
313 | - /** @var \Elgg\Notifications\Event $event */ |
|
314 | - $event = elgg_extract('event', $notification->params); |
|
315 | - |
|
316 | - if (($object instanceof \ElggEntity) && ($event instanceof \Elgg\Notifications\NotificationEvent)) { |
|
317 | - if ($event->getAction() === 'create') { |
|
318 | - // create event happens once per entity and we need to guarantee message id uniqueness |
|
319 | - // and at the same time have thread message id that we don't need to store |
|
320 | - $messageId = "<{$urlPath}.entity.{$object->guid}@{$hostname}>"; |
|
321 | - } else { |
|
322 | - $mt = microtime(true); |
|
323 | - $messageId = "<{$urlPath}.entity.{$object->guid}.$mt@{$hostname}>"; |
|
324 | - } |
|
325 | - $returnvalue['headers']["Message-ID"] = $messageId; |
|
326 | - $container = $object->getContainerEntity(); |
|
327 | - |
|
328 | - // let's just thread comments by default |
|
329 | - if (($container instanceof \ElggEntity) && ($object instanceof \ElggComment)) { |
|
330 | - $threadMessageId = "<{$urlPath}.entity.{$container->guid}@{$hostname}>"; |
|
331 | - $returnvalue['headers']['In-Reply-To'] = $threadMessageId; |
|
332 | - $returnvalue['headers']['References'] = $threadMessageId; |
|
333 | - } |
|
334 | - } |
|
335 | - |
|
336 | - return $returnvalue; |
|
296 | + if (!is_array($returnvalue) || !is_array($returnvalue['params'])) { |
|
297 | + // another hook handler returned a non-array, let's not override it |
|
298 | + return; |
|
299 | + } |
|
300 | + |
|
301 | + $notificationParams = elgg_extract('params', $returnvalue, []); |
|
302 | + /** @var \Elgg\Notifications\Notification */ |
|
303 | + $notification = elgg_extract('notification', $notificationParams); |
|
304 | + |
|
305 | + if (!($notification instanceof \Elgg\Notifications\Notification)) { |
|
306 | + return $returnvalue; |
|
307 | + } |
|
308 | + |
|
309 | + $hostname = parse_url(elgg_get_site_url(), PHP_URL_HOST); |
|
310 | + $urlPath = parse_url(elgg_get_site_url(), PHP_URL_PATH); |
|
311 | + |
|
312 | + $object = elgg_extract('object', $notification->params); |
|
313 | + /** @var \Elgg\Notifications\Event $event */ |
|
314 | + $event = elgg_extract('event', $notification->params); |
|
315 | + |
|
316 | + if (($object instanceof \ElggEntity) && ($event instanceof \Elgg\Notifications\NotificationEvent)) { |
|
317 | + if ($event->getAction() === 'create') { |
|
318 | + // create event happens once per entity and we need to guarantee message id uniqueness |
|
319 | + // and at the same time have thread message id that we don't need to store |
|
320 | + $messageId = "<{$urlPath}.entity.{$object->guid}@{$hostname}>"; |
|
321 | + } else { |
|
322 | + $mt = microtime(true); |
|
323 | + $messageId = "<{$urlPath}.entity.{$object->guid}.$mt@{$hostname}>"; |
|
324 | + } |
|
325 | + $returnvalue['headers']["Message-ID"] = $messageId; |
|
326 | + $container = $object->getContainerEntity(); |
|
327 | + |
|
328 | + // let's just thread comments by default |
|
329 | + if (($container instanceof \ElggEntity) && ($object instanceof \ElggComment)) { |
|
330 | + $threadMessageId = "<{$urlPath}.entity.{$container->guid}@{$hostname}>"; |
|
331 | + $returnvalue['headers']['In-Reply-To'] = $threadMessageId; |
|
332 | + $returnvalue['headers']['References'] = $threadMessageId; |
|
333 | + } |
|
334 | + } |
|
335 | + |
|
336 | + return $returnvalue; |
|
337 | 337 | } |
338 | 338 | |
339 | 339 | /** |
340 | 340 | * @access private |
341 | 341 | */ |
342 | 342 | function _elgg_notifications_init() { |
343 | - elgg_register_plugin_hook_handler('cron', 'minute', '_elgg_notifications_cron', 100); |
|
344 | - elgg_register_event_handler('all', 'all', '_elgg_enqueue_notification_event', 700); |
|
345 | - |
|
346 | - // add email notifications |
|
347 | - elgg_register_notification_method('email'); |
|
348 | - elgg_register_plugin_hook_handler('send', 'notification:email', '_elgg_send_email_notification'); |
|
349 | - elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_default_message_id_header', 1); |
|
350 | - elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_thread_headers'); |
|
351 | - |
|
352 | - // add ability to set personal notification method |
|
353 | - elgg_extend_view('forms/account/settings', 'core/settings/account/notifications'); |
|
354 | - elgg_register_plugin_hook_handler('usersettings:save', 'user', '_elgg_save_notification_user_settings'); |
|
343 | + elgg_register_plugin_hook_handler('cron', 'minute', '_elgg_notifications_cron', 100); |
|
344 | + elgg_register_event_handler('all', 'all', '_elgg_enqueue_notification_event', 700); |
|
345 | + |
|
346 | + // add email notifications |
|
347 | + elgg_register_notification_method('email'); |
|
348 | + elgg_register_plugin_hook_handler('send', 'notification:email', '_elgg_send_email_notification'); |
|
349 | + elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_default_message_id_header', 1); |
|
350 | + elgg_register_plugin_hook_handler('email', 'system', '_elgg_notifications_smtp_thread_headers'); |
|
351 | + |
|
352 | + // add ability to set personal notification method |
|
353 | + elgg_extend_view('forms/account/settings', 'core/settings/account/notifications'); |
|
354 | + elgg_register_plugin_hook_handler('usersettings:save', 'user', '_elgg_save_notification_user_settings'); |
|
355 | 355 | } |
356 | 356 | |
357 | 357 | /** |
@@ -371,73 +371,73 @@ discard block |
||
371 | 371 | */ |
372 | 372 | function _elgg_notify_user($to, $from, $subject, $message, array $params = null, $methods_override = "") { |
373 | 373 | |
374 | - $notify_service = _elgg_services()->notifications; |
|
375 | - |
|
376 | - // Sanitise |
|
377 | - if (!is_array($to)) { |
|
378 | - $to = [(int) $to]; |
|
379 | - } |
|
380 | - $from = (int) $from; |
|
381 | - //$subject = sanitise_string($subject); |
|
382 | - // Get notification methods |
|
383 | - if (($methods_override) && (!is_array($methods_override))) { |
|
384 | - $methods_override = [$methods_override]; |
|
385 | - } |
|
386 | - |
|
387 | - $result = []; |
|
388 | - |
|
389 | - foreach ($to as $guid) { |
|
390 | - // Results for a user are... |
|
391 | - $result[$guid] = []; |
|
392 | - |
|
393 | - if ($guid) { // Is the guid > 0? |
|
394 | - // Are we overriding delivery? |
|
395 | - $methods = $methods_override; |
|
396 | - if (!$methods) { |
|
397 | - $tmp = get_user_notification_settings($guid); |
|
398 | - $methods = []; |
|
399 | - // $tmp may be false. don't cast |
|
400 | - if (is_object($tmp)) { |
|
401 | - foreach ($tmp as $k => $v) { |
|
402 | - // Add method if method is turned on for user! |
|
403 | - if ($v) { |
|
404 | - $methods[] = $k; |
|
405 | - } |
|
406 | - } |
|
407 | - } |
|
408 | - } |
|
409 | - |
|
410 | - if ($methods) { |
|
411 | - // Deliver |
|
412 | - foreach ($methods as $method) { |
|
413 | - $handler = $notify_service->getDeprecatedHandler($method); |
|
414 | - /* @var callable $handler */ |
|
415 | - if (!$handler || !is_callable($handler)) { |
|
416 | - elgg_log("No handler registered for the method $method", 'WARNING'); |
|
417 | - continue; |
|
418 | - } |
|
419 | - |
|
420 | - elgg_log("Sending message to $guid using $method"); |
|
421 | - |
|
422 | - // Trigger handler and retrieve result. |
|
423 | - try { |
|
424 | - $result[$guid][$method] = call_user_func( |
|
425 | - $handler, |
|
426 | - $from ? get_entity($from) : null, |
|
427 | - get_entity($guid), |
|
428 | - $subject, |
|
429 | - $message, |
|
430 | - $params |
|
431 | - ); |
|
432 | - } catch (Exception $e) { |
|
433 | - error_log($e->getMessage()); |
|
434 | - } |
|
435 | - } |
|
436 | - } |
|
437 | - } |
|
438 | - } |
|
439 | - |
|
440 | - return $result; |
|
374 | + $notify_service = _elgg_services()->notifications; |
|
375 | + |
|
376 | + // Sanitise |
|
377 | + if (!is_array($to)) { |
|
378 | + $to = [(int) $to]; |
|
379 | + } |
|
380 | + $from = (int) $from; |
|
381 | + //$subject = sanitise_string($subject); |
|
382 | + // Get notification methods |
|
383 | + if (($methods_override) && (!is_array($methods_override))) { |
|
384 | + $methods_override = [$methods_override]; |
|
385 | + } |
|
386 | + |
|
387 | + $result = []; |
|
388 | + |
|
389 | + foreach ($to as $guid) { |
|
390 | + // Results for a user are... |
|
391 | + $result[$guid] = []; |
|
392 | + |
|
393 | + if ($guid) { // Is the guid > 0? |
|
394 | + // Are we overriding delivery? |
|
395 | + $methods = $methods_override; |
|
396 | + if (!$methods) { |
|
397 | + $tmp = get_user_notification_settings($guid); |
|
398 | + $methods = []; |
|
399 | + // $tmp may be false. don't cast |
|
400 | + if (is_object($tmp)) { |
|
401 | + foreach ($tmp as $k => $v) { |
|
402 | + // Add method if method is turned on for user! |
|
403 | + if ($v) { |
|
404 | + $methods[] = $k; |
|
405 | + } |
|
406 | + } |
|
407 | + } |
|
408 | + } |
|
409 | + |
|
410 | + if ($methods) { |
|
411 | + // Deliver |
|
412 | + foreach ($methods as $method) { |
|
413 | + $handler = $notify_service->getDeprecatedHandler($method); |
|
414 | + /* @var callable $handler */ |
|
415 | + if (!$handler || !is_callable($handler)) { |
|
416 | + elgg_log("No handler registered for the method $method", 'WARNING'); |
|
417 | + continue; |
|
418 | + } |
|
419 | + |
|
420 | + elgg_log("Sending message to $guid using $method"); |
|
421 | + |
|
422 | + // Trigger handler and retrieve result. |
|
423 | + try { |
|
424 | + $result[$guid][$method] = call_user_func( |
|
425 | + $handler, |
|
426 | + $from ? get_entity($from) : null, |
|
427 | + get_entity($guid), |
|
428 | + $subject, |
|
429 | + $message, |
|
430 | + $params |
|
431 | + ); |
|
432 | + } catch (Exception $e) { |
|
433 | + error_log($e->getMessage()); |
|
434 | + } |
|
435 | + } |
|
436 | + } |
|
437 | + } |
|
438 | + } |
|
439 | + |
|
440 | + return $result; |
|
441 | 441 | } |
442 | 442 | |
443 | 443 | /** |
@@ -490,30 +490,30 @@ discard block |
||
490 | 490 | */ |
491 | 491 | function notify_user($to, $from = 0, $subject = '', $message = '', array $params = [], $methods_override = null) { |
492 | 492 | |
493 | - $params['subject'] = $subject; |
|
494 | - $params['body'] = $message; |
|
495 | - $params['methods_override'] = $methods_override; |
|
496 | - |
|
497 | - if ($from) { |
|
498 | - $sender = get_entity($from); |
|
499 | - } else { |
|
500 | - $sender = elgg_get_site_entity(); |
|
501 | - } |
|
502 | - if (!$sender) { |
|
503 | - return []; |
|
504 | - } |
|
505 | - |
|
506 | - $recipients = []; |
|
507 | - $to = (array) $to; |
|
508 | - foreach ($to as $guid) { |
|
509 | - $recipient = get_entity($guid); |
|
510 | - if (!$recipient) { |
|
511 | - continue; |
|
512 | - } |
|
513 | - $recipients[] = $recipient; |
|
514 | - } |
|
515 | - |
|
516 | - return _elgg_services()->notifications->sendInstantNotifications($sender, $recipients, $params); |
|
493 | + $params['subject'] = $subject; |
|
494 | + $params['body'] = $message; |
|
495 | + $params['methods_override'] = $methods_override; |
|
496 | + |
|
497 | + if ($from) { |
|
498 | + $sender = get_entity($from); |
|
499 | + } else { |
|
500 | + $sender = elgg_get_site_entity(); |
|
501 | + } |
|
502 | + if (!$sender) { |
|
503 | + return []; |
|
504 | + } |
|
505 | + |
|
506 | + $recipients = []; |
|
507 | + $to = (array) $to; |
|
508 | + foreach ($to as $guid) { |
|
509 | + $recipient = get_entity($guid); |
|
510 | + if (!$recipient) { |
|
511 | + continue; |
|
512 | + } |
|
513 | + $recipients[] = $recipient; |
|
514 | + } |
|
515 | + |
|
516 | + return _elgg_services()->notifications->sendInstantNotifications($sender, $recipients, $params); |
|
517 | 517 | } |
518 | 518 | |
519 | 519 | /** |
@@ -526,18 +526,18 @@ discard block |
||
526 | 526 | */ |
527 | 527 | function get_user_notification_settings($user_guid = 0) { |
528 | 528 | |
529 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated by ElggUser::getNotificationSettings()', '2.3'); |
|
529 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated by ElggUser::getNotificationSettings()', '2.3'); |
|
530 | 530 | |
531 | - if ((int) $user_guid == 0) { |
|
532 | - $user_guid = elgg_get_logged_in_user_guid(); |
|
533 | - } |
|
531 | + if ((int) $user_guid == 0) { |
|
532 | + $user_guid = elgg_get_logged_in_user_guid(); |
|
533 | + } |
|
534 | 534 | |
535 | - $user = get_entity($user_guid); |
|
536 | - if (!$user instanceof \ElggUser) { |
|
537 | - return false; |
|
538 | - } |
|
535 | + $user = get_entity($user_guid); |
|
536 | + if (!$user instanceof \ElggUser) { |
|
537 | + return false; |
|
538 | + } |
|
539 | 539 | |
540 | - return (object) $user->getNotificationSettings(); |
|
540 | + return (object) $user->getNotificationSettings(); |
|
541 | 541 | } |
542 | 542 | |
543 | 543 | /** |
@@ -551,26 +551,26 @@ discard block |
||
551 | 551 | * @deprecated 2.3 |
552 | 552 | */ |
553 | 553 | function set_user_notification_setting($user_guid, $method, $value) { |
554 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated by ElggUser::setNotificationSetting()', '2.3'); |
|
555 | - |
|
556 | - if (!$user_guid) { |
|
557 | - $user_guid = elgg_get_logged_in_user_guid(); |
|
558 | - } |
|
559 | - $user = get_entity($user_guid); |
|
560 | - if (!$user instanceof \ElggUser) { |
|
561 | - return false; |
|
562 | - } |
|
563 | - |
|
564 | - if (is_string($value)) { |
|
565 | - $value = strtolower($value); |
|
566 | - } |
|
567 | - if ($value == 'yes' || $value == 'on' || $value == 'enabled') { |
|
568 | - $value = true; |
|
569 | - } else if ($value == 'no' || $value == 'off' || $value == 'disabled') { |
|
570 | - $value = false; |
|
571 | - } |
|
572 | - |
|
573 | - return $user->setNotificationSetting($method, (bool) $value); |
|
554 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated by ElggUser::setNotificationSetting()', '2.3'); |
|
555 | + |
|
556 | + if (!$user_guid) { |
|
557 | + $user_guid = elgg_get_logged_in_user_guid(); |
|
558 | + } |
|
559 | + $user = get_entity($user_guid); |
|
560 | + if (!$user instanceof \ElggUser) { |
|
561 | + return false; |
|
562 | + } |
|
563 | + |
|
564 | + if (is_string($value)) { |
|
565 | + $value = strtolower($value); |
|
566 | + } |
|
567 | + if ($value == 'yes' || $value == 'on' || $value == 'enabled') { |
|
568 | + $value = true; |
|
569 | + } else if ($value == 'no' || $value == 'off' || $value == 'disabled') { |
|
570 | + $value = false; |
|
571 | + } |
|
572 | + |
|
573 | + return $user->setNotificationSetting($method, (bool) $value); |
|
574 | 574 | } |
575 | 575 | |
576 | 576 | /** |
@@ -587,80 +587,80 @@ discard block |
||
587 | 587 | * @since 1.7.2 |
588 | 588 | */ |
589 | 589 | function elgg_send_email($from, $to, $subject, $body, array $params = null) { |
590 | - if (!$from) { |
|
591 | - $msg = "Missing a required parameter, '" . 'from' . "'"; |
|
592 | - throw new \NotificationException($msg); |
|
593 | - } |
|
594 | - |
|
595 | - if (!$to) { |
|
596 | - $msg = "Missing a required parameter, '" . 'to' . "'"; |
|
597 | - throw new \NotificationException($msg); |
|
598 | - } |
|
599 | - |
|
600 | - $headers = [ |
|
601 | - "Content-Type" => "text/plain; charset=UTF-8; format=flowed", |
|
602 | - "MIME-Version" => "1.0", |
|
603 | - "Content-Transfer-Encoding" => "8bit", |
|
604 | - ]; |
|
605 | - |
|
606 | - // return true/false to stop elgg_send_email() from sending |
|
607 | - $mail_params = [ |
|
608 | - 'to' => $to, |
|
609 | - 'from' => $from, |
|
610 | - 'subject' => $subject, |
|
611 | - 'body' => $body, |
|
612 | - 'headers' => $headers, |
|
613 | - 'params' => $params, |
|
614 | - ]; |
|
615 | - |
|
616 | - // $mail_params is passed as both params and return value. The former is for backwards |
|
617 | - // compatibility. The latter is so handlers can now alter the contents/headers of |
|
618 | - // the email by returning the array |
|
619 | - $result = _elgg_services()->hooks->trigger('email', 'system', $mail_params, $mail_params); |
|
620 | - if (!is_array($result)) { |
|
621 | - // don't need null check: Handlers can't set a hook value to null! |
|
622 | - return (bool) $result; |
|
623 | - } |
|
624 | - |
|
625 | - // strip name from to and from |
|
626 | - |
|
627 | - $to_address = Address::fromString($result['to']); |
|
628 | - $from_address = Address::fromString($result['from']); |
|
629 | - |
|
630 | - |
|
631 | - $subject = elgg_strip_tags($result['subject']); |
|
632 | - $subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8'); |
|
633 | - // Sanitise subject by stripping line endings |
|
634 | - $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); |
|
635 | - $subject = trim($subject); |
|
636 | - |
|
637 | - $body = elgg_strip_tags($result['body']); |
|
638 | - $body = html_entity_decode($body, ENT_QUOTES, 'UTF-8'); |
|
639 | - $body = wordwrap($body); |
|
640 | - |
|
641 | - $message = new Message(); |
|
642 | - $message->setEncoding('UTF-8'); |
|
643 | - $message->addFrom($from_address); |
|
644 | - $message->addTo($to_address); |
|
645 | - $message->setSubject($subject); |
|
646 | - $message->setBody($body); |
|
647 | - |
|
648 | - foreach ($result['headers'] as $headerName => $headerValue) { |
|
649 | - $message->getHeaders()->addHeaderLine($headerName, $headerValue); |
|
650 | - } |
|
651 | - |
|
652 | - // allow others to modify the $message content |
|
653 | - // eg. add html body, add attachments |
|
654 | - $message = _elgg_services()->hooks->trigger('email:message', 'system', $result, $message); |
|
590 | + if (!$from) { |
|
591 | + $msg = "Missing a required parameter, '" . 'from' . "'"; |
|
592 | + throw new \NotificationException($msg); |
|
593 | + } |
|
594 | + |
|
595 | + if (!$to) { |
|
596 | + $msg = "Missing a required parameter, '" . 'to' . "'"; |
|
597 | + throw new \NotificationException($msg); |
|
598 | + } |
|
599 | + |
|
600 | + $headers = [ |
|
601 | + "Content-Type" => "text/plain; charset=UTF-8; format=flowed", |
|
602 | + "MIME-Version" => "1.0", |
|
603 | + "Content-Transfer-Encoding" => "8bit", |
|
604 | + ]; |
|
605 | + |
|
606 | + // return true/false to stop elgg_send_email() from sending |
|
607 | + $mail_params = [ |
|
608 | + 'to' => $to, |
|
609 | + 'from' => $from, |
|
610 | + 'subject' => $subject, |
|
611 | + 'body' => $body, |
|
612 | + 'headers' => $headers, |
|
613 | + 'params' => $params, |
|
614 | + ]; |
|
615 | + |
|
616 | + // $mail_params is passed as both params and return value. The former is for backwards |
|
617 | + // compatibility. The latter is so handlers can now alter the contents/headers of |
|
618 | + // the email by returning the array |
|
619 | + $result = _elgg_services()->hooks->trigger('email', 'system', $mail_params, $mail_params); |
|
620 | + if (!is_array($result)) { |
|
621 | + // don't need null check: Handlers can't set a hook value to null! |
|
622 | + return (bool) $result; |
|
623 | + } |
|
624 | + |
|
625 | + // strip name from to and from |
|
626 | + |
|
627 | + $to_address = Address::fromString($result['to']); |
|
628 | + $from_address = Address::fromString($result['from']); |
|
629 | + |
|
630 | + |
|
631 | + $subject = elgg_strip_tags($result['subject']); |
|
632 | + $subject = html_entity_decode($subject, ENT_QUOTES, 'UTF-8'); |
|
633 | + // Sanitise subject by stripping line endings |
|
634 | + $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); |
|
635 | + $subject = trim($subject); |
|
636 | + |
|
637 | + $body = elgg_strip_tags($result['body']); |
|
638 | + $body = html_entity_decode($body, ENT_QUOTES, 'UTF-8'); |
|
639 | + $body = wordwrap($body); |
|
640 | + |
|
641 | + $message = new Message(); |
|
642 | + $message->setEncoding('UTF-8'); |
|
643 | + $message->addFrom($from_address); |
|
644 | + $message->addTo($to_address); |
|
645 | + $message->setSubject($subject); |
|
646 | + $message->setBody($body); |
|
647 | + |
|
648 | + foreach ($result['headers'] as $headerName => $headerValue) { |
|
649 | + $message->getHeaders()->addHeaderLine($headerName, $headerValue); |
|
650 | + } |
|
651 | + |
|
652 | + // allow others to modify the $message content |
|
653 | + // eg. add html body, add attachments |
|
654 | + $message = _elgg_services()->hooks->trigger('email:message', 'system', $result, $message); |
|
655 | 655 | |
656 | - try { |
|
657 | - _elgg_services()->mailer->send($message); |
|
658 | - } catch (\Zend\Mail\Exception\RuntimeException $e) { |
|
659 | - _elgg_services()->logger->error($e->getMessage()); |
|
660 | - return false; |
|
661 | - } |
|
662 | - |
|
663 | - return true; |
|
656 | + try { |
|
657 | + _elgg_services()->mailer->send($message); |
|
658 | + } catch (\Zend\Mail\Exception\RuntimeException $e) { |
|
659 | + _elgg_services()->logger->error($e->getMessage()); |
|
660 | + return false; |
|
661 | + } |
|
662 | + |
|
663 | + return true; |
|
664 | 664 | } |
665 | 665 | |
666 | 666 | /** |
@@ -671,44 +671,44 @@ discard block |
||
671 | 671 | */ |
672 | 672 | function _elgg_save_notification_user_settings() { |
673 | 673 | |
674 | - $user = elgg_get_logged_in_user_entity(); |
|
675 | - if (!$user) { |
|
676 | - return; |
|
677 | - } |
|
674 | + $user = elgg_get_logged_in_user_entity(); |
|
675 | + if (!$user) { |
|
676 | + return; |
|
677 | + } |
|
678 | 678 | |
679 | - $method = get_input('method'); |
|
679 | + $method = get_input('method'); |
|
680 | 680 | |
681 | - $current_settings = $user->getNotificationSettings(); |
|
681 | + $current_settings = $user->getNotificationSettings(); |
|
682 | 682 | |
683 | - $result = false; |
|
684 | - foreach ($method as $k => $v) { |
|
685 | - // check if setting has changed and skip if not |
|
686 | - if ($current_settings[$k] == ($v == 'yes')) { |
|
687 | - continue; |
|
688 | - } |
|
683 | + $result = false; |
|
684 | + foreach ($method as $k => $v) { |
|
685 | + // check if setting has changed and skip if not |
|
686 | + if ($current_settings[$k] == ($v == 'yes')) { |
|
687 | + continue; |
|
688 | + } |
|
689 | 689 | |
690 | - $result = $user->setNotificationSetting($k, ($v == 'yes')); |
|
691 | - if (!$result) { |
|
692 | - register_error(elgg_echo('notifications:usersettings:save:fail')); |
|
693 | - } |
|
694 | - } |
|
690 | + $result = $user->setNotificationSetting($k, ($v == 'yes')); |
|
691 | + if (!$result) { |
|
692 | + register_error(elgg_echo('notifications:usersettings:save:fail')); |
|
693 | + } |
|
694 | + } |
|
695 | 695 | |
696 | - if ($result) { |
|
697 | - system_message(elgg_echo('notifications:usersettings:save:ok')); |
|
698 | - } |
|
696 | + if ($result) { |
|
697 | + system_message(elgg_echo('notifications:usersettings:save:ok')); |
|
698 | + } |
|
699 | 699 | } |
700 | 700 | |
701 | 701 | /** |
702 | 702 | * @access private |
703 | 703 | */ |
704 | 704 | function _elgg_notifications_test($hook, $type, $tests) { |
705 | - global $CONFIG; |
|
706 | - $tests[] = "{$CONFIG->path}engine/tests/ElggCoreDatabaseQueueTest.php"; |
|
707 | - return $tests; |
|
705 | + global $CONFIG; |
|
706 | + $tests[] = "{$CONFIG->path}engine/tests/ElggCoreDatabaseQueueTest.php"; |
|
707 | + return $tests; |
|
708 | 708 | } |
709 | 709 | |
710 | 710 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
711 | - $events->registerHandler('init', 'system', '_elgg_notifications_init'); |
|
711 | + $events->registerHandler('init', 'system', '_elgg_notifications_init'); |
|
712 | 712 | |
713 | - $hooks->registerHandler('unit_test', 'system', '_elgg_notifications_test'); |
|
713 | + $hooks->registerHandler('unit_test', 'system', '_elgg_notifications_test'); |
|
714 | 714 | }; |
@@ -18,20 +18,20 @@ discard block |
||
18 | 18 | * @return int The size of the directory in bytes |
19 | 19 | */ |
20 | 20 | function get_dir_size($dir, $total_size = 0) { |
21 | - $handle = @opendir($dir); |
|
22 | - while ($file = @readdir($handle)) { |
|
23 | - if (in_array($file, ['.', '..'])) { |
|
24 | - continue; |
|
25 | - } |
|
26 | - if (is_dir($dir . $file)) { |
|
27 | - $total_size = get_dir_size($dir . $file . "/", $total_size); |
|
28 | - } else { |
|
29 | - $total_size += filesize($dir . $file); |
|
30 | - } |
|
31 | - } |
|
32 | - @closedir($handle); |
|
33 | - |
|
34 | - return($total_size); |
|
21 | + $handle = @opendir($dir); |
|
22 | + while ($file = @readdir($handle)) { |
|
23 | + if (in_array($file, ['.', '..'])) { |
|
24 | + continue; |
|
25 | + } |
|
26 | + if (is_dir($dir . $file)) { |
|
27 | + $total_size = get_dir_size($dir . $file . "/", $total_size); |
|
28 | + } else { |
|
29 | + $total_size += filesize($dir . $file); |
|
30 | + } |
|
31 | + } |
|
32 | + @closedir($handle); |
|
33 | + |
|
34 | + return($total_size); |
|
35 | 35 | } |
36 | 36 | |
37 | 37 | /** |
@@ -44,13 +44,13 @@ discard block |
||
44 | 44 | * @deprecated 2.3 |
45 | 45 | */ |
46 | 46 | function get_uploaded_file($input_name) { |
47 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated and will be removed', '2.3'); |
|
48 | - $inputs = elgg_get_uploaded_files($input_name); |
|
49 | - $input = array_shift($inputs); |
|
50 | - if (!$input || !$input->isValid()) { |
|
51 | - return false; |
|
52 | - } |
|
53 | - return file_get_contents($input->getPathname()); |
|
47 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated and will be removed', '2.3'); |
|
48 | + $inputs = elgg_get_uploaded_files($input_name); |
|
49 | + $input = array_shift($inputs); |
|
50 | + if (!$input || !$input->isValid()) { |
|
51 | + return false; |
|
52 | + } |
|
53 | + return file_get_contents($input->getPathname()); |
|
54 | 54 | } |
55 | 55 | |
56 | 56 | /** |
@@ -79,7 +79,7 @@ discard block |
||
79 | 79 | * @since 2.3 |
80 | 80 | */ |
81 | 81 | function elgg_save_resized_image($source, $destination = null, array $params = []) { |
82 | - return _elgg_services()->imageService->resize($source, $destination, $params); |
|
82 | + return _elgg_services()->imageService->resize($source, $destination, $params); |
|
83 | 83 | } |
84 | 84 | |
85 | 85 | /** |
@@ -98,25 +98,25 @@ discard block |
||
98 | 98 | * @deprecated 2.3 |
99 | 99 | */ |
100 | 100 | function get_resized_image_from_uploaded_file($input_name, $maxwidth, $maxheight, |
101 | - $square = false, $upscale = false) { |
|
101 | + $square = false, $upscale = false) { |
|
102 | 102 | |
103 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated. Use elgg_save_resized_image()', '2.3'); |
|
103 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated. Use elgg_save_resized_image()', '2.3'); |
|
104 | 104 | |
105 | - $files = _elgg_services()->request->files; |
|
106 | - if (!$files->has($input_name)) { |
|
107 | - return false; |
|
108 | - } |
|
105 | + $files = _elgg_services()->request->files; |
|
106 | + if (!$files->has($input_name)) { |
|
107 | + return false; |
|
108 | + } |
|
109 | 109 | |
110 | - $file = $files->get($input_name); |
|
111 | - if (empty($file)) { |
|
112 | - // a file input was provided but no file uploaded |
|
113 | - return false; |
|
114 | - } |
|
115 | - if ($file->getError() !== 0) { |
|
116 | - return false; |
|
117 | - } |
|
110 | + $file = $files->get($input_name); |
|
111 | + if (empty($file)) { |
|
112 | + // a file input was provided but no file uploaded |
|
113 | + return false; |
|
114 | + } |
|
115 | + if ($file->getError() !== 0) { |
|
116 | + return false; |
|
117 | + } |
|
118 | 118 | |
119 | - return get_resized_image_from_existing_file($file->getPathname(), $maxwidth, $maxheight, $square, 0, 0, 0, 0, $upscale); |
|
119 | + return get_resized_image_from_existing_file($file->getPathname(), $maxwidth, $maxheight, $square, 0, 0, 0, 0, $upscale); |
|
120 | 120 | } |
121 | 121 | |
122 | 122 | /** |
@@ -141,37 +141,37 @@ discard block |
||
141 | 141 | * @deprecated 2.3 |
142 | 142 | */ |
143 | 143 | function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight, |
144 | - $square = false, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = false) { |
|
144 | + $square = false, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = false) { |
|
145 | 145 | |
146 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated. Use elgg_save_resized_image()', '2.3'); |
|
146 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated. Use elgg_save_resized_image()', '2.3'); |
|
147 | 147 | |
148 | - if (!is_readable($input_name)) { |
|
149 | - return false; |
|
150 | - } |
|
148 | + if (!is_readable($input_name)) { |
|
149 | + return false; |
|
150 | + } |
|
151 | 151 | |
152 | - // we will write resized image to a temporary file and then delete it |
|
153 | - // need to add a valid image extension otherwise resizing fails |
|
154 | - $tmp_filename = tempnam(sys_get_temp_dir(), 'icon_resize'); |
|
152 | + // we will write resized image to a temporary file and then delete it |
|
153 | + // need to add a valid image extension otherwise resizing fails |
|
154 | + $tmp_filename = tempnam(sys_get_temp_dir(), 'icon_resize'); |
|
155 | 155 | |
156 | - $params = [ |
|
157 | - 'w' => $maxwidth, |
|
158 | - 'h' => $maxheight, |
|
159 | - 'x1' => $x1, |
|
160 | - 'y1' => $y1, |
|
161 | - 'x2' => $x2, |
|
162 | - 'y2' => $y2, |
|
163 | - 'square' => $square, |
|
164 | - 'upscale' => $upscale, |
|
165 | - ]; |
|
166 | - |
|
167 | - $image_bytes = false; |
|
168 | - if (elgg_save_resized_image($input_name, $tmp_filename, $params)) { |
|
169 | - $image_bytes = file_get_contents($tmp_filename); |
|
170 | - } |
|
171 | - |
|
172 | - unlink($tmp_filename); |
|
173 | - |
|
174 | - return $image_bytes; |
|
156 | + $params = [ |
|
157 | + 'w' => $maxwidth, |
|
158 | + 'h' => $maxheight, |
|
159 | + 'x1' => $x1, |
|
160 | + 'y1' => $y1, |
|
161 | + 'x2' => $x2, |
|
162 | + 'y2' => $y2, |
|
163 | + 'square' => $square, |
|
164 | + 'upscale' => $upscale, |
|
165 | + ]; |
|
166 | + |
|
167 | + $image_bytes = false; |
|
168 | + if (elgg_save_resized_image($input_name, $tmp_filename, $params)) { |
|
169 | + $image_bytes = file_get_contents($tmp_filename); |
|
170 | + } |
|
171 | + |
|
172 | + unlink($tmp_filename); |
|
173 | + |
|
174 | + return $image_bytes; |
|
175 | 175 | } |
176 | 176 | |
177 | 177 | /** |
@@ -192,26 +192,26 @@ discard block |
||
192 | 192 | */ |
193 | 193 | function get_image_resize_parameters($width, $height, array $params = []) { |
194 | 194 | |
195 | - elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated and will be removed from public API', '2.3'); |
|
196 | - |
|
197 | - try { |
|
198 | - $params['w'] = elgg_extract('maxwidth', $params); |
|
199 | - $params['h'] = elgg_extract('maxheight', $params); |
|
200 | - unset($params['maxwidth']); |
|
201 | - unset($params['maxheight']); |
|
202 | - $params = _elgg_services()->imageService->normalizeResizeParameters($width, $height, $params); |
|
203 | - return [ |
|
204 | - 'newwidth' => $params['w'], |
|
205 | - 'newheight' => $params['h'], |
|
206 | - 'selectionwidth' => $params['x2'] - $params['x1'], |
|
207 | - 'selectionheight' => $params['y2'] - $params['y1'], |
|
208 | - 'xoffset' => $params['x1'], |
|
209 | - 'yoffset' => $params['y1'], |
|
210 | - ]; |
|
211 | - } catch (\LogicException $ex) { |
|
212 | - elgg_log($ex->getMessage(), 'ERROR'); |
|
213 | - return false; |
|
214 | - } |
|
195 | + elgg_deprecated_notice(__FUNCTION__ . ' has been deprecated and will be removed from public API', '2.3'); |
|
196 | + |
|
197 | + try { |
|
198 | + $params['w'] = elgg_extract('maxwidth', $params); |
|
199 | + $params['h'] = elgg_extract('maxheight', $params); |
|
200 | + unset($params['maxwidth']); |
|
201 | + unset($params['maxheight']); |
|
202 | + $params = _elgg_services()->imageService->normalizeResizeParameters($width, $height, $params); |
|
203 | + return [ |
|
204 | + 'newwidth' => $params['w'], |
|
205 | + 'newheight' => $params['h'], |
|
206 | + 'selectionwidth' => $params['x2'] - $params['x1'], |
|
207 | + 'selectionheight' => $params['y2'] - $params['y1'], |
|
208 | + 'xoffset' => $params['x1'], |
|
209 | + 'yoffset' => $params['y1'], |
|
210 | + ]; |
|
211 | + } catch (\LogicException $ex) { |
|
212 | + elgg_log($ex->getMessage(), 'ERROR'); |
|
213 | + return false; |
|
214 | + } |
|
215 | 215 | } |
216 | 216 | |
217 | 217 | /** |
@@ -222,34 +222,34 @@ discard block |
||
222 | 222 | * @return bool |
223 | 223 | */ |
224 | 224 | function file_delete($guid) { |
225 | - $file = get_entity($guid); |
|
226 | - if (!$file || !$file->canEdit()) { |
|
227 | - return false; |
|
228 | - } |
|
229 | - |
|
230 | - $thumbnail = $file->thumbnail; |
|
231 | - $smallthumb = $file->smallthumb; |
|
232 | - $largethumb = $file->largethumb; |
|
233 | - if ($thumbnail) { |
|
234 | - $delfile = new \ElggFile(); |
|
235 | - $delfile->owner_guid = $file->owner_guid; |
|
236 | - $delfile->setFilename($thumbnail); |
|
237 | - $delfile->delete(); |
|
238 | - } |
|
239 | - if ($smallthumb) { |
|
240 | - $delfile = new \ElggFile(); |
|
241 | - $delfile->owner_guid = $file->owner_guid; |
|
242 | - $delfile->setFilename($smallthumb); |
|
243 | - $delfile->delete(); |
|
244 | - } |
|
245 | - if ($largethumb) { |
|
246 | - $delfile = new \ElggFile(); |
|
247 | - $delfile->owner_guid = $file->owner_guid; |
|
248 | - $delfile->setFilename($largethumb); |
|
249 | - $delfile->delete(); |
|
250 | - } |
|
251 | - |
|
252 | - return $file->delete(); |
|
225 | + $file = get_entity($guid); |
|
226 | + if (!$file || !$file->canEdit()) { |
|
227 | + return false; |
|
228 | + } |
|
229 | + |
|
230 | + $thumbnail = $file->thumbnail; |
|
231 | + $smallthumb = $file->smallthumb; |
|
232 | + $largethumb = $file->largethumb; |
|
233 | + if ($thumbnail) { |
|
234 | + $delfile = new \ElggFile(); |
|
235 | + $delfile->owner_guid = $file->owner_guid; |
|
236 | + $delfile->setFilename($thumbnail); |
|
237 | + $delfile->delete(); |
|
238 | + } |
|
239 | + if ($smallthumb) { |
|
240 | + $delfile = new \ElggFile(); |
|
241 | + $delfile->owner_guid = $file->owner_guid; |
|
242 | + $delfile->setFilename($smallthumb); |
|
243 | + $delfile->delete(); |
|
244 | + } |
|
245 | + if ($largethumb) { |
|
246 | + $delfile = new \ElggFile(); |
|
247 | + $delfile->owner_guid = $file->owner_guid; |
|
248 | + $delfile->setFilename($largethumb); |
|
249 | + $delfile->delete(); |
|
250 | + } |
|
251 | + |
|
252 | + return $file->delete(); |
|
253 | 253 | } |
254 | 254 | |
255 | 255 | /** |
@@ -260,32 +260,32 @@ discard block |
||
260 | 260 | * @return bool |
261 | 261 | */ |
262 | 262 | function delete_directory($directory) { |
263 | - // sanity check: must be a directory |
|
264 | - if (!$handle = opendir($directory)) { |
|
265 | - return false; |
|
266 | - } |
|
267 | - |
|
268 | - // loop through all files |
|
269 | - while (($file = readdir($handle)) !== false) { |
|
270 | - if (in_array($file, ['.', '..'])) { |
|
271 | - continue; |
|
272 | - } |
|
273 | - |
|
274 | - $path = "$directory/$file"; |
|
275 | - if (is_dir($path)) { |
|
276 | - // recurse down through directory |
|
277 | - if (!delete_directory($path)) { |
|
278 | - return false; |
|
279 | - } |
|
280 | - } else { |
|
281 | - // delete file |
|
282 | - unlink($path); |
|
283 | - } |
|
284 | - } |
|
285 | - |
|
286 | - // remove empty directory |
|
287 | - closedir($handle); |
|
288 | - return rmdir($directory); |
|
263 | + // sanity check: must be a directory |
|
264 | + if (!$handle = opendir($directory)) { |
|
265 | + return false; |
|
266 | + } |
|
267 | + |
|
268 | + // loop through all files |
|
269 | + while (($file = readdir($handle)) !== false) { |
|
270 | + if (in_array($file, ['.', '..'])) { |
|
271 | + continue; |
|
272 | + } |
|
273 | + |
|
274 | + $path = "$directory/$file"; |
|
275 | + if (is_dir($path)) { |
|
276 | + // recurse down through directory |
|
277 | + if (!delete_directory($path)) { |
|
278 | + return false; |
|
279 | + } |
|
280 | + } else { |
|
281 | + // delete file |
|
282 | + unlink($path); |
|
283 | + } |
|
284 | + } |
|
285 | + |
|
286 | + // remove empty directory |
|
287 | + closedir($handle); |
|
288 | + return rmdir($directory); |
|
289 | 289 | } |
290 | 290 | |
291 | 291 | /** |
@@ -304,11 +304,11 @@ discard block |
||
304 | 304 | * @access private |
305 | 305 | */ |
306 | 306 | function _elgg_clear_entity_files($entity) { |
307 | - $dir = new \Elgg\EntityDirLocator($entity->guid); |
|
308 | - $file_path = elgg_get_config('dataroot') . $dir; |
|
309 | - if (file_exists($file_path)) { |
|
310 | - delete_directory($file_path); |
|
311 | - } |
|
307 | + $dir = new \Elgg\EntityDirLocator($entity->guid); |
|
308 | + $file_path = elgg_get_config('dataroot') . $dir; |
|
309 | + if (file_exists($file_path)) { |
|
310 | + delete_directory($file_path); |
|
311 | + } |
|
312 | 312 | } |
313 | 313 | |
314 | 314 | /** |
@@ -320,8 +320,8 @@ discard block |
||
320 | 320 | * @since 1.10 |
321 | 321 | */ |
322 | 322 | function elgg_get_file_simple_type($mime_type) { |
323 | - $params = ['mime_type' => $mime_type]; |
|
324 | - return elgg_trigger_plugin_hook('simple_type', 'file', $params, 'general'); |
|
323 | + $params = ['mime_type' => $mime_type]; |
|
324 | + return elgg_trigger_plugin_hook('simple_type', 'file', $params, 'general'); |
|
325 | 325 | } |
326 | 326 | |
327 | 327 | /** |
@@ -332,25 +332,25 @@ discard block |
||
332 | 332 | */ |
333 | 333 | function _elgg_filestore_init() { |
334 | 334 | |
335 | - // Fix MIME type detection for Microsoft zipped formats |
|
336 | - elgg_register_plugin_hook_handler('mime_type', 'file', '_elgg_filestore_detect_mimetype'); |
|
335 | + // Fix MIME type detection for Microsoft zipped formats |
|
336 | + elgg_register_plugin_hook_handler('mime_type', 'file', '_elgg_filestore_detect_mimetype'); |
|
337 | 337 | |
338 | - // Parse category of file from MIME type |
|
339 | - elgg_register_plugin_hook_handler('simple_type', 'file', '_elgg_filestore_parse_simpletype'); |
|
338 | + // Parse category of file from MIME type |
|
339 | + elgg_register_plugin_hook_handler('simple_type', 'file', '_elgg_filestore_parse_simpletype'); |
|
340 | 340 | |
341 | - // Unit testing |
|
342 | - elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_filestore_test'); |
|
341 | + // Unit testing |
|
342 | + elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_filestore_test'); |
|
343 | 343 | |
344 | - // Handler for serving embedded icons |
|
345 | - elgg_register_page_handler('serve-icon', '_elgg_filestore_serve_icon_handler'); |
|
344 | + // Handler for serving embedded icons |
|
345 | + elgg_register_page_handler('serve-icon', '_elgg_filestore_serve_icon_handler'); |
|
346 | 346 | |
347 | - // Touch entity icons if entity access id has changed |
|
348 | - elgg_register_event_handler('update:after', 'object', '_elgg_filestore_touch_icons'); |
|
349 | - elgg_register_event_handler('update:after', 'group', '_elgg_filestore_touch_icons'); |
|
347 | + // Touch entity icons if entity access id has changed |
|
348 | + elgg_register_event_handler('update:after', 'object', '_elgg_filestore_touch_icons'); |
|
349 | + elgg_register_event_handler('update:after', 'group', '_elgg_filestore_touch_icons'); |
|
350 | 350 | |
351 | - // Move entity icons if entity owner has changed |
|
352 | - elgg_register_event_handler('update:after', 'object', '_elgg_filestore_move_icons'); |
|
353 | - elgg_register_event_handler('update:after', 'group', '_elgg_filestore_move_icons'); |
|
351 | + // Move entity icons if entity owner has changed |
|
352 | + elgg_register_event_handler('update:after', 'object', '_elgg_filestore_move_icons'); |
|
353 | + elgg_register_event_handler('update:after', 'group', '_elgg_filestore_move_icons'); |
|
354 | 354 | } |
355 | 355 | |
356 | 356 | /** |
@@ -366,10 +366,10 @@ discard block |
||
366 | 366 | */ |
367 | 367 | function _elgg_filestore_detect_mimetype($hook, $type, $mime_type, $params) { |
368 | 368 | |
369 | - $original_filename = elgg_extract('original_filename', $params); |
|
370 | - $ext = pathinfo($original_filename, PATHINFO_EXTENSION); |
|
369 | + $original_filename = elgg_extract('original_filename', $params); |
|
370 | + $ext = pathinfo($original_filename, PATHINFO_EXTENSION); |
|
371 | 371 | |
372 | - return (new \Elgg\Filesystem\MimeTypeDetector())->fixDetectionErrors($mime_type, $ext); |
|
372 | + return (new \Elgg\Filesystem\MimeTypeDetector())->fixDetectionErrors($mime_type, $ext); |
|
373 | 373 | } |
374 | 374 | |
375 | 375 | /** |
@@ -385,27 +385,27 @@ discard block |
||
385 | 385 | */ |
386 | 386 | function _elgg_filestore_parse_simpletype($hook, $type, $simple_type, $params) { |
387 | 387 | |
388 | - $mime_type = elgg_extract('mime_type', $params); |
|
388 | + $mime_type = elgg_extract('mime_type', $params); |
|
389 | 389 | |
390 | - switch ($mime_type) { |
|
391 | - case "application/msword": |
|
392 | - case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": |
|
393 | - case "application/pdf": |
|
394 | - return "document"; |
|
390 | + switch ($mime_type) { |
|
391 | + case "application/msword": |
|
392 | + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": |
|
393 | + case "application/pdf": |
|
394 | + return "document"; |
|
395 | 395 | |
396 | - case "application/ogg": |
|
397 | - return "audio"; |
|
398 | - } |
|
396 | + case "application/ogg": |
|
397 | + return "audio"; |
|
398 | + } |
|
399 | 399 | |
400 | - if (preg_match('~^(audio|image|video)/~', $mime_type, $m)) { |
|
401 | - return $m[1]; |
|
402 | - } |
|
403 | - if (0 === strpos($mime_type, 'text/') || false !== strpos($mime_type, 'opendocument')) { |
|
404 | - return "document"; |
|
405 | - } |
|
400 | + if (preg_match('~^(audio|image|video)/~', $mime_type, $m)) { |
|
401 | + return $m[1]; |
|
402 | + } |
|
403 | + if (0 === strpos($mime_type, 'text/') || false !== strpos($mime_type, 'opendocument')) { |
|
404 | + return "document"; |
|
405 | + } |
|
406 | 406 | |
407 | - // unrecognized MIME |
|
408 | - return $simple_type; |
|
407 | + // unrecognized MIME |
|
408 | + return $simple_type; |
|
409 | 409 | } |
410 | 410 | |
411 | 411 | /** |
@@ -419,9 +419,9 @@ discard block |
||
419 | 419 | * @access private |
420 | 420 | */ |
421 | 421 | function _elgg_filestore_test($hook, $type, $value) { |
422 | - global $CONFIG; |
|
423 | - $value[] = "{$CONFIG->path}engine/tests/ElggCoreFilestoreTest.php"; |
|
424 | - return $value; |
|
422 | + global $CONFIG; |
|
423 | + $value[] = "{$CONFIG->path}engine/tests/ElggCoreFilestoreTest.php"; |
|
424 | + return $value; |
|
425 | 425 | } |
426 | 426 | |
427 | 427 | /** |
@@ -435,12 +435,12 @@ discard block |
||
435 | 435 | * @return string |
436 | 436 | */ |
437 | 437 | function elgg_get_download_url(\ElggFile $file, $use_cookie = true, $expires = '+2 hours') { |
438 | - $file_svc = new Elgg\FileService\File(); |
|
439 | - $file_svc->setFile($file); |
|
440 | - $file_svc->setExpires($expires); |
|
441 | - $file_svc->setDisposition('attachment'); |
|
442 | - $file_svc->bindSession($use_cookie); |
|
443 | - return $file_svc->getURL(); |
|
438 | + $file_svc = new Elgg\FileService\File(); |
|
439 | + $file_svc->setFile($file); |
|
440 | + $file_svc->setExpires($expires); |
|
441 | + $file_svc->setDisposition('attachment'); |
|
442 | + $file_svc->bindSession($use_cookie); |
|
443 | + return $file_svc->getURL(); |
|
444 | 444 | } |
445 | 445 | |
446 | 446 | /** |
@@ -455,14 +455,14 @@ discard block |
||
455 | 455 | * @return string |
456 | 456 | */ |
457 | 457 | function elgg_get_inline_url(\ElggFile $file, $use_cookie = false, $expires = '') { |
458 | - $file_svc = new Elgg\FileService\File(); |
|
459 | - $file_svc->setFile($file); |
|
460 | - if ($expires) { |
|
461 | - $file_svc->setExpires($expires); |
|
462 | - } |
|
463 | - $file_svc->setDisposition('inline'); |
|
464 | - $file_svc->bindSession($use_cookie); |
|
465 | - return $file_svc->getURL(); |
|
458 | + $file_svc = new Elgg\FileService\File(); |
|
459 | + $file_svc->setFile($file); |
|
460 | + if ($expires) { |
|
461 | + $file_svc->setExpires($expires); |
|
462 | + } |
|
463 | + $file_svc->setDisposition('inline'); |
|
464 | + $file_svc->bindSession($use_cookie); |
|
465 | + return $file_svc->getURL(); |
|
466 | 466 | } |
467 | 467 | |
468 | 468 | /** |
@@ -478,7 +478,7 @@ discard block |
||
478 | 478 | * @since 2.2 |
479 | 479 | */ |
480 | 480 | function elgg_get_embed_url(\ElggEntity $entity, $size) { |
481 | - return elgg_normalize_url("serve-icon/$entity->guid/$size"); |
|
481 | + return elgg_normalize_url("serve-icon/$entity->guid/$size"); |
|
482 | 482 | } |
483 | 483 | |
484 | 484 | /** |
@@ -490,9 +490,9 @@ discard block |
||
490 | 490 | * @since 2.2 |
491 | 491 | */ |
492 | 492 | function _elgg_filestore_serve_icon_handler() { |
493 | - $response = _elgg_services()->iconService->handleServeIconRequest(); |
|
494 | - $response->send(); |
|
495 | - exit; |
|
493 | + $response = _elgg_services()->iconService->handleServeIconRequest(); |
|
494 | + $response->send(); |
|
495 | + exit; |
|
496 | 496 | } |
497 | 497 | |
498 | 498 | /** |
@@ -505,21 +505,21 @@ discard block |
||
505 | 505 | * @access private |
506 | 506 | */ |
507 | 507 | function _elgg_filestore_touch_icons($event, $type, $entity) { |
508 | - $original_attributes = $entity->getOriginalAttributes(); |
|
509 | - if (!array_key_exists('access_id', $original_attributes)) { |
|
510 | - return; |
|
511 | - } |
|
512 | - if ($entity instanceof \ElggFile) { |
|
513 | - // we touch the file to invalidate any previously generated download URLs |
|
514 | - $entity->setModifiedTime(); |
|
515 | - } |
|
516 | - $sizes = array_keys(elgg_get_icon_sizes($entity->getType(), $entity->getSubtype())); |
|
517 | - foreach ($sizes as $size) { |
|
518 | - $icon = $entity->getIcon($size); |
|
519 | - if ($icon->exists()) { |
|
520 | - $icon->setModifiedTime(); |
|
521 | - } |
|
522 | - } |
|
508 | + $original_attributes = $entity->getOriginalAttributes(); |
|
509 | + if (!array_key_exists('access_id', $original_attributes)) { |
|
510 | + return; |
|
511 | + } |
|
512 | + if ($entity instanceof \ElggFile) { |
|
513 | + // we touch the file to invalidate any previously generated download URLs |
|
514 | + $entity->setModifiedTime(); |
|
515 | + } |
|
516 | + $sizes = array_keys(elgg_get_icon_sizes($entity->getType(), $entity->getSubtype())); |
|
517 | + foreach ($sizes as $size) { |
|
518 | + $icon = $entity->getIcon($size); |
|
519 | + if ($icon->exists()) { |
|
520 | + $icon->setModifiedTime(); |
|
521 | + } |
|
522 | + } |
|
523 | 523 | } |
524 | 524 | |
525 | 525 | /** |
@@ -540,50 +540,50 @@ discard block |
||
540 | 540 | */ |
541 | 541 | function _elgg_filestore_move_icons($event, $type, $entity) { |
542 | 542 | |
543 | - $original_attributes = $entity->getOriginalAttributes(); |
|
544 | - if (empty($original_attributes['owner_guid'])) { |
|
545 | - return; |
|
546 | - } |
|
547 | - |
|
548 | - $previous_owner_guid = $original_attributes['owner_guid']; |
|
549 | - $new_owner_guid = $entity->owner_guid; |
|
550 | - |
|
551 | - $sizes = elgg_get_icon_sizes($entity->getType(), $entity->getSubtype()); |
|
552 | - |
|
553 | - foreach ($sizes as $size => $opts) { |
|
554 | - $new_icon = $entity->getIcon($size); |
|
555 | - if ($new_icon->owner_guid == $entity->guid) { |
|
556 | - // we do not need to update icons that are owned by the entity itself |
|
557 | - continue; |
|
558 | - } |
|
559 | - |
|
560 | - if ($new_icon->owner_guid != $new_owner_guid) { |
|
561 | - // a plugin implements some custom logic |
|
562 | - continue; |
|
563 | - } |
|
564 | - |
|
565 | - $old_icon = new \ElggIcon(); |
|
566 | - $old_icon->owner_guid = $previous_owner_guid; |
|
567 | - $old_icon->setFilename($new_icon->getFilename()); |
|
568 | - if (!$old_icon->exists()) { |
|
569 | - // there is no icon to move |
|
570 | - continue; |
|
571 | - } |
|
572 | - |
|
573 | - if ($new_icon->exists()) { |
|
574 | - // there is already a new icon |
|
575 | - // just removing the old one |
|
576 | - $old_icon->delete(); |
|
577 | - elgg_log("Entity $entity->guid has been transferred to a new owner but an icon was " |
|
578 | - . "left behind under {$old_icon->getFilenameOnFilestore()}. " |
|
579 | - . "Old icon has been deleted", 'NOTICE'); |
|
580 | - continue; |
|
581 | - } |
|
582 | - |
|
583 | - $old_icon->transfer($new_icon->owner_guid, $new_icon->getFilename()); |
|
584 | - elgg_log("Entity $entity->guid has been transferred to a new owner. " |
|
585 | - . "Icon was moved from {$old_icon->getFilenameOnFilestore()} to {$new_icon->getFilenameOnFilestore()}.", 'NOTICE'); |
|
586 | - } |
|
543 | + $original_attributes = $entity->getOriginalAttributes(); |
|
544 | + if (empty($original_attributes['owner_guid'])) { |
|
545 | + return; |
|
546 | + } |
|
547 | + |
|
548 | + $previous_owner_guid = $original_attributes['owner_guid']; |
|
549 | + $new_owner_guid = $entity->owner_guid; |
|
550 | + |
|
551 | + $sizes = elgg_get_icon_sizes($entity->getType(), $entity->getSubtype()); |
|
552 | + |
|
553 | + foreach ($sizes as $size => $opts) { |
|
554 | + $new_icon = $entity->getIcon($size); |
|
555 | + if ($new_icon->owner_guid == $entity->guid) { |
|
556 | + // we do not need to update icons that are owned by the entity itself |
|
557 | + continue; |
|
558 | + } |
|
559 | + |
|
560 | + if ($new_icon->owner_guid != $new_owner_guid) { |
|
561 | + // a plugin implements some custom logic |
|
562 | + continue; |
|
563 | + } |
|
564 | + |
|
565 | + $old_icon = new \ElggIcon(); |
|
566 | + $old_icon->owner_guid = $previous_owner_guid; |
|
567 | + $old_icon->setFilename($new_icon->getFilename()); |
|
568 | + if (!$old_icon->exists()) { |
|
569 | + // there is no icon to move |
|
570 | + continue; |
|
571 | + } |
|
572 | + |
|
573 | + if ($new_icon->exists()) { |
|
574 | + // there is already a new icon |
|
575 | + // just removing the old one |
|
576 | + $old_icon->delete(); |
|
577 | + elgg_log("Entity $entity->guid has been transferred to a new owner but an icon was " |
|
578 | + . "left behind under {$old_icon->getFilenameOnFilestore()}. " |
|
579 | + . "Old icon has been deleted", 'NOTICE'); |
|
580 | + continue; |
|
581 | + } |
|
582 | + |
|
583 | + $old_icon->transfer($new_icon->owner_guid, $new_icon->getFilename()); |
|
584 | + elgg_log("Entity $entity->guid has been transferred to a new owner. " |
|
585 | + . "Icon was moved from {$old_icon->getFilenameOnFilestore()} to {$new_icon->getFilenameOnFilestore()}.", 'NOTICE'); |
|
586 | + } |
|
587 | 587 | } |
588 | 588 | |
589 | 589 | /** |
@@ -593,9 +593,9 @@ discard block |
||
593 | 593 | * @return UploadedFile[]|false |
594 | 594 | */ |
595 | 595 | function elgg_get_uploaded_files($input_name) { |
596 | - return _elgg_services()->uploads->getUploadedFiles($input_name); |
|
596 | + return _elgg_services()->uploads->getUploadedFiles($input_name); |
|
597 | 597 | } |
598 | 598 | |
599 | 599 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
600 | - $events->registerHandler('init', 'system', '_elgg_filestore_init', 100); |
|
600 | + $events->registerHandler('init', 'system', '_elgg_filestore_init', 100); |
|
601 | 601 | }; |
@@ -91,29 +91,29 @@ discard block |
||
91 | 91 | * @since 1.8.0 |
92 | 92 | */ |
93 | 93 | function elgg_register_menu_item($menu_name, $menu_item) { |
94 | - global $CONFIG; |
|
95 | - |
|
96 | - if (is_array($menu_item)) { |
|
97 | - $options = $menu_item; |
|
98 | - $menu_item = \ElggMenuItem::factory($options); |
|
99 | - if (!$menu_item) { |
|
100 | - $menu_item_name = elgg_extract('name', $options, 'MISSING NAME'); |
|
101 | - elgg_log("Unable to add menu item '{$menu_item_name}' to '$menu_name' menu", 'WARNING'); |
|
102 | - return false; |
|
103 | - } |
|
104 | - } |
|
105 | - |
|
106 | - if (!$menu_item instanceof ElggMenuItem) { |
|
107 | - elgg_log('Second argument of elgg_register_menu_item() must be an instance of ' |
|
108 | - . 'ElggMenuItem or an array of menu item factory options', 'ERROR'); |
|
109 | - return false; |
|
110 | - } |
|
111 | - |
|
112 | - if (!isset($CONFIG->menus[$menu_name])) { |
|
113 | - $CONFIG->menus[$menu_name] = []; |
|
114 | - } |
|
115 | - $CONFIG->menus[$menu_name][] = $menu_item; |
|
116 | - return true; |
|
94 | + global $CONFIG; |
|
95 | + |
|
96 | + if (is_array($menu_item)) { |
|
97 | + $options = $menu_item; |
|
98 | + $menu_item = \ElggMenuItem::factory($options); |
|
99 | + if (!$menu_item) { |
|
100 | + $menu_item_name = elgg_extract('name', $options, 'MISSING NAME'); |
|
101 | + elgg_log("Unable to add menu item '{$menu_item_name}' to '$menu_name' menu", 'WARNING'); |
|
102 | + return false; |
|
103 | + } |
|
104 | + } |
|
105 | + |
|
106 | + if (!$menu_item instanceof ElggMenuItem) { |
|
107 | + elgg_log('Second argument of elgg_register_menu_item() must be an instance of ' |
|
108 | + . 'ElggMenuItem or an array of menu item factory options', 'ERROR'); |
|
109 | + return false; |
|
110 | + } |
|
111 | + |
|
112 | + if (!isset($CONFIG->menus[$menu_name])) { |
|
113 | + $CONFIG->menus[$menu_name] = []; |
|
114 | + } |
|
115 | + $CONFIG->menus[$menu_name][] = $menu_item; |
|
116 | + return true; |
|
117 | 117 | } |
118 | 118 | |
119 | 119 | /** |
@@ -126,22 +126,22 @@ discard block |
||
126 | 126 | * @since 1.8.0 |
127 | 127 | */ |
128 | 128 | function elgg_unregister_menu_item($menu_name, $item_name) { |
129 | - global $CONFIG; |
|
130 | - |
|
131 | - if (empty($CONFIG->menus[$menu_name])) { |
|
132 | - return null; |
|
133 | - } |
|
134 | - |
|
135 | - foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { |
|
136 | - /* @var \ElggMenuItem $menu_object */ |
|
137 | - if ($menu_object instanceof ElggMenuItem && $menu_object->getName() == $item_name) { |
|
138 | - $item = $CONFIG->menus[$menu_name][$index]; |
|
139 | - unset($CONFIG->menus[$menu_name][$index]); |
|
140 | - return $item; |
|
141 | - } |
|
142 | - } |
|
143 | - |
|
144 | - return null; |
|
129 | + global $CONFIG; |
|
130 | + |
|
131 | + if (empty($CONFIG->menus[$menu_name])) { |
|
132 | + return null; |
|
133 | + } |
|
134 | + |
|
135 | + foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { |
|
136 | + /* @var \ElggMenuItem $menu_object */ |
|
137 | + if ($menu_object instanceof ElggMenuItem && $menu_object->getName() == $item_name) { |
|
138 | + $item = $CONFIG->menus[$menu_name][$index]; |
|
139 | + unset($CONFIG->menus[$menu_name][$index]); |
|
140 | + return $item; |
|
141 | + } |
|
142 | + } |
|
143 | + |
|
144 | + return null; |
|
145 | 145 | } |
146 | 146 | |
147 | 147 | /** |
@@ -154,20 +154,20 @@ discard block |
||
154 | 154 | * @since 1.8.0 |
155 | 155 | */ |
156 | 156 | function elgg_is_menu_item_registered($menu_name, $item_name) { |
157 | - global $CONFIG; |
|
157 | + global $CONFIG; |
|
158 | 158 | |
159 | - if (!isset($CONFIG->menus[$menu_name])) { |
|
160 | - return false; |
|
161 | - } |
|
159 | + if (!isset($CONFIG->menus[$menu_name])) { |
|
160 | + return false; |
|
161 | + } |
|
162 | 162 | |
163 | - foreach ($CONFIG->menus[$menu_name] as $menu_object) { |
|
164 | - /* @var \ElggMenuItem $menu_object */ |
|
165 | - if ($menu_object->getName() == $item_name) { |
|
166 | - return true; |
|
167 | - } |
|
168 | - } |
|
163 | + foreach ($CONFIG->menus[$menu_name] as $menu_object) { |
|
164 | + /* @var \ElggMenuItem $menu_object */ |
|
165 | + if ($menu_object->getName() == $item_name) { |
|
166 | + return true; |
|
167 | + } |
|
168 | + } |
|
169 | 169 | |
170 | - return false; |
|
170 | + return false; |
|
171 | 171 | } |
172 | 172 | |
173 | 173 | /** |
@@ -180,20 +180,20 @@ discard block |
||
180 | 180 | * @since 1.9.0 |
181 | 181 | */ |
182 | 182 | function elgg_get_menu_item($menu_name, $item_name) { |
183 | - global $CONFIG; |
|
183 | + global $CONFIG; |
|
184 | 184 | |
185 | - if (!isset($CONFIG->menus[$menu_name])) { |
|
186 | - return null; |
|
187 | - } |
|
185 | + if (!isset($CONFIG->menus[$menu_name])) { |
|
186 | + return null; |
|
187 | + } |
|
188 | 188 | |
189 | - foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { |
|
190 | - /* @var \ElggMenuItem $menu_object */ |
|
191 | - if ($menu_object->getName() == $item_name) { |
|
192 | - return $CONFIG->menus[$menu_name][$index]; |
|
193 | - } |
|
194 | - } |
|
189 | + foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { |
|
190 | + /* @var \ElggMenuItem $menu_object */ |
|
191 | + if ($menu_object->getName() == $item_name) { |
|
192 | + return $CONFIG->menus[$menu_name][$index]; |
|
193 | + } |
|
194 | + } |
|
195 | 195 | |
196 | - return null; |
|
196 | + return null; |
|
197 | 197 | } |
198 | 198 | |
199 | 199 | /** |
@@ -214,25 +214,25 @@ discard block |
||
214 | 214 | */ |
215 | 215 | function elgg_register_title_button($handler = null, $name = 'add', $entity_type = 'all', $entity_subtype = 'all') { |
216 | 216 | |
217 | - if (!$handler) { |
|
218 | - $handler = elgg_get_context(); |
|
219 | - } |
|
220 | - |
|
221 | - $owner = elgg_get_page_owner_entity(); |
|
222 | - if (!$owner) { |
|
223 | - // noone owns the page so this is probably an all site list page |
|
224 | - $owner = elgg_get_logged_in_user_entity(); |
|
225 | - } |
|
226 | - if (!$owner || !$owner->canWriteToContainer(0, $entity_type, $entity_subtype)) { |
|
227 | - return; |
|
228 | - } |
|
229 | - |
|
230 | - elgg_register_menu_item('title', [ |
|
231 | - 'name' => $name, |
|
232 | - 'href' => "$handler/$name/$owner->guid", |
|
233 | - 'text' => elgg_echo("$handler:$name"), |
|
234 | - 'link_class' => 'elgg-button elgg-button-action', |
|
235 | - ]); |
|
217 | + if (!$handler) { |
|
218 | + $handler = elgg_get_context(); |
|
219 | + } |
|
220 | + |
|
221 | + $owner = elgg_get_page_owner_entity(); |
|
222 | + if (!$owner) { |
|
223 | + // noone owns the page so this is probably an all site list page |
|
224 | + $owner = elgg_get_logged_in_user_entity(); |
|
225 | + } |
|
226 | + if (!$owner || !$owner->canWriteToContainer(0, $entity_type, $entity_subtype)) { |
|
227 | + return; |
|
228 | + } |
|
229 | + |
|
230 | + elgg_register_menu_item('title', [ |
|
231 | + 'name' => $name, |
|
232 | + 'href' => "$handler/$name/$owner->guid", |
|
233 | + 'text' => elgg_echo("$handler:$name"), |
|
234 | + 'link_class' => 'elgg-button elgg-button-action', |
|
235 | + ]); |
|
236 | 236 | } |
237 | 237 | |
238 | 238 | /** |
@@ -249,9 +249,9 @@ discard block |
||
249 | 249 | * @see elgg_get_breadcrumbs |
250 | 250 | */ |
251 | 251 | function elgg_push_breadcrumb($title, $link = null) { |
252 | - $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
253 | - $breadcrumbs[] = ['title' => $title, 'link' => $link]; |
|
254 | - elgg_set_config('breadcrumbs', $breadcrumbs); |
|
252 | + $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
253 | + $breadcrumbs[] = ['title' => $title, 'link' => $link]; |
|
254 | + elgg_set_config('breadcrumbs', $breadcrumbs); |
|
255 | 255 | } |
256 | 256 | |
257 | 257 | /** |
@@ -261,16 +261,16 @@ discard block |
||
261 | 261 | * @since 1.8.0 |
262 | 262 | */ |
263 | 263 | function elgg_pop_breadcrumb() { |
264 | - $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
264 | + $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
265 | 265 | |
266 | - if (empty($breadcrumbs)) { |
|
267 | - return []; |
|
268 | - } |
|
266 | + if (empty($breadcrumbs)) { |
|
267 | + return []; |
|
268 | + } |
|
269 | 269 | |
270 | - $popped = array_pop($breadcrumbs); |
|
271 | - elgg_set_config('breadcrumbs', $breadcrumbs); |
|
270 | + $popped = array_pop($breadcrumbs); |
|
271 | + elgg_set_config('breadcrumbs', $breadcrumbs); |
|
272 | 272 | |
273 | - return $popped; |
|
273 | + return $popped; |
|
274 | 274 | } |
275 | 275 | |
276 | 276 | /** |
@@ -294,31 +294,31 @@ discard block |
||
294 | 294 | * @see elgg_prepare_breadcrumbs |
295 | 295 | */ |
296 | 296 | function elgg_get_breadcrumbs(array $breadcrumbs = null) { |
297 | - if (!isset($breadcrumbs)) { |
|
298 | - // if no crumbs set, still allow hook to populate it |
|
299 | - $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
300 | - } |
|
301 | - |
|
302 | - if (!is_array($breadcrumbs)) { |
|
303 | - _elgg_services()->logger->error(__FUNCTION__ . ' expects breadcrumbs as an array'); |
|
304 | - $breadcrumbs = []; |
|
305 | - } |
|
297 | + if (!isset($breadcrumbs)) { |
|
298 | + // if no crumbs set, still allow hook to populate it |
|
299 | + $breadcrumbs = (array) elgg_get_config('breadcrumbs'); |
|
300 | + } |
|
301 | + |
|
302 | + if (!is_array($breadcrumbs)) { |
|
303 | + _elgg_services()->logger->error(__FUNCTION__ . ' expects breadcrumbs as an array'); |
|
304 | + $breadcrumbs = []; |
|
305 | + } |
|
306 | 306 | |
307 | - $params = [ |
|
308 | - 'breadcrumbs' => $breadcrumbs, |
|
309 | - ]; |
|
307 | + $params = [ |
|
308 | + 'breadcrumbs' => $breadcrumbs, |
|
309 | + ]; |
|
310 | 310 | |
311 | - $params['identifier'] = _elgg_services()->request->getFirstUrlSegment(); |
|
312 | - $params['segments'] = _elgg_services()->request->getUrlSegments(); |
|
313 | - array_shift($params['segments']); |
|
311 | + $params['identifier'] = _elgg_services()->request->getFirstUrlSegment(); |
|
312 | + $params['segments'] = _elgg_services()->request->getUrlSegments(); |
|
313 | + array_shift($params['segments']); |
|
314 | 314 | |
315 | - $breadcrumbs = elgg_trigger_plugin_hook('prepare', 'breadcrumbs', $params, $breadcrumbs); |
|
316 | - if (!is_array($breadcrumbs)) { |
|
317 | - _elgg_services()->logger->error('"prepare, breadcrumbs" hook must return an array of breadcrumbs'); |
|
318 | - return []; |
|
319 | - } |
|
315 | + $breadcrumbs = elgg_trigger_plugin_hook('prepare', 'breadcrumbs', $params, $breadcrumbs); |
|
316 | + if (!is_array($breadcrumbs)) { |
|
317 | + _elgg_services()->logger->error('"prepare, breadcrumbs" hook must return an array of breadcrumbs'); |
|
318 | + return []; |
|
319 | + } |
|
320 | 320 | |
321 | - return $breadcrumbs; |
|
321 | + return $breadcrumbs; |
|
322 | 322 | } |
323 | 323 | |
324 | 324 | /** |
@@ -334,17 +334,17 @@ discard block |
||
334 | 334 | * @since 1.11 |
335 | 335 | */ |
336 | 336 | function elgg_prepare_breadcrumbs($hook, $type, $breadcrumbs, $params) { |
337 | - // remove last crumb if not a link |
|
338 | - $last_crumb = end($breadcrumbs); |
|
339 | - if (empty($last_crumb['link'])) { |
|
340 | - array_pop($breadcrumbs); |
|
341 | - } |
|
342 | - |
|
343 | - // apply excerpt to titles |
|
344 | - foreach (array_keys($breadcrumbs) as $i) { |
|
345 | - $breadcrumbs[$i]['title'] = elgg_get_excerpt($breadcrumbs[$i]['title'], 100); |
|
346 | - } |
|
347 | - return $breadcrumbs; |
|
337 | + // remove last crumb if not a link |
|
338 | + $last_crumb = end($breadcrumbs); |
|
339 | + if (empty($last_crumb['link'])) { |
|
340 | + array_pop($breadcrumbs); |
|
341 | + } |
|
342 | + |
|
343 | + // apply excerpt to titles |
|
344 | + foreach (array_keys($breadcrumbs) as $i) { |
|
345 | + $breadcrumbs[$i]['title'] = elgg_get_excerpt($breadcrumbs[$i]['title'], 100); |
|
346 | + } |
|
347 | + return $breadcrumbs; |
|
348 | 348 | } |
349 | 349 | |
350 | 350 | /** |
@@ -359,40 +359,40 @@ discard block |
||
359 | 359 | */ |
360 | 360 | function elgg_get_filter_tabs($context = null, $selected = null, ElggUser $user = null, array $vars = []) { |
361 | 361 | |
362 | - if (!isset($selected)) { |
|
363 | - $selected = 'all'; |
|
364 | - } |
|
365 | - |
|
366 | - if (!$user) { |
|
367 | - $user = elgg_get_logged_in_user_entity(); |
|
368 | - } |
|
369 | - |
|
370 | - $items = []; |
|
371 | - if ($user) { |
|
372 | - $items[] = ElggMenuItem::factory([ |
|
373 | - 'name' => 'all', |
|
374 | - 'text' => elgg_echo('all'), |
|
375 | - 'href' => (isset($vars['all_link'])) ? $vars['all_link'] : "$context/all", |
|
376 | - 'selected' => ($selected == 'all'), |
|
377 | - 'priority' => 200, |
|
378 | - ]); |
|
379 | - $items[] = ElggMenuItem::factory([ |
|
380 | - 'name' => 'mine', |
|
381 | - 'text' => elgg_echo('mine'), |
|
382 | - 'href' => (isset($vars['mine_link'])) ? $vars['mine_link'] : "$context/owner/{$user->username}", |
|
383 | - 'selected' => ($selected == 'mine'), |
|
384 | - 'priority' => 300, |
|
385 | - ]); |
|
386 | - } |
|
387 | - |
|
388 | - $params = [ |
|
389 | - 'selected' => $selected, |
|
390 | - 'user' => $user, |
|
391 | - 'vars' => $vars, |
|
392 | - ]; |
|
393 | - $items = _elgg_services()->hooks->trigger('filter_tabs', $context, $params, $items); |
|
394 | - |
|
395 | - return $items; |
|
362 | + if (!isset($selected)) { |
|
363 | + $selected = 'all'; |
|
364 | + } |
|
365 | + |
|
366 | + if (!$user) { |
|
367 | + $user = elgg_get_logged_in_user_entity(); |
|
368 | + } |
|
369 | + |
|
370 | + $items = []; |
|
371 | + if ($user) { |
|
372 | + $items[] = ElggMenuItem::factory([ |
|
373 | + 'name' => 'all', |
|
374 | + 'text' => elgg_echo('all'), |
|
375 | + 'href' => (isset($vars['all_link'])) ? $vars['all_link'] : "$context/all", |
|
376 | + 'selected' => ($selected == 'all'), |
|
377 | + 'priority' => 200, |
|
378 | + ]); |
|
379 | + $items[] = ElggMenuItem::factory([ |
|
380 | + 'name' => 'mine', |
|
381 | + 'text' => elgg_echo('mine'), |
|
382 | + 'href' => (isset($vars['mine_link'])) ? $vars['mine_link'] : "$context/owner/{$user->username}", |
|
383 | + 'selected' => ($selected == 'mine'), |
|
384 | + 'priority' => 300, |
|
385 | + ]); |
|
386 | + } |
|
387 | + |
|
388 | + $params = [ |
|
389 | + 'selected' => $selected, |
|
390 | + 'user' => $user, |
|
391 | + 'vars' => $vars, |
|
392 | + ]; |
|
393 | + $items = _elgg_services()->hooks->trigger('filter_tabs', $context, $params, $items); |
|
394 | + |
|
395 | + return $items; |
|
396 | 396 | } |
397 | 397 | |
398 | 398 | /** |
@@ -404,83 +404,83 @@ discard block |
||
404 | 404 | */ |
405 | 405 | function _elgg_site_menu_setup($hook, $type, $return, $params) { |
406 | 406 | |
407 | - $featured_menu_names = elgg_get_config('site_featured_menu_names'); |
|
408 | - $custom_menu_items = elgg_get_config('site_custom_menu_items'); |
|
409 | - if ($featured_menu_names || $custom_menu_items) { |
|
410 | - // we have featured or custom menu items |
|
411 | - |
|
412 | - $registered = $return['default']; |
|
413 | - /* @var \ElggMenuItem[] $registered */ |
|
414 | - |
|
415 | - // set up featured menu items |
|
416 | - $featured = []; |
|
417 | - foreach ($featured_menu_names as $name) { |
|
418 | - foreach ($registered as $index => $item) { |
|
419 | - if ($item->getName() == $name) { |
|
420 | - $featured[] = $item; |
|
421 | - unset($registered[$index]); |
|
422 | - } |
|
423 | - } |
|
424 | - } |
|
425 | - |
|
426 | - // add custom menu items |
|
427 | - $n = 1; |
|
428 | - foreach ($custom_menu_items as $title => $url) { |
|
429 | - $item = new \ElggMenuItem("custom$n", $title, $url); |
|
430 | - $featured[] = $item; |
|
431 | - $n++; |
|
432 | - } |
|
433 | - |
|
434 | - $return['default'] = $featured; |
|
435 | - if (count($registered) > 0) { |
|
436 | - $return['more'] = $registered; |
|
437 | - } |
|
438 | - } else { |
|
439 | - // no featured menu items set |
|
440 | - $max_display_items = 5; |
|
441 | - |
|
442 | - // the first n are shown, rest added to more list |
|
443 | - // if only one item on more menu, stick it with the rest |
|
444 | - $num_menu_items = count($return['default']); |
|
445 | - if ($num_menu_items > ($max_display_items + 1)) { |
|
446 | - $return['more'] = array_splice($return['default'], $max_display_items); |
|
447 | - } |
|
448 | - } |
|
407 | + $featured_menu_names = elgg_get_config('site_featured_menu_names'); |
|
408 | + $custom_menu_items = elgg_get_config('site_custom_menu_items'); |
|
409 | + if ($featured_menu_names || $custom_menu_items) { |
|
410 | + // we have featured or custom menu items |
|
411 | + |
|
412 | + $registered = $return['default']; |
|
413 | + /* @var \ElggMenuItem[] $registered */ |
|
414 | + |
|
415 | + // set up featured menu items |
|
416 | + $featured = []; |
|
417 | + foreach ($featured_menu_names as $name) { |
|
418 | + foreach ($registered as $index => $item) { |
|
419 | + if ($item->getName() == $name) { |
|
420 | + $featured[] = $item; |
|
421 | + unset($registered[$index]); |
|
422 | + } |
|
423 | + } |
|
424 | + } |
|
425 | + |
|
426 | + // add custom menu items |
|
427 | + $n = 1; |
|
428 | + foreach ($custom_menu_items as $title => $url) { |
|
429 | + $item = new \ElggMenuItem("custom$n", $title, $url); |
|
430 | + $featured[] = $item; |
|
431 | + $n++; |
|
432 | + } |
|
433 | + |
|
434 | + $return['default'] = $featured; |
|
435 | + if (count($registered) > 0) { |
|
436 | + $return['more'] = $registered; |
|
437 | + } |
|
438 | + } else { |
|
439 | + // no featured menu items set |
|
440 | + $max_display_items = 5; |
|
441 | + |
|
442 | + // the first n are shown, rest added to more list |
|
443 | + // if only one item on more menu, stick it with the rest |
|
444 | + $num_menu_items = count($return['default']); |
|
445 | + if ($num_menu_items > ($max_display_items + 1)) { |
|
446 | + $return['more'] = array_splice($return['default'], $max_display_items); |
|
447 | + } |
|
448 | + } |
|
449 | 449 | |
450 | - // check if we have anything selected |
|
451 | - $selected = false; |
|
452 | - foreach ($return as $section) { |
|
453 | - /* @var \ElggMenuItem[] $section */ |
|
454 | - |
|
455 | - foreach ($section as $item) { |
|
456 | - if ($item->getSelected()) { |
|
457 | - $selected = true; |
|
458 | - break 2; |
|
459 | - } |
|
460 | - } |
|
461 | - } |
|
450 | + // check if we have anything selected |
|
451 | + $selected = false; |
|
452 | + foreach ($return as $section) { |
|
453 | + /* @var \ElggMenuItem[] $section */ |
|
454 | + |
|
455 | + foreach ($section as $item) { |
|
456 | + if ($item->getSelected()) { |
|
457 | + $selected = true; |
|
458 | + break 2; |
|
459 | + } |
|
460 | + } |
|
461 | + } |
|
462 | 462 | |
463 | - if (!$selected) { |
|
464 | - // nothing selected, match name to context or match url |
|
465 | - $current_url = current_page_url(); |
|
466 | - foreach ($return as $section_name => $section) { |
|
467 | - foreach ($section as $key => $item) { |
|
468 | - // only highlight internal links |
|
469 | - if (strpos($item->getHref(), elgg_get_site_url()) === 0) { |
|
470 | - if ($item->getName() == elgg_get_context()) { |
|
471 | - $return[$section_name][$key]->setSelected(true); |
|
472 | - break 2; |
|
473 | - } |
|
474 | - if ($item->getHref() == $current_url) { |
|
475 | - $return[$section_name][$key]->setSelected(true); |
|
476 | - break 2; |
|
477 | - } |
|
478 | - } |
|
479 | - } |
|
480 | - } |
|
481 | - } |
|
482 | - |
|
483 | - return $return; |
|
463 | + if (!$selected) { |
|
464 | + // nothing selected, match name to context or match url |
|
465 | + $current_url = current_page_url(); |
|
466 | + foreach ($return as $section_name => $section) { |
|
467 | + foreach ($section as $key => $item) { |
|
468 | + // only highlight internal links |
|
469 | + if (strpos($item->getHref(), elgg_get_site_url()) === 0) { |
|
470 | + if ($item->getName() == elgg_get_context()) { |
|
471 | + $return[$section_name][$key]->setSelected(true); |
|
472 | + break 2; |
|
473 | + } |
|
474 | + if ($item->getHref() == $current_url) { |
|
475 | + $return[$section_name][$key]->setSelected(true); |
|
476 | + break 2; |
|
477 | + } |
|
478 | + } |
|
479 | + } |
|
480 | + } |
|
481 | + } |
|
482 | + |
|
483 | + return $return; |
|
484 | 484 | } |
485 | 485 | |
486 | 486 | /** |
@@ -492,30 +492,30 @@ discard block |
||
492 | 492 | * @access private |
493 | 493 | */ |
494 | 494 | function _elgg_page_menu_setup(\Elgg\Hook $hook) { |
495 | - $menu = $hook->getValue(); |
|
496 | - |
|
497 | - foreach ($menu as $section => $menu_items) { |
|
498 | - foreach ($menu_items as $menu_item) { |
|
499 | - if ($menu_item instanceof ElggMenuItem) { |
|
500 | - $child_menu_vars = $menu_item->getChildMenuOptions(); |
|
501 | - if (empty($child_menu_vars['display'])) { |
|
502 | - $child_menu_vars['display'] = 'toggle'; |
|
503 | - } |
|
504 | - $menu_item->setChildMenuOptions($child_menu_vars); |
|
505 | - } |
|
506 | - } |
|
507 | - } |
|
508 | - |
|
509 | - $selected_item = $hook->getParam('selected_item'); |
|
510 | - if ($selected_item instanceof \ElggMenuItem) { |
|
511 | - $parent = $selected_item->getParent(); |
|
512 | - while ($parent instanceof \ElggMenuItem) { |
|
513 | - $parent->setSelected(); |
|
514 | - $parent = $parent->getParent(); |
|
515 | - } |
|
516 | - } |
|
517 | - |
|
518 | - return $menu; |
|
495 | + $menu = $hook->getValue(); |
|
496 | + |
|
497 | + foreach ($menu as $section => $menu_items) { |
|
498 | + foreach ($menu_items as $menu_item) { |
|
499 | + if ($menu_item instanceof ElggMenuItem) { |
|
500 | + $child_menu_vars = $menu_item->getChildMenuOptions(); |
|
501 | + if (empty($child_menu_vars['display'])) { |
|
502 | + $child_menu_vars['display'] = 'toggle'; |
|
503 | + } |
|
504 | + $menu_item->setChildMenuOptions($child_menu_vars); |
|
505 | + } |
|
506 | + } |
|
507 | + } |
|
508 | + |
|
509 | + $selected_item = $hook->getParam('selected_item'); |
|
510 | + if ($selected_item instanceof \ElggMenuItem) { |
|
511 | + $parent = $selected_item->getParent(); |
|
512 | + while ($parent instanceof \ElggMenuItem) { |
|
513 | + $parent->setSelected(); |
|
514 | + $parent = $parent->getParent(); |
|
515 | + } |
|
516 | + } |
|
517 | + |
|
518 | + return $menu; |
|
519 | 519 | } |
520 | 520 | |
521 | 521 | /** |
@@ -523,39 +523,39 @@ discard block |
||
523 | 523 | * @access private |
524 | 524 | */ |
525 | 525 | function _elgg_river_menu_setup($hook, $type, $return, $params) { |
526 | - if (elgg_is_logged_in()) { |
|
527 | - $item = $params['item']; |
|
528 | - /* @var \ElggRiverItem $item */ |
|
529 | - $object = $item->getObjectEntity(); |
|
530 | - // add comment link but annotations cannot be commented on |
|
531 | - if ($item->annotation_id == 0) { |
|
532 | - if ($object->canComment()) { |
|
533 | - $options = [ |
|
534 | - 'name' => 'comment', |
|
535 | - 'href' => "#comments-add-{$object->guid}-{$item->id}", |
|
536 | - 'text' => elgg_view_icon('speech-bubble'), |
|
537 | - 'title' => elgg_echo('comment:this'), |
|
538 | - 'rel' => 'toggle', |
|
539 | - 'priority' => 50, |
|
540 | - ]; |
|
541 | - $return[] = \ElggMenuItem::factory($options); |
|
542 | - } |
|
543 | - } |
|
526 | + if (elgg_is_logged_in()) { |
|
527 | + $item = $params['item']; |
|
528 | + /* @var \ElggRiverItem $item */ |
|
529 | + $object = $item->getObjectEntity(); |
|
530 | + // add comment link but annotations cannot be commented on |
|
531 | + if ($item->annotation_id == 0) { |
|
532 | + if ($object->canComment()) { |
|
533 | + $options = [ |
|
534 | + 'name' => 'comment', |
|
535 | + 'href' => "#comments-add-{$object->guid}-{$item->id}", |
|
536 | + 'text' => elgg_view_icon('speech-bubble'), |
|
537 | + 'title' => elgg_echo('comment:this'), |
|
538 | + 'rel' => 'toggle', |
|
539 | + 'priority' => 50, |
|
540 | + ]; |
|
541 | + $return[] = \ElggMenuItem::factory($options); |
|
542 | + } |
|
543 | + } |
|
544 | 544 | |
545 | - if ($item->canDelete()) { |
|
546 | - $options = [ |
|
547 | - 'name' => 'delete', |
|
548 | - 'href' => elgg_add_action_tokens_to_url("action/river/delete?id={$item->id}"), |
|
549 | - 'text' => elgg_view_icon('delete'), |
|
550 | - 'title' => elgg_echo('river:delete'), |
|
551 | - 'confirm' => elgg_echo('deleteconfirm'), |
|
552 | - 'priority' => 200, |
|
553 | - ]; |
|
554 | - $return[] = \ElggMenuItem::factory($options); |
|
555 | - } |
|
556 | - } |
|
557 | - |
|
558 | - return $return; |
|
545 | + if ($item->canDelete()) { |
|
546 | + $options = [ |
|
547 | + 'name' => 'delete', |
|
548 | + 'href' => elgg_add_action_tokens_to_url("action/river/delete?id={$item->id}"), |
|
549 | + 'text' => elgg_view_icon('delete'), |
|
550 | + 'title' => elgg_echo('river:delete'), |
|
551 | + 'confirm' => elgg_echo('deleteconfirm'), |
|
552 | + 'priority' => 200, |
|
553 | + ]; |
|
554 | + $return[] = \ElggMenuItem::factory($options); |
|
555 | + } |
|
556 | + } |
|
557 | + |
|
558 | + return $return; |
|
559 | 559 | } |
560 | 560 | |
561 | 561 | /** |
@@ -563,45 +563,45 @@ discard block |
||
563 | 563 | * @access private |
564 | 564 | */ |
565 | 565 | function _elgg_entity_menu_setup($hook, $type, $return, $params) { |
566 | - if (elgg_in_context('widgets')) { |
|
567 | - return $return; |
|
568 | - } |
|
566 | + if (elgg_in_context('widgets')) { |
|
567 | + return $return; |
|
568 | + } |
|
569 | 569 | |
570 | - $entity = $params['entity']; |
|
571 | - /* @var \ElggEntity $entity */ |
|
572 | - $handler = elgg_extract('handler', $params, false); |
|
570 | + $entity = $params['entity']; |
|
571 | + /* @var \ElggEntity $entity */ |
|
572 | + $handler = elgg_extract('handler', $params, false); |
|
573 | 573 | |
574 | - if ($entity->canEdit() && $handler) { |
|
575 | - // edit link |
|
576 | - $options = [ |
|
577 | - 'name' => 'edit', |
|
578 | - 'text' => elgg_echo('edit'), |
|
579 | - 'title' => elgg_echo('edit:this'), |
|
580 | - 'href' => "$handler/edit/{$entity->getGUID()}", |
|
581 | - 'priority' => 200, |
|
582 | - ]; |
|
583 | - $return[] = \ElggMenuItem::factory($options); |
|
584 | - } |
|
585 | - |
|
586 | - if ($entity->canDelete() && $handler) { |
|
587 | - // delete link |
|
588 | - if (elgg_action_exists("$handler/delete")) { |
|
589 | - $action = "action/$handler/delete"; |
|
590 | - } else { |
|
591 | - $action = "action/entity/delete"; |
|
592 | - } |
|
593 | - $options = [ |
|
594 | - 'name' => 'delete', |
|
595 | - 'text' => elgg_view_icon('delete'), |
|
596 | - 'title' => elgg_echo('delete:this'), |
|
597 | - 'href' => "$action?guid={$entity->getGUID()}", |
|
598 | - 'confirm' => elgg_echo('deleteconfirm'), |
|
599 | - 'priority' => 300, |
|
600 | - ]; |
|
601 | - $return[] = \ElggMenuItem::factory($options); |
|
602 | - } |
|
603 | - |
|
604 | - return $return; |
|
574 | + if ($entity->canEdit() && $handler) { |
|
575 | + // edit link |
|
576 | + $options = [ |
|
577 | + 'name' => 'edit', |
|
578 | + 'text' => elgg_echo('edit'), |
|
579 | + 'title' => elgg_echo('edit:this'), |
|
580 | + 'href' => "$handler/edit/{$entity->getGUID()}", |
|
581 | + 'priority' => 200, |
|
582 | + ]; |
|
583 | + $return[] = \ElggMenuItem::factory($options); |
|
584 | + } |
|
585 | + |
|
586 | + if ($entity->canDelete() && $handler) { |
|
587 | + // delete link |
|
588 | + if (elgg_action_exists("$handler/delete")) { |
|
589 | + $action = "action/$handler/delete"; |
|
590 | + } else { |
|
591 | + $action = "action/entity/delete"; |
|
592 | + } |
|
593 | + $options = [ |
|
594 | + 'name' => 'delete', |
|
595 | + 'text' => elgg_view_icon('delete'), |
|
596 | + 'title' => elgg_echo('delete:this'), |
|
597 | + 'href' => "$action?guid={$entity->getGUID()}", |
|
598 | + 'confirm' => elgg_echo('deleteconfirm'), |
|
599 | + 'priority' => 300, |
|
600 | + ]; |
|
601 | + $return[] = \ElggMenuItem::factory($options); |
|
602 | + } |
|
603 | + |
|
604 | + return $return; |
|
605 | 605 | } |
606 | 606 | |
607 | 607 | /** |
@@ -610,49 +610,49 @@ discard block |
||
610 | 610 | */ |
611 | 611 | function _elgg_widget_menu_setup($hook, $type, $return, $params) { |
612 | 612 | |
613 | - $widget = $params['entity']; |
|
614 | - /* @var \ElggWidget $widget */ |
|
615 | - $show_edit = elgg_extract('show_edit', $params, true); |
|
616 | - |
|
617 | - $collapse = [ |
|
618 | - 'name' => 'collapse', |
|
619 | - 'text' => ' ', |
|
620 | - 'href' => "#elgg-widget-content-$widget->guid", |
|
621 | - 'link_class' => 'elgg-widget-collapse-button', |
|
622 | - 'rel' => 'toggle', |
|
623 | - 'priority' => 1, |
|
624 | - ]; |
|
625 | - $return[] = \ElggMenuItem::factory($collapse); |
|
626 | - |
|
627 | - if ($widget->canEdit()) { |
|
628 | - $delete = [ |
|
629 | - 'name' => 'delete', |
|
630 | - 'text' => elgg_view_icon('delete-alt'), |
|
631 | - 'title' => elgg_echo('widget:delete', [$widget->getTitle()]), |
|
632 | - 'href' => "action/widgets/delete?widget_guid=$widget->guid", |
|
633 | - 'is_action' => true, |
|
634 | - 'link_class' => 'elgg-widget-delete-button', |
|
635 | - 'id' => "elgg-widget-delete-button-$widget->guid", |
|
636 | - 'data-elgg-widget-type' => $widget->handler, |
|
637 | - 'priority' => 900, |
|
638 | - ]; |
|
639 | - $return[] = \ElggMenuItem::factory($delete); |
|
640 | - |
|
641 | - if ($show_edit) { |
|
642 | - $edit = [ |
|
643 | - 'name' => 'settings', |
|
644 | - 'text' => elgg_view_icon('settings-alt'), |
|
645 | - 'title' => elgg_echo('widget:edit'), |
|
646 | - 'href' => "#widget-edit-$widget->guid", |
|
647 | - 'link_class' => "elgg-widget-edit-button", |
|
648 | - 'rel' => 'toggle', |
|
649 | - 'priority' => 800, |
|
650 | - ]; |
|
651 | - $return[] = \ElggMenuItem::factory($edit); |
|
652 | - } |
|
653 | - } |
|
654 | - |
|
655 | - return $return; |
|
613 | + $widget = $params['entity']; |
|
614 | + /* @var \ElggWidget $widget */ |
|
615 | + $show_edit = elgg_extract('show_edit', $params, true); |
|
616 | + |
|
617 | + $collapse = [ |
|
618 | + 'name' => 'collapse', |
|
619 | + 'text' => ' ', |
|
620 | + 'href' => "#elgg-widget-content-$widget->guid", |
|
621 | + 'link_class' => 'elgg-widget-collapse-button', |
|
622 | + 'rel' => 'toggle', |
|
623 | + 'priority' => 1, |
|
624 | + ]; |
|
625 | + $return[] = \ElggMenuItem::factory($collapse); |
|
626 | + |
|
627 | + if ($widget->canEdit()) { |
|
628 | + $delete = [ |
|
629 | + 'name' => 'delete', |
|
630 | + 'text' => elgg_view_icon('delete-alt'), |
|
631 | + 'title' => elgg_echo('widget:delete', [$widget->getTitle()]), |
|
632 | + 'href' => "action/widgets/delete?widget_guid=$widget->guid", |
|
633 | + 'is_action' => true, |
|
634 | + 'link_class' => 'elgg-widget-delete-button', |
|
635 | + 'id' => "elgg-widget-delete-button-$widget->guid", |
|
636 | + 'data-elgg-widget-type' => $widget->handler, |
|
637 | + 'priority' => 900, |
|
638 | + ]; |
|
639 | + $return[] = \ElggMenuItem::factory($delete); |
|
640 | + |
|
641 | + if ($show_edit) { |
|
642 | + $edit = [ |
|
643 | + 'name' => 'settings', |
|
644 | + 'text' => elgg_view_icon('settings-alt'), |
|
645 | + 'title' => elgg_echo('widget:edit'), |
|
646 | + 'href' => "#widget-edit-$widget->guid", |
|
647 | + 'link_class' => "elgg-widget-edit-button", |
|
648 | + 'rel' => 'toggle', |
|
649 | + 'priority' => 800, |
|
650 | + ]; |
|
651 | + $return[] = \ElggMenuItem::factory($edit); |
|
652 | + } |
|
653 | + } |
|
654 | + |
|
655 | + return $return; |
|
656 | 656 | } |
657 | 657 | |
658 | 658 | /** |
@@ -661,23 +661,23 @@ discard block |
||
661 | 661 | */ |
662 | 662 | function _elgg_login_menu_setup($hook, $type, $return, $params) { |
663 | 663 | |
664 | - if (elgg_get_config('allow_registration')) { |
|
665 | - $return[] = \ElggMenuItem::factory([ |
|
666 | - 'name' => 'register', |
|
667 | - 'href' => elgg_get_registration_url(), |
|
668 | - 'text' => elgg_echo('register'), |
|
669 | - 'link_class' => 'registration_link', |
|
670 | - ]); |
|
671 | - } |
|
672 | - |
|
673 | - $return[] = \ElggMenuItem::factory([ |
|
674 | - 'name' => 'forgotpassword', |
|
675 | - 'href' => 'forgotpassword', |
|
676 | - 'text' => elgg_echo('user:password:lost'), |
|
677 | - 'link_class' => 'forgot_link', |
|
678 | - ]); |
|
679 | - |
|
680 | - return $return; |
|
664 | + if (elgg_get_config('allow_registration')) { |
|
665 | + $return[] = \ElggMenuItem::factory([ |
|
666 | + 'name' => 'register', |
|
667 | + 'href' => elgg_get_registration_url(), |
|
668 | + 'text' => elgg_echo('register'), |
|
669 | + 'link_class' => 'registration_link', |
|
670 | + ]); |
|
671 | + } |
|
672 | + |
|
673 | + $return[] = \ElggMenuItem::factory([ |
|
674 | + 'name' => 'forgotpassword', |
|
675 | + 'href' => 'forgotpassword', |
|
676 | + 'text' => elgg_echo('user:password:lost'), |
|
677 | + 'link_class' => 'forgot_link', |
|
678 | + ]); |
|
679 | + |
|
680 | + return $return; |
|
681 | 681 | } |
682 | 682 | |
683 | 683 | /** |
@@ -686,26 +686,26 @@ discard block |
||
686 | 686 | */ |
687 | 687 | function _elgg_extras_menu_setup($hook, $type, $return, $params) { |
688 | 688 | |
689 | - if (!elgg_is_logged_in()) { |
|
690 | - return; |
|
691 | - } |
|
689 | + if (!elgg_is_logged_in()) { |
|
690 | + return; |
|
691 | + } |
|
692 | 692 | |
693 | - if (!_elgg_has_rss_link()) { |
|
694 | - return; |
|
695 | - } |
|
696 | - |
|
697 | - $url = current_page_url(); |
|
698 | - $return[] = ElggMenuItem::factory([ |
|
699 | - 'name' => 'rss', |
|
700 | - 'text' => elgg_echo('feed:rss'), |
|
701 | - 'icon' => 'rss', |
|
702 | - 'href' => elgg_http_add_url_query_elements($url, [ |
|
703 | - 'view' => 'rss', |
|
704 | - ]), |
|
705 | - 'title' => elgg_echo('feed:rss'), |
|
706 | - ]); |
|
707 | - |
|
708 | - return $return; |
|
693 | + if (!_elgg_has_rss_link()) { |
|
694 | + return; |
|
695 | + } |
|
696 | + |
|
697 | + $url = current_page_url(); |
|
698 | + $return[] = ElggMenuItem::factory([ |
|
699 | + 'name' => 'rss', |
|
700 | + 'text' => elgg_echo('feed:rss'), |
|
701 | + 'icon' => 'rss', |
|
702 | + 'href' => elgg_http_add_url_query_elements($url, [ |
|
703 | + 'view' => 'rss', |
|
704 | + ]), |
|
705 | + 'title' => elgg_echo('feed:rss'), |
|
706 | + ]); |
|
707 | + |
|
708 | + return $return; |
|
709 | 709 | } |
710 | 710 | |
711 | 711 | /** |
@@ -713,32 +713,32 @@ discard block |
||
713 | 713 | * @access private |
714 | 714 | */ |
715 | 715 | function _elgg_nav_init() { |
716 | - elgg_register_plugin_hook_handler('prepare', 'breadcrumbs', 'elgg_prepare_breadcrumbs'); |
|
716 | + elgg_register_plugin_hook_handler('prepare', 'breadcrumbs', 'elgg_prepare_breadcrumbs'); |
|
717 | 717 | |
718 | - elgg_register_plugin_hook_handler('prepare', 'menu:site', '_elgg_site_menu_setup'); |
|
719 | - elgg_register_plugin_hook_handler('prepare', 'menu:page', '_elgg_page_menu_setup', 999); |
|
718 | + elgg_register_plugin_hook_handler('prepare', 'menu:site', '_elgg_site_menu_setup'); |
|
719 | + elgg_register_plugin_hook_handler('prepare', 'menu:page', '_elgg_page_menu_setup', 999); |
|
720 | 720 | |
721 | - elgg_register_plugin_hook_handler('register', 'menu:river', '_elgg_river_menu_setup'); |
|
722 | - elgg_register_plugin_hook_handler('register', 'menu:entity', '_elgg_entity_menu_setup'); |
|
723 | - elgg_register_plugin_hook_handler('register', 'menu:widget', '_elgg_widget_menu_setup'); |
|
724 | - elgg_register_plugin_hook_handler('register', 'menu:login', '_elgg_login_menu_setup'); |
|
725 | - elgg_register_plugin_hook_handler('register', 'menu:extras', '_elgg_extras_menu_setup'); |
|
721 | + elgg_register_plugin_hook_handler('register', 'menu:river', '_elgg_river_menu_setup'); |
|
722 | + elgg_register_plugin_hook_handler('register', 'menu:entity', '_elgg_entity_menu_setup'); |
|
723 | + elgg_register_plugin_hook_handler('register', 'menu:widget', '_elgg_widget_menu_setup'); |
|
724 | + elgg_register_plugin_hook_handler('register', 'menu:login', '_elgg_login_menu_setup'); |
|
725 | + elgg_register_plugin_hook_handler('register', 'menu:extras', '_elgg_extras_menu_setup'); |
|
726 | 726 | |
727 | - elgg_register_plugin_hook_handler('public_pages', 'walled_garden', '_elgg_nav_public_pages'); |
|
727 | + elgg_register_plugin_hook_handler('public_pages', 'walled_garden', '_elgg_nav_public_pages'); |
|
728 | 728 | |
729 | - elgg_register_menu_item('footer', \ElggMenuItem::factory([ |
|
730 | - 'name' => 'powered', |
|
731 | - 'text' => elgg_echo("elgg:powered"), |
|
732 | - 'href' => 'http://elgg.org', |
|
733 | - 'title' => 'Elgg ' . elgg_get_version(true), |
|
734 | - 'section' => 'meta', |
|
735 | - ])); |
|
729 | + elgg_register_menu_item('footer', \ElggMenuItem::factory([ |
|
730 | + 'name' => 'powered', |
|
731 | + 'text' => elgg_echo("elgg:powered"), |
|
732 | + 'href' => 'http://elgg.org', |
|
733 | + 'title' => 'Elgg ' . elgg_get_version(true), |
|
734 | + 'section' => 'meta', |
|
735 | + ])); |
|
736 | 736 | |
737 | - elgg_register_ajax_view('navigation/menu/user_hover/contents'); |
|
737 | + elgg_register_ajax_view('navigation/menu/user_hover/contents'); |
|
738 | 738 | |
739 | - // Using a view extension to ensure that themes that have replaced the item view |
|
740 | - // still load the required AMD modules |
|
741 | - elgg_extend_view('navigation/menu/elements/item', 'navigation/menu/elements/item_deps'); |
|
739 | + // Using a view extension to ensure that themes that have replaced the item view |
|
740 | + // still load the required AMD modules |
|
741 | + elgg_extend_view('navigation/menu/elements/item', 'navigation/menu/elements/item_deps'); |
|
742 | 742 | } |
743 | 743 | |
744 | 744 | /** |
@@ -754,13 +754,13 @@ discard block |
||
754 | 754 | * @since 1.11.0 |
755 | 755 | */ |
756 | 756 | function _elgg_nav_public_pages($hook_name, $entity_type, $return_value, $params) { |
757 | - if (is_array($return_value)) { |
|
758 | - $return_value[] = 'navigation/menu/user_hover/contents'; |
|
759 | - } |
|
757 | + if (is_array($return_value)) { |
|
758 | + $return_value[] = 'navigation/menu/user_hover/contents'; |
|
759 | + } |
|
760 | 760 | |
761 | - return $return_value; |
|
761 | + return $return_value; |
|
762 | 762 | } |
763 | 763 | |
764 | 764 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
765 | - $events->registerHandler('init', 'system', '_elgg_nav_init'); |
|
765 | + $events->registerHandler('init', 'system', '_elgg_nav_init'); |
|
766 | 766 | }; |
@@ -18,482 +18,482 @@ |
||
18 | 18 | */ |
19 | 19 | class ActionsService { |
20 | 20 | |
21 | - use \Elgg\TimeUsing; |
|
21 | + use \Elgg\TimeUsing; |
|
22 | 22 | |
23 | - /** |
|
24 | - * @var Config |
|
25 | - */ |
|
26 | - private $config; |
|
27 | - |
|
28 | - /** |
|
29 | - * @var ElggSession |
|
30 | - */ |
|
31 | - private $session; |
|
32 | - |
|
33 | - /** |
|
34 | - * @var ElggCrypto |
|
35 | - */ |
|
36 | - private $crypto; |
|
37 | - |
|
38 | - /** |
|
39 | - * Registered actions storage |
|
40 | - * |
|
41 | - * Each element has keys: |
|
42 | - * "file" => filename |
|
43 | - * "access" => access level |
|
44 | - * |
|
45 | - * @var array |
|
46 | - */ |
|
47 | - private $actions = []; |
|
48 | - |
|
49 | - /** |
|
50 | - * The current action being processed |
|
51 | - * @var string |
|
52 | - */ |
|
53 | - private $currentAction = null; |
|
54 | - |
|
55 | - /** |
|
56 | - * @var string[] |
|
57 | - */ |
|
58 | - private static $access_levels = ['public', 'logged_in', 'admin']; |
|
59 | - |
|
60 | - /** |
|
61 | - * Constructor |
|
62 | - * |
|
63 | - * @param Config $config Config |
|
64 | - * @param ElggSession $session Session |
|
65 | - * @param ElggCrypto $crypto Crypto service |
|
66 | - */ |
|
67 | - public function __construct(Config $config, ElggSession $session, ElggCrypto $crypto) { |
|
68 | - $this->config = $config; |
|
69 | - $this->session = $session; |
|
70 | - $this->crypto = $crypto; |
|
71 | - } |
|
72 | - |
|
73 | - /** |
|
74 | - * Executes an action |
|
75 | - * If called from action() redirect will be issued by the response factory |
|
76 | - * If called as /action page handler response will be handled by \Elgg\Router |
|
77 | - * |
|
78 | - * @param string $action Action name |
|
79 | - * @param string $forwarder URL to forward to after completion |
|
80 | - * @return ResponseBuilder|null |
|
81 | - * @see action |
|
82 | - * @access private |
|
83 | - */ |
|
84 | - public function execute($action, $forwarder = "") { |
|
85 | - $action = rtrim($action, '/'); |
|
86 | - $this->currentAction = $action; |
|
23 | + /** |
|
24 | + * @var Config |
|
25 | + */ |
|
26 | + private $config; |
|
27 | + |
|
28 | + /** |
|
29 | + * @var ElggSession |
|
30 | + */ |
|
31 | + private $session; |
|
32 | + |
|
33 | + /** |
|
34 | + * @var ElggCrypto |
|
35 | + */ |
|
36 | + private $crypto; |
|
37 | + |
|
38 | + /** |
|
39 | + * Registered actions storage |
|
40 | + * |
|
41 | + * Each element has keys: |
|
42 | + * "file" => filename |
|
43 | + * "access" => access level |
|
44 | + * |
|
45 | + * @var array |
|
46 | + */ |
|
47 | + private $actions = []; |
|
48 | + |
|
49 | + /** |
|
50 | + * The current action being processed |
|
51 | + * @var string |
|
52 | + */ |
|
53 | + private $currentAction = null; |
|
54 | + |
|
55 | + /** |
|
56 | + * @var string[] |
|
57 | + */ |
|
58 | + private static $access_levels = ['public', 'logged_in', 'admin']; |
|
59 | + |
|
60 | + /** |
|
61 | + * Constructor |
|
62 | + * |
|
63 | + * @param Config $config Config |
|
64 | + * @param ElggSession $session Session |
|
65 | + * @param ElggCrypto $crypto Crypto service |
|
66 | + */ |
|
67 | + public function __construct(Config $config, ElggSession $session, ElggCrypto $crypto) { |
|
68 | + $this->config = $config; |
|
69 | + $this->session = $session; |
|
70 | + $this->crypto = $crypto; |
|
71 | + } |
|
72 | + |
|
73 | + /** |
|
74 | + * Executes an action |
|
75 | + * If called from action() redirect will be issued by the response factory |
|
76 | + * If called as /action page handler response will be handled by \Elgg\Router |
|
77 | + * |
|
78 | + * @param string $action Action name |
|
79 | + * @param string $forwarder URL to forward to after completion |
|
80 | + * @return ResponseBuilder|null |
|
81 | + * @see action |
|
82 | + * @access private |
|
83 | + */ |
|
84 | + public function execute($action, $forwarder = "") { |
|
85 | + $action = rtrim($action, '/'); |
|
86 | + $this->currentAction = $action; |
|
87 | 87 | |
88 | - // @todo REMOVE THESE ONCE #1509 IS IN PLACE. |
|
89 | - // Allow users to disable plugins without a token in order to |
|
90 | - // remove plugins that are incompatible. |
|
91 | - // Login and logout are for convenience. |
|
92 | - // file/download (see #2010) |
|
93 | - $exceptions = [ |
|
94 | - 'admin/plugins/disable', |
|
95 | - 'logout', |
|
96 | - 'file/download', |
|
97 | - ]; |
|
88 | + // @todo REMOVE THESE ONCE #1509 IS IN PLACE. |
|
89 | + // Allow users to disable plugins without a token in order to |
|
90 | + // remove plugins that are incompatible. |
|
91 | + // Login and logout are for convenience. |
|
92 | + // file/download (see #2010) |
|
93 | + $exceptions = [ |
|
94 | + 'admin/plugins/disable', |
|
95 | + 'logout', |
|
96 | + 'file/download', |
|
97 | + ]; |
|
98 | 98 | |
99 | - if (!in_array($action, $exceptions)) { |
|
100 | - // All actions require a token. |
|
101 | - $pass = $this->gatekeeper($action); |
|
102 | - if (!$pass) { |
|
103 | - return; |
|
104 | - } |
|
105 | - } |
|
99 | + if (!in_array($action, $exceptions)) { |
|
100 | + // All actions require a token. |
|
101 | + $pass = $this->gatekeeper($action); |
|
102 | + if (!$pass) { |
|
103 | + return; |
|
104 | + } |
|
105 | + } |
|
106 | 106 | |
107 | - $forwarder = str_replace($this->config->getSiteUrl(), "", $forwarder); |
|
108 | - $forwarder = str_replace("http://", "", $forwarder); |
|
109 | - $forwarder = str_replace("@", "", $forwarder); |
|
110 | - if (substr($forwarder, 0, 1) == "/") { |
|
111 | - $forwarder = substr($forwarder, 1); |
|
112 | - } |
|
113 | - |
|
114 | - $ob_started = false; |
|
115 | - |
|
116 | - /** |
|
117 | - * Prepare action response |
|
118 | - * |
|
119 | - * @param string $error_key Error message key |
|
120 | - * @param int $status_code HTTP status code |
|
121 | - * @return ResponseBuilder |
|
122 | - */ |
|
123 | - $forward = function ($error_key = '', $status_code = ELGG_HTTP_OK) use ($action, $forwarder, &$ob_started) { |
|
124 | - if ($error_key) { |
|
125 | - if ($ob_started) { |
|
126 | - ob_end_clean(); |
|
127 | - } |
|
128 | - $msg = _elgg_services()->translator->translate($error_key, [$action]); |
|
129 | - _elgg_services()->systemMessages->addErrorMessage($msg); |
|
130 | - $response = new \Elgg\Http\ErrorResponse($msg, $status_code); |
|
131 | - } else { |
|
132 | - $content = ob_get_clean(); |
|
133 | - $response = new \Elgg\Http\OkResponse($content, $status_code); |
|
134 | - } |
|
107 | + $forwarder = str_replace($this->config->getSiteUrl(), "", $forwarder); |
|
108 | + $forwarder = str_replace("http://", "", $forwarder); |
|
109 | + $forwarder = str_replace("@", "", $forwarder); |
|
110 | + if (substr($forwarder, 0, 1) == "/") { |
|
111 | + $forwarder = substr($forwarder, 1); |
|
112 | + } |
|
113 | + |
|
114 | + $ob_started = false; |
|
115 | + |
|
116 | + /** |
|
117 | + * Prepare action response |
|
118 | + * |
|
119 | + * @param string $error_key Error message key |
|
120 | + * @param int $status_code HTTP status code |
|
121 | + * @return ResponseBuilder |
|
122 | + */ |
|
123 | + $forward = function ($error_key = '', $status_code = ELGG_HTTP_OK) use ($action, $forwarder, &$ob_started) { |
|
124 | + if ($error_key) { |
|
125 | + if ($ob_started) { |
|
126 | + ob_end_clean(); |
|
127 | + } |
|
128 | + $msg = _elgg_services()->translator->translate($error_key, [$action]); |
|
129 | + _elgg_services()->systemMessages->addErrorMessage($msg); |
|
130 | + $response = new \Elgg\Http\ErrorResponse($msg, $status_code); |
|
131 | + } else { |
|
132 | + $content = ob_get_clean(); |
|
133 | + $response = new \Elgg\Http\OkResponse($content, $status_code); |
|
134 | + } |
|
135 | 135 | |
136 | - $forwarder = empty($forwarder) ? REFERER : $forwarder; |
|
137 | - $response->setForwardURL($forwarder); |
|
138 | - return $response; |
|
139 | - }; |
|
140 | - |
|
141 | - if (!isset($this->actions[$action])) { |
|
142 | - return $forward('actionundefined', ELGG_HTTP_NOT_IMPLEMENTED); |
|
143 | - } |
|
144 | - |
|
145 | - $user = $this->session->getLoggedInUser(); |
|
146 | - |
|
147 | - // access checks |
|
148 | - switch ($this->actions[$action]['access']) { |
|
149 | - case 'public': |
|
150 | - break; |
|
151 | - case 'logged_in': |
|
152 | - if (!$user) { |
|
153 | - return $forward('actionloggedout', ELGG_HTTP_FORBIDDEN); |
|
154 | - } |
|
155 | - break; |
|
156 | - default: |
|
157 | - // admin or misspelling |
|
158 | - if (!$user || !$user->isAdmin()) { |
|
159 | - return $forward('actionunauthorized', ELGG_HTTP_FORBIDDEN); |
|
160 | - } |
|
161 | - } |
|
162 | - |
|
163 | - ob_start(); |
|
136 | + $forwarder = empty($forwarder) ? REFERER : $forwarder; |
|
137 | + $response->setForwardURL($forwarder); |
|
138 | + return $response; |
|
139 | + }; |
|
140 | + |
|
141 | + if (!isset($this->actions[$action])) { |
|
142 | + return $forward('actionundefined', ELGG_HTTP_NOT_IMPLEMENTED); |
|
143 | + } |
|
144 | + |
|
145 | + $user = $this->session->getLoggedInUser(); |
|
146 | + |
|
147 | + // access checks |
|
148 | + switch ($this->actions[$action]['access']) { |
|
149 | + case 'public': |
|
150 | + break; |
|
151 | + case 'logged_in': |
|
152 | + if (!$user) { |
|
153 | + return $forward('actionloggedout', ELGG_HTTP_FORBIDDEN); |
|
154 | + } |
|
155 | + break; |
|
156 | + default: |
|
157 | + // admin or misspelling |
|
158 | + if (!$user || !$user->isAdmin()) { |
|
159 | + return $forward('actionunauthorized', ELGG_HTTP_FORBIDDEN); |
|
160 | + } |
|
161 | + } |
|
162 | + |
|
163 | + ob_start(); |
|
164 | 164 | |
165 | - // To quietly cancel the file, return a falsey value in the "action" hook. |
|
166 | - if (!_elgg_services()->hooks->trigger('action', $action, null, true)) { |
|
167 | - return $forward('', ELGG_HTTP_OK); |
|
168 | - } |
|
169 | - |
|
170 | - $file = $this->actions[$action]['file']; |
|
171 | - |
|
172 | - if (!is_file($file) || !is_readable($file)) { |
|
173 | - return $forward('actionnotfound', ELGG_HTTP_NOT_IMPLEMENTED); |
|
174 | - } |
|
175 | - |
|
176 | - // set the maximum execution time for actions |
|
177 | - $action_timeout = $this->config->get('action_time_limit'); |
|
178 | - if (isset($action_timeout)) { |
|
179 | - set_time_limit($action_timeout); |
|
180 | - } |
|
181 | - |
|
182 | - $result = Includer::includeFile($file); |
|
183 | - if ($result instanceof ResponseBuilder) { |
|
184 | - ob_end_clean(); |
|
185 | - return $result; |
|
186 | - } |
|
187 | - |
|
188 | - return $forward('', ELGG_HTTP_OK); |
|
189 | - } |
|
165 | + // To quietly cancel the file, return a falsey value in the "action" hook. |
|
166 | + if (!_elgg_services()->hooks->trigger('action', $action, null, true)) { |
|
167 | + return $forward('', ELGG_HTTP_OK); |
|
168 | + } |
|
169 | + |
|
170 | + $file = $this->actions[$action]['file']; |
|
171 | + |
|
172 | + if (!is_file($file) || !is_readable($file)) { |
|
173 | + return $forward('actionnotfound', ELGG_HTTP_NOT_IMPLEMENTED); |
|
174 | + } |
|
175 | + |
|
176 | + // set the maximum execution time for actions |
|
177 | + $action_timeout = $this->config->get('action_time_limit'); |
|
178 | + if (isset($action_timeout)) { |
|
179 | + set_time_limit($action_timeout); |
|
180 | + } |
|
181 | + |
|
182 | + $result = Includer::includeFile($file); |
|
183 | + if ($result instanceof ResponseBuilder) { |
|
184 | + ob_end_clean(); |
|
185 | + return $result; |
|
186 | + } |
|
187 | + |
|
188 | + return $forward('', ELGG_HTTP_OK); |
|
189 | + } |
|
190 | 190 | |
191 | - /** |
|
192 | - * @see elgg_register_action |
|
193 | - * @access private |
|
194 | - */ |
|
195 | - public function register($action, $filename = "", $access = 'logged_in') { |
|
196 | - // plugins are encouraged to call actions with a trailing / to prevent 301 |
|
197 | - // redirects but we store the actions without it |
|
198 | - $action = rtrim($action, '/'); |
|
191 | + /** |
|
192 | + * @see elgg_register_action |
|
193 | + * @access private |
|
194 | + */ |
|
195 | + public function register($action, $filename = "", $access = 'logged_in') { |
|
196 | + // plugins are encouraged to call actions with a trailing / to prevent 301 |
|
197 | + // redirects but we store the actions without it |
|
198 | + $action = rtrim($action, '/'); |
|
199 | 199 | |
200 | - if (empty($filename)) { |
|
201 | - $path = __DIR__ . '/../../../actions'; |
|
202 | - $filename = realpath("$path/$action.php"); |
|
203 | - } |
|
204 | - |
|
205 | - if (!in_array($access, self::$access_levels)) { |
|
206 | - _elgg_services()->logger->error("Unrecognized value '$access' for \$access in " . __METHOD__); |
|
207 | - $access = 'admin'; |
|
208 | - } |
|
200 | + if (empty($filename)) { |
|
201 | + $path = __DIR__ . '/../../../actions'; |
|
202 | + $filename = realpath("$path/$action.php"); |
|
203 | + } |
|
204 | + |
|
205 | + if (!in_array($access, self::$access_levels)) { |
|
206 | + _elgg_services()->logger->error("Unrecognized value '$access' for \$access in " . __METHOD__); |
|
207 | + $access = 'admin'; |
|
208 | + } |
|
209 | 209 | |
210 | - $this->actions[$action] = [ |
|
211 | - 'file' => $filename, |
|
212 | - 'access' => $access, |
|
213 | - ]; |
|
214 | - return true; |
|
215 | - } |
|
210 | + $this->actions[$action] = [ |
|
211 | + 'file' => $filename, |
|
212 | + 'access' => $access, |
|
213 | + ]; |
|
214 | + return true; |
|
215 | + } |
|
216 | 216 | |
217 | - /** |
|
218 | - * @see elgg_unregister_action |
|
219 | - * @access private |
|
220 | - */ |
|
221 | - public function unregister($action) { |
|
222 | - if (isset($this->actions[$action])) { |
|
223 | - unset($this->actions[$action]); |
|
224 | - return true; |
|
225 | - } else { |
|
226 | - return false; |
|
227 | - } |
|
228 | - } |
|
229 | - |
|
230 | - /** |
|
231 | - * @see validate_action_token |
|
232 | - * @access private |
|
233 | - */ |
|
234 | - public function validateActionToken($visible_errors = true, $token = null, $ts = null) { |
|
235 | - if (!$token) { |
|
236 | - $token = get_input('__elgg_token'); |
|
237 | - } |
|
217 | + /** |
|
218 | + * @see elgg_unregister_action |
|
219 | + * @access private |
|
220 | + */ |
|
221 | + public function unregister($action) { |
|
222 | + if (isset($this->actions[$action])) { |
|
223 | + unset($this->actions[$action]); |
|
224 | + return true; |
|
225 | + } else { |
|
226 | + return false; |
|
227 | + } |
|
228 | + } |
|
229 | + |
|
230 | + /** |
|
231 | + * @see validate_action_token |
|
232 | + * @access private |
|
233 | + */ |
|
234 | + public function validateActionToken($visible_errors = true, $token = null, $ts = null) { |
|
235 | + if (!$token) { |
|
236 | + $token = get_input('__elgg_token'); |
|
237 | + } |
|
238 | 238 | |
239 | - if (!$ts) { |
|
240 | - $ts = get_input('__elgg_ts'); |
|
241 | - } |
|
242 | - |
|
243 | - $session_id = $this->session->getId(); |
|
244 | - |
|
245 | - if (($token) && ($ts) && ($session_id)) { |
|
246 | - if ($this->validateTokenOwnership($token, $ts)) { |
|
247 | - if ($this->validateTokenTimestamp($ts)) { |
|
248 | - // We have already got this far, so unless anything |
|
249 | - // else says something to the contrary we assume we're ok |
|
250 | - $returnval = _elgg_services()->hooks->trigger('action_gatekeeper:permissions:check', 'all', [ |
|
251 | - 'token' => $token, |
|
252 | - 'time' => $ts |
|
253 | - ], true); |
|
254 | - |
|
255 | - if ($returnval) { |
|
256 | - return true; |
|
257 | - } else if ($visible_errors) { |
|
258 | - register_error(_elgg_services()->translator->translate('actiongatekeeper:pluginprevents')); |
|
259 | - } |
|
260 | - } else if ($visible_errors) { |
|
261 | - // this is necessary because of #5133 |
|
262 | - if (elgg_is_xhr()) { |
|
263 | - register_error(_elgg_services()->translator->translate( |
|
264 | - 'js:security:token_refresh_failed', |
|
265 | - [$this->config->getSiteUrl()] |
|
266 | - )); |
|
267 | - } else { |
|
268 | - register_error(_elgg_services()->translator->translate('actiongatekeeper:timeerror')); |
|
269 | - } |
|
270 | - } |
|
271 | - } else if ($visible_errors) { |
|
272 | - // this is necessary because of #5133 |
|
273 | - if (elgg_is_xhr()) { |
|
274 | - register_error(_elgg_services()->translator->translate('js:security:token_refresh_failed', [$this->config->getSiteUrl()])); |
|
275 | - } else { |
|
276 | - register_error(_elgg_services()->translator->translate('actiongatekeeper:tokeninvalid')); |
|
277 | - } |
|
278 | - } |
|
279 | - } else { |
|
280 | - $req = _elgg_services()->request; |
|
281 | - $length = $req->server->get('CONTENT_LENGTH'); |
|
282 | - $post_count = count($req->request); |
|
283 | - if ($length && $post_count < 1) { |
|
284 | - // The size of $_POST or uploaded file has exceed the size limit |
|
285 | - $error_msg = _elgg_services()->hooks->trigger('action_gatekeeper:upload_exceeded_msg', 'all', [ |
|
286 | - 'post_size' => $length, |
|
287 | - 'visible_errors' => $visible_errors, |
|
288 | - ], _elgg_services()->translator->translate('actiongatekeeper:uploadexceeded')); |
|
289 | - } else { |
|
290 | - $error_msg = _elgg_services()->translator->translate('actiongatekeeper:missingfields'); |
|
291 | - } |
|
292 | - if ($visible_errors) { |
|
293 | - register_error($error_msg); |
|
294 | - } |
|
295 | - } |
|
296 | - |
|
297 | - return false; |
|
298 | - } |
|
299 | - |
|
300 | - /** |
|
301 | - * Is the token timestamp within acceptable range? |
|
302 | - * |
|
303 | - * @param int $ts timestamp from the CSRF token |
|
304 | - * |
|
305 | - * @return bool |
|
306 | - */ |
|
307 | - protected function validateTokenTimestamp($ts) { |
|
308 | - $timeout = $this->getActionTokenTimeout(); |
|
309 | - $now = $this->getCurrentTime()->getTimestamp(); |
|
310 | - return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)); |
|
311 | - } |
|
312 | - |
|
313 | - /** |
|
314 | - * @see ActionsService::validateActionToken |
|
315 | - * @access private |
|
316 | - * @since 1.9.0 |
|
317 | - * @return int number of seconds that action token is valid |
|
318 | - */ |
|
319 | - public function getActionTokenTimeout() { |
|
320 | - if (($timeout = $this->config->get('action_token_timeout')) === null) { |
|
321 | - // default to 2 hours |
|
322 | - $timeout = 2; |
|
323 | - } |
|
324 | - $hour = 60 * 60; |
|
325 | - return (int) ((float) $timeout * $hour); |
|
326 | - } |
|
327 | - |
|
328 | - /** |
|
329 | - * @return bool |
|
330 | - * @see action_gatekeeper |
|
331 | - * @access private |
|
332 | - */ |
|
333 | - public function gatekeeper($action) { |
|
334 | - if ($action === 'login') { |
|
335 | - if ($this->validateActionToken(false)) { |
|
336 | - return true; |
|
337 | - } |
|
338 | - |
|
339 | - $token = get_input('__elgg_token'); |
|
340 | - $ts = (int) get_input('__elgg_ts'); |
|
341 | - if ($token && $this->validateTokenTimestamp($ts)) { |
|
342 | - // The tokens are present and the time looks valid: this is probably a mismatch due to the |
|
343 | - // login form being on a different domain. |
|
344 | - register_error(_elgg_services()->translator->translate('actiongatekeeper:crosssitelogin')); |
|
345 | - _elgg_services()->responseFactory->redirect('login', 'csrf'); |
|
346 | - return false; |
|
347 | - } |
|
348 | - } |
|
239 | + if (!$ts) { |
|
240 | + $ts = get_input('__elgg_ts'); |
|
241 | + } |
|
242 | + |
|
243 | + $session_id = $this->session->getId(); |
|
244 | + |
|
245 | + if (($token) && ($ts) && ($session_id)) { |
|
246 | + if ($this->validateTokenOwnership($token, $ts)) { |
|
247 | + if ($this->validateTokenTimestamp($ts)) { |
|
248 | + // We have already got this far, so unless anything |
|
249 | + // else says something to the contrary we assume we're ok |
|
250 | + $returnval = _elgg_services()->hooks->trigger('action_gatekeeper:permissions:check', 'all', [ |
|
251 | + 'token' => $token, |
|
252 | + 'time' => $ts |
|
253 | + ], true); |
|
254 | + |
|
255 | + if ($returnval) { |
|
256 | + return true; |
|
257 | + } else if ($visible_errors) { |
|
258 | + register_error(_elgg_services()->translator->translate('actiongatekeeper:pluginprevents')); |
|
259 | + } |
|
260 | + } else if ($visible_errors) { |
|
261 | + // this is necessary because of #5133 |
|
262 | + if (elgg_is_xhr()) { |
|
263 | + register_error(_elgg_services()->translator->translate( |
|
264 | + 'js:security:token_refresh_failed', |
|
265 | + [$this->config->getSiteUrl()] |
|
266 | + )); |
|
267 | + } else { |
|
268 | + register_error(_elgg_services()->translator->translate('actiongatekeeper:timeerror')); |
|
269 | + } |
|
270 | + } |
|
271 | + } else if ($visible_errors) { |
|
272 | + // this is necessary because of #5133 |
|
273 | + if (elgg_is_xhr()) { |
|
274 | + register_error(_elgg_services()->translator->translate('js:security:token_refresh_failed', [$this->config->getSiteUrl()])); |
|
275 | + } else { |
|
276 | + register_error(_elgg_services()->translator->translate('actiongatekeeper:tokeninvalid')); |
|
277 | + } |
|
278 | + } |
|
279 | + } else { |
|
280 | + $req = _elgg_services()->request; |
|
281 | + $length = $req->server->get('CONTENT_LENGTH'); |
|
282 | + $post_count = count($req->request); |
|
283 | + if ($length && $post_count < 1) { |
|
284 | + // The size of $_POST or uploaded file has exceed the size limit |
|
285 | + $error_msg = _elgg_services()->hooks->trigger('action_gatekeeper:upload_exceeded_msg', 'all', [ |
|
286 | + 'post_size' => $length, |
|
287 | + 'visible_errors' => $visible_errors, |
|
288 | + ], _elgg_services()->translator->translate('actiongatekeeper:uploadexceeded')); |
|
289 | + } else { |
|
290 | + $error_msg = _elgg_services()->translator->translate('actiongatekeeper:missingfields'); |
|
291 | + } |
|
292 | + if ($visible_errors) { |
|
293 | + register_error($error_msg); |
|
294 | + } |
|
295 | + } |
|
296 | + |
|
297 | + return false; |
|
298 | + } |
|
299 | + |
|
300 | + /** |
|
301 | + * Is the token timestamp within acceptable range? |
|
302 | + * |
|
303 | + * @param int $ts timestamp from the CSRF token |
|
304 | + * |
|
305 | + * @return bool |
|
306 | + */ |
|
307 | + protected function validateTokenTimestamp($ts) { |
|
308 | + $timeout = $this->getActionTokenTimeout(); |
|
309 | + $now = $this->getCurrentTime()->getTimestamp(); |
|
310 | + return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)); |
|
311 | + } |
|
312 | + |
|
313 | + /** |
|
314 | + * @see ActionsService::validateActionToken |
|
315 | + * @access private |
|
316 | + * @since 1.9.0 |
|
317 | + * @return int number of seconds that action token is valid |
|
318 | + */ |
|
319 | + public function getActionTokenTimeout() { |
|
320 | + if (($timeout = $this->config->get('action_token_timeout')) === null) { |
|
321 | + // default to 2 hours |
|
322 | + $timeout = 2; |
|
323 | + } |
|
324 | + $hour = 60 * 60; |
|
325 | + return (int) ((float) $timeout * $hour); |
|
326 | + } |
|
327 | + |
|
328 | + /** |
|
329 | + * @return bool |
|
330 | + * @see action_gatekeeper |
|
331 | + * @access private |
|
332 | + */ |
|
333 | + public function gatekeeper($action) { |
|
334 | + if ($action === 'login') { |
|
335 | + if ($this->validateActionToken(false)) { |
|
336 | + return true; |
|
337 | + } |
|
338 | + |
|
339 | + $token = get_input('__elgg_token'); |
|
340 | + $ts = (int) get_input('__elgg_ts'); |
|
341 | + if ($token && $this->validateTokenTimestamp($ts)) { |
|
342 | + // The tokens are present and the time looks valid: this is probably a mismatch due to the |
|
343 | + // login form being on a different domain. |
|
344 | + register_error(_elgg_services()->translator->translate('actiongatekeeper:crosssitelogin')); |
|
345 | + _elgg_services()->responseFactory->redirect('login', 'csrf'); |
|
346 | + return false; |
|
347 | + } |
|
348 | + } |
|
349 | 349 | |
350 | - if ($this->validateActionToken()) { |
|
351 | - return true; |
|
352 | - } |
|
350 | + if ($this->validateActionToken()) { |
|
351 | + return true; |
|
352 | + } |
|
353 | 353 | |
354 | - _elgg_services()->responseFactory->redirect(REFERER, 'csrf'); |
|
355 | - return false; |
|
356 | - } |
|
357 | - |
|
358 | - /** |
|
359 | - * Was the given token generated for the session defined by session_token? |
|
360 | - * |
|
361 | - * @param string $token CSRF token |
|
362 | - * @param int $timestamp Unix time |
|
363 | - * @param string $session_token Session-specific token |
|
364 | - * |
|
365 | - * @return bool |
|
366 | - * @access private |
|
367 | - */ |
|
368 | - public function validateTokenOwnership($token, $timestamp, $session_token = '') { |
|
369 | - $required_token = $this->generateActionToken($timestamp, $session_token); |
|
370 | - |
|
371 | - return _elgg_services()->crypto->areEqual($token, $required_token); |
|
372 | - } |
|
354 | + _elgg_services()->responseFactory->redirect(REFERER, 'csrf'); |
|
355 | + return false; |
|
356 | + } |
|
357 | + |
|
358 | + /** |
|
359 | + * Was the given token generated for the session defined by session_token? |
|
360 | + * |
|
361 | + * @param string $token CSRF token |
|
362 | + * @param int $timestamp Unix time |
|
363 | + * @param string $session_token Session-specific token |
|
364 | + * |
|
365 | + * @return bool |
|
366 | + * @access private |
|
367 | + */ |
|
368 | + public function validateTokenOwnership($token, $timestamp, $session_token = '') { |
|
369 | + $required_token = $this->generateActionToken($timestamp, $session_token); |
|
370 | + |
|
371 | + return _elgg_services()->crypto->areEqual($token, $required_token); |
|
372 | + } |
|
373 | 373 | |
374 | - /** |
|
375 | - * Generate a token from a session token (specifying the user), the timestamp, and the site key. |
|
376 | - * |
|
377 | - * @see generate_action_token |
|
378 | - * |
|
379 | - * @param int $timestamp Unix timestamp |
|
380 | - * @param string $session_token Session-specific token |
|
381 | - * |
|
382 | - * @return string |
|
383 | - * @access private |
|
384 | - */ |
|
385 | - public function generateActionToken($timestamp, $session_token = '') { |
|
386 | - if (!$session_token) { |
|
387 | - $session_token = elgg_get_session()->get('__elgg_session'); |
|
388 | - if (!$session_token) { |
|
389 | - return false; |
|
390 | - } |
|
391 | - } |
|
392 | - |
|
393 | - return _elgg_services()->crypto->getHmac([(int) $timestamp, $session_token], 'md5') |
|
394 | - ->getToken(); |
|
395 | - } |
|
374 | + /** |
|
375 | + * Generate a token from a session token (specifying the user), the timestamp, and the site key. |
|
376 | + * |
|
377 | + * @see generate_action_token |
|
378 | + * |
|
379 | + * @param int $timestamp Unix timestamp |
|
380 | + * @param string $session_token Session-specific token |
|
381 | + * |
|
382 | + * @return string |
|
383 | + * @access private |
|
384 | + */ |
|
385 | + public function generateActionToken($timestamp, $session_token = '') { |
|
386 | + if (!$session_token) { |
|
387 | + $session_token = elgg_get_session()->get('__elgg_session'); |
|
388 | + if (!$session_token) { |
|
389 | + return false; |
|
390 | + } |
|
391 | + } |
|
392 | + |
|
393 | + return _elgg_services()->crypto->getHmac([(int) $timestamp, $session_token], 'md5') |
|
394 | + ->getToken(); |
|
395 | + } |
|
396 | 396 | |
397 | - /** |
|
398 | - * @see elgg_action_exists |
|
399 | - * @access private |
|
400 | - */ |
|
401 | - public function exists($action) { |
|
402 | - return (isset($this->actions[$action]) && file_exists($this->actions[$action]['file'])); |
|
403 | - } |
|
397 | + /** |
|
398 | + * @see elgg_action_exists |
|
399 | + * @access private |
|
400 | + */ |
|
401 | + public function exists($action) { |
|
402 | + return (isset($this->actions[$action]) && file_exists($this->actions[$action]['file'])); |
|
403 | + } |
|
404 | 404 | |
405 | - /** |
|
406 | - * @see ajax_forward_hook |
|
407 | - * @access private |
|
408 | - * @deprecated 2.3 |
|
409 | - */ |
|
410 | - public function ajaxForwardHook($hook, $reason, $forward_url, $params) { |
|
411 | - if (!elgg_is_xhr()) { |
|
412 | - return; |
|
413 | - } |
|
414 | - |
|
415 | - // grab any data echo'd in the action |
|
416 | - $output = ob_get_clean(); |
|
417 | - |
|
418 | - if ($reason == 'walled_garden' || $reason == 'csrf') { |
|
419 | - $reason = '403'; |
|
420 | - } |
|
421 | - |
|
422 | - $status_code = (int) $reason; |
|
423 | - if ($status_code < 100 || ($status_code > 299 && $status_code < 400) || $status_code > 599) { |
|
424 | - // We only want to preserve OK and error codes |
|
425 | - // Redirect responses should be converted to OK responses as this is an XHR request |
|
426 | - $status_code = ELGG_HTTP_OK; |
|
427 | - } |
|
428 | - |
|
429 | - $response = elgg_ok_response($output, '', $forward_url, $status_code); |
|
430 | - |
|
431 | - $headers = $response->getHeaders(); |
|
432 | - $headers['Content-Type'] = 'application/json; charset=UTF-8'; |
|
433 | - $response->setHeaders($headers); |
|
434 | - |
|
435 | - _elgg_services()->responseFactory->respond($response); |
|
436 | - exit; |
|
437 | - } |
|
405 | + /** |
|
406 | + * @see ajax_forward_hook |
|
407 | + * @access private |
|
408 | + * @deprecated 2.3 |
|
409 | + */ |
|
410 | + public function ajaxForwardHook($hook, $reason, $forward_url, $params) { |
|
411 | + if (!elgg_is_xhr()) { |
|
412 | + return; |
|
413 | + } |
|
414 | + |
|
415 | + // grab any data echo'd in the action |
|
416 | + $output = ob_get_clean(); |
|
417 | + |
|
418 | + if ($reason == 'walled_garden' || $reason == 'csrf') { |
|
419 | + $reason = '403'; |
|
420 | + } |
|
421 | + |
|
422 | + $status_code = (int) $reason; |
|
423 | + if ($status_code < 100 || ($status_code > 299 && $status_code < 400) || $status_code > 599) { |
|
424 | + // We only want to preserve OK and error codes |
|
425 | + // Redirect responses should be converted to OK responses as this is an XHR request |
|
426 | + $status_code = ELGG_HTTP_OK; |
|
427 | + } |
|
428 | + |
|
429 | + $response = elgg_ok_response($output, '', $forward_url, $status_code); |
|
430 | + |
|
431 | + $headers = $response->getHeaders(); |
|
432 | + $headers['Content-Type'] = 'application/json; charset=UTF-8'; |
|
433 | + $response->setHeaders($headers); |
|
434 | + |
|
435 | + _elgg_services()->responseFactory->respond($response); |
|
436 | + exit; |
|
437 | + } |
|
438 | 438 | |
439 | - /** |
|
440 | - * @see ajax_action_hook |
|
441 | - * @access private |
|
442 | - * @deprecated 2.3 |
|
443 | - */ |
|
444 | - public function ajaxActionHook() { |
|
445 | - if (elgg_is_xhr()) { |
|
446 | - ob_start(); |
|
447 | - } |
|
448 | - } |
|
449 | - |
|
450 | - /** |
|
451 | - * Get all actions |
|
452 | - * |
|
453 | - * @return array |
|
454 | - */ |
|
455 | - public function getAllActions() { |
|
456 | - return $this->actions; |
|
457 | - } |
|
458 | - |
|
459 | - /** |
|
460 | - * Send an updated CSRF token, provided the page's current tokens were not fake. |
|
461 | - * |
|
462 | - * @return ResponseBuilder |
|
463 | - * @access private |
|
464 | - */ |
|
465 | - public function handleTokenRefreshRequest() { |
|
466 | - if (!elgg_is_xhr()) { |
|
467 | - return false; |
|
468 | - } |
|
469 | - |
|
470 | - // the page's session_token might have expired (not matching __elgg_session in the session), but |
|
471 | - // we still allow it to be given to validate the tokens in the page. |
|
472 | - $session_token = get_input('session_token', null, false); |
|
473 | - $pairs = (array) get_input('pairs', [], false); |
|
474 | - $valid_tokens = (object) []; |
|
475 | - foreach ($pairs as $pair) { |
|
476 | - list($ts, $token) = explode(',', $pair, 2); |
|
477 | - if ($this->validateTokenOwnership($token, $ts, $session_token)) { |
|
478 | - $valid_tokens->{$token} = true; |
|
479 | - } |
|
480 | - } |
|
481 | - |
|
482 | - $ts = $this->getCurrentTime()->getTimestamp(); |
|
483 | - $token = $this->generateActionToken($ts); |
|
484 | - $data = [ |
|
485 | - 'token' => [ |
|
486 | - '__elgg_ts' => $ts, |
|
487 | - '__elgg_token' => $token, |
|
488 | - 'logged_in' => $this->session->isLoggedIn(), |
|
489 | - ], |
|
490 | - 'valid_tokens' => $valid_tokens, |
|
491 | - 'session_token' => $this->session->get('__elgg_session'), |
|
492 | - 'user_guid' => $this->session->getLoggedInUserGuid(), |
|
493 | - ]; |
|
494 | - |
|
495 | - elgg_set_http_header("Content-Type: application/json;charset=utf-8"); |
|
496 | - return elgg_ok_response($data); |
|
497 | - } |
|
439 | + /** |
|
440 | + * @see ajax_action_hook |
|
441 | + * @access private |
|
442 | + * @deprecated 2.3 |
|
443 | + */ |
|
444 | + public function ajaxActionHook() { |
|
445 | + if (elgg_is_xhr()) { |
|
446 | + ob_start(); |
|
447 | + } |
|
448 | + } |
|
449 | + |
|
450 | + /** |
|
451 | + * Get all actions |
|
452 | + * |
|
453 | + * @return array |
|
454 | + */ |
|
455 | + public function getAllActions() { |
|
456 | + return $this->actions; |
|
457 | + } |
|
458 | + |
|
459 | + /** |
|
460 | + * Send an updated CSRF token, provided the page's current tokens were not fake. |
|
461 | + * |
|
462 | + * @return ResponseBuilder |
|
463 | + * @access private |
|
464 | + */ |
|
465 | + public function handleTokenRefreshRequest() { |
|
466 | + if (!elgg_is_xhr()) { |
|
467 | + return false; |
|
468 | + } |
|
469 | + |
|
470 | + // the page's session_token might have expired (not matching __elgg_session in the session), but |
|
471 | + // we still allow it to be given to validate the tokens in the page. |
|
472 | + $session_token = get_input('session_token', null, false); |
|
473 | + $pairs = (array) get_input('pairs', [], false); |
|
474 | + $valid_tokens = (object) []; |
|
475 | + foreach ($pairs as $pair) { |
|
476 | + list($ts, $token) = explode(',', $pair, 2); |
|
477 | + if ($this->validateTokenOwnership($token, $ts, $session_token)) { |
|
478 | + $valid_tokens->{$token} = true; |
|
479 | + } |
|
480 | + } |
|
481 | + |
|
482 | + $ts = $this->getCurrentTime()->getTimestamp(); |
|
483 | + $token = $this->generateActionToken($ts); |
|
484 | + $data = [ |
|
485 | + 'token' => [ |
|
486 | + '__elgg_ts' => $ts, |
|
487 | + '__elgg_token' => $token, |
|
488 | + 'logged_in' => $this->session->isLoggedIn(), |
|
489 | + ], |
|
490 | + 'valid_tokens' => $valid_tokens, |
|
491 | + 'session_token' => $this->session->get('__elgg_session'), |
|
492 | + 'user_guid' => $this->session->getLoggedInUserGuid(), |
|
493 | + ]; |
|
494 | + |
|
495 | + elgg_set_http_header("Content-Type: application/json;charset=utf-8"); |
|
496 | + return elgg_ok_response($data); |
|
497 | + } |
|
498 | 498 | } |
499 | 499 |
@@ -15,718 +15,718 @@ |
||
15 | 15 | * @property-read string $prefix Elgg table prefix (read only) |
16 | 16 | */ |
17 | 17 | class Database { |
18 | - use Profilable; |
|
19 | - |
|
20 | - const DELAYED_QUERY = 'q'; |
|
21 | - const DELAYED_TYPE = 't'; |
|
22 | - const DELAYED_HANDLER = 'h'; |
|
23 | - const DELAYED_PARAMS = 'p'; |
|
24 | - |
|
25 | - /** |
|
26 | - * @var string $table_prefix Prefix for database tables |
|
27 | - */ |
|
28 | - private $table_prefix; |
|
29 | - |
|
30 | - /** |
|
31 | - * @var Connection[] |
|
32 | - */ |
|
33 | - private $connections = []; |
|
34 | - |
|
35 | - /** |
|
36 | - * @var int $query_count The number of queries made |
|
37 | - */ |
|
38 | - private $query_count = 0; |
|
39 | - |
|
40 | - /** |
|
41 | - * Query cache for select queries. |
|
42 | - * |
|
43 | - * Queries and their results are stored in this cache as: |
|
44 | - * <code> |
|
45 | - * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN) |
|
46 | - * </code> |
|
47 | - * @see \Elgg\Database::getResults() for details on the hash. |
|
48 | - * |
|
49 | - * @var \Elgg\Cache\LRUCache $query_cache The cache |
|
50 | - */ |
|
51 | - private $query_cache = null; |
|
52 | - |
|
53 | - /** |
|
54 | - * @var int $query_cache_size The number of queries to cache |
|
55 | - */ |
|
56 | - private $query_cache_size = 50; |
|
57 | - |
|
58 | - /** |
|
59 | - * Queries are saved as an array with the DELAYED_* constants as keys. |
|
60 | - * |
|
61 | - * @see registerDelayedQuery |
|
62 | - * |
|
63 | - * @var array $delayed_queries Queries to be run during shutdown |
|
64 | - */ |
|
65 | - private $delayed_queries = []; |
|
66 | - |
|
67 | - /** |
|
68 | - * @var bool $installed Is the database installed? |
|
69 | - */ |
|
70 | - private $installed = false; |
|
71 | - |
|
72 | - /** |
|
73 | - * @var \Elgg\Database\Config $config Database configuration |
|
74 | - */ |
|
75 | - private $config; |
|
76 | - |
|
77 | - /** |
|
78 | - * @var \Elgg\Logger $logger The logger |
|
79 | - */ |
|
80 | - private $logger; |
|
81 | - |
|
82 | - /** |
|
83 | - * Constructor |
|
84 | - * |
|
85 | - * @param \Elgg\Database\Config $config Database configuration |
|
86 | - * @param \Elgg\Logger $logger The logger |
|
87 | - */ |
|
88 | - public function __construct(\Elgg\Database\Config $config, \Elgg\Logger $logger = null) { |
|
89 | - |
|
90 | - $this->logger = $logger; |
|
91 | - $this->config = $config; |
|
92 | - |
|
93 | - $this->table_prefix = $config->getTablePrefix(); |
|
94 | - |
|
95 | - $this->enableQueryCache(); |
|
96 | - } |
|
97 | - |
|
98 | - /** |
|
99 | - * Set the logger object |
|
100 | - * |
|
101 | - * @param Logger $logger The logger |
|
102 | - * @return void |
|
103 | - * @access private |
|
104 | - */ |
|
105 | - public function setLogger(Logger $logger) { |
|
106 | - $this->logger = $logger; |
|
107 | - } |
|
108 | - |
|
109 | - /** |
|
110 | - * Gets (if required, also creates) a DB connection. |
|
111 | - * |
|
112 | - * @param string $type The type of link we want: "read", "write" or "readwrite". |
|
113 | - * |
|
114 | - * @return Connection |
|
115 | - * @throws \DatabaseException |
|
116 | - */ |
|
117 | - protected function getConnection($type) { |
|
118 | - if (isset($this->connections[$type])) { |
|
119 | - return $this->connections[$type]; |
|
120 | - } else if (isset($this->connections['readwrite'])) { |
|
121 | - return $this->connections['readwrite']; |
|
122 | - } else { |
|
123 | - $this->setupConnections(); |
|
124 | - return $this->getConnection($type); |
|
125 | - } |
|
126 | - } |
|
127 | - |
|
128 | - /** |
|
129 | - * Establish database connections |
|
130 | - * |
|
131 | - * If the configuration has been set up for multiple read/write databases, set those |
|
132 | - * links up separately; otherwise just create the one database link. |
|
133 | - * |
|
134 | - * @return void |
|
135 | - * @throws \DatabaseException |
|
136 | - * @access private |
|
137 | - */ |
|
138 | - public function setupConnections() { |
|
139 | - if ($this->config->isDatabaseSplit()) { |
|
140 | - $this->connect('read'); |
|
141 | - $this->connect('write'); |
|
142 | - } else { |
|
143 | - $this->connect('readwrite'); |
|
144 | - } |
|
145 | - } |
|
146 | - |
|
147 | - /** |
|
148 | - * Establish a connection to the database server |
|
149 | - * |
|
150 | - * Connect to the database server and use the Elgg database for a particular database link |
|
151 | - * |
|
152 | - * @param string $type The type of database connection. "read", "write", or "readwrite". |
|
153 | - * |
|
154 | - * @return void |
|
155 | - * @throws \DatabaseException |
|
156 | - * @access private |
|
157 | - */ |
|
158 | - public function connect($type = "readwrite") { |
|
159 | - $conf = $this->config->getConnectionConfig($type); |
|
160 | - |
|
161 | - $params = [ |
|
162 | - 'dbname' => $conf['database'], |
|
163 | - 'user' => $conf['user'], |
|
164 | - 'password' => $conf['password'], |
|
165 | - 'host' => $conf['host'], |
|
166 | - 'charset' => 'utf8', |
|
167 | - 'driver' => 'pdo_mysql', |
|
168 | - ]; |
|
169 | - |
|
170 | - try { |
|
171 | - $this->connections[$type] = DriverManager::getConnection($params); |
|
172 | - $this->connections[$type]->setFetchMode(\PDO::FETCH_OBJ); |
|
173 | - |
|
174 | - // https://github.com/Elgg/Elgg/issues/8121 |
|
175 | - $sub_query = "SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '')"; |
|
176 | - $this->connections[$type]->exec("SET SESSION sql_mode=($sub_query);"); |
|
177 | - } catch (\PDOException $e) { |
|
178 | - // @todo just allow PDO exceptions |
|
179 | - // http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html |
|
180 | - if ($e->getCode() == 1102 || $e->getCode() == 1049) { |
|
181 | - $msg = "Elgg couldn't select the database '{$conf['database']}'. " |
|
182 | - . "Please check that the database is created and you have access to it."; |
|
183 | - } else { |
|
184 | - $msg = "Elgg couldn't connect to the database using the given credentials. Check the settings file."; |
|
185 | - } |
|
186 | - throw new \DatabaseException($msg); |
|
187 | - } |
|
188 | - } |
|
189 | - |
|
190 | - /** |
|
191 | - * Retrieve rows from the database. |
|
192 | - * |
|
193 | - * Queries are executed with {@link \Elgg\Database::executeQuery()} and results |
|
194 | - * are retrieved with {@link \PDO::fetchObject()}. If a callback |
|
195 | - * function $callback is defined, each row will be passed as a single |
|
196 | - * argument to $callback. If no callback function is defined, the |
|
197 | - * entire result set is returned as an array. |
|
198 | - * |
|
199 | - * @param string $query The query being passed. |
|
200 | - * @param callable $callback Optionally, the name of a function to call back to on each row |
|
201 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
202 | - * |
|
203 | - * @return array An array of database result objects or callback function results. If the query |
|
204 | - * returned nothing, an empty array. |
|
205 | - * @throws \DatabaseException |
|
206 | - */ |
|
207 | - public function getData($query, $callback = null, array $params = []) { |
|
208 | - return $this->getResults($query, $callback, false, $params); |
|
209 | - } |
|
210 | - |
|
211 | - /** |
|
212 | - * Retrieve a single row from the database. |
|
213 | - * |
|
214 | - * Similar to {@link \Elgg\Database::getData()} but returns only the first row |
|
215 | - * matched. If a callback function $callback is specified, the row will be passed |
|
216 | - * as the only argument to $callback. |
|
217 | - * |
|
218 | - * @param string $query The query to execute. |
|
219 | - * @param callable $callback A callback function to apply to the row |
|
220 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
221 | - * |
|
222 | - * @return mixed A single database result object or the result of the callback function. |
|
223 | - * @throws \DatabaseException |
|
224 | - */ |
|
225 | - public function getDataRow($query, $callback = null, array $params = []) { |
|
226 | - return $this->getResults($query, $callback, true, $params); |
|
227 | - } |
|
228 | - |
|
229 | - /** |
|
230 | - * Insert a row into the database. |
|
231 | - * |
|
232 | - * @note Altering the DB invalidates all queries in the query cache. |
|
233 | - * |
|
234 | - * @param string $query The query to execute. |
|
235 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
236 | - * |
|
237 | - * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is |
|
238 | - * defined, 0 if not, and false on failure. |
|
239 | - * @throws \DatabaseException |
|
240 | - */ |
|
241 | - public function insertData($query, array $params = []) { |
|
242 | - |
|
243 | - if ($this->logger) { |
|
244 | - $this->logger->info("DB query $query"); |
|
245 | - } |
|
246 | - |
|
247 | - $connection = $this->getConnection('write'); |
|
248 | - |
|
249 | - $this->invalidateQueryCache(); |
|
250 | - |
|
251 | - $this->executeQuery($query, $connection, $params); |
|
252 | - return (int) $connection->lastInsertId(); |
|
253 | - } |
|
254 | - |
|
255 | - /** |
|
256 | - * Update the database. |
|
257 | - * |
|
258 | - * @note Altering the DB invalidates all queries in the query cache. |
|
259 | - * |
|
260 | - * @note WARNING! update_data() has the 2nd and 3rd arguments reversed. |
|
261 | - * |
|
262 | - * @param string $query The query to run. |
|
263 | - * @param bool $get_num_rows Return the number of rows affected (default: false). |
|
264 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
265 | - * |
|
266 | - * @return bool|int |
|
267 | - * @throws \DatabaseException |
|
268 | - */ |
|
269 | - public function updateData($query, $get_num_rows = false, array $params = []) { |
|
270 | - |
|
271 | - if ($this->logger) { |
|
272 | - $this->logger->info("DB query $query"); |
|
273 | - } |
|
274 | - |
|
275 | - $this->invalidateQueryCache(); |
|
276 | - |
|
277 | - $stmt = $this->executeQuery($query, $this->getConnection('write'), $params); |
|
278 | - if ($get_num_rows) { |
|
279 | - return $stmt->rowCount(); |
|
280 | - } else { |
|
281 | - return true; |
|
282 | - } |
|
283 | - } |
|
284 | - |
|
285 | - /** |
|
286 | - * Delete data from the database |
|
287 | - * |
|
288 | - * @note Altering the DB invalidates all queries in query cache. |
|
289 | - * |
|
290 | - * @param string $query The SQL query to run |
|
291 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
292 | - * |
|
293 | - * @return int The number of affected rows |
|
294 | - * @throws \DatabaseException |
|
295 | - */ |
|
296 | - public function deleteData($query, array $params = []) { |
|
297 | - |
|
298 | - if ($this->logger) { |
|
299 | - $this->logger->info("DB query $query"); |
|
300 | - } |
|
301 | - |
|
302 | - $connection = $this->getConnection('write'); |
|
303 | - |
|
304 | - $this->invalidateQueryCache(); |
|
305 | - |
|
306 | - $stmt = $this->executeQuery("$query", $connection, $params); |
|
307 | - return (int) $stmt->rowCount(); |
|
308 | - } |
|
309 | - |
|
310 | - /** |
|
311 | - * Get a string that uniquely identifies a callback during the current request. |
|
312 | - * |
|
313 | - * This is used to cache queries whose results were transformed by the callback. If the callback involves |
|
314 | - * object method calls of the same class, different instances will return different values. |
|
315 | - * |
|
316 | - * @param callable $callback The callable value to fingerprint |
|
317 | - * |
|
318 | - * @return string A string that is unique for each callable passed in |
|
319 | - * @since 1.9.4 |
|
320 | - * @access private |
|
321 | - * @todo Make this protected once we can setAccessible(true) via reflection |
|
322 | - */ |
|
323 | - public function fingerprintCallback($callback) { |
|
324 | - if (is_string($callback)) { |
|
325 | - return $callback; |
|
326 | - } |
|
327 | - if (is_object($callback)) { |
|
328 | - return spl_object_hash($callback) . "::__invoke"; |
|
329 | - } |
|
330 | - if (is_array($callback)) { |
|
331 | - if (is_string($callback[0])) { |
|
332 | - return "{$callback[0]}::{$callback[1]}"; |
|
333 | - } |
|
334 | - return spl_object_hash($callback[0]) . "::{$callback[1]}"; |
|
335 | - } |
|
336 | - // this should not happen |
|
337 | - return ""; |
|
338 | - } |
|
339 | - |
|
340 | - /** |
|
341 | - * Handles queries that return results, running the results through a |
|
342 | - * an optional callback function. This is for R queries (from CRUD). |
|
343 | - * |
|
344 | - * @param string $query The select query to execute |
|
345 | - * @param string $callback An optional callback function to run on each row |
|
346 | - * @param bool $single Return only a single result? |
|
347 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
348 | - * |
|
349 | - * @return array An array of database result objects or callback function results. If the query |
|
350 | - * returned nothing, an empty array. |
|
351 | - * @throws \DatabaseException |
|
352 | - */ |
|
353 | - protected function getResults($query, $callback = null, $single = false, array $params = []) { |
|
354 | - |
|
355 | - // Since we want to cache results of running the callback, we need to |
|
356 | - // need to namespace the query with the callback and single result request. |
|
357 | - // https://github.com/elgg/elgg/issues/4049 |
|
358 | - $query_id = (int) $single . $query . '|'; |
|
359 | - if ($params) { |
|
360 | - $query_id .= serialize($params) . '|'; |
|
361 | - } |
|
362 | - |
|
363 | - if ($callback) { |
|
364 | - if (!is_callable($callback)) { |
|
365 | - throw new \RuntimeException('$callback must be a callable function. Given ' |
|
366 | - . _elgg_services()->handlers->describeCallable($callback)); |
|
367 | - } |
|
368 | - $query_id .= $this->fingerprintCallback($callback); |
|
369 | - } |
|
370 | - // MD5 yields smaller mem usage for cache and cleaner logs |
|
371 | - $hash = md5($query_id); |
|
372 | - |
|
373 | - // Is cached? |
|
374 | - if ($this->query_cache) { |
|
375 | - if (isset($this->query_cache[$hash])) { |
|
376 | - if ($this->logger) { |
|
377 | - // TODO add params in $query here |
|
378 | - $this->logger->info("DB query $query results returned from cache (hash: $hash)"); |
|
379 | - } |
|
380 | - return $this->query_cache[$hash]; |
|
381 | - } |
|
382 | - } |
|
383 | - |
|
384 | - $return = []; |
|
385 | - |
|
386 | - $stmt = $this->executeQuery($query, $this->getConnection('read'), $params); |
|
387 | - while ($row = $stmt->fetch()) { |
|
388 | - if ($callback) { |
|
389 | - $row = call_user_func($callback, $row); |
|
390 | - } |
|
391 | - |
|
392 | - if ($single) { |
|
393 | - $return = $row; |
|
394 | - break; |
|
395 | - } else { |
|
396 | - $return[] = $row; |
|
397 | - } |
|
398 | - } |
|
399 | - |
|
400 | - // Cache result |
|
401 | - if ($this->query_cache) { |
|
402 | - $this->query_cache[$hash] = $return; |
|
403 | - if ($this->logger) { |
|
404 | - // TODO add params in $query here |
|
405 | - $this->logger->info("DB query $query results cached (hash: $hash)"); |
|
406 | - } |
|
407 | - } |
|
408 | - |
|
409 | - return $return; |
|
410 | - } |
|
411 | - |
|
412 | - /** |
|
413 | - * Execute a query. |
|
414 | - * |
|
415 | - * $query is executed via {@link Connection::query}. If there is an SQL error, |
|
416 | - * a {@link DatabaseException} is thrown. |
|
417 | - * |
|
418 | - * @param string $query The query |
|
419 | - * @param Connection $connection The DB connection |
|
420 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
421 | - * |
|
422 | - * @return Statement The result of the query |
|
423 | - * @throws \DatabaseException |
|
424 | - */ |
|
425 | - protected function executeQuery($query, Connection $connection, array $params = []) { |
|
426 | - if ($query == null) { |
|
427 | - throw new \DatabaseException("Query cannot be null"); |
|
428 | - } |
|
429 | - |
|
430 | - $this->query_count++; |
|
431 | - |
|
432 | - if ($this->timer) { |
|
433 | - $timer_key = preg_replace('~\\s+~', ' ', trim($query . '|' . serialize($params))); |
|
434 | - $this->timer->begin(['SQL', $timer_key]); |
|
435 | - } |
|
436 | - |
|
437 | - try { |
|
438 | - if ($params) { |
|
439 | - $value = $connection->executeQuery($query, $params); |
|
440 | - } else { |
|
441 | - // faster |
|
442 | - $value = $connection->query($query); |
|
443 | - } |
|
444 | - } catch (\Exception $e) { |
|
445 | - throw new \DatabaseException($e->getMessage() . "\n\n" |
|
446 | - . "QUERY: $query \n\n" |
|
447 | - . "PARAMS: " . print_r($params, true)); |
|
448 | - } |
|
449 | - |
|
450 | - if ($this->timer) { |
|
451 | - $this->timer->end(['SQL', $timer_key]); |
|
452 | - } |
|
453 | - |
|
454 | - return $value; |
|
455 | - } |
|
456 | - |
|
457 | - /** |
|
458 | - * Runs a full database script from disk. |
|
459 | - * |
|
460 | - * The file specified should be a standard SQL file as created by |
|
461 | - * mysqldump or similar. Statements must be terminated with ; |
|
462 | - * and a newline character (\n or \r\n). |
|
463 | - * |
|
464 | - * The special string 'prefix_' is replaced with the database prefix |
|
465 | - * as defined in {@link $this->tablePrefix}. |
|
466 | - * |
|
467 | - * @warning Only single line comments are supported. A comment |
|
468 | - * must start with '-- ' or '# ', where the comment sign is at the |
|
469 | - * very beginning of each line. |
|
470 | - * |
|
471 | - * @warning Errors do not halt execution of the script. If a line |
|
472 | - * generates an error, the error message is saved and the |
|
473 | - * next line is executed. After the file is run, any errors |
|
474 | - * are displayed as a {@link DatabaseException} |
|
475 | - * |
|
476 | - * @param string $scriptlocation The full path to the script |
|
477 | - * |
|
478 | - * @return void |
|
479 | - * @throws \DatabaseException |
|
480 | - * @access private |
|
481 | - */ |
|
482 | - public function runSqlScript($scriptlocation) { |
|
483 | - $script = file_get_contents($scriptlocation); |
|
484 | - if ($script) { |
|
485 | - $errors = []; |
|
486 | - |
|
487 | - // Remove MySQL '-- ' and '# ' style comments |
|
488 | - $script = preg_replace('/^(?:--|#) .*$/m', '', $script); |
|
489 | - |
|
490 | - // Statements must end with ; and a newline |
|
491 | - $sql_statements = preg_split('/;[\n\r]+/', "$script\n"); |
|
492 | - |
|
493 | - foreach ($sql_statements as $statement) { |
|
494 | - $statement = trim($statement); |
|
495 | - $statement = str_replace("prefix_", $this->table_prefix, $statement); |
|
496 | - if (!empty($statement)) { |
|
497 | - try { |
|
498 | - $this->updateData($statement); |
|
499 | - } catch (\DatabaseException $e) { |
|
500 | - $errors[] = $e->getMessage(); |
|
501 | - } |
|
502 | - } |
|
503 | - } |
|
504 | - if (!empty($errors)) { |
|
505 | - $errortxt = ""; |
|
506 | - foreach ($errors as $error) { |
|
507 | - $errortxt .= " {$error};"; |
|
508 | - } |
|
509 | - |
|
510 | - $msg = "There were a number of issues: " . $errortxt; |
|
511 | - throw new \DatabaseException($msg); |
|
512 | - } |
|
513 | - } else { |
|
514 | - $msg = "Elgg couldn't find the requested database script at " . $scriptlocation . "."; |
|
515 | - throw new \DatabaseException($msg); |
|
516 | - } |
|
517 | - } |
|
518 | - |
|
519 | - /** |
|
520 | - * Queue a query for execution upon shutdown. |
|
521 | - * |
|
522 | - * You can specify a callback if you care about the result. This function will always |
|
523 | - * be passed a \Doctrine\DBAL\Driver\Statement. |
|
524 | - * |
|
525 | - * @param string $query The query to execute |
|
526 | - * @param string $type The query type ('read' or 'write') |
|
527 | - * @param callable $callback A callback function to pass the results array to |
|
528 | - * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
529 | - * |
|
530 | - * @return boolean Whether registering was successful. |
|
531 | - * @access private |
|
532 | - */ |
|
533 | - public function registerDelayedQuery($query, $type, $callback = null, array $params = []) { |
|
534 | - if ($type != 'read' && $type != 'write') { |
|
535 | - return false; |
|
536 | - } |
|
537 | - |
|
538 | - $this->delayed_queries[] = [ |
|
539 | - self::DELAYED_QUERY => $query, |
|
540 | - self::DELAYED_TYPE => $type, |
|
541 | - self::DELAYED_HANDLER => $callback, |
|
542 | - self::DELAYED_PARAMS => $params, |
|
543 | - ]; |
|
544 | - |
|
545 | - return true; |
|
546 | - } |
|
547 | - |
|
548 | - /** |
|
549 | - * Trigger all queries that were registered as "delayed" queries. This is |
|
550 | - * called by the system automatically on shutdown. |
|
551 | - * |
|
552 | - * @return void |
|
553 | - * @access private |
|
554 | - * @todo make protected once this class is part of public API |
|
555 | - */ |
|
556 | - public function executeDelayedQueries() { |
|
557 | - |
|
558 | - foreach ($this->delayed_queries as $set) { |
|
559 | - $query = $set[self::DELAYED_QUERY]; |
|
560 | - $type = $set[self::DELAYED_TYPE]; |
|
561 | - $handler = $set[self::DELAYED_HANDLER]; |
|
562 | - $params = $set[self::DELAYED_PARAMS]; |
|
563 | - |
|
564 | - try { |
|
565 | - $stmt = $this->executeQuery($query, $this->getConnection($type), $params); |
|
566 | - |
|
567 | - if (is_callable($handler)) { |
|
568 | - call_user_func($handler, $stmt); |
|
569 | - } |
|
570 | - } catch (\Exception $e) { |
|
571 | - if ($this->logger) { |
|
572 | - // Suppress all exceptions since page already sent to requestor |
|
573 | - $this->logger->error($e); |
|
574 | - } |
|
575 | - } |
|
576 | - } |
|
577 | - } |
|
578 | - |
|
579 | - /** |
|
580 | - * Enable the query cache |
|
581 | - * |
|
582 | - * This does not take precedence over the \Elgg\Database\Config setting. |
|
583 | - * |
|
584 | - * @return void |
|
585 | - * @access private |
|
586 | - */ |
|
587 | - public function enableQueryCache() { |
|
588 | - if ($this->config->isQueryCacheEnabled() && $this->query_cache === null) { |
|
589 | - // @todo if we keep this cache, expose the size as a config parameter |
|
590 | - $this->query_cache = new \Elgg\Cache\LRUCache($this->query_cache_size); |
|
591 | - } |
|
592 | - } |
|
593 | - |
|
594 | - /** |
|
595 | - * Disable the query cache |
|
596 | - * |
|
597 | - * This is useful for special scripts that pull large amounts of data back |
|
598 | - * in single queries. |
|
599 | - * |
|
600 | - * @return void |
|
601 | - * @access private |
|
602 | - */ |
|
603 | - public function disableQueryCache() { |
|
604 | - $this->query_cache = null; |
|
605 | - } |
|
606 | - |
|
607 | - /** |
|
608 | - * Invalidate the query cache |
|
609 | - * |
|
610 | - * @return void |
|
611 | - */ |
|
612 | - protected function invalidateQueryCache() { |
|
613 | - if ($this->query_cache) { |
|
614 | - $this->query_cache->clear(); |
|
615 | - if ($this->logger) { |
|
616 | - $this->logger->info("Query cache invalidated"); |
|
617 | - } |
|
618 | - } |
|
619 | - } |
|
620 | - |
|
621 | - /** |
|
622 | - * Test that the Elgg database is installed |
|
623 | - * |
|
624 | - * @return void |
|
625 | - * @throws \InstallationException |
|
626 | - * @access private |
|
627 | - */ |
|
628 | - public function assertInstalled() { |
|
629 | - |
|
630 | - if ($this->installed) { |
|
631 | - return; |
|
632 | - } |
|
633 | - |
|
634 | - try { |
|
635 | - $sql = "SELECT value FROM {$this->table_prefix}config WHERE name = 'installed'"; |
|
636 | - $this->getConnection('read')->query($sql); |
|
637 | - } catch (\DatabaseException $e) { |
|
638 | - throw new \InstallationException("Unable to handle this request. This site is not " |
|
639 | - . "configured or the database is down."); |
|
640 | - } |
|
641 | - |
|
642 | - $this->installed = true; |
|
643 | - } |
|
644 | - |
|
645 | - /** |
|
646 | - * Get the number of queries made to the database |
|
647 | - * |
|
648 | - * @return int |
|
649 | - * @access private |
|
650 | - */ |
|
651 | - public function getQueryCount() { |
|
652 | - return $this->query_count; |
|
653 | - } |
|
654 | - |
|
655 | - /** |
|
656 | - * Sanitizes an integer value for use in a query |
|
657 | - * |
|
658 | - * @param int $value Value to sanitize |
|
659 | - * @param bool $signed Whether negative values are allowed (default: true) |
|
660 | - * @return int |
|
661 | - * @deprecated Use query parameters where possible |
|
662 | - */ |
|
663 | - public function sanitizeInt($value, $signed = true) { |
|
664 | - $value = (int) $value; |
|
665 | - |
|
666 | - if ($signed === false) { |
|
667 | - if ($value < 0) { |
|
668 | - $value = 0; |
|
669 | - } |
|
670 | - } |
|
671 | - |
|
672 | - return $value; |
|
673 | - } |
|
674 | - |
|
675 | - /** |
|
676 | - * Sanitizes a string for use in a query |
|
677 | - * |
|
678 | - * @param string $value Value to escape |
|
679 | - * @return string |
|
680 | - * @throws \DatabaseException |
|
681 | - * @deprecated Use query parameters where possible |
|
682 | - */ |
|
683 | - public function sanitizeString($value) { |
|
684 | - $quoted = $this->getConnection('read')->quote($value); |
|
685 | - if ($quoted[0] !== "'" || substr($quoted, -1) !== "'") { |
|
686 | - throw new \DatabaseException("PDO::quote did not return surrounding single quotes."); |
|
687 | - } |
|
688 | - return substr($quoted, 1, -1); |
|
689 | - } |
|
690 | - |
|
691 | - /** |
|
692 | - * Get the server version number |
|
693 | - * |
|
694 | - * @param string $type Connection type (Config constants, e.g. Config::READ_WRITE) |
|
695 | - * |
|
696 | - * @return string Empty if version cannot be determined |
|
697 | - * @access private |
|
698 | - */ |
|
699 | - public function getServerVersion($type) { |
|
700 | - $driver = $this->getConnection($type)->getWrappedConnection(); |
|
701 | - if ($driver instanceof ServerInfoAwareConnection) { |
|
702 | - return $driver->getServerVersion(); |
|
703 | - } |
|
704 | - |
|
705 | - return null; |
|
706 | - } |
|
707 | - |
|
708 | - /** |
|
709 | - * Handle magic property reads |
|
710 | - * |
|
711 | - * @param string $name Property name |
|
712 | - * @return mixed |
|
713 | - */ |
|
714 | - public function __get($name) { |
|
715 | - if ($name === 'prefix') { |
|
716 | - return $this->table_prefix; |
|
717 | - } |
|
718 | - |
|
719 | - throw new \RuntimeException("Cannot read property '$name'"); |
|
720 | - } |
|
721 | - |
|
722 | - /** |
|
723 | - * Handle magic property writes |
|
724 | - * |
|
725 | - * @param string $name Property name |
|
726 | - * @param mixed $value Value |
|
727 | - * @return void |
|
728 | - */ |
|
729 | - public function __set($name, $value) { |
|
730 | - throw new \RuntimeException("Cannot write property '$name'"); |
|
731 | - } |
|
18 | + use Profilable; |
|
19 | + |
|
20 | + const DELAYED_QUERY = 'q'; |
|
21 | + const DELAYED_TYPE = 't'; |
|
22 | + const DELAYED_HANDLER = 'h'; |
|
23 | + const DELAYED_PARAMS = 'p'; |
|
24 | + |
|
25 | + /** |
|
26 | + * @var string $table_prefix Prefix for database tables |
|
27 | + */ |
|
28 | + private $table_prefix; |
|
29 | + |
|
30 | + /** |
|
31 | + * @var Connection[] |
|
32 | + */ |
|
33 | + private $connections = []; |
|
34 | + |
|
35 | + /** |
|
36 | + * @var int $query_count The number of queries made |
|
37 | + */ |
|
38 | + private $query_count = 0; |
|
39 | + |
|
40 | + /** |
|
41 | + * Query cache for select queries. |
|
42 | + * |
|
43 | + * Queries and their results are stored in this cache as: |
|
44 | + * <code> |
|
45 | + * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN) |
|
46 | + * </code> |
|
47 | + * @see \Elgg\Database::getResults() for details on the hash. |
|
48 | + * |
|
49 | + * @var \Elgg\Cache\LRUCache $query_cache The cache |
|
50 | + */ |
|
51 | + private $query_cache = null; |
|
52 | + |
|
53 | + /** |
|
54 | + * @var int $query_cache_size The number of queries to cache |
|
55 | + */ |
|
56 | + private $query_cache_size = 50; |
|
57 | + |
|
58 | + /** |
|
59 | + * Queries are saved as an array with the DELAYED_* constants as keys. |
|
60 | + * |
|
61 | + * @see registerDelayedQuery |
|
62 | + * |
|
63 | + * @var array $delayed_queries Queries to be run during shutdown |
|
64 | + */ |
|
65 | + private $delayed_queries = []; |
|
66 | + |
|
67 | + /** |
|
68 | + * @var bool $installed Is the database installed? |
|
69 | + */ |
|
70 | + private $installed = false; |
|
71 | + |
|
72 | + /** |
|
73 | + * @var \Elgg\Database\Config $config Database configuration |
|
74 | + */ |
|
75 | + private $config; |
|
76 | + |
|
77 | + /** |
|
78 | + * @var \Elgg\Logger $logger The logger |
|
79 | + */ |
|
80 | + private $logger; |
|
81 | + |
|
82 | + /** |
|
83 | + * Constructor |
|
84 | + * |
|
85 | + * @param \Elgg\Database\Config $config Database configuration |
|
86 | + * @param \Elgg\Logger $logger The logger |
|
87 | + */ |
|
88 | + public function __construct(\Elgg\Database\Config $config, \Elgg\Logger $logger = null) { |
|
89 | + |
|
90 | + $this->logger = $logger; |
|
91 | + $this->config = $config; |
|
92 | + |
|
93 | + $this->table_prefix = $config->getTablePrefix(); |
|
94 | + |
|
95 | + $this->enableQueryCache(); |
|
96 | + } |
|
97 | + |
|
98 | + /** |
|
99 | + * Set the logger object |
|
100 | + * |
|
101 | + * @param Logger $logger The logger |
|
102 | + * @return void |
|
103 | + * @access private |
|
104 | + */ |
|
105 | + public function setLogger(Logger $logger) { |
|
106 | + $this->logger = $logger; |
|
107 | + } |
|
108 | + |
|
109 | + /** |
|
110 | + * Gets (if required, also creates) a DB connection. |
|
111 | + * |
|
112 | + * @param string $type The type of link we want: "read", "write" or "readwrite". |
|
113 | + * |
|
114 | + * @return Connection |
|
115 | + * @throws \DatabaseException |
|
116 | + */ |
|
117 | + protected function getConnection($type) { |
|
118 | + if (isset($this->connections[$type])) { |
|
119 | + return $this->connections[$type]; |
|
120 | + } else if (isset($this->connections['readwrite'])) { |
|
121 | + return $this->connections['readwrite']; |
|
122 | + } else { |
|
123 | + $this->setupConnections(); |
|
124 | + return $this->getConnection($type); |
|
125 | + } |
|
126 | + } |
|
127 | + |
|
128 | + /** |
|
129 | + * Establish database connections |
|
130 | + * |
|
131 | + * If the configuration has been set up for multiple read/write databases, set those |
|
132 | + * links up separately; otherwise just create the one database link. |
|
133 | + * |
|
134 | + * @return void |
|
135 | + * @throws \DatabaseException |
|
136 | + * @access private |
|
137 | + */ |
|
138 | + public function setupConnections() { |
|
139 | + if ($this->config->isDatabaseSplit()) { |
|
140 | + $this->connect('read'); |
|
141 | + $this->connect('write'); |
|
142 | + } else { |
|
143 | + $this->connect('readwrite'); |
|
144 | + } |
|
145 | + } |
|
146 | + |
|
147 | + /** |
|
148 | + * Establish a connection to the database server |
|
149 | + * |
|
150 | + * Connect to the database server and use the Elgg database for a particular database link |
|
151 | + * |
|
152 | + * @param string $type The type of database connection. "read", "write", or "readwrite". |
|
153 | + * |
|
154 | + * @return void |
|
155 | + * @throws \DatabaseException |
|
156 | + * @access private |
|
157 | + */ |
|
158 | + public function connect($type = "readwrite") { |
|
159 | + $conf = $this->config->getConnectionConfig($type); |
|
160 | + |
|
161 | + $params = [ |
|
162 | + 'dbname' => $conf['database'], |
|
163 | + 'user' => $conf['user'], |
|
164 | + 'password' => $conf['password'], |
|
165 | + 'host' => $conf['host'], |
|
166 | + 'charset' => 'utf8', |
|
167 | + 'driver' => 'pdo_mysql', |
|
168 | + ]; |
|
169 | + |
|
170 | + try { |
|
171 | + $this->connections[$type] = DriverManager::getConnection($params); |
|
172 | + $this->connections[$type]->setFetchMode(\PDO::FETCH_OBJ); |
|
173 | + |
|
174 | + // https://github.com/Elgg/Elgg/issues/8121 |
|
175 | + $sub_query = "SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '')"; |
|
176 | + $this->connections[$type]->exec("SET SESSION sql_mode=($sub_query);"); |
|
177 | + } catch (\PDOException $e) { |
|
178 | + // @todo just allow PDO exceptions |
|
179 | + // http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html |
|
180 | + if ($e->getCode() == 1102 || $e->getCode() == 1049) { |
|
181 | + $msg = "Elgg couldn't select the database '{$conf['database']}'. " |
|
182 | + . "Please check that the database is created and you have access to it."; |
|
183 | + } else { |
|
184 | + $msg = "Elgg couldn't connect to the database using the given credentials. Check the settings file."; |
|
185 | + } |
|
186 | + throw new \DatabaseException($msg); |
|
187 | + } |
|
188 | + } |
|
189 | + |
|
190 | + /** |
|
191 | + * Retrieve rows from the database. |
|
192 | + * |
|
193 | + * Queries are executed with {@link \Elgg\Database::executeQuery()} and results |
|
194 | + * are retrieved with {@link \PDO::fetchObject()}. If a callback |
|
195 | + * function $callback is defined, each row will be passed as a single |
|
196 | + * argument to $callback. If no callback function is defined, the |
|
197 | + * entire result set is returned as an array. |
|
198 | + * |
|
199 | + * @param string $query The query being passed. |
|
200 | + * @param callable $callback Optionally, the name of a function to call back to on each row |
|
201 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
202 | + * |
|
203 | + * @return array An array of database result objects or callback function results. If the query |
|
204 | + * returned nothing, an empty array. |
|
205 | + * @throws \DatabaseException |
|
206 | + */ |
|
207 | + public function getData($query, $callback = null, array $params = []) { |
|
208 | + return $this->getResults($query, $callback, false, $params); |
|
209 | + } |
|
210 | + |
|
211 | + /** |
|
212 | + * Retrieve a single row from the database. |
|
213 | + * |
|
214 | + * Similar to {@link \Elgg\Database::getData()} but returns only the first row |
|
215 | + * matched. If a callback function $callback is specified, the row will be passed |
|
216 | + * as the only argument to $callback. |
|
217 | + * |
|
218 | + * @param string $query The query to execute. |
|
219 | + * @param callable $callback A callback function to apply to the row |
|
220 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
221 | + * |
|
222 | + * @return mixed A single database result object or the result of the callback function. |
|
223 | + * @throws \DatabaseException |
|
224 | + */ |
|
225 | + public function getDataRow($query, $callback = null, array $params = []) { |
|
226 | + return $this->getResults($query, $callback, true, $params); |
|
227 | + } |
|
228 | + |
|
229 | + /** |
|
230 | + * Insert a row into the database. |
|
231 | + * |
|
232 | + * @note Altering the DB invalidates all queries in the query cache. |
|
233 | + * |
|
234 | + * @param string $query The query to execute. |
|
235 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
236 | + * |
|
237 | + * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is |
|
238 | + * defined, 0 if not, and false on failure. |
|
239 | + * @throws \DatabaseException |
|
240 | + */ |
|
241 | + public function insertData($query, array $params = []) { |
|
242 | + |
|
243 | + if ($this->logger) { |
|
244 | + $this->logger->info("DB query $query"); |
|
245 | + } |
|
246 | + |
|
247 | + $connection = $this->getConnection('write'); |
|
248 | + |
|
249 | + $this->invalidateQueryCache(); |
|
250 | + |
|
251 | + $this->executeQuery($query, $connection, $params); |
|
252 | + return (int) $connection->lastInsertId(); |
|
253 | + } |
|
254 | + |
|
255 | + /** |
|
256 | + * Update the database. |
|
257 | + * |
|
258 | + * @note Altering the DB invalidates all queries in the query cache. |
|
259 | + * |
|
260 | + * @note WARNING! update_data() has the 2nd and 3rd arguments reversed. |
|
261 | + * |
|
262 | + * @param string $query The query to run. |
|
263 | + * @param bool $get_num_rows Return the number of rows affected (default: false). |
|
264 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
265 | + * |
|
266 | + * @return bool|int |
|
267 | + * @throws \DatabaseException |
|
268 | + */ |
|
269 | + public function updateData($query, $get_num_rows = false, array $params = []) { |
|
270 | + |
|
271 | + if ($this->logger) { |
|
272 | + $this->logger->info("DB query $query"); |
|
273 | + } |
|
274 | + |
|
275 | + $this->invalidateQueryCache(); |
|
276 | + |
|
277 | + $stmt = $this->executeQuery($query, $this->getConnection('write'), $params); |
|
278 | + if ($get_num_rows) { |
|
279 | + return $stmt->rowCount(); |
|
280 | + } else { |
|
281 | + return true; |
|
282 | + } |
|
283 | + } |
|
284 | + |
|
285 | + /** |
|
286 | + * Delete data from the database |
|
287 | + * |
|
288 | + * @note Altering the DB invalidates all queries in query cache. |
|
289 | + * |
|
290 | + * @param string $query The SQL query to run |
|
291 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
292 | + * |
|
293 | + * @return int The number of affected rows |
|
294 | + * @throws \DatabaseException |
|
295 | + */ |
|
296 | + public function deleteData($query, array $params = []) { |
|
297 | + |
|
298 | + if ($this->logger) { |
|
299 | + $this->logger->info("DB query $query"); |
|
300 | + } |
|
301 | + |
|
302 | + $connection = $this->getConnection('write'); |
|
303 | + |
|
304 | + $this->invalidateQueryCache(); |
|
305 | + |
|
306 | + $stmt = $this->executeQuery("$query", $connection, $params); |
|
307 | + return (int) $stmt->rowCount(); |
|
308 | + } |
|
309 | + |
|
310 | + /** |
|
311 | + * Get a string that uniquely identifies a callback during the current request. |
|
312 | + * |
|
313 | + * This is used to cache queries whose results were transformed by the callback. If the callback involves |
|
314 | + * object method calls of the same class, different instances will return different values. |
|
315 | + * |
|
316 | + * @param callable $callback The callable value to fingerprint |
|
317 | + * |
|
318 | + * @return string A string that is unique for each callable passed in |
|
319 | + * @since 1.9.4 |
|
320 | + * @access private |
|
321 | + * @todo Make this protected once we can setAccessible(true) via reflection |
|
322 | + */ |
|
323 | + public function fingerprintCallback($callback) { |
|
324 | + if (is_string($callback)) { |
|
325 | + return $callback; |
|
326 | + } |
|
327 | + if (is_object($callback)) { |
|
328 | + return spl_object_hash($callback) . "::__invoke"; |
|
329 | + } |
|
330 | + if (is_array($callback)) { |
|
331 | + if (is_string($callback[0])) { |
|
332 | + return "{$callback[0]}::{$callback[1]}"; |
|
333 | + } |
|
334 | + return spl_object_hash($callback[0]) . "::{$callback[1]}"; |
|
335 | + } |
|
336 | + // this should not happen |
|
337 | + return ""; |
|
338 | + } |
|
339 | + |
|
340 | + /** |
|
341 | + * Handles queries that return results, running the results through a |
|
342 | + * an optional callback function. This is for R queries (from CRUD). |
|
343 | + * |
|
344 | + * @param string $query The select query to execute |
|
345 | + * @param string $callback An optional callback function to run on each row |
|
346 | + * @param bool $single Return only a single result? |
|
347 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
348 | + * |
|
349 | + * @return array An array of database result objects or callback function results. If the query |
|
350 | + * returned nothing, an empty array. |
|
351 | + * @throws \DatabaseException |
|
352 | + */ |
|
353 | + protected function getResults($query, $callback = null, $single = false, array $params = []) { |
|
354 | + |
|
355 | + // Since we want to cache results of running the callback, we need to |
|
356 | + // need to namespace the query with the callback and single result request. |
|
357 | + // https://github.com/elgg/elgg/issues/4049 |
|
358 | + $query_id = (int) $single . $query . '|'; |
|
359 | + if ($params) { |
|
360 | + $query_id .= serialize($params) . '|'; |
|
361 | + } |
|
362 | + |
|
363 | + if ($callback) { |
|
364 | + if (!is_callable($callback)) { |
|
365 | + throw new \RuntimeException('$callback must be a callable function. Given ' |
|
366 | + . _elgg_services()->handlers->describeCallable($callback)); |
|
367 | + } |
|
368 | + $query_id .= $this->fingerprintCallback($callback); |
|
369 | + } |
|
370 | + // MD5 yields smaller mem usage for cache and cleaner logs |
|
371 | + $hash = md5($query_id); |
|
372 | + |
|
373 | + // Is cached? |
|
374 | + if ($this->query_cache) { |
|
375 | + if (isset($this->query_cache[$hash])) { |
|
376 | + if ($this->logger) { |
|
377 | + // TODO add params in $query here |
|
378 | + $this->logger->info("DB query $query results returned from cache (hash: $hash)"); |
|
379 | + } |
|
380 | + return $this->query_cache[$hash]; |
|
381 | + } |
|
382 | + } |
|
383 | + |
|
384 | + $return = []; |
|
385 | + |
|
386 | + $stmt = $this->executeQuery($query, $this->getConnection('read'), $params); |
|
387 | + while ($row = $stmt->fetch()) { |
|
388 | + if ($callback) { |
|
389 | + $row = call_user_func($callback, $row); |
|
390 | + } |
|
391 | + |
|
392 | + if ($single) { |
|
393 | + $return = $row; |
|
394 | + break; |
|
395 | + } else { |
|
396 | + $return[] = $row; |
|
397 | + } |
|
398 | + } |
|
399 | + |
|
400 | + // Cache result |
|
401 | + if ($this->query_cache) { |
|
402 | + $this->query_cache[$hash] = $return; |
|
403 | + if ($this->logger) { |
|
404 | + // TODO add params in $query here |
|
405 | + $this->logger->info("DB query $query results cached (hash: $hash)"); |
|
406 | + } |
|
407 | + } |
|
408 | + |
|
409 | + return $return; |
|
410 | + } |
|
411 | + |
|
412 | + /** |
|
413 | + * Execute a query. |
|
414 | + * |
|
415 | + * $query is executed via {@link Connection::query}. If there is an SQL error, |
|
416 | + * a {@link DatabaseException} is thrown. |
|
417 | + * |
|
418 | + * @param string $query The query |
|
419 | + * @param Connection $connection The DB connection |
|
420 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
421 | + * |
|
422 | + * @return Statement The result of the query |
|
423 | + * @throws \DatabaseException |
|
424 | + */ |
|
425 | + protected function executeQuery($query, Connection $connection, array $params = []) { |
|
426 | + if ($query == null) { |
|
427 | + throw new \DatabaseException("Query cannot be null"); |
|
428 | + } |
|
429 | + |
|
430 | + $this->query_count++; |
|
431 | + |
|
432 | + if ($this->timer) { |
|
433 | + $timer_key = preg_replace('~\\s+~', ' ', trim($query . '|' . serialize($params))); |
|
434 | + $this->timer->begin(['SQL', $timer_key]); |
|
435 | + } |
|
436 | + |
|
437 | + try { |
|
438 | + if ($params) { |
|
439 | + $value = $connection->executeQuery($query, $params); |
|
440 | + } else { |
|
441 | + // faster |
|
442 | + $value = $connection->query($query); |
|
443 | + } |
|
444 | + } catch (\Exception $e) { |
|
445 | + throw new \DatabaseException($e->getMessage() . "\n\n" |
|
446 | + . "QUERY: $query \n\n" |
|
447 | + . "PARAMS: " . print_r($params, true)); |
|
448 | + } |
|
449 | + |
|
450 | + if ($this->timer) { |
|
451 | + $this->timer->end(['SQL', $timer_key]); |
|
452 | + } |
|
453 | + |
|
454 | + return $value; |
|
455 | + } |
|
456 | + |
|
457 | + /** |
|
458 | + * Runs a full database script from disk. |
|
459 | + * |
|
460 | + * The file specified should be a standard SQL file as created by |
|
461 | + * mysqldump or similar. Statements must be terminated with ; |
|
462 | + * and a newline character (\n or \r\n). |
|
463 | + * |
|
464 | + * The special string 'prefix_' is replaced with the database prefix |
|
465 | + * as defined in {@link $this->tablePrefix}. |
|
466 | + * |
|
467 | + * @warning Only single line comments are supported. A comment |
|
468 | + * must start with '-- ' or '# ', where the comment sign is at the |
|
469 | + * very beginning of each line. |
|
470 | + * |
|
471 | + * @warning Errors do not halt execution of the script. If a line |
|
472 | + * generates an error, the error message is saved and the |
|
473 | + * next line is executed. After the file is run, any errors |
|
474 | + * are displayed as a {@link DatabaseException} |
|
475 | + * |
|
476 | + * @param string $scriptlocation The full path to the script |
|
477 | + * |
|
478 | + * @return void |
|
479 | + * @throws \DatabaseException |
|
480 | + * @access private |
|
481 | + */ |
|
482 | + public function runSqlScript($scriptlocation) { |
|
483 | + $script = file_get_contents($scriptlocation); |
|
484 | + if ($script) { |
|
485 | + $errors = []; |
|
486 | + |
|
487 | + // Remove MySQL '-- ' and '# ' style comments |
|
488 | + $script = preg_replace('/^(?:--|#) .*$/m', '', $script); |
|
489 | + |
|
490 | + // Statements must end with ; and a newline |
|
491 | + $sql_statements = preg_split('/;[\n\r]+/', "$script\n"); |
|
492 | + |
|
493 | + foreach ($sql_statements as $statement) { |
|
494 | + $statement = trim($statement); |
|
495 | + $statement = str_replace("prefix_", $this->table_prefix, $statement); |
|
496 | + if (!empty($statement)) { |
|
497 | + try { |
|
498 | + $this->updateData($statement); |
|
499 | + } catch (\DatabaseException $e) { |
|
500 | + $errors[] = $e->getMessage(); |
|
501 | + } |
|
502 | + } |
|
503 | + } |
|
504 | + if (!empty($errors)) { |
|
505 | + $errortxt = ""; |
|
506 | + foreach ($errors as $error) { |
|
507 | + $errortxt .= " {$error};"; |
|
508 | + } |
|
509 | + |
|
510 | + $msg = "There were a number of issues: " . $errortxt; |
|
511 | + throw new \DatabaseException($msg); |
|
512 | + } |
|
513 | + } else { |
|
514 | + $msg = "Elgg couldn't find the requested database script at " . $scriptlocation . "."; |
|
515 | + throw new \DatabaseException($msg); |
|
516 | + } |
|
517 | + } |
|
518 | + |
|
519 | + /** |
|
520 | + * Queue a query for execution upon shutdown. |
|
521 | + * |
|
522 | + * You can specify a callback if you care about the result. This function will always |
|
523 | + * be passed a \Doctrine\DBAL\Driver\Statement. |
|
524 | + * |
|
525 | + * @param string $query The query to execute |
|
526 | + * @param string $type The query type ('read' or 'write') |
|
527 | + * @param callable $callback A callback function to pass the results array to |
|
528 | + * @param array $params Query params. E.g. [1, 'steve'] or [':id' => 1, ':name' => 'steve'] |
|
529 | + * |
|
530 | + * @return boolean Whether registering was successful. |
|
531 | + * @access private |
|
532 | + */ |
|
533 | + public function registerDelayedQuery($query, $type, $callback = null, array $params = []) { |
|
534 | + if ($type != 'read' && $type != 'write') { |
|
535 | + return false; |
|
536 | + } |
|
537 | + |
|
538 | + $this->delayed_queries[] = [ |
|
539 | + self::DELAYED_QUERY => $query, |
|
540 | + self::DELAYED_TYPE => $type, |
|
541 | + self::DELAYED_HANDLER => $callback, |
|
542 | + self::DELAYED_PARAMS => $params, |
|
543 | + ]; |
|
544 | + |
|
545 | + return true; |
|
546 | + } |
|
547 | + |
|
548 | + /** |
|
549 | + * Trigger all queries that were registered as "delayed" queries. This is |
|
550 | + * called by the system automatically on shutdown. |
|
551 | + * |
|
552 | + * @return void |
|
553 | + * @access private |
|
554 | + * @todo make protected once this class is part of public API |
|
555 | + */ |
|
556 | + public function executeDelayedQueries() { |
|
557 | + |
|
558 | + foreach ($this->delayed_queries as $set) { |
|
559 | + $query = $set[self::DELAYED_QUERY]; |
|
560 | + $type = $set[self::DELAYED_TYPE]; |
|
561 | + $handler = $set[self::DELAYED_HANDLER]; |
|
562 | + $params = $set[self::DELAYED_PARAMS]; |
|
563 | + |
|
564 | + try { |
|
565 | + $stmt = $this->executeQuery($query, $this->getConnection($type), $params); |
|
566 | + |
|
567 | + if (is_callable($handler)) { |
|
568 | + call_user_func($handler, $stmt); |
|
569 | + } |
|
570 | + } catch (\Exception $e) { |
|
571 | + if ($this->logger) { |
|
572 | + // Suppress all exceptions since page already sent to requestor |
|
573 | + $this->logger->error($e); |
|
574 | + } |
|
575 | + } |
|
576 | + } |
|
577 | + } |
|
578 | + |
|
579 | + /** |
|
580 | + * Enable the query cache |
|
581 | + * |
|
582 | + * This does not take precedence over the \Elgg\Database\Config setting. |
|
583 | + * |
|
584 | + * @return void |
|
585 | + * @access private |
|
586 | + */ |
|
587 | + public function enableQueryCache() { |
|
588 | + if ($this->config->isQueryCacheEnabled() && $this->query_cache === null) { |
|
589 | + // @todo if we keep this cache, expose the size as a config parameter |
|
590 | + $this->query_cache = new \Elgg\Cache\LRUCache($this->query_cache_size); |
|
591 | + } |
|
592 | + } |
|
593 | + |
|
594 | + /** |
|
595 | + * Disable the query cache |
|
596 | + * |
|
597 | + * This is useful for special scripts that pull large amounts of data back |
|
598 | + * in single queries. |
|
599 | + * |
|
600 | + * @return void |
|
601 | + * @access private |
|
602 | + */ |
|
603 | + public function disableQueryCache() { |
|
604 | + $this->query_cache = null; |
|
605 | + } |
|
606 | + |
|
607 | + /** |
|
608 | + * Invalidate the query cache |
|
609 | + * |
|
610 | + * @return void |
|
611 | + */ |
|
612 | + protected function invalidateQueryCache() { |
|
613 | + if ($this->query_cache) { |
|
614 | + $this->query_cache->clear(); |
|
615 | + if ($this->logger) { |
|
616 | + $this->logger->info("Query cache invalidated"); |
|
617 | + } |
|
618 | + } |
|
619 | + } |
|
620 | + |
|
621 | + /** |
|
622 | + * Test that the Elgg database is installed |
|
623 | + * |
|
624 | + * @return void |
|
625 | + * @throws \InstallationException |
|
626 | + * @access private |
|
627 | + */ |
|
628 | + public function assertInstalled() { |
|
629 | + |
|
630 | + if ($this->installed) { |
|
631 | + return; |
|
632 | + } |
|
633 | + |
|
634 | + try { |
|
635 | + $sql = "SELECT value FROM {$this->table_prefix}config WHERE name = 'installed'"; |
|
636 | + $this->getConnection('read')->query($sql); |
|
637 | + } catch (\DatabaseException $e) { |
|
638 | + throw new \InstallationException("Unable to handle this request. This site is not " |
|
639 | + . "configured or the database is down."); |
|
640 | + } |
|
641 | + |
|
642 | + $this->installed = true; |
|
643 | + } |
|
644 | + |
|
645 | + /** |
|
646 | + * Get the number of queries made to the database |
|
647 | + * |
|
648 | + * @return int |
|
649 | + * @access private |
|
650 | + */ |
|
651 | + public function getQueryCount() { |
|
652 | + return $this->query_count; |
|
653 | + } |
|
654 | + |
|
655 | + /** |
|
656 | + * Sanitizes an integer value for use in a query |
|
657 | + * |
|
658 | + * @param int $value Value to sanitize |
|
659 | + * @param bool $signed Whether negative values are allowed (default: true) |
|
660 | + * @return int |
|
661 | + * @deprecated Use query parameters where possible |
|
662 | + */ |
|
663 | + public function sanitizeInt($value, $signed = true) { |
|
664 | + $value = (int) $value; |
|
665 | + |
|
666 | + if ($signed === false) { |
|
667 | + if ($value < 0) { |
|
668 | + $value = 0; |
|
669 | + } |
|
670 | + } |
|
671 | + |
|
672 | + return $value; |
|
673 | + } |
|
674 | + |
|
675 | + /** |
|
676 | + * Sanitizes a string for use in a query |
|
677 | + * |
|
678 | + * @param string $value Value to escape |
|
679 | + * @return string |
|
680 | + * @throws \DatabaseException |
|
681 | + * @deprecated Use query parameters where possible |
|
682 | + */ |
|
683 | + public function sanitizeString($value) { |
|
684 | + $quoted = $this->getConnection('read')->quote($value); |
|
685 | + if ($quoted[0] !== "'" || substr($quoted, -1) !== "'") { |
|
686 | + throw new \DatabaseException("PDO::quote did not return surrounding single quotes."); |
|
687 | + } |
|
688 | + return substr($quoted, 1, -1); |
|
689 | + } |
|
690 | + |
|
691 | + /** |
|
692 | + * Get the server version number |
|
693 | + * |
|
694 | + * @param string $type Connection type (Config constants, e.g. Config::READ_WRITE) |
|
695 | + * |
|
696 | + * @return string Empty if version cannot be determined |
|
697 | + * @access private |
|
698 | + */ |
|
699 | + public function getServerVersion($type) { |
|
700 | + $driver = $this->getConnection($type)->getWrappedConnection(); |
|
701 | + if ($driver instanceof ServerInfoAwareConnection) { |
|
702 | + return $driver->getServerVersion(); |
|
703 | + } |
|
704 | + |
|
705 | + return null; |
|
706 | + } |
|
707 | + |
|
708 | + /** |
|
709 | + * Handle magic property reads |
|
710 | + * |
|
711 | + * @param string $name Property name |
|
712 | + * @return mixed |
|
713 | + */ |
|
714 | + public function __get($name) { |
|
715 | + if ($name === 'prefix') { |
|
716 | + return $this->table_prefix; |
|
717 | + } |
|
718 | + |
|
719 | + throw new \RuntimeException("Cannot read property '$name'"); |
|
720 | + } |
|
721 | + |
|
722 | + /** |
|
723 | + * Handle magic property writes |
|
724 | + * |
|
725 | + * @param string $name Property name |
|
726 | + * @param mixed $value Value |
|
727 | + * @return void |
|
728 | + */ |
|
729 | + public function __set($name, $value) { |
|
730 | + throw new \RuntimeException("Cannot write property '$name'"); |
|
731 | + } |
|
732 | 732 | } |
@@ -38,177 +38,177 @@ discard block |
||
38 | 38 | */ |
39 | 39 | class EntityTable { |
40 | 40 | |
41 | - use \Elgg\TimeUsing; |
|
41 | + use \Elgg\TimeUsing; |
|
42 | 42 | |
43 | - /** |
|
44 | - * @var Conf |
|
45 | - */ |
|
46 | - protected $config; |
|
47 | - |
|
48 | - /** |
|
49 | - * @var Database |
|
50 | - */ |
|
51 | - protected $db; |
|
52 | - |
|
53 | - /** |
|
54 | - * @var string |
|
55 | - */ |
|
56 | - protected $table; |
|
57 | - |
|
58 | - /** |
|
59 | - * @var SubtypeTable |
|
60 | - */ |
|
61 | - protected $subtype_table; |
|
62 | - |
|
63 | - /** |
|
64 | - * @var EntityCache |
|
65 | - */ |
|
66 | - protected $entity_cache; |
|
67 | - |
|
68 | - /** |
|
69 | - * @var EntityPreloader |
|
70 | - */ |
|
71 | - protected $entity_preloader; |
|
72 | - |
|
73 | - /** |
|
74 | - * @var MetadataCache |
|
75 | - */ |
|
76 | - protected $metadata_cache; |
|
77 | - |
|
78 | - /** |
|
79 | - * @var EventsService |
|
80 | - */ |
|
81 | - protected $events; |
|
82 | - |
|
83 | - /** |
|
84 | - * @var ElggSession |
|
85 | - */ |
|
86 | - protected $session; |
|
87 | - |
|
88 | - /** |
|
89 | - * @var Translator |
|
90 | - */ |
|
91 | - protected $translator; |
|
92 | - |
|
93 | - /** |
|
94 | - * @var Logger |
|
95 | - */ |
|
96 | - protected $logger; |
|
97 | - |
|
98 | - /** |
|
99 | - * Constructor |
|
100 | - * |
|
101 | - * @param Conf $config Config |
|
102 | - * @param Database $db Database |
|
103 | - * @param EntityCache $entity_cache Entity cache |
|
104 | - * @param MetadataCache $metadata_cache Metadata cache |
|
105 | - * @param SubtypeTable $subtype_table Subtype table |
|
106 | - * @param EventsService $events Events service |
|
107 | - * @param ElggSession $session Session |
|
108 | - * @param Translator $translator Translator |
|
109 | - * @param Logger $logger Logger |
|
110 | - */ |
|
111 | - public function __construct( |
|
112 | - Conf $config, |
|
113 | - Database $db, |
|
114 | - EntityCache $entity_cache, |
|
115 | - MetadataCache $metadata_cache, |
|
116 | - SubtypeTable $subtype_table, |
|
117 | - EventsService $events, |
|
118 | - ElggSession $session, |
|
119 | - Translator $translator, |
|
120 | - Logger $logger |
|
121 | - ) { |
|
122 | - $this->config = $config; |
|
123 | - $this->db = $db; |
|
124 | - $this->table = $this->db->prefix . 'entities'; |
|
125 | - $this->entity_cache = $entity_cache; |
|
126 | - $this->metadata_cache = $metadata_cache; |
|
127 | - $this->subtype_table = $subtype_table; |
|
128 | - $this->events = $events; |
|
129 | - $this->session = $session; |
|
130 | - $this->translator = $translator; |
|
131 | - $this->logger = $logger; |
|
132 | - } |
|
133 | - |
|
134 | - /** |
|
135 | - * Returns a database row from the entities table. |
|
136 | - * |
|
137 | - * @see entity_row_to_elggstar() |
|
138 | - * |
|
139 | - * @tip Use get_entity() to return the fully loaded entity. |
|
140 | - * |
|
141 | - * @warning This will only return results if a) it exists, b) you have access to it. |
|
142 | - * see {@link _elgg_get_access_where_sql()}. |
|
143 | - * |
|
144 | - * @param int $guid The GUID of the object to extract |
|
145 | - * @param int $user_guid GUID of the user accessing the row |
|
146 | - * Defaults to logged in user if null |
|
147 | - * Builds an access query for a logged out user if 0 |
|
148 | - * @return stdClass|false |
|
149 | - * @access private |
|
150 | - */ |
|
151 | - public function getRow($guid, $user_guid = null) { |
|
152 | - |
|
153 | - if (!$guid) { |
|
154 | - return false; |
|
155 | - } |
|
156 | - |
|
157 | - $access = _elgg_get_access_where_sql([ |
|
158 | - 'table_alias' => '', |
|
159 | - 'user_guid' => $user_guid, |
|
160 | - ]); |
|
161 | - |
|
162 | - $sql = "SELECT * FROM {$this->db->prefix}entities |
|
43 | + /** |
|
44 | + * @var Conf |
|
45 | + */ |
|
46 | + protected $config; |
|
47 | + |
|
48 | + /** |
|
49 | + * @var Database |
|
50 | + */ |
|
51 | + protected $db; |
|
52 | + |
|
53 | + /** |
|
54 | + * @var string |
|
55 | + */ |
|
56 | + protected $table; |
|
57 | + |
|
58 | + /** |
|
59 | + * @var SubtypeTable |
|
60 | + */ |
|
61 | + protected $subtype_table; |
|
62 | + |
|
63 | + /** |
|
64 | + * @var EntityCache |
|
65 | + */ |
|
66 | + protected $entity_cache; |
|
67 | + |
|
68 | + /** |
|
69 | + * @var EntityPreloader |
|
70 | + */ |
|
71 | + protected $entity_preloader; |
|
72 | + |
|
73 | + /** |
|
74 | + * @var MetadataCache |
|
75 | + */ |
|
76 | + protected $metadata_cache; |
|
77 | + |
|
78 | + /** |
|
79 | + * @var EventsService |
|
80 | + */ |
|
81 | + protected $events; |
|
82 | + |
|
83 | + /** |
|
84 | + * @var ElggSession |
|
85 | + */ |
|
86 | + protected $session; |
|
87 | + |
|
88 | + /** |
|
89 | + * @var Translator |
|
90 | + */ |
|
91 | + protected $translator; |
|
92 | + |
|
93 | + /** |
|
94 | + * @var Logger |
|
95 | + */ |
|
96 | + protected $logger; |
|
97 | + |
|
98 | + /** |
|
99 | + * Constructor |
|
100 | + * |
|
101 | + * @param Conf $config Config |
|
102 | + * @param Database $db Database |
|
103 | + * @param EntityCache $entity_cache Entity cache |
|
104 | + * @param MetadataCache $metadata_cache Metadata cache |
|
105 | + * @param SubtypeTable $subtype_table Subtype table |
|
106 | + * @param EventsService $events Events service |
|
107 | + * @param ElggSession $session Session |
|
108 | + * @param Translator $translator Translator |
|
109 | + * @param Logger $logger Logger |
|
110 | + */ |
|
111 | + public function __construct( |
|
112 | + Conf $config, |
|
113 | + Database $db, |
|
114 | + EntityCache $entity_cache, |
|
115 | + MetadataCache $metadata_cache, |
|
116 | + SubtypeTable $subtype_table, |
|
117 | + EventsService $events, |
|
118 | + ElggSession $session, |
|
119 | + Translator $translator, |
|
120 | + Logger $logger |
|
121 | + ) { |
|
122 | + $this->config = $config; |
|
123 | + $this->db = $db; |
|
124 | + $this->table = $this->db->prefix . 'entities'; |
|
125 | + $this->entity_cache = $entity_cache; |
|
126 | + $this->metadata_cache = $metadata_cache; |
|
127 | + $this->subtype_table = $subtype_table; |
|
128 | + $this->events = $events; |
|
129 | + $this->session = $session; |
|
130 | + $this->translator = $translator; |
|
131 | + $this->logger = $logger; |
|
132 | + } |
|
133 | + |
|
134 | + /** |
|
135 | + * Returns a database row from the entities table. |
|
136 | + * |
|
137 | + * @see entity_row_to_elggstar() |
|
138 | + * |
|
139 | + * @tip Use get_entity() to return the fully loaded entity. |
|
140 | + * |
|
141 | + * @warning This will only return results if a) it exists, b) you have access to it. |
|
142 | + * see {@link _elgg_get_access_where_sql()}. |
|
143 | + * |
|
144 | + * @param int $guid The GUID of the object to extract |
|
145 | + * @param int $user_guid GUID of the user accessing the row |
|
146 | + * Defaults to logged in user if null |
|
147 | + * Builds an access query for a logged out user if 0 |
|
148 | + * @return stdClass|false |
|
149 | + * @access private |
|
150 | + */ |
|
151 | + public function getRow($guid, $user_guid = null) { |
|
152 | + |
|
153 | + if (!$guid) { |
|
154 | + return false; |
|
155 | + } |
|
156 | + |
|
157 | + $access = _elgg_get_access_where_sql([ |
|
158 | + 'table_alias' => '', |
|
159 | + 'user_guid' => $user_guid, |
|
160 | + ]); |
|
161 | + |
|
162 | + $sql = "SELECT * FROM {$this->db->prefix}entities |
|
163 | 163 | WHERE guid = :guid AND $access"; |
164 | 164 | |
165 | - $params = [ |
|
166 | - ':guid' => (int) $guid, |
|
167 | - ]; |
|
168 | - |
|
169 | - return $this->db->getDataRow($sql, null, $params); |
|
170 | - } |
|
171 | - |
|
172 | - /** |
|
173 | - * Adds a new row to the entity table |
|
174 | - * |
|
175 | - * @param stdClass $row Entity base information |
|
176 | - * @param array $attributes All primary and secondary table attributes |
|
177 | - * Used by database mock services to allow mocking |
|
178 | - * entities that were instantiated using new keyword |
|
179 | - * and calling ElggEntity::save() |
|
180 | - * @return int|false |
|
181 | - */ |
|
182 | - public function insertRow(stdClass $row, array $attributes = []) { |
|
183 | - |
|
184 | - $sql = "INSERT INTO {$this->db->prefix}entities |
|
165 | + $params = [ |
|
166 | + ':guid' => (int) $guid, |
|
167 | + ]; |
|
168 | + |
|
169 | + return $this->db->getDataRow($sql, null, $params); |
|
170 | + } |
|
171 | + |
|
172 | + /** |
|
173 | + * Adds a new row to the entity table |
|
174 | + * |
|
175 | + * @param stdClass $row Entity base information |
|
176 | + * @param array $attributes All primary and secondary table attributes |
|
177 | + * Used by database mock services to allow mocking |
|
178 | + * entities that were instantiated using new keyword |
|
179 | + * and calling ElggEntity::save() |
|
180 | + * @return int|false |
|
181 | + */ |
|
182 | + public function insertRow(stdClass $row, array $attributes = []) { |
|
183 | + |
|
184 | + $sql = "INSERT INTO {$this->db->prefix}entities |
|
185 | 185 | (type, subtype, owner_guid, container_guid, |
186 | 186 | access_id, time_created, time_updated, last_action) |
187 | 187 | VALUES |
188 | 188 | (:type, :subtype_id, :owner_guid, :container_guid, |
189 | 189 | :access_id, :time_created, :time_updated, :last_action)"; |
190 | 190 | |
191 | - return $this->db->insertData($sql, [ |
|
192 | - ':type' => $row->type, |
|
193 | - ':subtype_id' => $row->subtype_id, |
|
194 | - ':owner_guid' => $row->owner_guid, |
|
195 | - ':container_guid' => $row->container_guid, |
|
196 | - ':access_id' => $row->access_id, |
|
197 | - ':time_created' => $row->time_created, |
|
198 | - ':time_updated' => $row->time_updated, |
|
199 | - ':last_action' => $row->last_action, |
|
200 | - ]); |
|
201 | - } |
|
202 | - |
|
203 | - /** |
|
204 | - * Update entity table row |
|
205 | - * |
|
206 | - * @param int $guid Entity guid |
|
207 | - * @param stdClass $row Updated data |
|
208 | - * @return int|false |
|
209 | - */ |
|
210 | - public function updateRow($guid, stdClass $row) { |
|
211 | - $sql = " |
|
191 | + return $this->db->insertData($sql, [ |
|
192 | + ':type' => $row->type, |
|
193 | + ':subtype_id' => $row->subtype_id, |
|
194 | + ':owner_guid' => $row->owner_guid, |
|
195 | + ':container_guid' => $row->container_guid, |
|
196 | + ':access_id' => $row->access_id, |
|
197 | + ':time_created' => $row->time_created, |
|
198 | + ':time_updated' => $row->time_updated, |
|
199 | + ':last_action' => $row->last_action, |
|
200 | + ]); |
|
201 | + } |
|
202 | + |
|
203 | + /** |
|
204 | + * Update entity table row |
|
205 | + * |
|
206 | + * @param int $guid Entity guid |
|
207 | + * @param stdClass $row Updated data |
|
208 | + * @return int|false |
|
209 | + */ |
|
210 | + public function updateRow($guid, stdClass $row) { |
|
211 | + $sql = " |
|
212 | 212 | UPDATE {$this->db->prefix}entities |
213 | 213 | SET owner_guid = :owner_guid, |
214 | 214 | access_id = :access_id, |
@@ -218,1291 +218,1291 @@ discard block |
||
218 | 218 | WHERE guid = :guid |
219 | 219 | "; |
220 | 220 | |
221 | - $params = [ |
|
222 | - ':owner_guid' => $row->owner_guid, |
|
223 | - ':access_id' => $row->access_id, |
|
224 | - ':container_guid' => $row->container_guid, |
|
225 | - ':time_created' => $row->time_created, |
|
226 | - ':time_updated' => $row->time_updated, |
|
227 | - ':guid' => $guid, |
|
228 | - ]; |
|
229 | - |
|
230 | - return $this->db->updateData($sql, false, $params); |
|
231 | - } |
|
232 | - |
|
233 | - /** |
|
234 | - * Create an Elgg* object from a given entity row. |
|
235 | - * |
|
236 | - * Handles loading all tables into the correct class. |
|
237 | - * |
|
238 | - * @see get_entity_as_row() |
|
239 | - * @see add_subtype() |
|
240 | - * @see get_entity() |
|
241 | - * |
|
242 | - * @access private |
|
243 | - * |
|
244 | - * @param stdClass $row The row of the entry in the entities table. |
|
245 | - * @return ElggEntity|false |
|
246 | - * @throws ClassException |
|
247 | - * @throws InstallationException |
|
248 | - */ |
|
249 | - public function rowToElggStar($row) { |
|
250 | - if (!$row instanceof stdClass) { |
|
251 | - return $row; |
|
252 | - } |
|
253 | - |
|
254 | - if (!isset($row->guid) || !isset($row->subtype)) { |
|
255 | - return $row; |
|
256 | - } |
|
221 | + $params = [ |
|
222 | + ':owner_guid' => $row->owner_guid, |
|
223 | + ':access_id' => $row->access_id, |
|
224 | + ':container_guid' => $row->container_guid, |
|
225 | + ':time_created' => $row->time_created, |
|
226 | + ':time_updated' => $row->time_updated, |
|
227 | + ':guid' => $guid, |
|
228 | + ]; |
|
229 | + |
|
230 | + return $this->db->updateData($sql, false, $params); |
|
231 | + } |
|
232 | + |
|
233 | + /** |
|
234 | + * Create an Elgg* object from a given entity row. |
|
235 | + * |
|
236 | + * Handles loading all tables into the correct class. |
|
237 | + * |
|
238 | + * @see get_entity_as_row() |
|
239 | + * @see add_subtype() |
|
240 | + * @see get_entity() |
|
241 | + * |
|
242 | + * @access private |
|
243 | + * |
|
244 | + * @param stdClass $row The row of the entry in the entities table. |
|
245 | + * @return ElggEntity|false |
|
246 | + * @throws ClassException |
|
247 | + * @throws InstallationException |
|
248 | + */ |
|
249 | + public function rowToElggStar($row) { |
|
250 | + if (!$row instanceof stdClass) { |
|
251 | + return $row; |
|
252 | + } |
|
253 | + |
|
254 | + if (!isset($row->guid) || !isset($row->subtype)) { |
|
255 | + return $row; |
|
256 | + } |
|
257 | 257 | |
258 | - $class_name = $this->subtype_table->getClassFromId($row->subtype); |
|
259 | - if ($class_name && !class_exists($class_name)) { |
|
260 | - $this->logger->error("Class '$class_name' was not found, missing plugin?"); |
|
261 | - $class_name = ''; |
|
262 | - } |
|
263 | - |
|
264 | - if (!$class_name) { |
|
265 | - $map = [ |
|
266 | - 'object' => ElggObject::class, |
|
267 | - 'user' => ElggUser::class, |
|
268 | - 'group' => ElggGroup::class, |
|
269 | - 'site' => ElggSite::class, |
|
270 | - ]; |
|
271 | - |
|
272 | - if (isset($map[$row->type])) { |
|
273 | - $class_name = $map[$row->type]; |
|
274 | - } else { |
|
275 | - throw new InstallationException("Entity type {$row->type} is not supported."); |
|
276 | - } |
|
277 | - } |
|
278 | - |
|
279 | - $entity = new $class_name($row); |
|
280 | - if (!$entity instanceof ElggEntity) { |
|
281 | - throw new ClassException("$class_name must extend " . ElggEntity::class); |
|
282 | - } |
|
283 | - |
|
284 | - return $entity; |
|
285 | - } |
|
286 | - |
|
287 | - /** |
|
288 | - * Get an entity from the in-memory or memcache caches |
|
289 | - * |
|
290 | - * @param int $guid GUID |
|
291 | - * |
|
292 | - * @return \ElggEntity |
|
293 | - */ |
|
294 | - protected function getFromCache($guid) { |
|
295 | - $entity = $this->entity_cache->get($guid); |
|
296 | - if ($entity) { |
|
297 | - return $entity; |
|
298 | - } |
|
299 | - |
|
300 | - $memcache = _elgg_get_memcache('new_entity_cache'); |
|
301 | - $entity = $memcache->load($guid); |
|
302 | - if (!$entity instanceof ElggEntity) { |
|
303 | - return false; |
|
304 | - } |
|
305 | - |
|
306 | - // Validate accessibility if from memcache |
|
307 | - if (!elgg_get_ignore_access() && !has_access_to_entity($entity)) { |
|
308 | - return false; |
|
309 | - } |
|
310 | - |
|
311 | - $this->entity_cache->set($entity); |
|
312 | - return $entity; |
|
313 | - } |
|
314 | - |
|
315 | - /** |
|
316 | - * Loads and returns an entity object from a guid. |
|
317 | - * |
|
318 | - * @param int $guid The GUID of the entity |
|
319 | - * @param string $type The type of the entity. If given, even an existing entity with the given GUID |
|
320 | - * will not be returned unless its type matches. |
|
321 | - * |
|
322 | - * @return ElggEntity|stdClass|false The correct Elgg or custom object based upon entity type and subtype |
|
323 | - * @throws ClassException |
|
324 | - * @throws InstallationException |
|
325 | - */ |
|
326 | - public function get($guid, $type = '') { |
|
327 | - // We could also use: if (!(int) $guid) { return false }, |
|
328 | - // but that evaluates to a false positive for $guid = true. |
|
329 | - // This is a bit slower, but more thorough. |
|
330 | - if (!is_numeric($guid) || $guid === 0 || $guid === '0') { |
|
331 | - return false; |
|
332 | - } |
|
333 | - |
|
334 | - $guid = (int) $guid; |
|
335 | - |
|
336 | - $entity = $this->getFromCache($guid); |
|
337 | - if ($entity && (!$type || elgg_instanceof($entity, $type))) { |
|
338 | - return $entity; |
|
339 | - } |
|
340 | - |
|
341 | - $row = $this->getRow($guid); |
|
342 | - if (!$row) { |
|
343 | - return false; |
|
344 | - } |
|
345 | - |
|
346 | - if ($type && $row->type != $type) { |
|
347 | - return false; |
|
348 | - } |
|
349 | - |
|
350 | - $entity = $this->rowToElggStar($row); |
|
351 | - |
|
352 | - if ($entity instanceof ElggEntity) { |
|
353 | - $entity->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
354 | - } |
|
355 | - |
|
356 | - return $entity; |
|
357 | - } |
|
358 | - |
|
359 | - /** |
|
360 | - * Does an entity exist? |
|
361 | - * |
|
362 | - * This function checks for the existence of an entity independent of access |
|
363 | - * permissions. It is useful for situations when a user cannot access an entity |
|
364 | - * and it must be determined whether entity has been deleted or the access level |
|
365 | - * has changed. |
|
366 | - * |
|
367 | - * @param int $guid The GUID of the entity |
|
368 | - * @return bool |
|
369 | - */ |
|
370 | - public function exists($guid) { |
|
371 | - |
|
372 | - // need to ignore access and show hidden entities to check existence |
|
373 | - $ia = $this->session->setIgnoreAccess(true); |
|
374 | - $show_hidden = access_show_hidden_entities(true); |
|
375 | - |
|
376 | - $result = $this->getRow($guid); |
|
377 | - |
|
378 | - $this->session->setIgnoreAccess($ia); |
|
379 | - access_show_hidden_entities($show_hidden); |
|
380 | - |
|
381 | - return !empty($result); |
|
382 | - } |
|
383 | - |
|
384 | - /** |
|
385 | - * Enable an entity. |
|
386 | - * |
|
387 | - * @param int $guid GUID of entity to enable |
|
388 | - * @param bool $recursive Recursively enable all entities disabled with the entity? |
|
389 | - * @return bool |
|
390 | - */ |
|
391 | - public function enable($guid, $recursive = true) { |
|
392 | - |
|
393 | - // Override access only visible entities |
|
394 | - $old_access_status = access_get_show_hidden_status(); |
|
395 | - access_show_hidden_entities(true); |
|
396 | - |
|
397 | - $result = false; |
|
398 | - $entity = get_entity($guid); |
|
399 | - if ($entity) { |
|
400 | - $result = $entity->enable($recursive); |
|
401 | - } |
|
402 | - |
|
403 | - access_show_hidden_entities($old_access_status); |
|
404 | - return $result; |
|
405 | - } |
|
406 | - |
|
407 | - /** |
|
408 | - * Returns an array of entities with optional filtering. |
|
409 | - * |
|
410 | - * Entities are the basic unit of storage in Elgg. This function |
|
411 | - * provides the simplest way to get an array of entities. There |
|
412 | - * are many options available that can be passed to filter |
|
413 | - * what sorts of entities are returned. |
|
414 | - * |
|
415 | - * @tip To output formatted strings of entities, use {@link elgg_list_entities()} and |
|
416 | - * its cousins. |
|
417 | - * |
|
418 | - * @tip Plural arguments can be written as singular if only specifying a |
|
419 | - * single element. ('type' => 'object' vs 'types' => array('object')). |
|
420 | - * |
|
421 | - * @see elgg_get_entities_from_metadata() |
|
422 | - * @see elgg_get_entities_from_relationship() |
|
423 | - * @see elgg_get_entities_from_access_id() |
|
424 | - * @see elgg_get_entities_from_annotations() |
|
425 | - * @see elgg_list_entities() |
|
426 | - * |
|
427 | - * @param array $options Array in format: |
|
428 | - * |
|
429 | - * types => null|STR entity type (type IN ('type1', 'type2') |
|
430 | - * Joined with subtypes by AND. See below) |
|
431 | - * |
|
432 | - * subtypes => null|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2)) |
|
433 | - * Use ELGG_ENTITIES_NO_VALUE to match the default subtype. |
|
434 | - * Use ELGG_ENTITIES_ANY_VALUE to match any subtype. |
|
435 | - * |
|
436 | - * type_subtype_pairs => null|ARR (array('type' => 'subtype')) |
|
437 | - * array( |
|
438 | - * 'object' => array('blog', 'file'), // All objects with subtype of 'blog' or 'file' |
|
439 | - * 'user' => ELGG_ENTITY_ANY_VALUE, // All users irrespective of subtype |
|
440 | - * ); |
|
441 | - * |
|
442 | - * guids => null|ARR Array of entity guids |
|
443 | - * |
|
444 | - * owner_guids => null|ARR Array of owner guids |
|
445 | - * |
|
446 | - * container_guids => null|ARR Array of container_guids |
|
447 | - * |
|
448 | - * order_by => null (time_created desc)|STR SQL order by clause |
|
449 | - * |
|
450 | - * reverse_order_by => BOOL Reverse the default order by clause |
|
451 | - * |
|
452 | - * limit => null (10)|INT SQL limit clause (0 means no limit) |
|
453 | - * |
|
454 | - * offset => null (0)|INT SQL offset clause |
|
455 | - * |
|
456 | - * created_time_lower => null|INT Created time lower boundary in epoch time |
|
457 | - * |
|
458 | - * created_time_upper => null|INT Created time upper boundary in epoch time |
|
459 | - * |
|
460 | - * modified_time_lower => null|INT Modified time lower boundary in epoch time |
|
461 | - * |
|
462 | - * modified_time_upper => null|INT Modified time upper boundary in epoch time |
|
463 | - * |
|
464 | - * count => true|false return a count instead of entities |
|
465 | - * |
|
466 | - * wheres => array() Additional where clauses to AND together |
|
467 | - * |
|
468 | - * joins => array() Additional joins |
|
469 | - * |
|
470 | - * preload_owners => bool (false) If set to true, this function will preload |
|
471 | - * all the owners of the returned entities resulting in better |
|
472 | - * performance if those owners need to be displayed |
|
473 | - * |
|
474 | - * preload_containers => bool (false) If set to true, this function will preload |
|
475 | - * all the containers of the returned entities resulting in better |
|
476 | - * performance if those containers need to be displayed |
|
477 | - * |
|
478 | - * |
|
479 | - * callback => string A callback function to pass each row through |
|
480 | - * |
|
481 | - * distinct => bool (true) If set to false, Elgg will drop the DISTINCT clause from |
|
482 | - * the MySQL query, which will improve performance in some situations. |
|
483 | - * Avoid setting this option without a full understanding of the underlying |
|
484 | - * SQL query Elgg creates. |
|
485 | - * |
|
486 | - * batch => bool (false) If set to true, an Elgg\BatchResult object will be returned instead of an array. |
|
487 | - * Since 2.3 |
|
488 | - * |
|
489 | - * batch_inc_offset => bool (true) If "batch" is used, this tells the batch to increment the offset |
|
490 | - * on each fetch. This must be set to false if you delete the batched results. |
|
491 | - * |
|
492 | - * batch_size => int (25) If "batch" is used, this is the number of entities/rows to pull in before |
|
493 | - * requesting more. |
|
494 | - * |
|
495 | - * @return \ElggEntity[]|int|mixed If count, int. Otherwise an array or an Elgg\BatchResult. false on errors. |
|
496 | - * |
|
497 | - * @see elgg_get_entities_from_metadata() |
|
498 | - * @see elgg_get_entities_from_relationship() |
|
499 | - * @see elgg_get_entities_from_access_id() |
|
500 | - * @see elgg_get_entities_from_annotations() |
|
501 | - * @see elgg_list_entities() |
|
502 | - */ |
|
503 | - public function getEntities(array $options = []) { |
|
504 | - _elgg_check_unsupported_site_guid($options); |
|
505 | - |
|
506 | - $defaults = [ |
|
507 | - 'types' => ELGG_ENTITIES_ANY_VALUE, |
|
508 | - 'subtypes' => ELGG_ENTITIES_ANY_VALUE, |
|
509 | - 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
510 | - |
|
511 | - 'guids' => ELGG_ENTITIES_ANY_VALUE, |
|
512 | - 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
513 | - 'container_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
514 | - |
|
515 | - 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
516 | - 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
517 | - 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
518 | - 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
519 | - |
|
520 | - 'reverse_order_by' => false, |
|
521 | - 'order_by' => 'e.time_created desc', |
|
522 | - 'group_by' => ELGG_ENTITIES_ANY_VALUE, |
|
523 | - 'limit' => $this->config->get('default_limit'), |
|
524 | - 'offset' => 0, |
|
525 | - 'count' => false, |
|
526 | - 'selects' => [], |
|
527 | - 'wheres' => [], |
|
528 | - 'joins' => [], |
|
529 | - |
|
530 | - 'preload_owners' => false, |
|
531 | - 'preload_containers' => false, |
|
532 | - 'callback' => 'entity_row_to_elggstar', |
|
533 | - 'distinct' => true, |
|
534 | - |
|
535 | - 'batch' => false, |
|
536 | - 'batch_inc_offset' => true, |
|
537 | - 'batch_size' => 25, |
|
538 | - |
|
539 | - // private API |
|
540 | - '__ElggBatch' => null, |
|
541 | - ]; |
|
542 | - |
|
543 | - $options = array_merge($defaults, $options); |
|
544 | - |
|
545 | - if ($options['batch'] && !$options['count']) { |
|
546 | - $batch_size = $options['batch_size']; |
|
547 | - $batch_inc_offset = $options['batch_inc_offset']; |
|
548 | - |
|
549 | - // clean batch keys from $options. |
|
550 | - unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']); |
|
551 | - |
|
552 | - return new \ElggBatch([$this, 'getEntities'], $options, null, $batch_size, $batch_inc_offset); |
|
553 | - } |
|
258 | + $class_name = $this->subtype_table->getClassFromId($row->subtype); |
|
259 | + if ($class_name && !class_exists($class_name)) { |
|
260 | + $this->logger->error("Class '$class_name' was not found, missing plugin?"); |
|
261 | + $class_name = ''; |
|
262 | + } |
|
263 | + |
|
264 | + if (!$class_name) { |
|
265 | + $map = [ |
|
266 | + 'object' => ElggObject::class, |
|
267 | + 'user' => ElggUser::class, |
|
268 | + 'group' => ElggGroup::class, |
|
269 | + 'site' => ElggSite::class, |
|
270 | + ]; |
|
271 | + |
|
272 | + if (isset($map[$row->type])) { |
|
273 | + $class_name = $map[$row->type]; |
|
274 | + } else { |
|
275 | + throw new InstallationException("Entity type {$row->type} is not supported."); |
|
276 | + } |
|
277 | + } |
|
278 | + |
|
279 | + $entity = new $class_name($row); |
|
280 | + if (!$entity instanceof ElggEntity) { |
|
281 | + throw new ClassException("$class_name must extend " . ElggEntity::class); |
|
282 | + } |
|
283 | + |
|
284 | + return $entity; |
|
285 | + } |
|
286 | + |
|
287 | + /** |
|
288 | + * Get an entity from the in-memory or memcache caches |
|
289 | + * |
|
290 | + * @param int $guid GUID |
|
291 | + * |
|
292 | + * @return \ElggEntity |
|
293 | + */ |
|
294 | + protected function getFromCache($guid) { |
|
295 | + $entity = $this->entity_cache->get($guid); |
|
296 | + if ($entity) { |
|
297 | + return $entity; |
|
298 | + } |
|
299 | + |
|
300 | + $memcache = _elgg_get_memcache('new_entity_cache'); |
|
301 | + $entity = $memcache->load($guid); |
|
302 | + if (!$entity instanceof ElggEntity) { |
|
303 | + return false; |
|
304 | + } |
|
305 | + |
|
306 | + // Validate accessibility if from memcache |
|
307 | + if (!elgg_get_ignore_access() && !has_access_to_entity($entity)) { |
|
308 | + return false; |
|
309 | + } |
|
310 | + |
|
311 | + $this->entity_cache->set($entity); |
|
312 | + return $entity; |
|
313 | + } |
|
314 | + |
|
315 | + /** |
|
316 | + * Loads and returns an entity object from a guid. |
|
317 | + * |
|
318 | + * @param int $guid The GUID of the entity |
|
319 | + * @param string $type The type of the entity. If given, even an existing entity with the given GUID |
|
320 | + * will not be returned unless its type matches. |
|
321 | + * |
|
322 | + * @return ElggEntity|stdClass|false The correct Elgg or custom object based upon entity type and subtype |
|
323 | + * @throws ClassException |
|
324 | + * @throws InstallationException |
|
325 | + */ |
|
326 | + public function get($guid, $type = '') { |
|
327 | + // We could also use: if (!(int) $guid) { return false }, |
|
328 | + // but that evaluates to a false positive for $guid = true. |
|
329 | + // This is a bit slower, but more thorough. |
|
330 | + if (!is_numeric($guid) || $guid === 0 || $guid === '0') { |
|
331 | + return false; |
|
332 | + } |
|
333 | + |
|
334 | + $guid = (int) $guid; |
|
335 | + |
|
336 | + $entity = $this->getFromCache($guid); |
|
337 | + if ($entity && (!$type || elgg_instanceof($entity, $type))) { |
|
338 | + return $entity; |
|
339 | + } |
|
340 | + |
|
341 | + $row = $this->getRow($guid); |
|
342 | + if (!$row) { |
|
343 | + return false; |
|
344 | + } |
|
345 | + |
|
346 | + if ($type && $row->type != $type) { |
|
347 | + return false; |
|
348 | + } |
|
349 | + |
|
350 | + $entity = $this->rowToElggStar($row); |
|
351 | + |
|
352 | + if ($entity instanceof ElggEntity) { |
|
353 | + $entity->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
354 | + } |
|
355 | + |
|
356 | + return $entity; |
|
357 | + } |
|
358 | + |
|
359 | + /** |
|
360 | + * Does an entity exist? |
|
361 | + * |
|
362 | + * This function checks for the existence of an entity independent of access |
|
363 | + * permissions. It is useful for situations when a user cannot access an entity |
|
364 | + * and it must be determined whether entity has been deleted or the access level |
|
365 | + * has changed. |
|
366 | + * |
|
367 | + * @param int $guid The GUID of the entity |
|
368 | + * @return bool |
|
369 | + */ |
|
370 | + public function exists($guid) { |
|
371 | + |
|
372 | + // need to ignore access and show hidden entities to check existence |
|
373 | + $ia = $this->session->setIgnoreAccess(true); |
|
374 | + $show_hidden = access_show_hidden_entities(true); |
|
375 | + |
|
376 | + $result = $this->getRow($guid); |
|
377 | + |
|
378 | + $this->session->setIgnoreAccess($ia); |
|
379 | + access_show_hidden_entities($show_hidden); |
|
380 | + |
|
381 | + return !empty($result); |
|
382 | + } |
|
383 | + |
|
384 | + /** |
|
385 | + * Enable an entity. |
|
386 | + * |
|
387 | + * @param int $guid GUID of entity to enable |
|
388 | + * @param bool $recursive Recursively enable all entities disabled with the entity? |
|
389 | + * @return bool |
|
390 | + */ |
|
391 | + public function enable($guid, $recursive = true) { |
|
392 | + |
|
393 | + // Override access only visible entities |
|
394 | + $old_access_status = access_get_show_hidden_status(); |
|
395 | + access_show_hidden_entities(true); |
|
396 | + |
|
397 | + $result = false; |
|
398 | + $entity = get_entity($guid); |
|
399 | + if ($entity) { |
|
400 | + $result = $entity->enable($recursive); |
|
401 | + } |
|
402 | + |
|
403 | + access_show_hidden_entities($old_access_status); |
|
404 | + return $result; |
|
405 | + } |
|
406 | + |
|
407 | + /** |
|
408 | + * Returns an array of entities with optional filtering. |
|
409 | + * |
|
410 | + * Entities are the basic unit of storage in Elgg. This function |
|
411 | + * provides the simplest way to get an array of entities. There |
|
412 | + * are many options available that can be passed to filter |
|
413 | + * what sorts of entities are returned. |
|
414 | + * |
|
415 | + * @tip To output formatted strings of entities, use {@link elgg_list_entities()} and |
|
416 | + * its cousins. |
|
417 | + * |
|
418 | + * @tip Plural arguments can be written as singular if only specifying a |
|
419 | + * single element. ('type' => 'object' vs 'types' => array('object')). |
|
420 | + * |
|
421 | + * @see elgg_get_entities_from_metadata() |
|
422 | + * @see elgg_get_entities_from_relationship() |
|
423 | + * @see elgg_get_entities_from_access_id() |
|
424 | + * @see elgg_get_entities_from_annotations() |
|
425 | + * @see elgg_list_entities() |
|
426 | + * |
|
427 | + * @param array $options Array in format: |
|
428 | + * |
|
429 | + * types => null|STR entity type (type IN ('type1', 'type2') |
|
430 | + * Joined with subtypes by AND. See below) |
|
431 | + * |
|
432 | + * subtypes => null|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2)) |
|
433 | + * Use ELGG_ENTITIES_NO_VALUE to match the default subtype. |
|
434 | + * Use ELGG_ENTITIES_ANY_VALUE to match any subtype. |
|
435 | + * |
|
436 | + * type_subtype_pairs => null|ARR (array('type' => 'subtype')) |
|
437 | + * array( |
|
438 | + * 'object' => array('blog', 'file'), // All objects with subtype of 'blog' or 'file' |
|
439 | + * 'user' => ELGG_ENTITY_ANY_VALUE, // All users irrespective of subtype |
|
440 | + * ); |
|
441 | + * |
|
442 | + * guids => null|ARR Array of entity guids |
|
443 | + * |
|
444 | + * owner_guids => null|ARR Array of owner guids |
|
445 | + * |
|
446 | + * container_guids => null|ARR Array of container_guids |
|
447 | + * |
|
448 | + * order_by => null (time_created desc)|STR SQL order by clause |
|
449 | + * |
|
450 | + * reverse_order_by => BOOL Reverse the default order by clause |
|
451 | + * |
|
452 | + * limit => null (10)|INT SQL limit clause (0 means no limit) |
|
453 | + * |
|
454 | + * offset => null (0)|INT SQL offset clause |
|
455 | + * |
|
456 | + * created_time_lower => null|INT Created time lower boundary in epoch time |
|
457 | + * |
|
458 | + * created_time_upper => null|INT Created time upper boundary in epoch time |
|
459 | + * |
|
460 | + * modified_time_lower => null|INT Modified time lower boundary in epoch time |
|
461 | + * |
|
462 | + * modified_time_upper => null|INT Modified time upper boundary in epoch time |
|
463 | + * |
|
464 | + * count => true|false return a count instead of entities |
|
465 | + * |
|
466 | + * wheres => array() Additional where clauses to AND together |
|
467 | + * |
|
468 | + * joins => array() Additional joins |
|
469 | + * |
|
470 | + * preload_owners => bool (false) If set to true, this function will preload |
|
471 | + * all the owners of the returned entities resulting in better |
|
472 | + * performance if those owners need to be displayed |
|
473 | + * |
|
474 | + * preload_containers => bool (false) If set to true, this function will preload |
|
475 | + * all the containers of the returned entities resulting in better |
|
476 | + * performance if those containers need to be displayed |
|
477 | + * |
|
478 | + * |
|
479 | + * callback => string A callback function to pass each row through |
|
480 | + * |
|
481 | + * distinct => bool (true) If set to false, Elgg will drop the DISTINCT clause from |
|
482 | + * the MySQL query, which will improve performance in some situations. |
|
483 | + * Avoid setting this option without a full understanding of the underlying |
|
484 | + * SQL query Elgg creates. |
|
485 | + * |
|
486 | + * batch => bool (false) If set to true, an Elgg\BatchResult object will be returned instead of an array. |
|
487 | + * Since 2.3 |
|
488 | + * |
|
489 | + * batch_inc_offset => bool (true) If "batch" is used, this tells the batch to increment the offset |
|
490 | + * on each fetch. This must be set to false if you delete the batched results. |
|
491 | + * |
|
492 | + * batch_size => int (25) If "batch" is used, this is the number of entities/rows to pull in before |
|
493 | + * requesting more. |
|
494 | + * |
|
495 | + * @return \ElggEntity[]|int|mixed If count, int. Otherwise an array or an Elgg\BatchResult. false on errors. |
|
496 | + * |
|
497 | + * @see elgg_get_entities_from_metadata() |
|
498 | + * @see elgg_get_entities_from_relationship() |
|
499 | + * @see elgg_get_entities_from_access_id() |
|
500 | + * @see elgg_get_entities_from_annotations() |
|
501 | + * @see elgg_list_entities() |
|
502 | + */ |
|
503 | + public function getEntities(array $options = []) { |
|
504 | + _elgg_check_unsupported_site_guid($options); |
|
505 | + |
|
506 | + $defaults = [ |
|
507 | + 'types' => ELGG_ENTITIES_ANY_VALUE, |
|
508 | + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, |
|
509 | + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
510 | + |
|
511 | + 'guids' => ELGG_ENTITIES_ANY_VALUE, |
|
512 | + 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
513 | + 'container_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
514 | + |
|
515 | + 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
516 | + 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
517 | + 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
518 | + 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
519 | + |
|
520 | + 'reverse_order_by' => false, |
|
521 | + 'order_by' => 'e.time_created desc', |
|
522 | + 'group_by' => ELGG_ENTITIES_ANY_VALUE, |
|
523 | + 'limit' => $this->config->get('default_limit'), |
|
524 | + 'offset' => 0, |
|
525 | + 'count' => false, |
|
526 | + 'selects' => [], |
|
527 | + 'wheres' => [], |
|
528 | + 'joins' => [], |
|
529 | + |
|
530 | + 'preload_owners' => false, |
|
531 | + 'preload_containers' => false, |
|
532 | + 'callback' => 'entity_row_to_elggstar', |
|
533 | + 'distinct' => true, |
|
534 | + |
|
535 | + 'batch' => false, |
|
536 | + 'batch_inc_offset' => true, |
|
537 | + 'batch_size' => 25, |
|
538 | + |
|
539 | + // private API |
|
540 | + '__ElggBatch' => null, |
|
541 | + ]; |
|
542 | + |
|
543 | + $options = array_merge($defaults, $options); |
|
544 | + |
|
545 | + if ($options['batch'] && !$options['count']) { |
|
546 | + $batch_size = $options['batch_size']; |
|
547 | + $batch_inc_offset = $options['batch_inc_offset']; |
|
548 | + |
|
549 | + // clean batch keys from $options. |
|
550 | + unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']); |
|
551 | + |
|
552 | + return new \ElggBatch([$this, 'getEntities'], $options, null, $batch_size, $batch_inc_offset); |
|
553 | + } |
|
554 | 554 | |
555 | - // can't use helper function with type_subtype_pair because |
|
556 | - // it's already an array...just need to merge it |
|
557 | - if (isset($options['type_subtype_pair'])) { |
|
558 | - if (isset($options['type_subtype_pairs'])) { |
|
559 | - $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], |
|
560 | - $options['type_subtype_pair']); |
|
561 | - } else { |
|
562 | - $options['type_subtype_pairs'] = $options['type_subtype_pair']; |
|
563 | - } |
|
564 | - } |
|
565 | - |
|
566 | - $singulars = ['type', 'subtype', 'guid', 'owner_guid', 'container_guid']; |
|
567 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
568 | - |
|
569 | - $options = $this->autoJoinTables($options); |
|
570 | - |
|
571 | - // evaluate where clauses |
|
572 | - if (!is_array($options['wheres'])) { |
|
573 | - $options['wheres'] = [$options['wheres']]; |
|
574 | - } |
|
575 | - |
|
576 | - $wheres = $options['wheres']; |
|
577 | - |
|
578 | - $wheres[] = $this->getEntityTypeSubtypeWhereSql('e', $options['types'], |
|
579 | - $options['subtypes'], $options['type_subtype_pairs']); |
|
580 | - |
|
581 | - $wheres[] = $this->getGuidBasedWhereSql('e.guid', $options['guids']); |
|
582 | - $wheres[] = $this->getGuidBasedWhereSql('e.owner_guid', $options['owner_guids']); |
|
583 | - $wheres[] = $this->getGuidBasedWhereSql('e.container_guid', $options['container_guids']); |
|
584 | - |
|
585 | - $wheres[] = $this->getEntityTimeWhereSql('e', $options['created_time_upper'], |
|
586 | - $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']); |
|
587 | - |
|
588 | - // see if any functions failed |
|
589 | - // remove empty strings on successful functions |
|
590 | - foreach ($wheres as $i => $where) { |
|
591 | - if ($where === false) { |
|
592 | - return false; |
|
593 | - } elseif (empty($where)) { |
|
594 | - unset($wheres[$i]); |
|
595 | - } |
|
596 | - } |
|
597 | - |
|
598 | - // remove identical where clauses |
|
599 | - $wheres = array_unique($wheres); |
|
600 | - |
|
601 | - // evaluate join clauses |
|
602 | - if (!is_array($options['joins'])) { |
|
603 | - $options['joins'] = [$options['joins']]; |
|
604 | - } |
|
605 | - |
|
606 | - // remove identical join clauses |
|
607 | - $joins = array_unique($options['joins']); |
|
608 | - |
|
609 | - foreach ($joins as $i => $join) { |
|
610 | - if ($join === false) { |
|
611 | - return false; |
|
612 | - } elseif (empty($join)) { |
|
613 | - unset($joins[$i]); |
|
614 | - } |
|
615 | - } |
|
616 | - |
|
617 | - // evalutate selects |
|
618 | - if ($options['selects']) { |
|
619 | - $selects = ''; |
|
620 | - foreach ($options['selects'] as $select) { |
|
621 | - $selects .= ", $select"; |
|
622 | - } |
|
623 | - } else { |
|
624 | - $selects = ''; |
|
625 | - } |
|
626 | - |
|
627 | - if (!$options['count']) { |
|
628 | - $distinct = $options['distinct'] ? "DISTINCT" : ""; |
|
629 | - $query = "SELECT $distinct e.*{$selects} FROM {$this->db->prefix}entities e "; |
|
630 | - } else { |
|
631 | - // note: when DISTINCT unneeded, it's slightly faster to compute COUNT(*) than GUIDs |
|
632 | - $count_expr = $options['distinct'] ? "DISTINCT e.guid" : "*"; |
|
633 | - $query = "SELECT COUNT($count_expr) as total FROM {$this->db->prefix}entities e "; |
|
634 | - } |
|
635 | - |
|
636 | - // add joins |
|
637 | - foreach ($joins as $j) { |
|
638 | - $query .= " $j "; |
|
639 | - } |
|
640 | - |
|
641 | - // add wheres |
|
642 | - $query .= ' WHERE '; |
|
643 | - |
|
644 | - foreach ($wheres as $w) { |
|
645 | - $query .= " $w AND "; |
|
646 | - } |
|
647 | - |
|
648 | - // Add access controls |
|
649 | - $query .= _elgg_get_access_where_sql(); |
|
650 | - |
|
651 | - // reverse order by |
|
652 | - if ($options['reverse_order_by']) { |
|
653 | - $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']); |
|
654 | - } |
|
655 | - |
|
656 | - if ($options['count']) { |
|
657 | - $total = $this->db->getDataRow($query); |
|
658 | - return (int) $total->total; |
|
659 | - } |
|
660 | - |
|
661 | - if ($options['group_by']) { |
|
662 | - $query .= " GROUP BY {$options['group_by']}"; |
|
663 | - } |
|
664 | - |
|
665 | - if ($options['order_by']) { |
|
666 | - $query .= " ORDER BY {$options['order_by']}"; |
|
667 | - } |
|
668 | - |
|
669 | - if ($options['limit']) { |
|
670 | - $limit = sanitise_int($options['limit'], false); |
|
671 | - $offset = sanitise_int($options['offset'], false); |
|
672 | - $query .= " LIMIT $offset, $limit"; |
|
673 | - } |
|
674 | - |
|
675 | - if ($options['callback'] === 'entity_row_to_elggstar') { |
|
676 | - $results = $this->fetchFromSql($query, $options['__ElggBatch']); |
|
677 | - } else { |
|
678 | - $results = $this->db->getData($query, $options['callback']); |
|
679 | - } |
|
680 | - |
|
681 | - if (!$results) { |
|
682 | - // no results, no preloading |
|
683 | - return $results; |
|
684 | - } |
|
685 | - |
|
686 | - // populate entity and metadata caches, and prepare $entities for preloader |
|
687 | - $guids = []; |
|
688 | - foreach ($results as $item) { |
|
689 | - // A custom callback could result in items that aren't \ElggEntity's, so check for them |
|
690 | - if ($item instanceof ElggEntity) { |
|
691 | - $this->entity_cache->set($item); |
|
692 | - // plugins usually have only settings |
|
693 | - if (!$item instanceof ElggPlugin) { |
|
694 | - $guids[] = $item->guid; |
|
695 | - } |
|
696 | - } |
|
697 | - } |
|
698 | - // @todo Without this, recursive delete fails. See #4568 |
|
699 | - reset($results); |
|
700 | - |
|
701 | - if ($guids) { |
|
702 | - // there were entities in the result set, preload metadata for them |
|
703 | - $this->metadata_cache->populateFromEntities($guids); |
|
704 | - } |
|
705 | - |
|
706 | - if (count($results) > 1) { |
|
707 | - $props_to_preload = []; |
|
708 | - if ($options['preload_owners']) { |
|
709 | - $props_to_preload[] = 'owner_guid'; |
|
710 | - } |
|
711 | - if ($options['preload_containers']) { |
|
712 | - $props_to_preload[] = 'container_guid'; |
|
713 | - } |
|
714 | - if ($props_to_preload) { |
|
715 | - // note, ElggEntityPreloaderIntegrationTest assumes it can swap out |
|
716 | - // the preloader after boot. If you inject this component at construction |
|
717 | - // time that unit test will break. :/ |
|
718 | - _elgg_services()->entityPreloader->preload($results, $props_to_preload); |
|
719 | - } |
|
720 | - } |
|
721 | - |
|
722 | - return $results; |
|
723 | - } |
|
724 | - |
|
725 | - /** |
|
726 | - * Decorate getEntities() options in order to auto-join secondary tables where it's |
|
727 | - * safe to do so. |
|
728 | - * |
|
729 | - * @param array $options Options array in getEntities() after normalization |
|
730 | - * @return array |
|
731 | - */ |
|
732 | - protected function autoJoinTables(array $options) { |
|
733 | - // we must be careful that the query doesn't specify any options that may join |
|
734 | - // tables or change the selected columns |
|
735 | - if (!is_array($options['types']) |
|
736 | - || count($options['types']) !== 1 |
|
737 | - || !empty($options['selects']) |
|
738 | - || !empty($options['wheres']) |
|
739 | - || !empty($options['joins']) |
|
740 | - || $options['callback'] !== 'entity_row_to_elggstar' |
|
741 | - || $options['count']) { |
|
742 | - // Too dangerous to auto-join |
|
743 | - return $options; |
|
744 | - } |
|
745 | - |
|
746 | - $join_types = [ |
|
747 | - // Each class must have a static getExternalAttributes() : array |
|
748 | - 'object' => 'ElggObject', |
|
749 | - 'user' => 'ElggUser', |
|
750 | - 'group' => 'ElggGroup', |
|
751 | - 'site' => 'ElggSite', |
|
752 | - ]; |
|
753 | - |
|
754 | - // We use reset() because $options['types'] may not have a numeric key |
|
755 | - $type = reset($options['types']); |
|
756 | - if (empty($join_types[$type])) { |
|
757 | - return $options; |
|
758 | - } |
|
759 | - |
|
760 | - // Get the columns we'll need to select. We can't use st.* because the order_by |
|
761 | - // clause may reference "guid", which MySQL will complain about being ambiguous |
|
762 | - if (!is_callable([$join_types[$type], 'getExternalAttributes'])) { |
|
763 | - // for some reason can't get external attributes. |
|
764 | - return $options; |
|
765 | - } |
|
766 | - |
|
767 | - $attributes = $join_types[$type]::getExternalAttributes(); |
|
768 | - foreach (array_keys($attributes) as $col) { |
|
769 | - $options['selects'][] = "st.$col"; |
|
770 | - } |
|
771 | - |
|
772 | - // join the secondary table |
|
773 | - $options['joins'][] = "JOIN {$this->db->prefix}{$type}s_entity st ON (e.guid = st.guid)"; |
|
774 | - |
|
775 | - return $options; |
|
776 | - } |
|
777 | - |
|
778 | - /** |
|
779 | - * Return entities from an SQL query generated by elgg_get_entities. |
|
780 | - * |
|
781 | - * @access private |
|
782 | - * |
|
783 | - * @param string $sql |
|
784 | - * @param ElggBatch $batch |
|
785 | - * @return ElggEntity[] |
|
786 | - * @throws LogicException |
|
787 | - */ |
|
788 | - public function fetchFromSql($sql, \ElggBatch $batch = null) { |
|
789 | - $plugin_subtype = $this->subtype_table->getId('object', 'plugin'); |
|
790 | - |
|
791 | - // Keys are types, values are columns that, if present, suggest that the secondary |
|
792 | - // table is already JOINed. Note it's OK if guess incorrectly because entity load() |
|
793 | - // will fetch any missing attributes. |
|
794 | - $types_to_optimize = [ |
|
795 | - 'object' => 'title', |
|
796 | - 'user' => 'password_hash', |
|
797 | - 'group' => 'name', |
|
798 | - 'site' => 'url', |
|
799 | - ]; |
|
800 | - |
|
801 | - $rows = $this->db->getData($sql); |
|
802 | - |
|
803 | - // guids to look up in each type |
|
804 | - $lookup_types = []; |
|
805 | - // maps GUIDs to the $rows key |
|
806 | - $guid_to_key = []; |
|
807 | - |
|
808 | - if (isset($rows[0]->type, $rows[0]->subtype) |
|
809 | - && $rows[0]->type === 'object' |
|
810 | - && $rows[0]->subtype == $plugin_subtype) { |
|
811 | - // Likely the entire resultset is plugins, which have already been optimized |
|
812 | - // to JOIN the secondary table. In this case we allow retrieving from cache, |
|
813 | - // but abandon the extra queries. |
|
814 | - $types_to_optimize = []; |
|
815 | - } |
|
816 | - |
|
817 | - // First pass: use cache where possible, gather GUIDs that we're optimizing |
|
818 | - foreach ($rows as $i => $row) { |
|
819 | - if (empty($row->guid) || empty($row->type)) { |
|
820 | - throw new LogicException('Entity row missing guid or type'); |
|
821 | - } |
|
822 | - |
|
823 | - // We try ephemeral cache because it's blazingly fast and we ideally want to access |
|
824 | - // the same PHP instance. We don't try memcache because it isn't worth the overhead. |
|
825 | - $entity = $this->entity_cache->get($row->guid); |
|
826 | - if ($entity) { |
|
827 | - // from static var, must be refreshed in case row has extra columns |
|
828 | - $entity->refresh($row); |
|
829 | - $rows[$i] = $entity; |
|
830 | - continue; |
|
831 | - } |
|
832 | - |
|
833 | - if (isset($types_to_optimize[$row->type])) { |
|
834 | - // check if row already looks JOINed. |
|
835 | - if (isset($row->{$types_to_optimize[$row->type]})) { |
|
836 | - // Row probably already contains JOINed secondary table. Don't make another query just |
|
837 | - // to pull data that's already there |
|
838 | - continue; |
|
839 | - } |
|
840 | - $lookup_types[$row->type][] = $row->guid; |
|
841 | - $guid_to_key[$row->guid] = $i; |
|
842 | - } |
|
843 | - } |
|
844 | - // Do secondary queries and merge rows |
|
845 | - if ($lookup_types) { |
|
846 | - foreach ($lookup_types as $type => $guids) { |
|
847 | - $set = "(" . implode(',', $guids) . ")"; |
|
848 | - $sql = "SELECT * FROM {$this->db->prefix}{$type}s_entity WHERE guid IN $set"; |
|
849 | - $secondary_rows = $this->db->getData($sql); |
|
850 | - if ($secondary_rows) { |
|
851 | - foreach ($secondary_rows as $secondary_row) { |
|
852 | - $key = $guid_to_key[$secondary_row->guid]; |
|
853 | - // cast to arrays to merge then cast back |
|
854 | - $rows[$key] = (object) array_merge((array) $rows[$key], (array) $secondary_row); |
|
855 | - } |
|
856 | - } |
|
857 | - } |
|
858 | - } |
|
859 | - // Second pass to finish conversion |
|
860 | - foreach ($rows as $i => $row) { |
|
861 | - if ($row instanceof ElggEntity) { |
|
862 | - continue; |
|
863 | - } else { |
|
864 | - try { |
|
865 | - $rows[$i] = $this->rowToElggStar($row); |
|
866 | - } catch (IncompleteEntityException $e) { |
|
867 | - // don't let incomplete entities throw fatal errors |
|
868 | - unset($rows[$i]); |
|
869 | - |
|
870 | - // report incompletes to the batch process that spawned this query |
|
871 | - if ($batch) { |
|
872 | - $batch->reportIncompleteEntity($row); |
|
873 | - } |
|
874 | - } |
|
875 | - } |
|
876 | - } |
|
877 | - return $rows; |
|
878 | - } |
|
879 | - |
|
880 | - /** |
|
881 | - * Returns SQL where clause for type and subtype on main entity table |
|
882 | - * |
|
883 | - * @param string $table Entity table prefix as defined in SELECT...FROM entities $table |
|
884 | - * @param null|array $types Array of types or null if none. |
|
885 | - * @param null|array $subtypes Array of subtypes or null if none |
|
886 | - * @param null|array $pairs Array of pairs of types and subtypes |
|
887 | - * |
|
888 | - * @return false|string |
|
889 | - * @access private |
|
890 | - */ |
|
891 | - public function getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs) { |
|
892 | - // subtype depends upon type. |
|
893 | - if ($subtypes && !$types) { |
|
894 | - $this->logger->warn("Cannot set subtypes without type."); |
|
895 | - return false; |
|
896 | - } |
|
897 | - |
|
898 | - // short circuit if nothing is requested |
|
899 | - if (!$types && !$subtypes && !$pairs) { |
|
900 | - return ''; |
|
901 | - } |
|
902 | - |
|
903 | - // these are the only valid types for entities in elgg |
|
904 | - $valid_types = $this->config->get('entity_types'); |
|
905 | - |
|
906 | - // pairs override |
|
907 | - $wheres = []; |
|
908 | - if (!is_array($pairs)) { |
|
909 | - if (!is_array($types)) { |
|
910 | - $types = [$types]; |
|
911 | - } |
|
912 | - |
|
913 | - if ($subtypes && !is_array($subtypes)) { |
|
914 | - $subtypes = [$subtypes]; |
|
915 | - } |
|
916 | - |
|
917 | - // decrementer for valid types. Return false if no valid types |
|
918 | - $valid_types_count = count($types); |
|
919 | - $valid_subtypes_count = 0; |
|
920 | - // remove invalid types to get an accurate count of |
|
921 | - // valid types for the invalid subtype detection to use |
|
922 | - // below. |
|
923 | - // also grab the count of ALL subtypes on valid types to decrement later on |
|
924 | - // and check against. |
|
925 | - // |
|
926 | - // yes this is duplicating a foreach on $types. |
|
927 | - foreach ($types as $type) { |
|
928 | - if (!in_array($type, $valid_types)) { |
|
929 | - $valid_types_count--; |
|
930 | - unset($types[array_search($type, $types)]); |
|
931 | - } else { |
|
932 | - // do the checking (and decrementing) in the subtype section. |
|
933 | - $valid_subtypes_count += count($subtypes); |
|
934 | - } |
|
935 | - } |
|
936 | - |
|
937 | - // return false if nothing is valid. |
|
938 | - if (!$valid_types_count) { |
|
939 | - return false; |
|
940 | - } |
|
941 | - |
|
942 | - // subtypes are based upon types, so we need to look at each |
|
943 | - // type individually to get the right subtype id. |
|
944 | - foreach ($types as $type) { |
|
945 | - $subtype_ids = []; |
|
946 | - if ($subtypes) { |
|
947 | - foreach ($subtypes as $subtype) { |
|
948 | - // check that the subtype is valid |
|
949 | - if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) { |
|
950 | - // subtype value is 0 |
|
951 | - $subtype_ids[] = ELGG_ENTITIES_NO_VALUE; |
|
952 | - } elseif (!$subtype) { |
|
953 | - // subtype is ignored. |
|
954 | - // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0 |
|
955 | - continue; |
|
956 | - } else { |
|
957 | - $subtype_id = get_subtype_id($type, $subtype); |
|
958 | - |
|
959 | - if ($subtype_id) { |
|
960 | - $subtype_ids[] = $subtype_id; |
|
961 | - } else { |
|
962 | - $valid_subtypes_count--; |
|
963 | - $this->logger->notice("Type-subtype '$type:$subtype' does not exist!"); |
|
964 | - continue; |
|
965 | - } |
|
966 | - } |
|
967 | - } |
|
968 | - |
|
969 | - // return false if we're all invalid subtypes in the only valid type |
|
970 | - if ($valid_subtypes_count <= 0) { |
|
971 | - return false; |
|
972 | - } |
|
973 | - } |
|
974 | - |
|
975 | - if (is_array($subtype_ids) && count($subtype_ids)) { |
|
976 | - $subtype_ids_str = implode(',', $subtype_ids); |
|
977 | - $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))"; |
|
978 | - } else { |
|
979 | - $wheres[] = "({$table}.type = '$type')"; |
|
980 | - } |
|
981 | - } |
|
982 | - } else { |
|
983 | - // using type/subtype pairs |
|
984 | - $valid_pairs_count = count($pairs); |
|
985 | - $valid_pairs_subtypes_count = 0; |
|
986 | - |
|
987 | - // same deal as above--we need to know how many valid types |
|
988 | - // and subtypes we have before hitting the subtype section. |
|
989 | - // also normalize the subtypes into arrays here. |
|
990 | - foreach ($pairs as $paired_type => $paired_subtypes) { |
|
991 | - if (!in_array($paired_type, $valid_types)) { |
|
992 | - $valid_pairs_count--; |
|
993 | - unset($pairs[array_search($paired_type, $pairs)]); |
|
994 | - } else { |
|
995 | - if ($paired_subtypes && !is_array($paired_subtypes)) { |
|
996 | - $pairs[$paired_type] = [$paired_subtypes]; |
|
997 | - } |
|
998 | - $valid_pairs_subtypes_count += count($paired_subtypes); |
|
999 | - } |
|
1000 | - } |
|
1001 | - |
|
1002 | - if ($valid_pairs_count <= 0) { |
|
1003 | - return false; |
|
1004 | - } |
|
1005 | - foreach ($pairs as $paired_type => $paired_subtypes) { |
|
1006 | - // this will always be an array because of line 2027, right? |
|
1007 | - // no...some overly clever person can say pair => array('object' => null) |
|
1008 | - if (is_array($paired_subtypes)) { |
|
1009 | - $paired_subtype_ids = []; |
|
1010 | - foreach ($paired_subtypes as $paired_subtype) { |
|
1011 | - if (ELGG_ENTITIES_NO_VALUE === $paired_subtype || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) { |
|
1012 | - $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ? |
|
1013 | - ELGG_ENTITIES_NO_VALUE : $paired_subtype_id; |
|
1014 | - } else { |
|
1015 | - $valid_pairs_subtypes_count--; |
|
1016 | - $this->logger->notice("Type-subtype '$paired_type:$paired_subtype' does not exist!"); |
|
1017 | - // return false if we're all invalid subtypes in the only valid type |
|
1018 | - continue; |
|
1019 | - } |
|
1020 | - } |
|
1021 | - |
|
1022 | - // return false if there are no valid subtypes. |
|
1023 | - if ($valid_pairs_subtypes_count <= 0) { |
|
1024 | - return false; |
|
1025 | - } |
|
1026 | - |
|
1027 | - |
|
1028 | - if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) { |
|
1029 | - $wheres[] = "({$table}.type = '$paired_type'" |
|
1030 | - . " AND {$table}.subtype IN ($paired_subtype_ids_str))"; |
|
1031 | - } |
|
1032 | - } else { |
|
1033 | - $wheres[] = "({$table}.type = '$paired_type')"; |
|
1034 | - } |
|
1035 | - } |
|
1036 | - } |
|
1037 | - |
|
1038 | - // pairs override the above. return false if they don't exist. |
|
1039 | - if (is_array($wheres) && count($wheres)) { |
|
1040 | - $where = implode(' OR ', $wheres); |
|
1041 | - return "($where)"; |
|
1042 | - } |
|
1043 | - |
|
1044 | - return ''; |
|
1045 | - } |
|
1046 | - |
|
1047 | - /** |
|
1048 | - * Returns SQL where clause for owner and containers. |
|
1049 | - * |
|
1050 | - * @param string $column Column name the guids should be checked against. Usually |
|
1051 | - * best to provide in table.column format. |
|
1052 | - * @param null|array $guids Array of GUIDs. |
|
1053 | - * |
|
1054 | - * @return false|string |
|
1055 | - * @access private |
|
1056 | - */ |
|
1057 | - public function getGuidBasedWhereSql($column, $guids) { |
|
1058 | - // short circuit if nothing requested |
|
1059 | - // 0 is a valid guid |
|
1060 | - if (!$guids && $guids !== 0) { |
|
1061 | - return ''; |
|
1062 | - } |
|
1063 | - |
|
1064 | - // normalize and sanitise owners |
|
1065 | - if (!is_array($guids)) { |
|
1066 | - $guids = [$guids]; |
|
1067 | - } |
|
1068 | - |
|
1069 | - $guids_sanitized = []; |
|
1070 | - foreach ($guids as $guid) { |
|
1071 | - if ($guid !== ELGG_ENTITIES_NO_VALUE) { |
|
1072 | - $guid = sanitise_int($guid); |
|
1073 | - |
|
1074 | - if (!$guid) { |
|
1075 | - return false; |
|
1076 | - } |
|
1077 | - } |
|
1078 | - $guids_sanitized[] = $guid; |
|
1079 | - } |
|
1080 | - |
|
1081 | - $where = ''; |
|
1082 | - $guid_str = implode(',', $guids_sanitized); |
|
1083 | - |
|
1084 | - // implode(',', 0) returns 0. |
|
1085 | - if ($guid_str !== false && $guid_str !== '') { |
|
1086 | - $where = "($column IN ($guid_str))"; |
|
1087 | - } |
|
1088 | - |
|
1089 | - return $where; |
|
1090 | - } |
|
1091 | - |
|
1092 | - /** |
|
1093 | - * Returns SQL where clause for entity time limits. |
|
1094 | - * |
|
1095 | - * @param string $table Entity table prefix as defined in |
|
1096 | - * SELECT...FROM entities $table |
|
1097 | - * @param null|int $time_created_upper Time created upper limit |
|
1098 | - * @param null|int $time_created_lower Time created lower limit |
|
1099 | - * @param null|int $time_updated_upper Time updated upper limit |
|
1100 | - * @param null|int $time_updated_lower Time updated lower limit |
|
1101 | - * |
|
1102 | - * @return false|string false on fail, string on success. |
|
1103 | - * @access private |
|
1104 | - */ |
|
1105 | - public function getEntityTimeWhereSql($table, $time_created_upper = null, |
|
1106 | - $time_created_lower = null, $time_updated_upper = null, $time_updated_lower = null) { |
|
1107 | - |
|
1108 | - $wheres = []; |
|
1109 | - |
|
1110 | - // exploit PHP's loose typing (quack) to check that they are INTs and not str cast to 0 |
|
1111 | - if ($time_created_upper && $time_created_upper == sanitise_int($time_created_upper)) { |
|
1112 | - $wheres[] = "{$table}.time_created <= $time_created_upper"; |
|
1113 | - } |
|
1114 | - |
|
1115 | - if ($time_created_lower && $time_created_lower == sanitise_int($time_created_lower)) { |
|
1116 | - $wheres[] = "{$table}.time_created >= $time_created_lower"; |
|
1117 | - } |
|
1118 | - |
|
1119 | - if ($time_updated_upper && $time_updated_upper == sanitise_int($time_updated_upper)) { |
|
1120 | - $wheres[] = "{$table}.time_updated <= $time_updated_upper"; |
|
1121 | - } |
|
1122 | - |
|
1123 | - if ($time_updated_lower && $time_updated_lower == sanitise_int($time_updated_lower)) { |
|
1124 | - $wheres[] = "{$table}.time_updated >= $time_updated_lower"; |
|
1125 | - } |
|
1126 | - |
|
1127 | - if (is_array($wheres) && count($wheres) > 0) { |
|
1128 | - $where_str = implode(' AND ', $wheres); |
|
1129 | - return "($where_str)"; |
|
1130 | - } |
|
1131 | - |
|
1132 | - return ''; |
|
1133 | - } |
|
1134 | - |
|
1135 | - /** |
|
1136 | - * Gets entities based upon attributes in secondary tables. |
|
1137 | - * Also accepts all options available to elgg_get_entities(), |
|
1138 | - * elgg_get_entities_from_metadata(), and elgg_get_entities_from_relationship(). |
|
1139 | - * |
|
1140 | - * @warning requires that the entity type be specified and there can only be one |
|
1141 | - * type. |
|
1142 | - * |
|
1143 | - * @see elgg_get_entities |
|
1144 | - * @see elgg_get_entities_from_metadata |
|
1145 | - * @see elgg_get_entities_from_relationship |
|
1146 | - * |
|
1147 | - * @param array $options Array in format: |
|
1148 | - * |
|
1149 | - * attribute_name_value_pairs => ARR ( |
|
1150 | - * 'name' => 'name', |
|
1151 | - * 'value' => 'value', |
|
1152 | - * 'operand' => '=', (optional) |
|
1153 | - * 'case_sensitive' => false (optional) |
|
1154 | - * ) |
|
1155 | - * If multiple values are sent via |
|
1156 | - * an array ('value' => array('value1', 'value2') |
|
1157 | - * the pair's operand will be forced to "IN". |
|
1158 | - * |
|
1159 | - * attribute_name_value_pairs_operator => null|STR The operator to use for combining |
|
1160 | - * (name = value) OPERATOR (name = value); default is AND |
|
1161 | - * |
|
1162 | - * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. |
|
1163 | - * @throws InvalidArgumentException |
|
1164 | - * @todo Does not support ordering by attributes or using an attribute pair shortcut like this ('title' => 'foo') |
|
1165 | - */ |
|
1166 | - public function getEntitiesFromAttributes(array $options = []) { |
|
1167 | - $defaults = [ |
|
1168 | - 'attribute_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
1169 | - 'attribute_name_value_pairs_operator' => 'AND', |
|
1170 | - ]; |
|
1171 | - |
|
1172 | - $options = array_merge($defaults, $options); |
|
1173 | - |
|
1174 | - $singulars = ['type', 'attribute_name_value_pair']; |
|
1175 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
1176 | - |
|
1177 | - $clauses = _elgg_get_entity_attribute_where_sql($options); |
|
1178 | - |
|
1179 | - if ($clauses) { |
|
1180 | - // merge wheres to pass to elgg_get_entities() |
|
1181 | - if (isset($options['wheres']) && !is_array($options['wheres'])) { |
|
1182 | - $options['wheres'] = [$options['wheres']]; |
|
1183 | - } elseif (!isset($options['wheres'])) { |
|
1184 | - $options['wheres'] = []; |
|
1185 | - } |
|
1186 | - |
|
1187 | - $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); |
|
1188 | - |
|
1189 | - // merge joins to pass to elgg_get_entities() |
|
1190 | - if (isset($options['joins']) && !is_array($options['joins'])) { |
|
1191 | - $options['joins'] = [$options['joins']]; |
|
1192 | - } elseif (!isset($options['joins'])) { |
|
1193 | - $options['joins'] = []; |
|
1194 | - } |
|
1195 | - |
|
1196 | - $options['joins'] = array_merge($options['joins'], $clauses['joins']); |
|
1197 | - } |
|
1198 | - |
|
1199 | - return elgg_get_entities_from_relationship($options); |
|
1200 | - } |
|
1201 | - |
|
1202 | - /** |
|
1203 | - * Get the join and where clauses for working with entity attributes |
|
1204 | - * |
|
1205 | - * @return false|array False on fail, array('joins', 'wheres') |
|
1206 | - * @access private |
|
1207 | - * @throws InvalidArgumentException |
|
1208 | - */ |
|
1209 | - public function getEntityAttributeWhereSql(array $options = []) { |
|
1210 | - |
|
1211 | - if (!isset($options['types'])) { |
|
1212 | - throw new InvalidArgumentException("The entity type must be defined for elgg_get_entities_from_attributes()"); |
|
1213 | - } |
|
1214 | - |
|
1215 | - if (is_array($options['types']) && count($options['types']) !== 1) { |
|
1216 | - throw new InvalidArgumentException("Only one type can be passed to elgg_get_entities_from_attributes()"); |
|
1217 | - } |
|
1218 | - |
|
1219 | - // type can be passed as string or array |
|
1220 | - $type = $options['types']; |
|
1221 | - if (is_array($type)) { |
|
1222 | - $type = $type[0]; |
|
1223 | - } |
|
1224 | - |
|
1225 | - // @todo the types should be defined somewhere (as constant on \ElggEntity?) |
|
1226 | - if (!in_array($type, ['group', 'object', 'site', 'user'])) { |
|
1227 | - throw new InvalidArgumentException("Invalid type '$type' passed to elgg_get_entities_from_attributes()"); |
|
1228 | - } |
|
1229 | - |
|
1230 | - |
|
1231 | - $type_table = "{$this->db->prefix}{$type}s_entity"; |
|
1232 | - |
|
1233 | - $return = [ |
|
1234 | - 'joins' => [], |
|
1235 | - 'wheres' => [], |
|
1236 | - ]; |
|
1237 | - |
|
1238 | - // short circuit if nothing requested |
|
1239 | - if ($options['attribute_name_value_pairs'] == ELGG_ENTITIES_ANY_VALUE) { |
|
1240 | - return $return; |
|
1241 | - } |
|
1242 | - |
|
1243 | - if (!is_array($options['attribute_name_value_pairs'])) { |
|
1244 | - throw new InvalidArgumentException("attribute_name_value_pairs must be an array for elgg_get_entities_from_attributes()"); |
|
1245 | - } |
|
1246 | - |
|
1247 | - $wheres = []; |
|
1248 | - |
|
1249 | - // check if this is an array of pairs or just a single pair. |
|
1250 | - $pairs = $options['attribute_name_value_pairs']; |
|
1251 | - if (isset($pairs['name']) || isset($pairs['value'])) { |
|
1252 | - $pairs = [$pairs]; |
|
1253 | - } |
|
1254 | - |
|
1255 | - $pair_wheres = []; |
|
1256 | - foreach ($pairs as $index => $pair) { |
|
1257 | - // must have at least a name and value |
|
1258 | - if (!isset($pair['name']) || !isset($pair['value'])) { |
|
1259 | - continue; |
|
1260 | - } |
|
1261 | - |
|
1262 | - if (isset($pair['operand'])) { |
|
1263 | - $operand = sanitize_string($pair['operand']); |
|
1264 | - } else { |
|
1265 | - $operand = '='; |
|
1266 | - } |
|
1267 | - |
|
1268 | - if (is_numeric($pair['value'])) { |
|
1269 | - $value = sanitize_string($pair['value']); |
|
1270 | - } else if (is_array($pair['value'])) { |
|
1271 | - $values_array = []; |
|
1272 | - foreach ($pair['value'] as $pair_value) { |
|
1273 | - if (is_numeric($pair_value)) { |
|
1274 | - $values_array[] = sanitize_string($pair_value); |
|
1275 | - } else { |
|
1276 | - $values_array[] = "'" . sanitize_string($pair_value) . "'"; |
|
1277 | - } |
|
1278 | - } |
|
1279 | - |
|
1280 | - $operand = 'IN'; |
|
1281 | - if ($values_array) { |
|
1282 | - $value = '(' . implode(', ', $values_array) . ')'; |
|
1283 | - } |
|
1284 | - } else { |
|
1285 | - $value = "'" . sanitize_string($pair['value']) . "'"; |
|
1286 | - } |
|
1287 | - |
|
1288 | - $name = sanitize_string($pair['name']); |
|
1289 | - |
|
1290 | - // case sensitivity can be specified per pair |
|
1291 | - $pair_binary = ''; |
|
1292 | - if (isset($pair['case_sensitive'])) { |
|
1293 | - $pair_binary = ($pair['case_sensitive']) ? 'BINARY ' : ''; |
|
1294 | - } |
|
1295 | - |
|
1296 | - $pair_wheres[] = "({$pair_binary}type_table.$name $operand $value)"; |
|
1297 | - } |
|
1298 | - |
|
1299 | - if ($where = implode(" {$options['attribute_name_value_pairs_operator']} ", $pair_wheres)) { |
|
1300 | - $return['wheres'][] = "($where)"; |
|
1301 | - |
|
1302 | - $return['joins'][] = "JOIN $type_table type_table ON e.guid = type_table.guid"; |
|
1303 | - } |
|
1304 | - |
|
1305 | - return $return; |
|
1306 | - } |
|
1307 | - |
|
1308 | - /** |
|
1309 | - * Returns a list of months in which entities were updated or created. |
|
1310 | - * |
|
1311 | - * @tip Use this to generate a list of archives by month for when entities were added or updated. |
|
1312 | - * |
|
1313 | - * @todo document how to pass in array for $subtype |
|
1314 | - * |
|
1315 | - * @warning Months are returned in the form YYYYMM. |
|
1316 | - * |
|
1317 | - * @param string $type The type of entity |
|
1318 | - * @param string $subtype The subtype of entity |
|
1319 | - * @param int $container_guid The container GUID that the entities belong to |
|
1320 | - * @param string $order_by Order_by SQL order by clause |
|
1321 | - * |
|
1322 | - * @return array|false Either an array months as YYYYMM, or false on failure |
|
1323 | - */ |
|
1324 | - public function getDates($type = '', $subtype = '', $container_guid = 0, $order_by = 'time_created') { |
|
1325 | - |
|
1326 | - $where = []; |
|
1327 | - |
|
1328 | - if ($type != "") { |
|
1329 | - $type = sanitise_string($type); |
|
1330 | - $where[] = "type='$type'"; |
|
1331 | - } |
|
1332 | - |
|
1333 | - if (is_array($subtype)) { |
|
1334 | - $tempwhere = ""; |
|
1335 | - if (sizeof($subtype)) { |
|
1336 | - foreach ($subtype as $typekey => $subtypearray) { |
|
1337 | - foreach ($subtypearray as $subtypeval) { |
|
1338 | - $typekey = sanitise_string($typekey); |
|
1339 | - if (!empty($subtypeval)) { |
|
1340 | - if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) { |
|
1341 | - return false; |
|
1342 | - } |
|
1343 | - } else { |
|
1344 | - $subtypeval = 0; |
|
1345 | - } |
|
1346 | - if (!empty($tempwhere)) { |
|
1347 | - $tempwhere .= " or "; |
|
1348 | - } |
|
1349 | - $tempwhere .= "(type = '{$typekey}' and subtype = {$subtypeval})"; |
|
1350 | - } |
|
1351 | - } |
|
1352 | - } |
|
1353 | - if (!empty($tempwhere)) { |
|
1354 | - $where[] = "({$tempwhere})"; |
|
1355 | - } |
|
1356 | - } else { |
|
1357 | - if ($subtype) { |
|
1358 | - if (!$subtype_id = get_subtype_id($type, $subtype)) { |
|
1359 | - return false; |
|
1360 | - } else { |
|
1361 | - $where[] = "subtype=$subtype_id"; |
|
1362 | - } |
|
1363 | - } |
|
1364 | - } |
|
1365 | - |
|
1366 | - if ($container_guid !== 0) { |
|
1367 | - if (is_array($container_guid)) { |
|
1368 | - foreach ($container_guid as $key => $val) { |
|
1369 | - $container_guid[$key] = (int) $val; |
|
1370 | - } |
|
1371 | - $where[] = "container_guid in (" . implode(",", $container_guid) . ")"; |
|
1372 | - } else { |
|
1373 | - $container_guid = (int) $container_guid; |
|
1374 | - $where[] = "container_guid = {$container_guid}"; |
|
1375 | - } |
|
1376 | - } |
|
1377 | - |
|
1378 | - $where[] = _elgg_get_access_where_sql(['table_alias' => '']); |
|
1379 | - |
|
1380 | - $sql = "SELECT DISTINCT EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time_created)) AS yearmonth |
|
555 | + // can't use helper function with type_subtype_pair because |
|
556 | + // it's already an array...just need to merge it |
|
557 | + if (isset($options['type_subtype_pair'])) { |
|
558 | + if (isset($options['type_subtype_pairs'])) { |
|
559 | + $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], |
|
560 | + $options['type_subtype_pair']); |
|
561 | + } else { |
|
562 | + $options['type_subtype_pairs'] = $options['type_subtype_pair']; |
|
563 | + } |
|
564 | + } |
|
565 | + |
|
566 | + $singulars = ['type', 'subtype', 'guid', 'owner_guid', 'container_guid']; |
|
567 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
568 | + |
|
569 | + $options = $this->autoJoinTables($options); |
|
570 | + |
|
571 | + // evaluate where clauses |
|
572 | + if (!is_array($options['wheres'])) { |
|
573 | + $options['wheres'] = [$options['wheres']]; |
|
574 | + } |
|
575 | + |
|
576 | + $wheres = $options['wheres']; |
|
577 | + |
|
578 | + $wheres[] = $this->getEntityTypeSubtypeWhereSql('e', $options['types'], |
|
579 | + $options['subtypes'], $options['type_subtype_pairs']); |
|
580 | + |
|
581 | + $wheres[] = $this->getGuidBasedWhereSql('e.guid', $options['guids']); |
|
582 | + $wheres[] = $this->getGuidBasedWhereSql('e.owner_guid', $options['owner_guids']); |
|
583 | + $wheres[] = $this->getGuidBasedWhereSql('e.container_guid', $options['container_guids']); |
|
584 | + |
|
585 | + $wheres[] = $this->getEntityTimeWhereSql('e', $options['created_time_upper'], |
|
586 | + $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']); |
|
587 | + |
|
588 | + // see if any functions failed |
|
589 | + // remove empty strings on successful functions |
|
590 | + foreach ($wheres as $i => $where) { |
|
591 | + if ($where === false) { |
|
592 | + return false; |
|
593 | + } elseif (empty($where)) { |
|
594 | + unset($wheres[$i]); |
|
595 | + } |
|
596 | + } |
|
597 | + |
|
598 | + // remove identical where clauses |
|
599 | + $wheres = array_unique($wheres); |
|
600 | + |
|
601 | + // evaluate join clauses |
|
602 | + if (!is_array($options['joins'])) { |
|
603 | + $options['joins'] = [$options['joins']]; |
|
604 | + } |
|
605 | + |
|
606 | + // remove identical join clauses |
|
607 | + $joins = array_unique($options['joins']); |
|
608 | + |
|
609 | + foreach ($joins as $i => $join) { |
|
610 | + if ($join === false) { |
|
611 | + return false; |
|
612 | + } elseif (empty($join)) { |
|
613 | + unset($joins[$i]); |
|
614 | + } |
|
615 | + } |
|
616 | + |
|
617 | + // evalutate selects |
|
618 | + if ($options['selects']) { |
|
619 | + $selects = ''; |
|
620 | + foreach ($options['selects'] as $select) { |
|
621 | + $selects .= ", $select"; |
|
622 | + } |
|
623 | + } else { |
|
624 | + $selects = ''; |
|
625 | + } |
|
626 | + |
|
627 | + if (!$options['count']) { |
|
628 | + $distinct = $options['distinct'] ? "DISTINCT" : ""; |
|
629 | + $query = "SELECT $distinct e.*{$selects} FROM {$this->db->prefix}entities e "; |
|
630 | + } else { |
|
631 | + // note: when DISTINCT unneeded, it's slightly faster to compute COUNT(*) than GUIDs |
|
632 | + $count_expr = $options['distinct'] ? "DISTINCT e.guid" : "*"; |
|
633 | + $query = "SELECT COUNT($count_expr) as total FROM {$this->db->prefix}entities e "; |
|
634 | + } |
|
635 | + |
|
636 | + // add joins |
|
637 | + foreach ($joins as $j) { |
|
638 | + $query .= " $j "; |
|
639 | + } |
|
640 | + |
|
641 | + // add wheres |
|
642 | + $query .= ' WHERE '; |
|
643 | + |
|
644 | + foreach ($wheres as $w) { |
|
645 | + $query .= " $w AND "; |
|
646 | + } |
|
647 | + |
|
648 | + // Add access controls |
|
649 | + $query .= _elgg_get_access_where_sql(); |
|
650 | + |
|
651 | + // reverse order by |
|
652 | + if ($options['reverse_order_by']) { |
|
653 | + $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']); |
|
654 | + } |
|
655 | + |
|
656 | + if ($options['count']) { |
|
657 | + $total = $this->db->getDataRow($query); |
|
658 | + return (int) $total->total; |
|
659 | + } |
|
660 | + |
|
661 | + if ($options['group_by']) { |
|
662 | + $query .= " GROUP BY {$options['group_by']}"; |
|
663 | + } |
|
664 | + |
|
665 | + if ($options['order_by']) { |
|
666 | + $query .= " ORDER BY {$options['order_by']}"; |
|
667 | + } |
|
668 | + |
|
669 | + if ($options['limit']) { |
|
670 | + $limit = sanitise_int($options['limit'], false); |
|
671 | + $offset = sanitise_int($options['offset'], false); |
|
672 | + $query .= " LIMIT $offset, $limit"; |
|
673 | + } |
|
674 | + |
|
675 | + if ($options['callback'] === 'entity_row_to_elggstar') { |
|
676 | + $results = $this->fetchFromSql($query, $options['__ElggBatch']); |
|
677 | + } else { |
|
678 | + $results = $this->db->getData($query, $options['callback']); |
|
679 | + } |
|
680 | + |
|
681 | + if (!$results) { |
|
682 | + // no results, no preloading |
|
683 | + return $results; |
|
684 | + } |
|
685 | + |
|
686 | + // populate entity and metadata caches, and prepare $entities for preloader |
|
687 | + $guids = []; |
|
688 | + foreach ($results as $item) { |
|
689 | + // A custom callback could result in items that aren't \ElggEntity's, so check for them |
|
690 | + if ($item instanceof ElggEntity) { |
|
691 | + $this->entity_cache->set($item); |
|
692 | + // plugins usually have only settings |
|
693 | + if (!$item instanceof ElggPlugin) { |
|
694 | + $guids[] = $item->guid; |
|
695 | + } |
|
696 | + } |
|
697 | + } |
|
698 | + // @todo Without this, recursive delete fails. See #4568 |
|
699 | + reset($results); |
|
700 | + |
|
701 | + if ($guids) { |
|
702 | + // there were entities in the result set, preload metadata for them |
|
703 | + $this->metadata_cache->populateFromEntities($guids); |
|
704 | + } |
|
705 | + |
|
706 | + if (count($results) > 1) { |
|
707 | + $props_to_preload = []; |
|
708 | + if ($options['preload_owners']) { |
|
709 | + $props_to_preload[] = 'owner_guid'; |
|
710 | + } |
|
711 | + if ($options['preload_containers']) { |
|
712 | + $props_to_preload[] = 'container_guid'; |
|
713 | + } |
|
714 | + if ($props_to_preload) { |
|
715 | + // note, ElggEntityPreloaderIntegrationTest assumes it can swap out |
|
716 | + // the preloader after boot. If you inject this component at construction |
|
717 | + // time that unit test will break. :/ |
|
718 | + _elgg_services()->entityPreloader->preload($results, $props_to_preload); |
|
719 | + } |
|
720 | + } |
|
721 | + |
|
722 | + return $results; |
|
723 | + } |
|
724 | + |
|
725 | + /** |
|
726 | + * Decorate getEntities() options in order to auto-join secondary tables where it's |
|
727 | + * safe to do so. |
|
728 | + * |
|
729 | + * @param array $options Options array in getEntities() after normalization |
|
730 | + * @return array |
|
731 | + */ |
|
732 | + protected function autoJoinTables(array $options) { |
|
733 | + // we must be careful that the query doesn't specify any options that may join |
|
734 | + // tables or change the selected columns |
|
735 | + if (!is_array($options['types']) |
|
736 | + || count($options['types']) !== 1 |
|
737 | + || !empty($options['selects']) |
|
738 | + || !empty($options['wheres']) |
|
739 | + || !empty($options['joins']) |
|
740 | + || $options['callback'] !== 'entity_row_to_elggstar' |
|
741 | + || $options['count']) { |
|
742 | + // Too dangerous to auto-join |
|
743 | + return $options; |
|
744 | + } |
|
745 | + |
|
746 | + $join_types = [ |
|
747 | + // Each class must have a static getExternalAttributes() : array |
|
748 | + 'object' => 'ElggObject', |
|
749 | + 'user' => 'ElggUser', |
|
750 | + 'group' => 'ElggGroup', |
|
751 | + 'site' => 'ElggSite', |
|
752 | + ]; |
|
753 | + |
|
754 | + // We use reset() because $options['types'] may not have a numeric key |
|
755 | + $type = reset($options['types']); |
|
756 | + if (empty($join_types[$type])) { |
|
757 | + return $options; |
|
758 | + } |
|
759 | + |
|
760 | + // Get the columns we'll need to select. We can't use st.* because the order_by |
|
761 | + // clause may reference "guid", which MySQL will complain about being ambiguous |
|
762 | + if (!is_callable([$join_types[$type], 'getExternalAttributes'])) { |
|
763 | + // for some reason can't get external attributes. |
|
764 | + return $options; |
|
765 | + } |
|
766 | + |
|
767 | + $attributes = $join_types[$type]::getExternalAttributes(); |
|
768 | + foreach (array_keys($attributes) as $col) { |
|
769 | + $options['selects'][] = "st.$col"; |
|
770 | + } |
|
771 | + |
|
772 | + // join the secondary table |
|
773 | + $options['joins'][] = "JOIN {$this->db->prefix}{$type}s_entity st ON (e.guid = st.guid)"; |
|
774 | + |
|
775 | + return $options; |
|
776 | + } |
|
777 | + |
|
778 | + /** |
|
779 | + * Return entities from an SQL query generated by elgg_get_entities. |
|
780 | + * |
|
781 | + * @access private |
|
782 | + * |
|
783 | + * @param string $sql |
|
784 | + * @param ElggBatch $batch |
|
785 | + * @return ElggEntity[] |
|
786 | + * @throws LogicException |
|
787 | + */ |
|
788 | + public function fetchFromSql($sql, \ElggBatch $batch = null) { |
|
789 | + $plugin_subtype = $this->subtype_table->getId('object', 'plugin'); |
|
790 | + |
|
791 | + // Keys are types, values are columns that, if present, suggest that the secondary |
|
792 | + // table is already JOINed. Note it's OK if guess incorrectly because entity load() |
|
793 | + // will fetch any missing attributes. |
|
794 | + $types_to_optimize = [ |
|
795 | + 'object' => 'title', |
|
796 | + 'user' => 'password_hash', |
|
797 | + 'group' => 'name', |
|
798 | + 'site' => 'url', |
|
799 | + ]; |
|
800 | + |
|
801 | + $rows = $this->db->getData($sql); |
|
802 | + |
|
803 | + // guids to look up in each type |
|
804 | + $lookup_types = []; |
|
805 | + // maps GUIDs to the $rows key |
|
806 | + $guid_to_key = []; |
|
807 | + |
|
808 | + if (isset($rows[0]->type, $rows[0]->subtype) |
|
809 | + && $rows[0]->type === 'object' |
|
810 | + && $rows[0]->subtype == $plugin_subtype) { |
|
811 | + // Likely the entire resultset is plugins, which have already been optimized |
|
812 | + // to JOIN the secondary table. In this case we allow retrieving from cache, |
|
813 | + // but abandon the extra queries. |
|
814 | + $types_to_optimize = []; |
|
815 | + } |
|
816 | + |
|
817 | + // First pass: use cache where possible, gather GUIDs that we're optimizing |
|
818 | + foreach ($rows as $i => $row) { |
|
819 | + if (empty($row->guid) || empty($row->type)) { |
|
820 | + throw new LogicException('Entity row missing guid or type'); |
|
821 | + } |
|
822 | + |
|
823 | + // We try ephemeral cache because it's blazingly fast and we ideally want to access |
|
824 | + // the same PHP instance. We don't try memcache because it isn't worth the overhead. |
|
825 | + $entity = $this->entity_cache->get($row->guid); |
|
826 | + if ($entity) { |
|
827 | + // from static var, must be refreshed in case row has extra columns |
|
828 | + $entity->refresh($row); |
|
829 | + $rows[$i] = $entity; |
|
830 | + continue; |
|
831 | + } |
|
832 | + |
|
833 | + if (isset($types_to_optimize[$row->type])) { |
|
834 | + // check if row already looks JOINed. |
|
835 | + if (isset($row->{$types_to_optimize[$row->type]})) { |
|
836 | + // Row probably already contains JOINed secondary table. Don't make another query just |
|
837 | + // to pull data that's already there |
|
838 | + continue; |
|
839 | + } |
|
840 | + $lookup_types[$row->type][] = $row->guid; |
|
841 | + $guid_to_key[$row->guid] = $i; |
|
842 | + } |
|
843 | + } |
|
844 | + // Do secondary queries and merge rows |
|
845 | + if ($lookup_types) { |
|
846 | + foreach ($lookup_types as $type => $guids) { |
|
847 | + $set = "(" . implode(',', $guids) . ")"; |
|
848 | + $sql = "SELECT * FROM {$this->db->prefix}{$type}s_entity WHERE guid IN $set"; |
|
849 | + $secondary_rows = $this->db->getData($sql); |
|
850 | + if ($secondary_rows) { |
|
851 | + foreach ($secondary_rows as $secondary_row) { |
|
852 | + $key = $guid_to_key[$secondary_row->guid]; |
|
853 | + // cast to arrays to merge then cast back |
|
854 | + $rows[$key] = (object) array_merge((array) $rows[$key], (array) $secondary_row); |
|
855 | + } |
|
856 | + } |
|
857 | + } |
|
858 | + } |
|
859 | + // Second pass to finish conversion |
|
860 | + foreach ($rows as $i => $row) { |
|
861 | + if ($row instanceof ElggEntity) { |
|
862 | + continue; |
|
863 | + } else { |
|
864 | + try { |
|
865 | + $rows[$i] = $this->rowToElggStar($row); |
|
866 | + } catch (IncompleteEntityException $e) { |
|
867 | + // don't let incomplete entities throw fatal errors |
|
868 | + unset($rows[$i]); |
|
869 | + |
|
870 | + // report incompletes to the batch process that spawned this query |
|
871 | + if ($batch) { |
|
872 | + $batch->reportIncompleteEntity($row); |
|
873 | + } |
|
874 | + } |
|
875 | + } |
|
876 | + } |
|
877 | + return $rows; |
|
878 | + } |
|
879 | + |
|
880 | + /** |
|
881 | + * Returns SQL where clause for type and subtype on main entity table |
|
882 | + * |
|
883 | + * @param string $table Entity table prefix as defined in SELECT...FROM entities $table |
|
884 | + * @param null|array $types Array of types or null if none. |
|
885 | + * @param null|array $subtypes Array of subtypes or null if none |
|
886 | + * @param null|array $pairs Array of pairs of types and subtypes |
|
887 | + * |
|
888 | + * @return false|string |
|
889 | + * @access private |
|
890 | + */ |
|
891 | + public function getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs) { |
|
892 | + // subtype depends upon type. |
|
893 | + if ($subtypes && !$types) { |
|
894 | + $this->logger->warn("Cannot set subtypes without type."); |
|
895 | + return false; |
|
896 | + } |
|
897 | + |
|
898 | + // short circuit if nothing is requested |
|
899 | + if (!$types && !$subtypes && !$pairs) { |
|
900 | + return ''; |
|
901 | + } |
|
902 | + |
|
903 | + // these are the only valid types for entities in elgg |
|
904 | + $valid_types = $this->config->get('entity_types'); |
|
905 | + |
|
906 | + // pairs override |
|
907 | + $wheres = []; |
|
908 | + if (!is_array($pairs)) { |
|
909 | + if (!is_array($types)) { |
|
910 | + $types = [$types]; |
|
911 | + } |
|
912 | + |
|
913 | + if ($subtypes && !is_array($subtypes)) { |
|
914 | + $subtypes = [$subtypes]; |
|
915 | + } |
|
916 | + |
|
917 | + // decrementer for valid types. Return false if no valid types |
|
918 | + $valid_types_count = count($types); |
|
919 | + $valid_subtypes_count = 0; |
|
920 | + // remove invalid types to get an accurate count of |
|
921 | + // valid types for the invalid subtype detection to use |
|
922 | + // below. |
|
923 | + // also grab the count of ALL subtypes on valid types to decrement later on |
|
924 | + // and check against. |
|
925 | + // |
|
926 | + // yes this is duplicating a foreach on $types. |
|
927 | + foreach ($types as $type) { |
|
928 | + if (!in_array($type, $valid_types)) { |
|
929 | + $valid_types_count--; |
|
930 | + unset($types[array_search($type, $types)]); |
|
931 | + } else { |
|
932 | + // do the checking (and decrementing) in the subtype section. |
|
933 | + $valid_subtypes_count += count($subtypes); |
|
934 | + } |
|
935 | + } |
|
936 | + |
|
937 | + // return false if nothing is valid. |
|
938 | + if (!$valid_types_count) { |
|
939 | + return false; |
|
940 | + } |
|
941 | + |
|
942 | + // subtypes are based upon types, so we need to look at each |
|
943 | + // type individually to get the right subtype id. |
|
944 | + foreach ($types as $type) { |
|
945 | + $subtype_ids = []; |
|
946 | + if ($subtypes) { |
|
947 | + foreach ($subtypes as $subtype) { |
|
948 | + // check that the subtype is valid |
|
949 | + if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) { |
|
950 | + // subtype value is 0 |
|
951 | + $subtype_ids[] = ELGG_ENTITIES_NO_VALUE; |
|
952 | + } elseif (!$subtype) { |
|
953 | + // subtype is ignored. |
|
954 | + // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0 |
|
955 | + continue; |
|
956 | + } else { |
|
957 | + $subtype_id = get_subtype_id($type, $subtype); |
|
958 | + |
|
959 | + if ($subtype_id) { |
|
960 | + $subtype_ids[] = $subtype_id; |
|
961 | + } else { |
|
962 | + $valid_subtypes_count--; |
|
963 | + $this->logger->notice("Type-subtype '$type:$subtype' does not exist!"); |
|
964 | + continue; |
|
965 | + } |
|
966 | + } |
|
967 | + } |
|
968 | + |
|
969 | + // return false if we're all invalid subtypes in the only valid type |
|
970 | + if ($valid_subtypes_count <= 0) { |
|
971 | + return false; |
|
972 | + } |
|
973 | + } |
|
974 | + |
|
975 | + if (is_array($subtype_ids) && count($subtype_ids)) { |
|
976 | + $subtype_ids_str = implode(',', $subtype_ids); |
|
977 | + $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))"; |
|
978 | + } else { |
|
979 | + $wheres[] = "({$table}.type = '$type')"; |
|
980 | + } |
|
981 | + } |
|
982 | + } else { |
|
983 | + // using type/subtype pairs |
|
984 | + $valid_pairs_count = count($pairs); |
|
985 | + $valid_pairs_subtypes_count = 0; |
|
986 | + |
|
987 | + // same deal as above--we need to know how many valid types |
|
988 | + // and subtypes we have before hitting the subtype section. |
|
989 | + // also normalize the subtypes into arrays here. |
|
990 | + foreach ($pairs as $paired_type => $paired_subtypes) { |
|
991 | + if (!in_array($paired_type, $valid_types)) { |
|
992 | + $valid_pairs_count--; |
|
993 | + unset($pairs[array_search($paired_type, $pairs)]); |
|
994 | + } else { |
|
995 | + if ($paired_subtypes && !is_array($paired_subtypes)) { |
|
996 | + $pairs[$paired_type] = [$paired_subtypes]; |
|
997 | + } |
|
998 | + $valid_pairs_subtypes_count += count($paired_subtypes); |
|
999 | + } |
|
1000 | + } |
|
1001 | + |
|
1002 | + if ($valid_pairs_count <= 0) { |
|
1003 | + return false; |
|
1004 | + } |
|
1005 | + foreach ($pairs as $paired_type => $paired_subtypes) { |
|
1006 | + // this will always be an array because of line 2027, right? |
|
1007 | + // no...some overly clever person can say pair => array('object' => null) |
|
1008 | + if (is_array($paired_subtypes)) { |
|
1009 | + $paired_subtype_ids = []; |
|
1010 | + foreach ($paired_subtypes as $paired_subtype) { |
|
1011 | + if (ELGG_ENTITIES_NO_VALUE === $paired_subtype || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) { |
|
1012 | + $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ? |
|
1013 | + ELGG_ENTITIES_NO_VALUE : $paired_subtype_id; |
|
1014 | + } else { |
|
1015 | + $valid_pairs_subtypes_count--; |
|
1016 | + $this->logger->notice("Type-subtype '$paired_type:$paired_subtype' does not exist!"); |
|
1017 | + // return false if we're all invalid subtypes in the only valid type |
|
1018 | + continue; |
|
1019 | + } |
|
1020 | + } |
|
1021 | + |
|
1022 | + // return false if there are no valid subtypes. |
|
1023 | + if ($valid_pairs_subtypes_count <= 0) { |
|
1024 | + return false; |
|
1025 | + } |
|
1026 | + |
|
1027 | + |
|
1028 | + if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) { |
|
1029 | + $wheres[] = "({$table}.type = '$paired_type'" |
|
1030 | + . " AND {$table}.subtype IN ($paired_subtype_ids_str))"; |
|
1031 | + } |
|
1032 | + } else { |
|
1033 | + $wheres[] = "({$table}.type = '$paired_type')"; |
|
1034 | + } |
|
1035 | + } |
|
1036 | + } |
|
1037 | + |
|
1038 | + // pairs override the above. return false if they don't exist. |
|
1039 | + if (is_array($wheres) && count($wheres)) { |
|
1040 | + $where = implode(' OR ', $wheres); |
|
1041 | + return "($where)"; |
|
1042 | + } |
|
1043 | + |
|
1044 | + return ''; |
|
1045 | + } |
|
1046 | + |
|
1047 | + /** |
|
1048 | + * Returns SQL where clause for owner and containers. |
|
1049 | + * |
|
1050 | + * @param string $column Column name the guids should be checked against. Usually |
|
1051 | + * best to provide in table.column format. |
|
1052 | + * @param null|array $guids Array of GUIDs. |
|
1053 | + * |
|
1054 | + * @return false|string |
|
1055 | + * @access private |
|
1056 | + */ |
|
1057 | + public function getGuidBasedWhereSql($column, $guids) { |
|
1058 | + // short circuit if nothing requested |
|
1059 | + // 0 is a valid guid |
|
1060 | + if (!$guids && $guids !== 0) { |
|
1061 | + return ''; |
|
1062 | + } |
|
1063 | + |
|
1064 | + // normalize and sanitise owners |
|
1065 | + if (!is_array($guids)) { |
|
1066 | + $guids = [$guids]; |
|
1067 | + } |
|
1068 | + |
|
1069 | + $guids_sanitized = []; |
|
1070 | + foreach ($guids as $guid) { |
|
1071 | + if ($guid !== ELGG_ENTITIES_NO_VALUE) { |
|
1072 | + $guid = sanitise_int($guid); |
|
1073 | + |
|
1074 | + if (!$guid) { |
|
1075 | + return false; |
|
1076 | + } |
|
1077 | + } |
|
1078 | + $guids_sanitized[] = $guid; |
|
1079 | + } |
|
1080 | + |
|
1081 | + $where = ''; |
|
1082 | + $guid_str = implode(',', $guids_sanitized); |
|
1083 | + |
|
1084 | + // implode(',', 0) returns 0. |
|
1085 | + if ($guid_str !== false && $guid_str !== '') { |
|
1086 | + $where = "($column IN ($guid_str))"; |
|
1087 | + } |
|
1088 | + |
|
1089 | + return $where; |
|
1090 | + } |
|
1091 | + |
|
1092 | + /** |
|
1093 | + * Returns SQL where clause for entity time limits. |
|
1094 | + * |
|
1095 | + * @param string $table Entity table prefix as defined in |
|
1096 | + * SELECT...FROM entities $table |
|
1097 | + * @param null|int $time_created_upper Time created upper limit |
|
1098 | + * @param null|int $time_created_lower Time created lower limit |
|
1099 | + * @param null|int $time_updated_upper Time updated upper limit |
|
1100 | + * @param null|int $time_updated_lower Time updated lower limit |
|
1101 | + * |
|
1102 | + * @return false|string false on fail, string on success. |
|
1103 | + * @access private |
|
1104 | + */ |
|
1105 | + public function getEntityTimeWhereSql($table, $time_created_upper = null, |
|
1106 | + $time_created_lower = null, $time_updated_upper = null, $time_updated_lower = null) { |
|
1107 | + |
|
1108 | + $wheres = []; |
|
1109 | + |
|
1110 | + // exploit PHP's loose typing (quack) to check that they are INTs and not str cast to 0 |
|
1111 | + if ($time_created_upper && $time_created_upper == sanitise_int($time_created_upper)) { |
|
1112 | + $wheres[] = "{$table}.time_created <= $time_created_upper"; |
|
1113 | + } |
|
1114 | + |
|
1115 | + if ($time_created_lower && $time_created_lower == sanitise_int($time_created_lower)) { |
|
1116 | + $wheres[] = "{$table}.time_created >= $time_created_lower"; |
|
1117 | + } |
|
1118 | + |
|
1119 | + if ($time_updated_upper && $time_updated_upper == sanitise_int($time_updated_upper)) { |
|
1120 | + $wheres[] = "{$table}.time_updated <= $time_updated_upper"; |
|
1121 | + } |
|
1122 | + |
|
1123 | + if ($time_updated_lower && $time_updated_lower == sanitise_int($time_updated_lower)) { |
|
1124 | + $wheres[] = "{$table}.time_updated >= $time_updated_lower"; |
|
1125 | + } |
|
1126 | + |
|
1127 | + if (is_array($wheres) && count($wheres) > 0) { |
|
1128 | + $where_str = implode(' AND ', $wheres); |
|
1129 | + return "($where_str)"; |
|
1130 | + } |
|
1131 | + |
|
1132 | + return ''; |
|
1133 | + } |
|
1134 | + |
|
1135 | + /** |
|
1136 | + * Gets entities based upon attributes in secondary tables. |
|
1137 | + * Also accepts all options available to elgg_get_entities(), |
|
1138 | + * elgg_get_entities_from_metadata(), and elgg_get_entities_from_relationship(). |
|
1139 | + * |
|
1140 | + * @warning requires that the entity type be specified and there can only be one |
|
1141 | + * type. |
|
1142 | + * |
|
1143 | + * @see elgg_get_entities |
|
1144 | + * @see elgg_get_entities_from_metadata |
|
1145 | + * @see elgg_get_entities_from_relationship |
|
1146 | + * |
|
1147 | + * @param array $options Array in format: |
|
1148 | + * |
|
1149 | + * attribute_name_value_pairs => ARR ( |
|
1150 | + * 'name' => 'name', |
|
1151 | + * 'value' => 'value', |
|
1152 | + * 'operand' => '=', (optional) |
|
1153 | + * 'case_sensitive' => false (optional) |
|
1154 | + * ) |
|
1155 | + * If multiple values are sent via |
|
1156 | + * an array ('value' => array('value1', 'value2') |
|
1157 | + * the pair's operand will be forced to "IN". |
|
1158 | + * |
|
1159 | + * attribute_name_value_pairs_operator => null|STR The operator to use for combining |
|
1160 | + * (name = value) OPERATOR (name = value); default is AND |
|
1161 | + * |
|
1162 | + * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. |
|
1163 | + * @throws InvalidArgumentException |
|
1164 | + * @todo Does not support ordering by attributes or using an attribute pair shortcut like this ('title' => 'foo') |
|
1165 | + */ |
|
1166 | + public function getEntitiesFromAttributes(array $options = []) { |
|
1167 | + $defaults = [ |
|
1168 | + 'attribute_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
1169 | + 'attribute_name_value_pairs_operator' => 'AND', |
|
1170 | + ]; |
|
1171 | + |
|
1172 | + $options = array_merge($defaults, $options); |
|
1173 | + |
|
1174 | + $singulars = ['type', 'attribute_name_value_pair']; |
|
1175 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
1176 | + |
|
1177 | + $clauses = _elgg_get_entity_attribute_where_sql($options); |
|
1178 | + |
|
1179 | + if ($clauses) { |
|
1180 | + // merge wheres to pass to elgg_get_entities() |
|
1181 | + if (isset($options['wheres']) && !is_array($options['wheres'])) { |
|
1182 | + $options['wheres'] = [$options['wheres']]; |
|
1183 | + } elseif (!isset($options['wheres'])) { |
|
1184 | + $options['wheres'] = []; |
|
1185 | + } |
|
1186 | + |
|
1187 | + $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); |
|
1188 | + |
|
1189 | + // merge joins to pass to elgg_get_entities() |
|
1190 | + if (isset($options['joins']) && !is_array($options['joins'])) { |
|
1191 | + $options['joins'] = [$options['joins']]; |
|
1192 | + } elseif (!isset($options['joins'])) { |
|
1193 | + $options['joins'] = []; |
|
1194 | + } |
|
1195 | + |
|
1196 | + $options['joins'] = array_merge($options['joins'], $clauses['joins']); |
|
1197 | + } |
|
1198 | + |
|
1199 | + return elgg_get_entities_from_relationship($options); |
|
1200 | + } |
|
1201 | + |
|
1202 | + /** |
|
1203 | + * Get the join and where clauses for working with entity attributes |
|
1204 | + * |
|
1205 | + * @return false|array False on fail, array('joins', 'wheres') |
|
1206 | + * @access private |
|
1207 | + * @throws InvalidArgumentException |
|
1208 | + */ |
|
1209 | + public function getEntityAttributeWhereSql(array $options = []) { |
|
1210 | + |
|
1211 | + if (!isset($options['types'])) { |
|
1212 | + throw new InvalidArgumentException("The entity type must be defined for elgg_get_entities_from_attributes()"); |
|
1213 | + } |
|
1214 | + |
|
1215 | + if (is_array($options['types']) && count($options['types']) !== 1) { |
|
1216 | + throw new InvalidArgumentException("Only one type can be passed to elgg_get_entities_from_attributes()"); |
|
1217 | + } |
|
1218 | + |
|
1219 | + // type can be passed as string or array |
|
1220 | + $type = $options['types']; |
|
1221 | + if (is_array($type)) { |
|
1222 | + $type = $type[0]; |
|
1223 | + } |
|
1224 | + |
|
1225 | + // @todo the types should be defined somewhere (as constant on \ElggEntity?) |
|
1226 | + if (!in_array($type, ['group', 'object', 'site', 'user'])) { |
|
1227 | + throw new InvalidArgumentException("Invalid type '$type' passed to elgg_get_entities_from_attributes()"); |
|
1228 | + } |
|
1229 | + |
|
1230 | + |
|
1231 | + $type_table = "{$this->db->prefix}{$type}s_entity"; |
|
1232 | + |
|
1233 | + $return = [ |
|
1234 | + 'joins' => [], |
|
1235 | + 'wheres' => [], |
|
1236 | + ]; |
|
1237 | + |
|
1238 | + // short circuit if nothing requested |
|
1239 | + if ($options['attribute_name_value_pairs'] == ELGG_ENTITIES_ANY_VALUE) { |
|
1240 | + return $return; |
|
1241 | + } |
|
1242 | + |
|
1243 | + if (!is_array($options['attribute_name_value_pairs'])) { |
|
1244 | + throw new InvalidArgumentException("attribute_name_value_pairs must be an array for elgg_get_entities_from_attributes()"); |
|
1245 | + } |
|
1246 | + |
|
1247 | + $wheres = []; |
|
1248 | + |
|
1249 | + // check if this is an array of pairs or just a single pair. |
|
1250 | + $pairs = $options['attribute_name_value_pairs']; |
|
1251 | + if (isset($pairs['name']) || isset($pairs['value'])) { |
|
1252 | + $pairs = [$pairs]; |
|
1253 | + } |
|
1254 | + |
|
1255 | + $pair_wheres = []; |
|
1256 | + foreach ($pairs as $index => $pair) { |
|
1257 | + // must have at least a name and value |
|
1258 | + if (!isset($pair['name']) || !isset($pair['value'])) { |
|
1259 | + continue; |
|
1260 | + } |
|
1261 | + |
|
1262 | + if (isset($pair['operand'])) { |
|
1263 | + $operand = sanitize_string($pair['operand']); |
|
1264 | + } else { |
|
1265 | + $operand = '='; |
|
1266 | + } |
|
1267 | + |
|
1268 | + if (is_numeric($pair['value'])) { |
|
1269 | + $value = sanitize_string($pair['value']); |
|
1270 | + } else if (is_array($pair['value'])) { |
|
1271 | + $values_array = []; |
|
1272 | + foreach ($pair['value'] as $pair_value) { |
|
1273 | + if (is_numeric($pair_value)) { |
|
1274 | + $values_array[] = sanitize_string($pair_value); |
|
1275 | + } else { |
|
1276 | + $values_array[] = "'" . sanitize_string($pair_value) . "'"; |
|
1277 | + } |
|
1278 | + } |
|
1279 | + |
|
1280 | + $operand = 'IN'; |
|
1281 | + if ($values_array) { |
|
1282 | + $value = '(' . implode(', ', $values_array) . ')'; |
|
1283 | + } |
|
1284 | + } else { |
|
1285 | + $value = "'" . sanitize_string($pair['value']) . "'"; |
|
1286 | + } |
|
1287 | + |
|
1288 | + $name = sanitize_string($pair['name']); |
|
1289 | + |
|
1290 | + // case sensitivity can be specified per pair |
|
1291 | + $pair_binary = ''; |
|
1292 | + if (isset($pair['case_sensitive'])) { |
|
1293 | + $pair_binary = ($pair['case_sensitive']) ? 'BINARY ' : ''; |
|
1294 | + } |
|
1295 | + |
|
1296 | + $pair_wheres[] = "({$pair_binary}type_table.$name $operand $value)"; |
|
1297 | + } |
|
1298 | + |
|
1299 | + if ($where = implode(" {$options['attribute_name_value_pairs_operator']} ", $pair_wheres)) { |
|
1300 | + $return['wheres'][] = "($where)"; |
|
1301 | + |
|
1302 | + $return['joins'][] = "JOIN $type_table type_table ON e.guid = type_table.guid"; |
|
1303 | + } |
|
1304 | + |
|
1305 | + return $return; |
|
1306 | + } |
|
1307 | + |
|
1308 | + /** |
|
1309 | + * Returns a list of months in which entities were updated or created. |
|
1310 | + * |
|
1311 | + * @tip Use this to generate a list of archives by month for when entities were added or updated. |
|
1312 | + * |
|
1313 | + * @todo document how to pass in array for $subtype |
|
1314 | + * |
|
1315 | + * @warning Months are returned in the form YYYYMM. |
|
1316 | + * |
|
1317 | + * @param string $type The type of entity |
|
1318 | + * @param string $subtype The subtype of entity |
|
1319 | + * @param int $container_guid The container GUID that the entities belong to |
|
1320 | + * @param string $order_by Order_by SQL order by clause |
|
1321 | + * |
|
1322 | + * @return array|false Either an array months as YYYYMM, or false on failure |
|
1323 | + */ |
|
1324 | + public function getDates($type = '', $subtype = '', $container_guid = 0, $order_by = 'time_created') { |
|
1325 | + |
|
1326 | + $where = []; |
|
1327 | + |
|
1328 | + if ($type != "") { |
|
1329 | + $type = sanitise_string($type); |
|
1330 | + $where[] = "type='$type'"; |
|
1331 | + } |
|
1332 | + |
|
1333 | + if (is_array($subtype)) { |
|
1334 | + $tempwhere = ""; |
|
1335 | + if (sizeof($subtype)) { |
|
1336 | + foreach ($subtype as $typekey => $subtypearray) { |
|
1337 | + foreach ($subtypearray as $subtypeval) { |
|
1338 | + $typekey = sanitise_string($typekey); |
|
1339 | + if (!empty($subtypeval)) { |
|
1340 | + if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) { |
|
1341 | + return false; |
|
1342 | + } |
|
1343 | + } else { |
|
1344 | + $subtypeval = 0; |
|
1345 | + } |
|
1346 | + if (!empty($tempwhere)) { |
|
1347 | + $tempwhere .= " or "; |
|
1348 | + } |
|
1349 | + $tempwhere .= "(type = '{$typekey}' and subtype = {$subtypeval})"; |
|
1350 | + } |
|
1351 | + } |
|
1352 | + } |
|
1353 | + if (!empty($tempwhere)) { |
|
1354 | + $where[] = "({$tempwhere})"; |
|
1355 | + } |
|
1356 | + } else { |
|
1357 | + if ($subtype) { |
|
1358 | + if (!$subtype_id = get_subtype_id($type, $subtype)) { |
|
1359 | + return false; |
|
1360 | + } else { |
|
1361 | + $where[] = "subtype=$subtype_id"; |
|
1362 | + } |
|
1363 | + } |
|
1364 | + } |
|
1365 | + |
|
1366 | + if ($container_guid !== 0) { |
|
1367 | + if (is_array($container_guid)) { |
|
1368 | + foreach ($container_guid as $key => $val) { |
|
1369 | + $container_guid[$key] = (int) $val; |
|
1370 | + } |
|
1371 | + $where[] = "container_guid in (" . implode(",", $container_guid) . ")"; |
|
1372 | + } else { |
|
1373 | + $container_guid = (int) $container_guid; |
|
1374 | + $where[] = "container_guid = {$container_guid}"; |
|
1375 | + } |
|
1376 | + } |
|
1377 | + |
|
1378 | + $where[] = _elgg_get_access_where_sql(['table_alias' => '']); |
|
1379 | + |
|
1380 | + $sql = "SELECT DISTINCT EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time_created)) AS yearmonth |
|
1381 | 1381 | FROM {$this->db->prefix}entities where "; |
1382 | 1382 | |
1383 | - foreach ($where as $w) { |
|
1384 | - $sql .= " $w and "; |
|
1385 | - } |
|
1386 | - |
|
1387 | - $sql .= "1=1 ORDER BY $order_by"; |
|
1388 | - if ($result = $this->db->getData($sql)) { |
|
1389 | - $endresult = []; |
|
1390 | - foreach ($result as $res) { |
|
1391 | - $endresult[] = $res->yearmonth; |
|
1392 | - } |
|
1393 | - return $endresult; |
|
1394 | - } |
|
1395 | - return false; |
|
1396 | - } |
|
1397 | - |
|
1398 | - /** |
|
1399 | - * Update the last_action column in the entities table for $guid. |
|
1400 | - * |
|
1401 | - * @warning This is different to time_updated. Time_updated is automatically set, |
|
1402 | - * while last_action is only set when explicitly called. |
|
1403 | - * |
|
1404 | - * @param ElggEntity $entity Entity annotation|relationship action carried out on |
|
1405 | - * @param int $posted Timestamp of last action |
|
1406 | - * @return int|false |
|
1407 | - * @access private |
|
1408 | - */ |
|
1409 | - public function updateLastAction(ElggEntity $entity, $posted = null) { |
|
1410 | - |
|
1411 | - if (!$posted) { |
|
1412 | - $posted = $this->getCurrentTime()->getTimestamp(); |
|
1413 | - } |
|
1383 | + foreach ($where as $w) { |
|
1384 | + $sql .= " $w and "; |
|
1385 | + } |
|
1386 | + |
|
1387 | + $sql .= "1=1 ORDER BY $order_by"; |
|
1388 | + if ($result = $this->db->getData($sql)) { |
|
1389 | + $endresult = []; |
|
1390 | + foreach ($result as $res) { |
|
1391 | + $endresult[] = $res->yearmonth; |
|
1392 | + } |
|
1393 | + return $endresult; |
|
1394 | + } |
|
1395 | + return false; |
|
1396 | + } |
|
1397 | + |
|
1398 | + /** |
|
1399 | + * Update the last_action column in the entities table for $guid. |
|
1400 | + * |
|
1401 | + * @warning This is different to time_updated. Time_updated is automatically set, |
|
1402 | + * while last_action is only set when explicitly called. |
|
1403 | + * |
|
1404 | + * @param ElggEntity $entity Entity annotation|relationship action carried out on |
|
1405 | + * @param int $posted Timestamp of last action |
|
1406 | + * @return int|false |
|
1407 | + * @access private |
|
1408 | + */ |
|
1409 | + public function updateLastAction(ElggEntity $entity, $posted = null) { |
|
1410 | + |
|
1411 | + if (!$posted) { |
|
1412 | + $posted = $this->getCurrentTime()->getTimestamp(); |
|
1413 | + } |
|
1414 | 1414 | |
1415 | - $query = " |
|
1415 | + $query = " |
|
1416 | 1416 | UPDATE {$this->db->prefix}entities |
1417 | 1417 | SET last_action = :last_action |
1418 | 1418 | WHERE guid = :guid |
1419 | 1419 | "; |
1420 | 1420 | |
1421 | - $params = [ |
|
1422 | - ':last_action' => (int) $posted, |
|
1423 | - ':guid' => (int) $entity->guid, |
|
1424 | - ]; |
|
1421 | + $params = [ |
|
1422 | + ':last_action' => (int) $posted, |
|
1423 | + ':guid' => (int) $entity->guid, |
|
1424 | + ]; |
|
1425 | 1425 | |
1426 | - $result = $this->db->updateData($query, true, $params); |
|
1427 | - if ($result) { |
|
1428 | - $entity->last_action = $posted; |
|
1429 | - _elgg_services()->entityCache->set($entity); |
|
1430 | - $entity->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
1431 | - return (int) $posted; |
|
1432 | - } |
|
1433 | - |
|
1434 | - return false; |
|
1435 | - } |
|
1436 | - |
|
1437 | - /** |
|
1438 | - * Get a user by GUID even if the entity is hidden or disabled |
|
1439 | - * |
|
1440 | - * @param int $guid User GUID. Default is logged in user |
|
1441 | - * |
|
1442 | - * @return ElggUser|false |
|
1443 | - * @throws UserFetchFailureException |
|
1444 | - * @access private |
|
1445 | - */ |
|
1446 | - public function getUserForPermissionsCheck($guid = 0) { |
|
1447 | - if (!$guid) { |
|
1448 | - return $this->session->getLoggedInUser(); |
|
1449 | - } |
|
1450 | - |
|
1451 | - // need to ignore access and show hidden entities for potential hidden/disabled users |
|
1452 | - $ia = $this->session->setIgnoreAccess(true); |
|
1453 | - $show_hidden = access_show_hidden_entities(true); |
|
1426 | + $result = $this->db->updateData($query, true, $params); |
|
1427 | + if ($result) { |
|
1428 | + $entity->last_action = $posted; |
|
1429 | + _elgg_services()->entityCache->set($entity); |
|
1430 | + $entity->storeInPersistedCache(_elgg_get_memcache('new_entity_cache')); |
|
1431 | + return (int) $posted; |
|
1432 | + } |
|
1433 | + |
|
1434 | + return false; |
|
1435 | + } |
|
1436 | + |
|
1437 | + /** |
|
1438 | + * Get a user by GUID even if the entity is hidden or disabled |
|
1439 | + * |
|
1440 | + * @param int $guid User GUID. Default is logged in user |
|
1441 | + * |
|
1442 | + * @return ElggUser|false |
|
1443 | + * @throws UserFetchFailureException |
|
1444 | + * @access private |
|
1445 | + */ |
|
1446 | + public function getUserForPermissionsCheck($guid = 0) { |
|
1447 | + if (!$guid) { |
|
1448 | + return $this->session->getLoggedInUser(); |
|
1449 | + } |
|
1450 | + |
|
1451 | + // need to ignore access and show hidden entities for potential hidden/disabled users |
|
1452 | + $ia = $this->session->setIgnoreAccess(true); |
|
1453 | + $show_hidden = access_show_hidden_entities(true); |
|
1454 | 1454 | |
1455 | - $user = $this->get($guid, 'user'); |
|
1456 | - |
|
1457 | - $this->session->setIgnoreAccess($ia); |
|
1458 | - access_show_hidden_entities($show_hidden); |
|
1459 | - |
|
1460 | - if (!$user) { |
|
1461 | - // requested to check access for a specific user_guid, but there is no user entity, so the caller |
|
1462 | - // should cancel the check and return false |
|
1463 | - $message = $this->translator->translate('UserFetchFailureException', [$guid]); |
|
1464 | - $this->logger->warn($message); |
|
1465 | - |
|
1466 | - throw new UserFetchFailureException(); |
|
1467 | - } |
|
1468 | - |
|
1469 | - return $user; |
|
1470 | - } |
|
1471 | - |
|
1472 | - /** |
|
1473 | - * Disables all entities owned and contained by a user (or another entity) |
|
1474 | - * |
|
1475 | - * @param int $owner_guid The owner GUID |
|
1476 | - * @return bool |
|
1477 | - */ |
|
1478 | - public function disableEntities($owner_guid) { |
|
1479 | - $entity = get_entity($owner_guid); |
|
1480 | - if (!$entity || !$entity->canEdit()) { |
|
1481 | - return false; |
|
1482 | - } |
|
1483 | - |
|
1484 | - if (!$this->events->trigger('disable', $entity->type, $entity)) { |
|
1485 | - return false; |
|
1486 | - } |
|
1487 | - |
|
1488 | - $query = " |
|
1455 | + $user = $this->get($guid, 'user'); |
|
1456 | + |
|
1457 | + $this->session->setIgnoreAccess($ia); |
|
1458 | + access_show_hidden_entities($show_hidden); |
|
1459 | + |
|
1460 | + if (!$user) { |
|
1461 | + // requested to check access for a specific user_guid, but there is no user entity, so the caller |
|
1462 | + // should cancel the check and return false |
|
1463 | + $message = $this->translator->translate('UserFetchFailureException', [$guid]); |
|
1464 | + $this->logger->warn($message); |
|
1465 | + |
|
1466 | + throw new UserFetchFailureException(); |
|
1467 | + } |
|
1468 | + |
|
1469 | + return $user; |
|
1470 | + } |
|
1471 | + |
|
1472 | + /** |
|
1473 | + * Disables all entities owned and contained by a user (or another entity) |
|
1474 | + * |
|
1475 | + * @param int $owner_guid The owner GUID |
|
1476 | + * @return bool |
|
1477 | + */ |
|
1478 | + public function disableEntities($owner_guid) { |
|
1479 | + $entity = get_entity($owner_guid); |
|
1480 | + if (!$entity || !$entity->canEdit()) { |
|
1481 | + return false; |
|
1482 | + } |
|
1483 | + |
|
1484 | + if (!$this->events->trigger('disable', $entity->type, $entity)) { |
|
1485 | + return false; |
|
1486 | + } |
|
1487 | + |
|
1488 | + $query = " |
|
1489 | 1489 | UPDATE {$this->table}entities |
1490 | 1490 | SET enabled='no' |
1491 | 1491 | WHERE owner_guid = :owner_guid |
1492 | 1492 | OR container_guid = :owner_guid"; |
1493 | 1493 | |
1494 | - $params = [ |
|
1495 | - ':owner_guid' => (int) $owner_guid, |
|
1496 | - ]; |
|
1494 | + $params = [ |
|
1495 | + ':owner_guid' => (int) $owner_guid, |
|
1496 | + ]; |
|
1497 | 1497 | |
1498 | - _elgg_invalidate_cache_for_entity($entity->guid); |
|
1499 | - _elgg_invalidate_memcache_for_entity($entity->guid); |
|
1498 | + _elgg_invalidate_cache_for_entity($entity->guid); |
|
1499 | + _elgg_invalidate_memcache_for_entity($entity->guid); |
|
1500 | 1500 | |
1501 | - if ($this->db->updateData($query, true, $params)) { |
|
1502 | - return true; |
|
1503 | - } |
|
1501 | + if ($this->db->updateData($query, true, $params)) { |
|
1502 | + return true; |
|
1503 | + } |
|
1504 | 1504 | |
1505 | - return false; |
|
1506 | - } |
|
1505 | + return false; |
|
1506 | + } |
|
1507 | 1507 | |
1508 | 1508 | } |
@@ -12,439 +12,439 @@ |
||
12 | 12 | */ |
13 | 13 | class Annotations { |
14 | 14 | |
15 | - use \Elgg\TimeUsing; |
|
15 | + use \Elgg\TimeUsing; |
|
16 | 16 | |
17 | - /** |
|
18 | - * @var \Elgg\Database |
|
19 | - */ |
|
20 | - protected $db; |
|
17 | + /** |
|
18 | + * @var \Elgg\Database |
|
19 | + */ |
|
20 | + protected $db; |
|
21 | 21 | |
22 | - /** |
|
23 | - * @var \ElggSession |
|
24 | - */ |
|
25 | - protected $session; |
|
22 | + /** |
|
23 | + * @var \ElggSession |
|
24 | + */ |
|
25 | + protected $session; |
|
26 | 26 | |
27 | - /** |
|
28 | - * @var \Elgg\EventsService |
|
29 | - */ |
|
30 | - protected $events; |
|
27 | + /** |
|
28 | + * @var \Elgg\EventsService |
|
29 | + */ |
|
30 | + protected $events; |
|
31 | 31 | |
32 | - /** |
|
33 | - * Constructor |
|
34 | - * |
|
35 | - * @param \Elgg\Database $db Database |
|
36 | - * @param \ElggSession $session Session |
|
37 | - * @param \Elgg\EventsService $events Events |
|
38 | - */ |
|
39 | - public function __construct(\Elgg\Database $db, \ElggSession $session, \Elgg\EventsService $events) { |
|
40 | - $this->db = $db; |
|
41 | - $this->session = $session; |
|
42 | - $this->events = $events; |
|
43 | - } |
|
32 | + /** |
|
33 | + * Constructor |
|
34 | + * |
|
35 | + * @param \Elgg\Database $db Database |
|
36 | + * @param \ElggSession $session Session |
|
37 | + * @param \Elgg\EventsService $events Events |
|
38 | + */ |
|
39 | + public function __construct(\Elgg\Database $db, \ElggSession $session, \Elgg\EventsService $events) { |
|
40 | + $this->db = $db; |
|
41 | + $this->session = $session; |
|
42 | + $this->events = $events; |
|
43 | + } |
|
44 | 44 | |
45 | - /** |
|
46 | - * Get a specific annotation by its id. |
|
47 | - * If you want multiple annotation objects, use |
|
48 | - * {@link elgg_get_annotations()}. |
|
49 | - * |
|
50 | - * @param int $id The id of the annotation object being retrieved. |
|
51 | - * |
|
52 | - * @return \ElggAnnotation|false |
|
53 | - */ |
|
54 | - function get($id) { |
|
55 | - return _elgg_get_metastring_based_object_from_id($id, 'annotation'); |
|
56 | - } |
|
57 | - |
|
58 | - /** |
|
59 | - * Deletes an annotation using its ID. |
|
60 | - * |
|
61 | - * @param int $id The annotation ID to delete. |
|
62 | - * @return bool |
|
63 | - */ |
|
64 | - function delete($id) { |
|
65 | - $annotation = $this->get($id); |
|
66 | - if (!$annotation) { |
|
67 | - return false; |
|
68 | - } |
|
69 | - return $annotation->delete(); |
|
70 | - } |
|
71 | - |
|
72 | - /** |
|
73 | - * Create a new annotation. |
|
74 | - * |
|
75 | - * @param int $entity_guid GUID of entity to be annotated |
|
76 | - * @param string $name Name of annotation |
|
77 | - * @param string $value Value of annotation |
|
78 | - * @param string $value_type Type of value (default is auto detection) |
|
79 | - * @param int $owner_guid Owner of annotation (default is logged in user) |
|
80 | - * @param int $access_id Access level of annotation |
|
81 | - * |
|
82 | - * @return int|bool id on success or false on failure |
|
83 | - */ |
|
84 | - function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, $access_id = ACCESS_PRIVATE) { |
|
45 | + /** |
|
46 | + * Get a specific annotation by its id. |
|
47 | + * If you want multiple annotation objects, use |
|
48 | + * {@link elgg_get_annotations()}. |
|
49 | + * |
|
50 | + * @param int $id The id of the annotation object being retrieved. |
|
51 | + * |
|
52 | + * @return \ElggAnnotation|false |
|
53 | + */ |
|
54 | + function get($id) { |
|
55 | + return _elgg_get_metastring_based_object_from_id($id, 'annotation'); |
|
56 | + } |
|
57 | + |
|
58 | + /** |
|
59 | + * Deletes an annotation using its ID. |
|
60 | + * |
|
61 | + * @param int $id The annotation ID to delete. |
|
62 | + * @return bool |
|
63 | + */ |
|
64 | + function delete($id) { |
|
65 | + $annotation = $this->get($id); |
|
66 | + if (!$annotation) { |
|
67 | + return false; |
|
68 | + } |
|
69 | + return $annotation->delete(); |
|
70 | + } |
|
71 | + |
|
72 | + /** |
|
73 | + * Create a new annotation. |
|
74 | + * |
|
75 | + * @param int $entity_guid GUID of entity to be annotated |
|
76 | + * @param string $name Name of annotation |
|
77 | + * @param string $value Value of annotation |
|
78 | + * @param string $value_type Type of value (default is auto detection) |
|
79 | + * @param int $owner_guid Owner of annotation (default is logged in user) |
|
80 | + * @param int $access_id Access level of annotation |
|
81 | + * |
|
82 | + * @return int|bool id on success or false on failure |
|
83 | + */ |
|
84 | + function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, $access_id = ACCESS_PRIVATE) { |
|
85 | 85 | |
86 | - $result = false; |
|
86 | + $result = false; |
|
87 | 87 | |
88 | - $entity_guid = (int) $entity_guid; |
|
89 | - $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
88 | + $entity_guid = (int) $entity_guid; |
|
89 | + $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
90 | 90 | |
91 | - $owner_guid = (int) $owner_guid; |
|
92 | - if ($owner_guid == 0) { |
|
93 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
94 | - } |
|
91 | + $owner_guid = (int) $owner_guid; |
|
92 | + if ($owner_guid == 0) { |
|
93 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
94 | + } |
|
95 | 95 | |
96 | - $access_id = (int) $access_id; |
|
96 | + $access_id = (int) $access_id; |
|
97 | 97 | |
98 | - // @todo we don't check that the entity is loaded which means the user may |
|
99 | - // not have access to the entity |
|
100 | - $entity = get_entity($entity_guid); |
|
98 | + // @todo we don't check that the entity is loaded which means the user may |
|
99 | + // not have access to the entity |
|
100 | + $entity = get_entity($entity_guid); |
|
101 | 101 | |
102 | - if ($this->events->trigger('annotate', $entity->type, $entity)) { |
|
103 | - $sql = "INSERT INTO {$this->db->prefix}annotations |
|
102 | + if ($this->events->trigger('annotate', $entity->type, $entity)) { |
|
103 | + $sql = "INSERT INTO {$this->db->prefix}annotations |
|
104 | 104 | (entity_guid, name, value, value_type, owner_guid, time_created, access_id) |
105 | 105 | VALUES |
106 | 106 | (:entity_guid, :name, :value, :value_type, :owner_guid, :time_created, :access_id)"; |
107 | 107 | |
108 | - $result = $this->db->insertData($sql, [ |
|
109 | - ':entity_guid' => $entity_guid, |
|
110 | - ':name' => $name, |
|
111 | - ':value' => $value, |
|
112 | - ':value_type' => $value_type, |
|
113 | - ':owner_guid' => $owner_guid, |
|
114 | - ':time_created' => $this->getCurrentTime()->getTimestamp(), |
|
115 | - ':access_id' => $access_id, |
|
116 | - ]); |
|
108 | + $result = $this->db->insertData($sql, [ |
|
109 | + ':entity_guid' => $entity_guid, |
|
110 | + ':name' => $name, |
|
111 | + ':value' => $value, |
|
112 | + ':value_type' => $value_type, |
|
113 | + ':owner_guid' => $owner_guid, |
|
114 | + ':time_created' => $this->getCurrentTime()->getTimestamp(), |
|
115 | + ':access_id' => $access_id, |
|
116 | + ]); |
|
117 | 117 | |
118 | - if ($result !== false) { |
|
119 | - $obj = elgg_get_annotation_from_id($result); |
|
120 | - if ($this->events->trigger('create', 'annotation', $obj)) { |
|
121 | - return $result; |
|
122 | - } else { |
|
123 | - // plugin returned false to reject annotation |
|
124 | - elgg_delete_annotation_by_id($result); |
|
125 | - return false; |
|
126 | - } |
|
127 | - } |
|
128 | - } |
|
129 | - |
|
130 | - return $result; |
|
131 | - } |
|
132 | - |
|
133 | - /** |
|
134 | - * Update an annotation. |
|
135 | - * |
|
136 | - * @param int $annotation_id Annotation ID |
|
137 | - * @param string $name Name of annotation |
|
138 | - * @param string $value Value of annotation |
|
139 | - * @param string $value_type Type of value |
|
140 | - * @param int $owner_guid Owner of annotation |
|
141 | - * @param int $access_id Access level of annotation |
|
142 | - * |
|
143 | - * @return bool |
|
144 | - */ |
|
145 | - function update($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
118 | + if ($result !== false) { |
|
119 | + $obj = elgg_get_annotation_from_id($result); |
|
120 | + if ($this->events->trigger('create', 'annotation', $obj)) { |
|
121 | + return $result; |
|
122 | + } else { |
|
123 | + // plugin returned false to reject annotation |
|
124 | + elgg_delete_annotation_by_id($result); |
|
125 | + return false; |
|
126 | + } |
|
127 | + } |
|
128 | + } |
|
129 | + |
|
130 | + return $result; |
|
131 | + } |
|
132 | + |
|
133 | + /** |
|
134 | + * Update an annotation. |
|
135 | + * |
|
136 | + * @param int $annotation_id Annotation ID |
|
137 | + * @param string $name Name of annotation |
|
138 | + * @param string $value Value of annotation |
|
139 | + * @param string $value_type Type of value |
|
140 | + * @param int $owner_guid Owner of annotation |
|
141 | + * @param int $access_id Access level of annotation |
|
142 | + * |
|
143 | + * @return bool |
|
144 | + */ |
|
145 | + function update($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
146 | 146 | |
147 | - $annotation_id = (int) $annotation_id; |
|
147 | + $annotation_id = (int) $annotation_id; |
|
148 | 148 | |
149 | - $annotation = $this->get($annotation_id); |
|
150 | - if (!$annotation) { |
|
151 | - return false; |
|
152 | - } |
|
153 | - if (!$annotation->canEdit()) { |
|
154 | - return false; |
|
155 | - } |
|
149 | + $annotation = $this->get($annotation_id); |
|
150 | + if (!$annotation) { |
|
151 | + return false; |
|
152 | + } |
|
153 | + if (!$annotation->canEdit()) { |
|
154 | + return false; |
|
155 | + } |
|
156 | 156 | |
157 | - $name = trim($name); |
|
158 | - $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
157 | + $name = trim($name); |
|
158 | + $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
159 | 159 | |
160 | - $owner_guid = (int) $owner_guid; |
|
161 | - if ($owner_guid == 0) { |
|
162 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
163 | - } |
|
160 | + $owner_guid = (int) $owner_guid; |
|
161 | + if ($owner_guid == 0) { |
|
162 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
163 | + } |
|
164 | 164 | |
165 | - $access_id = (int) $access_id; |
|
165 | + $access_id = (int) $access_id; |
|
166 | 166 | |
167 | - $sql = "UPDATE {$this->db->prefix}annotations |
|
167 | + $sql = "UPDATE {$this->db->prefix}annotations |
|
168 | 168 | (name, value, value_type, access_id, owner_guid) |
169 | 169 | VALUES |
170 | 170 | (:name, :value, :value_type, :access_id, :owner_guid) |
171 | 171 | WHERE id = :annotation_id"; |
172 | 172 | |
173 | - $result = $this->db->updateData($sql, false, [ |
|
174 | - ':name' => $name, |
|
175 | - ':value' => $value, |
|
176 | - ':value_type' => $value_type, |
|
177 | - ':access_id' => $access_id, |
|
178 | - ':owner_guid' => $owner_guid, |
|
179 | - ':annotation_id' => $annotation_id, |
|
180 | - ]); |
|
173 | + $result = $this->db->updateData($sql, false, [ |
|
174 | + ':name' => $name, |
|
175 | + ':value' => $value, |
|
176 | + ':value_type' => $value_type, |
|
177 | + ':access_id' => $access_id, |
|
178 | + ':owner_guid' => $owner_guid, |
|
179 | + ':annotation_id' => $annotation_id, |
|
180 | + ]); |
|
181 | 181 | |
182 | - if ($result !== false) { |
|
183 | - // @todo add plugin hook that sends old and new annotation information before db access |
|
184 | - $obj = $this->get($annotation_id); |
|
185 | - $this->events->trigger('update', 'annotation', $obj); |
|
186 | - } |
|
187 | - |
|
188 | - return $result; |
|
189 | - } |
|
190 | - |
|
191 | - /** |
|
192 | - * Returns annotations. Accepts all elgg_get_entities() options for entity |
|
193 | - * restraints. |
|
194 | - * |
|
195 | - * @see elgg_get_entities |
|
196 | - * |
|
197 | - * @param array $options Array in format: |
|
198 | - * |
|
199 | - * annotation_names => null|ARR Annotation names |
|
200 | - * annotation_values => null|ARR Annotation values |
|
201 | - * annotation_ids => null|ARR annotation ids |
|
202 | - * annotation_case_sensitive => BOOL Overall Case sensitive |
|
203 | - * annotation_owner_guids => null|ARR guids for annotation owners |
|
204 | - * annotation_created_time_lower => INT Lower limit for created time. |
|
205 | - * annotation_created_time_upper => INT Upper limit for created time. |
|
206 | - * annotation_calculation => STR Perform the MySQL function on the annotation values returned. |
|
207 | - * Do not confuse this "annotation_calculation" option with the |
|
208 | - * "calculation" option to elgg_get_entities_from_annotation_calculation(). |
|
209 | - * The "annotation_calculation" option causes this function to |
|
210 | - * return the result of performing a mathematical calculation on |
|
211 | - * all annotations that match the query instead of \ElggAnnotation |
|
212 | - * objects. |
|
213 | - * See the docs for elgg_get_entities_from_annotation_calculation() |
|
214 | - * for the proper use of the "calculation" option. |
|
215 | - * |
|
216 | - * |
|
217 | - * @return \ElggAnnotation[]|mixed |
|
218 | - */ |
|
219 | - function find(array $options = []) { |
|
182 | + if ($result !== false) { |
|
183 | + // @todo add plugin hook that sends old and new annotation information before db access |
|
184 | + $obj = $this->get($annotation_id); |
|
185 | + $this->events->trigger('update', 'annotation', $obj); |
|
186 | + } |
|
187 | + |
|
188 | + return $result; |
|
189 | + } |
|
190 | + |
|
191 | + /** |
|
192 | + * Returns annotations. Accepts all elgg_get_entities() options for entity |
|
193 | + * restraints. |
|
194 | + * |
|
195 | + * @see elgg_get_entities |
|
196 | + * |
|
197 | + * @param array $options Array in format: |
|
198 | + * |
|
199 | + * annotation_names => null|ARR Annotation names |
|
200 | + * annotation_values => null|ARR Annotation values |
|
201 | + * annotation_ids => null|ARR annotation ids |
|
202 | + * annotation_case_sensitive => BOOL Overall Case sensitive |
|
203 | + * annotation_owner_guids => null|ARR guids for annotation owners |
|
204 | + * annotation_created_time_lower => INT Lower limit for created time. |
|
205 | + * annotation_created_time_upper => INT Upper limit for created time. |
|
206 | + * annotation_calculation => STR Perform the MySQL function on the annotation values returned. |
|
207 | + * Do not confuse this "annotation_calculation" option with the |
|
208 | + * "calculation" option to elgg_get_entities_from_annotation_calculation(). |
|
209 | + * The "annotation_calculation" option causes this function to |
|
210 | + * return the result of performing a mathematical calculation on |
|
211 | + * all annotations that match the query instead of \ElggAnnotation |
|
212 | + * objects. |
|
213 | + * See the docs for elgg_get_entities_from_annotation_calculation() |
|
214 | + * for the proper use of the "calculation" option. |
|
215 | + * |
|
216 | + * |
|
217 | + * @return \ElggAnnotation[]|mixed |
|
218 | + */ |
|
219 | + function find(array $options = []) { |
|
220 | 220 | |
221 | - // support shortcut of 'count' => true for 'annotation_calculation' => 'count' |
|
222 | - if (isset($options['count']) && $options['count']) { |
|
223 | - $options['annotation_calculation'] = 'count'; |
|
224 | - unset($options['count']); |
|
225 | - } |
|
221 | + // support shortcut of 'count' => true for 'annotation_calculation' => 'count' |
|
222 | + if (isset($options['count']) && $options['count']) { |
|
223 | + $options['annotation_calculation'] = 'count'; |
|
224 | + unset($options['count']); |
|
225 | + } |
|
226 | 226 | |
227 | - $options['metastring_type'] = 'annotations'; |
|
228 | - return _elgg_get_metastring_based_objects($options); |
|
229 | - } |
|
230 | - |
|
231 | - /** |
|
232 | - * Deletes annotations based on $options. |
|
233 | - * |
|
234 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
235 | - * This requires at least one constraint: annotation_owner_guid(s), |
|
236 | - * annotation_name(s), annotation_value(s), or guid(s) must be set. |
|
237 | - * |
|
238 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
239 | - * @return bool|null true on success, false on failure, null if no annotations to delete. |
|
240 | - */ |
|
241 | - function deleteAll(array $options) { |
|
242 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
243 | - return false; |
|
244 | - } |
|
245 | - |
|
246 | - $options['metastring_type'] = 'annotations'; |
|
247 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
248 | - } |
|
249 | - |
|
250 | - /** |
|
251 | - * Disables annotations based on $options. |
|
252 | - * |
|
253 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
254 | - * |
|
255 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
256 | - * @return bool|null true on success, false on failure, null if no annotations disabled. |
|
257 | - */ |
|
258 | - function disableAll(array $options) { |
|
259 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
260 | - return false; |
|
261 | - } |
|
227 | + $options['metastring_type'] = 'annotations'; |
|
228 | + return _elgg_get_metastring_based_objects($options); |
|
229 | + } |
|
230 | + |
|
231 | + /** |
|
232 | + * Deletes annotations based on $options. |
|
233 | + * |
|
234 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
235 | + * This requires at least one constraint: annotation_owner_guid(s), |
|
236 | + * annotation_name(s), annotation_value(s), or guid(s) must be set. |
|
237 | + * |
|
238 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
239 | + * @return bool|null true on success, false on failure, null if no annotations to delete. |
|
240 | + */ |
|
241 | + function deleteAll(array $options) { |
|
242 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
243 | + return false; |
|
244 | + } |
|
245 | + |
|
246 | + $options['metastring_type'] = 'annotations'; |
|
247 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
248 | + } |
|
249 | + |
|
250 | + /** |
|
251 | + * Disables annotations based on $options. |
|
252 | + * |
|
253 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
254 | + * |
|
255 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
256 | + * @return bool|null true on success, false on failure, null if no annotations disabled. |
|
257 | + */ |
|
258 | + function disableAll(array $options) { |
|
259 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
260 | + return false; |
|
261 | + } |
|
262 | 262 | |
263 | - // if we can see hidden (disabled) we need to use the offset |
|
264 | - // otherwise we risk an infinite loop if there are more than 50 |
|
265 | - $inc_offset = access_get_show_hidden_status(); |
|
266 | - |
|
267 | - $options['metastring_type'] = 'annotations'; |
|
268 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
269 | - } |
|
270 | - |
|
271 | - /** |
|
272 | - * Enables annotations based on $options. |
|
273 | - * |
|
274 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
275 | - * |
|
276 | - * @warning In order to enable annotations, you must first use |
|
277 | - * {@link access_show_hidden_entities()}. |
|
278 | - * |
|
279 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
280 | - * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
281 | - */ |
|
282 | - function enableAll(array $options) { |
|
283 | - if (!$options || !is_array($options)) { |
|
284 | - return false; |
|
285 | - } |
|
286 | - |
|
287 | - $options['metastring_type'] = 'annotations'; |
|
288 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
289 | - } |
|
290 | - |
|
291 | - /** |
|
292 | - * Returns entities based upon annotations. Also accepts all options available |
|
293 | - * to elgg_get_entities() and elgg_get_entities_from_metadata(). |
|
294 | - * |
|
295 | - * @see elgg_get_entities |
|
296 | - * @see elgg_get_entities_from_metadata |
|
297 | - * |
|
298 | - * @param array $options Array in format: |
|
299 | - * |
|
300 | - * annotation_names => null|ARR annotations names |
|
301 | - * |
|
302 | - * annotation_values => null|ARR annotations values |
|
303 | - * |
|
304 | - * annotation_name_value_pairs => null|ARR (name = 'name', value => 'value', |
|
305 | - * 'operator' => '=', 'case_sensitive' => true) entries. |
|
306 | - * Currently if multiple values are sent via an array (value => array('value1', 'value2') |
|
307 | - * the pair's operator will be forced to "IN". |
|
308 | - * |
|
309 | - * annotation_name_value_pairs_operator => null|STR The operator to use for combining |
|
310 | - * (name = value) OPERATOR (name = value); default AND |
|
311 | - * |
|
312 | - * annotation_case_sensitive => BOOL Overall Case sensitive |
|
313 | - * |
|
314 | - * order_by_annotation => null|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC, |
|
315 | - * 'as' => text|integer), |
|
316 | - * |
|
317 | - * Also supports array('name' => 'annotation_text1') |
|
318 | - * |
|
319 | - * annotation_owner_guids => null|ARR guids for annotaiton owners |
|
320 | - * |
|
321 | - * @return mixed If count, int. If not count, array. false on errors. |
|
322 | - */ |
|
323 | - function getEntities(array $options = []) { |
|
324 | - $defaults = [ |
|
325 | - 'annotation_names' => ELGG_ENTITIES_ANY_VALUE, |
|
326 | - 'annotation_values' => ELGG_ENTITIES_ANY_VALUE, |
|
327 | - 'annotation_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
263 | + // if we can see hidden (disabled) we need to use the offset |
|
264 | + // otherwise we risk an infinite loop if there are more than 50 |
|
265 | + $inc_offset = access_get_show_hidden_status(); |
|
266 | + |
|
267 | + $options['metastring_type'] = 'annotations'; |
|
268 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
269 | + } |
|
270 | + |
|
271 | + /** |
|
272 | + * Enables annotations based on $options. |
|
273 | + * |
|
274 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
275 | + * |
|
276 | + * @warning In order to enable annotations, you must first use |
|
277 | + * {@link access_show_hidden_entities()}. |
|
278 | + * |
|
279 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
280 | + * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
281 | + */ |
|
282 | + function enableAll(array $options) { |
|
283 | + if (!$options || !is_array($options)) { |
|
284 | + return false; |
|
285 | + } |
|
286 | + |
|
287 | + $options['metastring_type'] = 'annotations'; |
|
288 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
289 | + } |
|
290 | + |
|
291 | + /** |
|
292 | + * Returns entities based upon annotations. Also accepts all options available |
|
293 | + * to elgg_get_entities() and elgg_get_entities_from_metadata(). |
|
294 | + * |
|
295 | + * @see elgg_get_entities |
|
296 | + * @see elgg_get_entities_from_metadata |
|
297 | + * |
|
298 | + * @param array $options Array in format: |
|
299 | + * |
|
300 | + * annotation_names => null|ARR annotations names |
|
301 | + * |
|
302 | + * annotation_values => null|ARR annotations values |
|
303 | + * |
|
304 | + * annotation_name_value_pairs => null|ARR (name = 'name', value => 'value', |
|
305 | + * 'operator' => '=', 'case_sensitive' => true) entries. |
|
306 | + * Currently if multiple values are sent via an array (value => array('value1', 'value2') |
|
307 | + * the pair's operator will be forced to "IN". |
|
308 | + * |
|
309 | + * annotation_name_value_pairs_operator => null|STR The operator to use for combining |
|
310 | + * (name = value) OPERATOR (name = value); default AND |
|
311 | + * |
|
312 | + * annotation_case_sensitive => BOOL Overall Case sensitive |
|
313 | + * |
|
314 | + * order_by_annotation => null|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC, |
|
315 | + * 'as' => text|integer), |
|
316 | + * |
|
317 | + * Also supports array('name' => 'annotation_text1') |
|
318 | + * |
|
319 | + * annotation_owner_guids => null|ARR guids for annotaiton owners |
|
320 | + * |
|
321 | + * @return mixed If count, int. If not count, array. false on errors. |
|
322 | + */ |
|
323 | + function getEntities(array $options = []) { |
|
324 | + $defaults = [ |
|
325 | + 'annotation_names' => ELGG_ENTITIES_ANY_VALUE, |
|
326 | + 'annotation_values' => ELGG_ENTITIES_ANY_VALUE, |
|
327 | + 'annotation_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
328 | 328 | |
329 | - 'annotation_name_value_pairs_operator' => 'AND', |
|
330 | - 'annotation_case_sensitive' => true, |
|
331 | - 'order_by_annotation' => [], |
|
329 | + 'annotation_name_value_pairs_operator' => 'AND', |
|
330 | + 'annotation_case_sensitive' => true, |
|
331 | + 'order_by_annotation' => [], |
|
332 | 332 | |
333 | - 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
334 | - 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
335 | - 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
336 | - ]; |
|
333 | + 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
334 | + 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
335 | + 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
336 | + ]; |
|
337 | 337 | |
338 | - $options = array_merge($defaults, $options); |
|
338 | + $options = array_merge($defaults, $options); |
|
339 | 339 | |
340 | - $singulars = ['annotation_name', 'annotation_value', 'annotation_name_value_pair', 'annotation_owner_guid']; |
|
340 | + $singulars = ['annotation_name', 'annotation_value', 'annotation_name_value_pair', 'annotation_owner_guid']; |
|
341 | 341 | |
342 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
343 | - $options = _elgg_entities_get_metastrings_options('annotation', $options); |
|
342 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
343 | + $options = _elgg_entities_get_metastrings_options('annotation', $options); |
|
344 | 344 | |
345 | - if (!$options) { |
|
346 | - return false; |
|
347 | - } |
|
345 | + if (!$options) { |
|
346 | + return false; |
|
347 | + } |
|
348 | 348 | |
349 | - $time_wheres = _elgg_get_entity_time_where_sql('n_table', $options['annotation_created_time_upper'], |
|
350 | - $options['annotation_created_time_lower']); |
|
349 | + $time_wheres = _elgg_get_entity_time_where_sql('n_table', $options['annotation_created_time_upper'], |
|
350 | + $options['annotation_created_time_lower']); |
|
351 | 351 | |
352 | - if ($time_wheres) { |
|
353 | - $options['wheres'][] = $time_wheres; |
|
354 | - } |
|
355 | - |
|
356 | - return elgg_get_entities_from_metadata($options); |
|
357 | - } |
|
358 | - |
|
359 | - /** |
|
360 | - * Get entities ordered by a mathematical calculation on annotation values |
|
361 | - * |
|
362 | - * @tip Note that this function uses { @link elgg_get_annotations() } to return a list of entities ordered by a mathematical |
|
363 | - * calculation on annotation values, and { @link elgg_get_entities_from_annotations() } to return a count of entities |
|
364 | - * if $options['count'] is set to a truthy value |
|
365 | - * |
|
366 | - * @param array $options An options array: |
|
367 | - * 'calculation' => The calculation to use. Must be a valid MySQL function. |
|
368 | - * Defaults to sum. Result selected as 'annotation_calculation'. |
|
369 | - * Don't confuse this "calculation" option with the |
|
370 | - * "annotation_calculation" option to elgg_get_annotations(). |
|
371 | - * This "calculation" option is applied to each entity's set of |
|
372 | - * annotations and is selected as annotation_calculation for that row. |
|
373 | - * See the docs for elgg_get_annotations() for proper use of the |
|
374 | - * "annotation_calculation" option. |
|
375 | - * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'. |
|
376 | - * 'annotation_names' => The names of annotations on the entity. |
|
377 | - * 'annotation_values' => The values of annotations on the entity. |
|
378 | - * |
|
379 | - * 'metadata_names' => The name of metadata on the entity. |
|
380 | - * 'metadata_values' => The value of metadata on the entitiy. |
|
381 | - * 'callback' => Callback function to pass each row through. |
|
382 | - * @tip This function is different from other ege* functions, |
|
383 | - * as it uses a metastring-based getter function { @link elgg_get_annotations() }, |
|
384 | - * therefore the callback function should be a derivative of { @link entity_row_to_elggstar() } |
|
385 | - * and not of { @link row_to_annotation() } |
|
386 | - * |
|
387 | - * @return \ElggEntity[]|int An array or a count of entities |
|
388 | - * @see elgg_get_annotations() |
|
389 | - * @see elgg_get_entities_from_annotations() |
|
390 | - */ |
|
391 | - function getEntitiesFromCalculation($options) { |
|
352 | + if ($time_wheres) { |
|
353 | + $options['wheres'][] = $time_wheres; |
|
354 | + } |
|
355 | + |
|
356 | + return elgg_get_entities_from_metadata($options); |
|
357 | + } |
|
358 | + |
|
359 | + /** |
|
360 | + * Get entities ordered by a mathematical calculation on annotation values |
|
361 | + * |
|
362 | + * @tip Note that this function uses { @link elgg_get_annotations() } to return a list of entities ordered by a mathematical |
|
363 | + * calculation on annotation values, and { @link elgg_get_entities_from_annotations() } to return a count of entities |
|
364 | + * if $options['count'] is set to a truthy value |
|
365 | + * |
|
366 | + * @param array $options An options array: |
|
367 | + * 'calculation' => The calculation to use. Must be a valid MySQL function. |
|
368 | + * Defaults to sum. Result selected as 'annotation_calculation'. |
|
369 | + * Don't confuse this "calculation" option with the |
|
370 | + * "annotation_calculation" option to elgg_get_annotations(). |
|
371 | + * This "calculation" option is applied to each entity's set of |
|
372 | + * annotations and is selected as annotation_calculation for that row. |
|
373 | + * See the docs for elgg_get_annotations() for proper use of the |
|
374 | + * "annotation_calculation" option. |
|
375 | + * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'. |
|
376 | + * 'annotation_names' => The names of annotations on the entity. |
|
377 | + * 'annotation_values' => The values of annotations on the entity. |
|
378 | + * |
|
379 | + * 'metadata_names' => The name of metadata on the entity. |
|
380 | + * 'metadata_values' => The value of metadata on the entitiy. |
|
381 | + * 'callback' => Callback function to pass each row through. |
|
382 | + * @tip This function is different from other ege* functions, |
|
383 | + * as it uses a metastring-based getter function { @link elgg_get_annotations() }, |
|
384 | + * therefore the callback function should be a derivative of { @link entity_row_to_elggstar() } |
|
385 | + * and not of { @link row_to_annotation() } |
|
386 | + * |
|
387 | + * @return \ElggEntity[]|int An array or a count of entities |
|
388 | + * @see elgg_get_annotations() |
|
389 | + * @see elgg_get_entities_from_annotations() |
|
390 | + */ |
|
391 | + function getEntitiesFromCalculation($options) { |
|
392 | 392 | |
393 | - if (isset($options['count']) && $options['count']) { |
|
394 | - return elgg_get_entities_from_annotations($options); |
|
395 | - } |
|
393 | + if (isset($options['count']) && $options['count']) { |
|
394 | + return elgg_get_entities_from_annotations($options); |
|
395 | + } |
|
396 | 396 | |
397 | - $db_prefix = $this->db->prefix; |
|
398 | - $defaults = [ |
|
399 | - 'calculation' => 'sum', |
|
400 | - 'order_by' => 'annotation_calculation desc' |
|
401 | - ]; |
|
397 | + $db_prefix = $this->db->prefix; |
|
398 | + $defaults = [ |
|
399 | + 'calculation' => 'sum', |
|
400 | + 'order_by' => 'annotation_calculation desc' |
|
401 | + ]; |
|
402 | 402 | |
403 | - $options = array_merge($defaults, $options); |
|
403 | + $options = array_merge($defaults, $options); |
|
404 | 404 | |
405 | - $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); |
|
405 | + $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); |
|
406 | 406 | |
407 | - // you must cast this as an int or it sorts wrong. |
|
408 | - $options['selects'][] = 'e.*'; |
|
409 | - $options['selects'][] = "$function(CAST(n_table.value AS signed)) AS annotation_calculation"; |
|
407 | + // you must cast this as an int or it sorts wrong. |
|
408 | + $options['selects'][] = 'e.*'; |
|
409 | + $options['selects'][] = "$function(CAST(n_table.value AS signed)) AS annotation_calculation"; |
|
410 | 410 | |
411 | - // don't need access control because it's taken care of by elgg_get_annotations. |
|
412 | - $options['group_by'] = 'n_table.entity_guid'; |
|
411 | + // don't need access control because it's taken care of by elgg_get_annotations. |
|
412 | + $options['group_by'] = 'n_table.entity_guid'; |
|
413 | 413 | |
414 | - // do not default to a callback function used in elgg_get_annotation() |
|
415 | - if (!isset($options['callback'])) { |
|
416 | - $options['callback'] = 'entity_row_to_elggstar'; |
|
417 | - } |
|
414 | + // do not default to a callback function used in elgg_get_annotation() |
|
415 | + if (!isset($options['callback'])) { |
|
416 | + $options['callback'] = 'entity_row_to_elggstar'; |
|
417 | + } |
|
418 | 418 | |
419 | - return elgg_get_annotations($options); |
|
420 | - } |
|
421 | - |
|
422 | - /** |
|
423 | - * Check to see if a user has already created an annotation on an object |
|
424 | - * |
|
425 | - * @param int $entity_guid Entity guid |
|
426 | - * @param string $annotation_type Type of annotation |
|
427 | - * @param int $owner_guid Defaults to logged in user. |
|
428 | - * |
|
429 | - * @return bool |
|
430 | - */ |
|
431 | - function exists($entity_guid, $annotation_type, $owner_guid = null) { |
|
432 | - |
|
433 | - if (!$owner_guid && !($owner_guid = $this->session->getLoggedInUserGuid())) { |
|
434 | - return false; |
|
435 | - } |
|
419 | + return elgg_get_annotations($options); |
|
420 | + } |
|
421 | + |
|
422 | + /** |
|
423 | + * Check to see if a user has already created an annotation on an object |
|
424 | + * |
|
425 | + * @param int $entity_guid Entity guid |
|
426 | + * @param string $annotation_type Type of annotation |
|
427 | + * @param int $owner_guid Defaults to logged in user. |
|
428 | + * |
|
429 | + * @return bool |
|
430 | + */ |
|
431 | + function exists($entity_guid, $annotation_type, $owner_guid = null) { |
|
432 | + |
|
433 | + if (!$owner_guid && !($owner_guid = $this->session->getLoggedInUserGuid())) { |
|
434 | + return false; |
|
435 | + } |
|
436 | 436 | |
437 | - $sql = "SELECT id FROM {$this->db->prefix}annotations |
|
437 | + $sql = "SELECT id FROM {$this->db->prefix}annotations |
|
438 | 438 | WHERE owner_guid = :owner_guid |
439 | 439 | AND entity_guid = :entity_guid |
440 | 440 | AND name = :annotation_type"; |
441 | 441 | |
442 | - $result = $this->db->getDataRow($sql, null, [ |
|
443 | - ':owner_guid' => (int) $owner_guid, |
|
444 | - ':entity_guid' => (int) $entity_guid, |
|
445 | - ':annotation_type' => $annotation_type, |
|
446 | - ]); |
|
447 | - |
|
448 | - return (bool) $result; |
|
449 | - } |
|
442 | + $result = $this->db->getDataRow($sql, null, [ |
|
443 | + ':owner_guid' => (int) $owner_guid, |
|
444 | + ':entity_guid' => (int) $entity_guid, |
|
445 | + ':annotation_type' => $annotation_type, |
|
446 | + ]); |
|
447 | + |
|
448 | + return (bool) $result; |
|
449 | + } |
|
450 | 450 | } |
@@ -43,260 +43,260 @@ discard block |
||
43 | 43 | * @access private |
44 | 44 | */ |
45 | 45 | function _elgg_get_metastring_based_objects($options) { |
46 | - $options = _elgg_normalize_metastrings_options($options); |
|
47 | - |
|
48 | - switch ($options['metastring_type']) { |
|
49 | - case 'metadata': |
|
50 | - $type = 'metadata'; |
|
51 | - $callback = 'row_to_elggmetadata'; |
|
52 | - break; |
|
53 | - |
|
54 | - case 'annotations': |
|
55 | - case 'annotation': |
|
56 | - $type = 'annotations'; |
|
57 | - $callback = 'row_to_elggannotation'; |
|
58 | - break; |
|
59 | - |
|
60 | - default: |
|
61 | - return false; |
|
62 | - } |
|
63 | - |
|
64 | - $defaults = [ |
|
65 | - // entities |
|
66 | - 'types' => ELGG_ENTITIES_ANY_VALUE, |
|
67 | - 'subtypes' => ELGG_ENTITIES_ANY_VALUE, |
|
68 | - 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
69 | - |
|
70 | - 'guids' => ELGG_ENTITIES_ANY_VALUE, |
|
71 | - 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
72 | - 'container_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
73 | - |
|
74 | - 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
75 | - 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
76 | - 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
77 | - 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
78 | - |
|
79 | - // options are normalized to the plural in case we ever add support for them. |
|
80 | - 'metastring_names' => ELGG_ENTITIES_ANY_VALUE, |
|
81 | - 'metastring_values' => ELGG_ENTITIES_ANY_VALUE, |
|
82 | - |
|
83 | - 'metastring_case_sensitive' => true, |
|
84 | - 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE, |
|
85 | - |
|
86 | - 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
87 | - 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
88 | - |
|
89 | - 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
46 | + $options = _elgg_normalize_metastrings_options($options); |
|
47 | + |
|
48 | + switch ($options['metastring_type']) { |
|
49 | + case 'metadata': |
|
50 | + $type = 'metadata'; |
|
51 | + $callback = 'row_to_elggmetadata'; |
|
52 | + break; |
|
53 | + |
|
54 | + case 'annotations': |
|
55 | + case 'annotation': |
|
56 | + $type = 'annotations'; |
|
57 | + $callback = 'row_to_elggannotation'; |
|
58 | + break; |
|
59 | + |
|
60 | + default: |
|
61 | + return false; |
|
62 | + } |
|
63 | + |
|
64 | + $defaults = [ |
|
65 | + // entities |
|
66 | + 'types' => ELGG_ENTITIES_ANY_VALUE, |
|
67 | + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, |
|
68 | + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
69 | + |
|
70 | + 'guids' => ELGG_ENTITIES_ANY_VALUE, |
|
71 | + 'owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
72 | + 'container_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
73 | + |
|
74 | + 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
75 | + 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
76 | + 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
77 | + 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
78 | + |
|
79 | + // options are normalized to the plural in case we ever add support for them. |
|
80 | + 'metastring_names' => ELGG_ENTITIES_ANY_VALUE, |
|
81 | + 'metastring_values' => ELGG_ENTITIES_ANY_VALUE, |
|
82 | + |
|
83 | + 'metastring_case_sensitive' => true, |
|
84 | + 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE, |
|
85 | + |
|
86 | + 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
87 | + 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
88 | + |
|
89 | + 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
90 | 90 | |
91 | - 'metastring_ids' => ELGG_ENTITIES_ANY_VALUE, |
|
92 | - |
|
93 | - // sql |
|
94 | - 'order_by' => 'n_table.time_created ASC, n_table.id ASC', |
|
95 | - 'limit' => elgg_get_config('default_limit'), |
|
96 | - 'offset' => 0, |
|
97 | - 'count' => false, |
|
98 | - 'selects' => [], |
|
99 | - 'wheres' => [], |
|
100 | - 'joins' => [], |
|
101 | - |
|
102 | - 'distinct' => true, |
|
103 | - 'preload_owners' => false, |
|
104 | - 'callback' => $callback, |
|
91 | + 'metastring_ids' => ELGG_ENTITIES_ANY_VALUE, |
|
92 | + |
|
93 | + // sql |
|
94 | + 'order_by' => 'n_table.time_created ASC, n_table.id ASC', |
|
95 | + 'limit' => elgg_get_config('default_limit'), |
|
96 | + 'offset' => 0, |
|
97 | + 'count' => false, |
|
98 | + 'selects' => [], |
|
99 | + 'wheres' => [], |
|
100 | + 'joins' => [], |
|
101 | + |
|
102 | + 'distinct' => true, |
|
103 | + 'preload_owners' => false, |
|
104 | + 'callback' => $callback, |
|
105 | 105 | |
106 | - 'batch' => false, |
|
107 | - 'batch_inc_offset' => true, |
|
108 | - 'batch_size' => 25, |
|
109 | - ]; |
|
110 | - |
|
111 | - $options = array_merge($defaults, $options); |
|
112 | - |
|
113 | - if ($options['batch'] && !$options['count']) { |
|
114 | - $batch_size = $options['batch_size']; |
|
115 | - $batch_inc_offset = $options['batch_inc_offset']; |
|
116 | - |
|
117 | - // clean batch keys from $options. |
|
118 | - unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']); |
|
119 | - |
|
120 | - return new \ElggBatch('_elgg_get_metastring_based_objects', $options, null, $batch_size, $batch_inc_offset); |
|
121 | - } |
|
122 | - |
|
123 | - // can't use helper function with type_subtype_pair because |
|
124 | - // it's already an array...just need to merge it |
|
125 | - if (isset($options['type_subtype_pair'])) { |
|
126 | - if (isset($options['type_subtype_pairs'])) { |
|
127 | - $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], |
|
128 | - $options['type_subtype_pair']); |
|
129 | - } else { |
|
130 | - $options['type_subtype_pairs'] = $options['type_subtype_pair']; |
|
131 | - } |
|
132 | - } |
|
133 | - |
|
134 | - $singulars = [ |
|
135 | - 'type', 'subtype', 'type_subtype_pair', |
|
136 | - 'guid', 'owner_guid', 'container_guid', |
|
137 | - 'metastring_name', 'metastring_value', |
|
138 | - 'metastring_owner_guid', 'metastring_id', |
|
139 | - 'select', 'where', 'join' |
|
140 | - ]; |
|
141 | - |
|
142 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
143 | - |
|
144 | - if (!$options) { |
|
145 | - return false; |
|
146 | - } |
|
147 | - |
|
148 | - $db_prefix = elgg_get_config('dbprefix'); |
|
149 | - |
|
150 | - // evaluate where clauses |
|
151 | - if (!is_array($options['wheres'])) { |
|
152 | - $options['wheres'] = [$options['wheres']]; |
|
153 | - } |
|
154 | - |
|
155 | - $wheres = $options['wheres']; |
|
156 | - |
|
157 | - // entities |
|
158 | - $wheres[] = _elgg_services()->entityTable->getEntityTypeSubtypeWhereSql('e', $options['types'], |
|
159 | - $options['subtypes'], $options['type_subtype_pairs']); |
|
160 | - |
|
161 | - $wheres[] = _elgg_get_guid_based_where_sql('e.guid', $options['guids']); |
|
162 | - $wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']); |
|
163 | - $wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']); |
|
164 | - |
|
165 | - $wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'], |
|
166 | - $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']); |
|
167 | - |
|
168 | - |
|
169 | - $wheres[] = _elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'], |
|
170 | - $options['metastring_created_time_lower'], null, null); |
|
171 | - |
|
172 | - $wheres[] = _elgg_get_guid_based_where_sql('n_table.owner_guid', |
|
173 | - $options['metastring_owner_guids']); |
|
174 | - |
|
175 | - // see if any functions failed |
|
176 | - // remove empty strings on successful functions |
|
177 | - foreach ($wheres as $i => $where) { |
|
178 | - if ($where === false) { |
|
179 | - return false; |
|
180 | - } elseif (empty($where)) { |
|
181 | - unset($wheres[$i]); |
|
182 | - } |
|
183 | - } |
|
184 | - |
|
185 | - // remove identical where clauses |
|
186 | - $wheres = array_unique($wheres); |
|
187 | - |
|
188 | - // evaluate join clauses |
|
189 | - if (!is_array($options['joins'])) { |
|
190 | - $options['joins'] = [$options['joins']]; |
|
191 | - } |
|
192 | - |
|
193 | - $joins = []; |
|
194 | - $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid"; |
|
195 | - |
|
196 | - // evaluate selects |
|
197 | - if (!is_array($options['selects'])) { |
|
198 | - $options['selects'] = [$options['selects']]; |
|
199 | - } |
|
200 | - |
|
201 | - $selects = $options['selects']; |
|
202 | - |
|
203 | - // add optional joins |
|
204 | - $joins = array_merge($joins, $options['joins']); |
|
205 | - |
|
206 | - // metastrings |
|
207 | - $metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'], |
|
208 | - $options['metastring_values'], null, $options['metastring_ids'], |
|
209 | - $options['metastring_case_sensitive']); |
|
210 | - |
|
211 | - if ($metastring_clauses) { |
|
212 | - $wheres = array_merge($wheres, $metastring_clauses['wheres']); |
|
213 | - $joins = array_merge($joins, $metastring_clauses['joins']); |
|
214 | - } else { |
|
215 | - $wheres[] = _elgg_get_access_where_sql([ |
|
216 | - 'table_alias' => 'n_table', |
|
217 | - 'guid_column' => 'entity_guid', |
|
218 | - ]); |
|
219 | - } |
|
220 | - |
|
221 | - $distinct = $options['distinct'] ? "DISTINCT " : ""; |
|
222 | - |
|
223 | - if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { |
|
224 | - $selects = array_unique($selects); |
|
225 | - // evalutate selects |
|
226 | - $select_str = ''; |
|
227 | - if ($selects) { |
|
228 | - foreach ($selects as $select) { |
|
229 | - $select_str .= ", $select"; |
|
230 | - } |
|
231 | - } |
|
232 | - |
|
233 | - $query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table"; |
|
234 | - } elseif ($options['count']) { |
|
235 | - // count is over the entities |
|
236 | - $query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table"; |
|
237 | - } else { |
|
238 | - $query = "SELECT {$options['metastring_calculation']}(n_table.value) as calculation FROM {$db_prefix}$type n_table"; |
|
239 | - } |
|
240 | - |
|
241 | - foreach ($joins as $i => $join) { |
|
242 | - if ($join === false) { |
|
243 | - return false; |
|
244 | - } elseif (empty($join)) { |
|
245 | - unset($joins[$i]); |
|
246 | - } |
|
247 | - } |
|
248 | - |
|
249 | - // remove identical join clauses |
|
250 | - $joins = array_unique($joins); |
|
251 | - |
|
252 | - // add joins |
|
253 | - foreach ($joins as $j) { |
|
254 | - $query .= " $j "; |
|
255 | - } |
|
256 | - |
|
257 | - // add wheres |
|
258 | - $query .= ' WHERE '; |
|
259 | - |
|
260 | - foreach ($wheres as $w) { |
|
261 | - $query .= " $w AND "; |
|
262 | - } |
|
263 | - |
|
264 | - // Add access controls |
|
265 | - $query .= _elgg_get_access_where_sql(['table_alias' => 'e']); |
|
266 | - |
|
267 | - // reverse order by |
|
268 | - if (isset($options['reverse_order_by']) && $options['reverse_order_by']) { |
|
269 | - $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']); |
|
270 | - } |
|
271 | - |
|
272 | - if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { |
|
273 | - if (isset($options['group_by'])) { |
|
274 | - $options['group_by'] = sanitise_string($options['group_by']); |
|
275 | - $query .= " GROUP BY {$options['group_by']}"; |
|
276 | - } |
|
277 | - |
|
278 | - if (isset($options['order_by']) && $options['order_by']) { |
|
279 | - $options['order_by'] = sanitise_string($options['order_by']); |
|
280 | - $query .= " ORDER BY {$options['order_by']}, n_table.id"; |
|
281 | - } |
|
282 | - |
|
283 | - if ($options['limit']) { |
|
284 | - $limit = sanitise_int($options['limit']); |
|
285 | - $offset = sanitise_int($options['offset'], false); |
|
286 | - $query .= " LIMIT $offset, $limit"; |
|
287 | - } |
|
288 | - |
|
289 | - $dt = get_data($query, $options['callback']); |
|
290 | - |
|
291 | - if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) { |
|
292 | - _elgg_services()->entityPreloader->preload($dt, ['owner_guid']); |
|
293 | - } |
|
294 | - |
|
295 | - return $dt; |
|
296 | - } else { |
|
297 | - $result = get_data_row($query); |
|
298 | - return $result->calculation; |
|
299 | - } |
|
106 | + 'batch' => false, |
|
107 | + 'batch_inc_offset' => true, |
|
108 | + 'batch_size' => 25, |
|
109 | + ]; |
|
110 | + |
|
111 | + $options = array_merge($defaults, $options); |
|
112 | + |
|
113 | + if ($options['batch'] && !$options['count']) { |
|
114 | + $batch_size = $options['batch_size']; |
|
115 | + $batch_inc_offset = $options['batch_inc_offset']; |
|
116 | + |
|
117 | + // clean batch keys from $options. |
|
118 | + unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']); |
|
119 | + |
|
120 | + return new \ElggBatch('_elgg_get_metastring_based_objects', $options, null, $batch_size, $batch_inc_offset); |
|
121 | + } |
|
122 | + |
|
123 | + // can't use helper function with type_subtype_pair because |
|
124 | + // it's already an array...just need to merge it |
|
125 | + if (isset($options['type_subtype_pair'])) { |
|
126 | + if (isset($options['type_subtype_pairs'])) { |
|
127 | + $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], |
|
128 | + $options['type_subtype_pair']); |
|
129 | + } else { |
|
130 | + $options['type_subtype_pairs'] = $options['type_subtype_pair']; |
|
131 | + } |
|
132 | + } |
|
133 | + |
|
134 | + $singulars = [ |
|
135 | + 'type', 'subtype', 'type_subtype_pair', |
|
136 | + 'guid', 'owner_guid', 'container_guid', |
|
137 | + 'metastring_name', 'metastring_value', |
|
138 | + 'metastring_owner_guid', 'metastring_id', |
|
139 | + 'select', 'where', 'join' |
|
140 | + ]; |
|
141 | + |
|
142 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
143 | + |
|
144 | + if (!$options) { |
|
145 | + return false; |
|
146 | + } |
|
147 | + |
|
148 | + $db_prefix = elgg_get_config('dbprefix'); |
|
149 | + |
|
150 | + // evaluate where clauses |
|
151 | + if (!is_array($options['wheres'])) { |
|
152 | + $options['wheres'] = [$options['wheres']]; |
|
153 | + } |
|
154 | + |
|
155 | + $wheres = $options['wheres']; |
|
156 | + |
|
157 | + // entities |
|
158 | + $wheres[] = _elgg_services()->entityTable->getEntityTypeSubtypeWhereSql('e', $options['types'], |
|
159 | + $options['subtypes'], $options['type_subtype_pairs']); |
|
160 | + |
|
161 | + $wheres[] = _elgg_get_guid_based_where_sql('e.guid', $options['guids']); |
|
162 | + $wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']); |
|
163 | + $wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']); |
|
164 | + |
|
165 | + $wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'], |
|
166 | + $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']); |
|
167 | + |
|
168 | + |
|
169 | + $wheres[] = _elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'], |
|
170 | + $options['metastring_created_time_lower'], null, null); |
|
171 | + |
|
172 | + $wheres[] = _elgg_get_guid_based_where_sql('n_table.owner_guid', |
|
173 | + $options['metastring_owner_guids']); |
|
174 | + |
|
175 | + // see if any functions failed |
|
176 | + // remove empty strings on successful functions |
|
177 | + foreach ($wheres as $i => $where) { |
|
178 | + if ($where === false) { |
|
179 | + return false; |
|
180 | + } elseif (empty($where)) { |
|
181 | + unset($wheres[$i]); |
|
182 | + } |
|
183 | + } |
|
184 | + |
|
185 | + // remove identical where clauses |
|
186 | + $wheres = array_unique($wheres); |
|
187 | + |
|
188 | + // evaluate join clauses |
|
189 | + if (!is_array($options['joins'])) { |
|
190 | + $options['joins'] = [$options['joins']]; |
|
191 | + } |
|
192 | + |
|
193 | + $joins = []; |
|
194 | + $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid"; |
|
195 | + |
|
196 | + // evaluate selects |
|
197 | + if (!is_array($options['selects'])) { |
|
198 | + $options['selects'] = [$options['selects']]; |
|
199 | + } |
|
200 | + |
|
201 | + $selects = $options['selects']; |
|
202 | + |
|
203 | + // add optional joins |
|
204 | + $joins = array_merge($joins, $options['joins']); |
|
205 | + |
|
206 | + // metastrings |
|
207 | + $metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'], |
|
208 | + $options['metastring_values'], null, $options['metastring_ids'], |
|
209 | + $options['metastring_case_sensitive']); |
|
210 | + |
|
211 | + if ($metastring_clauses) { |
|
212 | + $wheres = array_merge($wheres, $metastring_clauses['wheres']); |
|
213 | + $joins = array_merge($joins, $metastring_clauses['joins']); |
|
214 | + } else { |
|
215 | + $wheres[] = _elgg_get_access_where_sql([ |
|
216 | + 'table_alias' => 'n_table', |
|
217 | + 'guid_column' => 'entity_guid', |
|
218 | + ]); |
|
219 | + } |
|
220 | + |
|
221 | + $distinct = $options['distinct'] ? "DISTINCT " : ""; |
|
222 | + |
|
223 | + if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { |
|
224 | + $selects = array_unique($selects); |
|
225 | + // evalutate selects |
|
226 | + $select_str = ''; |
|
227 | + if ($selects) { |
|
228 | + foreach ($selects as $select) { |
|
229 | + $select_str .= ", $select"; |
|
230 | + } |
|
231 | + } |
|
232 | + |
|
233 | + $query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table"; |
|
234 | + } elseif ($options['count']) { |
|
235 | + // count is over the entities |
|
236 | + $query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table"; |
|
237 | + } else { |
|
238 | + $query = "SELECT {$options['metastring_calculation']}(n_table.value) as calculation FROM {$db_prefix}$type n_table"; |
|
239 | + } |
|
240 | + |
|
241 | + foreach ($joins as $i => $join) { |
|
242 | + if ($join === false) { |
|
243 | + return false; |
|
244 | + } elseif (empty($join)) { |
|
245 | + unset($joins[$i]); |
|
246 | + } |
|
247 | + } |
|
248 | + |
|
249 | + // remove identical join clauses |
|
250 | + $joins = array_unique($joins); |
|
251 | + |
|
252 | + // add joins |
|
253 | + foreach ($joins as $j) { |
|
254 | + $query .= " $j "; |
|
255 | + } |
|
256 | + |
|
257 | + // add wheres |
|
258 | + $query .= ' WHERE '; |
|
259 | + |
|
260 | + foreach ($wheres as $w) { |
|
261 | + $query .= " $w AND "; |
|
262 | + } |
|
263 | + |
|
264 | + // Add access controls |
|
265 | + $query .= _elgg_get_access_where_sql(['table_alias' => 'e']); |
|
266 | + |
|
267 | + // reverse order by |
|
268 | + if (isset($options['reverse_order_by']) && $options['reverse_order_by']) { |
|
269 | + $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']); |
|
270 | + } |
|
271 | + |
|
272 | + if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) { |
|
273 | + if (isset($options['group_by'])) { |
|
274 | + $options['group_by'] = sanitise_string($options['group_by']); |
|
275 | + $query .= " GROUP BY {$options['group_by']}"; |
|
276 | + } |
|
277 | + |
|
278 | + if (isset($options['order_by']) && $options['order_by']) { |
|
279 | + $options['order_by'] = sanitise_string($options['order_by']); |
|
280 | + $query .= " ORDER BY {$options['order_by']}, n_table.id"; |
|
281 | + } |
|
282 | + |
|
283 | + if ($options['limit']) { |
|
284 | + $limit = sanitise_int($options['limit']); |
|
285 | + $offset = sanitise_int($options['offset'], false); |
|
286 | + $query .= " LIMIT $offset, $limit"; |
|
287 | + } |
|
288 | + |
|
289 | + $dt = get_data($query, $options['callback']); |
|
290 | + |
|
291 | + if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) { |
|
292 | + _elgg_services()->entityPreloader->preload($dt, ['owner_guid']); |
|
293 | + } |
|
294 | + |
|
295 | + return $dt; |
|
296 | + } else { |
|
297 | + $result = get_data_row($query); |
|
298 | + return $result->calculation; |
|
299 | + } |
|
300 | 300 | } |
301 | 301 | |
302 | 302 | /** |
@@ -315,99 +315,99 @@ discard block |
||
315 | 315 | * @access private |
316 | 316 | */ |
317 | 317 | function _elgg_get_metastring_sql($table, $names = null, $values = null, |
318 | - $pairs = null, $ids = null, $case_sensitive = false) { |
|
319 | - |
|
320 | - if ((!$names && $names !== 0) |
|
321 | - && (!$values && $values !== 0) |
|
322 | - && !$ids |
|
323 | - && (!$pairs && $pairs !== 0)) { |
|
324 | - return []; |
|
325 | - } |
|
326 | - |
|
327 | - $db_prefix = elgg_get_config('dbprefix'); |
|
328 | - |
|
329 | - // binary forces byte-to-byte comparision of strings, making |
|
330 | - // it case- and diacritical-mark- sensitive. |
|
331 | - // only supported on values. |
|
332 | - $binary = ($case_sensitive) ? ' BINARY ' : ''; |
|
333 | - |
|
334 | - $return = [ |
|
335 | - 'joins' => [], |
|
336 | - 'wheres' => [] |
|
337 | - ]; |
|
338 | - |
|
339 | - $wheres = []; |
|
340 | - |
|
341 | - // get names wheres and joins |
|
342 | - $names_where = ''; |
|
343 | - if ($names !== null) { |
|
344 | - if (!is_array($names)) { |
|
345 | - $names = [$names]; |
|
346 | - } |
|
347 | - |
|
348 | - $sanitised_names = []; |
|
349 | - foreach ($names as $name) { |
|
350 | - // normalise to 0. |
|
351 | - if (!$name) { |
|
352 | - $name = '0'; |
|
353 | - } |
|
354 | - $sanitised_names[] = '\'' . sanitise_string($name) . '\''; |
|
355 | - } |
|
356 | - |
|
357 | - if ($names_str = implode(',', $sanitised_names)) { |
|
358 | - $names_where = "($table.name IN ($names_str))"; |
|
359 | - } |
|
360 | - } |
|
361 | - |
|
362 | - // get values wheres and joins |
|
363 | - $values_where = ''; |
|
364 | - if ($values !== null) { |
|
365 | - if (!is_array($values)) { |
|
366 | - $values = [$values]; |
|
367 | - } |
|
368 | - |
|
369 | - $sanitised_values = []; |
|
370 | - foreach ($values as $value) { |
|
371 | - // normalize to 0 |
|
372 | - if (!$value) { |
|
373 | - $value = 0; |
|
374 | - } |
|
375 | - $sanitised_values[] = '\'' . sanitise_string($value) . '\''; |
|
376 | - } |
|
377 | - |
|
378 | - if ($values_str = implode(',', $sanitised_values)) { |
|
379 | - $values_where = "({$binary}$table.value IN ($values_str))"; |
|
380 | - } |
|
381 | - } |
|
318 | + $pairs = null, $ids = null, $case_sensitive = false) { |
|
319 | + |
|
320 | + if ((!$names && $names !== 0) |
|
321 | + && (!$values && $values !== 0) |
|
322 | + && !$ids |
|
323 | + && (!$pairs && $pairs !== 0)) { |
|
324 | + return []; |
|
325 | + } |
|
326 | + |
|
327 | + $db_prefix = elgg_get_config('dbprefix'); |
|
328 | + |
|
329 | + // binary forces byte-to-byte comparision of strings, making |
|
330 | + // it case- and diacritical-mark- sensitive. |
|
331 | + // only supported on values. |
|
332 | + $binary = ($case_sensitive) ? ' BINARY ' : ''; |
|
333 | + |
|
334 | + $return = [ |
|
335 | + 'joins' => [], |
|
336 | + 'wheres' => [] |
|
337 | + ]; |
|
338 | + |
|
339 | + $wheres = []; |
|
340 | + |
|
341 | + // get names wheres and joins |
|
342 | + $names_where = ''; |
|
343 | + if ($names !== null) { |
|
344 | + if (!is_array($names)) { |
|
345 | + $names = [$names]; |
|
346 | + } |
|
347 | + |
|
348 | + $sanitised_names = []; |
|
349 | + foreach ($names as $name) { |
|
350 | + // normalise to 0. |
|
351 | + if (!$name) { |
|
352 | + $name = '0'; |
|
353 | + } |
|
354 | + $sanitised_names[] = '\'' . sanitise_string($name) . '\''; |
|
355 | + } |
|
356 | + |
|
357 | + if ($names_str = implode(',', $sanitised_names)) { |
|
358 | + $names_where = "($table.name IN ($names_str))"; |
|
359 | + } |
|
360 | + } |
|
361 | + |
|
362 | + // get values wheres and joins |
|
363 | + $values_where = ''; |
|
364 | + if ($values !== null) { |
|
365 | + if (!is_array($values)) { |
|
366 | + $values = [$values]; |
|
367 | + } |
|
368 | + |
|
369 | + $sanitised_values = []; |
|
370 | + foreach ($values as $value) { |
|
371 | + // normalize to 0 |
|
372 | + if (!$value) { |
|
373 | + $value = 0; |
|
374 | + } |
|
375 | + $sanitised_values[] = '\'' . sanitise_string($value) . '\''; |
|
376 | + } |
|
377 | + |
|
378 | + if ($values_str = implode(',', $sanitised_values)) { |
|
379 | + $values_where = "({$binary}$table.value IN ($values_str))"; |
|
380 | + } |
|
381 | + } |
|
382 | 382 | |
383 | - if ($ids !== null) { |
|
384 | - if (!is_array($ids)) { |
|
385 | - $ids = [$ids]; |
|
386 | - } |
|
387 | - $ids_str = implode(',', $ids); |
|
388 | - if ($ids_str) { |
|
389 | - $wheres[] = "n_table.id IN ($ids_str)"; |
|
390 | - } |
|
391 | - } |
|
392 | - |
|
393 | - if ($names_where && $values_where) { |
|
394 | - $wheres[] = "($names_where AND $values_where)"; |
|
395 | - } elseif ($names_where) { |
|
396 | - $wheres[] = $names_where; |
|
397 | - } elseif ($values_where) { |
|
398 | - $wheres[] = $values_where; |
|
399 | - } |
|
400 | - |
|
401 | - $wheres[] = _elgg_get_access_where_sql([ |
|
402 | - 'table_alias' => $table, |
|
403 | - 'guid_column' => 'entity_guid', |
|
404 | - ]); |
|
405 | - |
|
406 | - if ($where = implode(' AND ', $wheres)) { |
|
407 | - $return['wheres'][] = "($where)"; |
|
408 | - } |
|
409 | - |
|
410 | - return $return; |
|
383 | + if ($ids !== null) { |
|
384 | + if (!is_array($ids)) { |
|
385 | + $ids = [$ids]; |
|
386 | + } |
|
387 | + $ids_str = implode(',', $ids); |
|
388 | + if ($ids_str) { |
|
389 | + $wheres[] = "n_table.id IN ($ids_str)"; |
|
390 | + } |
|
391 | + } |
|
392 | + |
|
393 | + if ($names_where && $values_where) { |
|
394 | + $wheres[] = "($names_where AND $values_where)"; |
|
395 | + } elseif ($names_where) { |
|
396 | + $wheres[] = $names_where; |
|
397 | + } elseif ($values_where) { |
|
398 | + $wheres[] = $values_where; |
|
399 | + } |
|
400 | + |
|
401 | + $wheres[] = _elgg_get_access_where_sql([ |
|
402 | + 'table_alias' => $table, |
|
403 | + 'guid_column' => 'entity_guid', |
|
404 | + ]); |
|
405 | + |
|
406 | + if ($where = implode(' AND ', $wheres)) { |
|
407 | + $return['wheres'][] = "($where)"; |
|
408 | + } |
|
409 | + |
|
410 | + return $return; |
|
411 | 411 | } |
412 | 412 | |
413 | 413 | /** |
@@ -419,41 +419,41 @@ discard block |
||
419 | 419 | */ |
420 | 420 | function _elgg_normalize_metastrings_options(array $options = []) { |
421 | 421 | |
422 | - // support either metastrings_type or metastring_type |
|
423 | - // because I've made this mistake many times and hunting it down is a pain... |
|
424 | - $type = elgg_extract('metastring_type', $options, null); |
|
425 | - $type = elgg_extract('metastrings_type', $options, $type); |
|
426 | - |
|
427 | - $options['metastring_type'] = $type; |
|
428 | - |
|
429 | - // support annotation_ and annotations_ because they're way too easy to confuse |
|
430 | - $prefixes = ['metadata_', 'annotation_', 'annotations_']; |
|
431 | - |
|
432 | - // map the metadata_* options to metastring_* options |
|
433 | - $map = [ |
|
434 | - 'names' => 'metastring_names', |
|
435 | - 'values' => 'metastring_values', |
|
436 | - 'case_sensitive' => 'metastring_case_sensitive', |
|
437 | - 'owner_guids' => 'metastring_owner_guids', |
|
438 | - 'created_time_lower' => 'metastring_created_time_lower', |
|
439 | - 'created_time_upper' => 'metastring_created_time_upper', |
|
440 | - 'calculation' => 'metastring_calculation', |
|
441 | - 'ids' => 'metastring_ids', |
|
442 | - ]; |
|
443 | - |
|
444 | - foreach ($prefixes as $prefix) { |
|
445 | - $singulars = ["{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id"]; |
|
446 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
447 | - |
|
448 | - foreach ($map as $specific => $normalized) { |
|
449 | - $key = $prefix . $specific; |
|
450 | - if (isset($options[$key])) { |
|
451 | - $options[$normalized] = $options[$key]; |
|
452 | - } |
|
453 | - } |
|
454 | - } |
|
455 | - |
|
456 | - return $options; |
|
422 | + // support either metastrings_type or metastring_type |
|
423 | + // because I've made this mistake many times and hunting it down is a pain... |
|
424 | + $type = elgg_extract('metastring_type', $options, null); |
|
425 | + $type = elgg_extract('metastrings_type', $options, $type); |
|
426 | + |
|
427 | + $options['metastring_type'] = $type; |
|
428 | + |
|
429 | + // support annotation_ and annotations_ because they're way too easy to confuse |
|
430 | + $prefixes = ['metadata_', 'annotation_', 'annotations_']; |
|
431 | + |
|
432 | + // map the metadata_* options to metastring_* options |
|
433 | + $map = [ |
|
434 | + 'names' => 'metastring_names', |
|
435 | + 'values' => 'metastring_values', |
|
436 | + 'case_sensitive' => 'metastring_case_sensitive', |
|
437 | + 'owner_guids' => 'metastring_owner_guids', |
|
438 | + 'created_time_lower' => 'metastring_created_time_lower', |
|
439 | + 'created_time_upper' => 'metastring_created_time_upper', |
|
440 | + 'calculation' => 'metastring_calculation', |
|
441 | + 'ids' => 'metastring_ids', |
|
442 | + ]; |
|
443 | + |
|
444 | + foreach ($prefixes as $prefix) { |
|
445 | + $singulars = ["{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id"]; |
|
446 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
447 | + |
|
448 | + foreach ($map as $specific => $normalized) { |
|
449 | + $key = $prefix . $specific; |
|
450 | + if (isset($options[$key])) { |
|
451 | + $options[$normalized] = $options[$key]; |
|
452 | + } |
|
453 | + } |
|
454 | + } |
|
455 | + |
|
456 | + return $options; |
|
457 | 457 | } |
458 | 458 | |
459 | 459 | /** |
@@ -471,48 +471,48 @@ discard block |
||
471 | 471 | * @access private |
472 | 472 | */ |
473 | 473 | function _elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type) { |
474 | - $id = (int) $id; |
|
475 | - $db_prefix = elgg_get_config('dbprefix'); |
|
476 | - |
|
477 | - $object = _elgg_get_metastring_based_object_from_id($id, $type); |
|
478 | - |
|
479 | - switch ($type) { |
|
480 | - case 'annotation': |
|
481 | - case 'annotations': |
|
482 | - $type = 'annotation'; |
|
483 | - $table = "{$db_prefix}annotations"; |
|
484 | - break; |
|
485 | - |
|
486 | - case 'metadata': |
|
487 | - $table = "{$db_prefix}metadata"; |
|
488 | - break; |
|
489 | - } |
|
490 | - |
|
491 | - if ($enabled === 'yes' || $enabled === 1 || $enabled === true) { |
|
492 | - $enabled = 'yes'; |
|
493 | - $event = 'enable'; |
|
494 | - } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) { |
|
495 | - $enabled = 'no'; |
|
496 | - $event = 'disable'; |
|
497 | - } else { |
|
498 | - return false; |
|
499 | - } |
|
500 | - |
|
501 | - $return = false; |
|
502 | - |
|
503 | - if ($object) { |
|
504 | - // don't set it if it's already set. |
|
505 | - if ($object->enabled == $enabled) { |
|
506 | - $return = false; |
|
507 | - } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) { |
|
508 | - $return = update_data("UPDATE $table SET enabled = :enabled where id = :id", [ |
|
509 | - ':enabled' => $enabled, |
|
510 | - ':id' => $id, |
|
511 | - ]); |
|
512 | - } |
|
513 | - } |
|
514 | - |
|
515 | - return $return; |
|
474 | + $id = (int) $id; |
|
475 | + $db_prefix = elgg_get_config('dbprefix'); |
|
476 | + |
|
477 | + $object = _elgg_get_metastring_based_object_from_id($id, $type); |
|
478 | + |
|
479 | + switch ($type) { |
|
480 | + case 'annotation': |
|
481 | + case 'annotations': |
|
482 | + $type = 'annotation'; |
|
483 | + $table = "{$db_prefix}annotations"; |
|
484 | + break; |
|
485 | + |
|
486 | + case 'metadata': |
|
487 | + $table = "{$db_prefix}metadata"; |
|
488 | + break; |
|
489 | + } |
|
490 | + |
|
491 | + if ($enabled === 'yes' || $enabled === 1 || $enabled === true) { |
|
492 | + $enabled = 'yes'; |
|
493 | + $event = 'enable'; |
|
494 | + } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) { |
|
495 | + $enabled = 'no'; |
|
496 | + $event = 'disable'; |
|
497 | + } else { |
|
498 | + return false; |
|
499 | + } |
|
500 | + |
|
501 | + $return = false; |
|
502 | + |
|
503 | + if ($object) { |
|
504 | + // don't set it if it's already set. |
|
505 | + if ($object->enabled == $enabled) { |
|
506 | + $return = false; |
|
507 | + } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) { |
|
508 | + $return = update_data("UPDATE $table SET enabled = :enabled where id = :id", [ |
|
509 | + ':enabled' => $enabled, |
|
510 | + ':id' => $id, |
|
511 | + ]); |
|
512 | + } |
|
513 | + } |
|
514 | + |
|
515 | + return $return; |
|
516 | 516 | } |
517 | 517 | |
518 | 518 | /** |
@@ -531,12 +531,12 @@ discard block |
||
531 | 531 | * @access private |
532 | 532 | */ |
533 | 533 | function _elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) { |
534 | - if (!$options || !is_array($options)) { |
|
535 | - return false; |
|
536 | - } |
|
534 | + if (!$options || !is_array($options)) { |
|
535 | + return false; |
|
536 | + } |
|
537 | 537 | |
538 | - $batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset); |
|
539 | - return $batch->callbackResult; |
|
538 | + $batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset); |
|
539 | + return $batch->callbackResult; |
|
540 | 540 | } |
541 | 541 | |
542 | 542 | /** |
@@ -548,23 +548,23 @@ discard block |
||
548 | 548 | * @access private |
549 | 549 | */ |
550 | 550 | function _elgg_get_metastring_based_object_from_id($id, $type) { |
551 | - $id = (int) $id; |
|
552 | - if (!$id) { |
|
553 | - return false; |
|
554 | - } |
|
551 | + $id = (int) $id; |
|
552 | + if (!$id) { |
|
553 | + return false; |
|
554 | + } |
|
555 | 555 | |
556 | - $options = [ |
|
557 | - 'metastring_type' => $type, |
|
558 | - 'metastring_id' => $id, |
|
559 | - ]; |
|
556 | + $options = [ |
|
557 | + 'metastring_type' => $type, |
|
558 | + 'metastring_id' => $id, |
|
559 | + ]; |
|
560 | 560 | |
561 | - $obj = _elgg_get_metastring_based_objects($options); |
|
561 | + $obj = _elgg_get_metastring_based_objects($options); |
|
562 | 562 | |
563 | - if ($obj && count($obj) == 1) { |
|
564 | - return $obj[0]; |
|
565 | - } |
|
563 | + if ($obj && count($obj) == 1) { |
|
564 | + return $obj[0]; |
|
565 | + } |
|
566 | 566 | |
567 | - return false; |
|
567 | + return false; |
|
568 | 568 | } |
569 | 569 | |
570 | 570 | /** |
@@ -576,38 +576,38 @@ discard block |
||
576 | 576 | * @access private |
577 | 577 | */ |
578 | 578 | function _elgg_delete_metastring_based_object_by_id($id, $type) { |
579 | - $id = (int) $id; |
|
580 | - $db_prefix = elgg_get_config('dbprefix'); |
|
581 | - |
|
582 | - switch ($type) { |
|
583 | - case 'annotations': |
|
584 | - case 'annotation': |
|
585 | - $table = $db_prefix . 'annotations'; |
|
586 | - $type = 'annotation'; |
|
587 | - break; |
|
588 | - |
|
589 | - case 'metadata': |
|
590 | - $table = $db_prefix . 'metadata'; |
|
591 | - $type = 'metadata'; |
|
592 | - break; |
|
593 | - |
|
594 | - default: |
|
595 | - return false; |
|
596 | - } |
|
597 | - |
|
598 | - $obj = _elgg_get_metastring_based_object_from_id($id, $type); |
|
599 | - |
|
600 | - if ($obj) { |
|
601 | - if ($obj->canEdit()) { |
|
602 | - if (elgg_trigger_event('delete', $type, $obj)) { |
|
603 | - return (bool) delete_data("DELETE FROM $table WHERE id = :id", [ |
|
604 | - ':id' => $id, |
|
605 | - ]); |
|
606 | - } |
|
607 | - } |
|
608 | - } |
|
609 | - |
|
610 | - return false; |
|
579 | + $id = (int) $id; |
|
580 | + $db_prefix = elgg_get_config('dbprefix'); |
|
581 | + |
|
582 | + switch ($type) { |
|
583 | + case 'annotations': |
|
584 | + case 'annotation': |
|
585 | + $table = $db_prefix . 'annotations'; |
|
586 | + $type = 'annotation'; |
|
587 | + break; |
|
588 | + |
|
589 | + case 'metadata': |
|
590 | + $table = $db_prefix . 'metadata'; |
|
591 | + $type = 'metadata'; |
|
592 | + break; |
|
593 | + |
|
594 | + default: |
|
595 | + return false; |
|
596 | + } |
|
597 | + |
|
598 | + $obj = _elgg_get_metastring_based_object_from_id($id, $type); |
|
599 | + |
|
600 | + if ($obj) { |
|
601 | + if ($obj->canEdit()) { |
|
602 | + if (elgg_trigger_event('delete', $type, $obj)) { |
|
603 | + return (bool) delete_data("DELETE FROM $table WHERE id = :id", [ |
|
604 | + ':id' => $id, |
|
605 | + ]); |
|
606 | + } |
|
607 | + } |
|
608 | + } |
|
609 | + |
|
610 | + return false; |
|
611 | 611 | } |
612 | 612 | |
613 | 613 | /** |
@@ -620,54 +620,54 @@ discard block |
||
620 | 620 | * @access private |
621 | 621 | */ |
622 | 622 | function _elgg_entities_get_metastrings_options($type, $options) { |
623 | - $valid_types = ['metadata', 'annotation']; |
|
624 | - if (!in_array($type, $valid_types)) { |
|
625 | - return false; |
|
626 | - } |
|
627 | - |
|
628 | - // the options for annotations are singular (annotation_name) but the table |
|
629 | - // is plural (elgg_annotations) so rewrite for the table name. |
|
630 | - $n_table = ($type == 'annotation') ? 'annotations' : $type; |
|
631 | - |
|
632 | - $singulars = ["{$type}_name", "{$type}_value", |
|
633 | - "{$type}_name_value_pair", "{$type}_owner_guid"]; |
|
634 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
635 | - |
|
636 | - $clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"], |
|
637 | - $options["{$type}_values"], $options["{$type}_name_value_pairs"], |
|
638 | - $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"], |
|
639 | - $options["order_by_{$type}"], $options["{$type}_owner_guids"]); |
|
640 | - |
|
641 | - if ($clauses) { |
|
642 | - // merge wheres to pass to elgg_get_entities() |
|
643 | - if (isset($options['wheres']) && !is_array($options['wheres'])) { |
|
644 | - $options['wheres'] = [$options['wheres']]; |
|
645 | - } elseif (!isset($options['wheres'])) { |
|
646 | - $options['wheres'] = []; |
|
647 | - } |
|
648 | - |
|
649 | - $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); |
|
650 | - |
|
651 | - // merge joins to pass to elgg_get_entities() |
|
652 | - if (isset($options['joins']) && !is_array($options['joins'])) { |
|
653 | - $options['joins'] = [$options['joins']]; |
|
654 | - } elseif (!isset($options['joins'])) { |
|
655 | - $options['joins'] = []; |
|
656 | - } |
|
657 | - |
|
658 | - $options['joins'] = array_merge($options['joins'], $clauses['joins']); |
|
659 | - |
|
660 | - if ($clauses['orders']) { |
|
661 | - $order_by_metadata = implode(", ", $clauses['orders']); |
|
662 | - if (isset($options['order_by']) && $options['order_by']) { |
|
663 | - $options['order_by'] = "$order_by_metadata, {$options['order_by']}"; |
|
664 | - } else { |
|
665 | - $options['order_by'] = "$order_by_metadata, e.time_created DESC"; |
|
666 | - } |
|
667 | - } |
|
668 | - } |
|
669 | - |
|
670 | - return $options; |
|
623 | + $valid_types = ['metadata', 'annotation']; |
|
624 | + if (!in_array($type, $valid_types)) { |
|
625 | + return false; |
|
626 | + } |
|
627 | + |
|
628 | + // the options for annotations are singular (annotation_name) but the table |
|
629 | + // is plural (elgg_annotations) so rewrite for the table name. |
|
630 | + $n_table = ($type == 'annotation') ? 'annotations' : $type; |
|
631 | + |
|
632 | + $singulars = ["{$type}_name", "{$type}_value", |
|
633 | + "{$type}_name_value_pair", "{$type}_owner_guid"]; |
|
634 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
635 | + |
|
636 | + $clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"], |
|
637 | + $options["{$type}_values"], $options["{$type}_name_value_pairs"], |
|
638 | + $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"], |
|
639 | + $options["order_by_{$type}"], $options["{$type}_owner_guids"]); |
|
640 | + |
|
641 | + if ($clauses) { |
|
642 | + // merge wheres to pass to elgg_get_entities() |
|
643 | + if (isset($options['wheres']) && !is_array($options['wheres'])) { |
|
644 | + $options['wheres'] = [$options['wheres']]; |
|
645 | + } elseif (!isset($options['wheres'])) { |
|
646 | + $options['wheres'] = []; |
|
647 | + } |
|
648 | + |
|
649 | + $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']); |
|
650 | + |
|
651 | + // merge joins to pass to elgg_get_entities() |
|
652 | + if (isset($options['joins']) && !is_array($options['joins'])) { |
|
653 | + $options['joins'] = [$options['joins']]; |
|
654 | + } elseif (!isset($options['joins'])) { |
|
655 | + $options['joins'] = []; |
|
656 | + } |
|
657 | + |
|
658 | + $options['joins'] = array_merge($options['joins'], $clauses['joins']); |
|
659 | + |
|
660 | + if ($clauses['orders']) { |
|
661 | + $order_by_metadata = implode(", ", $clauses['orders']); |
|
662 | + if (isset($options['order_by']) && $options['order_by']) { |
|
663 | + $options['order_by'] = "$order_by_metadata, {$options['order_by']}"; |
|
664 | + } else { |
|
665 | + $options['order_by'] = "$order_by_metadata, e.time_created DESC"; |
|
666 | + } |
|
667 | + } |
|
668 | + } |
|
669 | + |
|
670 | + return $options; |
|
671 | 671 | } |
672 | 672 | |
673 | 673 | /** |
@@ -681,11 +681,11 @@ discard block |
||
681 | 681 | * @access private |
682 | 682 | */ |
683 | 683 | function _elgg_metastrings_test($hook, $type, $value) { |
684 | - global $CONFIG; |
|
685 | - $value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php'; |
|
686 | - return $value; |
|
684 | + global $CONFIG; |
|
685 | + $value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php'; |
|
686 | + return $value; |
|
687 | 687 | } |
688 | 688 | |
689 | 689 | return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) { |
690 | - $hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test'); |
|
690 | + $hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test'); |
|
691 | 691 | }; |