1 | <?php |
||||
2 | |||||
3 | namespace Elgg\Database; |
||||
4 | |||||
5 | use Elgg\Database; |
||||
6 | use Elgg\Database\Clauses\AnnotationWhereClause; |
||||
7 | use Elgg\EventsService; |
||||
8 | use ElggAnnotation; |
||||
9 | use ElggEntity; |
||||
10 | |||||
11 | /** |
||||
12 | * Interfaces with the database to perform CRUD operations on annotations |
||||
13 | * |
||||
14 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||||
15 | * |
||||
16 | * @access private |
||||
17 | */ |
||||
18 | class AnnotationsTable { |
||||
19 | |||||
20 | use \Elgg\TimeUsing; |
||||
21 | |||||
22 | /** |
||||
23 | * @var Database |
||||
24 | */ |
||||
25 | protected $db; |
||||
26 | |||||
27 | /** |
||||
28 | * @var EventsService |
||||
29 | */ |
||||
30 | protected $events; |
||||
31 | |||||
32 | /** |
||||
33 | * Constructor |
||||
34 | * |
||||
35 | * @param Database $db Database |
||||
36 | * @param EventsService $events Events |
||||
37 | */ |
||||
38 | 4863 | public function __construct(Database $db, EventsService $events) { |
|||
39 | 4863 | $this->db = $db; |
|||
40 | 4863 | $this->events = $events; |
|||
41 | 4863 | } |
|||
42 | |||||
43 | /** |
||||
44 | * Get a specific annotation by its id |
||||
45 | * |
||||
46 | * @param int $id The id of the annotation object |
||||
47 | * |
||||
48 | * @return ElggAnnotation|false |
||||
49 | */ |
||||
50 | 9 | public function get($id) { |
|||
51 | 9 | $qb = Select::fromTable('annotations'); |
|||
52 | 9 | $qb->select('*'); |
|||
53 | |||||
54 | 9 | $where = new AnnotationWhereClause(); |
|||
55 | 9 | $where->ids = $id; |
|||
56 | 9 | $qb->addClause($where); |
|||
57 | |||||
58 | 9 | $row = $this->db->getDataRow($qb); |
|||
59 | 9 | if ($row) { |
|||
60 | 9 | return new ElggAnnotation($row); |
|||
61 | } |
||||
62 | |||||
63 | 1 | return false; |
|||
64 | } |
||||
65 | |||||
66 | /** |
||||
67 | * Deletes an annotation using its ID |
||||
68 | * |
||||
69 | * @param ElggAnnotation $annotation Annotation |
||||
70 | * |
||||
71 | * @return bool |
||||
72 | */ |
||||
73 | 40 | public function delete(ElggAnnotation $annotation) { |
|||
74 | 40 | if (!$annotation->canEdit()) { |
|||
75 | return false; |
||||
76 | } |
||||
77 | |||||
78 | 40 | if (!$this->events->trigger('delete', 'annotation', $annotation)) { |
|||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
79 | return false; |
||||
80 | } |
||||
81 | |||||
82 | 40 | $qb = Delete::fromTable('annotations'); |
|||
83 | 40 | $qb->where($qb->compare('id', '=', $annotation->id, ELGG_VALUE_INTEGER)); |
|||
84 | 40 | $deleted = $this->db->deleteData($qb); |
|||
85 | |||||
86 | 40 | if ($deleted) { |
|||
87 | 40 | elgg_delete_river([ |
|||
88 | 40 | 'annotation_id' => $annotation->id, |
|||
89 | 'limit' => false, |
||||
90 | ]); |
||||
91 | } |
||||
92 | |||||
93 | 40 | return $deleted !== false; |
|||
94 | } |
||||
95 | |||||
96 | /** |
||||
97 | * Create a new annotation and return its ID |
||||
98 | * |
||||
99 | * @param ElggAnnotation $annotation Annotation |
||||
100 | * @param ElggEntity $entity Entity being annotated |
||||
101 | * |
||||
102 | * @return int|bool |
||||
103 | */ |
||||
104 | 128 | public function create(ElggAnnotation $annotation, ElggEntity $entity) { |
|||
105 | 128 | if ($annotation->id) { |
|||
106 | return $this->update($annotation); |
||||
107 | } |
||||
108 | |||||
109 | 128 | $annotation->entity_guid = $entity->guid; |
|||
110 | |||||
111 | // @todo It looks like annotations permissions are not being checked anywhere... |
||||
112 | // Uncomment once tests have been updated |
||||
113 | // See #11418 |
||||
114 | //if (!$entity->canAnnotate(0, $annotation->name)) { |
||||
115 | // return false; |
||||
116 | //} |
||||
117 | |||||
118 | 128 | if (!$this->events->trigger('annotate', $entity->getType(), $entity)) { |
|||
0 ignored issues
–
show
$entity of type ElggEntity is incompatible with the type string expected by parameter $object of Elgg\EventsService::trigger() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
119 | return false; |
||||
120 | } |
||||
121 | |||||
122 | 128 | if (!$this->events->triggerBefore('create', 'annotation', $annotation)) { |
|||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::triggerBefore() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
123 | return false; |
||||
124 | } |
||||
125 | |||||
126 | 128 | $time_created = $this->getCurrentTime()->getTimestamp(); |
|||
127 | |||||
128 | 128 | $qb = Insert::intoTable('annotations'); |
|||
129 | 128 | $qb->values([ |
|||
130 | 128 | 'entity_guid' => $qb->param($annotation->entity_guid, ELGG_VALUE_INTEGER), |
|||
131 | 128 | 'name' => $qb->param($annotation->name, ELGG_VALUE_STRING), |
|||
132 | 128 | 'value' => $qb->param($annotation->value, $annotation->value_type === 'integer' ? ELGG_VALUE_INTEGER : ELGG_VALUE_STRING), |
|||
133 | 128 | 'value_type' => $qb->param($annotation->value_type, ELGG_VALUE_STRING), |
|||
134 | 128 | 'owner_guid' => $qb->param($annotation->owner_guid, ELGG_VALUE_INTEGER), |
|||
135 | 128 | 'time_created' => $qb->param($time_created, ELGG_VALUE_INTEGER), |
|||
136 | 128 | 'access_id' => $qb->param($annotation->access_id, ELGG_VALUE_INTEGER), |
|||
137 | ]); |
||||
138 | |||||
139 | 128 | $result = $this->db->insertData($qb); |
|||
140 | 128 | if ($result === false) { |
|||
141 | return false; |
||||
142 | } |
||||
143 | |||||
144 | 128 | $annotation->id = $result; |
|||
145 | 128 | $annotation->time_created = $time_created; |
|||
146 | |||||
147 | 128 | if (!$this->events->trigger('create', 'annotation', $annotation)) { |
|||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::trigger() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
148 | elgg_delete_annotation_by_id($result); |
||||
149 | |||||
150 | return false; |
||||
151 | } |
||||
152 | |||||
153 | 128 | $this->events->triggerAfter('create', 'annotation', $annotation); |
|||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::triggerAfter() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
154 | |||||
155 | 128 | return $result; |
|||
156 | } |
||||
157 | |||||
158 | /** |
||||
159 | * Store updated annotation in the database |
||||
160 | * |
||||
161 | * @todo Add canAnnotate check if entity guid changes |
||||
162 | * |
||||
163 | * @param ElggAnnotation $annotation Annotation to store |
||||
164 | * |
||||
165 | * @return bool |
||||
166 | */ |
||||
167 | public function update(ElggAnnotation $annotation) { |
||||
168 | if (!$annotation->canEdit()) { |
||||
169 | return false; |
||||
170 | } |
||||
171 | |||||
172 | if (!$this->events->triggerBefore('update', 'annotation', $annotation)) { |
||||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::triggerBefore() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
173 | return false; |
||||
174 | } |
||||
175 | |||||
176 | $qb = Update::table('annotations'); |
||||
177 | $qb->set('name', $qb->param($annotation->name, ELGG_VALUE_STRING)) |
||||
178 | ->set('value', $qb->param($annotation->value, $annotation->value_type === 'integer' ? ELGG_VALUE_INTEGER : ELGG_VALUE_STRING)) |
||||
179 | ->set('value_type', $qb->param($annotation->value_type, ELGG_VALUE_STRING)) |
||||
180 | ->set('access_id', $qb->param($annotation->access_id, ELGG_VALUE_INTEGER)) |
||||
181 | ->set('owner_guid', $qb->param($annotation->owner_guid, ELGG_VALUE_INTEGER)) |
||||
182 | ->where($qb->compare('id', '=', $annotation->id, ELGG_VALUE_INTEGER)); |
||||
183 | |||||
184 | $result = $this->db->updateData($qb); |
||||
185 | |||||
186 | if ($result === false) { |
||||
187 | return false; |
||||
188 | } |
||||
189 | |||||
190 | $this->events->trigger('update', 'annotation', $annotation); |
||||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::trigger() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
191 | $this->events->triggerAfter('update', 'annotation', $annotation); |
||||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::triggerAfter() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
192 | |||||
193 | return $result; |
||||
194 | } |
||||
195 | |||||
196 | /** |
||||
197 | * Disable the annotation. |
||||
198 | * |
||||
199 | * @param ElggAnnotation $annotation Annotation |
||||
200 | * |
||||
201 | * @return bool |
||||
202 | * @since 1.8 |
||||
203 | */ |
||||
204 | 9 | public function disable(ElggAnnotation $annotation) { |
|||
205 | 9 | if ($annotation->enabled == 'no') { |
|||
206 | return true; |
||||
207 | } |
||||
208 | |||||
209 | 9 | if (!$annotation->canEdit()) { |
|||
210 | return false; |
||||
211 | } |
||||
212 | |||||
213 | 9 | if (!_elgg_services()->events->trigger('disable', $annotation->getType(), $annotation)) { |
|||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::trigger() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
214 | return false; |
||||
215 | } |
||||
216 | |||||
217 | 9 | if ($annotation->id) { |
|||
218 | 9 | $qb = Update::table('annotations'); |
|||
219 | 9 | $qb->set('enabled', $qb->param('no', ELGG_VALUE_STRING)) |
|||
220 | 9 | ->where($qb->compare('id', '=', $annotation->id, ELGG_VALUE_INTEGER)); |
|||
221 | |||||
222 | 9 | if (!$this->db->updateData($qb)) { |
|||
223 | return false; |
||||
224 | } |
||||
225 | } |
||||
226 | |||||
227 | 9 | $annotation->enabled = 'no'; |
|||
228 | |||||
229 | 9 | return true; |
|||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * Enable the annotation |
||||
234 | * |
||||
235 | * @param ElggAnnotation $annotation Annotation |
||||
236 | * |
||||
237 | * @return bool |
||||
238 | * @since 1.8 |
||||
239 | */ |
||||
240 | 4 | public function enable(ElggAnnotation $annotation) { |
|||
241 | 4 | if ($annotation->enabled == 'yes') { |
|||
242 | return true; |
||||
243 | } |
||||
244 | |||||
245 | 4 | if (!$annotation->canEdit()) { |
|||
246 | return false; |
||||
247 | } |
||||
248 | |||||
249 | 4 | if (!$this->events->trigger('enable', $annotation->getType(), $annotation)) { |
|||
0 ignored issues
–
show
$annotation of type ElggAnnotation is incompatible with the type string expected by parameter $object of Elgg\EventsService::trigger() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
250 | return false; |
||||
251 | } |
||||
252 | |||||
253 | 4 | if ($annotation->id) { |
|||
254 | 4 | $qb = Update::table('annotations'); |
|||
255 | 4 | $qb->set('enabled', $qb->param('yes', ELGG_VALUE_STRING)) |
|||
256 | 4 | ->where($qb->compare('id', '=', $annotation->id, ELGG_VALUE_INTEGER)); |
|||
257 | |||||
258 | 4 | if (!$this->db->updateData($qb)) { |
|||
259 | return false; |
||||
260 | } |
||||
261 | } |
||||
262 | |||||
263 | 4 | $annotation->enabled = 'yes'; |
|||
264 | |||||
265 | 4 | return true; |
|||
266 | } |
||||
267 | |||||
268 | /** |
||||
269 | * Returns annotations. Accepts all {@link elgg_get_entities()} options |
||||
270 | * |
||||
271 | * @see elgg_get_entities() |
||||
272 | * |
||||
273 | * @param array $options Options |
||||
274 | * |
||||
275 | * @return ElggAnnotation[]|mixed |
||||
276 | */ |
||||
277 | 22 | public function find(array $options = []) { |
|||
278 | 22 | $options['metastring_type'] = 'annotations'; |
|||
279 | 22 | $options = LegacyQueryOptionsAdapter::normalizeMetastringOptions($options); |
|||
280 | |||||
281 | 22 | return Annotations::find($options); |
|||
282 | } |
||||
283 | |||||
284 | /** |
||||
285 | * Deletes annotations based on $options. |
||||
286 | * |
||||
287 | * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
||||
288 | * This requires at least one constraint: annotation_owner_guid(s), |
||||
289 | * annotation_name(s), annotation_value(s), or guid(s) must be set. |
||||
290 | * |
||||
291 | * @see elgg_get_annotations() |
||||
292 | * @see elgg_get_entities() |
||||
293 | * |
||||
294 | * @param array $options Options |
||||
295 | * |
||||
296 | * @return bool|null true on success, false on failure, null if no annotations to delete. |
||||
297 | */ |
||||
298 | 252 | public function deleteAll(array $options) { |
|||
299 | 252 | if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|||
300 | return false; |
||||
301 | } |
||||
302 | |||||
303 | 252 | $options['batch'] = true; |
|||
304 | 252 | $options['batch_size'] = 50; |
|||
305 | 252 | $options['batch_inc_offset'] = false; |
|||
306 | |||||
307 | 252 | $annotations = Annotations::find($options); |
|||
308 | 252 | $count = $annotations->count(); |
|||
309 | |||||
310 | 252 | if (!$count) { |
|||
311 | 251 | return; |
|||
312 | } |
||||
313 | |||||
314 | 34 | $success = 0; |
|||
315 | 34 | foreach ($annotations as $annotation) { |
|||
316 | 34 | if ($annotation->delete()) { |
|||
317 | 34 | $success++; |
|||
318 | } |
||||
319 | } |
||||
320 | |||||
321 | 34 | return $success == $count; |
|||
322 | } |
||||
323 | |||||
324 | /** |
||||
325 | * Disables annotations based on $options. |
||||
326 | * |
||||
327 | * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
||||
328 | * |
||||
329 | * @param array $options An options array. {@link elgg_get_annotations()} |
||||
330 | * @return bool|null true on success, false on failure, null if no annotations disabled. |
||||
331 | */ |
||||
332 | 9 | public function disableAll(array $options) { |
|||
333 | 9 | if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|||
334 | return false; |
||||
335 | } |
||||
336 | |||||
337 | // if we can see hidden (disabled) we need to use the offset |
||||
338 | // otherwise we risk an infinite loop if there are more than 50 |
||||
339 | 9 | $inc_offset = _elgg_services()->session->getDisabledEntityVisibility(); |
|||
340 | |||||
341 | 9 | $options['batch'] = true; |
|||
342 | 9 | $options['batch_size'] = 50; |
|||
343 | 9 | $options['batch_inc_offset'] = $inc_offset; |
|||
344 | |||||
345 | 9 | $annotations = Annotations::find($options); |
|||
346 | 9 | $count = $annotations->count(); |
|||
347 | |||||
348 | 9 | if (!$count) { |
|||
349 | 4 | return; |
|||
350 | } |
||||
351 | |||||
352 | 6 | $success = 0; |
|||
353 | 6 | foreach ($annotations as $annotation) { |
|||
354 | 6 | if ($annotation->disable()) { |
|||
355 | 6 | $success++; |
|||
356 | } |
||||
357 | } |
||||
358 | |||||
359 | 6 | return $success == $count; |
|||
360 | } |
||||
361 | |||||
362 | /** |
||||
363 | * Enables annotations based on $options. |
||||
364 | * |
||||
365 | * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
||||
366 | * |
||||
367 | * @warning In order to enable annotations, you must first use |
||||
368 | * {@link access_show_hidden_entities()}. |
||||
369 | * |
||||
370 | * @param array $options An options array. {@link elgg_get_annotations()} |
||||
371 | * @return bool|null true on success, false on failure, null if no metadata enabled. |
||||
372 | */ |
||||
373 | 4 | public function enableAll(array $options) { |
|||
374 | 4 | if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|||
375 | return false; |
||||
376 | } |
||||
377 | |||||
378 | 4 | $options['batch'] = true; |
|||
379 | 4 | $options['batch_size'] = 50; |
|||
380 | |||||
381 | 4 | $annotations = Annotations::find($options); |
|||
382 | 4 | $count = $annotations->count(); |
|||
383 | |||||
384 | 4 | if (!$count) { |
|||
385 | 3 | return; |
|||
386 | } |
||||
387 | |||||
388 | 3 | $success = 0; |
|||
389 | 3 | foreach ($annotations as $annotation) { |
|||
390 | 3 | if ($annotation->enable()) { |
|||
391 | 3 | $success++; |
|||
392 | } |
||||
393 | } |
||||
394 | |||||
395 | 3 | return $success == $count; |
|||
396 | } |
||||
397 | |||||
398 | /** |
||||
399 | * Check to see if a user has already created an annotation on an object |
||||
400 | * |
||||
401 | * @param int $entity_guid Entity guid |
||||
402 | * @param string $name Annotation name |
||||
403 | * @param int $owner_guid Owner guid |
||||
404 | * |
||||
405 | * @return bool |
||||
406 | */ |
||||
407 | 1 | public function exists($entity_guid, $name, $owner_guid) { |
|||
408 | 1 | if (!$owner_guid) { |
|||
409 | return false; |
||||
410 | } |
||||
411 | |||||
412 | 1 | $qb = Select::fromTable('annotations'); |
|||
413 | 1 | $qb->select('id'); |
|||
414 | 1 | $qb->where($qb->compare('owner_guid', '=', $owner_guid, ELGG_VALUE_INTEGER)) |
|||
415 | 1 | ->andWhere($qb->compare('entity_guid', '=', $entity_guid, ELGG_VALUE_INTEGER)) |
|||
416 | 1 | ->andWhere($qb->compare('name', '=', $name, ELGG_VALUE_STRING)); |
|||
417 | |||||
418 | 1 | $result = $this->db->getDataRow($qb); |
|||
419 | |||||
420 | 1 | return $result && $result->id; |
|||
421 | } |
||||
422 | } |
||||
423 |