Passed
Push — master ( ccc0a5...5195be )
by Roeland
10:43 queued 11s
created

Notifier::parseShareExpiration()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 3
dl 0
loc 21
rs 9.7998
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2019, Roeland Jago Douma <[email protected]>
5
 * @copyright Copyright (c) 2019, Joas Schilling <[email protected]>
6
 *
7
 * @author Roeland Jago Douma <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 *
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Files_Sharing\Notification;
28
29
use OCP\Files\IRootFolder;
30
use OCP\IL10N;
31
use OCP\IGroupManager;
32
use OCP\IURLGenerator;
33
use OCP\IUser;
34
use OCP\IUserManager;
35
use OCP\L10N\IFactory;
36
use OCP\Notification\AlreadyProcessedException;
37
use OCP\Notification\INotification;
38
use OCP\Notification\INotifier;
39
use OCP\Share\Exceptions\ShareNotFound;
40
use OCP\Share\IManager;
41
use OCP\Share\IShare;
42
43
class Notifier implements INotifier {
44
	public const INCOMING_USER_SHARE = 'incoming_user_share';
45
	public const INCOMING_GROUP_SHARE = 'incoming_group_share';
46
47
	/** @var IFactory */
48
	protected $l10nFactory;
49
	/** @var IManager */
50
	private $shareManager;
51
	/** @var IRootFolder */
52
	private $rootFolder;
53
	/** @var IGroupManager  */
54
	protected $groupManager;
55
	/** @var IUserManager  */
56
	protected $userManager;
57
	/** @var IURLGenerator */
58
	protected $url;
59
60
61
	public function __construct(IFactory $l10nFactory,
62
								IManager $shareManager,
63
								IRootFolder $rootFolder,
64
								IGroupManager $groupManager,
65
								IUserManager $userManager,
66
								IURLGenerator $url) {
67
		$this->l10nFactory = $l10nFactory;
68
		$this->shareManager = $shareManager;
69
		$this->rootFolder = $rootFolder;
70
		$this->groupManager = $groupManager;
71
		$this->userManager = $userManager;
72
		$this->url = $url;
73
	}
74
75
	/**
76
	 * Identifier of the notifier, only use [a-z0-9_]
77
	 *
78
	 * @return string
79
	 * @since 17.0.0
80
	 */
81
	public function getID(): string {
82
		return 'files_sharing';
83
	}
84
85
	/**
86
	 * Human readable name describing the notifier
87
	 *
88
	 * @return string
89
	 * @since 17.0.0
90
	 */
91
	public function getName(): string {
92
		return $this->l10nFactory->get('files_sharing')->t('File sharing');
93
	}
94
95
	/**
96
	 * @param INotification $notification
97
	 * @param string $languageCode The code of the language that should be used to prepare the notification
98
	 * @return INotification
99
	 * @throws \InvalidArgumentException When the notification was not prepared by a notifier
100
	 * @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted
101
	 * @since 9.0.0
102
	 */
103
	public function prepare(INotification $notification, string $languageCode): INotification {
104
		if ($notification->getApp() !== 'files_sharing' ||
105
			($notification->getSubject() !== 'expiresTomorrow' &&
106
				$notification->getObjectType() !== 'share')) {
107
			throw new \InvalidArgumentException('Unhandled app or subject');
108
		}
109
110
		$l = $this->l10nFactory->get('files_sharing', $languageCode);
111
		$attemptId = $notification->getObjectId();
112
113
		try {
114
			$share = $this->shareManager->getShareById($attemptId);
115
		} catch (ShareNotFound $e) {
116
			throw new AlreadyProcessedException();
117
		}
118
119
		if ($notification->getSubject() === 'expiresTomorrow') {
120
			$notification = $this->parseShareExpiration($share, $notification, $l);
121
		} else {
122
			$notification = $this->parseShareInvitation($share, $notification, $l);
123
		}
124
		return $notification;
125
	}
126
127
	protected function parseShareExpiration(IShare $share, INotification $notification, IL10N $l): INotification {
128
		$node = $share->getNode();
129
		$userFolder = $this->rootFolder->getUserFolder($notification->getUser());
130
		$path = $userFolder->getRelativePath($node->getPath());
131
132
		$notification
133
			->setParsedSubject($l->t('Share will expire tomorrow'))
134
			->setParsedMessage($l->t('One or more of your shares will expire tomorrow'))
135
			->setRichMessage(
136
				$l->t('Your share of {node} will expire tomorrow'),
137
				[
138
					'node' => [
139
						'type' => 'file',
140
						'id' => $node->getId(),
141
						'name' => $node->getName(),
142
						'path' => $path,
143
					],
144
				]
145
			);
146
147
		return $notification;
148
	}
149
150
	protected function parseShareInvitation(IShare $share, INotification $notification, IL10N $l): INotification {
151
152
		if ($share->getShareType() === IShare::TYPE_USER) {
153
			if ($share->getStatus() !== IShare::STATUS_PENDING) {
154
				throw new AlreadyProcessedException();
155
			}
156
		} else if ($share->getShareType() === IShare::TYPE_GROUP) {
157
			if ($share->getStatus() !== IShare::STATUS_PENDING) {
158
				throw new AlreadyProcessedException();
159
			}
160
		}
161
162
		switch ($notification->getSubject()) {
163
			case self::INCOMING_USER_SHARE:
164
				if ($share->getSharedWith() !== $notification->getUser()) {
165
					throw new AlreadyProcessedException();
166
				}
167
168
				$sharer = $this->userManager->get($share->getSharedBy());
169
				if (!$sharer instanceof IUser) {
170
					throw new \InvalidArgumentException('Temporary failure');
171
				}
172
173
				$subject = $l->t('You received {share} as a share by {user}');
174
				$subjectParameters = [
175
					'share' => [
176
						'type' => 'highlight',
177
						'id' => $notification->getObjectId(),
178
						'name' => $share->getNode()->getName(),
179
					],
180
					'user' =>  [
181
						'type' => 'user',
182
						'id' => $sharer->getUID(),
183
						'name' => $sharer->getDisplayName(),
184
					],
185
				];
186
				break;
187
188
			case self::INCOMING_GROUP_SHARE:
189
				$user = $this->userManager->get($notification->getUser());
190
				if (!$user instanceof IUser) {
191
					throw new AlreadyProcessedException();
192
				}
193
194
				$group = $this->groupManager->get($share->getSharedWith());
195
				if (!$group->inGroup($user)) {
196
					throw new AlreadyProcessedException();
197
				}
198
199
				if ($share->getPermissions() === 0) {
200
					// Already rejected
201
					throw new AlreadyProcessedException();
202
				}
203
204
				$sharer = $this->userManager->get($share->getSharedBy());
205
				if (!$sharer instanceof IUser) {
206
					throw new \InvalidArgumentException('Temporary failure');
207
				}
208
209
				$subject = $l->t('You received {share} to group {group} as a share by {user}');
210
				$subjectParameters = [
211
					'share' => [
212
						'type' => 'highlight',
213
						'id' => $notification->getObjectId(),
214
						'name' => $share->getNode()->getName(),
215
					],
216
					'group' => [
217
						'type' => 'user-group',
218
						'id' => $group->getGID(),
219
						'name' => $group->getDisplayName(),
220
					],
221
					'user' =>  [
222
						'type' => 'user',
223
						'id' => $sharer->getUID(),
224
						'name' => $sharer->getDisplayName(),
225
					],
226
				];
227
				break;
228
229
			default:
230
				throw new \InvalidArgumentException('Invalid subject');
231
		}
232
233
		$placeholders = $replacements = [];
234
		foreach ($subjectParameters as $placeholder => $parameter) {
235
			$placeholders[] = '{' . $placeholder . '}';
236
			$replacements[] = $parameter['name'];
237
		}
238
239
		$notification->setParsedSubject(str_replace($placeholders, $replacements, $subject))
240
			->setRichSubject($subject, $subjectParameters)
241
			->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg')));
242
243
		$acceptAction = $notification->createAction();
244
		$acceptAction->setParsedLabel($l->t('Accept'))
245
			->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.acceptShare', ['id' => $share->getId()]), 'POST')
246
			->setPrimary(true);
247
		$notification->addParsedAction($acceptAction);
248
249
		$rejectAction = $notification->createAction();
250
		$rejectAction->setParsedLabel($l->t('Reject'))
251
			->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.deleteShare', ['id' => $share->getId()]), 'DELETE')
252
			->setPrimary(false);
253
		$notification->addParsedAction($rejectAction);
254
255
		return $notification;
256
	}
257
}
258