Completed
Push — master ( e2ea6d...b17745 )
by
unknown
30:23
created
lib/public/Profile/BeforeTemplateRenderedEvent.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -34,21 +34,21 @@
 block discarded – undo
34 34
  * @since 25.0.0
35 35
  */
36 36
 class BeforeTemplateRenderedEvent extends Event {
37
-	private string $userId;
37
+    private string $userId;
38 38
 
39
-	/**
40
-	 * @since 25.0.0
41
-	 */
42
-	public function __construct(string $userId) {
43
-		parent::__construct();
39
+    /**
40
+     * @since 25.0.0
41
+     */
42
+    public function __construct(string $userId) {
43
+        parent::__construct();
44 44
 
45
-		$this->userId = $userId;
46
-	}
45
+        $this->userId = $userId;
46
+    }
47 47
 
48
-	/**
49
-	 * @since 25.0.0
50
-	 */
51
-	public function getUserId(): string {
52
-		return $this->userId;
53
-	}
48
+    /**
49
+     * @since 25.0.0
50
+     */
51
+    public function getUserId(): string {
52
+        return $this->userId;
53
+    }
54 54
 }
Please login to merge, or discard this patch.
apps/settings/templates/settings/personal/personal.info.php 2 patches
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -27,12 +27,12 @@
 block discarded – undo
27 27
 /** @var array $_ */
28 28
 
29 29
 script('settings', [
30
-	'usersettings',
31
-	'templates',
32
-	'federationsettingsview',
33
-	'federationscopemenu',
34
-	'settings/personalInfo',
35
-	'vue-settings-personal-info',
30
+    'usersettings',
31
+    'templates',
32
+    'federationsettingsview',
33
+    'federationscopemenu',
34
+    'settings/personalInfo',
35
+    'vue-settings-personal-info',
36 36
 ]);
37 37
 ?>
38 38
 <?php if (!$_['isFairUseOfFreePushService']) : ?>
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -53,11 +53,14 @@
 block discarded – undo
53 53
 		<div class="personal-settings-setting-box personal-settings-setting-box-detail">
54 54
 			<div id="vue-details-section"></div>
55 55
 		</div>
56
-	<?php else: ?>
56
+	<?php else {
57
+    : ?>
57 58
 		<div class="personal-settings-setting-box personal-settings-setting-box-detail--without-profile">
58 59
 			<div id="vue-details-section"></div>
59 60
 		</div>
60
-	<?php endif; ?>
61
+	<?php endif;
62
+}
63
+?>
61 64
 	<div class="personal-settings-setting-box">
62 65
 		<div id="vue-displayname-section"></div>
63 66
 	</div>
Please login to merge, or discard this patch.
lib/public/Dashboard/IOptionWidget.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -30,9 +30,9 @@
 block discarded – undo
30 30
  * @since 25.0.0
31 31
  */
32 32
 interface IOptionWidget extends IWidget {
33
-	/**
34
-	 * Get additional options for the widget
35
-	 * @since 25.0.0
36
-	 */
37
-	public function getWidgetOptions(): WidgetOptions;
33
+    /**
34
+     * Get additional options for the widget
35
+     * @since 25.0.0
36
+     */
37
+    public function getWidgetOptions(): WidgetOptions;
38 38
 }
Please login to merge, or discard this patch.
lib/public/Dashboard/Model/WidgetOptions.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -29,33 +29,33 @@
 block discarded – undo
29 29
  * @since 25.0.0
30 30
  */
31 31
 class WidgetOptions {
32
-	private bool $roundItemIcons;
32
+    private bool $roundItemIcons;
33 33
 
34
-	/**
35
-	 * @param bool $roundItemIcons
36
-	 * @since 25.0.0
37
-	 */
38
-	public function __construct(bool $roundItemIcons) {
39
-		$this->roundItemIcons = $roundItemIcons;
40
-	}
34
+    /**
35
+     * @param bool $roundItemIcons
36
+     * @since 25.0.0
37
+     */
38
+    public function __construct(bool $roundItemIcons) {
39
+        $this->roundItemIcons = $roundItemIcons;
40
+    }
41 41
 
42
-	/**
43
-	 * Get the default set of options
44
-	 *
45
-	 * @return WidgetOptions
46
-	 * @since 25.0.0
47
-	 */
48
-	public static function getDefault(): WidgetOptions {
49
-		return new WidgetOptions(false);
50
-	}
42
+    /**
43
+     * Get the default set of options
44
+     *
45
+     * @return WidgetOptions
46
+     * @since 25.0.0
47
+     */
48
+    public static function getDefault(): WidgetOptions {
49
+        return new WidgetOptions(false);
50
+    }
51 51
 
52
-	/**
53
-	 * Whether the clients should render icons for widget items as round icons
54
-	 *
55
-	 * @return bool
56
-	 * @since 25.0.0
57
-	 */
58
-	public function withRoundItemIcons(): bool {
59
-		return $this->roundItemIcons;
60
-	}
52
+    /**
53
+     * Whether the clients should render icons for widget items as round icons
54
+     *
55
+     * @return bool
56
+     * @since 25.0.0
57
+     */
58
+    public function withRoundItemIcons(): bool {
59
+        return $this->roundItemIcons;
60
+    }
61 61
 }
Please login to merge, or discard this patch.
apps/settings/lib/Settings/Personal/PersonalInfo.php 2 patches
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
 	 */
235 235
 	private function getGroups(IUser $user): array {
236 236
 		$groups = array_map(
237
-			static function (IGroup $group) {
237
+			static function(IGroup $group) {
238 238
 				return $group->getDisplayName();
239 239
 			},
240 240
 			$this->groupManager->getUserGroups($user)
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
 		];
258 258
 
259 259
 		$additionalEmails = array_map(
260
-			function (IAccountProperty $property) {
260
+			function(IAccountProperty $property) {
261 261
 				return [
262 262
 					'name' => $property->getName(),
263 263
 					'value' => $property->getValue(),
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 		$emailMap = [
273 273
 			'primaryEmail' => $systemEmail,
274 274
 			'additionalEmails' => $additionalEmails,
275
-			'notificationEmail' => (string)$account->getUser()->getPrimaryEMailAddress(),
275
+			'notificationEmail' => (string) $account->getUser()->getPrimaryEMailAddress(),
276 276
 		];
277 277
 
278 278
 		return $emailMap;
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
 				default:
367 367
 					$message = $this->l->t('Verify');
368 368
 			}
369
-			$messageParameters[$property . 'Message'] = $message;
369
+			$messageParameters[$property.'Message'] = $message;
370 370
 		}
371 371
 		return $messageParameters;
372 372
 	}
Please login to merge, or discard this patch.
Indentation   +285 added lines, -285 removed lines patch added patch discarded remove patch
@@ -33,289 +33,289 @@
 block discarded – undo
33 33
 
34 34
 class PersonalInfo implements ISettings {
35 35
 
36
-	/** @var ProfileManager */
37
-	private $profileManager;
38
-
39
-	public function __construct(
40
-		private IConfig $config,
41
-		private IUserManager $userManager,
42
-		private IGroupManager $groupManager,
43
-		private IAccountManager $accountManager,
44
-		ProfileManager $profileManager,
45
-		private IAppManager $appManager,
46
-		private IFactory $l10nFactory,
47
-		private IL10N $l,
48
-		private IInitialState $initialStateService,
49
-		private IManager $manager,
50
-	) {
51
-		$this->profileManager = $profileManager;
52
-	}
53
-
54
-	public function getForm(): TemplateResponse {
55
-		$federationEnabled = $this->appManager->isEnabledForUser('federation');
56
-		$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
57
-		$lookupServerUploadEnabled = false;
58
-		if ($federatedFileSharingEnabled) {
59
-			/** @var FederatedShareProvider $shareProvider */
60
-			$shareProvider = Server::get(FederatedShareProvider::class);
61
-			$lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
62
-		}
63
-
64
-		$uid = \OC_User::getUser();
65
-		$user = $this->userManager->get($uid);
66
-		$account = $this->accountManager->getAccount($user);
67
-
68
-		// make sure FS is setup before querying storage related stuff...
69
-		\OC_Util::setupFS($user->getUID());
70
-
71
-		$storageInfo = \OC_Helper::getStorageInfo('/');
72
-		if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
73
-			$totalSpace = $this->l->t('Unlimited');
74
-		} else {
75
-			$totalSpace = Util::humanFileSize($storageInfo['total']);
76
-		}
77
-
78
-		$messageParameters = $this->getMessageParameters($account);
79
-
80
-		$parameters = [
81
-			'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
82
-			'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(),
83
-			'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(),
84
-		] + $messageParameters;
85
-
86
-		$personalInfoParameters = [
87
-			'userId' => $uid,
88
-			'avatar' => $this->getProperty($account, IAccountManager::PROPERTY_AVATAR),
89
-			'groups' => $this->getGroups($user),
90
-			'quota' => $storageInfo['quota'],
91
-			'totalSpace' => $totalSpace,
92
-			'usage' => Util::humanFileSize($storageInfo['used']),
93
-			'usageRelative' => round($storageInfo['relative']),
94
-			'displayName' => $this->getProperty($account, IAccountManager::PROPERTY_DISPLAYNAME),
95
-			'emailMap' => $this->getEmailMap($account),
96
-			'phone' => $this->getProperty($account, IAccountManager::PROPERTY_PHONE),
97
-			'defaultPhoneRegion' => $this->config->getSystemValueString('default_phone_region'),
98
-			'location' => $this->getProperty($account, IAccountManager::PROPERTY_ADDRESS),
99
-			'website' => $this->getProperty($account, IAccountManager::PROPERTY_WEBSITE),
100
-			'twitter' => $this->getProperty($account, IAccountManager::PROPERTY_TWITTER),
101
-			'bluesky' => $this->getProperty($account, IAccountManager::PROPERTY_BLUESKY),
102
-			'fediverse' => $this->getProperty($account, IAccountManager::PROPERTY_FEDIVERSE),
103
-			'languageMap' => $this->getLanguageMap($user),
104
-			'localeMap' => $this->getLocaleMap($user),
105
-			'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(),
106
-			'profileEnabled' => $this->profileManager->isProfileEnabled($user),
107
-			'organisation' => $this->getProperty($account, IAccountManager::PROPERTY_ORGANISATION),
108
-			'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE),
109
-			'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE),
110
-			'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY),
111
-			'birthdate' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDATE),
112
-			'firstDayOfWeek' => $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK),
113
-			'timezone' => $this->config->getUserValue($uid, 'core', 'timezone', ''),
114
-			'pronouns' => $this->getProperty($account, IAccountManager::PROPERTY_PRONOUNS),
115
-		];
116
-
117
-		$accountParameters = [
118
-			'avatarChangeSupported' => $user->canChangeAvatar(),
119
-			'displayNameChangeSupported' => $user->canChangeDisplayName(),
120
-			'emailChangeSupported' => $user->canChangeEmail(),
121
-			'federationEnabled' => $federationEnabled,
122
-			'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
123
-		];
124
-
125
-		$profileParameters = [
126
-			'profileConfig' => $this->profileManager->getProfileConfigWithMetadata($user, $user),
127
-		];
128
-
129
-		$this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled());
130
-		$this->initialStateService->provideInitialState('personalInfoParameters', $personalInfoParameters);
131
-		$this->initialStateService->provideInitialState('accountParameters', $accountParameters);
132
-		$this->initialStateService->provideInitialState('profileParameters', $profileParameters);
133
-
134
-		return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
135
-	}
136
-
137
-	/**
138
-	 * Check if is fair use of free push service
139
-	 * @return boolean
140
-	 */
141
-	private function isFairUseOfFreePushService(): bool {
142
-		return $this->manager->isFairUseOfFreePushService();
143
-	}
144
-
145
-	/**
146
-	 * returns the property data in an
147
-	 * associative array
148
-	 */
149
-	private function getProperty(IAccount $account, string $property): array {
150
-		$property = [
151
-			'name' => $account->getProperty($property)->getName(),
152
-			'value' => $account->getProperty($property)->getValue(),
153
-			'scope' => $account->getProperty($property)->getScope(),
154
-			'verified' => $account->getProperty($property)->getVerified(),
155
-		];
156
-
157
-		return $property;
158
-	}
159
-
160
-	/**
161
-	 * returns the section ID string, e.g. 'sharing'
162
-	 * @since 9.1
163
-	 */
164
-	public function getSection(): string {
165
-		return 'personal-info';
166
-	}
167
-
168
-	/**
169
-	 * @return int whether the form should be rather on the top or bottom of
170
-	 *             the admin section. The forms are arranged in ascending order of the
171
-	 *             priority values. It is required to return a value between 0 and 100.
172
-	 *
173
-	 * E.g.: 70
174
-	 * @since 9.1
175
-	 */
176
-	public function getPriority(): int {
177
-		return 10;
178
-	}
179
-
180
-	/**
181
-	 * returns a sorted list of the user's group GIDs
182
-	 */
183
-	private function getGroups(IUser $user): array {
184
-		$groups = array_map(
185
-			static function (IGroup $group) {
186
-				return $group->getDisplayName();
187
-			},
188
-			$this->groupManager->getUserGroups($user)
189
-		);
190
-		sort($groups);
191
-
192
-		return $groups;
193
-	}
194
-
195
-	/**
196
-	 * returns the primary email and additional emails in an
197
-	 * associative array
198
-	 */
199
-	private function getEmailMap(IAccount $account): array {
200
-		$systemEmail = [
201
-			'name' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getName(),
202
-			'value' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
203
-			'scope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
204
-			'verified' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
205
-		];
206
-
207
-		$additionalEmails = array_map(
208
-			function (IAccountProperty $property) {
209
-				return [
210
-					'name' => $property->getName(),
211
-					'value' => $property->getValue(),
212
-					'scope' => $property->getScope(),
213
-					'verified' => $property->getVerified(),
214
-					'locallyVerified' => $property->getLocallyVerified(),
215
-				];
216
-			},
217
-			$account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties(),
218
-		);
219
-
220
-		$emailMap = [
221
-			'primaryEmail' => $systemEmail,
222
-			'additionalEmails' => $additionalEmails,
223
-			'notificationEmail' => (string)$account->getUser()->getPrimaryEMailAddress(),
224
-		];
225
-
226
-		return $emailMap;
227
-	}
228
-
229
-	/**
230
-	 * returns the user's active language, common languages, and other languages in an
231
-	 * associative array
232
-	 */
233
-	private function getLanguageMap(IUser $user): array {
234
-		$forceLanguage = $this->config->getSystemValue('force_language', false);
235
-		if ($forceLanguage !== false) {
236
-			return [];
237
-		}
238
-
239
-		$uid = $user->getUID();
240
-
241
-		$userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
242
-		$languages = $this->l10nFactory->getLanguages();
243
-
244
-		// associate the user language with the proper array
245
-		$userLangIndex = array_search($userConfLang, array_column($languages['commonLanguages'], 'code'));
246
-		$userLang = $languages['commonLanguages'][$userLangIndex];
247
-		// search in the other languages
248
-		if ($userLangIndex === false) {
249
-			$userLangIndex = array_search($userConfLang, array_column($languages['otherLanguages'], 'code'));
250
-			$userLang = $languages['otherLanguages'][$userLangIndex];
251
-		}
252
-		// if user language is not available but set somehow: show the actual code as name
253
-		if (!is_array($userLang)) {
254
-			$userLang = [
255
-				'code' => $userConfLang,
256
-				'name' => $userConfLang,
257
-			];
258
-		}
259
-
260
-		return array_merge(
261
-			['activeLanguage' => $userLang],
262
-			$languages
263
-		);
264
-	}
265
-
266
-	private function getLocaleMap(IUser $user): array {
267
-		$forceLanguage = $this->config->getSystemValue('force_locale', false);
268
-		if ($forceLanguage !== false) {
269
-			return [];
270
-		}
271
-
272
-		$uid = $user->getUID();
273
-		$userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
274
-		$userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale($userLang));
275
-		$localeCodes = $this->l10nFactory->findAvailableLocales();
276
-		$userLocale = array_filter($localeCodes, fn ($value) => $userLocaleString === $value['code']);
277
-
278
-		if (!empty($userLocale)) {
279
-			$userLocale = reset($userLocale);
280
-		}
281
-
282
-		$localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => str_starts_with($localeCode['code'], $userLang)));
283
-		$otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => !str_starts_with($localeCode['code'], $userLang)));
284
-
285
-		if (!$userLocale) {
286
-			$userLocale = [
287
-				'code' => 'en',
288
-				'name' => 'English'
289
-			];
290
-		}
291
-
292
-		return [
293
-			'activeLocaleLang' => $userLocaleString,
294
-			'activeLocale' => $userLocale,
295
-			'localesForLanguage' => $localesForLanguage,
296
-			'otherLocales' => $otherLocales,
297
-		];
298
-	}
299
-
300
-	/**
301
-	 * returns the message parameters
302
-	 */
303
-	private function getMessageParameters(IAccount $account): array {
304
-		$needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
305
-		$messageParameters = [];
306
-		foreach ($needVerifyMessage as $property) {
307
-			switch ($account->getProperty($property)->getVerified()) {
308
-				case IAccountManager::VERIFIED:
309
-					$message = $this->l->t('Verifying');
310
-					break;
311
-				case IAccountManager::VERIFICATION_IN_PROGRESS:
312
-					$message = $this->l->t('Verifying …');
313
-					break;
314
-				default:
315
-					$message = $this->l->t('Verify');
316
-			}
317
-			$messageParameters[$property . 'Message'] = $message;
318
-		}
319
-		return $messageParameters;
320
-	}
36
+    /** @var ProfileManager */
37
+    private $profileManager;
38
+
39
+    public function __construct(
40
+        private IConfig $config,
41
+        private IUserManager $userManager,
42
+        private IGroupManager $groupManager,
43
+        private IAccountManager $accountManager,
44
+        ProfileManager $profileManager,
45
+        private IAppManager $appManager,
46
+        private IFactory $l10nFactory,
47
+        private IL10N $l,
48
+        private IInitialState $initialStateService,
49
+        private IManager $manager,
50
+    ) {
51
+        $this->profileManager = $profileManager;
52
+    }
53
+
54
+    public function getForm(): TemplateResponse {
55
+        $federationEnabled = $this->appManager->isEnabledForUser('federation');
56
+        $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
57
+        $lookupServerUploadEnabled = false;
58
+        if ($federatedFileSharingEnabled) {
59
+            /** @var FederatedShareProvider $shareProvider */
60
+            $shareProvider = Server::get(FederatedShareProvider::class);
61
+            $lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
62
+        }
63
+
64
+        $uid = \OC_User::getUser();
65
+        $user = $this->userManager->get($uid);
66
+        $account = $this->accountManager->getAccount($user);
67
+
68
+        // make sure FS is setup before querying storage related stuff...
69
+        \OC_Util::setupFS($user->getUID());
70
+
71
+        $storageInfo = \OC_Helper::getStorageInfo('/');
72
+        if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
73
+            $totalSpace = $this->l->t('Unlimited');
74
+        } else {
75
+            $totalSpace = Util::humanFileSize($storageInfo['total']);
76
+        }
77
+
78
+        $messageParameters = $this->getMessageParameters($account);
79
+
80
+        $parameters = [
81
+            'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
82
+            'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(),
83
+            'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(),
84
+        ] + $messageParameters;
85
+
86
+        $personalInfoParameters = [
87
+            'userId' => $uid,
88
+            'avatar' => $this->getProperty($account, IAccountManager::PROPERTY_AVATAR),
89
+            'groups' => $this->getGroups($user),
90
+            'quota' => $storageInfo['quota'],
91
+            'totalSpace' => $totalSpace,
92
+            'usage' => Util::humanFileSize($storageInfo['used']),
93
+            'usageRelative' => round($storageInfo['relative']),
94
+            'displayName' => $this->getProperty($account, IAccountManager::PROPERTY_DISPLAYNAME),
95
+            'emailMap' => $this->getEmailMap($account),
96
+            'phone' => $this->getProperty($account, IAccountManager::PROPERTY_PHONE),
97
+            'defaultPhoneRegion' => $this->config->getSystemValueString('default_phone_region'),
98
+            'location' => $this->getProperty($account, IAccountManager::PROPERTY_ADDRESS),
99
+            'website' => $this->getProperty($account, IAccountManager::PROPERTY_WEBSITE),
100
+            'twitter' => $this->getProperty($account, IAccountManager::PROPERTY_TWITTER),
101
+            'bluesky' => $this->getProperty($account, IAccountManager::PROPERTY_BLUESKY),
102
+            'fediverse' => $this->getProperty($account, IAccountManager::PROPERTY_FEDIVERSE),
103
+            'languageMap' => $this->getLanguageMap($user),
104
+            'localeMap' => $this->getLocaleMap($user),
105
+            'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(),
106
+            'profileEnabled' => $this->profileManager->isProfileEnabled($user),
107
+            'organisation' => $this->getProperty($account, IAccountManager::PROPERTY_ORGANISATION),
108
+            'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE),
109
+            'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE),
110
+            'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY),
111
+            'birthdate' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDATE),
112
+            'firstDayOfWeek' => $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK),
113
+            'timezone' => $this->config->getUserValue($uid, 'core', 'timezone', ''),
114
+            'pronouns' => $this->getProperty($account, IAccountManager::PROPERTY_PRONOUNS),
115
+        ];
116
+
117
+        $accountParameters = [
118
+            'avatarChangeSupported' => $user->canChangeAvatar(),
119
+            'displayNameChangeSupported' => $user->canChangeDisplayName(),
120
+            'emailChangeSupported' => $user->canChangeEmail(),
121
+            'federationEnabled' => $federationEnabled,
122
+            'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
123
+        ];
124
+
125
+        $profileParameters = [
126
+            'profileConfig' => $this->profileManager->getProfileConfigWithMetadata($user, $user),
127
+        ];
128
+
129
+        $this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled());
130
+        $this->initialStateService->provideInitialState('personalInfoParameters', $personalInfoParameters);
131
+        $this->initialStateService->provideInitialState('accountParameters', $accountParameters);
132
+        $this->initialStateService->provideInitialState('profileParameters', $profileParameters);
133
+
134
+        return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
135
+    }
136
+
137
+    /**
138
+     * Check if is fair use of free push service
139
+     * @return boolean
140
+     */
141
+    private function isFairUseOfFreePushService(): bool {
142
+        return $this->manager->isFairUseOfFreePushService();
143
+    }
144
+
145
+    /**
146
+     * returns the property data in an
147
+     * associative array
148
+     */
149
+    private function getProperty(IAccount $account, string $property): array {
150
+        $property = [
151
+            'name' => $account->getProperty($property)->getName(),
152
+            'value' => $account->getProperty($property)->getValue(),
153
+            'scope' => $account->getProperty($property)->getScope(),
154
+            'verified' => $account->getProperty($property)->getVerified(),
155
+        ];
156
+
157
+        return $property;
158
+    }
159
+
160
+    /**
161
+     * returns the section ID string, e.g. 'sharing'
162
+     * @since 9.1
163
+     */
164
+    public function getSection(): string {
165
+        return 'personal-info';
166
+    }
167
+
168
+    /**
169
+     * @return int whether the form should be rather on the top or bottom of
170
+     *             the admin section. The forms are arranged in ascending order of the
171
+     *             priority values. It is required to return a value between 0 and 100.
172
+     *
173
+     * E.g.: 70
174
+     * @since 9.1
175
+     */
176
+    public function getPriority(): int {
177
+        return 10;
178
+    }
179
+
180
+    /**
181
+     * returns a sorted list of the user's group GIDs
182
+     */
183
+    private function getGroups(IUser $user): array {
184
+        $groups = array_map(
185
+            static function (IGroup $group) {
186
+                return $group->getDisplayName();
187
+            },
188
+            $this->groupManager->getUserGroups($user)
189
+        );
190
+        sort($groups);
191
+
192
+        return $groups;
193
+    }
194
+
195
+    /**
196
+     * returns the primary email and additional emails in an
197
+     * associative array
198
+     */
199
+    private function getEmailMap(IAccount $account): array {
200
+        $systemEmail = [
201
+            'name' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getName(),
202
+            'value' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
203
+            'scope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
204
+            'verified' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
205
+        ];
206
+
207
+        $additionalEmails = array_map(
208
+            function (IAccountProperty $property) {
209
+                return [
210
+                    'name' => $property->getName(),
211
+                    'value' => $property->getValue(),
212
+                    'scope' => $property->getScope(),
213
+                    'verified' => $property->getVerified(),
214
+                    'locallyVerified' => $property->getLocallyVerified(),
215
+                ];
216
+            },
217
+            $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties(),
218
+        );
219
+
220
+        $emailMap = [
221
+            'primaryEmail' => $systemEmail,
222
+            'additionalEmails' => $additionalEmails,
223
+            'notificationEmail' => (string)$account->getUser()->getPrimaryEMailAddress(),
224
+        ];
225
+
226
+        return $emailMap;
227
+    }
228
+
229
+    /**
230
+     * returns the user's active language, common languages, and other languages in an
231
+     * associative array
232
+     */
233
+    private function getLanguageMap(IUser $user): array {
234
+        $forceLanguage = $this->config->getSystemValue('force_language', false);
235
+        if ($forceLanguage !== false) {
236
+            return [];
237
+        }
238
+
239
+        $uid = $user->getUID();
240
+
241
+        $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
242
+        $languages = $this->l10nFactory->getLanguages();
243
+
244
+        // associate the user language with the proper array
245
+        $userLangIndex = array_search($userConfLang, array_column($languages['commonLanguages'], 'code'));
246
+        $userLang = $languages['commonLanguages'][$userLangIndex];
247
+        // search in the other languages
248
+        if ($userLangIndex === false) {
249
+            $userLangIndex = array_search($userConfLang, array_column($languages['otherLanguages'], 'code'));
250
+            $userLang = $languages['otherLanguages'][$userLangIndex];
251
+        }
252
+        // if user language is not available but set somehow: show the actual code as name
253
+        if (!is_array($userLang)) {
254
+            $userLang = [
255
+                'code' => $userConfLang,
256
+                'name' => $userConfLang,
257
+            ];
258
+        }
259
+
260
+        return array_merge(
261
+            ['activeLanguage' => $userLang],
262
+            $languages
263
+        );
264
+    }
265
+
266
+    private function getLocaleMap(IUser $user): array {
267
+        $forceLanguage = $this->config->getSystemValue('force_locale', false);
268
+        if ($forceLanguage !== false) {
269
+            return [];
270
+        }
271
+
272
+        $uid = $user->getUID();
273
+        $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
274
+        $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale($userLang));
275
+        $localeCodes = $this->l10nFactory->findAvailableLocales();
276
+        $userLocale = array_filter($localeCodes, fn ($value) => $userLocaleString === $value['code']);
277
+
278
+        if (!empty($userLocale)) {
279
+            $userLocale = reset($userLocale);
280
+        }
281
+
282
+        $localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => str_starts_with($localeCode['code'], $userLang)));
283
+        $otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => !str_starts_with($localeCode['code'], $userLang)));
284
+
285
+        if (!$userLocale) {
286
+            $userLocale = [
287
+                'code' => 'en',
288
+                'name' => 'English'
289
+            ];
290
+        }
291
+
292
+        return [
293
+            'activeLocaleLang' => $userLocaleString,
294
+            'activeLocale' => $userLocale,
295
+            'localesForLanguage' => $localesForLanguage,
296
+            'otherLocales' => $otherLocales,
297
+        ];
298
+    }
299
+
300
+    /**
301
+     * returns the message parameters
302
+     */
303
+    private function getMessageParameters(IAccount $account): array {
304
+        $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
305
+        $messageParameters = [];
306
+        foreach ($needVerifyMessage as $property) {
307
+            switch ($account->getProperty($property)->getVerified()) {
308
+                case IAccountManager::VERIFIED:
309
+                    $message = $this->l->t('Verifying');
310
+                    break;
311
+                case IAccountManager::VERIFICATION_IN_PROGRESS:
312
+                    $message = $this->l->t('Verifying …');
313
+                    break;
314
+                default:
315
+                    $message = $this->l->t('Verify');
316
+            }
317
+            $messageParameters[$property . 'Message'] = $message;
318
+        }
319
+        return $messageParameters;
320
+    }
321 321
 }
Please login to merge, or discard this patch.
lib/private/User/Listeners/BeforeUserDeletedListener.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -57,7 +57,7 @@
 block discarded – undo
57 57
 			// no avatar to remove
58 58
 		} catch (\Exception $e) {
59 59
 			// Ignore exceptions
60
-			$this->logger->info('Could not cleanup avatar of ' . $user->getUID(), [
60
+			$this->logger->info('Could not cleanup avatar of '.$user->getUID(), [
61 61
 				'exception' => $e,
62 62
 			]);
63 63
 		}
Please login to merge, or discard this patch.
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -20,36 +20,36 @@
 block discarded – undo
20 20
  * @template-implements IEventListener<BeforeUserDeletedEvent>
21 21
  */
22 22
 class BeforeUserDeletedListener implements IEventListener {
23
-	private IAvatarManager $avatarManager;
24
-	private ICredentialsManager $credentialsManager;
25
-	private LoggerInterface $logger;
26
-
27
-	public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager, ICredentialsManager $credentialsManager) {
28
-		$this->avatarManager = $avatarManager;
29
-		$this->credentialsManager = $credentialsManager;
30
-		$this->logger = $logger;
31
-	}
32
-
33
-	public function handle(Event $event): void {
34
-		if (!($event instanceof BeforeUserDeletedEvent)) {
35
-			return;
36
-		}
37
-
38
-		$user = $event->getUser();
39
-
40
-		// Delete avatar on user deletion
41
-		try {
42
-			$avatar = $this->avatarManager->getAvatar($user->getUID());
43
-			$avatar->remove(true);
44
-		} catch (NotFoundException $e) {
45
-			// no avatar to remove
46
-		} catch (\Exception $e) {
47
-			// Ignore exceptions
48
-			$this->logger->info('Could not cleanup avatar of ' . $user->getUID(), [
49
-				'exception' => $e,
50
-			]);
51
-		}
52
-		// Delete storages credentials on user deletion
53
-		$this->credentialsManager->erase($user->getUID());
54
-	}
23
+    private IAvatarManager $avatarManager;
24
+    private ICredentialsManager $credentialsManager;
25
+    private LoggerInterface $logger;
26
+
27
+    public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager, ICredentialsManager $credentialsManager) {
28
+        $this->avatarManager = $avatarManager;
29
+        $this->credentialsManager = $credentialsManager;
30
+        $this->logger = $logger;
31
+    }
32
+
33
+    public function handle(Event $event): void {
34
+        if (!($event instanceof BeforeUserDeletedEvent)) {
35
+            return;
36
+        }
37
+
38
+        $user = $event->getUser();
39
+
40
+        // Delete avatar on user deletion
41
+        try {
42
+            $avatar = $this->avatarManager->getAvatar($user->getUID());
43
+            $avatar->remove(true);
44
+        } catch (NotFoundException $e) {
45
+            // no avatar to remove
46
+        } catch (\Exception $e) {
47
+            // Ignore exceptions
48
+            $this->logger->info('Could not cleanup avatar of ' . $user->getUID(), [
49
+                'exception' => $e,
50
+            ]);
51
+        }
52
+        // Delete storages credentials on user deletion
53
+        $this->credentialsManager->erase($user->getUID());
54
+    }
55 55
 }
Please login to merge, or discard this patch.
lib/private/TagManager.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -123,7 +123,7 @@
 block discarded – undo
123 123
 			return;
124 124
 		}
125 125
 
126
-		$tagsIds = array_map(fn (array $row) => (int)$row['id'], $result->fetchAll());
126
+		$tagsIds = array_map(fn (array $row) => (int) $row['id'], $result->fetchAll());
127 127
 		$result->closeCursor();
128 128
 
129 129
 		if (count($tagsIds) === 0) {
Please login to merge, or discard this patch.
Indentation   +98 added lines, -98 removed lines patch added patch discarded remove patch
@@ -25,113 +25,113 @@
 block discarded – undo
25 25
  */
26 26
 class TagManager implements ITagManager, IEventListener {
27 27
 
28
-	public function __construct(
29
-		private TagMapper $mapper,
30
-		private IUserSession $userSession,
31
-		private IDBConnection $connection,
32
-		private LoggerInterface $logger,
33
-		private IEventDispatcher $dispatcher,
34
-	) {
35
-	}
28
+    public function __construct(
29
+        private TagMapper $mapper,
30
+        private IUserSession $userSession,
31
+        private IDBConnection $connection,
32
+        private LoggerInterface $logger,
33
+        private IEventDispatcher $dispatcher,
34
+    ) {
35
+    }
36 36
 
37
-	/**
38
-	 * Create a new \OCP\ITags instance and load tags from db.
39
-	 *
40
-	 * @see \OCP\ITags
41
-	 * @param string $type The type identifier e.g. 'contact' or 'event'.
42
-	 * @param array $defaultTags An array of default tags to be used if none are stored.
43
-	 * @param boolean $includeShared Whether to include tags for items shared with this user by others.
44
-	 * @param string $userId user for which to retrieve the tags, defaults to the currently
45
-	 *                       logged in user
46
-	 * @return \OCP\ITags
47
-	 *
48
-	 * since 20.0.0 $includeShared isn't used anymore
49
-	 */
50
-	public function load($type, $defaultTags = [], $includeShared = false, $userId = null) {
51
-		if (is_null($userId)) {
52
-			$user = $this->userSession->getUser();
53
-			if ($user === null) {
54
-				// nothing we can do without a user
55
-				return null;
56
-			}
57
-			$userId = $this->userSession->getUser()->getUId();
58
-		}
59
-		return new Tags($this->mapper, $userId, $type, $this->logger, $this->connection, $this->dispatcher, $this->userSession, $defaultTags);
60
-	}
37
+    /**
38
+     * Create a new \OCP\ITags instance and load tags from db.
39
+     *
40
+     * @see \OCP\ITags
41
+     * @param string $type The type identifier e.g. 'contact' or 'event'.
42
+     * @param array $defaultTags An array of default tags to be used if none are stored.
43
+     * @param boolean $includeShared Whether to include tags for items shared with this user by others.
44
+     * @param string $userId user for which to retrieve the tags, defaults to the currently
45
+     *                       logged in user
46
+     * @return \OCP\ITags
47
+     *
48
+     * since 20.0.0 $includeShared isn't used anymore
49
+     */
50
+    public function load($type, $defaultTags = [], $includeShared = false, $userId = null) {
51
+        if (is_null($userId)) {
52
+            $user = $this->userSession->getUser();
53
+            if ($user === null) {
54
+                // nothing we can do without a user
55
+                return null;
56
+            }
57
+            $userId = $this->userSession->getUser()->getUId();
58
+        }
59
+        return new Tags($this->mapper, $userId, $type, $this->logger, $this->connection, $this->dispatcher, $this->userSession, $defaultTags);
60
+    }
61 61
 
62
-	/**
63
-	 * Get all users who favorited an object
64
-	 *
65
-	 * @param string $objectType
66
-	 * @param int $objectId
67
-	 * @return array
68
-	 */
69
-	public function getUsersFavoritingObject(string $objectType, int $objectId): array {
70
-		$query = $this->connection->getQueryBuilder();
71
-		$query->select('uid')
72
-			->from('vcategory_to_object', 'o')
73
-			->innerJoin('o', 'vcategory', 'c', $query->expr()->eq('o.categoryid', 'c.id'))
74
-			->where($query->expr()->eq('objid', $query->createNamedParameter($objectId, IQueryBuilder::PARAM_INT)))
75
-			->andWhere($query->expr()->eq('c.type', $query->createNamedParameter($objectType)))
76
-			->andWhere($query->expr()->eq('c.category', $query->createNamedParameter(ITags::TAG_FAVORITE)));
62
+    /**
63
+     * Get all users who favorited an object
64
+     *
65
+     * @param string $objectType
66
+     * @param int $objectId
67
+     * @return array
68
+     */
69
+    public function getUsersFavoritingObject(string $objectType, int $objectId): array {
70
+        $query = $this->connection->getQueryBuilder();
71
+        $query->select('uid')
72
+            ->from('vcategory_to_object', 'o')
73
+            ->innerJoin('o', 'vcategory', 'c', $query->expr()->eq('o.categoryid', 'c.id'))
74
+            ->where($query->expr()->eq('objid', $query->createNamedParameter($objectId, IQueryBuilder::PARAM_INT)))
75
+            ->andWhere($query->expr()->eq('c.type', $query->createNamedParameter($objectType)))
76
+            ->andWhere($query->expr()->eq('c.category', $query->createNamedParameter(ITags::TAG_FAVORITE)));
77 77
 
78
-		$result = $query->execute();
79
-		$users = $result->fetchAll(\PDO::FETCH_COLUMN);
80
-		$result->closeCursor();
78
+        $result = $query->execute();
79
+        $users = $result->fetchAll(\PDO::FETCH_COLUMN);
80
+        $result->closeCursor();
81 81
 
82
-		return $users;
83
-	}
82
+        return $users;
83
+    }
84 84
 
85
-	public function handle(Event $event): void {
86
-		if (!($event instanceof UserDeletedEvent)) {
87
-			return;
88
-		}
85
+    public function handle(Event $event): void {
86
+        if (!($event instanceof UserDeletedEvent)) {
87
+            return;
88
+        }
89 89
 
90
-		// Find all objectid/tagId pairs.
91
-		$user = $event->getUser();
92
-		$qb = $this->connection->getQueryBuilder();
93
-		$qb->select('id')
94
-			->from('vcategory')
95
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())));
96
-		try {
97
-			$result = $qb->executeQuery();
98
-		} catch (DBException $e) {
99
-			$this->logger->error($e->getMessage(), [
100
-				'app' => 'core',
101
-				'exception' => $e,
102
-			]);
103
-			return;
104
-		}
90
+        // Find all objectid/tagId pairs.
91
+        $user = $event->getUser();
92
+        $qb = $this->connection->getQueryBuilder();
93
+        $qb->select('id')
94
+            ->from('vcategory')
95
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())));
96
+        try {
97
+            $result = $qb->executeQuery();
98
+        } catch (DBException $e) {
99
+            $this->logger->error($e->getMessage(), [
100
+                'app' => 'core',
101
+                'exception' => $e,
102
+            ]);
103
+            return;
104
+        }
105 105
 
106
-		$tagsIds = array_map(fn (array $row) => (int)$row['id'], $result->fetchAll());
107
-		$result->closeCursor();
106
+        $tagsIds = array_map(fn (array $row) => (int)$row['id'], $result->fetchAll());
107
+        $result->closeCursor();
108 108
 
109
-		if (count($tagsIds) === 0) {
110
-			return;
111
-		}
109
+        if (count($tagsIds) === 0) {
110
+            return;
111
+        }
112 112
 
113
-		// Clean vcategory_to_object table
114
-		$qb = $this->connection->getQueryBuilder();
115
-		$qb = $qb->delete('vcategory_to_object')
116
-			->where($qb->expr()->in('categoryid', $qb->createParameter('chunk')));
113
+        // Clean vcategory_to_object table
114
+        $qb = $this->connection->getQueryBuilder();
115
+        $qb = $qb->delete('vcategory_to_object')
116
+            ->where($qb->expr()->in('categoryid', $qb->createParameter('chunk')));
117 117
 
118
-		// Clean vcategory
119
-		$qb1 = $this->connection->getQueryBuilder();
120
-		$qb1 = $qb1->delete('vcategory')
121
-			->where($qb1->expr()->in('uid', $qb1->createParameter('chunk')));
118
+        // Clean vcategory
119
+        $qb1 = $this->connection->getQueryBuilder();
120
+        $qb1 = $qb1->delete('vcategory')
121
+            ->where($qb1->expr()->in('uid', $qb1->createParameter('chunk')));
122 122
 
123
-		foreach (array_chunk($tagsIds, 1000) as $tagChunk) {
124
-			$qb->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY);
125
-			$qb1->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY);
126
-			try {
127
-				$qb->executeStatement();
128
-				$qb1->executeStatement();
129
-			} catch (DBException $e) {
130
-				$this->logger->error($e->getMessage(), [
131
-					'app' => 'core',
132
-					'exception' => $e,
133
-				]);
134
-			}
135
-		}
136
-	}
123
+        foreach (array_chunk($tagsIds, 1000) as $tagChunk) {
124
+            $qb->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY);
125
+            $qb1->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY);
126
+            try {
127
+                $qb->executeStatement();
128
+                $qb1->executeStatement();
129
+            } catch (DBException $e) {
130
+                $this->logger->error($e->getMessage(), [
131
+                    'app' => 'core',
132
+                    'exception' => $e,
133
+                ]);
134
+            }
135
+        }
136
+    }
137 137
 }
Please login to merge, or discard this patch.
apps/files/lib/Controller/OpenLocalEditorController.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -100,7 +100,7 @@
 block discarded – undo
100 100
 			}
101 101
 		}
102 102
 
103
-		$this->logger->error('Giving up after ' . self::TOKEN_RETRIES . ' retries to generate a unique local editor token for path hash: ' . $pathHash);
103
+		$this->logger->error('Giving up after '.self::TOKEN_RETRIES.' retries to generate a unique local editor token for path hash: '.$pathHash);
104 104
 		return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
105 105
 	}
106 106
 
Please login to merge, or discard this patch.
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -25,104 +25,104 @@
 block discarded – undo
25 25
 use Psr\Log\LoggerInterface;
26 26
 
27 27
 class OpenLocalEditorController extends OCSController {
28
-	public const TOKEN_LENGTH = 128;
29
-	public const TOKEN_DURATION = 600; // 10 Minutes
30
-	public const TOKEN_RETRIES = 50;
31
-
32
-	public function __construct(
33
-		string $appName,
34
-		IRequest $request,
35
-		protected ITimeFactory $timeFactory,
36
-		protected OpenLocalEditorMapper $mapper,
37
-		protected ISecureRandom $secureRandom,
38
-		protected LoggerInterface $logger,
39
-		protected ?string $userId,
40
-	) {
41
-		parent::__construct($appName, $request);
42
-	}
43
-
44
-	/**
45
-	 * Create a local editor
46
-	 *
47
-	 * @param string $path Path of the file
48
-	 *
49
-	 * @return DataResponse<Http::STATUS_OK, array{userId: ?string, pathHash: string, expirationTime: int, token: string}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, list<empty>, array{}>
50
-	 *
51
-	 * 200: Local editor returned
52
-	 */
53
-	#[NoAdminRequired]
54
-	#[UserRateLimit(limit: 10, period: 120)]
55
-	public function create(string $path): DataResponse {
56
-		$pathHash = sha1($path);
57
-
58
-		$entity = new OpenLocalEditor();
59
-		$entity->setUserId($this->userId);
60
-		$entity->setPathHash($pathHash);
61
-		$entity->setExpirationTime($this->timeFactory->getTime() + self::TOKEN_DURATION); // Expire in 10 minutes
62
-
63
-		for ($i = 1; $i <= self::TOKEN_RETRIES; $i++) {
64
-			$token = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_ALPHANUMERIC);
65
-			$entity->setToken($token);
66
-
67
-			try {
68
-				$this->mapper->insert($entity);
69
-
70
-				return new DataResponse([
71
-					'userId' => $this->userId,
72
-					'pathHash' => $pathHash,
73
-					'expirationTime' => $entity->getExpirationTime(),
74
-					'token' => $entity->getToken(),
75
-				]);
76
-			} catch (Exception $e) {
77
-				if ($e->getCode() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
78
-					// Only retry on unique constraint violation
79
-					throw $e;
80
-				}
81
-			}
82
-		}
83
-
84
-		$this->logger->error('Giving up after ' . self::TOKEN_RETRIES . ' retries to generate a unique local editor token for path hash: ' . $pathHash);
85
-		return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
86
-	}
87
-
88
-	/**
89
-	 * Validate a local editor
90
-	 *
91
-	 * @param string $path Path of the file
92
-	 * @param string $token Token of the local editor
93
-	 *
94
-	 * @return DataResponse<Http::STATUS_OK, array{userId: string, pathHash: string, expirationTime: int, token: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, list<empty>, array{}>
95
-	 *
96
-	 * 200: Local editor validated successfully
97
-	 * 404: Local editor not found
98
-	 */
99
-	#[NoAdminRequired]
100
-	#[BruteForceProtection(action: 'openLocalEditor')]
101
-	public function validate(string $path, string $token): DataResponse {
102
-		$pathHash = sha1($path);
103
-
104
-		try {
105
-			$entity = $this->mapper->verifyToken($this->userId, $pathHash, $token);
106
-		} catch (DoesNotExistException $e) {
107
-			$response = new DataResponse([], Http::STATUS_NOT_FOUND);
108
-			$response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
109
-			return $response;
110
-		}
111
-
112
-		$this->mapper->delete($entity);
113
-
114
-		if ($entity->getExpirationTime() <= $this->timeFactory->getTime()) {
115
-			$response = new DataResponse([], Http::STATUS_NOT_FOUND);
116
-			$response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
117
-			return $response;
118
-		}
119
-
120
-		return new DataResponse([
121
-			'userId' => $this->userId,
122
-			'pathHash' => $pathHash,
123
-			'expirationTime' => $entity->getExpirationTime(),
124
-			'token' => $entity->getToken(),
125
-		]);
126
-	}
28
+    public const TOKEN_LENGTH = 128;
29
+    public const TOKEN_DURATION = 600; // 10 Minutes
30
+    public const TOKEN_RETRIES = 50;
31
+
32
+    public function __construct(
33
+        string $appName,
34
+        IRequest $request,
35
+        protected ITimeFactory $timeFactory,
36
+        protected OpenLocalEditorMapper $mapper,
37
+        protected ISecureRandom $secureRandom,
38
+        protected LoggerInterface $logger,
39
+        protected ?string $userId,
40
+    ) {
41
+        parent::__construct($appName, $request);
42
+    }
43
+
44
+    /**
45
+     * Create a local editor
46
+     *
47
+     * @param string $path Path of the file
48
+     *
49
+     * @return DataResponse<Http::STATUS_OK, array{userId: ?string, pathHash: string, expirationTime: int, token: string}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, list<empty>, array{}>
50
+     *
51
+     * 200: Local editor returned
52
+     */
53
+    #[NoAdminRequired]
54
+    #[UserRateLimit(limit: 10, period: 120)]
55
+    public function create(string $path): DataResponse {
56
+        $pathHash = sha1($path);
57
+
58
+        $entity = new OpenLocalEditor();
59
+        $entity->setUserId($this->userId);
60
+        $entity->setPathHash($pathHash);
61
+        $entity->setExpirationTime($this->timeFactory->getTime() + self::TOKEN_DURATION); // Expire in 10 minutes
62
+
63
+        for ($i = 1; $i <= self::TOKEN_RETRIES; $i++) {
64
+            $token = $this->secureRandom->generate(self::TOKEN_LENGTH, ISecureRandom::CHAR_ALPHANUMERIC);
65
+            $entity->setToken($token);
66
+
67
+            try {
68
+                $this->mapper->insert($entity);
69
+
70
+                return new DataResponse([
71
+                    'userId' => $this->userId,
72
+                    'pathHash' => $pathHash,
73
+                    'expirationTime' => $entity->getExpirationTime(),
74
+                    'token' => $entity->getToken(),
75
+                ]);
76
+            } catch (Exception $e) {
77
+                if ($e->getCode() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
78
+                    // Only retry on unique constraint violation
79
+                    throw $e;
80
+                }
81
+            }
82
+        }
83
+
84
+        $this->logger->error('Giving up after ' . self::TOKEN_RETRIES . ' retries to generate a unique local editor token for path hash: ' . $pathHash);
85
+        return new DataResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
86
+    }
87
+
88
+    /**
89
+     * Validate a local editor
90
+     *
91
+     * @param string $path Path of the file
92
+     * @param string $token Token of the local editor
93
+     *
94
+     * @return DataResponse<Http::STATUS_OK, array{userId: string, pathHash: string, expirationTime: int, token: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, list<empty>, array{}>
95
+     *
96
+     * 200: Local editor validated successfully
97
+     * 404: Local editor not found
98
+     */
99
+    #[NoAdminRequired]
100
+    #[BruteForceProtection(action: 'openLocalEditor')]
101
+    public function validate(string $path, string $token): DataResponse {
102
+        $pathHash = sha1($path);
103
+
104
+        try {
105
+            $entity = $this->mapper->verifyToken($this->userId, $pathHash, $token);
106
+        } catch (DoesNotExistException $e) {
107
+            $response = new DataResponse([], Http::STATUS_NOT_FOUND);
108
+            $response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
109
+            return $response;
110
+        }
111
+
112
+        $this->mapper->delete($entity);
113
+
114
+        if ($entity->getExpirationTime() <= $this->timeFactory->getTime()) {
115
+            $response = new DataResponse([], Http::STATUS_NOT_FOUND);
116
+            $response->throttle(['userId' => $this->userId, 'pathHash' => $pathHash]);
117
+            return $response;
118
+        }
119
+
120
+        return new DataResponse([
121
+            'userId' => $this->userId,
122
+            'pathHash' => $pathHash,
123
+            'expirationTime' => $entity->getExpirationTime(),
124
+            'token' => $entity->getToken(),
125
+        ]);
126
+    }
127 127
 
128 128
 }
Please login to merge, or discard this patch.
apps/files/lib/Db/OpenLocalEditor.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -39,22 +39,22 @@
 block discarded – undo
39 39
  * @method string getToken()
40 40
  */
41 41
 class OpenLocalEditor extends Entity {
42
-	/** @var string */
43
-	protected $userId;
42
+    /** @var string */
43
+    protected $userId;
44 44
 
45
-	/** @var string */
46
-	protected $pathHash;
45
+    /** @var string */
46
+    protected $pathHash;
47 47
 
48
-	/** @var int */
49
-	protected $expirationTime;
48
+    /** @var int */
49
+    protected $expirationTime;
50 50
 
51
-	/** @var string */
52
-	protected $token;
51
+    /** @var string */
52
+    protected $token;
53 53
 
54
-	public function __construct() {
55
-		$this->addType('userId', 'string');
56
-		$this->addType('pathHash', 'string');
57
-		$this->addType('expirationTime', 'integer');
58
-		$this->addType('token', 'string');
59
-	}
54
+    public function __construct() {
55
+        $this->addType('userId', 'string');
56
+        $this->addType('pathHash', 'string');
57
+        $this->addType('expirationTime', 'integer');
58
+        $this->addType('token', 'string');
59
+    }
60 60
 }
Please login to merge, or discard this patch.