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

MailService::sendNotifications()   F

Complexity

Conditions 19
Paths 240

Size

Total Lines 118
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 380

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 85
c 1
b 0
f 1
dl 0
loc 118
ccs 0
cts 106
cp 0
rs 3.1833
cc 19
nc 240
nop 0
crap 380

How to fix   Long Method    Complexity   

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