Completed
Pull Request — master (#434)
by Joas
03:41
created

Data::get()   C

Complexity

Conditions 16
Paths 92

Size

Total Lines 80
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 30.2478

Importance

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

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
/**
4
 * ownCloud - Activity App
5
 *
6
 * @author Frank Karlitschek
7
 * @author Joas Schilling
8
 * @copyright 2013 Frank Karlitschek [email protected]
9
 *
10
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
12
 * License as published by the Free Software Foundation; either
13
 * version 3 of the License, or any later version.
14
 *
15
 * This library 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
21
 * License along with this library.  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 53
	public function __construct(IManager $activityManager, IDBConnection $connection, IUserSession $userSession) {
56 53
		$this->activityManager = $activityManager;
57 53
		$this->connection = $connection;
58 53
		$this->userSession = $userSession;
59 53
	}
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
	 */
190 5
	public function get(GroupHelper $groupHelper, UserSettings $userSettings, $user, $since, $limit, $sort, $filter, $objectType = '', $objectId = 0) {
191
		// get current user
192 5
		if ($user === '') {
193
			throw new \OutOfBoundsException('Invalid user', 1);
194
		}
195 5
		$groupHelper->setUser($user);
196
197 5
		$enabledNotifications = $userSettings->getNotificationTypes($user, 'stream');
198 5
		$enabledNotifications = $this->activityManager->filterNotificationTypes($enabledNotifications, $filter);
199 5
		$enabledNotifications = array_unique($enabledNotifications);
200
201
		// We don't want to display any activities
202 5
		if (empty($enabledNotifications)) {
203
			return array();
204
		}
205
206 5
		$query = $this->connection->getQueryBuilder();
207 5
		$query->select('*')
208 5
			->from('activity');
209
210 5
		$query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($user)))
211 5
			->andWhere($query->expr()->in('type', $query->createNamedParameter($enabledNotifications, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)));
212 5
		if ($filter === 'self') {
213
			$query->andWhere($query->expr()->eq('user', $query->createNamedParameter($user)));
214
215 5
		} else if ($filter === 'by' || $filter === 'all' && !$userSettings->getUserSetting($user, 'setting', 'self')) {
216
			$query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user)));
217
218 5
		} else if ($filter === 'filter') {
219
			if (!$userSettings->getUserSetting($user, 'setting', 'self')) {
220
				$query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user)));
221
			}
222
223
			$query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)));
224
			$query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
225
		}
226
227 5
		list($condition, $params) = $this->activityManager->getQueryForFilter($filter);
228 5
		if (!is_null($condition)) {
229
			// Strip away ' and '
230
			$condition = substr($condition, 5);
231
232
			if (is_array($params)) {
233
				// Replace ? placeholders with named parameters
234
				foreach ($params as $param) {
235
					$pos = strpos($condition, '?');
236
					if ($pos !== false) {
237
						$condition = substr($condition, 0, $pos) . $query->createNamedParameter($param) . substr($condition, $pos + 1);
238
					}
239
				}
240
			}
241
242
			$query->andWhere($query->createFunction($condition));
243
		}
244
245
		/**
246
		 * Order and specify the offset
247
		 */
248 5
		$sqlSort = ($sort === 'asc') ? 'ASC' : 'DESC';
249 5
		$headers = $this->setOffsetFromSince($query, $user, $since, $sqlSort);
250 5
		$query->orderBy('timestamp', $sqlSort)
251 5
			->addOrderBy('activity_id', $sqlSort);
252
253 5
		$query->setMaxResults($limit + 1);
254
255 5
		$result = $query->execute();
256 5
		$hasMore = false;
257 5
		while ($row = $result->fetch()) {
258 4
			if ($limit === 0) {
259 2
				$hasMore = true;
260 2
				break;
261
			}
262 4
			$headers['X-Activity-Last-Given'] = (int) $row['activity_id'];
263 4
			$groupHelper->addActivity($row);
264 4
			$limit--;
265 4
		}
266 5
		$result->closeCursor();
267
268 5
		return ['data' => $groupHelper->getActivities(), 'has_more' => $hasMore, 'headers' => $headers];
269
	}
270
271
	/**
272
	 * @param IQueryBuilder $query
273
	 * @param string $user
274
	 * @param int $since
275
	 * @param string $sort
276
	 *
277
	 * @return array Headers that should be set on the response
278
	 *
279
	 * @throws \OutOfBoundsException If $since is not owned by $user
280
	 */
281 5
	protected function setOffsetFromSince(IQueryBuilder $query, $user, $since, $sort) {
282 5
		if ($since) {
283 1
			$queryBuilder = $this->connection->getQueryBuilder();
284 1
			$queryBuilder->select('*')
285 1
				->from('activity')
286 1
				->where($queryBuilder->expr()->eq('activity_id', $queryBuilder->createNamedParameter((int) $since)));
287 1
			$result = $queryBuilder->execute();
288 1
			$activity = $result->fetch();
289 1
			$result->closeCursor();
290
291 1
			if ($activity) {
292 1
				if ($activity['affecteduser'] !== $user) {
293
					throw new \OutOfBoundsException('Invalid since', 2);
294
				}
295 1
				$timestamp = (int) $activity['timestamp'];
296
297 1
				if ($sort === 'DESC') {
298 1
					$query->andWhere($query->expr()->lte('timestamp', $query->createNamedParameter($timestamp)));
299 1
					$query->andWhere($query->expr()->lt('activity_id', $query->createNamedParameter($since)));
300 1
				} else {
301
					$query->andWhere($query->expr()->gte('timestamp', $query->createNamedParameter($timestamp)));
302
					$query->andWhere($query->expr()->gt('activity_id', $query->createNamedParameter($since)));
303
				}
304 1
				return [];
305
			}
306
		}
307
308
		/**
309
		 * Couldn't find the since, so find the oldest one and set the header
310
		 */
311 4
		$query = $this->connection->getQueryBuilder();
312 4
		$query->select('activity_id')
313 4
			->from('activity')
314 4
			->where($query->expr()->eq('affecteduser', $query->createNamedParameter($user)))
315 4
			->orderBy('timestamp', $sort)
316 4
			->setMaxResults(1);
317 4
		$result = $query->execute();
318 4
		$activity = $result->fetch();
319 4
		$result->closeCursor();
320
321 4
		if ($activity !== false) {
322
			return [
323 3
				'X-Activity-First-Known' => (int) $activity['activity_id'],
324 3
			];
325
		}
326
327 1
		return [];
328
	}
329
330
	/**
331
	 * Verify that the filter is valid
332
	 *
333
	 * @param string $filterValue
334
	 * @return string
335
	 */
336 6
	public function validateFilter($filterValue) {
337 6
		if (!isset($filterValue)) {
338 1
			return 'all';
339
		}
340
341
		switch ($filterValue) {
342 5
			case 'by':
343 5
			case 'self':
344 5
			case 'all':
345 5
			case 'filter':
346 3
				return $filterValue;
347 2
			default:
348 2
				if ($this->activityManager->isFilterValid($filterValue)) {
349 1
					return $filterValue;
350
				}
351 1
				return 'all';
352 2
		}
353
	}
354
355
	/**
356
	 * Delete old events
357
	 *
358
	 * @param int $expireDays Minimum 1 day
359
	 * @return null
360
	 */
361 2
	public function expire($expireDays = 365) {
362 2
		$ttl = (60 * 60 * 24 * max(1, $expireDays));
363
364 2
		$timelimit = time() - $ttl;
365 2
		$this->deleteActivities(array(
366 2
			'timestamp' => array($timelimit, '<'),
367 2
		));
368 2
	}
369
370
	/**
371
	 * Delete activities that match certain conditions
372
	 *
373
	 * @param array $conditions Array with conditions that have to be met
374
	 *                      'field' => 'value'  => `field` = 'value'
375
	 *    'field' => array('value', 'operator') => `field` operator 'value'
376
	 * @return null
377
	 */
378 12
	public function deleteActivities($conditions) {
379 12
		$sqlWhere = '';
380 12
		$sqlParameters = $sqlWhereList = array();
381 12
		foreach ($conditions as $column => $comparison) {
382 12
			$sqlWhereList[] = " `$column` " . ((is_array($comparison) && isset($comparison[1])) ? $comparison[1] : '=') . ' ? ';
383 12
			$sqlParameters[] = (is_array($comparison)) ? $comparison[0] : $comparison;
384 12
		}
385
386 12
		if (!empty($sqlWhereList)) {
387 12
			$sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList);
388 12
		}
389
390 12
		$query = $this->connection->prepare(
391 12
			'DELETE FROM `*PREFIX*activity`' . $sqlWhere);
392 12
		$query->execute($sqlParameters);
393 12
	}
394
}
395