Completed
Push — master ( 0c06e3...0bb60e )
by
unknown
12s
created

Data::get()   D

Complexity

Conditions 16
Paths 110

Size

Total Lines 99
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 53
CRAP Score 21.2661

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 99
ccs 53
cts 73
cp 0.726
rs 4.7112
cc 16
eloc 64
nc 110
nop 9
crap 21.2661

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
 * @author Frank Karlitschek <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
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, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\Activity;
25
26
use Doctrine\DBAL\Platforms\MySqlPlatform;
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 7
	public function getNotificationTypes(IL10N $l) {
72 7
		if (isset($this->notificationTypes[$l->getLanguageCode()])) {
73 1
			return $this->notificationTypes[$l->getLanguageCode()];
74
		}
75
76
		// Allow apps to add new notification types
77 7
		$notificationTypes = $this->activityManager->getNotificationTypes($l->getLanguageCode());
78 7
		$this->notificationTypes[$l->getLanguageCode()] = $notificationTypes;
79 7
		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') {
217 2
			$query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user)));
218
219 11
		} else if ($filter === 'all' && !$userSettings->getUserSetting($user, 'setting', 'self')) {
220
			$query->andWhere($query->expr()->orX(
221
				$query->expr()->neq('user', $query->createNamedParameter($user)),
222
				$query->expr()->notIn('type', $query->createNamedParameter([
223
					'file_created',
224
					'file_changed',
225
					'file_deleted',
226
					'file_restored',
227
				], IQueryBuilder::PARAM_STR_ARRAY))
228
			));
229
230 9
		} else if ($filter === 'filter') {
231 2
			if (!$userSettings->getUserSetting($user, 'setting', 'self')) {
232 2
				$query->andWhere($query->expr()->orX(
233 2
					$query->expr()->neq('user', $query->createNamedParameter($user)),
234 2
					$query->expr()->notIn('type', $query->createNamedParameter([
235 2
						'file_created',
236 2
						'file_changed',
237 2
						'file_deleted',
238 2
						'file_restored',
239 2
					], IQueryBuilder::PARAM_STR_ARRAY))
240 2
				));
241 2
			}
242
243 2
			$query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)));
244 2
			$query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
245 2
		}
246
247 13
		list($condition, $params) = $this->activityManager->getQueryForFilter($filter);
248 13
		if (!is_null($condition)) {
249
			// Strip away ' and '
250
			$condition = substr($condition, 5);
251
252
			if (is_array($params)) {
253
				// Replace ? placeholders with named parameters
254
				foreach ($params as $param) {
255
					$pos = strpos($condition, '?');
256
					if ($pos !== false) {
257
						$condition = substr($condition, 0, $pos) . $query->createNamedParameter($param) . substr($condition, $pos + 1);
258
					}
259
				}
260
			}
261
262
			$query->andWhere($query->createFunction($condition));
263
		}
264
265
		/**
266
		 * Order and specify the offset
267
		 */
268 13
		$sqlSort = ($sort === 'asc') ? 'ASC' : 'DESC';
269 13
		$headers = $this->setOffsetFromSince($query, $user, $since, $sqlSort);
270 13
		$query->orderBy('timestamp', $sqlSort)
271 13
			->addOrderBy('activity_id', $sqlSort);
272
273 13
		$query->setMaxResults($limit + 1);
274
275 13
		$result = $query->execute();
276 13
		$hasMore = false;
277 13
		while ($row = $result->fetch()) {
278 12
			if ($limit === 0) {
279 10
				$hasMore = true;
280 10
				break;
281
			}
282 12
			$headers['X-Activity-Last-Given'] = (int) $row['activity_id'];
283 12
			$groupHelper->addActivity($row);
284 12
			$limit--;
285 12
		}
286 13
		$result->closeCursor();
287
288 13
		return ['data' => $groupHelper->getActivities(), 'has_more' => $hasMore, 'headers' => $headers];
289
	}
290
291
	/**
292
	 * @param IQueryBuilder $query
293
	 * @param string $user
294
	 * @param int $since
295
	 * @param string $sort
296
	 *
297
	 * @return array Headers that should be set on the response
298
	 *
299
	 * @throws \OutOfBoundsException If $since is not owned by $user
300
	 */
301 18
	protected function setOffsetFromSince(IQueryBuilder $query, $user, $since, $sort) {
302 18
		if ($since) {
303 5
			$queryBuilder = $this->connection->getQueryBuilder();
304 5
			$queryBuilder->select(['affecteduser', 'timestamp'])
305 5
				->from('activity')
306 5
				->where($queryBuilder->expr()->eq('activity_id', $queryBuilder->createNamedParameter((int) $since)));
307 5
			$result = $queryBuilder->execute();
308 5
			$activity = $result->fetch();
309 5
			$result->closeCursor();
310
311 5
			if ($activity) {
312 4
				if ($activity['affecteduser'] !== $user) {
313 1
					throw new \OutOfBoundsException('Invalid since', 2);
314
				}
315 3
				$timestamp = (int) $activity['timestamp'];
316
317 3
				if ($sort === 'DESC') {
318 2
					$query->andWhere($query->expr()->lte('timestamp', $query->createNamedParameter($timestamp)));
319 2
					$query->andWhere($query->expr()->lt('activity_id', $query->createNamedParameter($since)));
320 2
				} else {
321 1
					$query->andWhere($query->expr()->gte('timestamp', $query->createNamedParameter($timestamp)));
322 1
					$query->andWhere($query->expr()->gt('activity_id', $query->createNamedParameter($since)));
323
				}
324 3
				return [];
325
			}
326 1
		}
327
328
		/**
329
		 * Couldn't find the since, so find the oldest one and set the header
330
		 */
331 14
		$fetchQuery = $this->connection->getQueryBuilder();
332 14
		$fetchQuery->select('activity_id')
333 14
			->from('activity')
334 14
			->where($fetchQuery->expr()->eq('affecteduser', $fetchQuery->createNamedParameter($user)))
335 14
			->orderBy('timestamp', $sort)
336 14
			->setMaxResults(1);
337 14
		$result = $fetchQuery->execute();
338 14
		$activity = $result->fetch();
339 14
		$result->closeCursor();
340
341 14
		if ($activity !== false) {
342
			return [
343 12
				'X-Activity-First-Known' => (int) $activity['activity_id'],
344 12
			];
345
		}
346
347 2
		return [];
348
	}
349
350
	/**
351
	 * Verify that the filter is valid
352
	 *
353
	 * @param string $filterValue
354
	 * @return string
355
	 */
356 6
	public function validateFilter($filterValue) {
357 6
		if (!isset($filterValue)) {
358 1
			return 'all';
359
		}
360
361
		switch ($filterValue) {
362 5
			case 'by':
363 5
			case 'self':
364 5
			case 'all':
365 5
			case 'filter':
366 3
				return $filterValue;
367 2
			default:
368 2
				if ($this->activityManager->isFilterValid($filterValue)) {
369 1
					return $filterValue;
370
				}
371 1
				return 'all';
372 2
		}
373
	}
374
375
	/**
376
	 * Delete old events
377
	 *
378
	 * @param int $expireDays Minimum 1 day
379
	 * @return null
380
	 */
381 2
	public function expire($expireDays = 365) {
382 2
		$ttl = (60 * 60 * 24 * max(1, $expireDays));
383
384 2
		$timelimit = time() - $ttl;
385 2
		$this->deleteActivities(array(
386 2
			'timestamp' => array($timelimit, '<'),
387 2
		));
388 2
	}
389
390
	/**
391
	 * Delete activities that match certain conditions
392
	 *
393
	 * @param array $conditions Array with conditions that have to be met
394
	 *                      'field' => 'value'  => `field` = 'value'
395
	 *    'field' => array('value', 'operator') => `field` operator 'value'
396
	 * @return null
397
	 */
398 14
	public function deleteActivities($conditions) {
399 14
		$sqlWhere = '';
400 14
		$sqlParameters = $sqlWhereList = array();
401 14
		foreach ($conditions as $column => $comparison) {
402 14
			$sqlWhereList[] = " `$column` " . ((is_array($comparison) && isset($comparison[1])) ? $comparison[1] : '=') . ' ? ';
403 14
			$sqlParameters[] = (is_array($comparison)) ? $comparison[0] : $comparison;
404 14
		}
405
406 14
		if (!empty($sqlWhereList)) {
407 14
			$sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList);
408 14
		}
409
410
		// Add galera safe delete chunking if using mysql
411
		// Stops us hitting wsrep_max_ws_rows when large row counts are deleted
412 14
		if($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Platforms\MySqlPlatform 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...
413
			// Then use chunked delete
414 1
			$max = 100000;
415 1
			$query = $this->connection->prepare(
416 1
				'DELETE FROM `*PREFIX*activity`' . $sqlWhere . " LIMIT " . $max);
417
			do {
418 1
				$query->execute($sqlParameters);
419 1
				$deleted = $query->rowCount();
420 1
			} while($deleted === $max);
421 1
		} else {
422
			// Dont use chunked delete - let the DB handle the large row count natively
423 14
			$query = $this->connection->prepare(
424 14
				'DELETE FROM `*PREFIX*activity`' . $sqlWhere);
425 14
			$query->execute($sqlParameters);
426
		}
427
428
429 14
	}
430
}
431