Completed
Pull Request — master (#429)
by René
04:19
created

PageController::gotoPoll()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 32
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 27
nc 5
nop 1
dl 0
loc 32
rs 9.488
c 0
b 0
f 0
ccs 0
cts 25
cp 0
crap 20
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Vinzenz Rosenkranz <[email protected]>
4
 *
5
 * @author Vinzenz Rosenkranz <[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 OCA\Polls\Db\Comment;
27
use OCA\Polls\Db\CommentMapper;
28
use OCA\Polls\Db\Event;
29
use OCA\Polls\Db\EventMapper;
30
use OCA\Polls\Db\Notification;
31
use OCA\Polls\Db\NotificationMapper;
32
use OCA\Polls\Db\Options;
33
use OCA\Polls\Db\OptionsMapper;
34
use OCA\Polls\Db\Votes;
35
use OCA\Polls\Db\VotesMapper;
36
use OCP\AppFramework\Controller;
37
use OCP\AppFramework\Db\DoesNotExistException;
38
use OCP\AppFramework\Http\ContentSecurityPolicy;
39
use OCP\AppFramework\Http\JSONResponse;
40
use OCP\AppFramework\Http\RedirectResponse;
41
use OCP\AppFramework\Http\TemplateResponse;
42
use OCP\IAvatarManager;
43
use OCP\IConfig;
44
use OCP\IGroupManager;
45
use OCP\IL10N;
46
use OCP\ILogger;
47
use OCP\IRequest;
48
use OCP\IURLGenerator;
49
use OCP\IUser;
50
use OCP\IUserManager;
51
use OCP\L10N\IFactory;
52
use OCP\Mail\IMailer;
53
use OCP\Security\ISecureRandom;
54
use OCP\User; //To do: replace according to API
55
use OCP\Util;
56
57
class PageController extends Controller {
58
59
	private $userId;
60
	private $config;
61
	private $commentMapper;
62
	private $eventMapper;
63
	private $notificationMapper;
64
	private $optionsMapper;
65
	private $votesMapper;
66
	private $urlGenerator;
67
	private $userMgr;
68
	private $avatarManager;
69
	private $logger;
70
	private $trans;
71
	private $transFactory;
72
	private $groupManager;
73
	private $mailer;
74
75
	/**
76
	 * PageController constructor.
77
	 * @param string $appName
78
	 * @param IRequest $request
79
	 * @param IConfig $config
80
	 * @param IUserManager $userMgr
81
	 * @param IGroupManager $groupManager
82
	 * @param IAvatarManager $avatarManager
83
	 * @param ILogger $logger
84
	 * @param IL10N $trans
85
	 * @param IFactory $transFactory
86
	 * @param IURLGenerator $urlGenerator
87
	 * @param string $userId
88
	 * @param CommentMapper $commentMapper
89
	 * @param OptionsMapper $optionsMapper
90
	 * @param EventMapper $eventMapper
91
	 * @param NotificationMapper $notificationMapper
92
	 * @param VotesMapper $VotesMapper
93
	 * @param IMailer $mailer
94
	 */
95 1
	public function __construct(
96
		$appName,
97
		IRequest $request,
98
		IConfig $config,
99
		IUserManager $userMgr,
100
		IGroupManager $groupManager,
101
		IAvatarManager $avatarManager,
102
		ILogger $logger,
103
		IL10N $trans,
104
		IFactory $transFactory,
105
		IURLGenerator $urlGenerator,
106
		$userId,
107
		CommentMapper $commentMapper,
108
		OptionsMapper $optionsMapper,
109
		EventMapper $eventMapper,
110
		NotificationMapper $notificationMapper,
111
		VotesMapper $VotesMapper,
112
		IMailer $mailer
113
	) {
114 1
		parent::__construct($appName, $request);
115 1
		$this->request = $request;
116 1
		$this->config = $config;
117 1
		$this->userMgr = $userMgr;
118 1
		$this->groupManager = $groupManager;
119 1
		$this->avatarManager = $avatarManager;
120 1
		$this->logger = $logger;
121 1
		$this->trans = $trans;
122 1
		$this->transFactory = $transFactory;
123 1
		$this->urlGenerator = $urlGenerator;
124 1
		$this->userId = $userId;
125 1
		$this->commentMapper = $commentMapper;
126 1
		$this->optionsMapper = $optionsMapper;
127 1
		$this->eventMapper = $eventMapper;
128 1
		$this->notificationMapper = $notificationMapper;
129 1
		$this->votesMapper = $VotesMapper;
130 1
		$this->mailer = $mailer;
131 1
	}
132
133
	/**
134
	 * @NoAdminRequired
135
	 * @NoCSRFRequired
136
	 */
137 1
	public function index() {
138 1
		$polls = $this->eventMapper->findAllForUserWithInfo($this->userId);
139 1
		$comments = $this->commentMapper->findDistinctByUser($this->userId);
140 1
		$votes = $this->votesMapper->findDistinctByUser($this->userId);
141 1
		$response = new TemplateResponse('polls', 'main.tmpl', [
142 1
			'polls' => $polls,
143 1
			'comments' => $comments,
144 1
			'votes' => $votes,
145 1
			'userId' => $this->userId,
146 1
			'userMgr' => $this->userMgr,
147 1
			'urlGenerator' => $this->urlGenerator
148
		]);
149 1
		$csp = new ContentSecurityPolicy();
150 1
		$response->setContentSecurityPolicy($csp);
151 1
		return $response;
152
	}
153
154
	/**
155
	 * @param int $pollId
156
	 * @param string $from
157
	 */
158
	private function sendNotifications($pollId, $from) {
159
		$poll = $this->eventMapper->find($pollId);
160
		$notifications = $this->notificationMapper->findAllByPoll($pollId);
161
		foreach ($notifications as $notification) {
162
			if ($from === $notification->getUserId()) {
163
				continue;
164
			}
165
			$recUser = $this->userMgr->get($notification->getUserId());
166
			if (!$recUser instanceof IUser) {
167
				continue;
168
			}
169
			$email = \OC::$server->getConfig()->getUserValue($notification->getUserId(), 'settings', 'email');
170
			if ($email === null || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
171
				continue;
172
			}
173
			$url = $this->urlGenerator->getAbsoluteURL(
174
				$this->urlGenerator->linkToRoute('polls.page.goto_poll',
175
					array('hash' => $poll->getHash()))
176
			);
177
178
			$sendUser = $this->userMgr->get($from);
179
			$sender = $from;
180
			if ($sendUser instanceof IUser) {
181
				$sender = $sendUser->getDisplayName();
182
			}
183
184
			$lang = $this->config->getUserValue($notification->getUserId(), 'core', 'lang');
185
			$trans = $this->transFactory->get('polls', $lang);
186
			$emailTemplate = $this->mailer->createEMailTemplate('polls.Notification', [
187
				'user' => $sender,
188
				'title' => $poll->getTitle(),
189
				'link' => $url,
190
			]);
191
			$emailTemplate->setSubject($trans->t('Polls App - New Activity'));
0 ignored issues
show
Bug introduced by
The method setSubject() does not exist on OCP\Mail\IEMailTemplate. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
			$emailTemplate->/** @scrutinizer ignore-call */ 
192
                   setSubject($trans->t('Polls App - New Activity'));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
192
			$emailTemplate->addHeader();
193
			$emailTemplate->addHeading($trans->t('Polls App - New Activity'), false);
194
195
			$emailTemplate->addBodyText(str_replace(
196
				['{user}', '{title}'],
197
				[$sender, $poll->getTitle()],
198
				$trans->t('{user} participated in the poll "{title}"')
199
			));
200
201
			$emailTemplate->addBodyButton(
202
				htmlspecialchars($trans->t('Go to poll')),
203
				$url,
204
				false
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $plainText of OCP\Mail\IEMailTemplate::addBodyButton(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

204
				/** @scrutinizer ignore-type */ false
Loading history...
205
			);
206
207
			$emailTemplate->addFooter();
208
			try {
209
				$message = $this->mailer->createMessage();
210
				$message->setTo([$email => $recUser->getDisplayName()]);
211
				$message->useTemplate($emailTemplate);
212
				$this->mailer->send($message);
213
			} catch (\Exception $e) {
214
				$this->logger->logException($e, ['app' => 'polls']);
215
			}
216
		}
217
	}
218
219
	/**
220
	 * @NoAdminRequired
221
	 * @NoCSRFRequired
222
	 * @PublicPage
223
	 * @param string $hash
224
	 * @return TemplateResponse
225
	 */
226
	public function gotoPoll($hash) {
227
		try {
228
			$poll = $this->eventMapper->findByHash($hash);
229
		} catch (DoesNotExistException $e) {
230
			return new TemplateResponse('polls', 'no.acc.tmpl', []);
231
		}
232
		$options = $this->optionsMapper->findByPoll($poll->getId());
233
		$votes = $this->votesMapper->findByPoll($poll->getId());
234
		$participants = $this->votesMapper->findParticipantsByPoll($poll->getId());
235
		$comments = $this->commentMapper->findByPoll($poll->getId());
236
237
		try {
238
			$notification = $this->notificationMapper->findByUserAndPoll($poll->getId(), $this->userId);
239
		} catch (DoesNotExistException $e) {
240
			$notification = null;
241
		}
242
		if ($this->hasUserAccess($poll)) {
243
			return new TemplateResponse('polls', 'goto.tmpl', [
244
				'poll' => $poll,
245
				'options' => $options,
246
				'comments' => $comments,
247
				'votes' => $votes,
248
				'participant' => $participants,
249
				'notification' => $notification,
250
				'userId' => $this->userId,
251
				'userMgr' => $this->userMgr,
252
				'urlGenerator' => $this->urlGenerator,
253
				'avatarManager' => $this->avatarManager
254
			]);
255
		} else {
256
			User::checkLoggedIn();
257
			return new TemplateResponse('polls', 'no.acc.tmpl', []);
258
		}
259
	}
260
261
	/**
262
	 * @NoAdminRequired
263
	 * @NoCSRFRequired
264
	 * @param int $pollId
265
	 * @return TemplateResponse|RedirectResponse
266
	 */
267
	public function deletePoll($pollId) {
268
		$pollToDelete = $this->eventMapper->find($pollId);
269
		if ($this->userId !== $pollToDelete->getOwner() && !$this->groupManager->isAdmin($this->userId)) {
270
			return new TemplateResponse('polls', 'no.delete.tmpl');
271
		}
272
		$poll = new Event();
273
		$poll->setId($pollId);
274
		$this->commentMapper->deleteByPoll($pollId);
275
		$this->votesMapper->deleteByPoll($pollId);
276
		$this->optionsMapper->deleteByPoll($pollId);
277
		$this->eventMapper->delete($poll);
278
		$url = $this->urlGenerator->linkToRoute('polls.page.index');
279
		return new RedirectResponse($url);
280
	}
281
282
	/**
283
	 * @NoAdminRequired
284
	 * @NoCSRFRequired
285
	 * @param string $hash
286
	 * @return TemplateResponse
287
	 */
288
	public function editPoll($hash) {
289
		return new TemplateResponse('polls', 'create.tmpl', [
290
			'urlGenerator' => $this->urlGenerator,
291
 			'hash' => $hash
292
		]);
293
	}
294
295
	/**
296
	 * @NoAdminRequired
297
	 * @NoCSRFRequired
298
	 */
299
	public function createPoll() {
300
		return new TemplateResponse('polls', 'create.tmpl',
301
			['urlGenerator' => $this->urlGenerator]);
302
	}
303
304
	/**
305
	 * @NoAdminRequired
306
	 * @NoCSRFRequired
307
	 * @PublicPage
308
	 * @param int $pollId
309
	 * @param string $userId
310
	 * @param string $answers
311
	 * @param string $options
312
	 * @param bool $receiveNotifications
313
	 * @param bool $changed
314
	 * @return RedirectResponse
315
	 */
316
	public function insertVote($pollId, $userId, $answers, $options, $receiveNotifications, $changed) {
317
		if ($this->userId !== null) {
318
			if ($receiveNotifications) {
319
				try {
320
					//check if user already set notification for this poll
321
					$this->notificationMapper->findByUserAndPoll($pollId, $userId);
322
				} catch (DoesNotExistException $e) {
323
					//insert if not exist
324
					$not = new Notification();
325
					$not->setUserId($userId);
326
					$not->setPollId($pollId);
327
					$this->notificationMapper->insert($not);
328
				}
329
			} else {
330
				try {
331
					//delete if entry is in db
332
					$not = $this->notificationMapper->findByUserAndPoll($pollId, $userId);
333
					$this->notificationMapper->delete($not);
334
				} catch (DoesNotExistException $e) {
335
					//doesn't exist in db, nothing to do
336
				}
337
			}
338
		}
339
		$poll = $this->eventMapper->find($pollId);
340
341
		if ($changed) {
342
			$options = json_decode($options);
343
			$answers = json_decode($answers);
344
			$count_options = count($options);
345
			$this->votesMapper->deleteByPollAndUser($pollId, $userId);
346
347
			for ($i = 0; $i < $count_options; $i++) {
348
				$vote = new Votes();
349
				$vote->setPollId($pollId);
350
				$vote->setUserId($userId);
351
				$vote->setVoteOptionText(htmlspecialchars($options[$i]));
352
				$vote->setVoteAnswer($answers[$i]);
353
				$this->votesMapper->insert($vote);
354
355
			}
356
			$this->sendNotifications($pollId, $userId);
357
		}
358
		$hash = $poll->getHash();
359
		$url = $this->urlGenerator->linkToRoute('polls.page.goto_poll', ['hash' => $hash]);
360
		return new RedirectResponse($url);
361
	}
362
363
	/**
364
	 * @NoAdminRequired
365
	 * @NoCSRFRequired
366
	 * @PublicPage
367
	 * @param int $pollId
368
	 * @param string $userId
369
	 * @param string $commentBox
370
	 * @return JSONResponse
371
	 */
372
	public function insertComment($pollId, $userId, $commentBox) {
373
		$comment = new Comment();
374
		$comment->setPollId($pollId);
375
		$comment->setUserId($userId);
376
		$comment->setComment($commentBox);
377
		$comment->setDt(date('Y-m-d H:i:s'));
378
		$this->commentMapper->insert($comment);
379
		$this->sendNotifications($pollId, $userId);
380
		$timeStamp = time();
381
		$displayName = $userId;
382
		$user = $this->userMgr->get($userId);
383
		if ($user !== null) {
384
			$displayName = $user->getDisplayName();
385
		}
386
		return new JSONResponse(array(
387
			'userId' => $userId,
388
			'displayName' => $displayName,
389
			'timeStamp' => $timeStamp * 100,
390
			'date' => date('Y-m-d H:i:s', $timeStamp),
391
			'relativeNow' => $this->trans->t('just now'),
392
			'comment' => $commentBox
393
		));
394
	}
395
396
	/**
397
	 * @NoAdminRequired
398
	 * @NoCSRFRequired
399
	 * @param string $searchTerm
400
	 * @param string $groups
401
	 * @param string $users
402
	 * @return array
403
	 */
404
	public function search($searchTerm, $groups, $users) {
405
		return array_merge($this->searchForGroups($searchTerm, $groups), $this->searchForUsers($searchTerm, $users));
406
	}
407
408
	/**
409
	 * @NoAdminRequired
410
	 * @NoCSRFRequired
411
	 * @param string $searchTerm
412
	 * @param string $groups
413
	 * @return array
414
	 */
415
	public function searchForGroups($searchTerm, $groups) {
416
		$selectedGroups = json_decode($groups);
417
		$groups = $this->groupManager->search($searchTerm);
418
		$gids = array();
419
		$sgids = array();
420
		foreach ($selectedGroups as $sg) {
421
			$sgids[] = str_replace('group_', '', $sg);
422
		}
423
		foreach ($groups as $g) {
424
			$gids[] = $g->getGID();
425
		}
426
		$diffGids = array_diff($gids, $sgids);
427
		$gids = array();
428
		foreach ($diffGids as $g) {
429
			$gids[] = ['gid' => $g, 'isGroup' => true];
430
		}
431
		return $gids;
432
	}
433
434
	/**
435
	 * @NoAdminRequired
436
	 * @NoCSRFRequired
437
	 * @param string $searchTerm
438
	 * @param string $users
439
	 * @return array
440
	 */
441
	public function searchForUsers($searchTerm, $users) {
442
		$selectedUsers = json_decode($users);
443
		Util::writeLog('polls', print_r($selectedUsers, true), Util::ERROR);
444
		$userNames = $this->userMgr->searchDisplayName($searchTerm);
445
		$users = array();
446
		$sUsers = array();
447
		foreach ($selectedUsers as $su) {
448
			$sUsers[] = str_replace('user_', '', $su);
449
		}
450
		foreach ($userNames as $u) {
451
			$allreadyAdded = false;
452
			foreach ($sUsers as &$su) {
453
				if ($su === $u->getUID()) {
454
					unset($su);
455
					$allreadyAdded = true;
456
					break;
457
				}
458
			}
459
			if (!$allreadyAdded) {
460
				$users[] = array('uid' => $u->getUID(), 'displayName' => $u->getDisplayName(), 'isGroup' => false);
461
			} else {
462
				continue;
463
			}
464
		}
465
		return $users;
466
	}
467
468
	/**
469
	 * @NoAdminRequired
470
	 * @NoCSRFRequired
471
	 * @param string $username
472
	 * @return string
473
	 */
474
	public function getDisplayName($username) {
475
		return $this->userMgr->get($username)->getDisplayName();
476
	}
477
478
	/**
479
	 * @return \OCP\IGroup[]
480
	 */
481
	private function getGroups() {
482
		if (class_exists('\OC_Group')) {
483
			// Nextcloud <= 11, ownCloud
484
			return \OC_Group::getUserGroups($this->userId);
0 ignored issues
show
Bug introduced by
The type OC_Group was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
485
		}
486
		// Nextcloud >= 12
487
		$groups = $this->groupManager->getUserGroups(\OC::$server->getUserSession()->getUser());
488
		return array_map(function($group) {
489
			return $group->getGID();
490
		}, $groups);
491
	}
492
493
	/**
494
	 * Check if user has access to this poll
495
	 *
496
	 * @param Event $poll
497
	 * @return bool
498
	 */
499
	private function hasUserAccess($poll) {
500
		$access = $poll->getAccess();
501
		$owner = $poll->getOwner();
502
		if ($access === 'public' || $access === 'hidden') {
503
			return true;
504
		}
505
		if ($this->userId === null) {
506
			return false;
507
		}
508
		if ($access === 'registered') {
509
			return true;
510
		}
511
		if ($owner === $this->userId) {
512
			return true;
513
		}
514
		Util::writeLog('polls', $this->userId, Util::ERROR);
515
		$userGroups = $this->getGroups();
516
		$arr = explode(';', $access);
517
		foreach ($arr as $item) {
518
			if (strpos($item, 'group_') === 0) {
519
				$grp = substr($item, 6);
520
				foreach ($userGroups as $userGroup) {
521
					if ($userGroup === $grp) {
522
						return true;
523
					}
524
				}
525
			} else {
526
				if (strpos($item, 'user_') === 0) {
527
					$usr = substr($item, 5);
528
					if ($usr === $this->userId) {
529
						return true;
530
					}
531
				}
532
			}
533
		}
534
		return false;
535
	}
536
	/**
537
	 * Check if user is owner of this poll
538
	 *
539
	 * @param Event $poll
540
	 * @return bool
541
	 */
542
543
	private function userIsOwner($poll) {
544
		$owner = $poll->getOwner();
545
546
		if ($owner === $this->userId) {
547
			return true;
548
		}
549
		Util::writeLog('polls', $this->userId, Util::ERROR);
550
		return false;
551
	}
552
}
553