Completed
Pull Request — master (#111)
by Joas
01:43
created

Manager::getAnnouncements()   F

Complexity

Conditions 12
Paths 544

Size

Total Lines 70

Duplication

Lines 6
Ratio 8.57 %

Code Coverage

Tests 48
CRAP Score 12

Importance

Changes 0
Metric Value
dl 6
loc 70
ccs 48
cts 48
cp 1
rs 3.4012
c 0
b 0
f 0
cc 12
nc 544
nop 3
crap 12

How to fix   Long Method    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
 * @copyright Copyright (c) 2016, Joas Schilling <[email protected]>
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\AnnouncementCenter;
25
26
use OCP\BackgroundJob\IJobList;
27
use OCP\Comments\ICommentsManager;
28
use OCP\DB\QueryBuilder\IQueryBuilder;
29
use OCP\IConfig;
30
use OCP\IDBConnection;
31
use OCP\IGroupManager;
32
use OCP\Notification\IManager as INotificationManager;
33
use OCP\IUser;
34
use OCP\IUserSession;
35
36
class Manager {
37
38
	/** @var IConfig */
39
	protected $config;
40
41
	/** @var IDBConnection */
42
	protected $connection;
43
44
	/** @var IGroupManager */
45
	protected $groupManager;
46
47
	/** @var INotificationManager */
48
	protected $notificationManager;
49
50
	/** @var ICommentsManager */
51
	protected $commentsManager;
52
53
	/** @var IJobList */
54
	protected $jobList;
55
56
	/** @var IUserSession */
57
	protected $userSession;
58
59
	/**
60
	 * @param IConfig $config
61
	 * @param IDBConnection $connection
62
	 * @param IGroupManager $groupManager
63
	 * @param INotificationManager $notificationManager
64
	 * @param ICommentsManager $commentsManager
65
	 * @param IJobList $jobList
66
	 * @param IUserSession $userSession
67
	 */
68 21
	public function __construct(IConfig $config,
69
								IDBConnection $connection,
70
								IGroupManager $groupManager,
71
								INotificationManager $notificationManager,
72
								ICommentsManager $commentsManager,
73
								IJobList $jobList,
74
								IUserSession $userSession) {
75 21
		$this->config = $config;
76 21
		$this->connection = $connection;
77 21
		$this->groupManager = $groupManager;
78 21
		$this->notificationManager = $notificationManager;
79 21
		$this->commentsManager = $commentsManager;
80 21
		$this->jobList = $jobList;
81 21
		$this->userSession = $userSession;
82 21
	}
83
84
	/**
85
	 * @param string $subject
86
	 * @param string $message
87
	 * @param string $user
88
	 * @param int $time
89
	 * @param string[] $groups
90
	 * @param bool $comments
91
	 * @return array
92
	 * @throws \InvalidArgumentException when the subject is empty or invalid
93
	 */
94 7
	public function announce($subject, $message, $user, $time, array $groups, $comments) {
95 7
		$subject = trim($subject);
96 7
		$message = trim($message);
97 7
		if (isset($subject[512])) {
98 1
			throw new \InvalidArgumentException('Invalid subject', 1);
99
		}
100
101 6
		if ($subject === '') {
102 1
			throw new \InvalidArgumentException('Invalid subject', 2);
103
		}
104
105 5
		$queryBuilder = $this->connection->getQueryBuilder();
106 5
		$queryBuilder->insert('announcements')
107 5
			->values([
108 5
				'announcement_time' => $queryBuilder->createNamedParameter($time),
109 5
				'announcement_user' => $queryBuilder->createNamedParameter($user),
110 5
				'announcement_subject' => $queryBuilder->createNamedParameter($subject),
111 5
				'announcement_message' => $queryBuilder->createNamedParameter($message),
112 5
				'allow_comments' => $queryBuilder->createNamedParameter((int) $comments),
113
			]);
114 5
		$queryBuilder->execute();
115
116 5
		$id = $queryBuilder->getLastInsertId();
117
118 5
		$addedGroups = 0;
119 5
		foreach ($groups as $group) {
120 5
			if ($this->groupManager->groupExists($group)) {
121 4
				$this->addGroupLink((int) $id, $group);
122 5
				$addedGroups++;
123
			}
124
		}
125
126 5
		if ($addedGroups === 0) {
127 4
			$this->addGroupLink((int) $id, 'everyone');
128
		}
129
130 5
		return $this->getAnnouncement($id, true, true);
131
	}
132
133
	/**
134
	 * @param int $announcementId
135
	 * @param string $group
136
	 */
137 5
	protected function addGroupLink($announcementId, $group) {
138 5
		$query = $this->connection->getQueryBuilder();
139 5
		$query->insert('announcements_groups')
140 5
			->values([
141 5
				'announcement_id' => $query->createNamedParameter($announcementId),
142 5
				'gid' => $query->createNamedParameter($group),
143
			]);
144 5
		$query->execute();
145 5
	}
146
147
	/**
148
	 * @param int $id
149
	 */
150 5
	public function delete($id) {
151
		// Delete notifications
152 5
		$notification = $this->notificationManager->createNotification();
153 5
		$notification->setApp('announcementcenter')
154 5
			->setObject('announcement', $id);
155 5
		$this->notificationManager->markProcessed($notification);
156
157
		// Delete comments
158 5
		$this->commentsManager->deleteCommentsAtObject('announcement', (string) $id);
159
160 5
		$query = $this->connection->getQueryBuilder();
161 5
		$query->delete('announcements')
162 5
			->where($query->expr()->eq('announcement_id', $query->createNamedParameter((int) $id)));
163 5
		$query->execute();
164
165 5
		$query = $this->connection->getQueryBuilder();
166 5
		$query->delete('announcements_groups')
167 5
			->where($query->expr()->eq('announcement_id', $query->createNamedParameter((int) $id)));
168 5
		$query->execute();
169 5
	}
170
171
	/**
172
	 * @param int $id
173
	 * @param bool $parseStrings
174
	 * @param bool $ignorePermissions
175
	 * @param bool $returnGroups
176
	 * @return array
177
	 * @throws \InvalidArgumentException when the id is invalid
178
	 */
179 6
	public function getAnnouncement($id, $parseStrings = true, $ignorePermissions = false, $returnGroups = true) {
180 6
		if (!$ignorePermissions) {
181 4
			$user = $this->userSession->getUser();
182 4 View Code Duplication
			if ($user instanceof IUser) {
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

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...
183 2
				$userGroups = $this->groupManager->getUserGroupIds($user);
184 2
				$userGroups[] = 'everyone';
185
			} else {
186 2
				$userGroups = ['everyone'];
187
			}
188 4
			$isInAdminGroups = array_intersect($this->getAdminGroups(), $userGroups);
189
190 4
			if (empty($isInAdminGroups)) {
191 3
				$query = $this->connection->getQueryBuilder();
192 3
				$query->select('*')
193 3
					->from('announcements_groups')
194 3
					->where($query->expr()->eq('announcement_id', $query->createNamedParameter((int) $id)))
195 3
					->andWhere($query->expr()->in('gid', $query->createNamedParameter($userGroups, IQueryBuilder::PARAM_STR_ARRAY)))
196 3
					->setMaxResults(1);
197 3
				$result = $query->execute();
198 3
				$entry = $result->fetch();
199 3
				$result->closeCursor();
200
201 3
				if (!$entry) {
202 3
					throw new \InvalidArgumentException('Invalid ID');
203
				}
204
			}
205
		}
206
207 5
		$queryBuilder = $this->connection->getQueryBuilder();
208 5
		$query = $queryBuilder->select('*')
209 5
			->from('announcements')
210 5
			->where($queryBuilder->expr()->eq('announcement_id', $queryBuilder->createParameter('id')))
211 5
			->setParameter('id', (int) $id);
212 5
		$result = $query->execute();
213 5
		$row = $result->fetch();
214 5
		$result->closeCursor();
215
216 5
		if ($row === false) {
217 3
			throw new \InvalidArgumentException('Invalid ID');
218
		}
219
220 5
		$groups = null;
221 5
		if ($returnGroups && ($ignorePermissions || !empty($isInAdminGroups))) {
222 5
			$groups = $this->getGroups((int) $id);
223
		}
224
225
		$announcement = [
226 5
			'id'		=> (int) $row['announcement_id'],
227 5
			'author'	=> $row['announcement_user'],
228 5
			'time'		=> (int) $row['announcement_time'],
229 5
			'subject'	=> $parseStrings ? $this->parseSubject($row['announcement_subject']) : $row['announcement_subject'],
230 5
			'message'	=> $parseStrings ? $this->parseMessage($row['announcement_message']) : $row['announcement_message'],
231 5
			'groups'	=> $groups,
232 5
			'comments'	=> $row['allow_comments'] ? 0 : false,
233
		];
234
235 5
		if ($ignorePermissions || !empty($isInAdminGroups)) {
236 5
			$announcement['notifications'] = $this->hasNotifications((int) $id);
237
		}
238
239 5
		return $announcement;
240
	}
241
242
	/**
243
	 * @param int $limit
244
	 * @param int $offset
245
	 * @param bool $parseStrings
246
	 * @return array
247
	 */
248 3
	public function getAnnouncements($limit = 15, $offset = 0, $parseStrings = true) {
249 3
		$query = $this->connection->getQueryBuilder();
250 3
		$query->select('a.announcement_id')
251 3
			->from('announcements', 'a')
252 3
			->orderBy('a.announcement_time', 'DESC')
253 3
			->groupBy('a.announcement_id')
254 3
			->setMaxResults($limit);
255
256 3
		$user = $this->userSession->getUser();
257 3 View Code Duplication
		if ($user instanceof IUser) {
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

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...
258 2
			$userGroups = $this->groupManager->getUserGroupIds($user);
259 2
			$userGroups[] = 'everyone';
260
		} else {
261 1
			$userGroups = ['everyone'];
262
		}
263
264 3
		$isInAdminGroups = array_intersect($this->getAdminGroups(), $userGroups);
265 3
		if (empty($isInAdminGroups)) {
266 2
			$query->leftJoin('a', 'announcements_groups', 'ag', $query->expr()->eq(
267 2
					'a.announcement_id', 'ag.announcement_id'
268
				))
269 2
				->andWhere($query->expr()->in('ag.gid', $query->createNamedParameter($userGroups, IQueryBuilder::PARAM_STR_ARRAY)));
270
		}
271
272 3
		if ($offset > 0) {
273 3
			$query->andWhere($query->expr()->lt('a.announcement_id', $query->createNamedParameter($offset, IQueryBuilder::PARAM_INT)));
274
		}
275
276 3
		$ids = [];
277 3
		$result = $query->execute();
278 3
		while ($row = $result->fetch()) {
279 3
			$ids[] = (int) $row['announcement_id'];
280
		}
281 3
		$result->closeCursor();
282
283 3
		$query = $this->connection->getQueryBuilder();
284 3
		$query->select('*')
285 3
			->from('announcements')
286 3
			->orderBy('announcement_time', 'DESC')
287 3
			->where($query->expr()->in('announcement_id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)));
288 3
		$result = $query->execute();
289
290 3
		$announcements = [];
291 3
		while ($row = $result->fetch()) {
292 3
			$id = (int) $row['announcement_id'];
293 3
			$announcements[$id] = [
294 3
				'id'		=> $id,
295 3
				'author'	=> $row['announcement_user'],
296 3
				'time'		=> (int) $row['announcement_time'],
297 3
				'subject'	=> $parseStrings ? $this->parseSubject($row['announcement_subject']) : $row['announcement_subject'],
298 3
				'message'	=> $parseStrings ? $this->parseMessage($row['announcement_message']) : $row['announcement_message'],
299
				'groups'	=> null,
300 3
				'comments'	=> $row['allow_comments'] ? $this->getNumberOfComments($id) : false,
301
			];
302
303 3
			if (!empty($isInAdminGroups)) {
304 1
				$announcements[$id]['notifications'] = $this->hasNotifications($id);
305
			}
306
		}
307 3
		$result->closeCursor();
308
309 3
		if (!empty($isInAdminGroups)) {
310 1
			$allGroups = $this->getGroups(array_keys($announcements));
311 1
			foreach ($allGroups as $id => $groups) {
312 1
				$announcements[$id]['groups'] = $groups;
313
			}
314
		}
315
316 3
		return $announcements;
317
	}
318
319
	/**
320
	 * Return the groups (or string everyone) which have access to the announcement(s)
321
	 *
322
	 * @param int|int[] $ids
323
	 * @return string[]|array[]
324
	 */
325 5
	public function getGroups($ids) {
326 5
		$returnSingleResult = false;
327 5
		if (is_int($ids)) {
328 5
			$ids = [$ids];
329 5
			$returnSingleResult = true;
330
		}
331
332 5
		$query = $this->connection->getQueryBuilder();
333 5
		$query->select('*')
334 5
			->from('announcements_groups')
335 5
			->where($query->expr()->in('announcement_id', $query->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)));
336 5
		$result = $query->execute();
337
338 5
		$groups = [];
339 5
		while ($row = $result->fetch()) {
340 5
			if (!isset($groups[(int) $row['announcement_id']])) {
341 5
				$groups[(int) $row['announcement_id']] = [];
342
			}
343 5
			$groups[(int) $row['announcement_id']][] = $row['gid'];
344
		}
345 5
		$result->closeCursor();
346
347 5
		return $returnSingleResult ? (array) array_pop($groups) : $groups;
348
	}
349
350
	/**
351
	 * @param int $id
352
	 */
353
	public function markNotificationRead($id) {
354
		$user = $this->userSession->getUser();
355
356
		if ($user instanceof IUser) {
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
357
			$notification = $this->notificationManager->createNotification();
358
			$notification->setApp('announcementcenter')
359
				->setUser($user->getUID())
360
				->setObject('announcement', $id);
361
			$this->notificationManager->markProcessed($notification);
362
		}
363
	}
364
365
	/**
366
	 * @param int $id
367
	 * @return int
368
	 */
369 2
	protected function getNumberOfComments($id) {
370 2
		return $this->commentsManager->getNumberOfCommentsForObject('announcement', (string) $id);
371
	}
372
373
	/**
374
	 * @param int $id
375
	 * @return bool
376
	 */
377 5
	protected function hasNotifications($id) {
378 5
		$hasJob = $this->jobList->has('OCA\AnnouncementCenter\BackgroundJob', [
379 5
			'id' => $id,
380
			'activities' => true,
381
			'notifications' => true,
382
		]);
383
384 5
		$hasJob = $hasJob || $this->jobList->has('OCA\AnnouncementCenter\BackgroundJob', [
385 5
			'id' => $id,
386
			'activities' => false,
387
			'notifications' => true,
388
		]);
389
390 5
		if ($hasJob) {
391
			return true;
392
		}
393
394 5
		$notification = $this->notificationManager->createNotification();
395 5
		$notification->setApp('announcementcenter')
396 5
			->setObject('announcement', $id);
397 5
		return $this->notificationManager->getCount($notification) > 0;
398
	}
399
400
	/**
401
	 * @param int $id
402
	 */
403
	public function removeNotifications($id) {
404
		if ($this->jobList->has('OCA\AnnouncementCenter\BackgroundJob', [
405
			'id' => $id,
406
			'activities' => true,
407
			'notifications' => true,
408
		])) {
409
			// Delete the current background job and add a new one without notifications
410
			$this->jobList->remove('OCA\AnnouncementCenter\BackgroundJob', [
411
				'id' => $id,
412
				'activities' => true,
413
				'notifications' => true,
414
			]);
415
			$this->jobList->add('OCA\AnnouncementCenter\BackgroundJob', [
416
				'id' => $id,
417
				'activities' => true,
418
				'notifications' => false,
419
			]);
420
421
		} else {
422
			$this->jobList->remove('OCA\AnnouncementCenter\BackgroundJob', [
423
				'id' => $id,
424
				'activities' => false,
425
				'notifications' => true,
426
			]);
427
		}
428
429
		$notification = $this->notificationManager->createNotification();
430
		$notification->setApp('announcementcenter')
431
			->setObject('announcement', $id);
432
		$this->notificationManager->markProcessed($notification);
433
	}
434
435
	/**
436
	 * @param string $message
437
	 * @return string
438
	 */
439 5
	protected function parseMessage($message) {
440 5
		return str_replace("\n", '<br />', str_replace(['<', '>'], ['&lt;', '&gt;'], $message));
441
	}
442
443
	/**
444
	 * @param string $subject
445
	 * @return string
446
	 */
447 5
	protected function parseSubject($subject) {
448 5
		return str_replace("\n", ' ', str_replace(['<', '>'], ['&lt;', '&gt;'], $subject));
449
	}
450
451
	/**
452
	 * Check if the user is in the admin group
453
	 * @return bool
454
	 */
455 6
	public function checkIsAdmin() {
456 6
		$user = $this->userSession->getUser();
457
458 6
		if ($user instanceof IUser) {
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
459 5
			$groups = $this->getAdminGroups();
460 5
			foreach ($groups as $group) {
461 5
				if ($this->groupManager->isInGroup($user->getUID(), $group)) {
462 5
					return true;
463
				}
464
			}
465
		}
466
467 3
		return false;
468
	}
469
470 9
	protected function getAdminGroups() {
471 9
		$adminGroups = $this->config->getAppValue('announcementcenter', 'admin_groups', '["admin"]');
472 9
		$adminGroups = json_decode($adminGroups, true);
473 9
		return $adminGroups;
474
	}
475
}
476