Completed
Push — master ( 0471ec...901b23 )
by René
10:22 queued 06:22
created

ApiController::getPolls()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 4
nop 0
dl 0
loc 18
ccs 0
cts 14
cp 0
crap 20
rs 9.9332
c 0
b 0
f 0
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\IUserManager;
35
use OCP\Security\ISecureRandom;
36
37
use OCA\Polls\Db\Event;
38
use OCA\Polls\Db\EventMapper;
39
use OCA\Polls\Db\Option;
40
use OCA\Polls\Db\OptionMapper;
41
use OCA\Polls\Db\Vote;
42
use OCA\Polls\Db\VoteMapper;
43
use OCA\Polls\Db\Comment;
44
use OCA\Polls\Db\CommentMapper;
45
46
47
48
class ApiController extends Controller {
49
50
	private $eventMapper;
51
	private $optionMapper;
52
	private $voteMapper;
53
	private $commentMapper;
54
55
	/**
56
	 * PageController constructor.
57
	 * @param string $appName
58
	 * @param IGroupManager $groupManager
59
	 * @param IRequest $request
60
	 * @param IUserManager $userManager
61
	 * @param string $userId
62
	 * @param EventMapper $eventMapper
63
	 * @param OptionMapper $optionMapper
64
	 * @param VoteMapper $VoteMapper
65
	 * @param CommentMapper $CommentMapper
66
	 */
67
	public function __construct(
68
		$appName,
69
		IGroupManager $groupManager,
70
		IRequest $request,
71
		IUserManager $userManager,
72
		$userId,
73
		EventMapper $eventMapper,
74
		OptionMapper $optionMapper,
75
		VoteMapper $VoteMapper,
76
		CommentMapper $CommentMapper
77
	) {
78
		parent::__construct($appName, $request);
79
		$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...
80
		$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...
81
		$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...
82
		$this->eventMapper = $eventMapper;
83
		$this->optionMapper = $optionMapper;
84
		$this->voteMapper = $VoteMapper;
85
		$this->commentMapper = $CommentMapper;
86
	}
87
88
	/**
89
	 * Transforms a string with user and group names to an array
90
	 * of nextcloud users and groups
91
	 * @NoAdminRequired
92
	 * @NoCSRFRequired
93
	 * @param string $item
94
	 * @return Array
95
	 */
96
	private function convertAccessList($item) {
97
		$split = array();
98
		if (strpos($item, 'user_') === 0) {
99
			$user = $this->userManager->get(substr($item, 5));
100
			$split = [
101
				'id' => $user->getUID(),
102
				'user' => $user->getUID(),
103
				'type' => 'user',
104
				'desc' => 'user',
105
				'icon' => 'icon-user',
106
				'displayName' => $user->getDisplayName(),
107
				'avatarURL' => '',
108
				'lastLogin' => $user->getLastLogin(),
109
				'cloudId' => $user->getCloudId()
110
			];
111
		} elseif (strpos($item, 'group_') === 0) {
112
			$group = substr($item, 6);
113
			$group = $this->groupManager->get($group);
114
			$split = [
115
				'id' => $group->getGID(),
116
				'user' => $group->getGID(),
117
				'type' => 'group',
118
				'desc' => 'group',
119
				'icon' => 'icon-group',
120
				'displayName' => $group->getDisplayName(),
121
				'avatarURL' => '',
122
			];
123
		}
124
125
		return($split);
126
	}
127
128
	/**
129
	 * Check if current user is in the access list
130
	 * @param Array $accessList
131
	 * @return Boolean
132
	 */
133
	private function checkUserAccess($accessList) {
134
		foreach ($accessList as $accessItem ) {
135
			if ($accessItem['type'] === 'user' && $accessItem['id'] === \OC::$server->getUserSession()->getUser()->getUID()) {
136
				return true;
137
			}
138
		}
139
140
		return false;
141
	}
142
143
	/**
144
	 * Check If current user is member of a group in the access list
145
	 * @param Array $accessList
146
	 * @return Boolean
147
	 */
148
	private function checkGroupAccess($accessList) {
149
		foreach ($accessList as $accessItem ) {
150
			if ($accessItem['type'] === 'group' && $this->groupManager->isInGroup(\OC::$server->getUserSession()->getUser()->getUID(),$accessItem['id'])) {
151
				return true;
152
			}
153
		}
154
155
		return false;
156
	}
157
158
	/**
159
	 * Read all options of a poll based on the poll id
160
	 * @NoAdminRequired
161
	 * @NoCSRFRequired
162
	 * @param Integer $pollId
163
	 * @return Array
164
	 */
165
	public function getOptions($pollId) {
166
		$optionList = array();
167
		$options = $this->optionMapper->findByPoll($pollId);
168
		foreach ($options as $optionElement) {
169
			$optionList[] = [
170
				'id' => $optionElement->getId(),
171
				'text' => htmlspecialchars_decode($optionElement->getPollOptionText()),
172
				'timestamp' => $optionElement->getTimestamp()
173
			];
174
		}
175
176
		return $optionList;
177
	}
178
179
	/**
180
	 * Read all votes of a poll based on the poll id
181
	 * @NoAdminRequired
182
	 * @NoCSRFRequired
183
	 * @param Integer $pollId
184
	 * @return Array
185
	 */
186
	public function getVotes($pollId) {
187
		$votesList = array();
188
		$votes = $this->voteMapper->findByPoll($pollId);
189
		foreach ($votes as $voteElement) {
190
			$votesList[] = [
191
				'id' => $voteElement->getId(),
192
				'userId' => $voteElement->getUserId(),
193
				'voteOptionId' => $voteElement->getVoteOptionId(),
194
				'voteOptionText' => htmlspecialchars_decode($voteElement->getVoteOptionText()),
195
				'voteAnswer' => $voteElement->getVoteAnswer()
196
			];
197
		}
198
199
		return $votesList;
200
	}
201
202
	/**
203
	 * Read all comments of a poll based on the poll id
204
	 * @NoAdminRequired
205
	 * @NoCSRFRequired
206
	 * @param Integer $pollId
207
	 * @return Array
208
	 */
209
	public function getComments($pollId) {
210
		$commentsList = array();
211
		$comments = $this->commentMapper->findByPoll($pollId);
212
		foreach ($comments as $commentElement) {
213
			$commentsList[] = [
214
				'id' => $commentElement->getId(),
215
				'userId' => $commentElement->getUserId(),
216
				'date' => $commentElement->getDt() . ' UTC',
217
				'comment' => $commentElement->getComment()
218
			];
219
		}
220
221
		return $commentsList;
222
	}
223
224
	/**
225
	 * Read an entire poll based on poll id
226
	 * @NoAdminRequired
227
	 * @NoCSRFRequired
228
	 * @param Integer $pollId
229
	 * @return Array
230
	 */
231
	public function getEvent($pollId) {
232
233
		$data = array();
234
235
		try {
236
			$event = $this->eventMapper->find($pollId);
237
238
			if ($event->getType() == 0) {
239
				$pollType = 'datePoll';
240
			} else {
241
				$pollType = 'textPoll';
242
			}
243
244
			$accessType = $event->getAccess();
245
			if (!strpos('|public|hidden|registered', $accessType)) {
246
				$accessType = 'select';
247
			}
248
249
			if ($event->getExpire() === null) {
0 ignored issues
show
introduced by
The condition $event->getExpire() === null is always false.
Loading history...
250
				$expired = false;
251
				$expiration = false;
252
			} else {
253
				$expired = time() > strtotime($event->getExpire());
254
				$expiration = true;
255
			}
256
257
			$data = [
258
				'id' => $event->getId(),
259
				'hash' => $event->getHash(),
260
				'type' => $pollType,
261
				'title' => $event->getTitle(),
262
				'description' => $event->getDescription(),
263
				'owner' => $event->getOwner(),
264
				'created' => $event->getCreated(),
265
				'access' => $accessType,
266
				'expiration' => $expiration,
267
				'expired' => $expired,
268
				'expirationDate' => $event->getExpire(),
269
				'isAnonymous' => $event->getIsAnonymous(),
270
				'fullAnonymous' => $event->getFullAnonymous(),
271
				'allowMaybe' => $event->getAllowMaybe()
272
			];
273
274
		} catch (DoesNotExistException $e) {
275
			// return silently
276
		} finally {
277
			return $data;
278
		}
279
280
	}
281
282
	/**
283
	 * Read all shares (users and groups with access) of a poll based on the poll id
284
	 * @NoAdminRequired
285
	 * @NoCSRFRequired
286
	 * @param Integer $pollId
287
	 * @return Array
288
	 */
289
	public function getShares($pollId) {
290
291
		$accessList = array();
292
293
		try {
294
			$poll = $this->eventMapper->find($pollId);
295
			if (!strpos('|public|hidden|registered', $poll->getAccess())) {
296
				$accessList = explode(';', $poll->getAccess());
297
				$accessList = array_filter($accessList);
298
				$accessList = array_map(array($this, 'convertAccessList'), $accessList);
299
			}
300
		} catch (DoesNotExistException $e) {
301
			// return silently
302
		} finally {
303
			return $accessList;
304
		}
305
306
	}
307
308
	/**
309
	 * Set the access right of the current user for the poll
310
	 * @param Integer $pollId
311
	 * @return String
312
	 */
313
	private function grantAccessAs($pollId) {
314
		if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
315
			$currentUser = '';
316
		} else {
317
			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
318
		}
319
320
		$event = $this->getEvent($pollId);
321
		$accessList = $this->getShares($pollId);
322
		$grantAccessAs = 'none';
323
324
		if ($event['owner'] === $currentUser) {
325
			$grantAccessAs = 'owner';
326
		} elseif ($event['access'] === 'public') {
327
			$grantAccessAs = 'public';
328
		} elseif ($event['access'] === 'registered' && \OC::$server->getUserSession()->getUser() instanceof IUser) {
329
			$grantAccessAs = 'registered';
330
		} elseif ($this->checkUserAccess($accessList)) {
331
			$grantAccessAs = 'userInvitation';
332
		} elseif ($this->checkGroupAccess($accessList)) {
333
			$grantAccessAs = 'groupInvitation';
334
		} elseif ($this->groupManager->isAdmin($currentUser)) {
335
			$grantAccessAs = 'admin';
336
		}
337
338
		return $grantAccessAs;
339
	}
340
341
342
	/**
343
	 * Read an entire poll based on the poll id or hash
344
	 * @NoAdminRequired
345
	 * @NoCSRFRequired
346
	 * @param String $pollIdOrHash poll id or hash
347
	 * @return Array
348
	 */
349
	public function getPoll($pollIdOrHash) {
350
351
		if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
352
			$currentUser = '';
353
		} else {
354
			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
355
		}
356
357
		$data = array();
358
359
		try {
360
361
			if (is_numeric($pollIdOrHash)) {
362
				$pollId = $this->eventMapper->find(intval($pollIdOrHash))->id;
363
				$result = 'foundById';
364
			} else {
365
				$pollId = $this->eventMapper->findByHash($pollIdOrHash)->id;
366
				$result = 'foundByHash';
367
			}
368
369
			$event = $this->getEvent($pollId);
370
371
			if ($event['owner'] !== $currentUser && !$this->groupManager->isAdmin($currentUser)) {
372
				$mode = 'create';
373
			} else {
374
				$mode = 'edit';
375
			}
376
377
			$data['poll'] = [
378
				'result' => $result,
379
				'grantedAs' => $this->grantAccessAs($event['id']),
380
				'mode' => $mode,
381
				'event' => $event,
382
				'comments' => $this->getComments($event['id']),
383
				'votes' => $this->getVotes($event['id']),
384
				'shares' => $this->getShares($event['id']),
385
				'options' => [
386
					'pollDates' => [],
387
					'pollTexts' => $this->getOptions($event['id'])
388
				]
389
			];
390
		} catch (DoesNotExistException $e) {
391
				$data['poll'] = ['result' => 'notFound'];
392
		} finally {
393
			return $data;
394
		}
395
	}
396
397
  	/**
398
	 * Get a list of NC users and groups
399
	 * @NoAdminRequired
400
	 * @NoCSRFRequired
401
	 * @return DataResponse
402
	 */
403
	public function getSiteUsersAndGroups($query = '', $getGroups = true, $getUsers = true, $skipGroups = array(), $skipUsers = array()) {
404
		$list = array();
405
		$data = array();
406
		if ($getGroups) {
407
			$groups = $this->groupManager->search($query);
408
			foreach ($groups as $group) {
409
				if (!in_array($group->getGID(), $skipGroups)) {
410
					$list['g_' . $group->getGID()] = [
411
						'id' => $group->getGID(),
412
						'user' => $group->getGID(),
413
						'type' => 'group',
414
						'desc' => 'group',
415
						'icon' => 'icon-group',
416
						'displayName' => $group->getGID(),
417
						'avatarURL' => ''
418
					];
419
				}
420
			}
421
		}
422
423
		if ($getUsers) {
424
			$users = $this->userManager->searchDisplayName($query);
425
			foreach ($users as $user) {
426
				if (!in_array($user->getUID(), $skipUsers)) {
427
					$list['u_' . $user->getUID()] = [
428
						'id' => $user->getUID(),
429
						'user' => $user->getUID(),
430
						'type' => 'user',
431
						'desc' => 'user',
432
						'icon' => 'icon-user',
433
						'displayName' => $user->getDisplayName(),
434
						'avatarURL' => '',
435
						'lastLogin' => $user->getLastLogin(),
436
						'cloudId' => $user->getCloudId()
437
					];
438
				}
439
			}
440
		}
441
442
		$data['siteusers'] = $list;
443
		return new DataResponse($data, Http::STATUS_OK);
444
	}
445
446
	/**
447
	 * Get all polls
448
	 * @NoAdminRequired
449
	 * @NoCSRFRequired
450
	 * @PublicPage
451
	 * @return DataResponse
452
	 */
453
454
	public function getPolls() {
455
		if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
456
			return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
457
		}
458
459
		try {
460
			$events = $this->eventMapper->findAll();
461
		} catch (DoesNotExistException $e) {
462
			return new DataResponse($e, Http::STATUS_NOT_FOUND);
463
		}
464
465
		$eventsList = array();
466
467
		foreach ($events as $eventElement) {
468
			$eventsList[$eventElement->id] = $this->getEvent($eventElement->id);
469
		}
470
471
		return new DataResponse($eventsList, Http::STATUS_OK);
472
	}
473
474
	/**
475
	 * Write poll (create/update)
476
	 * @NoAdminRequired
477
	 * @NoCSRFRequired
478
	 * @param Array $event
479
	 * @param Array $options
480
	 * @param Array  $shares
481
	 * @param String $mode
482
	 * @return DataResponse
483
	 */
484
	public function writePoll($event, $options, $shares, $mode) {
485
		if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
486
			return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
487
		} else {
488
			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
489
			$AdminAccess = $this->groupManager->isAdmin($currentUser);
490
		}
491
492
		$newEvent = new Event();
493
494
		// Set the configuration options entered by the user
495
		$newEvent->setTitle($event['title']);
496
		$newEvent->setDescription($event['description']);
497
498
		$newEvent->setType($event['type']);
499
		$newEvent->setIsAnonymous($event['isAnonymous']);
500
		$newEvent->setFullAnonymous($event['fullAnonymous']);
501
		$newEvent->setAllowMaybe($event['allowMaybe']);
502
503
		if ($event['access'] === 'select') {
504
			$shareAccess = '';
505
			foreach ($shares as $shareElement) {
506
				if ($shareElement['type'] === 'user') {
507
					$shareAccess = $shareAccess . 'user_' . $shareElement['id'] . ';';
508
				} elseif ($shareElement['type'] === 'group') {
509
					$shareAccess = $shareAccess . 'group_' . $shareElement['id'] . ';';
510
				}
511
			}
512
			$newEvent->setAccess(rtrim($shareAccess, ';'));
513
		} else {
514
			$newEvent->setAccess($event['access']);
515
		}
516
517
		if ($event['expiration']) {
518
			$newEvent->setExpire($event['expirationDate']);
519
		} else {
520
			$newEvent->setExpire(null);
521
		}
522
523
		if ($event['type'] === 'datePoll') {
524
			$newEvent->setType(0);
525
		} elseif ($event['type'] === 'textPoll') {
526
			$newEvent->setType(1);
527
		}
528
529
		if ($mode === 'edit') {
530
			// Edit existing poll
531
			$oldPoll = $this->eventMapper->findByHash($event['hash']);
532
533
			// Check if current user is allowed to edit existing poll
534
			if ($oldPoll->getOwner() !== $currentUser && !$AdminAccess) {
535
				// If current user is not owner of existing poll deny access
536
				return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
537
			}
538
539
			// else take owner, hash and id of existing poll
540
			$newEvent->setOwner($oldPoll->getOwner());
541
			$newEvent->setHash($oldPoll->getHash());
542
			$newEvent->setId($oldPoll->getId());
543
			$this->eventMapper->update($newEvent);
544
			$this->optionMapper->deleteByPoll($newEvent->getId());
545
546
		} elseif ($mode === 'create') {
547
			// Create new poll
548
			// Define current user as owner, set new creation date and create a new hash
549
			$newEvent->setOwner($currentUser);
550
			$newEvent->setCreated(date('Y-m-d H:i:s'));
551
			$newEvent->setHash(\OC::$server->getSecureRandom()->generate(
552
				16,
553
				ISecureRandom::CHAR_DIGITS .
554
				ISecureRandom::CHAR_LOWER .
555
				ISecureRandom::CHAR_UPPER
556
			));
557
			$newEvent = $this->eventMapper->insert($newEvent);
558
		}
559
560
		// Update options
561
		if ($event['type'] === 'datePoll') {
562
			foreach ($options['pollDates'] as $optionElement) {
563
				$newOption = new Option();
564
565
				$newOption->setPollId($newEvent->getId());
566
				$newOption->setPollOptionText(date('Y-m-d H:i:s', $optionElement['timestamp']));
567
				$newOption->setTimestamp($optionElement['timestamp']);
568
569
				$this->optionMapper->insert($newOption);
570
			}
571
		} elseif ($event['type'] === "textPoll") {
572
			foreach ($options['pollTexts'] as $optionElement) {
573
				$newOption = new Option();
574
575
				$newOption->setPollId($newEvent->getId());
576
				$newOption->setpollOptionText(trim(htmlspecialchars($optionElement['text'])));
577
578
				$this->optionMapper->insert($newOption);
579
			}
580
		}
581
582
		return new DataResponse(array(
583
			'id' => $newEvent->getId(),
584
			'hash' => $newEvent->getHash()
585
		), Http::STATUS_OK);
586
587
	}
588
}
589