These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Elgg\Database; |
||
3 | |||
4 | /** |
||
5 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||
6 | * |
||
7 | * @access private |
||
8 | * |
||
9 | * @package Elgg.Core |
||
10 | * @subpackage Database |
||
11 | * @since 1.10.0 |
||
12 | */ |
||
13 | class Annotations { |
||
14 | |||
15 | use \Elgg\TimeUsing; |
||
16 | |||
17 | /** |
||
18 | * @var \Elgg\Database |
||
19 | */ |
||
20 | protected $db; |
||
21 | |||
22 | /** |
||
23 | * @var \ElggSession |
||
24 | */ |
||
25 | protected $session; |
||
26 | |||
27 | /** |
||
28 | * @var \Elgg\EventsService |
||
29 | */ |
||
30 | protected $events; |
||
31 | |||
32 | /** |
||
33 | * Constructor |
||
34 | * |
||
35 | * @param \Elgg\Database $db Database |
||
36 | * @param \ElggSession $session Session |
||
37 | * @param \Elgg\EventsService $events Events |
||
38 | */ |
||
39 | 197 | public function __construct(\Elgg\Database $db, \ElggSession $session, \Elgg\EventsService $events) { |
|
40 | 197 | $this->db = $db; |
|
41 | 197 | $this->session = $session; |
|
42 | 197 | $this->events = $events; |
|
43 | 197 | } |
|
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) { |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
85 | |||
86 | $result = false; |
||
87 | |||
88 | $entity_guid = (int) $entity_guid; |
||
89 | $value_type = \ElggExtender::detectValueType($value, $value_type); |
||
90 | |||
91 | $owner_guid = (int) $owner_guid; |
||
92 | if ($owner_guid == 0) { |
||
93 | $owner_guid = $this->session->getLoggedInUserGuid(); |
||
94 | } |
||
95 | |||
96 | $access_id = (int) $access_id; |
||
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); |
||
101 | |||
102 | if ($this->events->trigger('annotate', $entity->type, $entity)) { |
||
103 | $sql = "INSERT INTO {$this->db->prefix}annotations |
||
104 | (entity_guid, name, value, value_type, owner_guid, time_created, access_id) |
||
105 | VALUES |
||
106 | (:entity_guid, :name, :value, :value_type, :owner_guid, :time_created, :access_id)"; |
||
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 | ]); |
||
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) { |
||
0 ignored issues
–
show
|
|||
146 | |||
147 | $annotation_id = (int) $annotation_id; |
||
148 | |||
149 | $annotation = $this->get($annotation_id); |
||
150 | if (!$annotation) { |
||
151 | return false; |
||
152 | } |
||
153 | if (!$annotation->canEdit()) { |
||
154 | return false; |
||
155 | } |
||
156 | |||
157 | $name = trim($name); |
||
158 | $value_type = \ElggExtender::detectValueType($value, $value_type); |
||
159 | |||
160 | $owner_guid = (int) $owner_guid; |
||
161 | if ($owner_guid == 0) { |
||
162 | $owner_guid = $this->session->getLoggedInUserGuid(); |
||
163 | } |
||
164 | |||
165 | $access_id = (int) $access_id; |
||
166 | |||
167 | $sql = "UPDATE {$this->db->prefix}annotations |
||
168 | (name, value, value_type, access_id, owner_guid) |
||
169 | VALUES |
||
170 | (:name, :value, :value_type, :access_id, :owner_guid) |
||
171 | WHERE id = :annotation_id"; |
||
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 | ]); |
||
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 | View Code Duplication | function find(array $options = []) { |
|
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 | } |
||
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 | 1 | View Code Duplication | function deleteAll(array $options) { |
0 ignored issues
–
show
This method 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...
|
|||
242 | 1 | if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
243 | return false; |
||
244 | } |
||
245 | |||
246 | 1 | $options['metastring_type'] = 'annotations'; |
|
247 | 1 | 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 | 1 | View Code Duplication | function disableAll(array $options) { |
0 ignored issues
–
show
This method 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...
|
|||
259 | 1 | if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
260 | return false; |
||
261 | } |
||
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 | 1 | $inc_offset = access_get_show_hidden_status(); |
|
266 | |||
267 | 1 | $options['metastring_type'] = 'annotations'; |
|
268 | 1 | 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) { |
||
0 ignored issues
–
show
|
|||
283 | if (!$options || !is_array($options)) { |
||
0 ignored issues
–
show
The expression
$options 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
Loading history...
|
|||
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 | |||
329 | 'annotation_name_value_pairs_operator' => 'AND', |
||
330 | 'annotation_case_sensitive' => true, |
||
331 | 'order_by_annotation' => [], |
||
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 | ]; |
||
337 | |||
338 | $options = array_merge($defaults, $options); |
||
339 | |||
340 | $singulars = ['annotation_name', 'annotation_value', 'annotation_name_value_pair', 'annotation_owner_guid']; |
||
341 | |||
342 | $options = _elgg_normalize_plural_options_array($options, $singulars); |
||
343 | $options = _elgg_entities_get_metastrings_options('annotation', $options); |
||
344 | |||
345 | if (!$options) { |
||
346 | return false; |
||
347 | } |
||
348 | |||
349 | $time_wheres = _elgg_get_entity_time_where_sql('n_table', $options['annotation_created_time_upper'], |
||
350 | $options['annotation_created_time_lower']); |
||
351 | |||
352 | if ($time_wheres) { |
||
1 ignored issue
–
show
The expression
$time_wheres of type false|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
Loading history...
|
|||
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) { |
||
0 ignored issues
–
show
|
|||
392 | |||
393 | if (isset($options['count']) && $options['count']) { |
||
394 | return elgg_get_entities_from_annotations($options); |
||
395 | } |
||
396 | |||
397 | $db_prefix = $this->db->prefix; |
||
398 | $defaults = [ |
||
399 | 'calculation' => 'sum', |
||
400 | 'order_by' => 'annotation_calculation desc' |
||
401 | ]; |
||
402 | |||
403 | $options = array_merge($defaults, $options); |
||
404 | |||
405 | $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); |
||
0 ignored issues
–
show
The function
sanitize_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...
|
|||
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"; |
||
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'; |
||
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 | } |
||
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) { |
||
0 ignored issues
–
show
|
|||
432 | |||
433 | if (!$owner_guid && !($owner_guid = $this->session->getLoggedInUserGuid())) { |
||
434 | return false; |
||
435 | } |
||
436 | |||
437 | $sql = "SELECT id FROM {$this->db->prefix}annotations |
||
438 | WHERE owner_guid = :owner_guid |
||
439 | AND entity_guid = :entity_guid |
||
440 | AND name = :annotation_type"; |
||
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 | } |
||
450 | } |
||
451 |
Adding explicit visibility (
private
,protected
, orpublic
) is generally recommend to communicate to other developers how, and from where this method is intended to be used.