Passed
Push — master ( 96b2fe...fab0a4 )
by Jeroen
29:16 queued 05:53
created

river.php ➔ _elgg_prefetch_river_entities()   D

Complexity

Conditions 13
Paths 108

Size

Total Lines 48
Code Lines 27

Duplication

Lines 22
Ratio 45.83 %

Code Coverage

Tests 25
CRAP Score 13

Importance

Changes 0
Metric Value
cc 13
eloc 27
nc 108
nop 1
dl 22
loc 48
ccs 25
cts 25
cp 1
crap 13
rs 4.9544
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Elgg river.
4
 * Activity stream functions.
5
 *
6
 * @package    Elgg.Core
7
 * @subpackage River
8
 */
9
10
use Elgg\Project\Paths;
11
12
/**
13
 * Adds an item to the river.
14
 *
15
 * @tip Read the item like "Lisa (subject) posted (action)
16
 * a comment (object) on John's blog (target)".
17
 *
18
 * @param array $options Array in format:
19
 *
20
 * 	view => STR The view that will handle the river item (must exist)
21
 *
22
 * 	action_type => STR An arbitrary string to define the action (eg 'comment', 'create')
23
 *
24
 *  subject_guid => INT The GUID of the entity doing the action
25
 *
26
 *  object_guid => INT The GUID of the entity being acted upon
27
 *
28
 *  target_guid => INT The GUID of the the object entity's container
29
 *
30
 *  access_id => INT The access ID of the river item (default: same as the object)
31
 *
32
 *  posted => INT The UNIX epoch timestamp of the river item (default: now)
33
 *
34
 *  annotation_id INT The annotation ID associated with this river entry
35
 *
36
 *  return_item => BOOL set to true to return the ElggRiverItem created
37
 *
38
 * @return int|ElggRiverItem|bool River ID/item or false on failure
39
 * @since 1.9
40
 */
41
function elgg_create_river_item(array $options = []) {
42 20
	$view = elgg_extract('view', $options);
43
	// use default viewtype for when called from web services api
44 20
	if (empty($view) || !(elgg_view_exists($view, 'default'))) {
45 2
		return false;
46
	}
47
48 19
	$action_type = elgg_extract('action_type', $options);
49 19
	if (empty($action_type)) {
50 1
		return false;
51
	}
52
53 19
	$subject_guid = elgg_extract('subject_guid', $options, 0);
54 19
	if (!($subject = get_entity($subject_guid))) {
55 2
		return false;
56
	}
57
58 19
	$object_guid = elgg_extract('object_guid', $options, 0);
59 19
	if (!($object = get_entity($object_guid))) {
60 2
		return false;
61
	}
62
63 18
	$target_guid = elgg_extract('target_guid', $options, 0);
64 18
	if ($target_guid) {
65
		// target_guid is not a required parameter so check
66
		// it only if it is included in the parameters
67 10
		if (!($target = get_entity($target_guid))) {
68 1
			return false;
69
		}
70
	}
71
72 17
	$access_id = elgg_extract('access_id', $options, $object->access_id);
73
74 17
	$posted = elgg_extract('posted', $options, time());
75
76 17
	$annotation_id = elgg_extract('annotation_id', $options, 0);
77 17
	if ($annotation_id) {
78
		if (!elgg_get_annotation_from_id($annotation_id)) {
79
			return false;
80
		}
81
	}
82
83 17
	$return_item = elgg_extract('return_item', $options, false);
84
85
	$values = [
86 17
		'type' => $object->getType(),
87 17
		'subtype' => $object->getSubtype(),
88 17
		'action_type' => $action_type,
89 17
		'access_id' => $access_id,
90 17
		'view' => $view,
91 17
		'subject_guid' => $subject_guid,
92 17
		'object_guid' => $object_guid,
93 17
		'target_guid' => $target_guid,
94 17
		'annotation_id' => $annotation_id,
95 17
		'posted' => $posted,
96
	];
97
	$col_types = [
98 17
		'type' => 'string',
99
		'subtype' => 'string',
100
		'action_type' => 'string',
101
		'access_id' => 'int',
102
		'view' => 'string',
103
		'subject_guid' => 'int',
104
		'object_guid' => 'int',
105
		'target_guid' => 'int',
106
		'annotation_id' => 'int',
107
		'posted' => 'int',
108
	];
109
110
	// return false to stop insert
111 17
	$values = elgg_trigger_plugin_hook('creating', 'river', null, $values);
112 17
	if ($values == false) {
113
		// inserting did not fail - it was just prevented
114 1
		return true;
115
	}
116
117 16
	$dbprefix = _elgg_config()->dbprefix;
118
119 16
	foreach ($values as $name => $value) {
120 16
		$sql_columns[] = $name;
121 16
		$sql_values[] = ":$name";
122 16
		$query_params[":$name"] = ($col_types[$name] === 'int') ? (int) $value : $value;
123
	}
124
	$sql = "
125 16
		INSERT INTO {$dbprefix}river (" . implode(',', $sql_columns) . ")
0 ignored issues
show
Bug introduced by
The variable $sql_columns does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
126 16
		VALUES (" . implode(',', $sql_values) . ")
0 ignored issues
show
Bug introduced by
The variable $sql_values does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
127
	";
128 16
	$id = insert_data($sql, $query_params);
0 ignored issues
show
Bug introduced by
The variable $query_params does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
129 16
	if (!$id) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $id of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
130
		return false;
131
	}
132
133
	// update the entities which had the action carried out on it
134
	// @todo shouldn't this be done elsewhere? Like when an annotation is saved?
135 16
	$object->updateLastAction($values['posted']);
136
137 16
	$ia = elgg_set_ignore_access(true);
138 16
	$river_items = elgg_get_river(['id' => $id]);
139 16
	elgg_set_ignore_access($ia);
140
141 16
	if (!$river_items) {
142
		return false;
143
	}
144
145 16
	elgg_trigger_event('created', 'river', $river_items[0]);
146
147 16
	return $return_item ? $river_items[0] : $id;
148
}
149
150
/**
151
 * Get river items
152
 *
153
 * @note If using types and subtypes in a query, they are joined with an AND.
154
 *
155
 * @param array $options Parameters:
156
 *   ids                  => INT|ARR River item id(s)
157
 *   subject_guids        => INT|ARR Subject guid(s)
158
 *   object_guids         => INT|ARR Object guid(s)
159
 *   target_guids         => INT|ARR Target guid(s)
160
 *   annotation_ids       => INT|ARR The identifier of the annotation(s)
161
 *   action_types         => STR|ARR The river action type(s) identifier
162
 *   posted_time_lower    => INT     The lower bound on the time posted
163
 *   posted_time_upper    => INT     The upper bound on the time posted
164
 *
165
 *   types                => STR|ARR Entity type string(s)
166
 *   subtypes             => STR|ARR Entity subtype string(s)
167
 *   type_subtype_pairs   => ARR     Array of type => subtype pairs where subtype
168
 *                                   can be an array of subtype strings
169
 *
170
 *   relationship         => STR     Relationship identifier
171
 *   relationship_guid    => INT|ARR Entity guid(s)
172
 *   inverse_relationship => BOOL    Subject or object of the relationship (false)
173
 *
174
 * 	 limit                => INT     Number to show per page (20)
175
 *   offset               => INT     Offset in list (0)
176
 *   count                => BOOL    Count the river items? (false)
177
 *   order_by             => STR     Order by clause (rv.posted desc)
178
 *   group_by             => STR     Group by clause
179
 *
180
 *   distinct             => BOOL    If set to false, Elgg will drop the DISTINCT
181
 *                                   clause from the MySQL query, which will improve
182
 *                                   performance in some situations. Avoid setting this
183
 *                                   option without a full understanding of the
184
 *                                   underlying SQL query Elgg creates. (true)
185
 *
186
 *   batch                => BOOL    If set to true, an Elgg\BatchResult object will be returned
187
 *                                   instead of an array. (false) Since 2.3.
188
 *
189
 *   batch_inc_offset     => BOOL    If "batch" is used, this tells the batch to increment the offset
190
 *                                   on each fetch. This must be set to false if you delete the batched
191
 *                                   results. (true)
192
 *
193
 *   batch_size           => INT     If "batch" is used, this is the number of entities/rows to pull
194
 *                                   in before requesting more. (25)
195
 *
196
 * @return \ElggRiverItem[]|\Elgg\BatchResult|array|int
197
 * @since 1.8.0
198
 */
199
function elgg_get_river(array $options = []) {
200
	$defaults = [
201 197
		'ids'                  => ELGG_ENTITIES_ANY_VALUE,
202
203
		'subject_guids'	       => ELGG_ENTITIES_ANY_VALUE,
204
		'object_guids'         => ELGG_ENTITIES_ANY_VALUE,
205
		'target_guids'         => ELGG_ENTITIES_ANY_VALUE,
206
		'annotation_ids'       => ELGG_ENTITIES_ANY_VALUE,
207
		'action_types'         => ELGG_ENTITIES_ANY_VALUE,
208
209
		'relationship'         => null,
210
		'relationship_guid'    => null,
211
		'inverse_relationship' => false,
212
213
		'types'	               => ELGG_ENTITIES_ANY_VALUE,
214
		'subtypes'             => ELGG_ENTITIES_ANY_VALUE,
215
		'type_subtype_pairs'   => ELGG_ENTITIES_ANY_VALUE,
216
217
		'posted_time_lower'	   => ELGG_ENTITIES_ANY_VALUE,
218
		'posted_time_upper'	   => ELGG_ENTITIES_ANY_VALUE,
219
220
		'limit'                => 20,
221
		'offset'               => 0,
222
		'count'                => false,
223
		'distinct'             => true,
224
225
		'batch'                => false,
226
		'batch_inc_offset'     => true,
227
		'batch_size'           => 25,
228
229
		'order_by'             => 'rv.posted desc',
230
		'group_by'             => ELGG_ENTITIES_ANY_VALUE,
231
232
		'wheres'               => [],
233
		'joins'                => [],
234
	];
235
236 197
	if (isset($options['view']) || isset($options['views'])) {
237
		$msg = __FUNCTION__ . ' does not support the "views" option, though you may specify values for'
238
			. ' the "rv.view" column using the "wheres" option.';
239
		elgg_log($msg, 'WARNING');
240
	}
241
242 197
	$options = array_merge($defaults, $options);
243
244 197 View Code Duplication
	if ($options['batch'] && !$options['count']) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
245
		$batch_size = $options['batch_size'];
246
		$batch_inc_offset = $options['batch_inc_offset'];
247
248
		// clean batch keys from $options.
249
		unset($options['batch'], $options['batch_size'], $options['batch_inc_offset']);
250
251
		return new \ElggBatch('elgg_get_river', $options, null, $batch_size, $batch_inc_offset);
252
	}
253
254 197
	$singulars = ['id', 'subject_guid', 'object_guid', 'target_guid', 'annotation_id', 'action_type', 'type', 'subtype'];
255 197
	$options = _elgg_normalize_plural_options_array($options, $singulars);
256
257 197
	$wheres = $options['wheres'];
258
259 197
	$wheres[] = _elgg_get_guid_based_where_sql('rv.id', $options['ids']);
260 197
	$wheres[] = _elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']);
261 197
	$wheres[] = _elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']);
262 197
	$wheres[] = _elgg_get_guid_based_where_sql('rv.target_guid', $options['target_guids']);
263 197
	$wheres[] = _elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']);
264 197
	$wheres[] = _elgg_river_get_action_where_sql($options['action_types']);
265 197
	$wheres[] = _elgg_get_river_type_subtype_where_sql('rv', $options['types'],
266 197
		$options['subtypes'], $options['type_subtype_pairs']);
267
268 197
	if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) {
269
		$wheres[] = "rv.posted >= {$options['posted_time_lower']}";
270
	}
271
272 197
	if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) {
273
		$wheres[] = "rv.posted <= {$options['posted_time_upper']}";
274
	}
275
276 197
	if (!access_get_show_hidden_status()) {
277 196
		$wheres[] = "rv.enabled = 'yes'";
278
	}
279
280 197
	$dbprefix = _elgg_config()->dbprefix;
281
282
	// joins
283 197
	$joins = [];
284 197
	$joins[] = "JOIN {$dbprefix}entities oe ON rv.object_guid = oe.guid";
285
286
	// LEFT JOIN is used because all river items do not necessarily have target
287 197
	$joins[] = "LEFT JOIN {$dbprefix}entities te ON rv.target_guid = te.guid";
288
289 197
	if ($options['relationship_guid']) {
290
		$clauses = elgg_get_entity_relationship_where_sql(
291
				'rv.subject_guid',
292
				$options['relationship'],
293
				$options['relationship_guid'],
294
				$options['inverse_relationship']);
295
		if ($clauses) {
296
			$wheres = array_merge($wheres, $clauses['wheres']);
297
			$joins = array_merge($joins, $clauses['joins']);
298
		}
299
	}
300
301
	// add optional joins
302 197
	$joins = array_merge($joins, $options['joins']);
303
304
	// see if any functions failed
305
	// remove empty strings on successful functions
306 197 View Code Duplication
	foreach ($wheres as $i => $where) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307 197
		if ($where === false) {
308
			return false;
309
		} elseif (empty($where)) {
310 197
			unset($wheres[$i]);
311
		}
312
	}
313
314
	// remove identical where clauses
315 197
	$wheres = array_unique($wheres);
316 197
	$prefix = _elgg_config()->dbprefix;
317
318 197
	if (!$options['count']) {
319 197
		$distinct = $options['distinct'] ? "DISTINCT" : "";
320
321 197
		$query = "SELECT $distinct rv.* FROM {$prefix}river rv ";
322
	} else {
323
		// note: when DISTINCT unneeded, it's slightly faster to compute COUNT(*) than IDs
324
		$count_expr = $options['distinct'] ? "DISTINCT rv.id" : "*";
325
326
		$query = "SELECT COUNT($count_expr) as total FROM {$prefix}river rv ";
327
	}
328
329
	// add joins
330 197
	foreach ($joins as $j) {
331 197
		$query .= " $j ";
332
	}
333
334
	// add wheres
335 197
	$query .= ' WHERE ';
336
337 197
	foreach ($wheres as $w) {
338 197
		$query .= " $w AND ";
339
	}
340
341
	// Make sure that user has access to all the entities referenced by each river item
342 197
	$object_access_where = _elgg_get_access_where_sql(['table_alias' => 'oe']);
343 197
	$target_access_where = _elgg_get_access_where_sql(['table_alias' => 'te']);
344
345
	// We use LEFT JOIN with entities table but the WHERE clauses are used
346
	// regardless if a JOIN is successfully made. The "te.guid IS NULL" is
347
	// needed because of this.
348 197
	$query .= "$object_access_where AND ($target_access_where OR te.guid IS NULL) ";
349
350 197
	if (!$options['count']) {
351 197
		$options['group_by'] = sanitise_string($options['group_by']);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
352 197
		if ($options['group_by']) {
353
			$query .= " GROUP BY {$options['group_by']}";
354
		}
355
356 197
		$options['order_by'] = sanitise_string($options['order_by']);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
357 197
		$query .= " ORDER BY {$options['order_by']}";
358
359 197 View Code Duplication
		if ($options['limit']) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
360 197
			$limit = sanitise_int($options['limit']);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_int() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
361 197
			$offset = sanitise_int($options['offset'], false);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_int() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
362 197
			$query .= " LIMIT $offset, $limit";
363
		}
364
365 197
		$river_items = get_data($query, '_elgg_row_to_elgg_river_item');
366 197
		_elgg_prefetch_river_entities($river_items);
367
368 197
		return $river_items;
369
	} else {
370
		$total = get_data_row($query);
371
		return (int) $total->total;
372
	}
373
}
374
375
/**
376
 * Delete river items based on $options.
377
 *
378
 * @warning Unlike elgg_get_river() this will not accept an empty options array!
379
 *          This requires at least one constraint: id(s), annotation_id(s)
380
 *          subject_guid(s), object_guid(s), target_guid(s)
381
 *          or view(s) must be set.
382
 *
383
 * @param array $options An options array. {@link elgg_get_river()}
384
 *
385
 * @return bool|null true on success, false on failure, null if no metadata to delete.
386
 *
387
 * @since 1.8.0
388
 */
389
function elgg_delete_river(array $options = []) {
390
	
391 196
	if (!_elgg_is_valid_options_for_batch_operation($options, 'river')) {
392
		// requirements not met
393
		return false;
394
	}
395
	
396 196
	$batch = new ElggBatch('elgg_get_river', $options, 'elgg_batch_delete_callback', 25, false);
397
	
398 196
	return $batch->callbackResult;
399
}
400
401
/**
402
 * Prefetch entities that will be displayed in the river.
403
 *
404
 * @param \ElggRiverItem[] $river_items
405
 * @access private
406
 */
407
function _elgg_prefetch_river_entities(array $river_items) {
408
	// prefetch objects, subjects and targets
409 197
	$guids = [];
410 197
	foreach ($river_items as $item) {
411 16
		if ($item->subject_guid && !_elgg_services()->entityCache->get($item->subject_guid)) {
412 10
			$guids[$item->subject_guid] = true;
413
		}
414 16
		if ($item->object_guid && !_elgg_services()->entityCache->get($item->object_guid)) {
415 9
			$guids[$item->object_guid] = true;
416
		}
417 16
		if ($item->target_guid && !_elgg_services()->entityCache->get($item->target_guid)) {
418 16
			$guids[$item->target_guid] = true;
419
		}
420
	}
421 197 View Code Duplication
	if ($guids) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $guids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
422
		// The entity cache only holds 256. We don't want to bump out any plugins.
423 10
		$guids = array_slice($guids, 0, 200, true);
424
		// return value unneeded, just priming cache
425 10
		elgg_get_entities([
426 10
			'guids' => array_keys($guids),
427 10
			'limit' => 0,
428
			'distinct' => false,
429
		]);
430
	}
431
432
	// prefetch object containers, in case they were not in the targets
433 197
	$guids = [];
434 197
	foreach ($river_items as $item) {
435 16
		$object = $item->getObjectEntity();
436 16
		if ($object->container_guid && !_elgg_services()->entityCache->get($object->container_guid)) {
437 16
			$guids[$object->container_guid] = true;
438
		}
439
	}
440 197 View Code Duplication
	if ($guids) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $guids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441 16
		$guids = array_slice($guids, 0, 200, true);
442 16
		elgg_get_entities([
443 16
			'guids' => array_keys($guids),
444 16
			'limit' => 0,
445
			'distinct' => false,
446
447
			// Why specify? user containers are likely already loaded via the owners
448 16
			'type' => 'group',
449
		]);
450
	}
451
452
	// Note: We've tried combining the above ege() calls into one (pulling containers at the same time).
453
	// Although it seems like it would reduce queries, it added some. o_O
454 197
}
455
456
/**
457
 * List river items
458
 *
459
 * @param array $options Any options from elgg_get_river() plus:
460
 *   item_view  => STR         Alternative view to render list items
461
 *   pagination => BOOL        Display pagination links (true)
462
 *   no_results => STR|Closure Message to display if no items
463
 *
464
 * @return string
465
 * @since 1.8.0
466
 */
467
function elgg_list_river(array $options = []) {
468
	elgg_register_rss_link();
469
470
	$defaults = [
471
		'offset'     => (int) max(get_input('offset', 0), 0),
472
		'limit'      => (int) max(get_input('limit', max(20, _elgg_config()->default_limit)), 0),
473
		'pagination' => true,
474
		'list_class' => 'elgg-list-river',
475
		'no_results' => '',
476
	];
477
478
	$options = array_merge($defaults, $options);
479
480 View Code Duplication
	if (!$options["limit"] && !$options["offset"]) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
481
		// no need for pagination if listing is unlimited
482
		$options["pagination"] = false;
483
	}
484
485
	$options['count'] = true;
486
	$count = elgg_get_river($options);
487
488
	if ($count > 0) {
489
		$options['count'] = false;
490
		$items = elgg_get_river($options);
491
	} else {
492
		$items = [];
493
	}
494
495
	$options['count'] = $count;
496
	$options['items'] = $items;
497
498
	return elgg_view('page/components/list', $options);
499
}
500
501
/**
502
 * Convert a database row to a new \ElggRiverItem
503
 *
504
 * @param \stdClass $row Database row from the river table
505
 *
506
 * @return \ElggRiverItem
507
 * @since 1.8.0
508
 * @access private
509
 */
510
function _elgg_row_to_elgg_river_item($row) {
511 16
	if (!($row instanceof \stdClass)) {
512
		return null;
513
	}
514
515 16
	return new \ElggRiverItem($row);
516
}
517
518
/**
519
 * Returns SQL where clause for type and subtype on river table
520
 *
521
 * @internal This is a simplified version of elgg_get_entity_type_subtype_where_sql()
522
 * which could be used for all queries once the subtypes have been denormalized.
523
 *
524
 * @param string     $table    'rv'
525
 * @param null|array $types    Array of types or null if none.
526
 * @param null|array $subtypes Array of subtypes or null if none
527
 * @param null|array $pairs    Array of pairs of types and subtypes
528
 *
529
 * @return string
530
 * @since 1.8.0
531
 * @access private
532
 */
533
function _elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
534
	// short circuit if nothing is requested
535 197
	if (!$types && !$subtypes && !$pairs) {
536 197
		return '';
537
	}
538
539 1
	$wheres = [];
540 1
	$types_wheres = [];
541 1
	$subtypes_wheres = [];
542
543
	// if no pairs, use types and subtypes
544 1
	if (!is_array($pairs)) {
545 1
		if ($types) {
546 1
			if (!is_array($types)) {
547
				$types = [$types];
548
			}
549 1
			foreach ($types as $type) {
550 1
				$type = sanitise_string($type);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
551 1
				$types_wheres[] = "({$table}.type = '$type')";
552
			}
553
		}
554
555 1
		if ($subtypes) {
556 1
			if (!is_array($subtypes)) {
557
				$subtypes = [$subtypes];
558
			}
559 1
			foreach ($subtypes as $subtype) {
560 1
				$subtype = sanitise_string($subtype);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
561 1
				$subtypes_wheres[] = "({$table}.subtype = '$subtype')";
562
			}
563
		}
564
565 1
		if (is_array($types_wheres) && count($types_wheres)) {
566 1
			$types_wheres = [implode(' OR ', $types_wheres)];
567
		}
568
569 1
		if (is_array($subtypes_wheres) && count($subtypes_wheres)) {
570 1
			$subtypes_wheres = ['(' . implode(' OR ', $subtypes_wheres) . ')'];
571
		}
572
573 1
		$wheres = [implode(' AND ', array_merge($types_wheres, $subtypes_wheres))];
574
	} else {
575
		// using type/subtype pairs
576
		foreach ($pairs as $paired_type => $paired_subtypes) {
577
			$paired_type = sanitise_string($paired_type);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
578
			if (is_array($paired_subtypes)) {
579
				$paired_subtypes = array_map('sanitise_string', $paired_subtypes);
580
				$paired_subtype_str = implode("','", $paired_subtypes);
581
				if ($paired_subtype_str) {
582
					$wheres[] = "({$table}.type = '$paired_type'"
583
						. " AND {$table}.subtype IN ('$paired_subtype_str'))";
584
				}
585
			} else {
586
				$paired_subtype = sanitise_string($paired_subtypes);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
587
				$wheres[] = "({$table}.type = '$paired_type'"
588
					. " AND {$table}.subtype = '$paired_subtype')";
589
			}
590
		}
591
	}
592
593 1 View Code Duplication
	if (is_array($wheres) && count($wheres)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
594 1
		$where = implode(' OR ', $wheres);
595 1
		return "($where)";
596
	}
597
598
	return '';
599
}
600
601
/**
602
 * Get the where clause based on river action type strings
603
 *
604
 * @param array $types Array of action type strings
605
 *
606
 * @return string
607
 * @since 1.8.0
608
 * @access private
609
 */
610 View Code Duplication
function _elgg_river_get_action_where_sql($types) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
611 197
	if (!$types) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $types of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
612 197
		return '';
613
	}
614
615
	if (!is_array($types)) {
616
		$types = sanitise_string($types);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
617
		return "(rv.action_type = '$types')";
618
	}
619
620
	// sanitize types array
621
	$types_sanitized = [];
622
	foreach ($types as $type) {
623
		$types_sanitized[] = sanitise_string($type);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
624
	}
625
626
	$type_str = implode("','", $types_sanitized);
627
	return "(rv.action_type IN ('$type_str'))";
628
}
629
630
/**
631
 * Get the where clause based on river view strings
632
 *
633
 * @param array $views Array of view strings
634
 *
635
 * @return string
636
 * @since 1.8.0
637
 * @access private
638
 */
639 View Code Duplication
function _elgg_river_get_view_where_sql($views) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
640
	if (!$views) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $views of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
641
		return '';
642
	}
643
644
	if (!is_array($views)) {
645
		$views = sanitise_string($views);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
646
		return "(rv.view = '$views')";
647
	}
648
649
	// sanitize views array
650
	$views_sanitized = [];
651
	foreach ($views as $view) {
652
		$views_sanitized[] = sanitise_string($view);
0 ignored issues
show
Deprecated Code introduced by
The function sanitise_string() has been deprecated with message: Use query parameters where possible

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

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

Loading history...
653
	}
654
655
	$view_str = implode("','", $views_sanitized);
656
	return "(rv.view IN ('$view_str'))";
657
}
658
659
/**
660
 * Sets the access ID on river items for a particular object
661
 *
662
 * @param int $object_guid The GUID of the entity
663
 * @param int $access_id   The access ID
664
 *
665
 * @return bool Depending on success
666
 */
667
function update_river_access_by_object($object_guid, $access_id) {
668
	
669 9
	$dbprefix = _elgg_config()->dbprefix;
670
	$query = "
671 9
		UPDATE {$dbprefix}river
672
		SET access_id = :access_id
673
		WHERE object_guid = :object_guid
674
	";
675
676
	$params = [
677 9
		':access_id' => (int) $access_id,
678 9
		':object_guid' => (int) $object_guid,
679
	];
680
681 9
	return update_data($query, $params);
682
}
683
684
/**
685
 * Register river unit tests
686
 * @access private
687
 */
688
function _elgg_river_test($hook, $type, $value) {
0 ignored issues
show
Unused Code introduced by
The parameter $hook is not used and could be removed.

This check looks from 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
The parameter $type is not used and could be removed.

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

Loading history...
689
	$value[] = ElggCoreRiverAPITest::class;
690
	return $value;
691
}
692
693
/**
694
 * Disable river entries that reference a disabled entity as subject/object/target
695
 *
696
 * @param string $event The event 'disable'
697
 * @param string $type Type of entity being disabled 'all'
698
 * @param mixed $entity The entity being disabled
699
 * @return boolean
700
 * @access private
701
 */
702 View Code Duplication
function _elgg_river_disable($event, $type, $entity) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from 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
The parameter $type is not used and could be removed.

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

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
703
704 4
	if (!elgg_instanceof($entity)) {
705
		return true;
706
	}
707
708 4
	$dbprefix = _elgg_config()->dbprefix;
709
	$query = <<<QUERY
710 4
	UPDATE {$dbprefix}river AS rv
711
	SET rv.enabled = 'no'
712 4
	WHERE (rv.subject_guid = {$entity->guid} OR rv.object_guid = {$entity->guid} OR rv.target_guid = {$entity->guid});
713
QUERY;
714
715 4
	update_data($query);
716 4
	return true;
717
}
718
719
720
/**
721
 * Enable river entries that reference a re-enabled entity as subject/object/target
722
 *
723
 * @param string $event The event 'enable'
724
 * @param string $type Type of entity being enabled 'all'
725
 * @param mixed $entity The entity being enabled
726
 * @return boolean
727
 * @access private
728
 */
729 View Code Duplication
function _elgg_river_enable($event, $type, $entity) {
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from 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
The parameter $type is not used and could be removed.

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

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
730
731 3
	if (!elgg_instanceof($entity)) {
732
		return true;
733
	}
734
735 3
	$dbprefix = _elgg_config()->dbprefix;
736
	$query = <<<QUERY
737 3
	UPDATE {$dbprefix}river AS rv
738 3
	LEFT JOIN {$dbprefix}entities AS se ON se.guid = rv.subject_guid
739 3
	LEFT JOIN {$dbprefix}entities AS oe ON oe.guid = rv.object_guid
740 3
	LEFT JOIN {$dbprefix}entities AS te ON te.guid = rv.target_guid
741
	SET rv.enabled = 'yes'
742
	WHERE (
743
			(se.enabled = 'yes' OR se.guid IS NULL) AND
744
			(oe.enabled = 'yes' OR oe.guid IS NULL) AND
745
			(te.enabled = 'yes' OR te.guid IS NULL)
746
		)
747 3
		AND (se.guid = {$entity->guid} OR oe.guid = {$entity->guid} OR te.guid = {$entity->guid});
748
QUERY;
749
750 3
	update_data($query);
751 3
	return true;
752
}
753
754
/**
755
 * Initialize river library
756
 * @access private
757
 */
758
function _elgg_river_init() {
759
760 312
	elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_river_test');
761
762
	// For BC, we want required AMD modules to be loaded even if plugins
763
	// overwrite these views
764 312
	elgg_extend_view('forms/comment/save', 'forms/comment/save_deps');
765
	
766 312
}
767
768
/**
769
 * @see \Elgg\Application::loadCore Do not do work here. Just register for events.
770
 */
771
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
0 ignored issues
show
Unused Code introduced by
The parameter $hooks is not used and could be removed.

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

Loading history...
772 312
	$events->registerHandler('init', 'system', '_elgg_river_init');
773 312
	$events->registerHandler('disable:after', 'all', '_elgg_river_disable', 600);
774 312
	$events->registerHandler('enable:after', 'all', '_elgg_river_enable', 600);
775
};
776