Passed
Pull Request — master (#1128)
by René
04:49
created

MailService::getRecipientsByShare()   B

Complexity

Conditions 9
Paths 32

Size

Total Lines 71
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
eloc 52
c 0
b 0
f 0
dl 0
loc 71
ccs 0
cts 68
cp 0
rs 7.4917
cc 9
nc 32
nop 3
crap 90

How to fix   Long Method   

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:

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\Service;
25
26
use Exception;
27
28
use OCP\IUser;
29
use OCP\IUserManager;
30
use OCP\IGroupManager;
31
use OCP\IConfig;
32
use OCP\IURLGenerator;
33
use OCP\IL10N;
34
use OCP\L10N\IFactory;
35
use OCP\Mail\IMailer;
36
use OCP\Mail\IEMailTemplate;
37
38
use OCA\Polls\Db\SubscriptionMapper;
39
use OCA\Polls\Db\PollMapper;
40
use OCA\Polls\Db\ShareMapper;
41
use OCA\Polls\Db\Share;
42
use OCA\Polls\Db\LogMapper;
43
use OCA\Polls\Model\Contact;
44
use OCA\Polls\Model\Email;
45
use OCA\Polls\Model\Group;
46
use OCA\Polls\Model\User;
47
48
class MailService {
49
50
	/** @var IUserManager */
51
	private $userManager;
52
53
	/** @var IGroupManager */
54
	private $groupManager;
55
56
	/** @var IConfig */
57
	private $config;
58
59
	/** @var IURLGenerator */
60
	private $urlGenerator;
61
62
	/** @var IL10N */
63
	private $trans;
64
65
	/** @var IFactory */
66
	private $transFactory;
67
68
	/** @var IMailer */
69
	private $mailer;
70
71
	/** @var SubscriptionMapper */
72
	private $subscriptionMapper;
73
74
	/** @var ShareMapper */
75
	private $shareMapper;
76
77
	/** @var PollMapper */
78
	private $pollMapper;
79
80
	/** @var LogMapper */
81
	private $logMapper;
82
83
	/**
84
	 * MailService constructor.
85
	 * @param IUserManager $userManager
86
	 * @param IGroupManager $groupManager
87
	 * @param IConfig $config
88
	 * @param IURLGenerator $urlGenerator
89
	 * @param IL10N $trans
90
	 * @param IFactory $transFactory
91
	 * @param IMailer $mailer
92
	 * @param SubscriptionMapper $subscriptionMapper
93
	 * @param ShareMapper $shareMapper
94
	 * @param PollMapper $pollMapper
95
	 * @param LogMapper $logMapper
96
	 */
97
98
	public function __construct(
99
		IUserManager $userManager,
100
		IGroupManager $groupManager,
101
		IConfig $config,
102
		IURLGenerator $urlGenerator,
103
		IL10N $trans,
104
		IFactory $transFactory,
105
		IMailer $mailer,
106
		ShareMapper $shareMapper,
107
		SubscriptionMapper $subscriptionMapper,
108
		PollMapper $pollMapper,
109
		LogMapper $logMapper
110
	) {
111
		$this->config = $config;
112
		$this->userManager = $userManager;
113
		$this->groupManager = $groupManager;
114
		$this->urlGenerator = $urlGenerator;
115
		$this->trans = $trans;
116
		$this->transFactory = $transFactory;
117
		$this->mailer = $mailer;
118
		$this->shareMapper = $shareMapper;
119
		$this->subscriptionMapper = $subscriptionMapper;
120
		$this->pollMapper = $pollMapper;
121
		$this->logMapper = $logMapper;
122
	}
123
124
125
	/**
126
	 * sendMail - Send eMail and evaluate recipient's mail address
127
	 * and displayname if $userId is a site user
128
	 * @param IEmailTemplate $emailTemplate
129
	 * @param String $userId
130
	 * @param String $emailAddress, ignored, when $userId is set
131
	 * @param String $displayName, ignored, when $userId is set
132
	 * @return String
133
	 */
134
135
	private function sendMail($emailTemplate, $userId = '', $emailAddress = '', $displayName = '') {
136
		if ($this->userManager->get($userId) instanceof IUser) {
137
			$emailAddress = \OC::$server->getConfig()->getUserValue($userId, 'settings', 'email');
138
			$displayName = $this->userManager->get($userId)->getDisplayName();
139
		}
140
141
		if (!$emailAddress || !filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
142
			throw new Exception('Invalid email address (' . $emailAddress . ')');
143
		}
144
145
		try {
146
			$message = $this->mailer->createMessage();
147
			$message->setTo([$emailAddress => $displayName]);
148
			$message->useTemplate($emailTemplate);
149
			$this->mailer->send($message);
150
151
			return null;
152
		} catch (\Exception $e) {
153
			\OC::$server->getLogger()->logException($e, ['app' => 'polls']);
154
			throw $e;
155
		}
156
	}
157
158
159
	/**
160
	 * @param integer $pollId
161
	 * @param string $userId
162
	 * @return string
163
	 */
164
	public function resolveEmailAddress($pollId, $userId) {
165
		if ($this->userManager->get($userId) instanceof IUser) {
166
			return \OC::$server->getConfig()->getUserValue($userId, 'settings', 'email');
167
		}
168
169
		// if $userId is no site user, eval via shares
170
		try {
171
			$share = $this->shareMapper->findByPollAndUser($pollId, $userId);
172
			if ($share->getUserEmail()) {
173
				return $share->getUserEmail();
174
			}
175
		} catch (\Exception $e) {
176
			// catch silently
177
		}
178
		return $userId;
179
	}
180
181
182
	/**
183
	 * @param Share $share
184
	 * @param String $defaultLang
185
	 * @param String $skipUser
186
	 * @return Array $recipients
187
	 */
188
	private function getRecipientsByShare($share, $defaultLang = 'en', $skipUser = null) {
189
		$recipients = [];
190
191
		$tokenLink = $this->urlGenerator->getAbsoluteURL(
192
			$this->urlGenerator->linkToRoute(
193
				'polls.page.vote_publicpublic',
194
				['token' => $share->getToken()]
195
			)
196
		);
197
198
		$internalLink = $this->urlGenerator->getAbsoluteURL(
199
			$this->urlGenerator->linkToRoute(
200
				'polls.page.indexvote',
201
				['id' => $share->getPollId()]
202
			)
203
		);
204
205
		switch ($share->getType()) {
206
			case Share::TYPE_USER:
207
				$user = new User($share->getUserId());
208
				$recipients[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
209
					'userId' => $user->getId(),
210
					'eMailAddress' => $user->getEmailAddress(),
211
					'displayName' => $user->getDisplayName(),
212
					'language' => $user->getLanguage(),
213
					'link' => $internalLink,
214
				];
215
			case Share::TYPE_EMAIL:
216
				$user = new Email($share->getUserId());
217
218
				$recipients[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
219
					'userId' => $user->getId(),
220
					'eMailAddress' => $user->getEmailAddress(),
221
					'displayName' => $user->getDisplayName(),
222
					'language' => $defaultLang,
223
					'link' => $tokenLink,
224
				];
225
			case Share::TYPE_CONTACT:
226
				$user = new Contact($share->getUserId());
227
228
				$recipients[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
229
					'userId' => $user->getId(),
230
					'eMailAddress' => $user->getEmailAddress(),
231
					'displayName' => $user->getDisplayname(),
232
					'language' => $defaultLang,
233
					'link' => $tokenLink,
234
				];
235
			case Share::TYPE_EXTERNAL:
236
				$recipients[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
237
					'userId' => $share->getUserId(),
238
					'eMailAddress' => $share->getUserEmail(),
239
					'displayName' => $share->getUserId(),
240
					'language' => $defaultLang,
241
					'link' => $tokenLink,
242
				];
243
			case Share::TYPE_GROUP:
244
				foreach ((new Group($share->getUserId()))->getMembers() as $user) {
245
					if ($skipUser === $user->getId() || !$user->getUserIsDisabled()) {
246
						continue;
247
					}
248
249
					$recipients[] = [
250
						'userId' => $user->getId(),
251
						'eMailAddress' => $user->getEmailAddress(),
252
						'displayName' => $user->getDisplayName(),
253
						'language' => $user->getLanguage(),
254
						'link' => $internalLink,
255
					];
256
				}
257
		}
258
		return $recipients;
259
	}
260
261
	/**
262
	 * @param string $token
263
	 */
264
	public function sendInvitationMail($token) {
265
		$share = $this->shareMapper->findByToken($token);
266
		$poll = $this->pollMapper->find($share->getPollId());
267
		$owner = $this->userManager->get($poll->getOwner());
268
		$sentMails = [];
269
		$abortedMails = [];
270
271
		$recipients = $this->getRecipientsByShare(
272
			$this->shareMapper->findByToken($token),
273
			$this->config->getUserValue($poll->getOwner(), 'core', 'lang'),
274
			$poll->getOwner()
275
		);
276
277
		foreach ($recipients as $recipient) {
278
			$trans = $this->transFactory->get('polls', $recipient['language']);
279
280
281
			$emailTemplate = $this->mailer->createEMailTemplate('polls.Invitation', [
282
				'owner' => $owner->getDisplayName(),
283
				'title' => $poll->getTitle(),
284
				'link' => $recipient['link']
285
			]);
286
287
			$emailTemplate->setSubject($trans->t('Poll invitation "%s"', $poll->getTitle()));
288
			$emailTemplate->addHeader();
289
			$emailTemplate->addHeading($trans->t('Poll invitation "%s"', $poll->getTitle()), false);
290
291
			$emailTemplate->addBodyText(str_replace(
292
				['{owner}', '{title}'],
293
				[$owner->getDisplayName(), $poll->getTitle()],
294
				$trans->t('{owner} invited you to take part in the poll "{title}"')
295
			));
296
297
			$emailTemplate->addBodyText($poll->getDescription());
298
299
			$emailTemplate->addBodyButton(
300
				htmlspecialchars($trans->t('Go to poll')),
301
				$recipient['link']
302
			);
303
304
			$emailTemplate->addBodyText($trans->t('This link gives you personal access to the poll named above. Press the button above or copy the following link and add it in your browser\'s location bar: '));
305
			$emailTemplate->addBodyText($recipient['link']);
306
307
			$emailTemplate->addFooter($trans->t('This email is sent to you, because you are invited to vote in this poll by the poll owner. At least your name or your email address is recorded in this poll. If you want to get removed from this poll, contact the site administrator or the initiator of this poll, where the mail is sent from.'));
308
309
			try {
310
				$this->sendMail(
311
					$emailTemplate,
312
					$recipient['userId'],
313
					$recipient['eMailAddress'],
314
					$recipient['displayName']
315
				);
316
				$share->setInvitationSent(time());
317
				$this->shareMapper->update($share);
318
				$sentMails[] = $recipient;
319
			} catch (Exception $e) {
320
				$abortedMails[] = $recipient;
321
				\OC::$server->getLogger()->alert('Error sending Mail to ' . json_encode($recipient));
322
			}
323
		}
324
		return ['sentMails' => $sentMails, 'abortedMails' => $abortedMails];
325
	}
326
327
	public function sendNotifications() {
328
		$subscriptions = [];
329
		$log = $this->logMapper->findUnprocessedPolls();
330
331
		foreach ($log as $logItem) {
332
			$subscriptions = array_merge($subscriptions, $this->subscriptionMapper->findAllByPoll($logItem->getPollId()));
333
		}
334
335
		$log = $this->logMapper->findUnprocessed();
336
337
		foreach ($subscriptions as $subscription) {
338
			$poll = $this->pollMapper->find($subscription->getPollId());
339
			$emailAddress = '';
340
			$displayName = '';
341
342
			if ($this->userManager->get($subscription->getUserId()) instanceof IUser) {
343
				$lang = $this->config->getUserValue($subscription->getUserId(), 'core', 'lang');
344
			} else {
345
				try {
346
					$emailAddress = $this->shareMapper->findByPollAndUser($subscription->getPollId(), $subscription->getUserId())->getUserEmail();
347
					$displayName = $subscription->getUserId();
348
					$lang = $this->config->getUserValue($poll->getOwner(), 'core', 'lang');
349
				} catch (\Exception $e) {
350
					continue;
351
				}
352
			}
353
354
			$trans = $this->transFactory->get('polls', $lang);
355
356
			$url = $this->urlGenerator->getAbsoluteURL(
357
				$this->urlGenerator->linkToRoute(
358
					'polls.page.indexvote',
359
					['id' => $subscription->getPollId()]
360
				)
361
			);
362
363
			$emailTemplate = $this->mailer->createEMailTemplate('polls.Invitation', [
364
				'title' => $poll->getTitle(),
365
				'link' => $url
366
			]);
367
			$emailTemplate->setSubject($trans->t('Polls App - New Activity'));
368
			$emailTemplate->addHeader();
369
			$emailTemplate->addHeading($trans->t('Polls App - New Activity'), false);
370
			$emailTemplate->addBodyText(str_replace(
371
				['{title}'],
372
				[$poll->getTitle()],
373
				$trans->t('"{title}" had recent activity: ')
374
			));
375
376
			foreach ($log as $logItem) {
377
				if ($logItem->getPollId() === $subscription->getPollId()) {
378
					if ($poll->getAnonymous() || $poll->getShowResults() !== "always") {
379
						$displayUser = $trans->t('A user');
380
					} elseif ($this->userManager->get($logItem->getUserId()) instanceof IUser) {
381
						$displayUser = $this->userManager->get($logItem->getUserId())->getDisplayName();
382
					} else {
383
						$displayUser = $logItem->getUserId();
384
					}
385
386
					if ($logItem->getMessage()) {
387
						$emailTemplate->addBodyText($logItem->getMessage());
388
					} elseif ($logItem->getMessageId() === Log::MSG_ID_SETVOTE) {
389
						$emailTemplate->addBodyText($trans->t(
390
							'- %s voted.',
391
							[$displayUser]
392
						));
393
					} elseif ($logItem->getMessageId() === Log::MSG_ID_UPDATEPOLL) {
394
						$emailTemplate->addBodyText($trans->t(
395
							'- %s updated the poll configuration. Please check your votes.',
396
							[$displayUser]
397
						));
398
					} elseif ($logItem->getMessageId() === Log::MSG_ID_DELETEPOLL) {
399
						$emailTemplate->addBodyText($trans->t(
400
							'- %s deleted the poll.',
401
							[$displayUser]
402
						));
403
					} elseif ($logItem->getMessageId() === Log::MSG_ID_RESTOREPOLL) {
404
						$emailTemplate->addBodyText($trans->t(
405
							'- %s restored the poll.',
406
							[$displayUser]
407
						));
408
					} elseif ($logItem->getMessageId() === Log::MSG_ID_EXPIREPOLL) {
409
						$emailTemplate->addBodyText($trans->t(
410
							'- The poll expired.',
411
							[$displayUser]
412
						));
413
					} elseif ($logItem->getMessageId() === Log::MSG_ID_ADDOPTION) {
414
						$emailTemplate->addBodyText($trans->t(
415
							'- %s added a vote option.',
416
							[$displayUser]
417
						));
418
					} elseif ($logItem->getMessageId() === Log::MSG_ID_DELETEOPTION) {
419
						$emailTemplate->addBodyText($trans->t(
420
							'- %s removed a vote option.',
421
							[$displayUser]
422
						));
423
					} else {
424
						$emailTemplate->addBodyText(
425
							$logItem->getMessageId() . " (" . $displayUser . ")"
426
						);
427
					}
428
				}
429
430
				$logItem->setProcessed(time());
431
				$this->logMapper->update($logItem);
432
			}
433
434
			$emailTemplate->addBodyButton(
435
				htmlspecialchars($trans->t('Go to poll')),
436
				$url,
437
				/** @scrutinizer ignore-type */ false
438
			);
439
			$emailTemplate->addFooter($trans->t('This email is sent to you, because you subscribed to notifications of this poll. To opt out, visit the poll and remove your subscription.'));
440
441
			try {
442
				$this->sendMail($emailTemplate, $subscription->getUserId(), $emailAddress, $displayName);
443
			} catch (Exception $e) {
444
				\OC::$server->getLogger()->alert('Error sending Mail to ' . $subscription->getUserId());
445
			}
446
		}
447
	}
448
}
449