Completed
Push — stable9 ( 4170cb...e5f189 )
by Joas
7s
created

Data::get()   C

Complexity

Conditions 16
Paths 92

Size

Total Lines 80
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 44
CRAP Score 18.0478

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 80
ccs 44
cts 55
cp 0.8
rs 5.0401
cc 16
eloc 48
nc 92
nop 9
crap 18.0478

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Frank Karlitschek <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 *
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OCA\Activity;
26
27
use OCA\Activity\Exception\InvalidFilterException;
28
use OCP\Activity\IEvent;
29
use OCP\Activity\IExtension;
30
use OCP\Activity\IManager;
31
use OCP\DB\QueryBuilder\IQueryBuilder;
32
use OCP\IDBConnection;
33
use OCP\IL10N;
34
use OCP\IUser;
35
use OCP\IUserSession;
36
37
/**
38
 * @brief Class for managing the data in the activities
39
 */
40
class Data {
41
	/** @var IManager */
42
	protected $activityManager;
43
44
	/** @var IDBConnection */
45
	protected $connection;
46
47
	/** @var IUserSession */
48
	protected $userSession;
49
50
	/**
51
	 * @param IManager $activityManager
52
	 * @param IDBConnection $connection
53
	 * @param IUserSession $userSession
54
	 */
55 67
	public function __construct(IManager $activityManager, IDBConnection $connection, IUserSession $userSession) {
56 67
		$this->activityManager = $activityManager;
57 67
		$this->connection = $connection;
58 67
		$this->userSession = $userSession;
59 67
	}
60
61
	protected $notificationTypes = array();
62
63
	/**
64
	 * @param IL10N $l
65
	 * @return array Array "stringID of the type" => "translated string description for the setting"
66
	 * 				or Array "stringID of the type" => [
67
	 * 					'desc' => "translated string description for the setting"
68
	 * 					'methods' => [\OCP\Activity\IExtension::METHOD_*],
69
	 * 				]
70
	 */
71 8
	public function getNotificationTypes(IL10N $l) {
72 8
		if (isset($this->notificationTypes[$l->getLanguageCode()])) {
73 1
			return $this->notificationTypes[$l->getLanguageCode()];
74
		}
75
76
		// Allow apps to add new notification types
77 8
		$notificationTypes = $this->activityManager->getNotificationTypes($l->getLanguageCode());
78 8
		$this->notificationTypes[$l->getLanguageCode()] = $notificationTypes;
79 8
		return $notificationTypes;
80
	}
81
82
	/**
83
	 * Send an event into the activity stream
84
	 *
85
	 * @param IEvent $event
86
	 * @return bool
87
	 */
88 4
	public function send(IEvent $event) {
89 4
		if ($event->getAffectedUser() === '' || $event->getAffectedUser() === null) {
90 2
			return false;
91
		}
92
93
		// store in DB
94 2
		$queryBuilder = $this->connection->getQueryBuilder();
95 2
		$queryBuilder->insert('activity')
96 2
			->values([
97 2
				'app' => $queryBuilder->createParameter('app'),
98 2
				'subject' => $queryBuilder->createParameter('subject'),
99 2
				'subjectparams' => $queryBuilder->createParameter('subjectparams'),
100 2
				'message' => $queryBuilder->createParameter('message'),
101 2
				'messageparams' => $queryBuilder->createParameter('messageparams'),
102 2
				'file' => $queryBuilder->createParameter('object_name'),
103 2
				'link' => $queryBuilder->createParameter('link'),
104 2
				'user' => $queryBuilder->createParameter('user'),
105 2
				'affecteduser' => $queryBuilder->createParameter('affecteduser'),
106 2
				'timestamp' => $queryBuilder->createParameter('timestamp'),
107 2
				'priority' => $queryBuilder->createParameter('priority'),
108 2
				'type' => $queryBuilder->createParameter('type'),
109 2
				'object_type' => $queryBuilder->createParameter('object_type'),
110 2
				'object_id' => $queryBuilder->createParameter('object_id'),
111 2
			])
112 2
			->setParameters([
113 2
				'app' => $event->getApp(),
114 2
				'type' => $event->getType(),
115 2
				'affecteduser' => $event->getAffectedUser(),
116 2
				'user' => $event->getAuthor(),
117 2
				'timestamp' => (int) $event->getTimestamp(),
118 2
				'subject' => $event->getSubject(),
119 2
				'subjectparams' => json_encode($event->getSubjectParameters()),
120 2
				'message' => $event->getMessage(),
121 2
				'messageparams' => json_encode($event->getMessageParameters()),
122 2
				'priority' => IExtension::PRIORITY_MEDIUM,
123 2
				'object_type' => $event->getObjectType(),
124 2
				'object_id' => (int) $event->getObjectId(),
125 2
				'object_name' => $event->getObjectName(),
126 2
				'link' => $event->getLink(),
127 2
			])
128 2
			->execute();
129
130 2
		return true;
131
	}
132
133
	/**
134
	 * Send an event as email
135
	 *
136
	 * @param IEvent $event
137
	 * @param int    $latestSendTime Activity $timestamp + batch setting of $affectedUser
138
	 * @return bool
139
	 */
140 4
	public function storeMail(IEvent $event, $latestSendTime) {
141 4
		if ($event->getAffectedUser() === '' || $event->getAffectedUser() === null) {
142 2
			return false;
143
		}
144
145
		// store in DB
146 2
		$queryBuilder = $this->connection->getQueryBuilder();
147 2
		$queryBuilder->insert('activity_mq')
148 2
			->values([
149 2
				'amq_appid' => $queryBuilder->createParameter('app'),
150 2
				'amq_subject' => $queryBuilder->createParameter('subject'),
151 2
				'amq_subjectparams' => $queryBuilder->createParameter('subjectparams'),
152 2
				'amq_affecteduser' => $queryBuilder->createParameter('affecteduser'),
153 2
				'amq_timestamp' => $queryBuilder->createParameter('timestamp'),
154 2
				'amq_type' => $queryBuilder->createParameter('type'),
155 2
				'amq_latest_send' => $queryBuilder->createParameter('latest_send'),
156 2
			])
157 2
			->setParameters([
158 2
				'app' => $event->getApp(),
159 2
				'subject' => $event->getSubject(),
160 2
				'subjectparams' => json_encode($event->getSubjectParameters()),
161 2
				'affecteduser' => $event->getAffectedUser(),
162 2
				'timestamp' => (int) $event->getTimestamp(),
163 2
				'type' => $event->getType(),
164 2
				'latest_send' => $latestSendTime,
165 2
			])
166 2
			->execute();
167
168 2
		return true;
169
	}
170
171
	/**
172
	 * Read a list of events from the activity stream
173
	 *
174
	 * @param GroupHelper $groupHelper Allows activities to be grouped
175
	 * @param UserSettings $userSettings Gets the settings of the user
176
	 * @param string $user User for whom we display the stream
177
	 *
178
	 * @param int $since The integer ID of the last activity that has been seen.
179
	 * @param int $limit How many activities should be returned
180
	 * @param string $sort Should activities be given ascending or descending
181
	 *
182
	 * @param string $filter Filter the activities
183
	 * @param string $objectType Allows to filter the activities to a given object. May only appear together with $objectId
184
	 * @param int $objectId Allows to filter the activities to a given object. May only appear together with $objectType
185
	 *
186
	 * @return array
187
	 *
188
	 * @throws \OutOfBoundsException if the user (Code: 1) or the since (Code: 2) is invalid
189
	 * @throws \BadMethodCallException if the user has selected to display no types for this filter (Code: 3)
190
	 */
191 15
	public function get(GroupHelper $groupHelper, UserSettings $userSettings, $user, $since, $limit, $sort, $filter, $objectType = '', $objectId = 0) {
192
		// get current user
193 15
		if ($user === '') {
194 1
			throw new \OutOfBoundsException('Invalid user', 1);
195
		}
196 14
		$groupHelper->setUser($user);
197
198 14
		$enabledNotifications = $userSettings->getNotificationTypes($user, 'stream');
199 14
		$enabledNotifications = $this->activityManager->filterNotificationTypes($enabledNotifications, $filter);
200 14
		$enabledNotifications = array_unique($enabledNotifications);
201
202
		// We don't want to display any activities
203 14
		if (empty($enabledNotifications)) {
204 1
			throw new \BadMethodCallException('No settings enabled', 3);
205
		}
206
207 13
		$query = $this->connection->getQueryBuilder();
208 13
		$query->select('*')
209 13
			->from('activity');
210
211 13
		$query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($user)))
212 13
			->andWhere($query->expr()->in('type', $query->createNamedParameter($enabledNotifications, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)));
213 13
		if ($filter === 'self') {
214 2
			$query->andWhere($query->expr()->eq('user', $query->createNamedParameter($user)));
215
216 13
		} else if ($filter === 'by' || $filter === 'all' && !$userSettings->getUserSetting($user, 'setting', 'self')) {
217 2
			$query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user)));
218
219 11
		} else if ($filter === 'filter') {
220 2
			if (!$userSettings->getUserSetting($user, 'setting', 'self')) {
221 2
				$query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user)));
222 2
			}
223
224 2
			$query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)));
225 2
			$query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
226 2
		}
227
228 13
		list($condition, $params) = $this->activityManager->getQueryForFilter($filter);
229 13
		if (!is_null($condition)) {
230
			// Strip away ' and '
231
			$condition = substr($condition, 5);
232
233
			if (is_array($params)) {
234
				// Replace ? placeholders with named parameters
235
				foreach ($params as $param) {
236
					$pos = strpos($condition, '?');
237
					if ($pos !== false) {
238
						$condition = substr($condition, 0, $pos) . $query->createNamedParameter($param) . substr($condition, $pos + 1);
239
					}
240
				}
241
			}
242
243
			$query->andWhere($query->createFunction($condition));
244
		}
245
246
		/**
247
		 * Order and specify the offset
248
		 */
249 13
		$sqlSort = ($sort === 'asc') ? 'ASC' : 'DESC';
250 13
		$headers = $this->setOffsetFromSince($query, $user, $since, $sqlSort);
251 13
		$query->orderBy('timestamp', $sqlSort)
252 13
			->addOrderBy('activity_id', $sqlSort);
253
254 13
		$query->setMaxResults($limit + 1);
255
256 13
		$result = $query->execute();
257 13
		$hasMore = false;
258 13
		while ($row = $result->fetch()) {
259 12
			if ($limit === 0) {
260 10
				$hasMore = true;
261 10
				break;
262
			}
263 12
			$headers['X-Activity-Last-Given'] = (int) $row['activity_id'];
264 12
			$groupHelper->addActivity($row);
265 12
			$limit--;
266 12
		}
267 13
		$result->closeCursor();
268
269 13
		return ['data' => $groupHelper->getActivities(), 'has_more' => $hasMore, 'headers' => $headers];
270
	}
271
272
	/**
273
	 * @param IQueryBuilder $query
274
	 * @param string $user
275
	 * @param int $since
276
	 * @param string $sort
277
	 *
278
	 * @return array Headers that should be set on the response
279
	 *
280
	 * @throws \OutOfBoundsException If $since is not owned by $user
281
	 */
282 18
	protected function setOffsetFromSince(IQueryBuilder $query, $user, $since, $sort) {
283 18
		if ($since) {
284 5
			$queryBuilder = $this->connection->getQueryBuilder();
285 5
			$queryBuilder->select('*')
286 5
				->from('activity')
287 5
				->where($queryBuilder->expr()->eq('activity_id', $queryBuilder->createNamedParameter((int) $since)));
288 5
			$result = $queryBuilder->execute();
289 5
			$activity = $result->fetch();
290 5
			$result->closeCursor();
291
292 5
			if ($activity) {
293 4
				if ($activity['affecteduser'] !== $user) {
294 1
					throw new \OutOfBoundsException('Invalid since', 2);
295
				}
296 3
				$timestamp = (int) $activity['timestamp'];
297
298 3
				if ($sort === 'DESC') {
299 2
					$query->andWhere($query->expr()->lte('timestamp', $query->createNamedParameter($timestamp)));
300 2
					$query->andWhere($query->expr()->lt('activity_id', $query->createNamedParameter($since)));
301 2
				} else {
302 1
					$query->andWhere($query->expr()->gte('timestamp', $query->createNamedParameter($timestamp)));
303 1
					$query->andWhere($query->expr()->gt('activity_id', $query->createNamedParameter($since)));
304
				}
305 3
				return [];
306
			}
307 1
		}
308
309
		/**
310
		 * Couldn't find the since, so find the oldest one and set the header
311
		 */
312 14
		$fetchQuery = $this->connection->getQueryBuilder();
313 14
		$fetchQuery->select('activity_id')
314 14
			->from('activity')
315 14
			->where($fetchQuery->expr()->eq('affecteduser', $fetchQuery->createNamedParameter($user)))
316 14
			->orderBy('timestamp', $sort)
317 14
			->setMaxResults(1);
318 14
		$result = $fetchQuery->execute();
319 14
		$activity = $result->fetch();
320 14
		$result->closeCursor();
321
322 14
		if ($activity !== false) {
323
			return [
324 12
				'X-Activity-First-Known' => (int) $activity['activity_id'],
325 12
			];
326
		}
327
328 2
		return [];
329
	}
330
331
	/**
332
	 * Verify that the filter is valid
333
	 *
334
	 * @param string $filterValue
335
	 * @return string
336
	 */
337 6
	public function validateFilter($filterValue) {
338 6
		if (!isset($filterValue)) {
339 1
			return 'all';
340
		}
341
342
		switch ($filterValue) {
343 5
			case 'by':
344 5
			case 'self':
345 5
			case 'all':
346 5
			case 'filter':
347 3
				return $filterValue;
348 2
			default:
349 2
				if ($this->activityManager->isFilterValid($filterValue)) {
350 1
					return $filterValue;
351
				}
352 1
				return 'all';
353 2
		}
354
	}
355
356
	/**
357
	 * Delete old events
358
	 *
359
	 * @param int $expireDays Minimum 1 day
360
	 * @return null
361
	 */
362 2
	public function expire($expireDays = 365) {
363 2
		$ttl = (60 * 60 * 24 * max(1, $expireDays));
364
365 2
		$timelimit = time() - $ttl;
366 2
		$this->deleteActivities(array(
367 2
			'timestamp' => array($timelimit, '<'),
368 2
		));
369 2
	}
370
371
	/**
372
	 * Delete activities that match certain conditions
373
	 *
374
	 * @param array $conditions Array with conditions that have to be met
375
	 *                      'field' => 'value'  => `field` = 'value'
376
	 *    'field' => array('value', 'operator') => `field` operator 'value'
377
	 * @return null
378
	 */
379 12
	public function deleteActivities($conditions) {
380 12
		$sqlWhere = '';
381 12
		$sqlParameters = $sqlWhereList = array();
382 12
		foreach ($conditions as $column => $comparison) {
383 12
			$sqlWhereList[] = " `$column` " . ((is_array($comparison) && isset($comparison[1])) ? $comparison[1] : '=') . ' ? ';
384 12
			$sqlParameters[] = (is_array($comparison)) ? $comparison[0] : $comparison;
385 12
		}
386
387 12
		if (!empty($sqlWhereList)) {
388 12
			$sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList);
389 12
		}
390
391 12
		$query = $this->connection->prepare(
392 12
			'DELETE FROM `*PREFIX*activity`' . $sqlWhere);
393 12
		$query->execute($sqlParameters);
394 12
	}
395
}
396