Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

_elgg_river_test()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Elgg river.
4
 * Activity stream functions.
5
 *
6
 * @package    Elgg.Core
7
 * @subpackage River
8
 */
9
10
/**
11
 * Adds an item to the river.
12
 *
13
 * @tip Read the item like "Lisa (subject) posted (action)
14
 * a comment (object) on John's blog (target)".
15
 *
16
 * @param array $options Array in format:
17
 *
18
 * 	view => STR The view that will handle the river item
19
 *
20
 * 	action_type => STR An arbitrary string to define the action (eg 'comment', 'create')
21
 *
22
 *  subject_guid => INT The GUID of the entity doing the action (default: current logged in user guid)
23
 *
24
 *  object_guid => INT The GUID of the entity being acted upon
25
 *
26
 *  target_guid => INT The GUID of the the object entity's container
27
 *
28
 *  access_id => INT The access ID of the river item (default: same as the object)
29
 *
30
 *  posted => INT The UNIX epoch timestamp of the river item (default: now)
31
 *
32
 *  annotation_id INT The annotation ID associated with this river entry
33
 *
34
 *  return_item => BOOL set to true to return the ElggRiverItem created
35
 *
36
 * @return int|ElggRiverItem|bool River ID/item or false on failure
37
 * @since 1.9
38
 */
39
function elgg_create_river_item(array $options = []) {
40
41 85
	$view = elgg_extract('view', $options, '');
42
	// use default viewtype for when called from web services api
43 85
	if (!empty($view) && !elgg_view_exists($view, 'default')) {
44 1
		return false;
45
	}
46
47 84
	$action_type = elgg_extract('action_type', $options);
48 84
	if (empty($action_type)) {
49 1
		return false;
50
	}
51
52 84
	$subject_guid = elgg_extract('subject_guid', $options, elgg_get_logged_in_user_guid());
53 84
	if (!($subject = get_entity($subject_guid))) {
0 ignored issues
show
Unused Code introduced by Juho Jaakkola
The assignment to $subject is dead and can be removed.
Loading history...
54 2
		return false;
55
	}
56
57 84
	$object_guid = elgg_extract('object_guid', $options, 0);
58 84
	if (!($object = get_entity($object_guid))) {
59 2
		return false;
60
	}
61
62 83
	$target_guid = elgg_extract('target_guid', $options, 0);
63 83
	if ($target_guid) {
64
		// target_guid is not a required parameter so check
65
		// it only if it is included in the parameters
66 74
		if (!($target = get_entity($target_guid))) {
0 ignored issues
show
Unused Code introduced by Juho Jaakkola
The assignment to $target is dead and can be removed.
Loading history...
67 1
			return false;
68
		}
69
	}
70
71 82
	$access_id = elgg_extract('access_id', $options, $object->access_id);
72
73 82
	$posted = elgg_extract('posted', $options, time());
74
75 82
	$annotation_id = elgg_extract('annotation_id', $options, 0);
76 82
	if ($annotation_id) {
77
		if (!elgg_get_annotation_from_id($annotation_id)) {
78
			return false;
79
		}
80
	}
81
82 82
	$return_item = elgg_extract('return_item', $options, false);
83
84
	$values = [
85 82
		'type' => $object->getType(),
86 82
		'subtype' => $object->getSubtype(),
87 82
		'action_type' => $action_type,
88 82
		'access_id' => $access_id,
89 82
		'view' => $view,
90 82
		'subject_guid' => $subject_guid,
91 82
		'object_guid' => $object_guid,
92 82
		'target_guid' => $target_guid,
93 82
		'annotation_id' => $annotation_id,
94 82
		'posted' => $posted,
95
	];
96
	$col_types = [
97 82
		'type' => ELGG_VALUE_STRING,
98
		'subtype' => ELGG_VALUE_STRING,
99
		'action_type' => ELGG_VALUE_STRING,
100
		'access_id' => ELGG_VALUE_INTEGER,
101
		'view' => ELGG_VALUE_STRING,
102
		'subject_guid' => ELGG_VALUE_INTEGER,
103
		'object_guid' => ELGG_VALUE_INTEGER,
104
		'target_guid' => ELGG_VALUE_INTEGER,
105
		'annotation_id' => ELGG_VALUE_INTEGER,
106
		'posted' => ELGG_VALUE_INTEGER,
107
	];
108
109
	// return false to stop insert
110 82
	$values = elgg_trigger_plugin_hook('creating', 'river', null, $values);
111 82
	if ($values == false) {
112
		// inserting did not fail - it was just prevented
113 1
		return true;
114
	}
115
116 81
	$qb = \Elgg\Database\Insert::intoTable('river');
117 81
	foreach ($values as $name => $value) {
118 81
		$query_params[$name] = $qb->param($value, $col_types[$name]);
119
	}
120 81
	$qb->values($query_params);
0 ignored issues
show
Comprehensibility Best Practice introduced by Ismayil Khayredinov
The variable $query_params seems to be defined by a foreach iteration on line 117. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
121
122 81
	$id = _elgg_services()->db->insertData($qb);
123 81
	if (!$id) {
124
		return false;
125
	}
126
127 81
	if (!$return_item) {
128 77
		return $id;
129
	}
130
131 5
	$ia = elgg_set_ignore_access(true);
132 5
	$item = elgg_get_river_item_from_id($id);
133 5
	elgg_set_ignore_access($ia);
134
135 5
	if (!$item) {
136
		return false;
137
	}
138
139 5
	elgg_trigger_event('created', 'river', $item);
140
141 5
	return $item;
142
}
143
144
/**
145
 * Get river items
146
 *
147
 * @note If using types and subtypes in a query, they are joined with an AND.
148
 *
149
 * @param array $options Parameters:
150
 *   ids                  => INT|ARR River item id(s)
151
 *   subject_guids        => INT|ARR Subject guid(s)
152
 *   object_guids         => INT|ARR Object guid(s)
153
 *   target_guids         => INT|ARR Target guid(s)
154
 *   action_types         => STR|ARR The river action type(s) identifier
155
 *   posted_time_lower    => INT     The lower bound on the time posted
156
 *   posted_time_upper    => INT     The upper bound on the time posted
157
 *
158
 *   Additionally accepts all "annotation_*" options supported by {@link elgg_get_entities()}
159
 *   annotation_ids       => INT|ARR The identifier of the annotation(s)
160
 *
161
 *   types                => STR|ARR Entity type string(s)
162
 *   subtypes             => STR|ARR Entity subtype string(s)
163
 *   type_subtype_pairs   => ARR     Array of type => subtype pairs where subtype
164
 *                                   can be an array of subtype strings
165
 *
166
 *   Additionally accepts all "relationship_*" options supported by {@link elgg_get_entities()}
167
 *   relationship         => STR     Relationship identifier
168
 *   relationship_guid    => INT|ARR Entity guid(s)
169
 *   inverse_relationship => BOOL    Subject or object of the relationship (false)
170
 *   relationship_join_on => STR     subject_guid|object_guid|target_guid (defaults to subject_guid)
171
 *
172
 *   limit                => INT     Number to show per page (20)
173
 *   offset               => INT     Offset in list (0)
174
 *   count                => BOOL    Count the river items? (false)
175
 *   order_by             => STR     Order by clause (rv.posted desc)
176
 *   group_by             => STR     Group by clause
177
 *
178
 *   distinct             => BOOL    If set to false, Elgg will drop the DISTINCT
179
 *                                   clause from the MySQL query, which will improve
180
 *                                   performance in some situations. Avoid setting this
181
 *                                   option without a full understanding of the
182
 *                                   underlying SQL query Elgg creates. (true)
183
 *
184
 *   batch                => BOOL    If set to true, an Elgg\BatchResult object will be returned
185
 *                                   instead of an array. (false) Since 2.3.
186
 *
187
 *   batch_inc_offset     => BOOL    If "batch" is used, this tells the batch to increment the offset
188
 *                                   on each fetch. This must be set to false if you delete the batched
189
 *                                   results. (true)
190
 *
191
 *   batch_size           => INT     If "batch" is used, this is the number of entities/rows to pull
192
 *                                   in before requesting more. (25)
193
 *
194
 * @return \ElggRiverItem[]|\Elgg\BatchResult|array|int
195
 * @since 1.8.0
196
 */
197
function elgg_get_river(array $options = []) {
198 215
	return \Elgg\Database\River::find($options);
199
}
200
201
/**
202
 * Get river item from its ID
203
 *
204
 * @param int $id ID
205
 * @return ElggRiverItem|false
206
 */
207
function elgg_get_river_item_from_id($id) {
208 5
	$items = elgg_get_river([
209 5
		'id' => $id,
210
	]);
211
212 5
	return $items ? $items[0] : false;
213
}
214
215
/**
216
 * Delete river items based on $options.
217
 *
218
 * @warning Unlike elgg_get_river() this will not accept an empty options array!
219
 *          This requires at least one constraint: id(s), annotation_id(s)
220
 *          subject_guid(s), object_guid(s), target_guid(s)
221
 *          or view(s) must be set.
222
 *
223
 * @param array $options An options array. {@link elgg_get_river()}
224
 *
225
 * @return bool|null true on success, false on failure, null if no metadata to delete.
226
 *
227
 * @since   1.8.0
228
 */
229
function elgg_delete_river(array $options = []) {
230
231 215
	if (!_elgg_is_valid_options_for_batch_operation($options, 'river')) {
232
		// requirements not met
233
		return false;
234
	}
235
236 215
	$options['batch'] = true;
237 215
	$options['batch_size'] = 25;
238 215
	$options['batch_inc_offset'] = false;
239
240 215
	$river = elgg_get_river($options);
241 215
	$count = $river->count();
0 ignored issues
show
Bug introduced by Ismayil Khayredinov
The method count() does not exist on integer. ( Ignorable by Annotation )

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

241
	/** @scrutinizer ignore-call */ 
242
 $count = $river->count();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
242
243 215
	if (!$count) {
244 215
		return;
245
	}
246
247 7
	$success = 0;
248 7
	foreach ($river as $river_item) {
0 ignored issues
show
Bug introduced by Ismayil Khayredinov
The expression $river of type integer is not traversable.
Loading history...
249 7
		if ($river_item->delete()) {
250 7
			$success++;
251
		}
252
	}
253
254 7
	return $success == $count;
255
}
256
257
/**
258
 * List river items
259
 *
260
 * @param array $options Any options from elgg_get_river() plus:
261
 *   item_view  => STR         Alternative view to render list items
262
 *   pagination => BOOL        Display pagination links (true)
263
 *   no_results => STR|Closure Message to display if no items
264
 *
265
 * @return string
266
 * @since 1.8.0
267
 */
268
function elgg_list_river(array $options = []) {
269
	elgg_register_rss_link();
270
271
	$defaults = [
272
		'offset'     => (int) max(get_input('offset', 0), 0),
273
		'limit'      => (int) max(get_input('limit', max(20, _elgg_config()->default_limit)), 0),
274
		'pagination' => true,
275
		'list_class' => 'elgg-list-river',
276
		'no_results' => '',
277
	];
278
279
	$options = array_merge($defaults, $options);
280
281
	if (!$options["limit"] && !$options["offset"]) {
282
		// no need for pagination if listing is unlimited
283
		$options["pagination"] = false;
284
	}
285
286
	$options['count'] = true;
287
	$count = elgg_get_river($options);
288
289
	if ($count > 0) {
290
		$options['count'] = false;
291
		$items = elgg_get_river($options);
292
	} else {
293
		$items = [];
294
	}
295
296
	$options['count'] = $count;
297
	$options['items'] = $items;
298
299
	return elgg_view('page/components/list', $options);
300
}
301
302
/**
303
 * Sets the access ID on river items for a particular object
304
 *
305
 * @param int $object_guid The GUID of the entity
306
 * @param int $access_id   The access ID
307
 *
308
 * @return bool Depending on success
309
 */
310
function update_river_access_by_object($object_guid, $access_id) {
311
312 5
	$dbprefix = _elgg_config()->dbprefix;
313
	$query = "
314 5
		UPDATE {$dbprefix}river
315
		SET access_id = :access_id
316
		WHERE object_guid = :object_guid
317
	";
318
319
	$params = [
320 5
		':access_id' => (int) $access_id,
321 5
		':object_guid' => (int) $object_guid,
322
	];
323
324 5
	return update_data($query, $params);
325
}
326
327
/**
328
 * Register river unit tests
329
 *
330
 * @param string $hook  'unit_test'
331
 * @param string $type  'system'
332
 * @param array  $value current return value
333
 *
334
 * @return array
335
 * @codeCoverageIgnore
336
 */
337
function _elgg_river_test($hook, $type, $value) {
2 ignored issues
show
Unused Code introduced by Cash Costello
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

337
function _elgg_river_test($hook, /** @scrutinizer ignore-unused */ $type, $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Cash Costello
The parameter $hook is not used and could be removed. ( Ignorable by Annotation )

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

337
function _elgg_river_test(/** @scrutinizer ignore-unused */ $hook, $type, $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
338
	$value[] = ElggCoreRiverAPITest::class;
0 ignored issues
show
Bug introduced by Ismayil Khayredinov
The type ElggCoreRiverAPITest was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
339
	return $value;
340
}
341
342
/**
343
 * Disable river entries that reference a disabled entity as subject/object/target
344
 *
345
 * @param string     $event  'disable'
346
 * @param string     $type   'all'
347
 * @param ElggEntity $entity The entity being disabled
348
 *
349
 * @return void
350
 *
351
 * @access private
352
 */
353
function _elgg_river_disable($event, $type, $entity) {
2 ignored issues
show
Unused Code introduced by Matt Beckett
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

353
function _elgg_river_disable(/** @scrutinizer ignore-unused */ $event, $type, $entity) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Matt Beckett
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

353
function _elgg_river_disable($event, /** @scrutinizer ignore-unused */ $type, $entity) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
354
355 4
	if (!$entity instanceof ElggEntity) {
356
		return;
357
	}
358
359 4
	$dbprefix = _elgg_config()->dbprefix;
360
	$query = <<<QUERY
361 4
	UPDATE {$dbprefix}river AS rv
362
	SET rv.enabled = 'no'
363 4
	WHERE (rv.subject_guid = {$entity->guid} OR rv.object_guid = {$entity->guid} OR rv.target_guid = {$entity->guid});
364
QUERY;
365
366 4
	update_data($query);
367 4
	return;
368
}
369
370
371
/**
372
 * Enable river entries that reference a re-enabled entity as subject/object/target
373
 *
374
 * @param string     $event  'enable'
375
 * @param string     $type   'all'
376
 * @param ElggEntity $entity The entity being enabled
377
 *
378
 * @return void
379
 *
380
 * @access private
381
 */
382
function _elgg_river_enable($event, $type, $entity) {
2 ignored issues
show
Unused Code introduced by Matt Beckett
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

382
function _elgg_river_enable(/** @scrutinizer ignore-unused */ $event, $type, $entity) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by Matt Beckett
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

382
function _elgg_river_enable($event, /** @scrutinizer ignore-unused */ $type, $entity) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
383
384 3
	if (!$entity instanceof ElggEntity) {
385
		return;
386
	}
387
388 3
	$dbprefix = _elgg_config()->dbprefix;
389
	$query = <<<QUERY
390 3
	UPDATE {$dbprefix}river AS rv
391 3
	LEFT JOIN {$dbprefix}entities AS se ON se.guid = rv.subject_guid
392 3
	LEFT JOIN {$dbprefix}entities AS oe ON oe.guid = rv.object_guid
393 3
	LEFT JOIN {$dbprefix}entities AS te ON te.guid = rv.target_guid
394
	SET rv.enabled = 'yes'
395
	WHERE (
396
			(se.enabled = 'yes' OR se.guid IS NULL) AND
397
			(oe.enabled = 'yes' OR oe.guid IS NULL) AND
398
			(te.enabled = 'yes' OR te.guid IS NULL)
399
		)
400 3
		AND (se.guid = {$entity->guid} OR oe.guid = {$entity->guid} OR te.guid = {$entity->guid});
401
QUERY;
402
403 3
	update_data($query);
404 3
	return;
405
}
406
407
/**
408
 * Add the delete to river actions menu
409
 *
410
 * @param \Elgg\Hook $hook 'register' 'menu:river'
411
 *
412
 * @return void|ElggMenuItem[]
413
 *
414
 * @access private
415
 */
416
function _elgg_river_menu_setup(\Elgg\Hook $hook) {
417 1
	if (!elgg_is_logged_in()) {
418 1
		return;
419
	}
420
421
	$item = $hook->getParam('item');
422
	if (!($item instanceof ElggRiverItem)) {
423
		return;
424
	}
425
426
	if (!$item->canDelete()) {
427
		return;
428
	}
429
430
	$return = $hook->getValue();
431
432
	$return[] = \ElggMenuItem::factory([
433
		'name' => 'delete',
434
		'href' => "action/river/delete?id={$item->id}",
435
		'is_action' => true,
436
		'icon' => 'delete',
437
		'text' => elgg_echo('river:delete'),
438
		'confirm' => elgg_echo('deleteconfirm'),
439
		'priority' => 999,
440
	]);
441
442
	return $return;
443
}
444
445
/**
446
 * Initialize river library
447
 *
448
 * @return void
449
 *
450
 * @access private
451
 */
452
function _elgg_river_init() {
453
454 31
	elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_river_test');
455
456 31
	elgg_register_plugin_hook_handler('register', 'menu:river', '_elgg_river_menu_setup');
457 31
}
458
459
/**
460
 * @see \Elgg\Application::loadCore Do not do work here. Just register for events.
461
 */
462
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
1 ignored issue
show
Unused Code introduced by Steve Clay
The parameter $hooks is not used and could be removed. ( Ignorable by Annotation )

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

462
return function(\Elgg\EventsService $events, /** @scrutinizer ignore-unused */ \Elgg\HooksRegistrationService $hooks) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
463 18
	$events->registerHandler('init', 'system', '_elgg_river_init');
464 18
	$events->registerHandler('disable:after', 'all', '_elgg_river_disable', 600);
465 18
	$events->registerHandler('enable:after', 'all', '_elgg_river_enable', 600);
466
};
467