Passed
Push — master ( 456412...602de2 )
by Joas
13:17 queued 10s
created
apps/dav/lib/CardDAV/Converter.php 1 patch
Indentation   +115 added lines, -115 removed lines patch added patch discarded remove patch
@@ -36,119 +36,119 @@
 block discarded – undo
36 36
 
37 37
 class Converter {
38 38
 
39
-	/** @var AccountManager */
40
-	private $accountManager;
41
-
42
-	/**
43
-	 * Converter constructor.
44
-	 *
45
-	 * @param AccountManager $accountManager
46
-	 */
47
-	public function __construct(AccountManager $accountManager) {
48
-		$this->accountManager = $accountManager;
49
-	}
50
-
51
-	/**
52
-	 * @param IUser $user
53
-	 * @return VCard|null
54
-	 */
55
-	public function createCardFromUser(IUser $user) {
56
-		$userData = $this->accountManager->getUser($user);
57
-
58
-		$uid = $user->getUID();
59
-		$cloudId = $user->getCloudId();
60
-		$image = $this->getAvatarImage($user);
61
-
62
-		$vCard = new VCard();
63
-		$vCard->VERSION = '3.0';
64
-		$vCard->UID = $uid;
65
-
66
-		$publish = false;
67
-
68
-		if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
69
-			$userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
70
-		}
71
-
72
-		foreach ($userData as $property => $value) {
73
-			$shareWithTrustedServers =
74
-				$value['scope'] === AccountManager::SCOPE_FEDERATED ||
75
-				$value['scope'] === AccountManager::SCOPE_PUBLISHED;
76
-
77
-			$emptyValue = !isset($value['value']) || $value['value'] === '';
78
-
79
-			if ($shareWithTrustedServers && !$emptyValue) {
80
-				$publish = true;
81
-				switch ($property) {
82
-					case IAccountManager::PROPERTY_DISPLAYNAME:
83
-						$vCard->add(new Text($vCard, 'FN', $value['value']));
84
-						$vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
85
-						break;
86
-					case IAccountManager::PROPERTY_AVATAR:
87
-						if ($image !== null) {
88
-							$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
89
-						}
90
-						break;
91
-					case IAccountManager::PROPERTY_EMAIL:
92
-						$vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
93
-						break;
94
-					case IAccountManager::PROPERTY_WEBSITE:
95
-						$vCard->add(new Text($vCard, 'URL', $value['value']));
96
-						break;
97
-					case IAccountManager::PROPERTY_PHONE:
98
-						$vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
99
-						break;
100
-					case IAccountManager::PROPERTY_ADDRESS:
101
-						$vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
102
-						break;
103
-					case IAccountManager::PROPERTY_TWITTER:
104
-						$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
105
-						break;
106
-				}
107
-			}
108
-		}
109
-
110
-		if ($publish && !empty($cloudId)) {
111
-			$vCard->add(new Text($vCard, 'CLOUD', $cloudId));
112
-			$vCard->validate();
113
-			return $vCard;
114
-		}
115
-
116
-		return null;
117
-	}
118
-
119
-	/**
120
-	 * @param string $fullName
121
-	 * @return string[]
122
-	 */
123
-	public function splitFullName($fullName) {
124
-		// Very basic western style parsing. I'm not gonna implement
125
-		// https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
126
-
127
-		$elements = explode(' ', $fullName);
128
-		$result = ['', '', '', '', ''];
129
-		if (count($elements) > 2) {
130
-			$result[0] = implode(' ', array_slice($elements, count($elements) - 1));
131
-			$result[1] = $elements[0];
132
-			$result[2] = implode(' ', array_slice($elements, 1, count($elements) - 2));
133
-		} elseif (count($elements) === 2) {
134
-			$result[0] = $elements[1];
135
-			$result[1] = $elements[0];
136
-		} else {
137
-			$result[0] = $elements[0];
138
-		}
139
-
140
-		return $result;
141
-	}
142
-
143
-	/**
144
-	 * @param IUser $user
145
-	 * @return null|IImage
146
-	 */
147
-	private function getAvatarImage(IUser $user) {
148
-		try {
149
-			return $user->getAvatarImage(-1);
150
-		} catch (\Exception $ex) {
151
-			return null;
152
-		}
153
-	}
39
+    /** @var AccountManager */
40
+    private $accountManager;
41
+
42
+    /**
43
+     * Converter constructor.
44
+     *
45
+     * @param AccountManager $accountManager
46
+     */
47
+    public function __construct(AccountManager $accountManager) {
48
+        $this->accountManager = $accountManager;
49
+    }
50
+
51
+    /**
52
+     * @param IUser $user
53
+     * @return VCard|null
54
+     */
55
+    public function createCardFromUser(IUser $user) {
56
+        $userData = $this->accountManager->getUser($user);
57
+
58
+        $uid = $user->getUID();
59
+        $cloudId = $user->getCloudId();
60
+        $image = $this->getAvatarImage($user);
61
+
62
+        $vCard = new VCard();
63
+        $vCard->VERSION = '3.0';
64
+        $vCard->UID = $uid;
65
+
66
+        $publish = false;
67
+
68
+        if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
69
+            $userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
70
+        }
71
+
72
+        foreach ($userData as $property => $value) {
73
+            $shareWithTrustedServers =
74
+                $value['scope'] === AccountManager::SCOPE_FEDERATED ||
75
+                $value['scope'] === AccountManager::SCOPE_PUBLISHED;
76
+
77
+            $emptyValue = !isset($value['value']) || $value['value'] === '';
78
+
79
+            if ($shareWithTrustedServers && !$emptyValue) {
80
+                $publish = true;
81
+                switch ($property) {
82
+                    case IAccountManager::PROPERTY_DISPLAYNAME:
83
+                        $vCard->add(new Text($vCard, 'FN', $value['value']));
84
+                        $vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
85
+                        break;
86
+                    case IAccountManager::PROPERTY_AVATAR:
87
+                        if ($image !== null) {
88
+                            $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
89
+                        }
90
+                        break;
91
+                    case IAccountManager::PROPERTY_EMAIL:
92
+                        $vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
93
+                        break;
94
+                    case IAccountManager::PROPERTY_WEBSITE:
95
+                        $vCard->add(new Text($vCard, 'URL', $value['value']));
96
+                        break;
97
+                    case IAccountManager::PROPERTY_PHONE:
98
+                        $vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
99
+                        break;
100
+                    case IAccountManager::PROPERTY_ADDRESS:
101
+                        $vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
102
+                        break;
103
+                    case IAccountManager::PROPERTY_TWITTER:
104
+                        $vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
105
+                        break;
106
+                }
107
+            }
108
+        }
109
+
110
+        if ($publish && !empty($cloudId)) {
111
+            $vCard->add(new Text($vCard, 'CLOUD', $cloudId));
112
+            $vCard->validate();
113
+            return $vCard;
114
+        }
115
+
116
+        return null;
117
+    }
118
+
119
+    /**
120
+     * @param string $fullName
121
+     * @return string[]
122
+     */
123
+    public function splitFullName($fullName) {
124
+        // Very basic western style parsing. I'm not gonna implement
125
+        // https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
126
+
127
+        $elements = explode(' ', $fullName);
128
+        $result = ['', '', '', '', ''];
129
+        if (count($elements) > 2) {
130
+            $result[0] = implode(' ', array_slice($elements, count($elements) - 1));
131
+            $result[1] = $elements[0];
132
+            $result[2] = implode(' ', array_slice($elements, 1, count($elements) - 2));
133
+        } elseif (count($elements) === 2) {
134
+            $result[0] = $elements[1];
135
+            $result[1] = $elements[0];
136
+        } else {
137
+            $result[0] = $elements[0];
138
+        }
139
+
140
+        return $result;
141
+    }
142
+
143
+    /**
144
+     * @param IUser $user
145
+     * @return null|IImage
146
+     */
147
+    private function getAvatarImage(IUser $user) {
148
+        try {
149
+            return $user->getAvatarImage(-1);
150
+        } catch (\Exception $ex) {
151
+            return null;
152
+        }
153
+    }
154 154
 }
Please login to merge, or discard this patch.
apps/settings/templates/settings/personal/personal.info.php 1 patch
Indentation   +62 added lines, -62 removed lines patch added patch discarded remove patch
@@ -26,11 +26,11 @@  discard block
 block discarded – undo
26 26
 /** @var array $_ */
27 27
 
28 28
 script('settings', [
29
-	'usersettings',
30
-	'templates',
31
-	'federationsettingsview',
32
-	'federationscopemenu',
33
-	'settings/personalInfo',
29
+    'usersettings',
30
+    'templates',
31
+    'federationsettingsview',
32
+    'federationscopemenu',
33
+    'settings/personalInfo',
34 34
 ]);
35 35
 ?>
36 36
 
@@ -84,10 +84,10 @@  discard block
 block discarded – undo
84 84
 					<p class="quotatext">
85 85
 						<?php if ($_['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED): ?>
86 86
 							<?php print_unescaped($l->t('You are using <strong>%s</strong>',
87
-								[$_['usage']]));?>
87
+                                [$_['usage']]));?>
88 88
 						<?php else: ?>
89 89
 							<?php print_unescaped($l->t('You are using <strong>%1$s</strong> of <strong>%2$s</strong> (<strong>%3$s %%</strong>)',
90
-								[$_['usage'], $_['total_space'],  $_['usage_relative']]));?>
90
+                                [$_['usage'], $_['total_space'],  $_['usage_relative']]));?>
91 91
 						<?php endif ?>
92 92
 					</p>
93 93
 				</div>
@@ -109,16 +109,16 @@  discard block
 block discarded – undo
109 109
 				</h3>
110 110
 				<input type="text" id="displayname" name="displayname"
111 111
 					<?php if (!$_['displayNameChangeSupported']) {
112
-									print_unescaped('class="hidden"');
113
-								} ?>
112
+                                    print_unescaped('class="hidden"');
113
+                                } ?>
114 114
 					   value="<?php p($_['displayName']) ?>"
115 115
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
116 116
 				<?php if (!$_['displayNameChangeSupported']) { ?>
117 117
 					<span><?php if (isset($_['displayName']) && !empty($_['displayName'])) {
118
-									p($_['displayName']);
119
-								} else {
120
-									p($l->t('No display name set'));
121
-								} ?></span>
118
+                                    p($_['displayName']);
119
+                                } else {
120
+                                    p($l->t('No display name set'));
121
+                                } ?></span>
122 122
 				<?php } ?>
123 123
 				<span class="icon-checkmark hidden"></span>
124 124
 				<span class="icon-error hidden" ></span>
@@ -138,36 +138,36 @@  discard block
 block discarded – undo
138 138
 					</div>
139 139
 				</h3>
140 140
 				<div class="verify <?php if ($_['email'] === '' || $_['emailScope'] !== 'public') {
141
-									p('hidden');
142
-								} ?>">
141
+                                    p('hidden');
142
+                                } ?>">
143 143
 					<img id="verify-email" title="<?php p($_['emailMessage']); ?>" data-status="<?php p($_['emailVerification']) ?>" src="
144 144
 				<?php
145
-					switch ($_['emailVerification']) {
146
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
147
-							p(image_path('core', 'actions/verifying.svg'));
148
-							break;
149
-						case \OC\Accounts\AccountManager::VERIFIED:
150
-							p(image_path('core', 'actions/verified.svg'));
151
-							break;
152
-						default:
153
-							p(image_path('core', 'actions/verify.svg'));
154
-					}
155
-					?>">
145
+                    switch ($_['emailVerification']) {
146
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
147
+                            p(image_path('core', 'actions/verifying.svg'));
148
+                            break;
149
+                        case \OC\Accounts\AccountManager::VERIFIED:
150
+                            p(image_path('core', 'actions/verified.svg'));
151
+                            break;
152
+                        default:
153
+                            p(image_path('core', 'actions/verify.svg'));
154
+                    }
155
+                    ?>">
156 156
 				</div>
157 157
 				<input type="email" name="email" id="email" value="<?php p($_['email']); ?>"
158 158
 					<?php if (!$_['displayNameChangeSupported']) {
159
-						print_unescaped('class="hidden"');
160
-					} ?>
159
+                        print_unescaped('class="hidden"');
160
+                    } ?>
161 161
 					   placeholder="<?php p($l->t('Your email address')); ?>"
162 162
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
163 163
 				<span class="icon-checkmark hidden"></span>
164 164
 				<span class="icon-error hidden" ></span>
165 165
 				<?php if (!$_['displayNameChangeSupported']) { ?>
166 166
 					<span><?php if (isset($_['email']) && !empty($_['email'])) {
167
-						p($_['email']);
168
-					} else {
169
-						p($l->t('No email address set'));
170
-					}?></span>
167
+                        p($_['email']);
168
+                    } else {
169
+                        p($l->t('No email address set'));
170
+                    }?></span>
171 171
 				<?php } ?>
172 172
 				<?php if ($_['displayNameChangeSupported']) { ?>
173 173
 					<em><?php p($l->t('For password reset and notifications')); ?></em>
@@ -227,24 +227,24 @@  discard block
 block discarded – undo
227 227
 				</h3>
228 228
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
229 229
 				<div class="verify <?php if ($_['website'] === '' || $_['websiteScope'] !== 'public') {
230
-						p('hidden');
231
-					} ?>">
230
+                        p('hidden');
231
+                    } ?>">
232 232
 					<img id="verify-website" title="<?php p($_['websiteMessage']); ?>" data-status="<?php p($_['websiteVerification']) ?>" src="
233 233
 					<?php
234
-					switch ($_['websiteVerification']) {
235
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
236
-							p(image_path('core', 'actions/verifying.svg'));
237
-							break;
238
-						case \OC\Accounts\AccountManager::VERIFIED:
239
-							p(image_path('core', 'actions/verified.svg'));
240
-							break;
241
-						default:
242
-							p(image_path('core', 'actions/verify.svg'));
243
-					}
244
-					?>"
234
+                    switch ($_['websiteVerification']) {
235
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
236
+                            p(image_path('core', 'actions/verifying.svg'));
237
+                            break;
238
+                        case \OC\Accounts\AccountManager::VERIFIED:
239
+                            p(image_path('core', 'actions/verified.svg'));
240
+                            break;
241
+                        default:
242
+                            p(image_path('core', 'actions/verify.svg'));
243
+                    }
244
+                    ?>"
245 245
 					<?php if ($_['websiteVerification'] === \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS || $_['websiteVerification'] === \OC\Accounts\AccountManager::NOT_VERIFIED) {
246
-						print_unescaped(' class="verify-action"');
247
-					} ?>
246
+                        print_unescaped(' class="verify-action"');
247
+                    } ?>
248 248
 					>
249 249
 					<div class="verification-dialog popovermenu bubble menu">
250 250
 						<div class="verification-dialog-content">
@@ -276,24 +276,24 @@  discard block
 block discarded – undo
276 276
 				</h3>
277 277
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
278 278
 				<div class="verify <?php if ($_['twitter'] === '' || $_['twitterScope'] !== 'public') {
279
-						p('hidden');
280
-					} ?>">
279
+                        p('hidden');
280
+                    } ?>">
281 281
 					<img id="verify-twitter" title="<?php p($_['twitterMessage']); ?>" data-status="<?php p($_['twitterVerification']) ?>" src="
282 282
 					<?php
283
-					switch ($_['twitterVerification']) {
284
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
285
-							p(image_path('core', 'actions/verifying.svg'));
286
-							break;
287
-						case \OC\Accounts\AccountManager::VERIFIED:
288
-							p(image_path('core', 'actions/verified.svg'));
289
-							break;
290
-						default:
291
-							p(image_path('core', 'actions/verify.svg'));
292
-					}
293
-					?>"
283
+                    switch ($_['twitterVerification']) {
284
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
285
+                            p(image_path('core', 'actions/verifying.svg'));
286
+                            break;
287
+                        case \OC\Accounts\AccountManager::VERIFIED:
288
+                            p(image_path('core', 'actions/verified.svg'));
289
+                            break;
290
+                        default:
291
+                            p(image_path('core', 'actions/verify.svg'));
292
+                    }
293
+                    ?>"
294 294
 					<?php if ($_['twitterVerification'] === \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS || $_['twitterVerification'] === \OC\Accounts\AccountManager::NOT_VERIFIED) {
295
-						print_unescaped(' class="verify-action"');
296
-					} ?>
295
+                        print_unescaped(' class="verify-action"');
296
+                    } ?>
297 297
 					>
298 298
 					<div class="verification-dialog popovermenu bubble menu">
299 299
 						<div class="verification-dialog-content">
Please login to merge, or discard this patch.
apps/settings/lib/Settings/Personal/PersonalInfo.php 1 patch
Indentation   +232 added lines, -232 removed lines patch added patch discarded remove patch
@@ -53,236 +53,236 @@
 block discarded – undo
53 53
 
54 54
 class PersonalInfo implements ISettings {
55 55
 
56
-	/** @var IConfig */
57
-	private $config;
58
-	/** @var IUserManager */
59
-	private $userManager;
60
-	/** @var AccountManager */
61
-	private $accountManager;
62
-	/** @var IGroupManager */
63
-	private $groupManager;
64
-	/** @var IAppManager */
65
-	private $appManager;
66
-	/** @var IFactory */
67
-	private $l10nFactory;
68
-	/** @var IL10N */
69
-	private $l;
70
-
71
-	public function __construct(
72
-		IConfig $config,
73
-		IUserManager $userManager,
74
-		IGroupManager $groupManager,
75
-		AccountManager $accountManager,
76
-		IAppManager $appManager,
77
-		IFactory $l10nFactory,
78
-		IL10N $l
79
-	) {
80
-		$this->config = $config;
81
-		$this->userManager = $userManager;
82
-		$this->accountManager = $accountManager;
83
-		$this->groupManager = $groupManager;
84
-		$this->appManager = $appManager;
85
-		$this->l10nFactory = $l10nFactory;
86
-		$this->l = $l;
87
-	}
88
-
89
-	public function getForm(): TemplateResponse {
90
-		$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
91
-		$lookupServerUploadEnabled = false;
92
-		if ($federatedFileSharingEnabled) {
93
-			/** @var FederatedShareProvider $shareProvider */
94
-			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
95
-			$lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
96
-		}
97
-
98
-		$uid = \OC_User::getUser();
99
-		$user = $this->userManager->get($uid);
100
-		$account = $this->accountManager->getAccount($user);
101
-
102
-		// make sure FS is setup before querying storage related stuff...
103
-		\OC_Util::setupFS($user->getUID());
104
-
105
-		$storageInfo = \OC_Helper::getStorageInfo('/');
106
-		if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
107
-			$totalSpace = $this->l->t('Unlimited');
108
-		} else {
109
-			$totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
110
-		}
111
-
112
-		$languageParameters = $this->getLanguages($user);
113
-		$localeParameters = $this->getLocales($user);
114
-		$messageParameters = $this->getMessageParameters($account);
115
-
116
-		$parameters = [
117
-			'total_space' => $totalSpace,
118
-			'usage' => \OC_Helper::humanFileSize($storageInfo['used']),
119
-			'usage_relative' => round($storageInfo['relative']),
120
-			'quota' => $storageInfo['quota'],
121
-			'avatarChangeSupported' => $user->canChangeAvatar(),
122
-			'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
123
-			'avatarScope' => $account->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope(),
124
-			'displayNameChangeSupported' => $user->canChangeDisplayName(),
125
-			'displayName' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue(),
126
-			'displayNameScope' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope(),
127
-			'email' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
128
-			'emailScope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
129
-			'emailVerification' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
130
-			'phone' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(),
131
-			'phoneScope' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getScope(),
132
-			'address' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getValue(),
133
-			'addressScope' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getScope(),
134
-			'website' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getValue(),
135
-			'websiteScope' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getScope(),
136
-			'websiteVerification' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getVerified(),
137
-			'twitter' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getValue(),
138
-			'twitterScope' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
139
-			'twitterVerification' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getVerified(),
140
-			'groups' => $this->getGroups($user),
141
-		] + $messageParameters + $languageParameters + $localeParameters;
142
-
143
-
144
-		return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
145
-	}
146
-
147
-	/**
148
-	 * @return string the section ID, e.g. 'sharing'
149
-	 * @since 9.1
150
-	 */
151
-	public function getSection(): string {
152
-		return 'personal-info';
153
-	}
154
-
155
-	/**
156
-	 * @return int whether the form should be rather on the top or bottom of
157
-	 * the admin section. The forms are arranged in ascending order of the
158
-	 * priority values. It is required to return a value between 0 and 100.
159
-	 *
160
-	 * E.g.: 70
161
-	 * @since 9.1
162
-	 */
163
-	public function getPriority(): int {
164
-		return 10;
165
-	}
166
-
167
-	/**
168
-	 * returns a sorted list of the user's group GIDs
169
-	 *
170
-	 * @param IUser $user
171
-	 * @return array
172
-	 */
173
-	private function getGroups(IUser $user): array {
174
-		$groups = array_map(
175
-			static function (IGroup $group) {
176
-				return $group->getDisplayName();
177
-			},
178
-			$this->groupManager->getUserGroups($user)
179
-		);
180
-		sort($groups);
181
-
182
-		return $groups;
183
-	}
184
-
185
-	/**
186
-	 * returns the user language, common language and other languages in an
187
-	 * associative array
188
-	 *
189
-	 * @param IUser $user
190
-	 * @return array
191
-	 */
192
-	private function getLanguages(IUser $user): array {
193
-		$forceLanguage = $this->config->getSystemValue('force_language', false);
194
-		if ($forceLanguage !== false) {
195
-			return [];
196
-		}
197
-
198
-		$uid = $user->getUID();
199
-
200
-		$userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
201
-		$languages = $this->l10nFactory->getLanguages();
202
-
203
-		// associate the user language with the proper array
204
-		$userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code'));
205
-		$userLang = $languages['commonlanguages'][$userLangIndex];
206
-		// search in the other languages
207
-		if ($userLangIndex === false) {
208
-			$userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code'));
209
-			$userLang = $languages['languages'][$userLangIndex];
210
-		}
211
-		// if user language is not available but set somehow: show the actual code as name
212
-		if (!is_array($userLang)) {
213
-			$userLang = [
214
-				'code' => $userConfLang,
215
-				'name' => $userConfLang,
216
-			];
217
-		}
218
-
219
-		return array_merge(
220
-			['activelanguage' => $userLang],
221
-			$languages
222
-		);
223
-	}
224
-
225
-	private function getLocales(IUser $user): array {
226
-		$forceLanguage = $this->config->getSystemValue('force_locale', false);
227
-		if ($forceLanguage !== false) {
228
-			return [];
229
-		}
230
-
231
-		$uid = $user->getUID();
232
-
233
-		$userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale());
234
-
235
-		$userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
236
-
237
-		$localeCodes = $this->l10nFactory->findAvailableLocales();
238
-
239
-		$userLocale = array_filter($localeCodes, function ($value) use ($userLocaleString) {
240
-			return $userLocaleString === $value['code'];
241
-		});
242
-
243
-		if (!empty($userLocale)) {
244
-			$userLocale = reset($userLocale);
245
-		}
246
-
247
-		$localesForLanguage = array_filter($localeCodes, function ($localeCode) use ($userLang) {
248
-			return 0 === strpos($localeCode['code'], $userLang);
249
-		});
250
-
251
-		if (!$userLocale) {
252
-			$userLocale = [
253
-				'code' => 'en',
254
-				'name' => 'English'
255
-			];
256
-		}
257
-
258
-		return [
259
-			'activelocaleLang' => $userLocaleString,
260
-			'activelocale' => $userLocale,
261
-			'locales' => $localeCodes,
262
-			'localesForLanguage' => $localesForLanguage,
263
-		];
264
-	}
265
-
266
-	/**
267
-	 * @param IAccount $account
268
-	 * @return array
269
-	 */
270
-	private function getMessageParameters(IAccount $account): array {
271
-		$needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
272
-		$messageParameters = [];
273
-		foreach ($needVerifyMessage as $property) {
274
-			switch ($account->getProperty($property)->getVerified()) {
275
-				case AccountManager::VERIFIED:
276
-					$message = $this->l->t('Verifying');
277
-					break;
278
-				case AccountManager::VERIFICATION_IN_PROGRESS:
279
-					$message = $this->l->t('Verifying …');
280
-					break;
281
-				default:
282
-					$message = $this->l->t('Verify');
283
-			}
284
-			$messageParameters[$property . 'Message'] = $message;
285
-		}
286
-		return $messageParameters;
287
-	}
56
+    /** @var IConfig */
57
+    private $config;
58
+    /** @var IUserManager */
59
+    private $userManager;
60
+    /** @var AccountManager */
61
+    private $accountManager;
62
+    /** @var IGroupManager */
63
+    private $groupManager;
64
+    /** @var IAppManager */
65
+    private $appManager;
66
+    /** @var IFactory */
67
+    private $l10nFactory;
68
+    /** @var IL10N */
69
+    private $l;
70
+
71
+    public function __construct(
72
+        IConfig $config,
73
+        IUserManager $userManager,
74
+        IGroupManager $groupManager,
75
+        AccountManager $accountManager,
76
+        IAppManager $appManager,
77
+        IFactory $l10nFactory,
78
+        IL10N $l
79
+    ) {
80
+        $this->config = $config;
81
+        $this->userManager = $userManager;
82
+        $this->accountManager = $accountManager;
83
+        $this->groupManager = $groupManager;
84
+        $this->appManager = $appManager;
85
+        $this->l10nFactory = $l10nFactory;
86
+        $this->l = $l;
87
+    }
88
+
89
+    public function getForm(): TemplateResponse {
90
+        $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
91
+        $lookupServerUploadEnabled = false;
92
+        if ($federatedFileSharingEnabled) {
93
+            /** @var FederatedShareProvider $shareProvider */
94
+            $shareProvider = \OC::$server->query(FederatedShareProvider::class);
95
+            $lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
96
+        }
97
+
98
+        $uid = \OC_User::getUser();
99
+        $user = $this->userManager->get($uid);
100
+        $account = $this->accountManager->getAccount($user);
101
+
102
+        // make sure FS is setup before querying storage related stuff...
103
+        \OC_Util::setupFS($user->getUID());
104
+
105
+        $storageInfo = \OC_Helper::getStorageInfo('/');
106
+        if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
107
+            $totalSpace = $this->l->t('Unlimited');
108
+        } else {
109
+            $totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
110
+        }
111
+
112
+        $languageParameters = $this->getLanguages($user);
113
+        $localeParameters = $this->getLocales($user);
114
+        $messageParameters = $this->getMessageParameters($account);
115
+
116
+        $parameters = [
117
+            'total_space' => $totalSpace,
118
+            'usage' => \OC_Helper::humanFileSize($storageInfo['used']),
119
+            'usage_relative' => round($storageInfo['relative']),
120
+            'quota' => $storageInfo['quota'],
121
+            'avatarChangeSupported' => $user->canChangeAvatar(),
122
+            'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
123
+            'avatarScope' => $account->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope(),
124
+            'displayNameChangeSupported' => $user->canChangeDisplayName(),
125
+            'displayName' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getValue(),
126
+            'displayNameScope' => $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME)->getScope(),
127
+            'email' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
128
+            'emailScope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
129
+            'emailVerification' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
130
+            'phone' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getValue(),
131
+            'phoneScope' => $account->getProperty(IAccountManager::PROPERTY_PHONE)->getScope(),
132
+            'address' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getValue(),
133
+            'addressScope' => $account->getProperty(IAccountManager::PROPERTY_ADDRESS)->getScope(),
134
+            'website' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getValue(),
135
+            'websiteScope' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getScope(),
136
+            'websiteVerification' => $account->getProperty(IAccountManager::PROPERTY_WEBSITE)->getVerified(),
137
+            'twitter' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getValue(),
138
+            'twitterScope' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
139
+            'twitterVerification' => $account->getProperty(IAccountManager::PROPERTY_TWITTER)->getVerified(),
140
+            'groups' => $this->getGroups($user),
141
+        ] + $messageParameters + $languageParameters + $localeParameters;
142
+
143
+
144
+        return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
145
+    }
146
+
147
+    /**
148
+     * @return string the section ID, e.g. 'sharing'
149
+     * @since 9.1
150
+     */
151
+    public function getSection(): string {
152
+        return 'personal-info';
153
+    }
154
+
155
+    /**
156
+     * @return int whether the form should be rather on the top or bottom of
157
+     * the admin section. The forms are arranged in ascending order of the
158
+     * priority values. It is required to return a value between 0 and 100.
159
+     *
160
+     * E.g.: 70
161
+     * @since 9.1
162
+     */
163
+    public function getPriority(): int {
164
+        return 10;
165
+    }
166
+
167
+    /**
168
+     * returns a sorted list of the user's group GIDs
169
+     *
170
+     * @param IUser $user
171
+     * @return array
172
+     */
173
+    private function getGroups(IUser $user): array {
174
+        $groups = array_map(
175
+            static function (IGroup $group) {
176
+                return $group->getDisplayName();
177
+            },
178
+            $this->groupManager->getUserGroups($user)
179
+        );
180
+        sort($groups);
181
+
182
+        return $groups;
183
+    }
184
+
185
+    /**
186
+     * returns the user language, common language and other languages in an
187
+     * associative array
188
+     *
189
+     * @param IUser $user
190
+     * @return array
191
+     */
192
+    private function getLanguages(IUser $user): array {
193
+        $forceLanguage = $this->config->getSystemValue('force_language', false);
194
+        if ($forceLanguage !== false) {
195
+            return [];
196
+        }
197
+
198
+        $uid = $user->getUID();
199
+
200
+        $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
201
+        $languages = $this->l10nFactory->getLanguages();
202
+
203
+        // associate the user language with the proper array
204
+        $userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code'));
205
+        $userLang = $languages['commonlanguages'][$userLangIndex];
206
+        // search in the other languages
207
+        if ($userLangIndex === false) {
208
+            $userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code'));
209
+            $userLang = $languages['languages'][$userLangIndex];
210
+        }
211
+        // if user language is not available but set somehow: show the actual code as name
212
+        if (!is_array($userLang)) {
213
+            $userLang = [
214
+                'code' => $userConfLang,
215
+                'name' => $userConfLang,
216
+            ];
217
+        }
218
+
219
+        return array_merge(
220
+            ['activelanguage' => $userLang],
221
+            $languages
222
+        );
223
+    }
224
+
225
+    private function getLocales(IUser $user): array {
226
+        $forceLanguage = $this->config->getSystemValue('force_locale', false);
227
+        if ($forceLanguage !== false) {
228
+            return [];
229
+        }
230
+
231
+        $uid = $user->getUID();
232
+
233
+        $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale());
234
+
235
+        $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
236
+
237
+        $localeCodes = $this->l10nFactory->findAvailableLocales();
238
+
239
+        $userLocale = array_filter($localeCodes, function ($value) use ($userLocaleString) {
240
+            return $userLocaleString === $value['code'];
241
+        });
242
+
243
+        if (!empty($userLocale)) {
244
+            $userLocale = reset($userLocale);
245
+        }
246
+
247
+        $localesForLanguage = array_filter($localeCodes, function ($localeCode) use ($userLang) {
248
+            return 0 === strpos($localeCode['code'], $userLang);
249
+        });
250
+
251
+        if (!$userLocale) {
252
+            $userLocale = [
253
+                'code' => 'en',
254
+                'name' => 'English'
255
+            ];
256
+        }
257
+
258
+        return [
259
+            'activelocaleLang' => $userLocaleString,
260
+            'activelocale' => $userLocale,
261
+            'locales' => $localeCodes,
262
+            'localesForLanguage' => $localesForLanguage,
263
+        ];
264
+    }
265
+
266
+    /**
267
+     * @param IAccount $account
268
+     * @return array
269
+     */
270
+    private function getMessageParameters(IAccount $account): array {
271
+        $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
272
+        $messageParameters = [];
273
+        foreach ($needVerifyMessage as $property) {
274
+            switch ($account->getProperty($property)->getVerified()) {
275
+                case AccountManager::VERIFIED:
276
+                    $message = $this->l->t('Verifying');
277
+                    break;
278
+                case AccountManager::VERIFICATION_IN_PROGRESS:
279
+                    $message = $this->l->t('Verifying …');
280
+                    break;
281
+                default:
282
+                    $message = $this->l->t('Verify');
283
+            }
284
+            $messageParameters[$property . 'Message'] = $message;
285
+        }
286
+        return $messageParameters;
287
+    }
288 288
 }
Please login to merge, or discard this patch.
apps/settings/lib/Controller/UsersController.php 1 patch
Indentation   +510 added lines, -510 removed lines patch added patch discarded remove patch
@@ -70,514 +70,514 @@
 block discarded – undo
70 70
 use function in_array;
71 71
 
72 72
 class UsersController extends Controller {
73
-	/** @var UserManager */
74
-	private $userManager;
75
-	/** @var GroupManager */
76
-	private $groupManager;
77
-	/** @var IUserSession */
78
-	private $userSession;
79
-	/** @var IConfig */
80
-	private $config;
81
-	/** @var bool */
82
-	private $isAdmin;
83
-	/** @var IL10N */
84
-	private $l10n;
85
-	/** @var IMailer */
86
-	private $mailer;
87
-	/** @var Factory */
88
-	private $l10nFactory;
89
-	/** @var IAppManager */
90
-	private $appManager;
91
-	/** @var AccountManager */
92
-	private $accountManager;
93
-	/** @var Manager */
94
-	private $keyManager;
95
-	/** @var IJobList */
96
-	private $jobList;
97
-	/** @var IManager */
98
-	private $encryptionManager;
99
-	/** @var KnownUserService */
100
-	private $knownUserService;
101
-	/** @var IEventDispatcher */
102
-	private $dispatcher;
103
-
104
-
105
-	public function __construct(
106
-		string $appName,
107
-		IRequest $request,
108
-		IUserManager $userManager,
109
-		IGroupManager $groupManager,
110
-		IUserSession $userSession,
111
-		IConfig $config,
112
-		bool $isAdmin,
113
-		IL10N $l10n,
114
-		IMailer $mailer,
115
-		IFactory $l10nFactory,
116
-		IAppManager $appManager,
117
-		AccountManager $accountManager,
118
-		Manager $keyManager,
119
-		IJobList $jobList,
120
-		IManager $encryptionManager,
121
-		KnownUserService $knownUserService,
122
-		IEventDispatcher $dispatcher
123
-	) {
124
-		parent::__construct($appName, $request);
125
-		$this->userManager = $userManager;
126
-		$this->groupManager = $groupManager;
127
-		$this->userSession = $userSession;
128
-		$this->config = $config;
129
-		$this->isAdmin = $isAdmin;
130
-		$this->l10n = $l10n;
131
-		$this->mailer = $mailer;
132
-		$this->l10nFactory = $l10nFactory;
133
-		$this->appManager = $appManager;
134
-		$this->accountManager = $accountManager;
135
-		$this->keyManager = $keyManager;
136
-		$this->jobList = $jobList;
137
-		$this->encryptionManager = $encryptionManager;
138
-		$this->knownUserService = $knownUserService;
139
-		$this->dispatcher = $dispatcher;
140
-	}
141
-
142
-
143
-	/**
144
-	 * @NoCSRFRequired
145
-	 * @NoAdminRequired
146
-	 *
147
-	 * Display users list template
148
-	 *
149
-	 * @return TemplateResponse
150
-	 */
151
-	public function usersListByGroup(): TemplateResponse {
152
-		return $this->usersList();
153
-	}
154
-
155
-	/**
156
-	 * @NoCSRFRequired
157
-	 * @NoAdminRequired
158
-	 *
159
-	 * Display users list template
160
-	 *
161
-	 * @return TemplateResponse
162
-	 */
163
-	public function usersList(): TemplateResponse {
164
-		$user = $this->userSession->getUser();
165
-		$uid = $user->getUID();
166
-
167
-		\OC::$server->getNavigationManager()->setActiveEntry('core_users');
168
-
169
-		/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
170
-		$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
171
-		$isLDAPUsed = false;
172
-		if ($this->config->getSystemValue('sort_groups_by_name', false)) {
173
-			$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
174
-		} else {
175
-			if ($this->appManager->isEnabledForUser('user_ldap')) {
176
-				$isLDAPUsed =
177
-					$this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
178
-				if ($isLDAPUsed) {
179
-					// LDAP user count can be slow, so we sort by group name here
180
-					$sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
181
-				}
182
-			}
183
-		}
184
-
185
-		$canChangePassword = $this->canAdminChangeUserPasswords();
186
-
187
-		/* GROUPS */
188
-		$groupsInfo = new \OC\Group\MetaData(
189
-			$uid,
190
-			$this->isAdmin,
191
-			$this->groupManager,
192
-			$this->userSession
193
-		);
194
-
195
-		$groupsInfo->setSorting($sortGroupsBy);
196
-		[$adminGroup, $groups] = $groupsInfo->get();
197
-
198
-		if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
199
-			$isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
200
-				return $ldapFound || $backend instanceof User_Proxy;
201
-			});
202
-		}
203
-
204
-		$disabledUsers = -1;
205
-		$userCount = 0;
206
-
207
-		if (!$isLDAPUsed) {
208
-			if ($this->isAdmin) {
209
-				$disabledUsers = $this->userManager->countDisabledUsers();
210
-				$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
211
-					return $v + (int)$w;
212
-				}, 0);
213
-			} else {
214
-				// User is subadmin !
215
-				// Map group list to names to retrieve the countDisabledUsersOfGroups
216
-				$userGroups = $this->groupManager->getUserGroups($user);
217
-				$groupsNames = [];
218
-
219
-				foreach ($groups as $key => $group) {
220
-					// $userCount += (int)$group['usercount'];
221
-					array_push($groupsNames, $group['name']);
222
-					// we prevent subadmins from looking up themselves
223
-					// so we lower the count of the groups he belongs to
224
-					if (array_key_exists($group['id'], $userGroups)) {
225
-						$groups[$key]['usercount']--;
226
-						$userCount -= 1; // we also lower from one the total count
227
-					}
228
-				}
229
-				$userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
230
-				$disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
231
-			}
232
-
233
-			$userCount -= $disabledUsers;
234
-		}
235
-
236
-		$disabledUsersGroup = [
237
-			'id' => 'disabled',
238
-			'name' => 'Disabled users',
239
-			'usercount' => $disabledUsers
240
-		];
241
-
242
-		/* QUOTAS PRESETS */
243
-		$quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
244
-		$defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
245
-
246
-		$event = new BeforeTemplateRenderedEvent();
247
-		$this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
248
-		$this->dispatcher->dispatchTyped($event);
249
-
250
-		/* LANGUAGES */
251
-		$languages = $this->l10nFactory->getLanguages();
252
-
253
-		/* FINAL DATA */
254
-		$serverData = [];
255
-		// groups
256
-		$serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
257
-		// Various data
258
-		$serverData['isAdmin'] = $this->isAdmin;
259
-		$serverData['sortGroups'] = $sortGroupsBy;
260
-		$serverData['quotaPreset'] = $quotaPreset;
261
-		$serverData['userCount'] = $userCount;
262
-		$serverData['languages'] = $languages;
263
-		$serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
264
-		$serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
265
-		// Settings
266
-		$serverData['defaultQuota'] = $defaultQuota;
267
-		$serverData['canChangePassword'] = $canChangePassword;
268
-		$serverData['newUserGenerateUserID'] = $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes';
269
-		$serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
270
-		$serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
271
-
272
-		return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
273
-	}
274
-
275
-	/**
276
-	 * @param string $key
277
-	 * @param string $value
278
-	 *
279
-	 * @return JSONResponse
280
-	 */
281
-	public function setPreference(string $key, string $value): JSONResponse {
282
-		$allowed = ['newUser.sendEmail'];
283
-		if (!in_array($key, $allowed, true)) {
284
-			return new JSONResponse([], Http::STATUS_FORBIDDEN);
285
-		}
286
-
287
-		$this->config->setAppValue('core', $key, $value);
288
-
289
-		return new JSONResponse([]);
290
-	}
291
-
292
-	/**
293
-	 * Parse the app value for quota_present
294
-	 *
295
-	 * @param string $quotaPreset
296
-	 * @return array
297
-	 */
298
-	protected function parseQuotaPreset(string $quotaPreset): array {
299
-		// 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
300
-		$presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
301
-		// Drop default and none, Make array indexes numerically
302
-		return array_values(array_diff($presets, ['default', 'none']));
303
-	}
304
-
305
-	/**
306
-	 * check if the admin can change the users password
307
-	 *
308
-	 * The admin can change the passwords if:
309
-	 *
310
-	 *   - no encryption module is loaded and encryption is disabled
311
-	 *   - encryption module is loaded but it doesn't require per user keys
312
-	 *
313
-	 * The admin can not change the passwords if:
314
-	 *
315
-	 *   - an encryption module is loaded and it uses per-user keys
316
-	 *   - encryption is enabled but no encryption modules are loaded
317
-	 *
318
-	 * @return bool
319
-	 */
320
-	protected function canAdminChangeUserPasswords(): bool {
321
-		$isEncryptionEnabled = $this->encryptionManager->isEnabled();
322
-		try {
323
-			$noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
324
-			$isEncryptionModuleLoaded = true;
325
-		} catch (ModuleDoesNotExistsException $e) {
326
-			$noUserSpecificEncryptionKeys = true;
327
-			$isEncryptionModuleLoaded = false;
328
-		}
329
-		$canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
330
-			|| (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
331
-
332
-		return $canChangePassword;
333
-	}
334
-
335
-	/**
336
-	 * @NoAdminRequired
337
-	 * @NoSubAdminRequired
338
-	 * @PasswordConfirmationRequired
339
-	 *
340
-	 * @param string|null $avatarScope
341
-	 * @param string|null $displayname
342
-	 * @param string|null $displaynameScope
343
-	 * @param string|null $phone
344
-	 * @param string|null $phoneScope
345
-	 * @param string|null $email
346
-	 * @param string|null $emailScope
347
-	 * @param string|null $website
348
-	 * @param string|null $websiteScope
349
-	 * @param string|null $address
350
-	 * @param string|null $addressScope
351
-	 * @param string|null $twitter
352
-	 * @param string|null $twitterScope
353
-	 *
354
-	 * @return DataResponse
355
-	 */
356
-	public function setUserSettings(?string $avatarScope = null,
357
-									?string $displayname = null,
358
-									?string $displaynameScope = null,
359
-									?string $phone = null,
360
-									?string $phoneScope = null,
361
-									?string $email = null,
362
-									?string $emailScope = null,
363
-									?string $website = null,
364
-									?string $websiteScope = null,
365
-									?string $address = null,
366
-									?string $addressScope = null,
367
-									?string $twitter = null,
368
-									?string $twitterScope = null
369
-	) {
370
-		$user = $this->userSession->getUser();
371
-		if (!$user instanceof IUser) {
372
-			return new DataResponse(
373
-				[
374
-					'status' => 'error',
375
-					'data' => [
376
-						'message' => $this->l10n->t('Invalid user')
377
-					]
378
-				],
379
-				Http::STATUS_UNAUTHORIZED
380
-			);
381
-		}
382
-
383
-		$email = strtolower($email);
384
-		if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
385
-			return new DataResponse(
386
-				[
387
-					'status' => 'error',
388
-					'data' => [
389
-						'message' => $this->l10n->t('Invalid mail address')
390
-					]
391
-				],
392
-				Http::STATUS_UNPROCESSABLE_ENTITY
393
-			);
394
-		}
395
-
396
-		$data = $this->accountManager->getUser($user);
397
-		$beforeData = $data;
398
-		$data[IAccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
399
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
400
-			$data[IAccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
401
-			$data[IAccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
402
-		}
403
-		$data[IAccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
404
-		$data[IAccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
405
-		$data[IAccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
406
-		$data[IAccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
407
-
408
-		try {
409
-			$data = $this->saveUserSettings($user, $data);
410
-			if ($beforeData[IAccountManager::PROPERTY_PHONE]['value'] !== $data[IAccountManager::PROPERTY_PHONE]['value']) {
411
-				$this->knownUserService->deleteByContactUserId($user->getUID());
412
-			}
413
-			return new DataResponse(
414
-				[
415
-					'status' => 'success',
416
-					'data' => [
417
-						'userId' => $user->getUID(),
418
-						'avatarScope' => $data[IAccountManager::PROPERTY_AVATAR]['scope'],
419
-						'displayname' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
420
-						'displaynameScope' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
421
-						'phone' => $data[IAccountManager::PROPERTY_PHONE]['value'],
422
-						'phoneScope' => $data[IAccountManager::PROPERTY_PHONE]['scope'],
423
-						'email' => $data[IAccountManager::PROPERTY_EMAIL]['value'],
424
-						'emailScope' => $data[IAccountManager::PROPERTY_EMAIL]['scope'],
425
-						'website' => $data[IAccountManager::PROPERTY_WEBSITE]['value'],
426
-						'websiteScope' => $data[IAccountManager::PROPERTY_WEBSITE]['scope'],
427
-						'address' => $data[IAccountManager::PROPERTY_ADDRESS]['value'],
428
-						'addressScope' => $data[IAccountManager::PROPERTY_ADDRESS]['scope'],
429
-						'twitter' => $data[IAccountManager::PROPERTY_TWITTER]['value'],
430
-						'twitterScope' => $data[IAccountManager::PROPERTY_TWITTER]['scope'],
431
-						'message' => $this->l10n->t('Settings saved')
432
-					]
433
-				],
434
-				Http::STATUS_OK
435
-			);
436
-		} catch (ForbiddenException $e) {
437
-			return new DataResponse([
438
-				'status' => 'error',
439
-				'data' => [
440
-					'message' => $e->getMessage()
441
-				],
442
-			]);
443
-		} catch (\InvalidArgumentException $e) {
444
-			return new DataResponse([
445
-				'status' => 'error',
446
-				'data' => [
447
-					'message' => $e->getMessage()
448
-				],
449
-			]);
450
-		}
451
-	}
452
-	/**
453
-	 * update account manager with new user data
454
-	 *
455
-	 * @param IUser $user
456
-	 * @param array $data
457
-	 * @return array
458
-	 * @throws ForbiddenException
459
-	 * @throws \InvalidArgumentException
460
-	 */
461
-	protected function saveUserSettings(IUser $user, array $data): array {
462
-		// keep the user back-end up-to-date with the latest display name and email
463
-		// address
464
-		$oldDisplayName = $user->getDisplayName();
465
-		$oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
466
-		if (isset($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
467
-			&& $oldDisplayName !== $data[IAccountManager::PROPERTY_DISPLAYNAME]['value']
468
-		) {
469
-			$result = $user->setDisplayName($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
470
-			if ($result === false) {
471
-				throw new ForbiddenException($this->l10n->t('Unable to change full name'));
472
-			}
473
-		}
474
-
475
-		$oldEmailAddress = $user->getEMailAddress();
476
-		$oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
477
-		if (isset($data[IAccountManager::PROPERTY_EMAIL]['value'])
478
-			&& $oldEmailAddress !== $data[IAccountManager::PROPERTY_EMAIL]['value']
479
-		) {
480
-			// this is the only permission a backend provides and is also used
481
-			// for the permission of setting a email address
482
-			if (!$user->canChangeDisplayName()) {
483
-				throw new ForbiddenException($this->l10n->t('Unable to change email address'));
484
-			}
485
-			$user->setEMailAddress($data[IAccountManager::PROPERTY_EMAIL]['value']);
486
-		}
487
-
488
-		try {
489
-			return $this->accountManager->updateUser($user, $data, true);
490
-		} catch (\InvalidArgumentException $e) {
491
-			if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
492
-				throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
493
-			}
494
-			throw new \InvalidArgumentException($this->l10n->t('Some account data was invalid'));
495
-		}
496
-	}
497
-
498
-	/**
499
-	 * Set the mail address of a user
500
-	 *
501
-	 * @NoAdminRequired
502
-	 * @NoSubAdminRequired
503
-	 * @PasswordConfirmationRequired
504
-	 *
505
-	 * @param string $account
506
-	 * @param bool $onlyVerificationCode only return verification code without updating the data
507
-	 * @return DataResponse
508
-	 */
509
-	public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
510
-		$user = $this->userSession->getUser();
511
-
512
-		if ($user === null) {
513
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
514
-		}
515
-
516
-		$accountData = $this->accountManager->getUser($user);
517
-		$cloudId = $user->getCloudId();
518
-		$message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
519
-		$signature = $this->signMessage($user, $message);
520
-
521
-		$code = $message . ' ' . $signature;
522
-		$codeMd5 = $message . ' ' . md5($signature);
523
-
524
-		switch ($account) {
525
-			case 'verify-twitter':
526
-				$accountData[IAccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
527
-				$msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
528
-				$code = $codeMd5;
529
-				$type = IAccountManager::PROPERTY_TWITTER;
530
-				$accountData[IAccountManager::PROPERTY_TWITTER]['signature'] = $signature;
531
-				break;
532
-			case 'verify-website':
533
-				$accountData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
534
-				$msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
535
-				$type = IAccountManager::PROPERTY_WEBSITE;
536
-				$accountData[IAccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
537
-				break;
538
-			default:
539
-				return new DataResponse([], Http::STATUS_BAD_REQUEST);
540
-		}
541
-
542
-		if ($onlyVerificationCode === false) {
543
-			$accountData = $this->accountManager->updateUser($user, $accountData);
544
-			$data = $accountData[$type]['value'];
545
-
546
-			$this->jobList->add(VerifyUserData::class,
547
-				[
548
-					'verificationCode' => $code,
549
-					'data' => $data,
550
-					'type' => $type,
551
-					'uid' => $user->getUID(),
552
-					'try' => 0,
553
-					'lastRun' => $this->getCurrentTime()
554
-				]
555
-			);
556
-		}
557
-
558
-		return new DataResponse(['msg' => $msg, 'code' => $code]);
559
-	}
560
-
561
-	/**
562
-	 * get current timestamp
563
-	 *
564
-	 * @return int
565
-	 */
566
-	protected function getCurrentTime(): int {
567
-		return time();
568
-	}
569
-
570
-	/**
571
-	 * sign message with users private key
572
-	 *
573
-	 * @param IUser $user
574
-	 * @param string $message
575
-	 *
576
-	 * @return string base64 encoded signature
577
-	 */
578
-	protected function signMessage(IUser $user, string $message): string {
579
-		$privateKey = $this->keyManager->getKey($user)->getPrivate();
580
-		openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
581
-		return base64_encode($signature);
582
-	}
73
+    /** @var UserManager */
74
+    private $userManager;
75
+    /** @var GroupManager */
76
+    private $groupManager;
77
+    /** @var IUserSession */
78
+    private $userSession;
79
+    /** @var IConfig */
80
+    private $config;
81
+    /** @var bool */
82
+    private $isAdmin;
83
+    /** @var IL10N */
84
+    private $l10n;
85
+    /** @var IMailer */
86
+    private $mailer;
87
+    /** @var Factory */
88
+    private $l10nFactory;
89
+    /** @var IAppManager */
90
+    private $appManager;
91
+    /** @var AccountManager */
92
+    private $accountManager;
93
+    /** @var Manager */
94
+    private $keyManager;
95
+    /** @var IJobList */
96
+    private $jobList;
97
+    /** @var IManager */
98
+    private $encryptionManager;
99
+    /** @var KnownUserService */
100
+    private $knownUserService;
101
+    /** @var IEventDispatcher */
102
+    private $dispatcher;
103
+
104
+
105
+    public function __construct(
106
+        string $appName,
107
+        IRequest $request,
108
+        IUserManager $userManager,
109
+        IGroupManager $groupManager,
110
+        IUserSession $userSession,
111
+        IConfig $config,
112
+        bool $isAdmin,
113
+        IL10N $l10n,
114
+        IMailer $mailer,
115
+        IFactory $l10nFactory,
116
+        IAppManager $appManager,
117
+        AccountManager $accountManager,
118
+        Manager $keyManager,
119
+        IJobList $jobList,
120
+        IManager $encryptionManager,
121
+        KnownUserService $knownUserService,
122
+        IEventDispatcher $dispatcher
123
+    ) {
124
+        parent::__construct($appName, $request);
125
+        $this->userManager = $userManager;
126
+        $this->groupManager = $groupManager;
127
+        $this->userSession = $userSession;
128
+        $this->config = $config;
129
+        $this->isAdmin = $isAdmin;
130
+        $this->l10n = $l10n;
131
+        $this->mailer = $mailer;
132
+        $this->l10nFactory = $l10nFactory;
133
+        $this->appManager = $appManager;
134
+        $this->accountManager = $accountManager;
135
+        $this->keyManager = $keyManager;
136
+        $this->jobList = $jobList;
137
+        $this->encryptionManager = $encryptionManager;
138
+        $this->knownUserService = $knownUserService;
139
+        $this->dispatcher = $dispatcher;
140
+    }
141
+
142
+
143
+    /**
144
+     * @NoCSRFRequired
145
+     * @NoAdminRequired
146
+     *
147
+     * Display users list template
148
+     *
149
+     * @return TemplateResponse
150
+     */
151
+    public function usersListByGroup(): TemplateResponse {
152
+        return $this->usersList();
153
+    }
154
+
155
+    /**
156
+     * @NoCSRFRequired
157
+     * @NoAdminRequired
158
+     *
159
+     * Display users list template
160
+     *
161
+     * @return TemplateResponse
162
+     */
163
+    public function usersList(): TemplateResponse {
164
+        $user = $this->userSession->getUser();
165
+        $uid = $user->getUID();
166
+
167
+        \OC::$server->getNavigationManager()->setActiveEntry('core_users');
168
+
169
+        /* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
170
+        $sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
171
+        $isLDAPUsed = false;
172
+        if ($this->config->getSystemValue('sort_groups_by_name', false)) {
173
+            $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
174
+        } else {
175
+            if ($this->appManager->isEnabledForUser('user_ldap')) {
176
+                $isLDAPUsed =
177
+                    $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy');
178
+                if ($isLDAPUsed) {
179
+                    // LDAP user count can be slow, so we sort by group name here
180
+                    $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME;
181
+                }
182
+            }
183
+        }
184
+
185
+        $canChangePassword = $this->canAdminChangeUserPasswords();
186
+
187
+        /* GROUPS */
188
+        $groupsInfo = new \OC\Group\MetaData(
189
+            $uid,
190
+            $this->isAdmin,
191
+            $this->groupManager,
192
+            $this->userSession
193
+        );
194
+
195
+        $groupsInfo->setSorting($sortGroupsBy);
196
+        [$adminGroup, $groups] = $groupsInfo->get();
197
+
198
+        if (!$isLDAPUsed && $this->appManager->isEnabledForUser('user_ldap')) {
199
+            $isLDAPUsed = (bool)array_reduce($this->userManager->getBackends(), function ($ldapFound, $backend) {
200
+                return $ldapFound || $backend instanceof User_Proxy;
201
+            });
202
+        }
203
+
204
+        $disabledUsers = -1;
205
+        $userCount = 0;
206
+
207
+        if (!$isLDAPUsed) {
208
+            if ($this->isAdmin) {
209
+                $disabledUsers = $this->userManager->countDisabledUsers();
210
+                $userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
211
+                    return $v + (int)$w;
212
+                }, 0);
213
+            } else {
214
+                // User is subadmin !
215
+                // Map group list to names to retrieve the countDisabledUsersOfGroups
216
+                $userGroups = $this->groupManager->getUserGroups($user);
217
+                $groupsNames = [];
218
+
219
+                foreach ($groups as $key => $group) {
220
+                    // $userCount += (int)$group['usercount'];
221
+                    array_push($groupsNames, $group['name']);
222
+                    // we prevent subadmins from looking up themselves
223
+                    // so we lower the count of the groups he belongs to
224
+                    if (array_key_exists($group['id'], $userGroups)) {
225
+                        $groups[$key]['usercount']--;
226
+                        $userCount -= 1; // we also lower from one the total count
227
+                    }
228
+                }
229
+                $userCount += $this->userManager->countUsersOfGroups($groupsInfo->getGroups());
230
+                $disabledUsers = $this->userManager->countDisabledUsersOfGroups($groupsNames);
231
+            }
232
+
233
+            $userCount -= $disabledUsers;
234
+        }
235
+
236
+        $disabledUsersGroup = [
237
+            'id' => 'disabled',
238
+            'name' => 'Disabled users',
239
+            'usercount' => $disabledUsers
240
+        ];
241
+
242
+        /* QUOTAS PRESETS */
243
+        $quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
244
+        $defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none');
245
+
246
+        $event = new BeforeTemplateRenderedEvent();
247
+        $this->dispatcher->dispatch('OC\Settings\Users::loadAdditionalScripts', $event);
248
+        $this->dispatcher->dispatchTyped($event);
249
+
250
+        /* LANGUAGES */
251
+        $languages = $this->l10nFactory->getLanguages();
252
+
253
+        /* FINAL DATA */
254
+        $serverData = [];
255
+        // groups
256
+        $serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups);
257
+        // Various data
258
+        $serverData['isAdmin'] = $this->isAdmin;
259
+        $serverData['sortGroups'] = $sortGroupsBy;
260
+        $serverData['quotaPreset'] = $quotaPreset;
261
+        $serverData['userCount'] = $userCount;
262
+        $serverData['languages'] = $languages;
263
+        $serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en');
264
+        $serverData['forceLanguage'] = $this->config->getSystemValue('force_language', false);
265
+        // Settings
266
+        $serverData['defaultQuota'] = $defaultQuota;
267
+        $serverData['canChangePassword'] = $canChangePassword;
268
+        $serverData['newUserGenerateUserID'] = $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes';
269
+        $serverData['newUserRequireEmail'] = $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes';
270
+        $serverData['newUserSendEmail'] = $this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes';
271
+
272
+        return new TemplateResponse('settings', 'settings-vue', ['serverData' => $serverData]);
273
+    }
274
+
275
+    /**
276
+     * @param string $key
277
+     * @param string $value
278
+     *
279
+     * @return JSONResponse
280
+     */
281
+    public function setPreference(string $key, string $value): JSONResponse {
282
+        $allowed = ['newUser.sendEmail'];
283
+        if (!in_array($key, $allowed, true)) {
284
+            return new JSONResponse([], Http::STATUS_FORBIDDEN);
285
+        }
286
+
287
+        $this->config->setAppValue('core', $key, $value);
288
+
289
+        return new JSONResponse([]);
290
+    }
291
+
292
+    /**
293
+     * Parse the app value for quota_present
294
+     *
295
+     * @param string $quotaPreset
296
+     * @return array
297
+     */
298
+    protected function parseQuotaPreset(string $quotaPreset): array {
299
+        // 1 GB, 5 GB, 10 GB => [1 GB, 5 GB, 10 GB]
300
+        $presets = array_filter(array_map('trim', explode(',', $quotaPreset)));
301
+        // Drop default and none, Make array indexes numerically
302
+        return array_values(array_diff($presets, ['default', 'none']));
303
+    }
304
+
305
+    /**
306
+     * check if the admin can change the users password
307
+     *
308
+     * The admin can change the passwords if:
309
+     *
310
+     *   - no encryption module is loaded and encryption is disabled
311
+     *   - encryption module is loaded but it doesn't require per user keys
312
+     *
313
+     * The admin can not change the passwords if:
314
+     *
315
+     *   - an encryption module is loaded and it uses per-user keys
316
+     *   - encryption is enabled but no encryption modules are loaded
317
+     *
318
+     * @return bool
319
+     */
320
+    protected function canAdminChangeUserPasswords(): bool {
321
+        $isEncryptionEnabled = $this->encryptionManager->isEnabled();
322
+        try {
323
+            $noUserSpecificEncryptionKeys = !$this->encryptionManager->getEncryptionModule()->needDetailedAccessList();
324
+            $isEncryptionModuleLoaded = true;
325
+        } catch (ModuleDoesNotExistsException $e) {
326
+            $noUserSpecificEncryptionKeys = true;
327
+            $isEncryptionModuleLoaded = false;
328
+        }
329
+        $canChangePassword = ($isEncryptionModuleLoaded && $noUserSpecificEncryptionKeys)
330
+            || (!$isEncryptionModuleLoaded && !$isEncryptionEnabled);
331
+
332
+        return $canChangePassword;
333
+    }
334
+
335
+    /**
336
+     * @NoAdminRequired
337
+     * @NoSubAdminRequired
338
+     * @PasswordConfirmationRequired
339
+     *
340
+     * @param string|null $avatarScope
341
+     * @param string|null $displayname
342
+     * @param string|null $displaynameScope
343
+     * @param string|null $phone
344
+     * @param string|null $phoneScope
345
+     * @param string|null $email
346
+     * @param string|null $emailScope
347
+     * @param string|null $website
348
+     * @param string|null $websiteScope
349
+     * @param string|null $address
350
+     * @param string|null $addressScope
351
+     * @param string|null $twitter
352
+     * @param string|null $twitterScope
353
+     *
354
+     * @return DataResponse
355
+     */
356
+    public function setUserSettings(?string $avatarScope = null,
357
+                                    ?string $displayname = null,
358
+                                    ?string $displaynameScope = null,
359
+                                    ?string $phone = null,
360
+                                    ?string $phoneScope = null,
361
+                                    ?string $email = null,
362
+                                    ?string $emailScope = null,
363
+                                    ?string $website = null,
364
+                                    ?string $websiteScope = null,
365
+                                    ?string $address = null,
366
+                                    ?string $addressScope = null,
367
+                                    ?string $twitter = null,
368
+                                    ?string $twitterScope = null
369
+    ) {
370
+        $user = $this->userSession->getUser();
371
+        if (!$user instanceof IUser) {
372
+            return new DataResponse(
373
+                [
374
+                    'status' => 'error',
375
+                    'data' => [
376
+                        'message' => $this->l10n->t('Invalid user')
377
+                    ]
378
+                ],
379
+                Http::STATUS_UNAUTHORIZED
380
+            );
381
+        }
382
+
383
+        $email = strtolower($email);
384
+        if (!empty($email) && !$this->mailer->validateMailAddress($email)) {
385
+            return new DataResponse(
386
+                [
387
+                    'status' => 'error',
388
+                    'data' => [
389
+                        'message' => $this->l10n->t('Invalid mail address')
390
+                    ]
391
+                ],
392
+                Http::STATUS_UNPROCESSABLE_ENTITY
393
+            );
394
+        }
395
+
396
+        $data = $this->accountManager->getUser($user);
397
+        $beforeData = $data;
398
+        $data[IAccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope];
399
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
400
+            $data[IAccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope];
401
+            $data[IAccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope];
402
+        }
403
+        $data[IAccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope];
404
+        $data[IAccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope];
405
+        $data[IAccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope];
406
+        $data[IAccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope];
407
+
408
+        try {
409
+            $data = $this->saveUserSettings($user, $data);
410
+            if ($beforeData[IAccountManager::PROPERTY_PHONE]['value'] !== $data[IAccountManager::PROPERTY_PHONE]['value']) {
411
+                $this->knownUserService->deleteByContactUserId($user->getUID());
412
+            }
413
+            return new DataResponse(
414
+                [
415
+                    'status' => 'success',
416
+                    'data' => [
417
+                        'userId' => $user->getUID(),
418
+                        'avatarScope' => $data[IAccountManager::PROPERTY_AVATAR]['scope'],
419
+                        'displayname' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
420
+                        'displaynameScope' => $data[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
421
+                        'phone' => $data[IAccountManager::PROPERTY_PHONE]['value'],
422
+                        'phoneScope' => $data[IAccountManager::PROPERTY_PHONE]['scope'],
423
+                        'email' => $data[IAccountManager::PROPERTY_EMAIL]['value'],
424
+                        'emailScope' => $data[IAccountManager::PROPERTY_EMAIL]['scope'],
425
+                        'website' => $data[IAccountManager::PROPERTY_WEBSITE]['value'],
426
+                        'websiteScope' => $data[IAccountManager::PROPERTY_WEBSITE]['scope'],
427
+                        'address' => $data[IAccountManager::PROPERTY_ADDRESS]['value'],
428
+                        'addressScope' => $data[IAccountManager::PROPERTY_ADDRESS]['scope'],
429
+                        'twitter' => $data[IAccountManager::PROPERTY_TWITTER]['value'],
430
+                        'twitterScope' => $data[IAccountManager::PROPERTY_TWITTER]['scope'],
431
+                        'message' => $this->l10n->t('Settings saved')
432
+                    ]
433
+                ],
434
+                Http::STATUS_OK
435
+            );
436
+        } catch (ForbiddenException $e) {
437
+            return new DataResponse([
438
+                'status' => 'error',
439
+                'data' => [
440
+                    'message' => $e->getMessage()
441
+                ],
442
+            ]);
443
+        } catch (\InvalidArgumentException $e) {
444
+            return new DataResponse([
445
+                'status' => 'error',
446
+                'data' => [
447
+                    'message' => $e->getMessage()
448
+                ],
449
+            ]);
450
+        }
451
+    }
452
+    /**
453
+     * update account manager with new user data
454
+     *
455
+     * @param IUser $user
456
+     * @param array $data
457
+     * @return array
458
+     * @throws ForbiddenException
459
+     * @throws \InvalidArgumentException
460
+     */
461
+    protected function saveUserSettings(IUser $user, array $data): array {
462
+        // keep the user back-end up-to-date with the latest display name and email
463
+        // address
464
+        $oldDisplayName = $user->getDisplayName();
465
+        $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName;
466
+        if (isset($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'])
467
+            && $oldDisplayName !== $data[IAccountManager::PROPERTY_DISPLAYNAME]['value']
468
+        ) {
469
+            $result = $user->setDisplayName($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
470
+            if ($result === false) {
471
+                throw new ForbiddenException($this->l10n->t('Unable to change full name'));
472
+            }
473
+        }
474
+
475
+        $oldEmailAddress = $user->getEMailAddress();
476
+        $oldEmailAddress = is_null($oldEmailAddress) ? '' : strtolower($oldEmailAddress);
477
+        if (isset($data[IAccountManager::PROPERTY_EMAIL]['value'])
478
+            && $oldEmailAddress !== $data[IAccountManager::PROPERTY_EMAIL]['value']
479
+        ) {
480
+            // this is the only permission a backend provides and is also used
481
+            // for the permission of setting a email address
482
+            if (!$user->canChangeDisplayName()) {
483
+                throw new ForbiddenException($this->l10n->t('Unable to change email address'));
484
+            }
485
+            $user->setEMailAddress($data[IAccountManager::PROPERTY_EMAIL]['value']);
486
+        }
487
+
488
+        try {
489
+            return $this->accountManager->updateUser($user, $data, true);
490
+        } catch (\InvalidArgumentException $e) {
491
+            if ($e->getMessage() === IAccountManager::PROPERTY_PHONE) {
492
+                throw new \InvalidArgumentException($this->l10n->t('Unable to set invalid phone number'));
493
+            }
494
+            throw new \InvalidArgumentException($this->l10n->t('Some account data was invalid'));
495
+        }
496
+    }
497
+
498
+    /**
499
+     * Set the mail address of a user
500
+     *
501
+     * @NoAdminRequired
502
+     * @NoSubAdminRequired
503
+     * @PasswordConfirmationRequired
504
+     *
505
+     * @param string $account
506
+     * @param bool $onlyVerificationCode only return verification code without updating the data
507
+     * @return DataResponse
508
+     */
509
+    public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse {
510
+        $user = $this->userSession->getUser();
511
+
512
+        if ($user === null) {
513
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
514
+        }
515
+
516
+        $accountData = $this->accountManager->getUser($user);
517
+        $cloudId = $user->getCloudId();
518
+        $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId;
519
+        $signature = $this->signMessage($user, $message);
520
+
521
+        $code = $message . ' ' . $signature;
522
+        $codeMd5 = $message . ' ' . md5($signature);
523
+
524
+        switch ($account) {
525
+            case 'verify-twitter':
526
+                $accountData[IAccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
527
+                $msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):');
528
+                $code = $codeMd5;
529
+                $type = IAccountManager::PROPERTY_TWITTER;
530
+                $accountData[IAccountManager::PROPERTY_TWITTER]['signature'] = $signature;
531
+                break;
532
+            case 'verify-website':
533
+                $accountData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS;
534
+                $msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):');
535
+                $type = IAccountManager::PROPERTY_WEBSITE;
536
+                $accountData[IAccountManager::PROPERTY_WEBSITE]['signature'] = $signature;
537
+                break;
538
+            default:
539
+                return new DataResponse([], Http::STATUS_BAD_REQUEST);
540
+        }
541
+
542
+        if ($onlyVerificationCode === false) {
543
+            $accountData = $this->accountManager->updateUser($user, $accountData);
544
+            $data = $accountData[$type]['value'];
545
+
546
+            $this->jobList->add(VerifyUserData::class,
547
+                [
548
+                    'verificationCode' => $code,
549
+                    'data' => $data,
550
+                    'type' => $type,
551
+                    'uid' => $user->getUID(),
552
+                    'try' => 0,
553
+                    'lastRun' => $this->getCurrentTime()
554
+                ]
555
+            );
556
+        }
557
+
558
+        return new DataResponse(['msg' => $msg, 'code' => $code]);
559
+    }
560
+
561
+    /**
562
+     * get current timestamp
563
+     *
564
+     * @return int
565
+     */
566
+    protected function getCurrentTime(): int {
567
+        return time();
568
+    }
569
+
570
+    /**
571
+     * sign message with users private key
572
+     *
573
+     * @param IUser $user
574
+     * @param string $message
575
+     *
576
+     * @return string base64 encoded signature
577
+     */
578
+    protected function signMessage(IUser $user, string $message): string {
579
+        $privateKey = $this->keyManager->getKey($user)->getPrivate();
580
+        openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512);
581
+        return base64_encode($signature);
582
+    }
583 583
 }
Please login to merge, or discard this patch.
apps/lookup_server_connector/lib/BackgroundJobs/RetryJob.php 1 patch
Indentation   +172 added lines, -172 removed lines patch added patch discarded remove patch
@@ -44,176 +44,176 @@
 block discarded – undo
44 44
 use OCP\IUserManager;
45 45
 
46 46
 class RetryJob extends Job {
47
-	/** @var IClientService */
48
-	private $clientService;
49
-	/** @var string */
50
-	private $lookupServer;
51
-	/** @var IConfig */
52
-	private $config;
53
-	/** @var IUserManager */
54
-	private $userManager;
55
-	/** @var IAccountManager */
56
-	private $accountManager;
57
-	/** @var Signer */
58
-	private $signer;
59
-	/** @var int */
60
-	protected $retries = 0;
61
-	/** @var bool */
62
-	protected $retainJob = false;
63
-
64
-	/**
65
-	 * @param ITimeFactory $time
66
-	 * @param IClientService $clientService
67
-	 * @param IConfig $config
68
-	 * @param IUserManager $userManager
69
-	 * @param IAccountManager $accountManager
70
-	 * @param Signer $signer
71
-	 */
72
-	public function __construct(ITimeFactory $time,
73
-								IClientService $clientService,
74
-								IConfig $config,
75
-								IUserManager $userManager,
76
-								IAccountManager $accountManager,
77
-								Signer $signer) {
78
-		parent::__construct($time);
79
-		$this->clientService = $clientService;
80
-		$this->config = $config;
81
-		$this->userManager = $userManager;
82
-		$this->accountManager = $accountManager;
83
-		$this->signer = $signer;
84
-
85
-		$this->lookupServer = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
86
-		if (!empty($this->lookupServer)) {
87
-			$this->lookupServer = rtrim($this->lookupServer, '/');
88
-			$this->lookupServer .= '/users';
89
-		}
90
-	}
91
-
92
-	/**
93
-	 * run the job, then remove it from the jobList
94
-	 *
95
-	 * @param IJobList $jobList
96
-	 * @param ILogger|null $logger
97
-	 */
98
-	public function execute(IJobList $jobList, ILogger $logger = null): void {
99
-		if (!isset($this->argument['userId'])) {
100
-			// Old background job without user id, just drop it.
101
-			$jobList->remove($this, $this->argument);
102
-			return;
103
-		}
104
-
105
-		$this->retries = (int) $this->config->getUserValue($this->argument['userId'], 'lookup_server_connector', 'update_retries', 0);
106
-
107
-		if ($this->shouldRemoveBackgroundJob()) {
108
-			$jobList->remove($this, $this->argument);
109
-			return;
110
-		}
111
-
112
-		if ($this->shouldRun()) {
113
-			parent::execute($jobList, $logger);
114
-			if (!$this->retainJob) {
115
-				$jobList->remove($this, $this->argument);
116
-			}
117
-		}
118
-	}
119
-
120
-	/**
121
-	 * Check if we should kill the background job:
122
-	 *
123
-	 * - internet connection is disabled
124
-	 * - no valid lookup server URL given
125
-	 * - lookup server was disabled by the admin
126
-	 * - max retries are reached (set to 5)
127
-	 *
128
-	 * @return bool
129
-	 */
130
-	protected function shouldRemoveBackgroundJob(): bool {
131
-		return $this->config->getSystemValueBool('has_internet_connection', true) === false ||
132
-			$this->config->getSystemValueString('lookup_server', 'https://lookup.nextcloud.com') === '' ||
133
-			$this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
134
-			$this->retries >= 5;
135
-	}
136
-
137
-	protected function shouldRun(): bool {
138
-		$delay = 100 * 6 ** $this->retries;
139
-		return ($this->time->getTime() - $this->lastRun) > $delay;
140
-	}
141
-
142
-	protected function run($argument): void {
143
-		$user = $this->userManager->get($this->argument['userId']);
144
-		if (!$user instanceof IUser) {
145
-			// User does not exist anymore
146
-			return;
147
-		}
148
-
149
-		$data = $this->getUserAccountData($user);
150
-		$signedData = $this->signer->sign('lookupserver', $data, $user);
151
-		$client = $this->clientService->newClient();
152
-
153
-		try {
154
-			if (count($data) === 1) {
155
-				// No public data, just the federation Id
156
-				$client->delete($this->lookupServer,
157
-					[
158
-						'body' => json_encode($signedData),
159
-						'timeout' => 10,
160
-						'connect_timeout' => 3,
161
-					]
162
-				);
163
-			} else {
164
-				$client->post($this->lookupServer,
165
-					[
166
-						'body' => json_encode($signedData),
167
-						'timeout' => 10,
168
-						'connect_timeout' => 3,
169
-					]
170
-				);
171
-			}
172
-
173
-			// Reset retry counter
174
-			$this->config->deleteUserValue(
175
-				$user->getUID(),
176
-				'lookup_server_connector',
177
-				'update_retries'
178
-			);
179
-		} catch (\Exception $e) {
180
-			// An error occurred, retry later
181
-			$this->retainJob = true;
182
-			$this->config->setUserValue(
183
-				$user->getUID(),
184
-				'lookup_server_connector',
185
-				'update_retries',
186
-				$this->retries + 1
187
-			);
188
-		}
189
-	}
190
-
191
-	protected function getUserAccountData(IUser $user): array {
192
-		$account = $this->accountManager->getAccount($user);
193
-
194
-		$publicData = [];
195
-		foreach ($account->getProperties() as $property) {
196
-			if ($property->getScope() === IAccountManager::SCOPE_PUBLISHED) {
197
-				$publicData[$property->getName()] = $property->getValue();
198
-			}
199
-		}
200
-
201
-		$data = ['federationId' => $user->getCloudId()];
202
-		if (!empty($publicData)) {
203
-			$data['name'] = $publicData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] ?? '';
204
-			$data['email'] = $publicData[IAccountManager::PROPERTY_EMAIL]['value'] ?? '';
205
-			$data['address'] = $publicData[IAccountManager::PROPERTY_ADDRESS]['value'] ?? '';
206
-			$data['website'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['value'] ?? '';
207
-			$data['twitter'] = $publicData[IAccountManager::PROPERTY_TWITTER]['value'] ?? '';
208
-			$data['phone'] = $publicData[IAccountManager::PROPERTY_PHONE]['value'] ?? '';
209
-			$data['twitter_signature'] = $publicData[IAccountManager::PROPERTY_TWITTER]['signature'] ?? '';
210
-			$data['website_signature'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['signature'] ?? '';
211
-			$data['verificationStatus'] = [
212
-				IAccountManager::PROPERTY_WEBSITE => $publicData[IAccountManager::PROPERTY_WEBSITE]['verified'] ?? '',
213
-				IAccountManager::PROPERTY_TWITTER => $publicData[IAccountManager::PROPERTY_TWITTER]['verified'] ?? '',
214
-			];
215
-		}
216
-
217
-		return $data;
218
-	}
47
+    /** @var IClientService */
48
+    private $clientService;
49
+    /** @var string */
50
+    private $lookupServer;
51
+    /** @var IConfig */
52
+    private $config;
53
+    /** @var IUserManager */
54
+    private $userManager;
55
+    /** @var IAccountManager */
56
+    private $accountManager;
57
+    /** @var Signer */
58
+    private $signer;
59
+    /** @var int */
60
+    protected $retries = 0;
61
+    /** @var bool */
62
+    protected $retainJob = false;
63
+
64
+    /**
65
+     * @param ITimeFactory $time
66
+     * @param IClientService $clientService
67
+     * @param IConfig $config
68
+     * @param IUserManager $userManager
69
+     * @param IAccountManager $accountManager
70
+     * @param Signer $signer
71
+     */
72
+    public function __construct(ITimeFactory $time,
73
+                                IClientService $clientService,
74
+                                IConfig $config,
75
+                                IUserManager $userManager,
76
+                                IAccountManager $accountManager,
77
+                                Signer $signer) {
78
+        parent::__construct($time);
79
+        $this->clientService = $clientService;
80
+        $this->config = $config;
81
+        $this->userManager = $userManager;
82
+        $this->accountManager = $accountManager;
83
+        $this->signer = $signer;
84
+
85
+        $this->lookupServer = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
86
+        if (!empty($this->lookupServer)) {
87
+            $this->lookupServer = rtrim($this->lookupServer, '/');
88
+            $this->lookupServer .= '/users';
89
+        }
90
+    }
91
+
92
+    /**
93
+     * run the job, then remove it from the jobList
94
+     *
95
+     * @param IJobList $jobList
96
+     * @param ILogger|null $logger
97
+     */
98
+    public function execute(IJobList $jobList, ILogger $logger = null): void {
99
+        if (!isset($this->argument['userId'])) {
100
+            // Old background job without user id, just drop it.
101
+            $jobList->remove($this, $this->argument);
102
+            return;
103
+        }
104
+
105
+        $this->retries = (int) $this->config->getUserValue($this->argument['userId'], 'lookup_server_connector', 'update_retries', 0);
106
+
107
+        if ($this->shouldRemoveBackgroundJob()) {
108
+            $jobList->remove($this, $this->argument);
109
+            return;
110
+        }
111
+
112
+        if ($this->shouldRun()) {
113
+            parent::execute($jobList, $logger);
114
+            if (!$this->retainJob) {
115
+                $jobList->remove($this, $this->argument);
116
+            }
117
+        }
118
+    }
119
+
120
+    /**
121
+     * Check if we should kill the background job:
122
+     *
123
+     * - internet connection is disabled
124
+     * - no valid lookup server URL given
125
+     * - lookup server was disabled by the admin
126
+     * - max retries are reached (set to 5)
127
+     *
128
+     * @return bool
129
+     */
130
+    protected function shouldRemoveBackgroundJob(): bool {
131
+        return $this->config->getSystemValueBool('has_internet_connection', true) === false ||
132
+            $this->config->getSystemValueString('lookup_server', 'https://lookup.nextcloud.com') === '' ||
133
+            $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
134
+            $this->retries >= 5;
135
+    }
136
+
137
+    protected function shouldRun(): bool {
138
+        $delay = 100 * 6 ** $this->retries;
139
+        return ($this->time->getTime() - $this->lastRun) > $delay;
140
+    }
141
+
142
+    protected function run($argument): void {
143
+        $user = $this->userManager->get($this->argument['userId']);
144
+        if (!$user instanceof IUser) {
145
+            // User does not exist anymore
146
+            return;
147
+        }
148
+
149
+        $data = $this->getUserAccountData($user);
150
+        $signedData = $this->signer->sign('lookupserver', $data, $user);
151
+        $client = $this->clientService->newClient();
152
+
153
+        try {
154
+            if (count($data) === 1) {
155
+                // No public data, just the federation Id
156
+                $client->delete($this->lookupServer,
157
+                    [
158
+                        'body' => json_encode($signedData),
159
+                        'timeout' => 10,
160
+                        'connect_timeout' => 3,
161
+                    ]
162
+                );
163
+            } else {
164
+                $client->post($this->lookupServer,
165
+                    [
166
+                        'body' => json_encode($signedData),
167
+                        'timeout' => 10,
168
+                        'connect_timeout' => 3,
169
+                    ]
170
+                );
171
+            }
172
+
173
+            // Reset retry counter
174
+            $this->config->deleteUserValue(
175
+                $user->getUID(),
176
+                'lookup_server_connector',
177
+                'update_retries'
178
+            );
179
+        } catch (\Exception $e) {
180
+            // An error occurred, retry later
181
+            $this->retainJob = true;
182
+            $this->config->setUserValue(
183
+                $user->getUID(),
184
+                'lookup_server_connector',
185
+                'update_retries',
186
+                $this->retries + 1
187
+            );
188
+        }
189
+    }
190
+
191
+    protected function getUserAccountData(IUser $user): array {
192
+        $account = $this->accountManager->getAccount($user);
193
+
194
+        $publicData = [];
195
+        foreach ($account->getProperties() as $property) {
196
+            if ($property->getScope() === IAccountManager::SCOPE_PUBLISHED) {
197
+                $publicData[$property->getName()] = $property->getValue();
198
+            }
199
+        }
200
+
201
+        $data = ['federationId' => $user->getCloudId()];
202
+        if (!empty($publicData)) {
203
+            $data['name'] = $publicData[IAccountManager::PROPERTY_DISPLAYNAME]['value'] ?? '';
204
+            $data['email'] = $publicData[IAccountManager::PROPERTY_EMAIL]['value'] ?? '';
205
+            $data['address'] = $publicData[IAccountManager::PROPERTY_ADDRESS]['value'] ?? '';
206
+            $data['website'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['value'] ?? '';
207
+            $data['twitter'] = $publicData[IAccountManager::PROPERTY_TWITTER]['value'] ?? '';
208
+            $data['phone'] = $publicData[IAccountManager::PROPERTY_PHONE]['value'] ?? '';
209
+            $data['twitter_signature'] = $publicData[IAccountManager::PROPERTY_TWITTER]['signature'] ?? '';
210
+            $data['website_signature'] = $publicData[IAccountManager::PROPERTY_WEBSITE]['signature'] ?? '';
211
+            $data['verificationStatus'] = [
212
+                IAccountManager::PROPERTY_WEBSITE => $publicData[IAccountManager::PROPERTY_WEBSITE]['verified'] ?? '',
213
+                IAccountManager::PROPERTY_TWITTER => $publicData[IAccountManager::PROPERTY_TWITTER]['verified'] ?? '',
214
+            ];
215
+        }
216
+
217
+        return $data;
218
+    }
219 219
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 1 patch
Indentation   +660 added lines, -660 removed lines patch added patch discarded remove patch
@@ -85,666 +85,666 @@
 block discarded – undo
85 85
  */
86 86
 class ShareController extends AuthPublicShareController {
87 87
 
88
-	/** @var IConfig */
89
-	protected $config;
90
-	/** @var IUserManager */
91
-	protected $userManager;
92
-	/** @var ILogger */
93
-	protected $logger;
94
-	/** @var \OCP\Activity\IManager */
95
-	protected $activityManager;
96
-	/** @var IPreview */
97
-	protected $previewManager;
98
-	/** @var IRootFolder */
99
-	protected $rootFolder;
100
-	/** @var FederatedShareProvider */
101
-	protected $federatedShareProvider;
102
-	/** @var IAccountManager */
103
-	protected $accountManager;
104
-	/** @var IEventDispatcher */
105
-	protected $eventDispatcher;
106
-	/** @var IL10N */
107
-	protected $l10n;
108
-	/** @var Defaults */
109
-	protected $defaults;
110
-	/** @var ShareManager */
111
-	protected $shareManager;
112
-
113
-	/** @var Share\IShare */
114
-	protected $share;
115
-
116
-	/**
117
-	 * @param string $appName
118
-	 * @param IRequest $request
119
-	 * @param IConfig $config
120
-	 * @param IURLGenerator $urlGenerator
121
-	 * @param IUserManager $userManager
122
-	 * @param ILogger $logger
123
-	 * @param \OCP\Activity\IManager $activityManager
124
-	 * @param \OCP\Share\IManager $shareManager
125
-	 * @param ISession $session
126
-	 * @param IPreview $previewManager
127
-	 * @param IRootFolder $rootFolder
128
-	 * @param FederatedShareProvider $federatedShareProvider
129
-	 * @param IAccountManager $accountManager
130
-	 * @param IEventDispatcher $eventDispatcher
131
-	 * @param IL10N $l10n
132
-	 * @param Defaults $defaults
133
-	 */
134
-	public function __construct(string $appName,
135
-								IRequest $request,
136
-								IConfig $config,
137
-								IURLGenerator $urlGenerator,
138
-								IUserManager $userManager,
139
-								ILogger $logger,
140
-								\OCP\Activity\IManager $activityManager,
141
-								ShareManager $shareManager,
142
-								ISession $session,
143
-								IPreview $previewManager,
144
-								IRootFolder $rootFolder,
145
-								FederatedShareProvider $federatedShareProvider,
146
-								IAccountManager $accountManager,
147
-								IEventDispatcher $eventDispatcher,
148
-								IL10N $l10n,
149
-								Defaults $defaults) {
150
-		parent::__construct($appName, $request, $session, $urlGenerator);
151
-
152
-		$this->config = $config;
153
-		$this->userManager = $userManager;
154
-		$this->logger = $logger;
155
-		$this->activityManager = $activityManager;
156
-		$this->previewManager = $previewManager;
157
-		$this->rootFolder = $rootFolder;
158
-		$this->federatedShareProvider = $federatedShareProvider;
159
-		$this->accountManager = $accountManager;
160
-		$this->eventDispatcher = $eventDispatcher;
161
-		$this->l10n = $l10n;
162
-		$this->defaults = $defaults;
163
-		$this->shareManager = $shareManager;
164
-	}
165
-
166
-	/**
167
-	 * @PublicPage
168
-	 * @NoCSRFRequired
169
-	 *
170
-	 * Show the authentication page
171
-	 * The form has to submit to the authenticate method route
172
-	 */
173
-	public function showAuthenticate(): TemplateResponse {
174
-		$templateParameters = ['share' => $this->share];
175
-
176
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
177
-
178
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
179
-		if ($this->share->getSendPasswordByTalk()) {
180
-			$csp = new ContentSecurityPolicy();
181
-			$csp->addAllowedConnectDomain('*');
182
-			$csp->addAllowedMediaDomain('blob:');
183
-			$response->setContentSecurityPolicy($csp);
184
-		}
185
-
186
-		return $response;
187
-	}
188
-
189
-	/**
190
-	 * The template to show when authentication failed
191
-	 */
192
-	protected function showAuthFailed(): TemplateResponse {
193
-		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
194
-
195
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
196
-
197
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
-		if ($this->share->getSendPasswordByTalk()) {
199
-			$csp = new ContentSecurityPolicy();
200
-			$csp->addAllowedConnectDomain('*');
201
-			$csp->addAllowedMediaDomain('blob:');
202
-			$response->setContentSecurityPolicy($csp);
203
-		}
204
-
205
-		return $response;
206
-	}
207
-
208
-	protected function verifyPassword(string $password): bool {
209
-		return $this->shareManager->checkPassword($this->share, $password);
210
-	}
211
-
212
-	protected function getPasswordHash(): string {
213
-		return $this->share->getPassword();
214
-	}
215
-
216
-	public function isValidToken(): bool {
217
-		try {
218
-			$this->share = $this->shareManager->getShareByToken($this->getToken());
219
-		} catch (ShareNotFound $e) {
220
-			return false;
221
-		}
222
-
223
-		return true;
224
-	}
225
-
226
-	protected function isPasswordProtected(): bool {
227
-		return $this->share->getPassword() !== null;
228
-	}
229
-
230
-	protected function authSucceeded() {
231
-		// For share this was always set so it is still used in other apps
232
-		$this->session->set('public_link_authenticated', (string)$this->share->getId());
233
-	}
234
-
235
-	protected function authFailed() {
236
-		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
-	}
238
-
239
-	/**
240
-	 * throws hooks when a share is attempted to be accessed
241
-	 *
242
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
243
-	 * otherwise token
244
-	 * @param int $errorCode
245
-	 * @param string $errorMessage
246
-	 * @throws \OC\HintException
247
-	 * @throws \OC\ServerNotAvailableException
248
-	 */
249
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
-		$itemType = $itemSource = $uidOwner = '';
251
-		$token = $share;
252
-		$exception = null;
253
-		if ($share instanceof \OCP\Share\IShare) {
254
-			try {
255
-				$token = $share->getToken();
256
-				$uidOwner = $share->getSharedBy();
257
-				$itemType = $share->getNodeType();
258
-				$itemSource = $share->getNodeId();
259
-			} catch (\Exception $e) {
260
-				// we log what we know and pass on the exception afterwards
261
-				$exception = $e;
262
-			}
263
-		}
264
-		\OC_Hook::emit(Share::class, 'share_link_access', [
265
-			'itemType' => $itemType,
266
-			'itemSource' => $itemSource,
267
-			'uidOwner' => $uidOwner,
268
-			'token' => $token,
269
-			'errorCode' => $errorCode,
270
-			'errorMessage' => $errorMessage,
271
-		]);
272
-		if (!is_null($exception)) {
273
-			throw $exception;
274
-		}
275
-	}
276
-
277
-	/**
278
-	 * Validate the permissions of the share
279
-	 *
280
-	 * @param Share\IShare $share
281
-	 * @return bool
282
-	 */
283
-	private function validateShare(\OCP\Share\IShare $share) {
284
-		// If the owner is disabled no access to the linke is granted
285
-		$owner = $this->userManager->get($share->getShareOwner());
286
-		if ($owner === null || !$owner->isEnabled()) {
287
-			return false;
288
-		}
289
-
290
-		// If the initiator of the share is disabled no access is granted
291
-		$initiator = $this->userManager->get($share->getSharedBy());
292
-		if ($initiator === null || !$initiator->isEnabled()) {
293
-			return false;
294
-		}
295
-
296
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
-	}
298
-
299
-	/**
300
-	 * @PublicPage
301
-	 * @NoCSRFRequired
302
-	 *
303
-	 *
304
-	 * @param string $path
305
-	 * @return TemplateResponse
306
-	 * @throws NotFoundException
307
-	 * @throws \Exception
308
-	 */
309
-	public function showShare($path = ''): TemplateResponse {
310
-		\OC_User::setIncognitoMode(true);
311
-
312
-		// Check whether share exists
313
-		try {
314
-			$share = $this->shareManager->getShareByToken($this->getToken());
315
-		} catch (ShareNotFound $e) {
316
-			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
-			throw new NotFoundException();
318
-		}
319
-
320
-		if (!$this->validateShare($share)) {
321
-			throw new NotFoundException();
322
-		}
323
-
324
-		$shareNode = $share->getNode();
325
-
326
-		// We can't get the path of a file share
327
-		try {
328
-			if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
-				$this->emitAccessShareHook($share, 404, 'Share not found');
330
-				throw new NotFoundException();
331
-			}
332
-		} catch (\Exception $e) {
333
-			$this->emitAccessShareHook($share, 404, 'Share not found');
334
-			throw $e;
335
-		}
336
-
337
-		$shareTmpl = [];
338
-		$shareTmpl['owner'] = '';
339
-		$shareTmpl['shareOwner'] = '';
340
-
341
-		$owner = $this->userManager->get($share->getShareOwner());
342
-		if ($owner instanceof IUser) {
343
-			$ownerAccount = $this->accountManager->getAccount($owner);
344
-
345
-			$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
-			if ($ownerName->getScope() === IAccountManager::SCOPE_PUBLISHED) {
347
-				$shareTmpl['owner'] = $owner->getUID();
348
-				$shareTmpl['shareOwner'] = $owner->getDisplayName();
349
-			}
350
-		}
351
-
352
-		$shareTmpl['filename'] = $shareNode->getName();
353
-		$shareTmpl['directory_path'] = $share->getTarget();
354
-		$shareTmpl['note'] = $share->getNote();
355
-		$shareTmpl['mimetype'] = $shareNode->getMimetype();
356
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
-		$shareTmpl['dirToken'] = $this->getToken();
358
-		$shareTmpl['sharingToken'] = $this->getToken();
359
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
-		$shareTmpl['dir'] = '';
362
-		$shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
-		$shareTmpl['hideDownload'] = $share->getHideDownload();
365
-
366
-		$hideFileList = false;
367
-
368
-		if ($shareNode instanceof \OCP\Files\Folder) {
369
-			$shareIsFolder = true;
370
-
371
-			try {
372
-				$folderNode = $shareNode->get($path);
373
-			} catch (\OCP\Files\NotFoundException $e) {
374
-				$this->emitAccessShareHook($share, 404, 'Share not found');
375
-				throw new NotFoundException();
376
-			}
377
-
378
-			$shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
379
-
380
-			/*
88
+    /** @var IConfig */
89
+    protected $config;
90
+    /** @var IUserManager */
91
+    protected $userManager;
92
+    /** @var ILogger */
93
+    protected $logger;
94
+    /** @var \OCP\Activity\IManager */
95
+    protected $activityManager;
96
+    /** @var IPreview */
97
+    protected $previewManager;
98
+    /** @var IRootFolder */
99
+    protected $rootFolder;
100
+    /** @var FederatedShareProvider */
101
+    protected $federatedShareProvider;
102
+    /** @var IAccountManager */
103
+    protected $accountManager;
104
+    /** @var IEventDispatcher */
105
+    protected $eventDispatcher;
106
+    /** @var IL10N */
107
+    protected $l10n;
108
+    /** @var Defaults */
109
+    protected $defaults;
110
+    /** @var ShareManager */
111
+    protected $shareManager;
112
+
113
+    /** @var Share\IShare */
114
+    protected $share;
115
+
116
+    /**
117
+     * @param string $appName
118
+     * @param IRequest $request
119
+     * @param IConfig $config
120
+     * @param IURLGenerator $urlGenerator
121
+     * @param IUserManager $userManager
122
+     * @param ILogger $logger
123
+     * @param \OCP\Activity\IManager $activityManager
124
+     * @param \OCP\Share\IManager $shareManager
125
+     * @param ISession $session
126
+     * @param IPreview $previewManager
127
+     * @param IRootFolder $rootFolder
128
+     * @param FederatedShareProvider $federatedShareProvider
129
+     * @param IAccountManager $accountManager
130
+     * @param IEventDispatcher $eventDispatcher
131
+     * @param IL10N $l10n
132
+     * @param Defaults $defaults
133
+     */
134
+    public function __construct(string $appName,
135
+                                IRequest $request,
136
+                                IConfig $config,
137
+                                IURLGenerator $urlGenerator,
138
+                                IUserManager $userManager,
139
+                                ILogger $logger,
140
+                                \OCP\Activity\IManager $activityManager,
141
+                                ShareManager $shareManager,
142
+                                ISession $session,
143
+                                IPreview $previewManager,
144
+                                IRootFolder $rootFolder,
145
+                                FederatedShareProvider $federatedShareProvider,
146
+                                IAccountManager $accountManager,
147
+                                IEventDispatcher $eventDispatcher,
148
+                                IL10N $l10n,
149
+                                Defaults $defaults) {
150
+        parent::__construct($appName, $request, $session, $urlGenerator);
151
+
152
+        $this->config = $config;
153
+        $this->userManager = $userManager;
154
+        $this->logger = $logger;
155
+        $this->activityManager = $activityManager;
156
+        $this->previewManager = $previewManager;
157
+        $this->rootFolder = $rootFolder;
158
+        $this->federatedShareProvider = $federatedShareProvider;
159
+        $this->accountManager = $accountManager;
160
+        $this->eventDispatcher = $eventDispatcher;
161
+        $this->l10n = $l10n;
162
+        $this->defaults = $defaults;
163
+        $this->shareManager = $shareManager;
164
+    }
165
+
166
+    /**
167
+     * @PublicPage
168
+     * @NoCSRFRequired
169
+     *
170
+     * Show the authentication page
171
+     * The form has to submit to the authenticate method route
172
+     */
173
+    public function showAuthenticate(): TemplateResponse {
174
+        $templateParameters = ['share' => $this->share];
175
+
176
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
177
+
178
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
179
+        if ($this->share->getSendPasswordByTalk()) {
180
+            $csp = new ContentSecurityPolicy();
181
+            $csp->addAllowedConnectDomain('*');
182
+            $csp->addAllowedMediaDomain('blob:');
183
+            $response->setContentSecurityPolicy($csp);
184
+        }
185
+
186
+        return $response;
187
+    }
188
+
189
+    /**
190
+     * The template to show when authentication failed
191
+     */
192
+    protected function showAuthFailed(): TemplateResponse {
193
+        $templateParameters = ['share' => $this->share, 'wrongpw' => true];
194
+
195
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
196
+
197
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
+        if ($this->share->getSendPasswordByTalk()) {
199
+            $csp = new ContentSecurityPolicy();
200
+            $csp->addAllowedConnectDomain('*');
201
+            $csp->addAllowedMediaDomain('blob:');
202
+            $response->setContentSecurityPolicy($csp);
203
+        }
204
+
205
+        return $response;
206
+    }
207
+
208
+    protected function verifyPassword(string $password): bool {
209
+        return $this->shareManager->checkPassword($this->share, $password);
210
+    }
211
+
212
+    protected function getPasswordHash(): string {
213
+        return $this->share->getPassword();
214
+    }
215
+
216
+    public function isValidToken(): bool {
217
+        try {
218
+            $this->share = $this->shareManager->getShareByToken($this->getToken());
219
+        } catch (ShareNotFound $e) {
220
+            return false;
221
+        }
222
+
223
+        return true;
224
+    }
225
+
226
+    protected function isPasswordProtected(): bool {
227
+        return $this->share->getPassword() !== null;
228
+    }
229
+
230
+    protected function authSucceeded() {
231
+        // For share this was always set so it is still used in other apps
232
+        $this->session->set('public_link_authenticated', (string)$this->share->getId());
233
+    }
234
+
235
+    protected function authFailed() {
236
+        $this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
+    }
238
+
239
+    /**
240
+     * throws hooks when a share is attempted to be accessed
241
+     *
242
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
243
+     * otherwise token
244
+     * @param int $errorCode
245
+     * @param string $errorMessage
246
+     * @throws \OC\HintException
247
+     * @throws \OC\ServerNotAvailableException
248
+     */
249
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
+        $itemType = $itemSource = $uidOwner = '';
251
+        $token = $share;
252
+        $exception = null;
253
+        if ($share instanceof \OCP\Share\IShare) {
254
+            try {
255
+                $token = $share->getToken();
256
+                $uidOwner = $share->getSharedBy();
257
+                $itemType = $share->getNodeType();
258
+                $itemSource = $share->getNodeId();
259
+            } catch (\Exception $e) {
260
+                // we log what we know and pass on the exception afterwards
261
+                $exception = $e;
262
+            }
263
+        }
264
+        \OC_Hook::emit(Share::class, 'share_link_access', [
265
+            'itemType' => $itemType,
266
+            'itemSource' => $itemSource,
267
+            'uidOwner' => $uidOwner,
268
+            'token' => $token,
269
+            'errorCode' => $errorCode,
270
+            'errorMessage' => $errorMessage,
271
+        ]);
272
+        if (!is_null($exception)) {
273
+            throw $exception;
274
+        }
275
+    }
276
+
277
+    /**
278
+     * Validate the permissions of the share
279
+     *
280
+     * @param Share\IShare $share
281
+     * @return bool
282
+     */
283
+    private function validateShare(\OCP\Share\IShare $share) {
284
+        // If the owner is disabled no access to the linke is granted
285
+        $owner = $this->userManager->get($share->getShareOwner());
286
+        if ($owner === null || !$owner->isEnabled()) {
287
+            return false;
288
+        }
289
+
290
+        // If the initiator of the share is disabled no access is granted
291
+        $initiator = $this->userManager->get($share->getSharedBy());
292
+        if ($initiator === null || !$initiator->isEnabled()) {
293
+            return false;
294
+        }
295
+
296
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
+    }
298
+
299
+    /**
300
+     * @PublicPage
301
+     * @NoCSRFRequired
302
+     *
303
+     *
304
+     * @param string $path
305
+     * @return TemplateResponse
306
+     * @throws NotFoundException
307
+     * @throws \Exception
308
+     */
309
+    public function showShare($path = ''): TemplateResponse {
310
+        \OC_User::setIncognitoMode(true);
311
+
312
+        // Check whether share exists
313
+        try {
314
+            $share = $this->shareManager->getShareByToken($this->getToken());
315
+        } catch (ShareNotFound $e) {
316
+            $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
+            throw new NotFoundException();
318
+        }
319
+
320
+        if (!$this->validateShare($share)) {
321
+            throw new NotFoundException();
322
+        }
323
+
324
+        $shareNode = $share->getNode();
325
+
326
+        // We can't get the path of a file share
327
+        try {
328
+            if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
+                $this->emitAccessShareHook($share, 404, 'Share not found');
330
+                throw new NotFoundException();
331
+            }
332
+        } catch (\Exception $e) {
333
+            $this->emitAccessShareHook($share, 404, 'Share not found');
334
+            throw $e;
335
+        }
336
+
337
+        $shareTmpl = [];
338
+        $shareTmpl['owner'] = '';
339
+        $shareTmpl['shareOwner'] = '';
340
+
341
+        $owner = $this->userManager->get($share->getShareOwner());
342
+        if ($owner instanceof IUser) {
343
+            $ownerAccount = $this->accountManager->getAccount($owner);
344
+
345
+            $ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
+            if ($ownerName->getScope() === IAccountManager::SCOPE_PUBLISHED) {
347
+                $shareTmpl['owner'] = $owner->getUID();
348
+                $shareTmpl['shareOwner'] = $owner->getDisplayName();
349
+            }
350
+        }
351
+
352
+        $shareTmpl['filename'] = $shareNode->getName();
353
+        $shareTmpl['directory_path'] = $share->getTarget();
354
+        $shareTmpl['note'] = $share->getNote();
355
+        $shareTmpl['mimetype'] = $shareNode->getMimetype();
356
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
+        $shareTmpl['dirToken'] = $this->getToken();
358
+        $shareTmpl['sharingToken'] = $this->getToken();
359
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
+        $shareTmpl['dir'] = '';
362
+        $shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
+        $shareTmpl['hideDownload'] = $share->getHideDownload();
365
+
366
+        $hideFileList = false;
367
+
368
+        if ($shareNode instanceof \OCP\Files\Folder) {
369
+            $shareIsFolder = true;
370
+
371
+            try {
372
+                $folderNode = $shareNode->get($path);
373
+            } catch (\OCP\Files\NotFoundException $e) {
374
+                $this->emitAccessShareHook($share, 404, 'Share not found');
375
+                throw new NotFoundException();
376
+            }
377
+
378
+            $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
379
+
380
+            /*
381 381
 			 * The OC_Util methods require a view. This just uses the node API
382 382
 			 */
383
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
384
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
385
-				$freeSpace = max($freeSpace, 0);
386
-			} else {
387
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
388
-			}
389
-
390
-			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
391
-			$maxUploadFilesize = $freeSpace;
392
-
393
-			$folder = new Template('files', 'list', '');
394
-
395
-			$folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
396
-			$folder->assign('dirToken', $this->getToken());
397
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
398
-			$folder->assign('isPublic', true);
399
-			$folder->assign('hideFileList', $hideFileList);
400
-			$folder->assign('publicUploadEnabled', 'no');
401
-			// default to list view
402
-			$folder->assign('showgridview', false);
403
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
404
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
405
-			$folder->assign('freeSpace', $freeSpace);
406
-			$folder->assign('usedSpacePercent', 0);
407
-			$folder->assign('trash', false);
408
-			$shareTmpl['folder'] = $folder->fetchPage();
409
-		} else {
410
-			$shareIsFolder = false;
411
-		}
412
-
413
-		// default to list view
414
-		$shareTmpl['showgridview'] = false;
415
-
416
-		$shareTmpl['hideFileList'] = $hideFileList;
417
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
418
-			'token' => $this->getToken(),
419
-			'filename' => $shareIsFolder ? null : $shareNode->getName()
420
-		]);
421
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
422
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
423
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
424
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
425
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
426
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
427
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
428
-
429
-		if ($shareTmpl['previewSupported']) {
430
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
431
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
432
-			$ogPreview = $shareTmpl['previewImage'];
433
-
434
-			// We just have direct previews for image files
435
-			if ($shareNode->getMimePart() === 'image') {
436
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
437
-
438
-				$ogPreview = $shareTmpl['previewURL'];
439
-
440
-				//Whatapp is kind of picky about their size requirements
441
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
442
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
443
-						'token' => $this->getToken(),
444
-						'x' => 256,
445
-						'y' => 256,
446
-						'a' => true,
447
-					]);
448
-				}
449
-			}
450
-		} else {
451
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
452
-			$ogPreview = $shareTmpl['previewImage'];
453
-		}
454
-
455
-		// Load files we need
456
-		\OCP\Util::addScript('files', 'semaphore');
457
-		\OCP\Util::addScript('files', 'file-upload');
458
-		\OCP\Util::addStyle('files_sharing', 'publicView');
459
-		\OCP\Util::addScript('files_sharing', 'public');
460
-		\OCP\Util::addScript('files_sharing', 'templates');
461
-		\OCP\Util::addScript('files', 'fileactions');
462
-		\OCP\Util::addScript('files', 'fileactionsmenu');
463
-		\OCP\Util::addScript('files', 'jquery.fileupload');
464
-		\OCP\Util::addScript('files_sharing', 'files_drop');
465
-
466
-		if (isset($shareTmpl['folder'])) {
467
-			// JS required for folders
468
-			\OCP\Util::addStyle('files', 'merged');
469
-			\OCP\Util::addScript('files', 'filesummary');
470
-			\OCP\Util::addScript('files', 'templates');
471
-			\OCP\Util::addScript('files', 'breadcrumb');
472
-			\OCP\Util::addScript('files', 'fileinfomodel');
473
-			\OCP\Util::addScript('files', 'newfilemenu');
474
-			\OCP\Util::addScript('files', 'files');
475
-			\OCP\Util::addScript('files', 'filemultiselectmenu');
476
-			\OCP\Util::addScript('files', 'filelist');
477
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
478
-			\OCP\Util::addScript('files', 'operationprogressbar');
479
-
480
-			// Load Viewer scripts
481
-			if (class_exists(LoadViewer::class)) {
482
-				$this->eventDispatcher->dispatchTyped(new LoadViewer());
483
-			}
484
-		}
485
-
486
-		// OpenGraph Support: http://ogp.me/
487
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
488
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
489
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
490
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
491
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
492
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
493
-
494
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
495
-
496
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
497
-		$csp->addAllowedFrameDomain('\'self\'');
498
-
499
-		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
500
-		$response->setHeaderTitle($shareTmpl['filename']);
501
-		if ($shareTmpl['shareOwner'] !== '') {
502
-			$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
503
-		}
504
-
505
-		$isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
506
-
507
-		if ($isNoneFileDropFolder && !$share->getHideDownload()) {
508
-			\OCP\Util::addScript('files_sharing', 'public_note');
509
-
510
-			$downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
-			$downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
512
-			$download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
-			$downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
514
-			$directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
515
-			$externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
516
-
517
-			$responseComposer = [];
518
-
519
-			if ($shareIsFolder) {
520
-				$responseComposer[] = $downloadAllWhite;
521
-				$responseComposer[] = $downloadAll;
522
-			} else {
523
-				$responseComposer[] = $downloadWhite;
524
-				$responseComposer[] = $download;
525
-			}
526
-			$responseComposer[] = $directLink;
527
-			if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
528
-				$responseComposer[] = $externalShare;
529
-			}
530
-
531
-			$response->setHeaderActions($responseComposer);
532
-		}
533
-
534
-		$response->setContentSecurityPolicy($csp);
535
-
536
-		$this->emitAccessShareHook($share);
537
-
538
-		return $response;
539
-	}
540
-
541
-	/**
542
-	 * @PublicPage
543
-	 * @NoCSRFRequired
544
-	 * @NoSameSiteCookieRequired
545
-	 *
546
-	 * @param string $token
547
-	 * @param string $files
548
-	 * @param string $path
549
-	 * @param string $downloadStartSecret
550
-	 * @return void|\OCP\AppFramework\Http\Response
551
-	 * @throws NotFoundException
552
-	 */
553
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
554
-		\OC_User::setIncognitoMode(true);
555
-
556
-		$share = $this->shareManager->getShareByToken($token);
557
-
558
-		if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
559
-			return new \OCP\AppFramework\Http\DataResponse('Share has no read permission');
560
-		}
561
-
562
-		$files_list = null;
563
-		if (!is_null($files)) { // download selected files
564
-			$files_list = json_decode($files);
565
-			// in case we get only a single file
566
-			if ($files_list === null) {
567
-				$files_list = [$files];
568
-			}
569
-			// Just in case $files is a single int like '1234'
570
-			if (!is_array($files_list)) {
571
-				$files_list = [$files_list];
572
-			}
573
-		}
574
-
575
-		if (!$this->validateShare($share)) {
576
-			throw new NotFoundException();
577
-		}
578
-
579
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
580
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
581
-
582
-
583
-		// Single file share
584
-		if ($share->getNode() instanceof \OCP\Files\File) {
585
-			// Single file download
586
-			$this->singleFileDownloaded($share, $share->getNode());
587
-		}
588
-		// Directory share
589
-		else {
590
-			/** @var \OCP\Files\Folder $node */
591
-			$node = $share->getNode();
592
-
593
-			// Try to get the path
594
-			if ($path !== '') {
595
-				try {
596
-					$node = $node->get($path);
597
-				} catch (NotFoundException $e) {
598
-					$this->emitAccessShareHook($share, 404, 'Share not found');
599
-					return new NotFoundResponse();
600
-				}
601
-			}
602
-
603
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
604
-
605
-			if ($node instanceof \OCP\Files\File) {
606
-				// Single file download
607
-				$this->singleFileDownloaded($share, $share->getNode());
608
-			} else {
609
-				try {
610
-					if (!empty($files_list)) {
611
-						$this->fileListDownloaded($share, $files_list, $node);
612
-					} else {
613
-						// The folder is downloaded
614
-						$this->singleFileDownloaded($share, $share->getNode());
615
-					}
616
-				} catch (NotFoundException $e) {
617
-					return new NotFoundResponse();
618
-				}
619
-			}
620
-		}
621
-
622
-		/* FIXME: We should do this all nicely in OCP */
623
-		OC_Util::tearDownFS();
624
-		OC_Util::setupFS($share->getShareOwner());
625
-
626
-		/**
627
-		 * this sets a cookie to be able to recognize the start of the download
628
-		 * the content must not be longer than 32 characters and must only contain
629
-		 * alphanumeric characters
630
-		 */
631
-		if (!empty($downloadStartSecret)
632
-			&& !isset($downloadStartSecret[32])
633
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
634
-
635
-			// FIXME: set on the response once we use an actual app framework response
636
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
637
-		}
638
-
639
-		$this->emitAccessShareHook($share);
640
-
641
-		$server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
642
-
643
-		/**
644
-		 * Http range requests support
645
-		 */
646
-		if (isset($_SERVER['HTTP_RANGE'])) {
647
-			$server_params['range'] = $this->request->getHeader('Range');
648
-		}
649
-
650
-		// download selected files
651
-		if (!is_null($files) && $files !== '') {
652
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
653
-			// after dispatching the request which results in a "Cannot modify header information" notice.
654
-			OC_Files::get($originalSharePath, $files_list, $server_params);
655
-			exit();
656
-		} else {
657
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
658
-			// after dispatching the request which results in a "Cannot modify header information" notice.
659
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
660
-			exit();
661
-		}
662
-	}
663
-
664
-	/**
665
-	 * create activity for every downloaded file
666
-	 *
667
-	 * @param Share\IShare $share
668
-	 * @param array $files_list
669
-	 * @param \OCP\Files\Folder $node
670
-	 * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
671
-	 */
672
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
673
-		if ($share->getHideDownload() && count($files_list) > 1) {
674
-			throw new NotFoundException('Downloading more than 1 file');
675
-		}
676
-
677
-		foreach ($files_list as $file) {
678
-			$subNode = $node->get($file);
679
-			$this->singleFileDownloaded($share, $subNode);
680
-		}
681
-	}
682
-
683
-	/**
684
-	 * create activity if a single file was downloaded from a link share
685
-	 *
686
-	 * @param Share\IShare $share
687
-	 * @throws NotFoundException when trying to download a folder of a "hide download" share
688
-	 */
689
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
690
-		if ($share->getHideDownload() && $node instanceof Folder) {
691
-			throw new NotFoundException('Downloading a folder');
692
-		}
693
-
694
-		$fileId = $node->getId();
695
-
696
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
697
-		$userNodeList = $userFolder->getById($fileId);
698
-		$userNode = $userNodeList[0];
699
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
700
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
701
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
702
-
703
-		$parameters = [$userPath];
704
-
705
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
706
-			if ($node instanceof \OCP\Files\File) {
707
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
708
-			} else {
709
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
710
-			}
711
-			$parameters[] = $share->getSharedWith();
712
-		} else {
713
-			if ($node instanceof \OCP\Files\File) {
714
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
715
-			} else {
716
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
717
-			}
718
-		}
719
-
720
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
721
-
722
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
723
-			$parameters[0] = $ownerPath;
724
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
725
-		}
726
-	}
727
-
728
-	/**
729
-	 * publish activity
730
-	 *
731
-	 * @param string $subject
732
-	 * @param array $parameters
733
-	 * @param string $affectedUser
734
-	 * @param int $fileId
735
-	 * @param string $filePath
736
-	 */
737
-	protected function publishActivity($subject,
738
-										array $parameters,
739
-										$affectedUser,
740
-										$fileId,
741
-										$filePath) {
742
-		$event = $this->activityManager->generateEvent();
743
-		$event->setApp('files_sharing')
744
-			->setType('public_links')
745
-			->setSubject($subject, $parameters)
746
-			->setAffectedUser($affectedUser)
747
-			->setObject('files', $fileId, $filePath);
748
-		$this->activityManager->publish($event);
749
-	}
383
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
384
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
385
+                $freeSpace = max($freeSpace, 0);
386
+            } else {
387
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
388
+            }
389
+
390
+            $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
391
+            $maxUploadFilesize = $freeSpace;
392
+
393
+            $folder = new Template('files', 'list', '');
394
+
395
+            $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
396
+            $folder->assign('dirToken', $this->getToken());
397
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
398
+            $folder->assign('isPublic', true);
399
+            $folder->assign('hideFileList', $hideFileList);
400
+            $folder->assign('publicUploadEnabled', 'no');
401
+            // default to list view
402
+            $folder->assign('showgridview', false);
403
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
404
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
405
+            $folder->assign('freeSpace', $freeSpace);
406
+            $folder->assign('usedSpacePercent', 0);
407
+            $folder->assign('trash', false);
408
+            $shareTmpl['folder'] = $folder->fetchPage();
409
+        } else {
410
+            $shareIsFolder = false;
411
+        }
412
+
413
+        // default to list view
414
+        $shareTmpl['showgridview'] = false;
415
+
416
+        $shareTmpl['hideFileList'] = $hideFileList;
417
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
418
+            'token' => $this->getToken(),
419
+            'filename' => $shareIsFolder ? null : $shareNode->getName()
420
+        ]);
421
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
422
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
423
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
424
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
425
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
426
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
427
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
428
+
429
+        if ($shareTmpl['previewSupported']) {
430
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
431
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
432
+            $ogPreview = $shareTmpl['previewImage'];
433
+
434
+            // We just have direct previews for image files
435
+            if ($shareNode->getMimePart() === 'image') {
436
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
437
+
438
+                $ogPreview = $shareTmpl['previewURL'];
439
+
440
+                //Whatapp is kind of picky about their size requirements
441
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
442
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
443
+                        'token' => $this->getToken(),
444
+                        'x' => 256,
445
+                        'y' => 256,
446
+                        'a' => true,
447
+                    ]);
448
+                }
449
+            }
450
+        } else {
451
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
452
+            $ogPreview = $shareTmpl['previewImage'];
453
+        }
454
+
455
+        // Load files we need
456
+        \OCP\Util::addScript('files', 'semaphore');
457
+        \OCP\Util::addScript('files', 'file-upload');
458
+        \OCP\Util::addStyle('files_sharing', 'publicView');
459
+        \OCP\Util::addScript('files_sharing', 'public');
460
+        \OCP\Util::addScript('files_sharing', 'templates');
461
+        \OCP\Util::addScript('files', 'fileactions');
462
+        \OCP\Util::addScript('files', 'fileactionsmenu');
463
+        \OCP\Util::addScript('files', 'jquery.fileupload');
464
+        \OCP\Util::addScript('files_sharing', 'files_drop');
465
+
466
+        if (isset($shareTmpl['folder'])) {
467
+            // JS required for folders
468
+            \OCP\Util::addStyle('files', 'merged');
469
+            \OCP\Util::addScript('files', 'filesummary');
470
+            \OCP\Util::addScript('files', 'templates');
471
+            \OCP\Util::addScript('files', 'breadcrumb');
472
+            \OCP\Util::addScript('files', 'fileinfomodel');
473
+            \OCP\Util::addScript('files', 'newfilemenu');
474
+            \OCP\Util::addScript('files', 'files');
475
+            \OCP\Util::addScript('files', 'filemultiselectmenu');
476
+            \OCP\Util::addScript('files', 'filelist');
477
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
478
+            \OCP\Util::addScript('files', 'operationprogressbar');
479
+
480
+            // Load Viewer scripts
481
+            if (class_exists(LoadViewer::class)) {
482
+                $this->eventDispatcher->dispatchTyped(new LoadViewer());
483
+            }
484
+        }
485
+
486
+        // OpenGraph Support: http://ogp.me/
487
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
488
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
489
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
490
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
491
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
492
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
493
+
494
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
495
+
496
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
497
+        $csp->addAllowedFrameDomain('\'self\'');
498
+
499
+        $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
500
+        $response->setHeaderTitle($shareTmpl['filename']);
501
+        if ($shareTmpl['shareOwner'] !== '') {
502
+            $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
503
+        }
504
+
505
+        $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
506
+
507
+        if ($isNoneFileDropFolder && !$share->getHideDownload()) {
508
+            \OCP\Util::addScript('files_sharing', 'public_note');
509
+
510
+            $downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
+            $downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
512
+            $download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
+            $downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
514
+            $directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
515
+            $externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
516
+
517
+            $responseComposer = [];
518
+
519
+            if ($shareIsFolder) {
520
+                $responseComposer[] = $downloadAllWhite;
521
+                $responseComposer[] = $downloadAll;
522
+            } else {
523
+                $responseComposer[] = $downloadWhite;
524
+                $responseComposer[] = $download;
525
+            }
526
+            $responseComposer[] = $directLink;
527
+            if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
528
+                $responseComposer[] = $externalShare;
529
+            }
530
+
531
+            $response->setHeaderActions($responseComposer);
532
+        }
533
+
534
+        $response->setContentSecurityPolicy($csp);
535
+
536
+        $this->emitAccessShareHook($share);
537
+
538
+        return $response;
539
+    }
540
+
541
+    /**
542
+     * @PublicPage
543
+     * @NoCSRFRequired
544
+     * @NoSameSiteCookieRequired
545
+     *
546
+     * @param string $token
547
+     * @param string $files
548
+     * @param string $path
549
+     * @param string $downloadStartSecret
550
+     * @return void|\OCP\AppFramework\Http\Response
551
+     * @throws NotFoundException
552
+     */
553
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
554
+        \OC_User::setIncognitoMode(true);
555
+
556
+        $share = $this->shareManager->getShareByToken($token);
557
+
558
+        if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
559
+            return new \OCP\AppFramework\Http\DataResponse('Share has no read permission');
560
+        }
561
+
562
+        $files_list = null;
563
+        if (!is_null($files)) { // download selected files
564
+            $files_list = json_decode($files);
565
+            // in case we get only a single file
566
+            if ($files_list === null) {
567
+                $files_list = [$files];
568
+            }
569
+            // Just in case $files is a single int like '1234'
570
+            if (!is_array($files_list)) {
571
+                $files_list = [$files_list];
572
+            }
573
+        }
574
+
575
+        if (!$this->validateShare($share)) {
576
+            throw new NotFoundException();
577
+        }
578
+
579
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
580
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
581
+
582
+
583
+        // Single file share
584
+        if ($share->getNode() instanceof \OCP\Files\File) {
585
+            // Single file download
586
+            $this->singleFileDownloaded($share, $share->getNode());
587
+        }
588
+        // Directory share
589
+        else {
590
+            /** @var \OCP\Files\Folder $node */
591
+            $node = $share->getNode();
592
+
593
+            // Try to get the path
594
+            if ($path !== '') {
595
+                try {
596
+                    $node = $node->get($path);
597
+                } catch (NotFoundException $e) {
598
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
599
+                    return new NotFoundResponse();
600
+                }
601
+            }
602
+
603
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
604
+
605
+            if ($node instanceof \OCP\Files\File) {
606
+                // Single file download
607
+                $this->singleFileDownloaded($share, $share->getNode());
608
+            } else {
609
+                try {
610
+                    if (!empty($files_list)) {
611
+                        $this->fileListDownloaded($share, $files_list, $node);
612
+                    } else {
613
+                        // The folder is downloaded
614
+                        $this->singleFileDownloaded($share, $share->getNode());
615
+                    }
616
+                } catch (NotFoundException $e) {
617
+                    return new NotFoundResponse();
618
+                }
619
+            }
620
+        }
621
+
622
+        /* FIXME: We should do this all nicely in OCP */
623
+        OC_Util::tearDownFS();
624
+        OC_Util::setupFS($share->getShareOwner());
625
+
626
+        /**
627
+         * this sets a cookie to be able to recognize the start of the download
628
+         * the content must not be longer than 32 characters and must only contain
629
+         * alphanumeric characters
630
+         */
631
+        if (!empty($downloadStartSecret)
632
+            && !isset($downloadStartSecret[32])
633
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
634
+
635
+            // FIXME: set on the response once we use an actual app framework response
636
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
637
+        }
638
+
639
+        $this->emitAccessShareHook($share);
640
+
641
+        $server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
642
+
643
+        /**
644
+         * Http range requests support
645
+         */
646
+        if (isset($_SERVER['HTTP_RANGE'])) {
647
+            $server_params['range'] = $this->request->getHeader('Range');
648
+        }
649
+
650
+        // download selected files
651
+        if (!is_null($files) && $files !== '') {
652
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
653
+            // after dispatching the request which results in a "Cannot modify header information" notice.
654
+            OC_Files::get($originalSharePath, $files_list, $server_params);
655
+            exit();
656
+        } else {
657
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
658
+            // after dispatching the request which results in a "Cannot modify header information" notice.
659
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
660
+            exit();
661
+        }
662
+    }
663
+
664
+    /**
665
+     * create activity for every downloaded file
666
+     *
667
+     * @param Share\IShare $share
668
+     * @param array $files_list
669
+     * @param \OCP\Files\Folder $node
670
+     * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
671
+     */
672
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
673
+        if ($share->getHideDownload() && count($files_list) > 1) {
674
+            throw new NotFoundException('Downloading more than 1 file');
675
+        }
676
+
677
+        foreach ($files_list as $file) {
678
+            $subNode = $node->get($file);
679
+            $this->singleFileDownloaded($share, $subNode);
680
+        }
681
+    }
682
+
683
+    /**
684
+     * create activity if a single file was downloaded from a link share
685
+     *
686
+     * @param Share\IShare $share
687
+     * @throws NotFoundException when trying to download a folder of a "hide download" share
688
+     */
689
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
690
+        if ($share->getHideDownload() && $node instanceof Folder) {
691
+            throw new NotFoundException('Downloading a folder');
692
+        }
693
+
694
+        $fileId = $node->getId();
695
+
696
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
697
+        $userNodeList = $userFolder->getById($fileId);
698
+        $userNode = $userNodeList[0];
699
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
700
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
701
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
702
+
703
+        $parameters = [$userPath];
704
+
705
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
706
+            if ($node instanceof \OCP\Files\File) {
707
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
708
+            } else {
709
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
710
+            }
711
+            $parameters[] = $share->getSharedWith();
712
+        } else {
713
+            if ($node instanceof \OCP\Files\File) {
714
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
715
+            } else {
716
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
717
+            }
718
+        }
719
+
720
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
721
+
722
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
723
+            $parameters[0] = $ownerPath;
724
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
725
+        }
726
+    }
727
+
728
+    /**
729
+     * publish activity
730
+     *
731
+     * @param string $subject
732
+     * @param array $parameters
733
+     * @param string $affectedUser
734
+     * @param int $fileId
735
+     * @param string $filePath
736
+     */
737
+    protected function publishActivity($subject,
738
+                                        array $parameters,
739
+                                        $affectedUser,
740
+                                        $fileId,
741
+                                        $filePath) {
742
+        $event = $this->activityManager->generateEvent();
743
+        $event->setApp('files_sharing')
744
+            ->setType('public_links')
745
+            ->setSubject($subject, $parameters)
746
+            ->setAffectedUser($affectedUser)
747
+            ->setObject('files', $fileId, $filePath);
748
+        $this->activityManager->publish($event);
749
+    }
750 750
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Capabilities.php 1 patch
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -29,34 +29,34 @@
 block discarded – undo
29 29
 
30 30
 class Capabilities implements ICapability {
31 31
 
32
-	/** @var IAppManager */
33
-	private $appManager;
34
-
35
-	public function __construct(IAppManager $appManager) {
36
-		$this->appManager = $appManager;
37
-	}
38
-
39
-	/**
40
-	 * Function an app uses to return the capabilities
41
-	 *
42
-	 * @return array Array containing the apps capabilities
43
-	 */
44
-	public function getCapabilities() {
45
-		$federationScopesEnabled = false;
46
-
47
-		$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
48
-		if ($federatedFileSharingEnabled) {
49
-			/** @var FederatedShareProvider $shareProvider */
50
-			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
51
-			$federationScopesEnabled = $shareProvider->isLookupServerUploadEnabled();
52
-		}
53
-
54
-		return [
55
-			'provisioning_api' => [
56
-				'version' => $this->appManager->getAppVersion('provisioning_api'),
57
-				'AccountPropertyScopesVersion' => 2,
58
-				'AccountPropertyScopesFederationEnabled' => $federationScopesEnabled,
59
-			]
60
-		];
61
-	}
32
+    /** @var IAppManager */
33
+    private $appManager;
34
+
35
+    public function __construct(IAppManager $appManager) {
36
+        $this->appManager = $appManager;
37
+    }
38
+
39
+    /**
40
+     * Function an app uses to return the capabilities
41
+     *
42
+     * @return array Array containing the apps capabilities
43
+     */
44
+    public function getCapabilities() {
45
+        $federationScopesEnabled = false;
46
+
47
+        $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
48
+        if ($federatedFileSharingEnabled) {
49
+            /** @var FederatedShareProvider $shareProvider */
50
+            $shareProvider = \OC::$server->query(FederatedShareProvider::class);
51
+            $federationScopesEnabled = $shareProvider->isLookupServerUploadEnabled();
52
+        }
53
+
54
+        return [
55
+            'provisioning_api' => [
56
+                'version' => $this->appManager->getAppVersion('provisioning_api'),
57
+                'AccountPropertyScopesVersion' => 2,
58
+                'AccountPropertyScopesFederationEnabled' => $federationScopesEnabled,
59
+            ]
60
+        ];
61
+    }
62 62
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/AppInfo/Application.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -54,48 +54,48 @@
 block discarded – undo
54 54
 use Psr\Container\ContainerInterface;
55 55
 
56 56
 class Application extends App implements IBootstrap {
57
-	public function __construct(array $urlParams = []) {
58
-		parent::__construct('provisioning_api', $urlParams);
59
-	}
57
+    public function __construct(array $urlParams = []) {
58
+        parent::__construct('provisioning_api', $urlParams);
59
+    }
60 60
 
61
-	public function register(IRegistrationContext $context): void {
62
-		$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
61
+    public function register(IRegistrationContext $context): void {
62
+        $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
63 63
 
64
-		$context->registerService(NewUserMailHelper::class, function (ContainerInterface $c) {
65
-			return new NewUserMailHelper(
66
-				$c->get(Defaults::class),
67
-				$c->get(IURLGenerator::class),
68
-				$c->get(IFactory::class),
69
-				$c->get(IMailer::class),
70
-				$c->get(ISecureRandom::class),
71
-				$c->get(ITimeFactory::class),
72
-				$c->get(IConfig::class),
73
-				$c->get(ICrypto::class),
74
-				Util::getDefaultEmailAddress('no-reply')
75
-			);
76
-		});
77
-		$context->registerService(ProvisioningApiMiddleware::class, function (ContainerInterface $c) {
78
-			$user = $c->get(IUserManager::class)->get($c->get('UserId'));
79
-			$isAdmin = false;
80
-			$isSubAdmin = false;
64
+        $context->registerService(NewUserMailHelper::class, function (ContainerInterface $c) {
65
+            return new NewUserMailHelper(
66
+                $c->get(Defaults::class),
67
+                $c->get(IURLGenerator::class),
68
+                $c->get(IFactory::class),
69
+                $c->get(IMailer::class),
70
+                $c->get(ISecureRandom::class),
71
+                $c->get(ITimeFactory::class),
72
+                $c->get(IConfig::class),
73
+                $c->get(ICrypto::class),
74
+                Util::getDefaultEmailAddress('no-reply')
75
+            );
76
+        });
77
+        $context->registerService(ProvisioningApiMiddleware::class, function (ContainerInterface $c) {
78
+            $user = $c->get(IUserManager::class)->get($c->get('UserId'));
79
+            $isAdmin = false;
80
+            $isSubAdmin = false;
81 81
 
82
-			if ($user instanceof IUser) {
83
-				$groupManager = $c->get(IGroupManager::class);
84
-				assert($groupManager instanceof GroupManager);
85
-				$isAdmin = $groupManager->isAdmin($user->getUID());
86
-				$isSubAdmin = $groupManager->getSubAdmin()->isSubAdmin($user);
87
-			}
82
+            if ($user instanceof IUser) {
83
+                $groupManager = $c->get(IGroupManager::class);
84
+                assert($groupManager instanceof GroupManager);
85
+                $isAdmin = $groupManager->isAdmin($user->getUID());
86
+                $isSubAdmin = $groupManager->getSubAdmin()->isSubAdmin($user);
87
+            }
88 88
 
89
-			return new ProvisioningApiMiddleware(
90
-				$c->get(IControllerMethodReflector::class),
91
-				$isAdmin,
92
-				$isSubAdmin
93
-			);
94
-		});
95
-		$context->registerMiddleware(ProvisioningApiMiddleware::class);
96
-		$context->registerCapability(Capabilities::class);
97
-	}
89
+            return new ProvisioningApiMiddleware(
90
+                $c->get(IControllerMethodReflector::class),
91
+                $isAdmin,
92
+                $isSubAdmin
93
+            );
94
+        });
95
+        $context->registerMiddleware(ProvisioningApiMiddleware::class);
96
+        $context->registerCapability(Capabilities::class);
97
+    }
98 98
 
99
-	public function boot(IBootContext $context): void {
100
-	}
99
+    public function boot(IBootContext $context): void {
100
+    }
101 101
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +1032 added lines, -1032 removed lines patch added patch discarded remove patch
@@ -74,1036 +74,1036 @@
 block discarded – undo
74 74
 
75 75
 class UsersController extends AUserData {
76 76
 
77
-	/** @var IAppManager */
78
-	private $appManager;
79
-	/** @var IURLGenerator */
80
-	protected $urlGenerator;
81
-	/** @var LoggerInterface */
82
-	private $logger;
83
-	/** @var IFactory */
84
-	protected $l10nFactory;
85
-	/** @var NewUserMailHelper */
86
-	private $newUserMailHelper;
87
-	/** @var ISecureRandom */
88
-	private $secureRandom;
89
-	/** @var RemoteWipe */
90
-	private $remoteWipe;
91
-	/** @var KnownUserService */
92
-	private $knownUserService;
93
-	/** @var IEventDispatcher */
94
-	private $eventDispatcher;
95
-
96
-	public function __construct(string $appName,
97
-								IRequest $request,
98
-								IUserManager $userManager,
99
-								IConfig $config,
100
-								IAppManager $appManager,
101
-								IGroupManager $groupManager,
102
-								IUserSession $userSession,
103
-								AccountManager $accountManager,
104
-								IURLGenerator $urlGenerator,
105
-								LoggerInterface $logger,
106
-								IFactory $l10nFactory,
107
-								NewUserMailHelper $newUserMailHelper,
108
-								ISecureRandom $secureRandom,
109
-								RemoteWipe $remoteWipe,
110
-								KnownUserService $knownUserService,
111
-								IEventDispatcher $eventDispatcher) {
112
-		parent::__construct($appName,
113
-							$request,
114
-							$userManager,
115
-							$config,
116
-							$groupManager,
117
-							$userSession,
118
-							$accountManager,
119
-							$l10nFactory);
120
-
121
-		$this->appManager = $appManager;
122
-		$this->urlGenerator = $urlGenerator;
123
-		$this->logger = $logger;
124
-		$this->l10nFactory = $l10nFactory;
125
-		$this->newUserMailHelper = $newUserMailHelper;
126
-		$this->secureRandom = $secureRandom;
127
-		$this->remoteWipe = $remoteWipe;
128
-		$this->knownUserService = $knownUserService;
129
-		$this->eventDispatcher = $eventDispatcher;
130
-	}
131
-
132
-	/**
133
-	 * @NoAdminRequired
134
-	 *
135
-	 * returns a list of users
136
-	 *
137
-	 * @param string $search
138
-	 * @param int $limit
139
-	 * @param int $offset
140
-	 * @return DataResponse
141
-	 */
142
-	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
143
-		$user = $this->userSession->getUser();
144
-		$users = [];
145
-
146
-		// Admin? Or SubAdmin?
147
-		$uid = $user->getUID();
148
-		$subAdminManager = $this->groupManager->getSubAdmin();
149
-		if ($this->groupManager->isAdmin($uid)) {
150
-			$users = $this->userManager->search($search, $limit, $offset);
151
-		} elseif ($subAdminManager->isSubAdmin($user)) {
152
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
153
-			foreach ($subAdminOfGroups as $key => $group) {
154
-				$subAdminOfGroups[$key] = $group->getGID();
155
-			}
156
-
157
-			$users = [];
158
-			foreach ($subAdminOfGroups as $group) {
159
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
160
-			}
161
-		}
162
-
163
-		$users = array_keys($users);
164
-
165
-		return new DataResponse([
166
-			'users' => $users
167
-		]);
168
-	}
169
-
170
-	/**
171
-	 * @NoAdminRequired
172
-	 *
173
-	 * returns a list of users and their data
174
-	 */
175
-	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
176
-		$currentUser = $this->userSession->getUser();
177
-		$users = [];
178
-
179
-		// Admin? Or SubAdmin?
180
-		$uid = $currentUser->getUID();
181
-		$subAdminManager = $this->groupManager->getSubAdmin();
182
-		if ($this->groupManager->isAdmin($uid)) {
183
-			$users = $this->userManager->search($search, $limit, $offset);
184
-			$users = array_keys($users);
185
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
186
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
187
-			foreach ($subAdminOfGroups as $key => $group) {
188
-				$subAdminOfGroups[$key] = $group->getGID();
189
-			}
190
-
191
-			$users = [];
192
-			foreach ($subAdminOfGroups as $group) {
193
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
194
-			}
195
-			$users = array_merge(...$users);
196
-		}
197
-
198
-		$usersDetails = [];
199
-		foreach ($users as $userId) {
200
-			$userId = (string) $userId;
201
-			$userData = $this->getUserData($userId);
202
-			// Do not insert empty entry
203
-			if (!empty($userData)) {
204
-				$usersDetails[$userId] = $userData;
205
-			} else {
206
-				// Logged user does not have permissions to see this user
207
-				// only showing its id
208
-				$usersDetails[$userId] = ['id' => $userId];
209
-			}
210
-		}
211
-
212
-		return new DataResponse([
213
-			'users' => $usersDetails
214
-		]);
215
-	}
216
-
217
-
218
-	/**
219
-	 * @NoAdminRequired
220
-	 * @NoSubAdminRequired
221
-	 *
222
-	 * @param string $location
223
-	 * @param array $search
224
-	 * @return DataResponse
225
-	 */
226
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
227
-		$phoneUtil = PhoneNumberUtil::getInstance();
228
-
229
-		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
230
-			// Not a valid region code
231
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
232
-		}
233
-
234
-		/** @var IUser $user */
235
-		$user = $this->userSession->getUser();
236
-		$knownTo = $user->getUID();
237
-
238
-		$normalizedNumberToKey = [];
239
-		foreach ($search as $key => $phoneNumbers) {
240
-			foreach ($phoneNumbers as $phone) {
241
-				try {
242
-					$phoneNumber = $phoneUtil->parse($phone, $location);
243
-					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
244
-						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
245
-						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
246
-					}
247
-				} catch (NumberParseException $e) {
248
-				}
249
-			}
250
-		}
251
-
252
-		$phoneNumbers = array_keys($normalizedNumberToKey);
253
-
254
-		if (empty($phoneNumbers)) {
255
-			return new DataResponse();
256
-		}
257
-
258
-		// Cleanup all previous entries and only allow new matches
259
-		$this->knownUserService->deleteKnownTo($knownTo);
260
-
261
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
262
-
263
-		if (empty($userMatches)) {
264
-			return new DataResponse();
265
-		}
266
-
267
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
268
-		if (strpos($cloudUrl, 'http://') === 0) {
269
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
270
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
271
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
272
-		}
273
-
274
-		$matches = [];
275
-		foreach ($userMatches as $phone => $userId) {
276
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
277
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
278
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
279
-		}
280
-
281
-		return new DataResponse($matches);
282
-	}
283
-
284
-	/**
285
-	 * @throws OCSException
286
-	 */
287
-	private function createNewUserId(): string {
288
-		$attempts = 0;
289
-		do {
290
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
291
-			if (!$this->userManager->userExists($uidCandidate)) {
292
-				return $uidCandidate;
293
-			}
294
-			$attempts++;
295
-		} while ($attempts < 10);
296
-		throw new OCSException('Could not create non-existing user id', 111);
297
-	}
298
-
299
-	/**
300
-	 * @PasswordConfirmationRequired
301
-	 * @NoAdminRequired
302
-	 *
303
-	 * @param string $userid
304
-	 * @param string $password
305
-	 * @param string $displayName
306
-	 * @param string $email
307
-	 * @param array $groups
308
-	 * @param array $subadmin
309
-	 * @param string $quota
310
-	 * @param string $language
311
-	 * @return DataResponse
312
-	 * @throws OCSException
313
-	 */
314
-	public function addUser(string $userid,
315
-							string $password = '',
316
-							string $displayName = '',
317
-							string $email = '',
318
-							array $groups = [],
319
-							array $subadmin = [],
320
-							string $quota = '',
321
-							string $language = ''): DataResponse {
322
-		$user = $this->userSession->getUser();
323
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
324
-		$subAdminManager = $this->groupManager->getSubAdmin();
325
-
326
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
327
-			$userid = $this->createNewUserId();
328
-		}
329
-
330
-		if ($this->userManager->userExists($userid)) {
331
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
332
-			throw new OCSException('User already exists', 102);
333
-		}
334
-
335
-		if ($groups !== []) {
336
-			foreach ($groups as $group) {
337
-				if (!$this->groupManager->groupExists($group)) {
338
-					throw new OCSException('group '.$group.' does not exist', 104);
339
-				}
340
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
341
-					throw new OCSException('insufficient privileges for group '. $group, 105);
342
-				}
343
-			}
344
-		} else {
345
-			if (!$isAdmin) {
346
-				throw new OCSException('no group specified (required for subadmins)', 106);
347
-			}
348
-		}
349
-
350
-		$subadminGroups = [];
351
-		if ($subadmin !== []) {
352
-			foreach ($subadmin as $groupid) {
353
-				$group = $this->groupManager->get($groupid);
354
-				// Check if group exists
355
-				if ($group === null) {
356
-					throw new OCSException('Subadmin group does not exist',  102);
357
-				}
358
-				// Check if trying to make subadmin of admin group
359
-				if ($group->getGID() === 'admin') {
360
-					throw new OCSException('Cannot create subadmins for admin group', 103);
361
-				}
362
-				// Check if has permission to promote subadmins
363
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
364
-					throw new OCSForbiddenException('No permissions to promote subadmins');
365
-				}
366
-				$subadminGroups[] = $group;
367
-			}
368
-		}
369
-
370
-		$generatePasswordResetToken = false;
371
-		if ($password === '') {
372
-			if ($email === '') {
373
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
374
-			}
375
-
376
-			$passwordEvent = new GenerateSecurePasswordEvent();
377
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
378
-
379
-			$password = $passwordEvent->getPassword();
380
-			if ($password === null) {
381
-				// Fallback: ensure to pass password_policy in any case
382
-				$password = $this->secureRandom->generate(10)
383
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
384
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
385
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
386
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
387
-			}
388
-			$generatePasswordResetToken = true;
389
-		}
390
-
391
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
392
-			throw new OCSException('Required email address was not provided', 110);
393
-		}
394
-
395
-		try {
396
-			$newUser = $this->userManager->createUser($userid, $password);
397
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
398
-
399
-			foreach ($groups as $group) {
400
-				$this->groupManager->get($group)->addUser($newUser);
401
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
402
-			}
403
-			foreach ($subadminGroups as $group) {
404
-				$subAdminManager->createSubAdmin($newUser, $group);
405
-			}
406
-
407
-			if ($displayName !== '') {
408
-				$this->editUser($userid, 'display', $displayName);
409
-			}
410
-
411
-			if ($quota !== '') {
412
-				$this->editUser($userid, 'quota', $quota);
413
-			}
414
-
415
-			if ($language !== '') {
416
-				$this->editUser($userid, 'language', $language);
417
-			}
418
-
419
-			// Send new user mail only if a mail is set
420
-			if ($email !== '') {
421
-				$newUser->setEMailAddress($email);
422
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
423
-					try {
424
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
425
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
426
-					} catch (\Exception $e) {
427
-						// Mail could be failing hard or just be plain not configured
428
-						// Logging error as it is the hardest of the two
429
-						$this->logger->error("Unable to send the invitation mail to $email",
430
-							[
431
-								'app' => 'ocs_api',
432
-								'exception' => $e,
433
-							]
434
-						);
435
-					}
436
-				}
437
-			}
438
-
439
-			return new DataResponse(['id' => $userid]);
440
-		} catch (HintException $e) {
441
-			$this->logger->warning('Failed addUser attempt with hint exception.',
442
-				[
443
-					'app' => 'ocs_api',
444
-					'exception' => $e,
445
-				]
446
-			);
447
-			throw new OCSException($e->getHint(), 107);
448
-		} catch (OCSException $e) {
449
-			$this->logger->warning('Failed addUser attempt with ocs exeption.',
450
-				[
451
-					'app' => 'ocs_api',
452
-					'exception' => $e,
453
-				]
454
-			);
455
-			throw $e;
456
-		} catch (\InvalidArgumentException $e) {
457
-			$this->logger->error('Failed addUser attempt with invalid argument exeption.',
458
-				[
459
-					'app' => 'ocs_api',
460
-					'exception' => $e,
461
-				]
462
-			);
463
-			throw new OCSException($e->getMessage(), 101);
464
-		} catch (\Exception $e) {
465
-			$this->logger->error('Failed addUser attempt with exception.',
466
-				[
467
-					'app' => 'ocs_api',
468
-					'exception' => $e
469
-				]
470
-			);
471
-			throw new OCSException('Bad request', 101);
472
-		}
473
-	}
474
-
475
-	/**
476
-	 * @NoAdminRequired
477
-	 * @NoSubAdminRequired
478
-	 *
479
-	 * gets user info
480
-	 *
481
-	 * @param string $userId
482
-	 * @return DataResponse
483
-	 * @throws OCSException
484
-	 */
485
-	public function getUser(string $userId): DataResponse {
486
-		$includeScopes = false;
487
-		$currentUser = $this->userSession->getUser();
488
-		if ($currentUser && $currentUser->getUID() === $userId) {
489
-			$includeScopes = true;
490
-		}
491
-
492
-		$data = $this->getUserData($userId, $includeScopes);
493
-		// getUserData returns empty array if not enough permissions
494
-		if (empty($data)) {
495
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
496
-		}
497
-		return new DataResponse($data);
498
-	}
499
-
500
-	/**
501
-	 * @NoAdminRequired
502
-	 * @NoSubAdminRequired
503
-	 *
504
-	 * gets user info from the currently logged in user
505
-	 *
506
-	 * @return DataResponse
507
-	 * @throws OCSException
508
-	 */
509
-	public function getCurrentUser(): DataResponse {
510
-		$user = $this->userSession->getUser();
511
-		if ($user) {
512
-			$data = $this->getUserData($user->getUID(), true);
513
-			// rename "displayname" to "display-name" only for this call to keep
514
-			// the API stable.
515
-			$data['display-name'] = $data['displayname'];
516
-			unset($data['displayname']);
517
-			return new DataResponse($data);
518
-		}
519
-
520
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
521
-	}
522
-
523
-	/**
524
-	 * @NoAdminRequired
525
-	 * @NoSubAdminRequired
526
-	 */
527
-	public function getEditableFields(): DataResponse {
528
-		$permittedFields = [];
529
-
530
-		// Editing self (display, email)
531
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
532
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
533
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
534
-		}
535
-
536
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
537
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
538
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
539
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
540
-
541
-		return new DataResponse($permittedFields);
542
-	}
543
-
544
-	/**
545
-	 * @NoAdminRequired
546
-	 * @NoSubAdminRequired
547
-	 * @PasswordConfirmationRequired
548
-	 *
549
-	 * edit users
550
-	 *
551
-	 * @param string $userId
552
-	 * @param string $key
553
-	 * @param string $value
554
-	 * @return DataResponse
555
-	 * @throws OCSException
556
-	 */
557
-	public function editUser(string $userId, string $key, string $value): DataResponse {
558
-		$currentLoggedInUser = $this->userSession->getUser();
559
-
560
-		$targetUser = $this->userManager->get($userId);
561
-		if ($targetUser === null) {
562
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
563
-		}
564
-
565
-		$permittedFields = [];
566
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
567
-			// Editing self (display, email)
568
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
569
-				$permittedFields[] = 'display';
570
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
571
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
572
-			}
573
-
574
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
575
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
576
-
577
-			$permittedFields[] = 'password';
578
-			if ($this->config->getSystemValue('force_language', false) === false ||
579
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
580
-				$permittedFields[] = 'language';
581
-			}
582
-
583
-			if ($this->config->getSystemValue('force_locale', false) === false ||
584
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
585
-				$permittedFields[] = 'locale';
586
-			}
587
-
588
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
589
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
590
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
591
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
592
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
593
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
594
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
595
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
596
-
597
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
598
-
599
-			// If admin they can edit their own quota
600
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
601
-				$permittedFields[] = 'quota';
602
-			}
603
-		} else {
604
-			// Check if admin / subadmin
605
-			$subAdminManager = $this->groupManager->getSubAdmin();
606
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
607
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
608
-				// They have permissions over the user
609
-				$permittedFields[] = 'display';
610
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
611
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
612
-				$permittedFields[] = 'password';
613
-				$permittedFields[] = 'language';
614
-				$permittedFields[] = 'locale';
615
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
616
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
617
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
618
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
619
-				$permittedFields[] = 'quota';
620
-			} else {
621
-				// No rights
622
-				throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
623
-			}
624
-		}
625
-		// Check if permitted to edit this field
626
-		if (!in_array($key, $permittedFields)) {
627
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
628
-		}
629
-		// Process the edit
630
-		switch ($key) {
631
-			case 'display':
632
-			case IAccountManager::PROPERTY_DISPLAYNAME:
633
-				$targetUser->setDisplayName($value);
634
-				break;
635
-			case 'quota':
636
-				$quota = $value;
637
-				if ($quota !== 'none' && $quota !== 'default') {
638
-					if (is_numeric($quota)) {
639
-						$quota = (float) $quota;
640
-					} else {
641
-						$quota = \OCP\Util::computerFileSize($quota);
642
-					}
643
-					if ($quota === false) {
644
-						throw new OCSException('Invalid quota value '.$value, 103);
645
-					}
646
-					if ($quota === -1) {
647
-						$quota = 'none';
648
-					} else {
649
-						$quota = \OCP\Util::humanFileSize($quota);
650
-					}
651
-				}
652
-				$targetUser->setQuota($quota);
653
-				break;
654
-			case 'password':
655
-				try {
656
-					if (!$targetUser->canChangePassword()) {
657
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
658
-					}
659
-					$targetUser->setPassword($value);
660
-				} catch (HintException $e) { // password policy error
661
-					throw new OCSException($e->getMessage(), 103);
662
-				}
663
-				break;
664
-			case 'language':
665
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
666
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
667
-					throw new OCSException('Invalid language', 102);
668
-				}
669
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
670
-				break;
671
-			case 'locale':
672
-				if (!$this->l10nFactory->localeExists($value)) {
673
-					throw new OCSException('Invalid locale', 102);
674
-				}
675
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
676
-				break;
677
-			case IAccountManager::PROPERTY_EMAIL:
678
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
679
-					$targetUser->setEMailAddress($value);
680
-				} else {
681
-					throw new OCSException('', 102);
682
-				}
683
-				break;
684
-			case IAccountManager::PROPERTY_PHONE:
685
-			case IAccountManager::PROPERTY_ADDRESS:
686
-			case IAccountManager::PROPERTY_WEBSITE:
687
-			case IAccountManager::PROPERTY_TWITTER:
688
-				$userAccount = $this->accountManager->getUser($targetUser);
689
-				if ($userAccount[$key]['value'] !== $value) {
690
-					$userAccount[$key]['value'] = $value;
691
-					try {
692
-						$this->accountManager->updateUser($targetUser, $userAccount, true);
693
-
694
-						if ($key === IAccountManager::PROPERTY_PHONE) {
695
-							$this->knownUserService->deleteByContactUserId($targetUser->getUID());
696
-						}
697
-					} catch (\InvalidArgumentException $e) {
698
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
699
-					}
700
-				}
701
-				break;
702
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
703
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
704
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
705
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
706
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
707
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
708
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
709
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
710
-				$userAccount = $this->accountManager->getUser($targetUser);
711
-				if ($userAccount[$propertyName]['scope'] !== $value) {
712
-					$userAccount[$propertyName]['scope'] = $value;
713
-					try {
714
-						$this->accountManager->updateUser($targetUser, $userAccount, true);
715
-					} catch (\InvalidArgumentException $e) {
716
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
717
-					}
718
-				}
719
-				break;
720
-			default:
721
-				throw new OCSException('', 103);
722
-		}
723
-		return new DataResponse();
724
-	}
725
-
726
-	/**
727
-	 * @PasswordConfirmationRequired
728
-	 * @NoAdminRequired
729
-	 *
730
-	 * @param string $userId
731
-	 *
732
-	 * @return DataResponse
733
-	 *
734
-	 * @throws OCSException
735
-	 */
736
-	public function wipeUserDevices(string $userId): DataResponse {
737
-		/** @var IUser $currentLoggedInUser */
738
-		$currentLoggedInUser = $this->userSession->getUser();
739
-
740
-		$targetUser = $this->userManager->get($userId);
741
-
742
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
743
-			throw new OCSException('', 101);
744
-		}
745
-
746
-		// If not permitted
747
-		$subAdminManager = $this->groupManager->getSubAdmin();
748
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
749
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
750
-		}
751
-
752
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
753
-
754
-		return new DataResponse();
755
-	}
756
-
757
-	/**
758
-	 * @PasswordConfirmationRequired
759
-	 * @NoAdminRequired
760
-	 *
761
-	 * @param string $userId
762
-	 * @return DataResponse
763
-	 * @throws OCSException
764
-	 */
765
-	public function deleteUser(string $userId): DataResponse {
766
-		$currentLoggedInUser = $this->userSession->getUser();
767
-
768
-		$targetUser = $this->userManager->get($userId);
769
-
770
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
771
-			throw new OCSException('', 101);
772
-		}
773
-
774
-		// If not permitted
775
-		$subAdminManager = $this->groupManager->getSubAdmin();
776
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
777
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
778
-		}
779
-
780
-		// Go ahead with the delete
781
-		if ($targetUser->delete()) {
782
-			return new DataResponse();
783
-		} else {
784
-			throw new OCSException('', 101);
785
-		}
786
-	}
787
-
788
-	/**
789
-	 * @PasswordConfirmationRequired
790
-	 * @NoAdminRequired
791
-	 *
792
-	 * @param string $userId
793
-	 * @return DataResponse
794
-	 * @throws OCSException
795
-	 * @throws OCSForbiddenException
796
-	 */
797
-	public function disableUser(string $userId): DataResponse {
798
-		return $this->setEnabled($userId, false);
799
-	}
800
-
801
-	/**
802
-	 * @PasswordConfirmationRequired
803
-	 * @NoAdminRequired
804
-	 *
805
-	 * @param string $userId
806
-	 * @return DataResponse
807
-	 * @throws OCSException
808
-	 * @throws OCSForbiddenException
809
-	 */
810
-	public function enableUser(string $userId): DataResponse {
811
-		return $this->setEnabled($userId, true);
812
-	}
813
-
814
-	/**
815
-	 * @param string $userId
816
-	 * @param bool $value
817
-	 * @return DataResponse
818
-	 * @throws OCSException
819
-	 */
820
-	private function setEnabled(string $userId, bool $value): DataResponse {
821
-		$currentLoggedInUser = $this->userSession->getUser();
822
-
823
-		$targetUser = $this->userManager->get($userId);
824
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
825
-			throw new OCSException('', 101);
826
-		}
827
-
828
-		// If not permitted
829
-		$subAdminManager = $this->groupManager->getSubAdmin();
830
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
831
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
832
-		}
833
-
834
-		// enable/disable the user now
835
-		$targetUser->setEnabled($value);
836
-		return new DataResponse();
837
-	}
838
-
839
-	/**
840
-	 * @NoAdminRequired
841
-	 * @NoSubAdminRequired
842
-	 *
843
-	 * @param string $userId
844
-	 * @return DataResponse
845
-	 * @throws OCSException
846
-	 */
847
-	public function getUsersGroups(string $userId): DataResponse {
848
-		$loggedInUser = $this->userSession->getUser();
849
-
850
-		$targetUser = $this->userManager->get($userId);
851
-		if ($targetUser === null) {
852
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
853
-		}
854
-
855
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
856
-			// Self lookup or admin lookup
857
-			return new DataResponse([
858
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
859
-			]);
860
-		} else {
861
-			$subAdminManager = $this->groupManager->getSubAdmin();
862
-
863
-			// Looking up someone else
864
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
865
-				// Return the group that the method caller is subadmin of for the user in question
866
-				/** @var IGroup[] $getSubAdminsGroups */
867
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
868
-				foreach ($getSubAdminsGroups as $key => $group) {
869
-					$getSubAdminsGroups[$key] = $group->getGID();
870
-				}
871
-				$groups = array_intersect(
872
-					$getSubAdminsGroups,
873
-					$this->groupManager->getUserGroupIds($targetUser)
874
-				);
875
-				return new DataResponse(['groups' => $groups]);
876
-			} else {
877
-				// Not permitted
878
-				throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
879
-			}
880
-		}
881
-	}
882
-
883
-	/**
884
-	 * @PasswordConfirmationRequired
885
-	 * @NoAdminRequired
886
-	 *
887
-	 * @param string $userId
888
-	 * @param string $groupid
889
-	 * @return DataResponse
890
-	 * @throws OCSException
891
-	 */
892
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
893
-		if ($groupid === '') {
894
-			throw new OCSException('', 101);
895
-		}
896
-
897
-		$group = $this->groupManager->get($groupid);
898
-		$targetUser = $this->userManager->get($userId);
899
-		if ($group === null) {
900
-			throw new OCSException('', 102);
901
-		}
902
-		if ($targetUser === null) {
903
-			throw new OCSException('', 103);
904
-		}
905
-
906
-		// If they're not an admin, check they are a subadmin of the group in question
907
-		$loggedInUser = $this->userSession->getUser();
908
-		$subAdminManager = $this->groupManager->getSubAdmin();
909
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
910
-			throw new OCSException('', 104);
911
-		}
912
-
913
-		// Add user to group
914
-		$group->addUser($targetUser);
915
-		return new DataResponse();
916
-	}
917
-
918
-	/**
919
-	 * @PasswordConfirmationRequired
920
-	 * @NoAdminRequired
921
-	 *
922
-	 * @param string $userId
923
-	 * @param string $groupid
924
-	 * @return DataResponse
925
-	 * @throws OCSException
926
-	 */
927
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
928
-		$loggedInUser = $this->userSession->getUser();
929
-
930
-		if ($groupid === null || trim($groupid) === '') {
931
-			throw new OCSException('', 101);
932
-		}
933
-
934
-		$group = $this->groupManager->get($groupid);
935
-		if ($group === null) {
936
-			throw new OCSException('', 102);
937
-		}
938
-
939
-		$targetUser = $this->userManager->get($userId);
940
-		if ($targetUser === null) {
941
-			throw new OCSException('', 103);
942
-		}
943
-
944
-		// If they're not an admin, check they are a subadmin of the group in question
945
-		$subAdminManager = $this->groupManager->getSubAdmin();
946
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
947
-			throw new OCSException('', 104);
948
-		}
949
-
950
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
951
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
952
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
953
-				if ($group->getGID() === 'admin') {
954
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
955
-				}
956
-			} else {
957
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
958
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
959
-			}
960
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
961
-			/** @var IGroup[] $subAdminGroups */
962
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
963
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
964
-				return $subAdminGroup->getGID();
965
-			}, $subAdminGroups);
966
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
967
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
968
-
969
-			if (count($userSubAdminGroups) <= 1) {
970
-				// Subadmin must not be able to remove a user from all their subadmin groups.
971
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
972
-			}
973
-		}
974
-
975
-		// Remove user from group
976
-		$group->removeUser($targetUser);
977
-		return new DataResponse();
978
-	}
979
-
980
-	/**
981
-	 * Creates a subadmin
982
-	 *
983
-	 * @PasswordConfirmationRequired
984
-	 *
985
-	 * @param string $userId
986
-	 * @param string $groupid
987
-	 * @return DataResponse
988
-	 * @throws OCSException
989
-	 */
990
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
991
-		$group = $this->groupManager->get($groupid);
992
-		$user = $this->userManager->get($userId);
993
-
994
-		// Check if the user exists
995
-		if ($user === null) {
996
-			throw new OCSException('User does not exist', 101);
997
-		}
998
-		// Check if group exists
999
-		if ($group === null) {
1000
-			throw new OCSException('Group does not exist',  102);
1001
-		}
1002
-		// Check if trying to make subadmin of admin group
1003
-		if ($group->getGID() === 'admin') {
1004
-			throw new OCSException('Cannot create subadmins for admin group', 103);
1005
-		}
1006
-
1007
-		$subAdminManager = $this->groupManager->getSubAdmin();
1008
-
1009
-		// We cannot be subadmin twice
1010
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1011
-			return new DataResponse();
1012
-		}
1013
-		// Go
1014
-		$subAdminManager->createSubAdmin($user, $group);
1015
-		return new DataResponse();
1016
-	}
1017
-
1018
-	/**
1019
-	 * Removes a subadmin from a group
1020
-	 *
1021
-	 * @PasswordConfirmationRequired
1022
-	 *
1023
-	 * @param string $userId
1024
-	 * @param string $groupid
1025
-	 * @return DataResponse
1026
-	 * @throws OCSException
1027
-	 */
1028
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1029
-		$group = $this->groupManager->get($groupid);
1030
-		$user = $this->userManager->get($userId);
1031
-		$subAdminManager = $this->groupManager->getSubAdmin();
1032
-
1033
-		// Check if the user exists
1034
-		if ($user === null) {
1035
-			throw new OCSException('User does not exist', 101);
1036
-		}
1037
-		// Check if the group exists
1038
-		if ($group === null) {
1039
-			throw new OCSException('Group does not exist', 101);
1040
-		}
1041
-		// Check if they are a subadmin of this said group
1042
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1043
-			throw new OCSException('User is not a subadmin of this group', 102);
1044
-		}
1045
-
1046
-		// Go
1047
-		$subAdminManager->deleteSubAdmin($user, $group);
1048
-		return new DataResponse();
1049
-	}
1050
-
1051
-	/**
1052
-	 * Get the groups a user is a subadmin of
1053
-	 *
1054
-	 * @param string $userId
1055
-	 * @return DataResponse
1056
-	 * @throws OCSException
1057
-	 */
1058
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1059
-		$groups = $this->getUserSubAdminGroupsData($userId);
1060
-		return new DataResponse($groups);
1061
-	}
1062
-
1063
-	/**
1064
-	 * @NoAdminRequired
1065
-	 * @PasswordConfirmationRequired
1066
-	 *
1067
-	 * resend welcome message
1068
-	 *
1069
-	 * @param string $userId
1070
-	 * @return DataResponse
1071
-	 * @throws OCSException
1072
-	 */
1073
-	public function resendWelcomeMessage(string $userId): DataResponse {
1074
-		$currentLoggedInUser = $this->userSession->getUser();
1075
-
1076
-		$targetUser = $this->userManager->get($userId);
1077
-		if ($targetUser === null) {
1078
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1079
-		}
1080
-
1081
-		// Check if admin / subadmin
1082
-		$subAdminManager = $this->groupManager->getSubAdmin();
1083
-		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1084
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1085
-			// No rights
1086
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
1087
-		}
1088
-
1089
-		$email = $targetUser->getEMailAddress();
1090
-		if ($email === '' || $email === null) {
1091
-			throw new OCSException('Email address not available', 101);
1092
-		}
1093
-
1094
-		try {
1095
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1096
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1097
-		} catch (\Exception $e) {
1098
-			$this->logger->error("Can't send new user mail to $email",
1099
-				[
1100
-					'app' => 'settings',
1101
-					'exception' => $e,
1102
-				]
1103
-			);
1104
-			throw new OCSException('Sending email failed', 102);
1105
-		}
1106
-
1107
-		return new DataResponse();
1108
-	}
77
+    /** @var IAppManager */
78
+    private $appManager;
79
+    /** @var IURLGenerator */
80
+    protected $urlGenerator;
81
+    /** @var LoggerInterface */
82
+    private $logger;
83
+    /** @var IFactory */
84
+    protected $l10nFactory;
85
+    /** @var NewUserMailHelper */
86
+    private $newUserMailHelper;
87
+    /** @var ISecureRandom */
88
+    private $secureRandom;
89
+    /** @var RemoteWipe */
90
+    private $remoteWipe;
91
+    /** @var KnownUserService */
92
+    private $knownUserService;
93
+    /** @var IEventDispatcher */
94
+    private $eventDispatcher;
95
+
96
+    public function __construct(string $appName,
97
+                                IRequest $request,
98
+                                IUserManager $userManager,
99
+                                IConfig $config,
100
+                                IAppManager $appManager,
101
+                                IGroupManager $groupManager,
102
+                                IUserSession $userSession,
103
+                                AccountManager $accountManager,
104
+                                IURLGenerator $urlGenerator,
105
+                                LoggerInterface $logger,
106
+                                IFactory $l10nFactory,
107
+                                NewUserMailHelper $newUserMailHelper,
108
+                                ISecureRandom $secureRandom,
109
+                                RemoteWipe $remoteWipe,
110
+                                KnownUserService $knownUserService,
111
+                                IEventDispatcher $eventDispatcher) {
112
+        parent::__construct($appName,
113
+                            $request,
114
+                            $userManager,
115
+                            $config,
116
+                            $groupManager,
117
+                            $userSession,
118
+                            $accountManager,
119
+                            $l10nFactory);
120
+
121
+        $this->appManager = $appManager;
122
+        $this->urlGenerator = $urlGenerator;
123
+        $this->logger = $logger;
124
+        $this->l10nFactory = $l10nFactory;
125
+        $this->newUserMailHelper = $newUserMailHelper;
126
+        $this->secureRandom = $secureRandom;
127
+        $this->remoteWipe = $remoteWipe;
128
+        $this->knownUserService = $knownUserService;
129
+        $this->eventDispatcher = $eventDispatcher;
130
+    }
131
+
132
+    /**
133
+     * @NoAdminRequired
134
+     *
135
+     * returns a list of users
136
+     *
137
+     * @param string $search
138
+     * @param int $limit
139
+     * @param int $offset
140
+     * @return DataResponse
141
+     */
142
+    public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
143
+        $user = $this->userSession->getUser();
144
+        $users = [];
145
+
146
+        // Admin? Or SubAdmin?
147
+        $uid = $user->getUID();
148
+        $subAdminManager = $this->groupManager->getSubAdmin();
149
+        if ($this->groupManager->isAdmin($uid)) {
150
+            $users = $this->userManager->search($search, $limit, $offset);
151
+        } elseif ($subAdminManager->isSubAdmin($user)) {
152
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
153
+            foreach ($subAdminOfGroups as $key => $group) {
154
+                $subAdminOfGroups[$key] = $group->getGID();
155
+            }
156
+
157
+            $users = [];
158
+            foreach ($subAdminOfGroups as $group) {
159
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
160
+            }
161
+        }
162
+
163
+        $users = array_keys($users);
164
+
165
+        return new DataResponse([
166
+            'users' => $users
167
+        ]);
168
+    }
169
+
170
+    /**
171
+     * @NoAdminRequired
172
+     *
173
+     * returns a list of users and their data
174
+     */
175
+    public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
176
+        $currentUser = $this->userSession->getUser();
177
+        $users = [];
178
+
179
+        // Admin? Or SubAdmin?
180
+        $uid = $currentUser->getUID();
181
+        $subAdminManager = $this->groupManager->getSubAdmin();
182
+        if ($this->groupManager->isAdmin($uid)) {
183
+            $users = $this->userManager->search($search, $limit, $offset);
184
+            $users = array_keys($users);
185
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
186
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
187
+            foreach ($subAdminOfGroups as $key => $group) {
188
+                $subAdminOfGroups[$key] = $group->getGID();
189
+            }
190
+
191
+            $users = [];
192
+            foreach ($subAdminOfGroups as $group) {
193
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
194
+            }
195
+            $users = array_merge(...$users);
196
+        }
197
+
198
+        $usersDetails = [];
199
+        foreach ($users as $userId) {
200
+            $userId = (string) $userId;
201
+            $userData = $this->getUserData($userId);
202
+            // Do not insert empty entry
203
+            if (!empty($userData)) {
204
+                $usersDetails[$userId] = $userData;
205
+            } else {
206
+                // Logged user does not have permissions to see this user
207
+                // only showing its id
208
+                $usersDetails[$userId] = ['id' => $userId];
209
+            }
210
+        }
211
+
212
+        return new DataResponse([
213
+            'users' => $usersDetails
214
+        ]);
215
+    }
216
+
217
+
218
+    /**
219
+     * @NoAdminRequired
220
+     * @NoSubAdminRequired
221
+     *
222
+     * @param string $location
223
+     * @param array $search
224
+     * @return DataResponse
225
+     */
226
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
227
+        $phoneUtil = PhoneNumberUtil::getInstance();
228
+
229
+        if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
230
+            // Not a valid region code
231
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
232
+        }
233
+
234
+        /** @var IUser $user */
235
+        $user = $this->userSession->getUser();
236
+        $knownTo = $user->getUID();
237
+
238
+        $normalizedNumberToKey = [];
239
+        foreach ($search as $key => $phoneNumbers) {
240
+            foreach ($phoneNumbers as $phone) {
241
+                try {
242
+                    $phoneNumber = $phoneUtil->parse($phone, $location);
243
+                    if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
244
+                        $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
245
+                        $normalizedNumberToKey[$normalizedNumber] = (string) $key;
246
+                    }
247
+                } catch (NumberParseException $e) {
248
+                }
249
+            }
250
+        }
251
+
252
+        $phoneNumbers = array_keys($normalizedNumberToKey);
253
+
254
+        if (empty($phoneNumbers)) {
255
+            return new DataResponse();
256
+        }
257
+
258
+        // Cleanup all previous entries and only allow new matches
259
+        $this->knownUserService->deleteKnownTo($knownTo);
260
+
261
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
262
+
263
+        if (empty($userMatches)) {
264
+            return new DataResponse();
265
+        }
266
+
267
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
268
+        if (strpos($cloudUrl, 'http://') === 0) {
269
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
270
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
271
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
272
+        }
273
+
274
+        $matches = [];
275
+        foreach ($userMatches as $phone => $userId) {
276
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
277
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
278
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
279
+        }
280
+
281
+        return new DataResponse($matches);
282
+    }
283
+
284
+    /**
285
+     * @throws OCSException
286
+     */
287
+    private function createNewUserId(): string {
288
+        $attempts = 0;
289
+        do {
290
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
291
+            if (!$this->userManager->userExists($uidCandidate)) {
292
+                return $uidCandidate;
293
+            }
294
+            $attempts++;
295
+        } while ($attempts < 10);
296
+        throw new OCSException('Could not create non-existing user id', 111);
297
+    }
298
+
299
+    /**
300
+     * @PasswordConfirmationRequired
301
+     * @NoAdminRequired
302
+     *
303
+     * @param string $userid
304
+     * @param string $password
305
+     * @param string $displayName
306
+     * @param string $email
307
+     * @param array $groups
308
+     * @param array $subadmin
309
+     * @param string $quota
310
+     * @param string $language
311
+     * @return DataResponse
312
+     * @throws OCSException
313
+     */
314
+    public function addUser(string $userid,
315
+                            string $password = '',
316
+                            string $displayName = '',
317
+                            string $email = '',
318
+                            array $groups = [],
319
+                            array $subadmin = [],
320
+                            string $quota = '',
321
+                            string $language = ''): DataResponse {
322
+        $user = $this->userSession->getUser();
323
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
324
+        $subAdminManager = $this->groupManager->getSubAdmin();
325
+
326
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
327
+            $userid = $this->createNewUserId();
328
+        }
329
+
330
+        if ($this->userManager->userExists($userid)) {
331
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
332
+            throw new OCSException('User already exists', 102);
333
+        }
334
+
335
+        if ($groups !== []) {
336
+            foreach ($groups as $group) {
337
+                if (!$this->groupManager->groupExists($group)) {
338
+                    throw new OCSException('group '.$group.' does not exist', 104);
339
+                }
340
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
341
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
342
+                }
343
+            }
344
+        } else {
345
+            if (!$isAdmin) {
346
+                throw new OCSException('no group specified (required for subadmins)', 106);
347
+            }
348
+        }
349
+
350
+        $subadminGroups = [];
351
+        if ($subadmin !== []) {
352
+            foreach ($subadmin as $groupid) {
353
+                $group = $this->groupManager->get($groupid);
354
+                // Check if group exists
355
+                if ($group === null) {
356
+                    throw new OCSException('Subadmin group does not exist',  102);
357
+                }
358
+                // Check if trying to make subadmin of admin group
359
+                if ($group->getGID() === 'admin') {
360
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
361
+                }
362
+                // Check if has permission to promote subadmins
363
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
364
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
365
+                }
366
+                $subadminGroups[] = $group;
367
+            }
368
+        }
369
+
370
+        $generatePasswordResetToken = false;
371
+        if ($password === '') {
372
+            if ($email === '') {
373
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
374
+            }
375
+
376
+            $passwordEvent = new GenerateSecurePasswordEvent();
377
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
378
+
379
+            $password = $passwordEvent->getPassword();
380
+            if ($password === null) {
381
+                // Fallback: ensure to pass password_policy in any case
382
+                $password = $this->secureRandom->generate(10)
383
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
384
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
385
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
386
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
387
+            }
388
+            $generatePasswordResetToken = true;
389
+        }
390
+
391
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
392
+            throw new OCSException('Required email address was not provided', 110);
393
+        }
394
+
395
+        try {
396
+            $newUser = $this->userManager->createUser($userid, $password);
397
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
398
+
399
+            foreach ($groups as $group) {
400
+                $this->groupManager->get($group)->addUser($newUser);
401
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
402
+            }
403
+            foreach ($subadminGroups as $group) {
404
+                $subAdminManager->createSubAdmin($newUser, $group);
405
+            }
406
+
407
+            if ($displayName !== '') {
408
+                $this->editUser($userid, 'display', $displayName);
409
+            }
410
+
411
+            if ($quota !== '') {
412
+                $this->editUser($userid, 'quota', $quota);
413
+            }
414
+
415
+            if ($language !== '') {
416
+                $this->editUser($userid, 'language', $language);
417
+            }
418
+
419
+            // Send new user mail only if a mail is set
420
+            if ($email !== '') {
421
+                $newUser->setEMailAddress($email);
422
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
423
+                    try {
424
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
425
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
426
+                    } catch (\Exception $e) {
427
+                        // Mail could be failing hard or just be plain not configured
428
+                        // Logging error as it is the hardest of the two
429
+                        $this->logger->error("Unable to send the invitation mail to $email",
430
+                            [
431
+                                'app' => 'ocs_api',
432
+                                'exception' => $e,
433
+                            ]
434
+                        );
435
+                    }
436
+                }
437
+            }
438
+
439
+            return new DataResponse(['id' => $userid]);
440
+        } catch (HintException $e) {
441
+            $this->logger->warning('Failed addUser attempt with hint exception.',
442
+                [
443
+                    'app' => 'ocs_api',
444
+                    'exception' => $e,
445
+                ]
446
+            );
447
+            throw new OCSException($e->getHint(), 107);
448
+        } catch (OCSException $e) {
449
+            $this->logger->warning('Failed addUser attempt with ocs exeption.',
450
+                [
451
+                    'app' => 'ocs_api',
452
+                    'exception' => $e,
453
+                ]
454
+            );
455
+            throw $e;
456
+        } catch (\InvalidArgumentException $e) {
457
+            $this->logger->error('Failed addUser attempt with invalid argument exeption.',
458
+                [
459
+                    'app' => 'ocs_api',
460
+                    'exception' => $e,
461
+                ]
462
+            );
463
+            throw new OCSException($e->getMessage(), 101);
464
+        } catch (\Exception $e) {
465
+            $this->logger->error('Failed addUser attempt with exception.',
466
+                [
467
+                    'app' => 'ocs_api',
468
+                    'exception' => $e
469
+                ]
470
+            );
471
+            throw new OCSException('Bad request', 101);
472
+        }
473
+    }
474
+
475
+    /**
476
+     * @NoAdminRequired
477
+     * @NoSubAdminRequired
478
+     *
479
+     * gets user info
480
+     *
481
+     * @param string $userId
482
+     * @return DataResponse
483
+     * @throws OCSException
484
+     */
485
+    public function getUser(string $userId): DataResponse {
486
+        $includeScopes = false;
487
+        $currentUser = $this->userSession->getUser();
488
+        if ($currentUser && $currentUser->getUID() === $userId) {
489
+            $includeScopes = true;
490
+        }
491
+
492
+        $data = $this->getUserData($userId, $includeScopes);
493
+        // getUserData returns empty array if not enough permissions
494
+        if (empty($data)) {
495
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
496
+        }
497
+        return new DataResponse($data);
498
+    }
499
+
500
+    /**
501
+     * @NoAdminRequired
502
+     * @NoSubAdminRequired
503
+     *
504
+     * gets user info from the currently logged in user
505
+     *
506
+     * @return DataResponse
507
+     * @throws OCSException
508
+     */
509
+    public function getCurrentUser(): DataResponse {
510
+        $user = $this->userSession->getUser();
511
+        if ($user) {
512
+            $data = $this->getUserData($user->getUID(), true);
513
+            // rename "displayname" to "display-name" only for this call to keep
514
+            // the API stable.
515
+            $data['display-name'] = $data['displayname'];
516
+            unset($data['displayname']);
517
+            return new DataResponse($data);
518
+        }
519
+
520
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
521
+    }
522
+
523
+    /**
524
+     * @NoAdminRequired
525
+     * @NoSubAdminRequired
526
+     */
527
+    public function getEditableFields(): DataResponse {
528
+        $permittedFields = [];
529
+
530
+        // Editing self (display, email)
531
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
532
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
533
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
534
+        }
535
+
536
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
537
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
538
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
539
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
540
+
541
+        return new DataResponse($permittedFields);
542
+    }
543
+
544
+    /**
545
+     * @NoAdminRequired
546
+     * @NoSubAdminRequired
547
+     * @PasswordConfirmationRequired
548
+     *
549
+     * edit users
550
+     *
551
+     * @param string $userId
552
+     * @param string $key
553
+     * @param string $value
554
+     * @return DataResponse
555
+     * @throws OCSException
556
+     */
557
+    public function editUser(string $userId, string $key, string $value): DataResponse {
558
+        $currentLoggedInUser = $this->userSession->getUser();
559
+
560
+        $targetUser = $this->userManager->get($userId);
561
+        if ($targetUser === null) {
562
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
563
+        }
564
+
565
+        $permittedFields = [];
566
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
567
+            // Editing self (display, email)
568
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
569
+                $permittedFields[] = 'display';
570
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
571
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
572
+            }
573
+
574
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
575
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
576
+
577
+            $permittedFields[] = 'password';
578
+            if ($this->config->getSystemValue('force_language', false) === false ||
579
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
580
+                $permittedFields[] = 'language';
581
+            }
582
+
583
+            if ($this->config->getSystemValue('force_locale', false) === false ||
584
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
585
+                $permittedFields[] = 'locale';
586
+            }
587
+
588
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
589
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
590
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
591
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
592
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
593
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
594
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
595
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
596
+
597
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
598
+
599
+            // If admin they can edit their own quota
600
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
601
+                $permittedFields[] = 'quota';
602
+            }
603
+        } else {
604
+            // Check if admin / subadmin
605
+            $subAdminManager = $this->groupManager->getSubAdmin();
606
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
607
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
608
+                // They have permissions over the user
609
+                $permittedFields[] = 'display';
610
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
611
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
612
+                $permittedFields[] = 'password';
613
+                $permittedFields[] = 'language';
614
+                $permittedFields[] = 'locale';
615
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
616
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
617
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
618
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
619
+                $permittedFields[] = 'quota';
620
+            } else {
621
+                // No rights
622
+                throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
623
+            }
624
+        }
625
+        // Check if permitted to edit this field
626
+        if (!in_array($key, $permittedFields)) {
627
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
628
+        }
629
+        // Process the edit
630
+        switch ($key) {
631
+            case 'display':
632
+            case IAccountManager::PROPERTY_DISPLAYNAME:
633
+                $targetUser->setDisplayName($value);
634
+                break;
635
+            case 'quota':
636
+                $quota = $value;
637
+                if ($quota !== 'none' && $quota !== 'default') {
638
+                    if (is_numeric($quota)) {
639
+                        $quota = (float) $quota;
640
+                    } else {
641
+                        $quota = \OCP\Util::computerFileSize($quota);
642
+                    }
643
+                    if ($quota === false) {
644
+                        throw new OCSException('Invalid quota value '.$value, 103);
645
+                    }
646
+                    if ($quota === -1) {
647
+                        $quota = 'none';
648
+                    } else {
649
+                        $quota = \OCP\Util::humanFileSize($quota);
650
+                    }
651
+                }
652
+                $targetUser->setQuota($quota);
653
+                break;
654
+            case 'password':
655
+                try {
656
+                    if (!$targetUser->canChangePassword()) {
657
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
658
+                    }
659
+                    $targetUser->setPassword($value);
660
+                } catch (HintException $e) { // password policy error
661
+                    throw new OCSException($e->getMessage(), 103);
662
+                }
663
+                break;
664
+            case 'language':
665
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
666
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
667
+                    throw new OCSException('Invalid language', 102);
668
+                }
669
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
670
+                break;
671
+            case 'locale':
672
+                if (!$this->l10nFactory->localeExists($value)) {
673
+                    throw new OCSException('Invalid locale', 102);
674
+                }
675
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
676
+                break;
677
+            case IAccountManager::PROPERTY_EMAIL:
678
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
679
+                    $targetUser->setEMailAddress($value);
680
+                } else {
681
+                    throw new OCSException('', 102);
682
+                }
683
+                break;
684
+            case IAccountManager::PROPERTY_PHONE:
685
+            case IAccountManager::PROPERTY_ADDRESS:
686
+            case IAccountManager::PROPERTY_WEBSITE:
687
+            case IAccountManager::PROPERTY_TWITTER:
688
+                $userAccount = $this->accountManager->getUser($targetUser);
689
+                if ($userAccount[$key]['value'] !== $value) {
690
+                    $userAccount[$key]['value'] = $value;
691
+                    try {
692
+                        $this->accountManager->updateUser($targetUser, $userAccount, true);
693
+
694
+                        if ($key === IAccountManager::PROPERTY_PHONE) {
695
+                            $this->knownUserService->deleteByContactUserId($targetUser->getUID());
696
+                        }
697
+                    } catch (\InvalidArgumentException $e) {
698
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
699
+                    }
700
+                }
701
+                break;
702
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
703
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
704
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
705
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
706
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
707
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
708
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
709
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
710
+                $userAccount = $this->accountManager->getUser($targetUser);
711
+                if ($userAccount[$propertyName]['scope'] !== $value) {
712
+                    $userAccount[$propertyName]['scope'] = $value;
713
+                    try {
714
+                        $this->accountManager->updateUser($targetUser, $userAccount, true);
715
+                    } catch (\InvalidArgumentException $e) {
716
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
717
+                    }
718
+                }
719
+                break;
720
+            default:
721
+                throw new OCSException('', 103);
722
+        }
723
+        return new DataResponse();
724
+    }
725
+
726
+    /**
727
+     * @PasswordConfirmationRequired
728
+     * @NoAdminRequired
729
+     *
730
+     * @param string $userId
731
+     *
732
+     * @return DataResponse
733
+     *
734
+     * @throws OCSException
735
+     */
736
+    public function wipeUserDevices(string $userId): DataResponse {
737
+        /** @var IUser $currentLoggedInUser */
738
+        $currentLoggedInUser = $this->userSession->getUser();
739
+
740
+        $targetUser = $this->userManager->get($userId);
741
+
742
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
743
+            throw new OCSException('', 101);
744
+        }
745
+
746
+        // If not permitted
747
+        $subAdminManager = $this->groupManager->getSubAdmin();
748
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
749
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
750
+        }
751
+
752
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
753
+
754
+        return new DataResponse();
755
+    }
756
+
757
+    /**
758
+     * @PasswordConfirmationRequired
759
+     * @NoAdminRequired
760
+     *
761
+     * @param string $userId
762
+     * @return DataResponse
763
+     * @throws OCSException
764
+     */
765
+    public function deleteUser(string $userId): DataResponse {
766
+        $currentLoggedInUser = $this->userSession->getUser();
767
+
768
+        $targetUser = $this->userManager->get($userId);
769
+
770
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
771
+            throw new OCSException('', 101);
772
+        }
773
+
774
+        // If not permitted
775
+        $subAdminManager = $this->groupManager->getSubAdmin();
776
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
777
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
778
+        }
779
+
780
+        // Go ahead with the delete
781
+        if ($targetUser->delete()) {
782
+            return new DataResponse();
783
+        } else {
784
+            throw new OCSException('', 101);
785
+        }
786
+    }
787
+
788
+    /**
789
+     * @PasswordConfirmationRequired
790
+     * @NoAdminRequired
791
+     *
792
+     * @param string $userId
793
+     * @return DataResponse
794
+     * @throws OCSException
795
+     * @throws OCSForbiddenException
796
+     */
797
+    public function disableUser(string $userId): DataResponse {
798
+        return $this->setEnabled($userId, false);
799
+    }
800
+
801
+    /**
802
+     * @PasswordConfirmationRequired
803
+     * @NoAdminRequired
804
+     *
805
+     * @param string $userId
806
+     * @return DataResponse
807
+     * @throws OCSException
808
+     * @throws OCSForbiddenException
809
+     */
810
+    public function enableUser(string $userId): DataResponse {
811
+        return $this->setEnabled($userId, true);
812
+    }
813
+
814
+    /**
815
+     * @param string $userId
816
+     * @param bool $value
817
+     * @return DataResponse
818
+     * @throws OCSException
819
+     */
820
+    private function setEnabled(string $userId, bool $value): DataResponse {
821
+        $currentLoggedInUser = $this->userSession->getUser();
822
+
823
+        $targetUser = $this->userManager->get($userId);
824
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
825
+            throw new OCSException('', 101);
826
+        }
827
+
828
+        // If not permitted
829
+        $subAdminManager = $this->groupManager->getSubAdmin();
830
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
831
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
832
+        }
833
+
834
+        // enable/disable the user now
835
+        $targetUser->setEnabled($value);
836
+        return new DataResponse();
837
+    }
838
+
839
+    /**
840
+     * @NoAdminRequired
841
+     * @NoSubAdminRequired
842
+     *
843
+     * @param string $userId
844
+     * @return DataResponse
845
+     * @throws OCSException
846
+     */
847
+    public function getUsersGroups(string $userId): DataResponse {
848
+        $loggedInUser = $this->userSession->getUser();
849
+
850
+        $targetUser = $this->userManager->get($userId);
851
+        if ($targetUser === null) {
852
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
853
+        }
854
+
855
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
856
+            // Self lookup or admin lookup
857
+            return new DataResponse([
858
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
859
+            ]);
860
+        } else {
861
+            $subAdminManager = $this->groupManager->getSubAdmin();
862
+
863
+            // Looking up someone else
864
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
865
+                // Return the group that the method caller is subadmin of for the user in question
866
+                /** @var IGroup[] $getSubAdminsGroups */
867
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
868
+                foreach ($getSubAdminsGroups as $key => $group) {
869
+                    $getSubAdminsGroups[$key] = $group->getGID();
870
+                }
871
+                $groups = array_intersect(
872
+                    $getSubAdminsGroups,
873
+                    $this->groupManager->getUserGroupIds($targetUser)
874
+                );
875
+                return new DataResponse(['groups' => $groups]);
876
+            } else {
877
+                // Not permitted
878
+                throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
879
+            }
880
+        }
881
+    }
882
+
883
+    /**
884
+     * @PasswordConfirmationRequired
885
+     * @NoAdminRequired
886
+     *
887
+     * @param string $userId
888
+     * @param string $groupid
889
+     * @return DataResponse
890
+     * @throws OCSException
891
+     */
892
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
893
+        if ($groupid === '') {
894
+            throw new OCSException('', 101);
895
+        }
896
+
897
+        $group = $this->groupManager->get($groupid);
898
+        $targetUser = $this->userManager->get($userId);
899
+        if ($group === null) {
900
+            throw new OCSException('', 102);
901
+        }
902
+        if ($targetUser === null) {
903
+            throw new OCSException('', 103);
904
+        }
905
+
906
+        // If they're not an admin, check they are a subadmin of the group in question
907
+        $loggedInUser = $this->userSession->getUser();
908
+        $subAdminManager = $this->groupManager->getSubAdmin();
909
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
910
+            throw new OCSException('', 104);
911
+        }
912
+
913
+        // Add user to group
914
+        $group->addUser($targetUser);
915
+        return new DataResponse();
916
+    }
917
+
918
+    /**
919
+     * @PasswordConfirmationRequired
920
+     * @NoAdminRequired
921
+     *
922
+     * @param string $userId
923
+     * @param string $groupid
924
+     * @return DataResponse
925
+     * @throws OCSException
926
+     */
927
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
928
+        $loggedInUser = $this->userSession->getUser();
929
+
930
+        if ($groupid === null || trim($groupid) === '') {
931
+            throw new OCSException('', 101);
932
+        }
933
+
934
+        $group = $this->groupManager->get($groupid);
935
+        if ($group === null) {
936
+            throw new OCSException('', 102);
937
+        }
938
+
939
+        $targetUser = $this->userManager->get($userId);
940
+        if ($targetUser === null) {
941
+            throw new OCSException('', 103);
942
+        }
943
+
944
+        // If they're not an admin, check they are a subadmin of the group in question
945
+        $subAdminManager = $this->groupManager->getSubAdmin();
946
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
947
+            throw new OCSException('', 104);
948
+        }
949
+
950
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
951
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
952
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
953
+                if ($group->getGID() === 'admin') {
954
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
955
+                }
956
+            } else {
957
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
958
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
959
+            }
960
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
961
+            /** @var IGroup[] $subAdminGroups */
962
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
963
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
964
+                return $subAdminGroup->getGID();
965
+            }, $subAdminGroups);
966
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
967
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
968
+
969
+            if (count($userSubAdminGroups) <= 1) {
970
+                // Subadmin must not be able to remove a user from all their subadmin groups.
971
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
972
+            }
973
+        }
974
+
975
+        // Remove user from group
976
+        $group->removeUser($targetUser);
977
+        return new DataResponse();
978
+    }
979
+
980
+    /**
981
+     * Creates a subadmin
982
+     *
983
+     * @PasswordConfirmationRequired
984
+     *
985
+     * @param string $userId
986
+     * @param string $groupid
987
+     * @return DataResponse
988
+     * @throws OCSException
989
+     */
990
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
991
+        $group = $this->groupManager->get($groupid);
992
+        $user = $this->userManager->get($userId);
993
+
994
+        // Check if the user exists
995
+        if ($user === null) {
996
+            throw new OCSException('User does not exist', 101);
997
+        }
998
+        // Check if group exists
999
+        if ($group === null) {
1000
+            throw new OCSException('Group does not exist',  102);
1001
+        }
1002
+        // Check if trying to make subadmin of admin group
1003
+        if ($group->getGID() === 'admin') {
1004
+            throw new OCSException('Cannot create subadmins for admin group', 103);
1005
+        }
1006
+
1007
+        $subAdminManager = $this->groupManager->getSubAdmin();
1008
+
1009
+        // We cannot be subadmin twice
1010
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1011
+            return new DataResponse();
1012
+        }
1013
+        // Go
1014
+        $subAdminManager->createSubAdmin($user, $group);
1015
+        return new DataResponse();
1016
+    }
1017
+
1018
+    /**
1019
+     * Removes a subadmin from a group
1020
+     *
1021
+     * @PasswordConfirmationRequired
1022
+     *
1023
+     * @param string $userId
1024
+     * @param string $groupid
1025
+     * @return DataResponse
1026
+     * @throws OCSException
1027
+     */
1028
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1029
+        $group = $this->groupManager->get($groupid);
1030
+        $user = $this->userManager->get($userId);
1031
+        $subAdminManager = $this->groupManager->getSubAdmin();
1032
+
1033
+        // Check if the user exists
1034
+        if ($user === null) {
1035
+            throw new OCSException('User does not exist', 101);
1036
+        }
1037
+        // Check if the group exists
1038
+        if ($group === null) {
1039
+            throw new OCSException('Group does not exist', 101);
1040
+        }
1041
+        // Check if they are a subadmin of this said group
1042
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1043
+            throw new OCSException('User is not a subadmin of this group', 102);
1044
+        }
1045
+
1046
+        // Go
1047
+        $subAdminManager->deleteSubAdmin($user, $group);
1048
+        return new DataResponse();
1049
+    }
1050
+
1051
+    /**
1052
+     * Get the groups a user is a subadmin of
1053
+     *
1054
+     * @param string $userId
1055
+     * @return DataResponse
1056
+     * @throws OCSException
1057
+     */
1058
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1059
+        $groups = $this->getUserSubAdminGroupsData($userId);
1060
+        return new DataResponse($groups);
1061
+    }
1062
+
1063
+    /**
1064
+     * @NoAdminRequired
1065
+     * @PasswordConfirmationRequired
1066
+     *
1067
+     * resend welcome message
1068
+     *
1069
+     * @param string $userId
1070
+     * @return DataResponse
1071
+     * @throws OCSException
1072
+     */
1073
+    public function resendWelcomeMessage(string $userId): DataResponse {
1074
+        $currentLoggedInUser = $this->userSession->getUser();
1075
+
1076
+        $targetUser = $this->userManager->get($userId);
1077
+        if ($targetUser === null) {
1078
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1079
+        }
1080
+
1081
+        // Check if admin / subadmin
1082
+        $subAdminManager = $this->groupManager->getSubAdmin();
1083
+        if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1084
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1085
+            // No rights
1086
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
1087
+        }
1088
+
1089
+        $email = $targetUser->getEMailAddress();
1090
+        if ($email === '' || $email === null) {
1091
+            throw new OCSException('Email address not available', 101);
1092
+        }
1093
+
1094
+        try {
1095
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1096
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1097
+        } catch (\Exception $e) {
1098
+            $this->logger->error("Can't send new user mail to $email",
1099
+                [
1100
+                    'app' => 'settings',
1101
+                    'exception' => $e,
1102
+                ]
1103
+            );
1104
+            throw new OCSException('Sending email failed', 102);
1105
+        }
1106
+
1107
+        return new DataResponse();
1108
+    }
1109 1109
 }
Please login to merge, or discard this patch.