Passed
Pull Request — master (#1278)
by René
03:51
created

MailService::resendInvitation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 12
ccs 0
cts 10
cp 0
rs 9.9332
cc 1
nc 1
nop 1
crap 2
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 OCP\IUser;
27
use OCP\IUserManager;
28
use OCP\IGroupManager;
29
use OCP\IConfig;
30
use OCP\IURLGenerator;
31
use OCP\IL10N;
32
use OCP\L10N\IFactory;
33
use OCP\Mail\IMailer;
34
use OCP\Mail\IEMailTemplate;
35
36
use OCA\Polls\Db\SubscriptionMapper;
37
use OCA\Polls\Db\PollMapper;
38
use OCA\Polls\Db\Poll;
39
use OCA\Polls\Db\ShareMapper;
40
use OCA\Polls\Db\Share;
41
use OCA\Polls\Db\LogMapper;
42
use OCA\Polls\Db\Log;
43
use OCA\Polls\Model\UserGroupClass;
44
use OCA\Polls\Model\User;
45
46
class MailService {
47
48
	/** @var IUserManager */
49
	private $userManager;
50
51
	/** @var IGroupManager */
52
	private $groupManager;
53
54
	/** @var IConfig */
55
	private $config;
56
57
	/** @var IURLGenerator */
58
	private $urlGenerator;
59
60
	/** @var IL10N */
61
	private $trans;
62
63
	/** @var IFactory */
64
	private $transFactory;
65
66
	/** @var IMailer */
67
	private $mailer;
68
69
	/** @var SubscriptionMapper */
70
	private $subscriptionMapper;
71
72
	/** @var ShareMapper */
73
	private $shareMapper;
74
75
	/** @var PollMapper */
76
	private $pollMapper;
77
78
	/** @var LogMapper */
79
	private $logMapper;
80
81
	public function __construct(
82
		IUserManager $userManager,
83
		IGroupManager $groupManager,
84
		IConfig $config,
85
		IURLGenerator $urlGenerator,
86
		IL10N $trans,
87
		IFactory $transFactory,
88
		IMailer $mailer,
89
		ShareMapper $shareMapper,
90
		SubscriptionMapper $subscriptionMapper,
91
		PollMapper $pollMapper,
92
		LogMapper $logMapper
93
	) {
94
		$this->config = $config;
95
		$this->userManager = $userManager;
96
		$this->groupManager = $groupManager;
97
		$this->urlGenerator = $urlGenerator;
98
		$this->trans = $trans;
99
		$this->transFactory = $transFactory;
100
		$this->mailer = $mailer;
101
		$this->shareMapper = $shareMapper;
102
		$this->subscriptionMapper = $subscriptionMapper;
103
		$this->pollMapper = $pollMapper;
104
		$this->logMapper = $logMapper;
105
	}
106
107
	private function sendMail(IEmailTemplate $emailTemplate, string $emailAddress, string $displayName): void {
108
		if (!$emailAddress || !filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
109
			throw new \Exception('Invalid email address (' . $emailAddress . ')');
110
		}
111
112
		try {
113
			$message = $this->mailer->createMessage();
114
			$message->setTo([$emailAddress => $displayName]);
115
			$message->useTemplate($emailTemplate);
116
			$this->mailer->send($message);
117
		} catch (\Exception $e) {
118
			\OC::$server->getLogger()->logException($e->getMessage(), ['app' => 'polls']);
119
			throw $e;
120
		}
121
	}
122
123
	public function resolveEmailAddress(int $pollId, string $userId): string {
124
		if ($this->userManager->get($userId) instanceof IUser) {
125
			return \OC::$server->getConfig()->getUserValue($userId, 'settings', 'email');
126
		}
127
128
		// if $userId is no site user, eval via shares
129
		try {
130
			$share = $this->shareMapper->findByPollAndUser($pollId, $userId);
131
			if ($share->getEmailAddress()) {
132
				return $share->getEmailAddress();
133
			}
134
		} catch (\Exception $e) {
135
			// catch silently
136
		}
137
		return $userId;
138
	}
139
140
	public function resendInvitation(string $token): Share {
141
		$share = $this->shareMapper->findByToken($token);
142
		$poll = $this->pollMapper->find($share->getPollId());
143
		$recipient = $share->getUserObject();
144
		$emailTemplate = $this->generateInvitation($recipient, $poll, $share->getURL());
145
		$this->sendMail(
146
			$emailTemplate,
147
			$recipient->getEmailAddress(),
148
			$recipient->getDisplayName()
149
		);
150
		$share->setInvitationSent(time());
151
		return $this->shareMapper->update($share);
152
	}
153
154
	public function sendInvitation(string $token): array {
155
		$share = $this->shareMapper->findByToken($token);
156
		$poll = $this->pollMapper->find($share->getPollId());
157
		$sentMails = [];
158
		$abortedMails = [];
159
160
		foreach ($share->getMembers() as $recipient) {
161
			//skip poll owner
162
			if ($recipient->getId() === $poll->getOwner()) {
163
				continue;
164
			}
165
166
			$emailTemplate = $this->generateInvitation($recipient, $poll, $share->getURL());
167
168
			try {
169
				$this->sendMail(
170
					$emailTemplate,
171
					$recipient->getEmailAddress(),
172
					$recipient->getDisplayName()
173
				);
174
				$share->setInvitationSent(time());
175
				$this->shareMapper->update($share);
176
				$sentMails[] = $recipient->getId();
177
			} catch (\Exception $e) {
178
				$abortedMails[] = $recipient->getId();
179
				\OC::$server->getLogger()->alert('Error sending Mail to ' . json_encode($recipient));
180
			}
181
		}
182
		return ['sentMails' => $sentMails, 'abortedMails' => $abortedMails];
183
	}
184
185
	public function sendNotifications(): void {
186
		$subscriptions = [];
187
		$log = $this->logMapper->findUnprocessedPolls();
188
189
		foreach ($log as $logItem) {
190
			$subscriptions = array_merge($subscriptions, $this->subscriptionMapper->findAllByPoll($logItem->getPollId()));
191
		}
192
193
		$log = $this->logMapper->findUnprocessed();
194
195
		foreach ($subscriptions as $subscription) {
196
			$poll = $this->pollMapper->find($subscription->getPollId());
197
198
			if ($this->userManager->get($subscription->getUserId()) instanceof IUser) {
199
				$recipient = new User($subscription->getUserId());
200
				$url = $this->urlGenerator->linkToRouteAbsolute(
201
					'polls.page.vote',
202
					['id' => $subscription->getPollId()]
203
				);
204
			} else {
205
				try {
206
					$share = $this->shareMapper->findByPollAndUser($subscription->getPollId(), $subscription->getUserId());
207
					$recipient = $share->getUserObject();
208
					$url = $share->getURL();
209
				} catch (\Exception $e) {
210
					continue;
211
				}
212
			}
213
214
			$emailTemplate = $this->generateNotification($recipient, $poll, $url, $log);
215
216
			try {
217
				$this->sendMail(
218
					$emailTemplate,
219
					$recipient->getEmailAddress(),
220
					$recipient->getDisplayName()
221
				);
222
			} catch (\Exception $e) {
223
				\OC::$server->getLogger()->alert('Error sending Mail to ' . $recipient->getId());
224
			}
225
		}
226
	}
227
228
	private function getLogString(Log $logItem, string $displayName): string {
229
		if ($logItem->getMessage()) {
230
			return $logItem->getMessage();
231
		}
232
233
		switch ($logItem->getMessageId()) {
234
			case Log::MSG_ID_SETVOTE:
235
				return $this->trans->t('- %s voted.', [$displayName]);
236
			case Log::MSG_ID_UPDATEPOLL:
237
				return $this->trans->t('- Updated poll configuration. Please check your votes.');
238
			case Log::MSG_ID_DELETEPOLL:
239
				return $this->trans->t('- The poll got deleted.');
240
			case Log::MSG_ID_RESTOREPOLL:
241
				return $this->trans->t('- The poll got restored.');
242
			case Log::MSG_ID_EXPIREPOLL:
243
				return $this->trans->t('- The poll was closed.');
244
			case Log::MSG_ID_ADDOPTION:
245
				return $this->trans->t('- A vote option was added.');
246
			case Log::MSG_ID_DELETEOPTION:
247
				return $this->trans->t('- A vote option was removed.');
248
			default:
249
				return $logItem->getMessageId() . " (" . $displayName . ")";
250
		}
251
	}
252
253
	private function generateNotification(UserGroupClass $recipient, Poll $poll, string $url, array $log): IEMailTemplate {
254
		if ($recipient->getLanguage()) {
255
			$this->trans = $this->transFactory->get('polls', $recipient->getLanguage());
256
		} else {
257
			$this->trans = $this->transFactory->get('polls', $poll->getOwnerUserObject()->getLanguage());
258
		}
259
		$emailTemplate = $this->mailer->createEMailTemplate('polls.Notification', [
260
			'title' => $poll->getTitle(),
261
			'link' => $url
262
		]);
263
264
		$emailTemplate->setSubject($this->trans->t('Polls App - New Activity'));
265
		$emailTemplate->addHeader();
266
		$emailTemplate->addHeading($this->trans->t('Polls App - New Activity'), false);
267
		$emailTemplate->addBodyText(str_replace(
268
			['{title}'],
269
			[$poll->getTitle()],
270
			$this->trans->t('"{title}" had recent activity: ')
271
		));
272
		foreach ($log as $logItem) {
273
			if ($logItem->getPollId() === $poll->getId()) {
274
				if ($poll->getAnonymous() || $poll->getShowResults() !== "always") {
275
					$displayName = $this->trans->t('A user');
276
				} elseif ($this->userManager->get($logItem->getUserId()) instanceof IUser) {
277
					$actor = new User($logItem->getUserId());
278
					$displayName = $actor->getDisplayName();
279
				} else {
280
					try {
281
						$share = $this->shareMapper->findByPollAndUser($poll->getId(), $logItem->getUserId());
282
						$displayName = $share->getUserObject()->getDisplayName();
283
					} catch (\Exception $e) {
284
						$displayName = $logItem->getUserId();
285
					}
286
				}
287
288
				$emailTemplate->addBodyText($this->getLogString($logItem, $displayName));
289
			}
290
291
			$logItem->setProcessed(time());
292
			$this->logMapper->update($logItem);
293
		}
294
295
		$emailTemplate->addBodyButton(
296
			htmlspecialchars($this->trans->t('Go to poll')),
297
			$url,
298
			/** @scrutinizer ignore-type */ false
299
		);
300
		$emailTemplate->addFooter($this->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.'));
301
302
		return $emailTemplate;
303
	}
304
305
	private function generateInvitation(UserGroupClass $recipient, Poll $poll, string $url): IEMailTemplate {
306
		$owner = $poll->getOwnerUserObject();
307
		if ($recipient->getLanguage()) {
308
			$this->trans = $this->transFactory->get('polls', $recipient->getLanguage());
309
		} else {
310
			$this->trans = $this->transFactory->get('polls', $owner->getLanguage());
311
		}
312
313
		$emailTemplate = $this->mailer->createEMailTemplate('polls.Invitation', [
314
			'owner' => $owner->getDisplayName(),
315
			'title' => $poll->getTitle(),
316
			'link' => $url
317
		]);
318
319
		$emailTemplate->setSubject($this->trans->t('Poll invitation "%s"', $poll->getTitle()));
320
		$emailTemplate->addHeader();
321
		$emailTemplate->addHeading($this->trans->t('Poll invitation "%s"', $poll->getTitle()), false);
322
		$emailTemplate->addBodyText(str_replace(
323
				['{owner}', '{title}'],
324
				[$owner->getDisplayName(), $poll->getTitle()],
325
				$this->trans->t('{owner} invited you to take part in the poll "{title}"')
326
			));
327
		$emailTemplate->addBodyText($poll->getDescription());
328
		$emailTemplate->addBodyButton(
329
				htmlspecialchars($this->trans->t('Go to poll')),
330
				$url
331
			);
332
		$emailTemplate->addBodyText($this->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: '));
333
		$emailTemplate->addBodyText($url);
334
		$emailTemplate->addBodyText($this->trans->t('Do not share this link with other people, because it is connected to your votes.'));
335
		$emailTemplate->addFooter($this->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.'));
336
337
		return $emailTemplate;
338
	}
339
}
340