Passed
Pull Request — master (#1128)
by René
06:43
created

MailService::getRecipientsByShare()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 75
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
eloc 56
c 0
b 0
f 0
dl 0
loc 75
ccs 0
cts 72
cp 0
rs 7.4044
cc 9
nc 6
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[] = [
209
					'userId' => $user->getId(),
210
					'eMailAddress' => $user->getEmailAddress(),
211
					'displayName' => $user->getDisplayName(),
212
					'language' => $user->getLanguage(),
213
					'link' => $internalLink,
214
				];
215
				break;
216
			case Share::TYPE_EMAIL:
217
				$user = new Email($share->getUserId());
218
219
				$recipients[] = [
220
					'userId' => $user->getId(),
221
					'eMailAddress' => $user->getEmailAddress(),
222
					'displayName' => $user->getDisplayName(),
223
					'language' => $defaultLang,
224
					'link' => $tokenLink,
225
				];
226
				break;
227
			case Share::TYPE_CONTACT:
228
				$user = new Contact($share->getUserId());
229
230
				$recipients[] = [
231
					'userId' => $user->getId(),
232
					'eMailAddress' => $user->getEmailAddress(),
233
					'displayName' => $user->getDisplayname(),
234
					'language' => $defaultLang,
235
					'link' => $tokenLink,
236
				];
237
				break;
238
			case Share::TYPE_EXTERNAL:
239
				$recipients[] = [
240
					'userId' => $share->getUserId(),
241
					'eMailAddress' => $share->getUserEmail(),
242
					'displayName' => $share->getUserId(),
243
					'language' => $defaultLang,
244
					'link' => $tokenLink,
245
				];
246
				break;
247
			case Share::TYPE_GROUP:
248
				foreach ((new Group($share->getUserId()))->getMembers() as $user) {
249
					if ($skipUser === $user->getId() || !$user->getUserIsDisabled()) {
250
						continue;
251
					}
252
253
					$recipients[] = [
254
						'userId' => $user->getId(),
255
						'eMailAddress' => $user->getEmailAddress(),
256
						'displayName' => $user->getDisplayName(),
257
						'language' => $user->getLanguage(),
258
						'link' => $internalLink,
259
					];
260
				}
261
		}
262
		return $recipients;
263
	}
264
265
	/**
266
	 * @param string $token
267
	 */
268
	public function sendInvitationMail($token) {
269
		$share = $this->shareMapper->findByToken($token);
270
		$poll = $this->pollMapper->find($share->getPollId());
271
		$owner = $this->userManager->get($poll->getOwner());
272
		$sentMails = [];
273
		$abortedMails = [];
274
275
		$recipients = $this->getRecipientsByShare(
276
			$this->shareMapper->findByToken($token),
277
			$this->config->getUserValue($poll->getOwner(), 'core', 'lang'),
278
			$poll->getOwner()
279
		);
280
281
		foreach ($recipients as $recipient) {
282
			$trans = $this->transFactory->get('polls', $recipient['language']);
283
284
285
			$emailTemplate = $this->mailer->createEMailTemplate('polls.Invitation', [
286
				'owner' => $owner->getDisplayName(),
287
				'title' => $poll->getTitle(),
288
				'link' => $recipient['link']
289
			]);
290
291
			$emailTemplate->setSubject($trans->t('Poll invitation "%s"', $poll->getTitle()));
292
			$emailTemplate->addHeader();
293
			$emailTemplate->addHeading($trans->t('Poll invitation "%s"', $poll->getTitle()), false);
294
295
			$emailTemplate->addBodyText(str_replace(
296
				['{owner}', '{title}'],
297
				[$owner->getDisplayName(), $poll->getTitle()],
298
				$trans->t('{owner} invited you to take part in the poll "{title}"')
299
			));
300
301
			$emailTemplate->addBodyText($poll->getDescription());
302
303
			$emailTemplate->addBodyButton(
304
				htmlspecialchars($trans->t('Go to poll')),
305
				$recipient['link']
306
			);
307
308
			$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: '));
309
			$emailTemplate->addBodyText($recipient['link']);
310
311
			$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.'));
312
313
			try {
314
				$this->sendMail(
315
					$emailTemplate,
316
					$recipient['userId'],
317
					$recipient['eMailAddress'],
318
					$recipient['displayName']
319
				);
320
				$share->setInvitationSent(time());
321
				$this->shareMapper->update($share);
322
				$sentMails[] = $recipient;
323
			} catch (Exception $e) {
324
				$abortedMails[] = $recipient;
325
				\OC::$server->getLogger()->alert('Error sending Mail to ' . json_encode($recipient));
326
			}
327
		}
328
		return ['sentMails' => $sentMails, 'abortedMails' => $abortedMails];
329
	}
330
331
	public function sendNotifications() {
332
		$subscriptions = [];
333
		$log = $this->logMapper->findUnprocessedPolls();
334
335
		foreach ($log as $logItem) {
336
			$subscriptions = array_merge($subscriptions, $this->subscriptionMapper->findAllByPoll($logItem->getPollId()));
337
		}
338
339
		$log = $this->logMapper->findUnprocessed();
340
341
		foreach ($subscriptions as $subscription) {
342
			$poll = $this->pollMapper->find($subscription->getPollId());
343
			$emailAddress = '';
344
			$displayName = '';
345
346
			if ($this->userManager->get($subscription->getUserId()) instanceof IUser) {
347
				$lang = $this->config->getUserValue($subscription->getUserId(), 'core', 'lang');
348
			} else {
349
				try {
350
					$emailAddress = $this->shareMapper->findByPollAndUser($subscription->getPollId(), $subscription->getUserId())->getUserEmail();
351
					$displayName = $subscription->getUserId();
352
					$lang = $this->config->getUserValue($poll->getOwner(), 'core', 'lang');
353
				} catch (\Exception $e) {
354
					continue;
355
				}
356
			}
357
358
			$trans = $this->transFactory->get('polls', $lang);
359
360
			$url = $this->urlGenerator->getAbsoluteURL(
361
				$this->urlGenerator->linkToRoute(
362
					'polls.page.indexvote',
363
					['id' => $subscription->getPollId()]
364
				)
365
			);
366
367
			$emailTemplate = $this->mailer->createEMailTemplate('polls.Invitation', [
368
				'title' => $poll->getTitle(),
369
				'link' => $url
370
			]);
371
			$emailTemplate->setSubject($trans->t('Polls App - New Activity'));
372
			$emailTemplate->addHeader();
373
			$emailTemplate->addHeading($trans->t('Polls App - New Activity'), false);
374
			$emailTemplate->addBodyText(str_replace(
375
				['{title}'],
376
				[$poll->getTitle()],
377
				$trans->t('"{title}" had recent activity: ')
378
			));
379
380
			foreach ($log as $logItem) {
381
				if ($logItem->getPollId() === $subscription->getPollId()) {
382
					if ($poll->getAnonymous() || $poll->getShowResults() !== "always") {
383
						$displayUser = $trans->t('A user');
384
					} elseif ($this->userManager->get($logItem->getUserId()) instanceof IUser) {
385
						$displayUser = $this->userManager->get($logItem->getUserId())->getDisplayName();
386
					} else {
387
						$displayUser = $logItem->getUserId();
388
					}
389
390
					if ($logItem->getMessage()) {
391
						$emailTemplate->addBodyText($logItem->getMessage());
392
					} elseif ($logItem->getMessageId() === Log::MSG_ID_SETVOTE) {
393
						$emailTemplate->addBodyText($trans->t(
394
							'- %s voted.',
395
							[$displayUser]
396
						));
397
					} elseif ($logItem->getMessageId() === Log::MSG_ID_UPDATEPOLL) {
398
						$emailTemplate->addBodyText($trans->t(
399
							'- %s updated the poll configuration. Please check your votes.',
400
							[$displayUser]
401
						));
402
					} elseif ($logItem->getMessageId() === Log::MSG_ID_DELETEPOLL) {
403
						$emailTemplate->addBodyText($trans->t(
404
							'- %s deleted the poll.',
405
							[$displayUser]
406
						));
407
					} elseif ($logItem->getMessageId() === Log::MSG_ID_RESTOREPOLL) {
408
						$emailTemplate->addBodyText($trans->t(
409
							'- %s restored the poll.',
410
							[$displayUser]
411
						));
412
					} elseif ($logItem->getMessageId() === Log::MSG_ID_EXPIREPOLL) {
413
						$emailTemplate->addBodyText($trans->t(
414
							'- The poll expired.',
415
							[$displayUser]
416
						));
417
					} elseif ($logItem->getMessageId() === Log::MSG_ID_ADDOPTION) {
418
						$emailTemplate->addBodyText($trans->t(
419
							'- %s added a vote option.',
420
							[$displayUser]
421
						));
422
					} elseif ($logItem->getMessageId() === Log::MSG_ID_DELETEOPTION) {
423
						$emailTemplate->addBodyText($trans->t(
424
							'- %s removed a vote option.',
425
							[$displayUser]
426
						));
427
					} else {
428
						$emailTemplate->addBodyText(
429
							$logItem->getMessageId() . " (" . $displayUser . ")"
430
						);
431
					}
432
				}
433
434
				$logItem->setProcessed(time());
435
				$this->logMapper->update($logItem);
436
			}
437
438
			$emailTemplate->addBodyButton(
439
				htmlspecialchars($trans->t('Go to poll')),
440
				$url,
441
				/** @scrutinizer ignore-type */ false
442
			);
443
			$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.'));
444
445
			try {
446
				$this->sendMail($emailTemplate, $subscription->getUserId(), $emailAddress, $displayName);
447
			} catch (Exception $e) {
448
				\OC::$server->getLogger()->alert('Error sending Mail to ' . $subscription->getUserId());
449
			}
450
		}
451
	}
452
}
453