Completed
Pull Request — master (#32506)
by Sujith
10:32
created

MailNotifications::sendLinkShareMail()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 16
nop 6
dl 0
loc 29
rs 9.1448
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Lukas Reschke <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Robin McCorkell <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author scolebrook <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 * @author Vincent Petry <[email protected]>
12
 *
13
 * @copyright Copyright (c) 2018, ownCloud GmbH
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\Share;
31
32
use DateTime;
33
use OCP\IConfig;
34
use OCP\IL10N;
35
use OCP\IURLGenerator;
36
use OCP\IUser;
37
use OCP\Mail\IMailer;
38
use OCP\ILogger;
39
use OCP\Defaults;
40
use OCP\Util;
41
use OC\Share\Filters\MailNotificationFilter;
42
use Symfony\Component\EventDispatcher\EventDispatcher;
43
use Symfony\Component\EventDispatcher\GenericEvent;
44
45
/**
46
 * Class MailNotifications
47
 *
48
 * @package OC\Share
49
 */
50
class MailNotifications {
51
52
	/** @var IUser sender userId */
53
	private $user;
54
	/** @var string sender email address */
55
	private $replyTo;
56
	/** @var string */
57
	private $senderDisplayName;
58
	/** @var IL10N */
59
	private $l;
60
	/** @var IMailer */
61
	private $mailer;
62
	/** @var IConfig */
63
	private $config;
64
	/** @var Defaults */
65
	private $defaults;
66
	/** @var ILogger */
67
	private $logger;
68
	/** @var IURLGenerator */
69
	private $urlGenerator;
70
	/** @var EventDispatcher  */
71
	private $eventDispatcher;
72
73
	/**
74
	 * @param IUser $user
75
	 * @param IL10N $l10n
76
	 * @param IMailer $mailer
77
	 * @param IConfig $config
78
	 * @param ILogger $logger
79
	 * @param Defaults $defaults
80
	 * @param IURLGenerator $urlGenerator
81
	 */
82
	public function __construct(IUser $user,
83
								IL10N $l10n,
84
								IMailer $mailer,
85
								IConfig $config,
86
								ILogger $logger,
87
								Defaults $defaults,
88
								IURLGenerator $urlGenerator,
89
								EventDispatcher $eventDispatcher) {
90
		$this->l = $l10n;
91
		$this->user = $user;
92
		$this->mailer = $mailer;
93
		$this->config = $config;
94
		$this->logger = $logger;
95
		$this->defaults = $defaults;
96
		$this->urlGenerator = $urlGenerator;
97
		$this->eventDispatcher = $eventDispatcher;
98
99
		$this->replyTo = $this->user->getEMailAddress();
100
101
		$filter = new MailNotificationFilter([
102
			'senderDisplayName' => $this->user->getDisplayName(),
103
		]);
104
105
		$this->senderDisplayName = $filter->getSenderDisplayName();
106
	}
107
108
	/**
109
	 * split a list of comma or semicolon separated email addresses
110
	 *
111
	 * @param string $mailsstring email addresses
112
	 * @return array list of individual addresses
113
	 */
114
	private function _mailStringToArray($mailsstring) {
115
		$sanatised  = \str_replace([', ', '; ', ',', ';', ' '], ',', $mailsstring);
116
		$mail_array = \explode(',', $sanatised);
117
118
		return $mail_array;
119
	}
120
121
	/**
122
	 * inform users if a file was shared with them
123
	 *
124
	 * @param IUser[] $recipientList list of recipients
125
	 * @param string $itemSource shared item source
126
	 * @param string $itemType shared item type
127
	 * @return array list of user to whom the mail send operation failed
128
	 */
129
	public function sendInternalShareMail($recipientList, $itemSource, $itemType) {
130
		$noMail = [];
131
132
		foreach ($recipientList as $recipient) {
133
			$recipientDisplayName = $recipient->getDisplayName();
134
			$to = $recipient->getEMailAddress();
135
136
			if ($to === null || $to === '') {
137
				$noMail[] = $recipientDisplayName;
138
				continue;
139
			}
140
141
			$items = $this->getItemSharedWithUser($itemSource, $itemType, $recipient);
142
			$filename = \trim($items[0]['file_target'], '/');
143
			$expiration = null;
144
			if (isset($items[0]['expiration'])) {
145
				try {
146
					$date = new DateTime($items[0]['expiration']);
147
					$expiration = $date->getTimestamp();
148
				} catch (\Exception $e) {
149
					$this->logger->error("Couldn't read date: ".$e->getMessage(), ['app' => 'sharing']);
150
				}
151
			}
152
153
			$link = $this->urlGenerator->linkToRouteAbsolute(
154
				'files.viewcontroller.showFile',
155
				['fileId' => $items[0]['item_source']]
156
			);
157
158
			$filter = new MailNotificationFilter([
159
				'link' => $link,
160
				'file' => $filename,
161
			]);
162
163
			$filename = $filter->getFile();
164
			$link = $filter->getLink();
165
166
			$subject = (string) $this->l->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
167
			list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration, null, 'internal');
168
169
			// send it out now
170
			try {
171
				$message = $this->mailer->createMessage();
172
				$message->setSubject($subject);
173
				$message->setTo([$to => $recipientDisplayName]);
174
				$message->setHtmlBody($htmlBody);
0 ignored issues
show
Bug introduced by
It seems like $htmlBody defined by $this->createMailBody($f...tion, null, 'internal') on line 167 can also be of type boolean; however, OC\Mail\Message::setHtmlBody() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
175
				$message->setPlainBody($textBody);
0 ignored issues
show
Bug introduced by
It seems like $textBody defined by $this->createMailBody($f...tion, null, 'internal') on line 167 can also be of type boolean; however, OC\Mail\Message::setPlainBody() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
176
				$message->setFrom($this->getFrom($this->l));
177
				if ($this->replyTo !== null) {
178
					$message->setReplyTo([$this->replyTo]);
179
				}
180
181
				$this->mailer->send($message);
182
			} catch (\Exception $e) {
183
				$this->logger->error("Can't send mail to inform the user about an internal share: ".$e->getMessage(), ['app' => 'sharing']);
184
				$noMail[] = $recipientDisplayName;
185
			}
186
		}
187
188
		return $noMail;
189
	}
190
191
	public function sendLinkShareMail($recipient, $filename, $link, $expiration, $personalNote = null, $options = []) {
192
		$notificationLang = $this->config->getAppValue(
193
			'core',
194
			'shareapi_public_notification_lang',
195
			null
196
		);
197
		if ($notificationLang !== null) {
198
			$l10n = \OC::$server->getL10N('lib', $notificationLang);
199
		} else {
200
			$l10n = $this->l;
201
		}
202
		$subject = (string)$l10n->t('%s shared »%s« with you', [$this->senderDisplayName, $filename]);
203
		list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration, $personalNote, '', $l10n);
204
205
		/**
206
		 * The event consumer of share.sendmail would have following data
207
		 * - link ( the public link created )
208
		 * - to ( the to recipient in mail )
209
		 * - cc ( the cc recipient in mail )
210
		 * - bcc ( the bcc recipient in mail )
211
		 */
212
		$toRecipients = isset($options['to']) ? $options['to'] : '';
213
		$ccRecipients = isset($options['cc']) ? $options['cc'] : '';
214
		$bccRecipients = isset($options['bcc']) ? $options['bcc'] : '';
215
		$event = new GenericEvent(null, ['link' => $link, 'to' => $toRecipients, 'cc' => $ccRecipients, 'bcc' => $bccRecipients]);
216
		$this->eventDispatcher->dispatch('share.sendmail', $event);
217
		$options['l10n'] = $l10n;
218
		return $this->sendLinkShareMailFromBody($recipient, $subject, $htmlBody, $textBody, $options);
219
	}
220
221
	/**
222
	 * inform recipient about public link share
223
	 *
224
	 * @param string $recipient recipient email address
225
	 * @param string $filename the shared file
0 ignored issues
show
Bug introduced by
There is no parameter named $filename. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
226
	 * @param string $link the public link
0 ignored issues
show
Bug introduced by
There is no parameter named $link. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
227
	 * @param array $options allows ['to], ['cc'] and ['bcc'] recipients
228
	 * @param int $expiration expiration date (timestamp)
0 ignored issues
show
Bug introduced by
There is no parameter named $expiration. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
229
	 * @return string[] $result of failed recipients
230
	 */
231
	public function sendLinkShareMailFromBody($recipient, $subject, $htmlBody, $textBody, $options = []) {
232
		$recipients = [];
233
		if ($recipient !== null) {
234
			$recipients    = $this->_mailStringToArray($recipient);
235
		}
236
		$toRecipients  = (isset($options['to']) && $options['to'] !== '') ? $this->_mailStringToArray($options['to']) : null;
237
		$ccRecipients  = (isset($options['cc']) && $options['cc'] !== '') ? $this->_mailStringToArray($options['cc']) : null;
238
		$bccRecipients = (isset($options['bcc']) && $options['bcc'] !== '') ? $this->_mailStringToArray($options['bcc']) : null;
239
		$l10n = (isset($options['l10n'])) ? $options['l10n'] : $this->l;
240
241
		try {
242
			$message = $this->mailer->createMessage();
243
			$message->setSubject($subject);
244
			$message->setTo($recipients);
245
			if ($htmlBody !== null) {
246
				$message->setHtmlBody($htmlBody);
247
			}
248
			if ($toRecipients !== null) {
249
				$message->setTo($toRecipients);
250
			}
251
			if ($bccRecipients !== null) {
252
				$message->setBcc($bccRecipients);
253
			}
254
			if ($ccRecipients !== null) {
255
				$message->setCc($ccRecipients);
256
			}
257
			$message->setPlainBody($textBody);
258
			$message->setFrom($this->getFrom($l10n));
259
			if ($this->replyTo !== null) {
260
				$message->setReplyTo([$this->replyTo]);
261
			}
262
263
			return $this->mailer->send($message);
264
		} catch (\Exception $e) {
265
			$allRecipientsArr = [];
266
			if ($recipient !== null && $recipient !== '') {
267
				$allRecipientsArr = \explode(',', $recipient);
268
			}
269 View Code Duplication
			if (isset($options['to']) && $options['to'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
				$allRecipientsArr = \array_merge(
271
					$allRecipientsArr,
272
					\explode(',', $options['to'])
273
				);
274
			}
275 View Code Duplication
			if (isset($options['cc']) && $options['cc'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
276
				$allRecipientsArr = \array_merge(
277
					$allRecipientsArr,
278
					\explode(',', $options['cc'])
279
				);
280
			}
281 View Code Duplication
			if (isset($options['bcc']) && $options['bcc'] !== '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
				$allRecipientsArr = \array_merge(
283
					$allRecipientsArr,
284
					\explode(',', $options['bcc'])
285
				);
286
			}
287
			$allRecipients = \implode(',', $allRecipientsArr);
288
			$this->logger->error(
289
				"Can't send mail with public link to $allRecipients: ".$e->getMessage(),
290
				['app' => 'sharing']
291
			);
292
			return [$allRecipients];
293
		}
294
	}
295
296
	/**
297
	 * create mail body for plain text and html mail
298
	 *
299
	 * @param string $filename the shared file
300
	 * @param string $link link to the shared file
301
	 * @param int $expiration expiration date (timestamp)
302
	 * @param string $personalNote optional personal note
303
	 * @param string $prefix prefix of mail template files
304
	 * @param IL10N|null $overrideL10n
305
	 *
306
	 * @return array an array of the html mail body and the plain text mail body
307
	 */
308
	public function createMailBody($filename,
309
								   $link,
310
								   $expiration,
311
								   $personalNote = null,
312
								   $prefix = '',
313
								   $overrideL10n = null
314
	) {
315
		$l10n = $overrideL10n === null ? $this->l : $overrideL10n;
316
		$formattedDate = $expiration ?  $l10n->l('date', $expiration) : null;
317
318
		$html = new \OC_Template(
319
			'core',
320
			$prefix . 'mail',
321
			'',
322
			true,
323
			$l10n->getLanguageCode()
324
		);
325
		$html->assign('link', $link);
326
		$html->assign('user_displayname', $this->senderDisplayName);
327
		$html->assign('filename', $filename);
328
		$html->assign('expiration', $formattedDate);
329
		if ($personalNote !== null && $personalNote !== '') {
330
			$html->assign('personal_note', \nl2br($personalNote));
331
		}
332
		$htmlMail = $html->fetchPage();
333
334
		$plainText = new \OC_Template(
335
			'core',
336
			$prefix . 'altmail',
337
			'',
338
			true,
339
			$l10n->getLanguageCode()
340
		);
341
		$plainText->assign('link', $link);
342
		$plainText->assign('user_displayname', $this->senderDisplayName);
343
		$plainText->assign('filename', $filename);
344
		$plainText->assign('expiration', $formattedDate);
345
		if ($personalNote !== null && $personalNote !== '') {
346
			$plainText->assign('personal_note', $personalNote);
347
		}
348
		$plainTextMail = $plainText->fetchPage();
349
350
		return [$htmlMail, $plainTextMail];
351
	}
352
353
	/**
354
	 * @param string $itemSource
355
	 * @param string $itemType
356
	 * @param IUser $recipient
357
	 * @return array
358
	 */
359
	protected function getItemSharedWithUser($itemSource, $itemType, $recipient) {
360
		return Share::getItemSharedWithUser($itemType, $itemSource, $recipient->getUID());
361
	}
362
363
	/**
364
	 * Get default sender details
365
	 *
366
	 * @param IL10N $l10n
367
	 *
368
	 * @return string[]
369
	 */
370
	protected function getFrom($l10n) {
371
		return [
372
			Util::getDefaultEmailAddress('sharing-noreply') => (string) $l10n->t(
373
				'%s via %s',
374
				[
375
					$this->senderDisplayName,
376
					$this->defaults->getName()
377
				]
378
			)
379
		];
380
	}
381
}
382