Passed
Push — master ( b80fb7...0c270b )
by René
11:50
created

ApiController   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 212
dl 0
loc 369
ccs 0
cts 272
cp 0
rs 9.0399
c 0
b 0
f 0
wmc 42

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 1
A convertAccessList() 0 23 3
A getSystem() 0 9 1
A getVendor() 0 5 1
C writePoll() 0 101 16
F getPoll() 0 117 13
B getSiteUsersAndGroups() 0 34 7

How to fix   Complexity   

Complex Class

Complex classes like ApiController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ApiController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Vinzenz Rosenkranz <[email protected]>
4
 *
5
 * @author René Gieling <[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\Polls\Controller;
25
26
use OCP\AppFramework\Controller;
27
use OCP\AppFramework\Http;
28
use OCP\AppFramework\Http\DataResponse;
29
use OCP\AppFramework\Db\DoesNotExistException;
30
31
use OCP\IGroupManager;
32
use OCP\IRequest;
33
use OCP\IUser;
34
use OCP\IConfig;
35
use OCP\IUserManager;
36
use OCP\Security\ISecureRandom;
37
38
use OCA\Polls\Db\Event;
39
use OCA\Polls\Db\EventMapper;
40
use OCA\Polls\Db\Options;
41
use OCA\Polls\Db\OptionsMapper;
42
use OCA\Polls\Db\Votes;
43
use OCA\Polls\Db\VotesMapper;
44
use OCA\Polls\Db\Comment;
45
use OCA\Polls\Db\CommentMapper;
46
47
48
49
class ApiController extends Controller {
50
51
	private $eventMapper;
52
	private $optionsMapper;
53
	private $votesMapper;
54
	private $commentMapper;
55
	private $systemConfig;
56
57
	/**
58
	 * PageController constructor.
59
	 * @param string $appName
60
	 * @param IRequest $request
61
	 * @param string $userId
62
	 * @param EventMapper $eventMapper
63
	 * @param OptionsMapper $optionsMapper
64
	 * @param VotesMapper $VotesMapper
65
	 * @param CommentMapper $CommentMapper
66
	 */
67
	public function __construct(
68
		$appName,
69
		IConfig $systemConfig,
70
		IGroupManager $groupManager,
71
		IRequest $request,
72
		IUserManager $userManager,
73
		$userId,
74
		EventMapper $eventMapper,
75
		OptionsMapper $optionsMapper,
76
		VotesMapper $VotesMapper,
77
		CommentMapper $CommentMapper
78
	) {
79
		parent::__construct($appName, $request);
80
		$this->userId = $userId;
0 ignored issues
show
Bug Best Practice introduced by
The property userId does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
81
		$this->groupManager = $groupManager;
0 ignored issues
show
Bug Best Practice introduced by
The property groupManager does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
82
		$this->systemConfig = $systemConfig;
83
		$this->userManager = $userManager;
0 ignored issues
show
Bug Best Practice introduced by
The property userManager does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
84
		$this->eventMapper = $eventMapper;
85
		$this->optionsMapper = $optionsMapper;
86
		$this->votesMapper = $VotesMapper;
87
		$this->commentMapper = $CommentMapper;
88
	}
89
90
  	/**
91
	* @NoAdminRequired
92
	* @NoCSRFRequired
93
	* @return DataResponse
94
	*/
95
	public function getSiteUsersAndGroups($query = '', $getGroups = true, $getUsers = true, $skipGroups = array(), $skipUsers = array()) {
96
		$list = array();
97
		$data = array();
98
		if ($getGroups) {
99
			$groups = $this->groupManager->search($query);
100
			foreach ($groups as $group) {
101
				if (!in_array($group->getGID(), $skipGroups)) {
102
					$list[] = [
103
						'id' => $group->getGID(),
104
						'type' => 'group',
105
						'displayName' => $group->getGID(),
106
						'avatarURL' => ''
107
					];
108
				}
109
			}
110
		}
111
		if ($getUsers) {
112
			$users = $this->userManager->searchDisplayName($query);
113
			foreach ($users as $user) {
114
				if (!in_array($user->getUID(), $skipUsers)) {
115
					$list[] = [
116
						'id' => $user->getUID(),
117
						'type' => 'user',
118
						'displayName' => $user->getDisplayName(),
119
						'avatarURL' => '',
120
						'lastLogin' => $user->getLastLogin(),
121
						'cloudId' => $user->getCloudId()
122
					];
123
				}
124
			}
125
		}
126
127
		$data['siteusers'] = $list;
128
		return new DataResponse($data, Http::STATUS_OK);
129
	}
130
  	/**
131
	* @NoAdminRequired
132
	* @NoCSRFRequired
133
	* @return Array
134
	*/	
135
	function convertAccessList($item) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
136
		$split = Array();
137
		if (strpos($item, 'user_') === 0) {
138
			$user = $this->userManager->get(substr($item, 5));
139
			$split = [
140
				'id' => $user->getUID(),
141
				'type' => 'user',
142
				'displayName' => $user->getDisplayName(),
143
				'avatarURL' => ''
144
			];
145
		} elseif (strpos($item, 'group_') === 0) {
146
			$group = substr($item, 6);
147
			$group = $this->groupManager->get($group);
148
			$split = [
149
				'id' => $group->getGID(),
150
				'type' => 'group',
151
				'displayName' => $group->getDisplayName(),
152
				'avatarURL' => '',
153
			];
154
		}
155
		
156
157
		return($split);
158
	}
159
160
  	/**
161
	* @NoAdminRequired
162
	* @NoCSRFRequired
163
	* @PublicPage
164
	* @param string $hash
165
	* @return DataResponse
166
	*/
167
	
168
	public function getPoll($hash) {
169
		if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
170
			return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
171
		}
172
		
173
		try {
174
			$poll = $this->eventMapper->findByHash($hash);
175
176
			if ($poll->getExpire() === null) {
0 ignored issues
show
introduced by
The condition $poll->getExpire() === null is always false.
Loading history...
177
				$expired = false;
178
				$expiration = false;
179
			} else {
180
				$expired = time() > strtotime($poll->getExpire());
181
				$expiration = true;
182
			}
183
184
			if ($poll->getType() == 0) {
185
				$pollType = 'datePoll'; 
186
			} else {
187
				$pollType = 'textPoll';
188
			};
189
190
			if ($poll->getOwner() !== \OC::$server->getUserSession()->getUser()->getUID()) {
191
				$mode = 'create';
192
			} else {
193
				$mode = 'edit';
194
			}
195
			$accessList = Array();
196
			$accessType = $poll->getAccess();
197
			if (!strpos('|public|hidden|registered', $accessType)) {
198
				$accessList = explode(';',$accessType);
199
				$accessList = array_filter($accessList);
200
				$accessList = array_map(Array($this,'convertAccessList'), $accessList);
201
				$accessType = 'select';
202
			}
203
				
204
			$data = array();
205
			$commentsList = array();
206
			$optionList = array();
207
			$votesList = array();
208
			
209
		} catch (DoesNotExistException $e) {
210
			return new DataResponse($e, Http::STATUS_NOT_FOUND);
211
		};
212
213
214
		try {
215
			$options = $this->optionsMapper->findByPoll($poll->getId());
216
			foreach ($options as $optionElement) {
217
				$optionList[] = [
218
					'id' => $optionElement->getId(),
219
					'text' => $optionElement->getPollOptionText(),
220
					'timestamp' => $optionElement->getTimestamp()
221
				];
222
			};
223
		} catch (DoesNotExistException $e) {
224
			// ignore
225
		};
226
227
		try {
228
			$votes = $this->votesMapper->findByPoll($poll->getId());
229
			foreach ($votes as $voteElement) {
230
				$votesList[] = [
231
					'id' => $voteElement->getId(),
232
					'userId' => $voteElement->getUserId(),
233
					'voteOptionId' => $voteElement->getVoteOptionId(),
234
					'voteOptionText' => $voteElement->getVoteOptionText(),
235
					'voteAnswer' => $voteElement->getVoteAnswer()
236
				];
237
			};
238
		} catch (DoesNotExistException $e) {
239
			// ignore
240
		};
241
242
		try {
243
			$comments = $this->commentMapper->findByPoll($poll->getId());
244
			foreach ($comments as $commentElement) {
245
				$commentsList[] = [
246
					'id' => $commentElement->getId(),
247
					'userId' => $commentElement->getUserId(),
248
					'date' => $commentElement->getDt() . ' UTC',
249
					'comment' => $commentElement->getComment()
250
				];
251
			};
252
		} catch (DoesNotExistException $e) {
253
			// ignore
254
		};
255
	
256
		$data['poll'] = [
257
			'result' => 'found',
258
			'mode' => $mode,
259
			'comments' => $commentsList,
260
			'votes' => $votesList,
261
			'shares' => $accessList,
262
			'event' => [
263
				'id' => $poll->getId(),
264
				'hash' => $hash,
265
				'type' => $pollType,
266
				'title' => $poll->getTitle(),
267
				'description' => $poll->getDescription(),
268
				'owner' => $poll->getOwner(),
269
				'created' => $poll->getCreated(),
270
				'access' => $accessType,
271
				'expiration' => $expiration,
272
				'expired' => $expired,
273
				'expirationDate' => $poll->getExpire(),
274
				'isAnonymous' => $poll->getIsAnonymous(),
275
				'fullAnonymous' => $poll->getFullAnonymous(),
276
				'disallowMaybe' => $poll->getDisallowMaybe()
277
			],
278
			'options' => [
279
				'pollDates' => [],
280
				'pollTexts' => $optionList
281
			]
282
		];
283
284
		return new DataResponse($data, Http::STATUS_OK);
285
	}
286
	
287
	/**
288
	 * @NoAdminRequired
289
	 * @NoCSRFRequired
290
	 * @param string $poll
291
	 * @return DataResponse
292
	 */
293
	public function writePoll($event, $options, $shares, $mode) {
294
		$user = \OC::$server->getUserSession()->getUser();
295
296
		if (!$user instanceof IUser) {
297
			return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
298
		}
299
		$userId = \OC::$server->getUserSession()->getUser()->getUID();
300
301
		$newEvent = new Event();
302
303
		// Set the configuration options entered by the user
304
		$newEvent->setTitle($event['title']);
305
		$newEvent->setDescription($event['description']);
306
307
		$newEvent->setType($event['type']);
308
		$newEvent->setIsAnonymous($event['isAnonymous']);
309
		$newEvent->setFullAnonymous($event['fullAnonymous']);
310
		$newEvent->setDisallowMaybe($event['disallowMaybe']);
311
	
312
		if ($event['access'] === 'select') {
313
			$shareAccess = '';
314
			foreach ($shares as $shareElement) {
315
				if ($shareElement['type'] === 'user') {
316
					$shareAccess = $shareAccess . 'user_' . $shareElement['id'] . ';';
317
				} elseif ($shareElement['type'] === 'group') {
318
					$shareAccess = $shareAccess . 'group_' . $shareElement['id'] . ';';
319
				}
320
			}
321
			$newEvent->setAccess(rtrim($shareAccess, ';'));
322
		} else {
323
			$newEvent->setAccess($event['access']);
324
		}
325
326
		if ($event['expiration']) {
327
			$newEvent->setExpire($event['expirationDate']);
328
		} else {
329
			$newEvent->setExpire(null);
330
		}
331
		
332
		if ($event['type'] === 'datePoll') {
333
			$newEvent->setType(0);
334
		} elseif ($event['type'] === 'textPoll') {
335
			$newEvent->setType(1);
336
		}
337
338
		if ($mode === 'edit') {
339
			// Edit existing poll
340
			$oldPoll = $this->eventMapper->findByHash($event['hash']);
341
342
			// Check if current user is allowed to edit existing poll
343
			if ($oldPoll->getOwner() !== $userId) {
344
				// If current user is not owner of existing poll deny access
345
				return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
346
			} 
347
348
			// else take owner, hash and id of existing poll
349
			$newEvent->setOwner($oldPoll->getOwner());
350
			$newEvent->setHash($oldPoll->getHash());
351
			$newEvent->setId($oldPoll->getId());
352
			$this->eventMapper->update($newEvent);
353
			$this->optionsMapper->deleteByPoll($newEvent->getId());
354
					
355
		} elseif ($mode === 'create') {
356
			// Create new poll
357
			// Define current user as owner, set new creation date and create a new hash
358
			$newEvent->setOwner($userId);
359
			$newEvent->setCreated(date('Y-m-d H:i:s'));
360
			$newEvent->setHash(\OC::$server->getSecureRandom()->generate(
361
				16,
362
				ISecureRandom::CHAR_DIGITS .
363
				ISecureRandom::CHAR_LOWER .
364
				ISecureRandom::CHAR_UPPER
365
			));
366
			$newEvent = $this->eventMapper->insert($newEvent);
367
		}
368
		
369
		// Update options
370
		if ($event['type'] === 'datePoll') {
371
			foreach ($options['pollDates'] as $optionElement) {
372
				$newOption = new Options();
373
				
374
				$newOption->setPollId($newEvent->getId());
375
				$newOption->setPollOptionText(date('Y-m-d H:i:s', $optionElement['timestamp']));
376
				$newOption->setTimestamp($optionElement['timestamp']);
377
				
378
				$this->optionsMapper->insert($newOption);
379
			}
380
		} elseif ($event['type'] === "textPoll") {
381
			foreach ($options['pollTexts'] as $optionElement) {
382
				$newOption = new Options();
383
				
384
				$newOption->setPollId($newEvent->getId());
385
				$newOption->setpollOptionText(htmlspecialchars($optionElement['text']));
386
				
387
				$this->optionsMapper->insert($newOption);
388
			}
389
		}
390
		return new DataResponse(array(
391
			'id' => $newEvent->getId(),
392
			'hash' => $newEvent->getHash()
393
		), Http::STATUS_OK);
394
395
	}
396
397
	private function getVendor() {
398
		// this should really be a JSON file
399
		require \OC::$SERVERROOT . '/version.php';
400
		/** @var string $vendor */
401
		return (string) $vendor;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $vendor seems to be never defined.
Loading history...
402
	}
403
404
	/**
405
	 * @NoAdminRequired
406
	 * @NoCSRFRequired
407
	 * @return DataResponse
408
	 */
409
	public function getSystem() {
410
		$userId = \OC::$server->getUserSession()->getUser()->getUID();
411
		$data['system'] = [
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
412
			'versionArray' => \OCP\Util::getVersion(),
413
			'version' => implode('.', \OCP\Util::getVersion()),
414
			'vendor' => $this->getVendor(),
415
			'language' => $this->systemConfig->getUserValue($userId, 'core', 'lang')
416
		];
417
		return new DataResponse($data, Http::STATUS_OK);
418
	}
419
}
420