Completed
Pull Request — master (#459)
by Maxence
01:52
created

MemberAdd::verify()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 9.296
c 0
b 0
f 0
cc 4
nc 8
nop 3
1
<?php declare(strict_types=1);
2
3
4
/**
5
 * Circles - Bring cloud-users closer together.
6
 *
7
 * This file is licensed under the Affero General Public License version 3 or
8
 * later. See the COPYING file.
9
 *
10
 * @author Maxence Lange <[email protected]>
11
 * @copyright 2017
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
30
namespace OCA\Circles\GlobalScale;
31
32
33
use daita\MySmallPhpTools\Model\SimpleDataStore;
34
use Exception;
35
use OC\User\NoUserException;
36
use OCA\Circles\Exceptions\CircleDoesNotExistException;
37
use OCA\Circles\Exceptions\CircleTypeNotValidException;
38
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
39
use OCA\Circles\Exceptions\EmailAccountInvalidFormatException;
40
use OCA\Circles\Exceptions\GlobalScaleDSyncException;
41
use OCA\Circles\Exceptions\GlobalScaleEventException;
42
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
43
use OCA\Circles\Exceptions\MemberCantJoinCircleException;
44
use OCA\Circles\Exceptions\MemberIsNotModeratorException;
45
use OCA\Circles\Exceptions\MembersLimitException;
46
use OCA\Circles\Exceptions\TokenDoesNotExistException;
47
use OCA\Circles\Model\Circle;
48
use OCA\Circles\Model\GlobalScale\GSEvent;
49
use OCA\Circles\Model\Member;
50
use OCA\Circles\Model\SharesToken;
51
use OCP\IUser;
52
use OCP\Mail\IEMailTemplate;
53
use OCP\Util;
54
55
56
/**
57
 * Class MemberAdd
58
 *
59
 * @package OCA\Circles\GlobalScale
60
 */
61
class MemberAdd extends AGlobalScaleEvent {
62
63
64
	/**
65
	 * @param GSEvent $event
66
	 * @param bool $localCheck
67
	 * @param bool $mustBeChecked
68
	 *
69
	 * @throws CircleDoesNotExistException
70
	 * @throws ConfigNoCircleAvailableException
71
	 * @throws EmailAccountInvalidFormatException
72
	 * @throws GlobalScaleDSyncException
73
	 * @throws GlobalScaleEventException
74
	 * @throws MemberAlreadyExistsException
75
	 * @throws MemberCantJoinCircleException
76
	 * @throws MembersLimitException
77
	 * @throws NoUserException
78
	 * @throws CircleTypeNotValidException
79
	 * @throws MemberIsNotModeratorException
80
	 */
81
	public function verify(GSEvent $event, bool $localCheck = false, bool $mustBeChecked = false): void {
82
		parent::verify($event, $localCheck, true);
83
84
		$eventMember = $event->getMember();
85
		$this->cleanMember($eventMember);
86
87
		if ($eventMember->getInstance() === '') {
88
			$eventMember->setInstance($event->getSource());
89
		}
90
91
		$ident = $eventMember->getUserId();
92
		$this->membersService->verifyIdentBasedOnItsType(
93
			$ident, $eventMember->getType(), $eventMember->getInstance()
94
		);
95
96
		$circle = $event->getCircle();
97
//		$this->storeAuthorFromEvent($circle);
98
99
		if (!$event->isForced()) {
100
			$circle->getHigherViewer()
101
				   ->hasToBeModerator();
102
		}
103
104
		$member = $this->membersRequest->getFreshNewMember(
105
			$circle->getUniqueId(), $ident, $eventMember->getType(), $eventMember->getInstance()
106
		);
107
		$member->hasToBeInviteAble();
108
109
		$this->circlesService->checkThatCircleIsNotFull($circle);
110
		$this->membersService->addMemberBasedOnItsType($circle, $member);
111
112
		$password = '';
113
		if ($this->configService->enforcePasswordProtection($circle)) {
114
			$password = $this->miscService->token(15);
115
		}
116
117
		$event->setData(new SimpleDataStore(['password' => $password]));
118
		$event->setMember($member);
119
	}
120
121
122
	/**
123
	 * @param GSEvent $event
124
	 *
125
	 * @throws MemberAlreadyExistsException
126
	 */
127
	public function manage(GSEvent $event): void {
128
		$circle = $event->getCircle();
129
		$member = $event->getMember();
130
		if ($member->getJoined() === '') {
131
			$this->membersRequest->createMember($member);
132
		} else {
133
			$this->membersRequest->updateMember($member);
134
		}
135
136
		$password = $event->getData()
137
						  ->g('password');
138
		$shares = $this->generateUnknownSharesLinks($circle, $member, $password);
139
140
		$event->setResult(new SimpleDataStore(['unknownShares' => $shares]));
141
		$this->eventsService->onMemberNew($circle, $member);
142
	}
143
144
145
	/**
146
	 * @param GSEvent[] $events
147
	 */
148
	public function result(array $events): void {
149
		$password = '';
150
		$circle = $member = null;
151
		$links = [];
152
		foreach ($events as $event) {
153
			$password = $event->getData()
154
							  ->g('password');
155
156
			$circle = $event->getCircle();
157
			$member = $event->getMember();
158
159
			$links = array_merge(
160
				$links, $event->getResult()
161
							  ->gArray('unknownShares')
162
			);
163
		}
164
165 View Code Duplication
		if ($circle->getViewer() === null) {
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...
166
			$author = $circle->getOwner()
167
							 ->getUserId();
168
		} else {
169
			$author = $circle->getViewer()
170
							 ->getUserId();
171
		}
172
		$recipient = $member->getUserId();
173
174
		try {
175
			$template = $this->generateMailExitingShares($author, $circle->getName());
176
			$this->fillMailExistingShares($template, $links);
177
			$this->sendMailExistingShares($template, $author, $recipient);
178
			$this->sendPasswordExistingShares($author, $recipient, $password);
179
		} catch (Exception $e) {
180
			$this->miscService->log('Failed to send mail about existing share ' . $e->getMessage());
181
		}
182
	}
183
184
185
	/**
186
	 * @param Circle $circle
187
	 * @param Member $member
188
	 * @param string $password
189
	 *
190
	 * @return array
191
	 */
192
	private function generateUnknownSharesLinks(Circle $circle, Member $member, string $password): array {
0 ignored issues
show
Unused Code introduced by
The parameter $circle is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
193
		$unknownShares = $this->getUnknownShares($member);
194
195
		$data = [];
196
		foreach ($unknownShares as $share) {
197
			try {
198
				$data[] = $this->getMailLinkFromShare($share, $member, $password);
199
			} catch (TokenDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
200
			}
201
		}
202
203
		return $data;
204
	}
205
206
207
	/**
208
	 * @param Member $member
209
	 *
210
	 * @return array
211
	 */
212
	private function getUnknownShares(Member $member): array {
213
		$allShares = $this->sharesRequest->getSharesForCircle($member->getCircleId());
214
		$knownShares = array_map(
215
			function(SharesToken $shareToken) {
216
				return $shareToken->getShareId();
217
			},
218
			$this->tokensRequest->getTokensFromMember($member)
219
		);
220
221
		$unknownShares = [];
222
		foreach ($allShares as $share) {
223
			if (!in_array($share['id'], $knownShares)) {
224
				$unknownShares[] = $share;
225
			}
226
		}
227
228
		return $unknownShares;
229
	}
230
231
232
	/**
233
	 * @param array $share
234
	 * @param Member $member
235
	 * @param string $password
236
	 *
237
	 * @return array
238
	 * @throws TokenDoesNotExistException
239
	 */
240 View Code Duplication
	private function getMailLinkFromShare(array $share, Member $member, string $password = '') {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
241
		$sharesToken = $this->tokensRequest->generateTokenForMember($member, (int)$share['id'], $password);
242
		$link = $this->urlGenerator->linkToRouteAbsolute(
243
			'files_sharing.sharecontroller.showShare',
244
			['token' => $sharesToken->getToken()]
245
		);
246
		$author = $share['uid_initiator'];
247
		$filename = basename($share['file_target']);
248
249
		return [
250
			'author'   => $author,
251
			'link'     => $link,
252
			'filename' => $filename
253
		];
254
	}
255
256
257
	/**
258
	 * @param string $author
259
	 * @param string $circleName
260
	 *
261
	 * @return IEMailTemplate
262
	 */
263
	private function generateMailExitingShares(string $author, string $circleName): IEMailTemplate {
264
		$emailTemplate = $this->mailer->createEMailTemplate('circles.ExistingShareNotification', []);
265
		$emailTemplate->addHeader();
266
267
		$text = $this->l10n->t('%s shared multiple files with \'%s\'.', [$author, $circleName]);
268
		$emailTemplate->addBodyText(htmlspecialchars($text), $text);
269
270
		return $emailTemplate;
271
	}
272
273
	/**
274
	 * @param IEMailTemplate $emailTemplate
275
	 * @param array $links
276
	 */
277 View Code Duplication
	private function fillMailExistingShares(IEMailTemplate $emailTemplate, array $links) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
278
		foreach ($links as $item) {
279
			$emailTemplate->addBodyButton(
280
				$this->l10n->t('Open »%s«', [htmlspecialchars($item['filename'])]), $item['link']
281
			);
282
		}
283
	}
284
285
286
	/**
287
	 * @param IEMailTemplate $emailTemplate
288
	 * @param string $author
289
	 * @param string $recipient
290
	 *
291
	 * @throws Exception
292
	 */
293 View Code Duplication
	private function sendMailExistingShares(IEMailTemplate $emailTemplate, string $author, string $recipient
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
294
	) {
295
		$subject = $this->l10n->t('%s shared multiple files with you.', [$author]);
296
297
		$instanceName = $this->defaults->getName();
298
		$senderName = $this->l10n->t('%s on %s', [$author, $instanceName]);
299
300
		$message = $this->mailer->createMessage();
301
302
		$message->setFrom([Util::getDefaultEmailAddress($instanceName) => $senderName]);
303
		$message->setSubject($subject);
304
		$message->setPlainBody($emailTemplate->renderText());
305
		$message->setHtmlBody($emailTemplate->renderHtml());
306
		$message->setTo([$recipient]);
307
308
		$this->mailer->send($message);
309
	}
310
311
312
	/**
313
	 * @param string $author
314
	 * @param string $email
315
	 * @param string $password
316
	 *
317
	 * @throws Exception
318
	 */
319 View Code Duplication
	protected function sendPasswordExistingShares(string $author, string $email, string $password) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
320
		if ($password === '') {
321
			return;
322
		}
323
324
		$message = $this->mailer->createMessage();
325
326
		$authorUser = $this->userManager->get($author);
327
		$authorName = ($authorUser instanceof IUser) ? $authorUser->getDisplayName() : $author;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
328
		$authorEmail = ($authorUser instanceof IUser) ? $authorUser->getEMailAddress() : null;
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
329
330
		$this->miscService->log("Sending password mail about existing files to '" . $email . "'", 0);
331
332
		$plainBodyPart = $this->l10n->t(
333
			"%1\$s shared multiple files with you.\nYou should have already received a separate mail with a link to access them.\n",
334
			[$authorName]
335
		);
336
		$htmlBodyPart = $this->l10n->t(
337
			'%1$s shared multiple files with you. You should have already received a separate mail with a link to access them.',
338
			[$authorName]
339
		);
340
341
		$emailTemplate = $this->mailer->createEMailTemplate(
342
			'sharebymail.RecipientPasswordNotification', [
343
														   'password' => $password,
344
														   'author'   => $author
345
													   ]
346
		);
347
348
		$emailTemplate->setSubject(
349
			$this->l10n->t(
350
				'Password to access files shared to you by %1$s', [$authorName]
351
			)
352
		);
353
		$emailTemplate->addHeader();
354
		$emailTemplate->addHeading($this->l10n->t('Password to access files'), false);
355
		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
356
		$emailTemplate->addBodyText($this->l10n->t('It is protected with the following password:'));
357
		$emailTemplate->addBodyText($password);
358
359
		// The "From" contains the sharers name
360
		$instanceName = $this->defaults->getName();
361
		$senderName = $this->l10n->t(
362
			'%1$s via %2$s',
363
			[
364
				$authorName,
365
				$instanceName
366
			]
367
		);
368
369
		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
370
		if ($authorEmail !== null) {
371
			$message->setReplyTo([$authorEmail => $authorName]);
372
			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
373
		} else {
374
			$emailTemplate->addFooter();
375
		}
376
377
		$message->setTo([$email]);
378
		$message->useTemplate($emailTemplate);
379
		$this->mailer->send($message);
380
	}
381
382
383
}
384