Completed
Push — 3.0 ( a99576...2abced )
by Jeroen
87:04 queued 29:21
created

engine/classes/Elgg/Database/AnnotationsTable.php (10 issues)

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
$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 ignore-type  annotation

78
		if (!$this->events->trigger('delete', 'annotation', /** @scrutinizer ignore-type */ $annotation)) {
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 ignore-type  annotation

118
		if (!$this->events->trigger('annotate', $entity->getType(), /** @scrutinizer ignore-type */ $entity)) {
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 ignore-type  annotation

122
		if (!$this->events->triggerBefore('create', 'annotation', /** @scrutinizer ignore-type */ $annotation)) {
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 ignore-type  annotation

147
		if (!$this->events->trigger('create', 'annotation', /** @scrutinizer ignore-type */ $annotation)) {
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 ignore-type  annotation

153
		$this->events->triggerAfter('create', 'annotation', /** @scrutinizer ignore-type */ $annotation);
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 ignore-type  annotation

172
		if (!$this->events->triggerBefore('update', 'annotation', /** @scrutinizer ignore-type */ $annotation)) {
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 ignore-type  annotation

190
		$this->events->trigger('update', 'annotation', /** @scrutinizer ignore-type */ $annotation);
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 ignore-type  annotation

191
		$this->events->triggerAfter('update', 'annotation', /** @scrutinizer ignore-type */ $annotation);
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 ignore-type  annotation

213
		if (!_elgg_services()->events->trigger('disable', $annotation->getType(), /** @scrutinizer ignore-type */ $annotation)) {
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 ignore-type  annotation

249
		if (!$this->events->trigger('enable', $annotation->getType(), /** @scrutinizer ignore-type */ $annotation)) {
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