Skip to content

Commit 256658a

Browse files
author
Kent Delante
committed
feat: provide the guest form to allow guest creation anywhere
Signed-off-by: Kent Delante <kent.delante@proton.me>
1 parent 8a1fc00 commit 256658a

14 files changed

Lines changed: 308 additions & 473 deletions

File tree

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
use OCA\Files\Event\LoadAdditionalScriptsEvent;
1111
use OCA\Guests\Capabilities;
12+
use OCA\Guests\Events\GuestCreatedEvent;
1213
use OCA\Guests\GroupBackend;
1314
use OCA\Guests\Hooks;
1415
use OCA\Guests\Listener\BeforeUserManagementRenderedListener;
@@ -32,6 +33,7 @@
3233
use OCP\Notification\IManager as INotificationManager;
3334
use OCP\Share\Events\ShareCreatedEvent;
3435
use OCP\User\Events\UserFirstTimeLoggedInEvent;
36+
use OCP\Util;
3537

3638
class Application extends App implements IBootstrap {
3739
public const APP_ID = 'guests';
@@ -59,12 +61,14 @@ public function boot(IBootContext $context): void {
5961
$this->setupGuestRestrictions($context->getAppContainer(), $context->getServerContainer());
6062
$this->setupNotifications($context->getAppContainer());
6163
$context->getAppContainer()->query(RestrictionManager::class)->lateSetupRestrictions();
64+
Util::addScript('guests', 'guests-init');
6265
}
6366

6467
private function setupGuestManagement(IAppContainer $container, IServerContainer $server): void {
6568
$hookManager = $container->query(Hooks::class);
6669
$server->get(IEventDispatcher::class)->addListener(ShareCreatedEvent::class, [$hookManager, 'handlePostShare']);
6770
$server->get(IEventDispatcher::class)->addListener(UserFirstTimeLoggedInEvent::class, [$hookManager, 'handleFirstLogin']);
71+
$server->get(IEventDispatcher::class)->addListener(GuestCreatedEvent::class, [$hookManager, 'handlePostCreate']);
6872
}
6973

7074
private function setupGuestRestrictions(IAppContainer $container, IServerContainer $server): void {

lib/Controller/UsersController.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
use OCA\Guests\Config;
1414
use OCA\Guests\Db\Transfer;
1515
use OCA\Guests\Db\TransferMapper;
16+
use OCA\Guests\Events\GuestCreatedEvent;
1617
use OCA\Guests\GuestManager;
1718
use OCA\Guests\TransferService;
1819
use OCP\AppFramework\Db\DoesNotExistException;
1920
use OCP\AppFramework\Http;
2021
use OCP\AppFramework\Http\DataResponse;
2122
use OCP\AppFramework\OCSController;
23+
use OCP\EventDispatcher\IEventDispatcher;
2224
use OCP\Group\ISubAdmin;
2325
use OCP\IGroupManager;
2426
use OCP\IL10N;
@@ -42,6 +44,7 @@ public function __construct(
4244
private IGroupManager $groupManager,
4345
private TransferService $transferService,
4446
private TransferMapper $transferMapper,
47+
private IEventDispatcher $eventDispatcher,
4548
) {
4649
parent::__construct($appName, $request);
4750
}
@@ -55,7 +58,7 @@ public function __construct(
5558
* @param array $groups
5659
* @return DataResponse
5760
*/
58-
public function create(string $email, string $displayName, string $language, array $groups): DataResponse {
61+
public function create(string $email, string $displayName, string $language, array $groups, bool $sendInvite = true): DataResponse {
5962
$errorMessages = [];
6063
$currentUser = $this->userSession->getUser();
6164

@@ -142,6 +145,7 @@ public function create(string $email, string $displayName, string $language, arr
142145
if ($this->userManager instanceof PublicEmitter) {
143146
$this->userManager->emit('\OC\User', 'assignedUserId', [$username]);
144147
}
148+
$this->eventDispatcher->dispatchTyped(new GuestCreatedEvent($username, $sendInvite));
145149
foreach ($groupObjects as $group) {
146150
$group->addUser($guestUser);
147151
}

lib/Events/GuestCreatedEvent.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Guests\Events;
10+
11+
use OCP\EventDispatcher\Event;
12+
13+
class GuestCreatedEvent extends Event {
14+
public function __construct(
15+
private string $userId,
16+
private bool $sendInviteEmail,
17+
) {
18+
parent::__construct();
19+
}
20+
21+
public function getUserId(): string {
22+
return $this->userId;
23+
}
24+
25+
public function shouldSendInviteEmail(): bool {
26+
return $this->sendInviteEmail;
27+
}
28+
}

lib/Hooks.php

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use OC\Files\Filesystem;
1212
use OCA\Guests\AppInfo\Application;
13+
use OCA\Guests\Events\GuestCreatedEvent;
1314
use OCA\Guests\Storage\ReadOnlyJail;
1415
use OCP\AppFramework\Db\DoesNotExistException;
1516
use OCP\AppFramework\IAppContainer;
@@ -76,7 +77,6 @@ public function handlePostShare(ShareCreatedEvent $event): void {
7677
$this->logger->debug("checking if '$shareWith' has a password",
7778
['app' => Application::APP_ID]);
7879

79-
8080
$passwordToken = $this->config->getUserValue(
8181
$shareWith,
8282
'core',
@@ -97,9 +97,9 @@ public function handlePostShare(ShareCreatedEvent $event): void {
9797
$this->mail->sendGuestInviteMail(
9898
$uid,
9999
$shareWith,
100-
$share,
101100
$token,
102-
$lang
101+
$lang,
102+
$share
103103
);
104104
$share->setMailSend(false);
105105
}
@@ -170,4 +170,64 @@ public function handleFirstLogin(UserFirstTimeLoggedInEvent $event): void {
170170
$guestUser->delete();
171171
}
172172
}
173+
174+
public function handlePostCreate(GuestCreatedEvent $event): void {
175+
if (!$event->shouldSendInviteEmail()) {
176+
return;
177+
}
178+
179+
$guest = $event->getUserId();
180+
181+
if (!$this->guestManager->isGuest($guest)) {
182+
$this->logger->debug(
183+
"ignoring user '$guest', not a guest",
184+
['app' => Application::APP_ID]
185+
);
186+
187+
return;
188+
}
189+
190+
$user = $this->userSession->getUser();
191+
192+
if (!$user) {
193+
throw new \Exception(
194+
'post_assign_id hook triggered without user in session'
195+
);
196+
}
197+
198+
$this->logger->debug("checking if '$guest' has a password",
199+
['app' => Application::APP_ID]);
200+
201+
202+
$guestUser = $this->userManager->get($guest);
203+
204+
$passwordToken = $this->config->getUserValue(
205+
$guest,
206+
'core',
207+
'lostpassword',
208+
null
209+
);
210+
211+
$uid = $user->getUID();
212+
213+
try {
214+
if ($passwordToken) {
215+
// user has not yet activated his account
216+
$decryptedToken = $this->crypto->decrypt($passwordToken, strtolower($guest) . $this->config->getSystemValue('secret'));
217+
[, $token] = explode(':', $decryptedToken);
218+
$lang = $this->config->getUserValue($guest, 'core', 'lang', '');
219+
// send invitation
220+
$this->mail->sendGuestInviteMail(
221+
$uid,
222+
$guest,
223+
$token,
224+
$lang
225+
);
226+
}
227+
} catch (DoesNotExistException $ex) {
228+
$this->logger->error("'$guest' does not exist", ['app' => Application::APP_ID]);
229+
} catch (\Exception $e) {
230+
$this->logger->error('Failed to send guest activation mail', ['app' => Application::APP_ID, 'exception' => $e]);
231+
}
232+
}
173233
}

lib/Listener/LoadAdditionalScriptsListener.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use OCA\Files\Event\LoadAdditionalScriptsEvent;
1414
use OCA\Guests\Config;
15+
use OCP\AppFramework\Services\IInitialState;
1516
use OCP\EventDispatcher\Event;
1617
use OCP\EventDispatcher\IEventListener;
1718
use OCP\Util;
@@ -23,10 +24,13 @@ class LoadAdditionalScriptsListener implements IEventListener {
2324

2425
public function __construct(
2526
private Config $config,
27+
private IInitialState $initialState,
2628
) {
2729
}
2830

2931
public function handle(Event $event): void {
32+
$this->initialState->provideInitialState('canCreateGuests', $this->config->canCreateGuests());
33+
3034
// If the user cannot create guests, we don't need to load the script
3135
if (!$this->config->canCreateGuests()) {
3236
return;

lib/Mail.php

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use OCP\Defaults;
1212
use OCP\IConfig;
13+
use OCP\IL10N;
1314
use OCP\IURLGenerator;
1415
use OCP\IUserManager;
1516
use OCP\IUserSession;
@@ -40,53 +41,83 @@ public function __construct(
4041
* @param $uid
4142
* @throws \Exception
4243
*/
43-
public function sendGuestInviteMail(string $uid, string $shareWith, Share\IShare $share, string $token, string $language = ''): void {
44+
public function sendGuestInviteMail(string $uid, string $guest, string $token, string $language = '', ?Share\IShare $share = null): void {
4445
if ($language === '') {
4546
$language = null;
4647
}
4748
$l10n = $this->l10nFactory->get('guests', $language);
4849

4950
$passwordLink = $this->urlGenerator->linkToRouteAbsolute(
5051
'core.lost.resetform',
51-
['userId' => $shareWith, 'token' => $token]
52+
['userId' => $guest, 'token' => $token]
5253
);
5354

54-
$this->logger->debug("sending invite to $shareWith: $passwordLink", ['app' => 'guests']);
55-
56-
$targetUser = $this->userManager->get($shareWith);
57-
$shareWithEmail = $targetUser->getEMailAddress();
58-
if (!$shareWithEmail) {
55+
$targetUser = $this->userManager->get($guest);
56+
$guestEmail = $targetUser->getEMailAddress();
57+
if (!$guestEmail) {
5958
throw new \Exception('Guest user created without email');
6059
}
6160
$replyTo = $this->userManager->get($uid)->getEMailAddress();
6261
$senderDisplayName = $this->userSession->getUser()->getDisplayName();
6362

63+
if (empty($share)) {
64+
[ $subject, $emailTemplate ] = $this->composeInviteMessage($senderDisplayName, $guestEmail, $passwordLink, $l10n);
65+
} else {
66+
[ $subject, $emailTemplate ] = $this->composeShareMessage($share, $senderDisplayName, $guestEmail, $passwordLink, $l10n);
67+
}
68+
69+
try {
70+
$message = $this->mailer->createMessage();
71+
$message->setTo([$guestEmail => $targetUser->getDisplayName()]);
72+
$message->setSubject($subject);
73+
$message->setHtmlBody($emailTemplate->renderHtml());
74+
$message->setPlainBody($emailTemplate->renderText());
75+
$message->setFrom([
76+
Util::getDefaultEmailAddress('sharing-noreply') =>
77+
$l10n->t('%s via %s', [
78+
$senderDisplayName,
79+
$this->defaults->getName()
80+
]),
81+
]);
82+
83+
if (!is_null($replyTo)) {
84+
$message->setReplyTo([$replyTo]);
85+
}
86+
87+
$this->mailer->send($message);
88+
} catch (\Exception $e) {
89+
throw new \Exception($l10n->t(
90+
'Couldn\'t send reset email. Please contact your administrator.'
91+
));
92+
}
93+
}
94+
95+
private function composeShareMessage(Share\IShare $share, string $senderDisplayName, string $guestEmail, string $passwordLink, IL10N $l10n): array {
6496
$filename = trim($share->getTarget(), '/');
65-
$subject = $l10n->t('%s shared »%s« with you', [$senderDisplayName, $filename]);
97+
$subject = $l10n->t('%s shared a file with you', [$senderDisplayName]);
6698
$expiration = $share->getExpirationDate();
6799

68100
$link = $this->urlGenerator->linkToRouteAbsolute(
69101
'files.viewcontroller.showFile', ['fileid' => $share->getNodeId(), 'direct' => 1]
70102
);
71-
72-
$emailTemplate = $this->mailer->createEMailTemplate('guest.invite');
103+
$emailTemplate = $this->mailer->createEMailTemplate('guest.share');
73104

74105
$emailTemplate->addHeader();
75-
$emailTemplate->addHeading($l10n->t('Incoming share'));
106+
$emailTemplate->addHeading($l10n->t('%s shared a file with you', [$senderDisplayName]));
76107

77108
$emailTemplate->addBodyText(
78109
$l10n->t('Hey there,')
79110
);
80111

81112
$emailTemplate->addBodyText(
82-
$l10n->t('%s just shared »%s« with you.', [$senderDisplayName, $filename])
113+
$l10n->t('%s just invited you and shared »%s« with you.', [$senderDisplayName, $filename])
83114
);
84115

85116
$emailTemplate->addBodyText(
86117
$l10n->t('You can access the shared file by activating your guest account.')
87118
);
88119
$emailTemplate->addBodyText(
89-
$l10n->t('After your account is activated you can view the share by logging in with %s.', [$shareWithEmail])
120+
$l10n->t('After your account is activated you can view the share by logging in with %s.', [$guestEmail])
90121
);
91122

92123
if ($expiration) {
@@ -104,29 +135,38 @@ public function sendGuestInviteMail(string $uid, string $shareWith, Share\IShare
104135
);
105136
$emailTemplate->addFooter();
106137

107-
try {
108-
$message = $this->mailer->createMessage();
109-
$message->setTo([$shareWithEmail => $targetUser->getDisplayName()]);
110-
$message->setSubject($subject);
111-
$message->setHtmlBody($emailTemplate->renderHtml());
112-
$message->setPlainBody($emailTemplate->renderText());
113-
$message->setFrom([
114-
Util::getDefaultEmailAddress('sharing-noreply') =>
115-
$l10n->t('%s via %s', [
116-
$senderDisplayName,
117-
$this->defaults->getName()
118-
]),
119-
]);
138+
return [ $subject, $emailTemplate ];
139+
}
120140

121-
if (!is_null($replyTo)) {
122-
$message->setReplyTo([$replyTo]);
123-
}
141+
private function composeInviteMessage(string $senderDisplayName, string $guestEmail, string $passwordLink, IL10N $l10n): array {
142+
$subject = $l10n->t('%s invited you as a guest', [$senderDisplayName]);
124143

125-
$this->mailer->send($message);
126-
} catch (\Exception $e) {
127-
throw new \Exception($l10n->t(
128-
'Couldn\'t send reset email. Please contact your administrator.'
129-
));
130-
}
144+
$emailTemplate = $this->mailer->createEMailTemplate('guest.invite');
145+
146+
$emailTemplate->addHeader();
147+
$emailTemplate->addHeading($l10n->t('You have been invited'));
148+
149+
$emailTemplate->addBodyText(
150+
$l10n->t('Hey there,')
151+
);
152+
153+
$emailTemplate->addBodyText(
154+
$l10n->t('%s just invited you.', [$senderDisplayName])
155+
);
156+
157+
$emailTemplate->addBodyText(
158+
$l10n->t('You can activate your guest account with the button below.')
159+
);
160+
$emailTemplate->addBodyText(
161+
$l10n->t('After your account is activated you can log in with %s.', [$guestEmail])
162+
);
163+
164+
$emailTemplate->addBodyButton(
165+
$l10n->t('Activate account'),
166+
$passwordLink
167+
);
168+
$emailTemplate->addFooter();
169+
170+
return [ $subject, $emailTemplate ];
131171
}
132172
}

0 commit comments

Comments
 (0)