Completed
Push — master ( e0a21e...61fe4c )
by Maxence
21:04 queued 15s
created
lib/private/TemplateLayout.php 1 patch
Indentation   +371 added lines, -371 removed lines patch added patch discarded remove patch
@@ -36,375 +36,375 @@
 block discarded – undo
36 36
 use OCP\Util;
37 37
 
38 38
 class TemplateLayout {
39
-	private static string $versionHash = '';
40
-	/** @var string[] */
41
-	private static array $cacheBusterCache = [];
42
-
43
-	public static ?CSSResourceLocator $cssLocator = null;
44
-	public static ?JSResourceLocator $jsLocator = null;
45
-
46
-	public function __construct(
47
-		private IConfig $config,
48
-		private readonly IAppConfig $appConfig,
49
-		private IAppManager $appManager,
50
-		private InitialStateService $initialState,
51
-		private INavigationManager $navigationManager,
52
-		private ITemplateManager $templateManager,
53
-		private ServerVersion $serverVersion,
54
-	) {
55
-	}
56
-
57
-	public function getPageTemplate(string $renderAs, string $appId): ITemplate {
58
-		// Add fallback theming variables if not rendered as user
59
-		if ($renderAs !== TemplateResponse::RENDER_AS_USER) {
60
-			// TODO cache generated default theme if enabled for fallback if server is erroring ?
61
-			Util::addStyle('theming', 'default');
62
-		}
63
-
64
-		// Decide which page we show
65
-		switch ($renderAs) {
66
-			case TemplateResponse::RENDER_AS_USER:
67
-				$page = $this->templateManager->getTemplate('core', 'layout.user');
68
-				if (in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) {
69
-					$page->assign('bodyid', 'body-settings');
70
-				} else {
71
-					$page->assign('bodyid', 'body-user');
72
-				}
73
-
74
-				$this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
75
-				$this->initialState->provideInitialState('core', 'apps', array_values($this->navigationManager->getAll()));
76
-
77
-				if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) {
78
-					$this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
79
-					$this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1));
80
-					$this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
81
-					Util::addScript('core', 'legacy-unified-search', 'core');
82
-				} else {
83
-					Util::addScript('core', 'unified-search', 'core');
84
-				}
85
-
86
-				// Set logo link target
87
-				$logoUrl = $this->config->getSystemValueString('logo_url', '');
88
-				$page->assign('logoUrl', $logoUrl);
89
-
90
-				// Set default entry name
91
-				$defaultEntryId = $this->navigationManager->getDefaultEntryIdForUser();
92
-				$defaultEntry = $this->navigationManager->get($defaultEntryId);
93
-				$page->assign('defaultAppName', $defaultEntry['name'] ?? '');
94
-
95
-				// Add navigation entry
96
-				$page->assign('application', '');
97
-				$page->assign('appid', $appId);
98
-
99
-				$navigation = $this->navigationManager->getAll();
100
-				$page->assign('navigation', $navigation);
101
-				$settingsNavigation = $this->navigationManager->getAll('settings');
102
-				$this->initialState->provideInitialState('core', 'settingsNavEntries', $settingsNavigation);
103
-
104
-				foreach ($navigation as $entry) {
105
-					if ($entry['active']) {
106
-						$page->assign('application', $entry['name']);
107
-						break;
108
-					}
109
-				}
110
-
111
-				foreach ($settingsNavigation as $entry) {
112
-					if ($entry['active']) {
113
-						$page->assign('application', $entry['name']);
114
-						break;
115
-					}
116
-				}
117
-
118
-				$user = Server::get(IUserSession::class)->getUser();
119
-
120
-				if ($user === null) {
121
-					$page->assign('user_uid', false);
122
-					$page->assign('user_displayname', false);
123
-					$page->assign('userAvatarSet', false);
124
-					$page->assign('userStatus', false);
125
-				} else {
126
-					$page->assign('user_uid', $user->getUID());
127
-					$page->assign('user_displayname', $user->getDisplayName());
128
-					$page->assign('userAvatarSet', true);
129
-					$page->assign('userAvatarVersion', $this->config->getUserValue($user->getUID(), 'avatar', 'version', 0));
130
-				}
131
-				break;
132
-			case TemplateResponse::RENDER_AS_ERROR:
133
-				$page = $this->templateManager->getTemplate('core', 'layout.guest', '', false);
134
-				$page->assign('bodyid', 'body-login');
135
-				$page->assign('user_displayname', '');
136
-				$page->assign('user_uid', '');
137
-				break;
138
-			case TemplateResponse::RENDER_AS_GUEST:
139
-				$page = $this->templateManager->getTemplate('core', 'layout.guest');
140
-				Util::addStyle('guest');
141
-				$page->assign('bodyid', 'body-login');
142
-
143
-				$userDisplayName = false;
144
-				$user = Server::get(IUserSession::class)->getUser();
145
-				if ($user) {
146
-					$userDisplayName = $user->getDisplayName();
147
-				}
148
-
149
-				$page->assign('user_displayname', $userDisplayName);
150
-				$page->assign('user_uid', \OC_User::getUser());
151
-				break;
152
-			case TemplateResponse::RENDER_AS_PUBLIC:
153
-				$page = $this->templateManager->getTemplate('core', 'layout.public');
154
-				$page->assign('appid', $appId);
155
-				$page->assign('bodyid', 'body-public');
156
-
157
-				// Set logo link target
158
-				$logoUrl = $this->config->getSystemValueString('logo_url', '');
159
-				$page->assign('logoUrl', $logoUrl);
160
-
161
-				$subscription = Server::get(IRegistry::class);
162
-				$showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true);
163
-				if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) {
164
-					$showSimpleSignup = false;
165
-				}
166
-
167
-				$defaultSignUpLink = 'https://nextcloud.com/signup/';
168
-				$signUpLink = $this->config->getSystemValueString('registration_link', $defaultSignUpLink);
169
-				if ($signUpLink !== $defaultSignUpLink) {
170
-					$showSimpleSignup = true;
171
-				}
172
-
173
-				if ($this->appManager->isEnabledForUser('registration')) {
174
-					$urlGenerator = Server::get(IURLGenerator::class);
175
-					$signUpLink = $urlGenerator->getAbsoluteURL('/index.php/apps/registration/');
176
-				}
177
-
178
-				$page->assign('showSimpleSignUpLink', $showSimpleSignup);
179
-				$page->assign('signUpLink', $signUpLink);
180
-				break;
181
-			default:
182
-				$page = $this->templateManager->getTemplate('core', 'layout.base');
183
-				break;
184
-		}
185
-		// Send the language, locale, and direction to our layouts
186
-		$l10nFactory = Server::get(IFactory::class);
187
-		$lang = $l10nFactory->findLanguage();
188
-		$locale = $l10nFactory->findLocale($lang);
189
-		$direction = $l10nFactory->getLanguageDirection($lang);
190
-
191
-		$lang = str_replace('_', '-', $lang);
192
-		$page->assign('language', $lang);
193
-		$page->assign('locale', $locale);
194
-		$page->assign('direction', $direction);
195
-
196
-		// Set body data-theme
197
-		try {
198
-			$themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
199
-		} catch (\Exception) {
200
-			$themesService = null;
201
-		}
202
-		$page->assign('enabledThemes', $themesService?->getEnabledThemes() ?? []);
203
-
204
-		if ($this->config->getSystemValueBool('installed', false)) {
205
-			if (empty(self::$versionHash)) {
206
-				$v = $this->appManager->getAppInstalledVersions(true);
207
-				$v['core'] = implode('.', $this->serverVersion->getVersion());
208
-				self::$versionHash = substr(md5(implode(',', $v)), 0, 8);
209
-			}
210
-		} else {
211
-			self::$versionHash = md5('not installed');
212
-		}
213
-
214
-		// Add the js files
215
-		$jsFiles = self::findJavascriptFiles(Util::getScripts());
216
-		$page->assign('jsfiles', []);
217
-		if ($this->config->getSystemValueBool('installed', false) && $renderAs != TemplateResponse::RENDER_AS_ERROR) {
218
-			// this is on purpose outside of the if statement below so that the initial state is prefilled (done in the getConfig() call)
219
-			// see https://github.com/nextcloud/server/pull/22636 for details
220
-			$jsConfigHelper = new JSConfigHelper(
221
-				$this->serverVersion,
222
-				\OCP\Util::getL10N('lib'),
223
-				\OCP\Server::get(Defaults::class),
224
-				$this->appManager,
225
-				\OC::$server->getSession(),
226
-				\OC::$server->getUserSession()->getUser(),
227
-				$this->config,
228
-				$this->appConfig,
229
-				\OC::$server->getGroupManager(),
230
-				\OC::$server->get(IniGetWrapper::class),
231
-				\OC::$server->getURLGenerator(),
232
-				\OC::$server->get(CapabilitiesManager::class),
233
-				\OCP\Server::get(IInitialStateService::class),
234
-				\OCP\Server::get(IProvider::class),
235
-				\OCP\Server::get(FilenameValidator::class),
236
-			);
237
-			$config = $jsConfigHelper->getConfig();
238
-			if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) {
239
-				$page->assign('inline_ocjs', $config);
240
-			} else {
241
-				$page->append('jsfiles', \OC::$server->getURLGenerator()->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
242
-			}
243
-		}
244
-		foreach ($jsFiles as $info) {
245
-			$web = $info[1];
246
-			$file = $info[2];
247
-			$page->append('jsfiles', $web . '/' . $file . $this->getVersionHashSuffix());
248
-		}
249
-
250
-		$request = \OCP\Server::get(IRequest::class);
251
-
252
-		try {
253
-			$pathInfo = $request->getPathInfo();
254
-		} catch (\Exception $e) {
255
-			$pathInfo = '';
256
-		}
257
-
258
-		// Do not initialise scss appdata until we have a fully installed instance
259
-		// Do not load scss for update, errors, installation or login page
260
-		if ($this->config->getSystemValueBool('installed', false)
261
-			&& !\OCP\Util::needUpgrade()
262
-			&& $pathInfo !== ''
263
-			&& !preg_match('/^\/login/', $pathInfo)
264
-			&& $renderAs !== TemplateResponse::RENDER_AS_ERROR
265
-		) {
266
-			$cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
267
-		} else {
268
-			// If we ignore the scss compiler,
269
-			// we need to load the guest css fallback
270
-			Util::addStyle('guest');
271
-			$cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
272
-		}
273
-
274
-		$page->assign('cssfiles', []);
275
-		$page->assign('printcssfiles', []);
276
-		$this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
277
-		foreach ($cssFiles as $info) {
278
-			$web = $info[1];
279
-			$file = $info[2];
280
-
281
-			if (str_ends_with($file, 'print.css')) {
282
-				$page->append('printcssfiles', $web . '/' . $file . $this->getVersionHashSuffix());
283
-			} else {
284
-				$suffix = $this->getVersionHashSuffix($web, $file);
285
-
286
-				if (!str_contains($file, '?v=')) {
287
-					$page->append('cssfiles', $web . '/' . $file . $suffix);
288
-				} else {
289
-					$page->append('cssfiles', $web . '/' . $file . '-' . substr($suffix, 3));
290
-				}
291
-			}
292
-		}
293
-
294
-		if ($request->isUserAgent([Request::USER_AGENT_CLIENT_IOS, Request::USER_AGENT_SAFARI, Request::USER_AGENT_SAFARI_MOBILE])) {
295
-			// Prevent auto zoom with iOS but still allow user zoom
296
-			// On chrome (and others) this does not work (will also disable user zoom)
297
-			$page->assign('viewport_maximum_scale', '1.0');
298
-		}
299
-
300
-		$page->assign('initialStates', $this->initialState->getInitialStates());
301
-
302
-		$page->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
303
-		$page->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
304
-
305
-		return $page;
306
-	}
307
-
308
-	protected function getVersionHashSuffix(string $path = '', string $file = ''): string {
309
-		if ($this->config->getSystemValueBool('debug', false)) {
310
-			// allows chrome workspace mapping in debug mode
311
-			return '';
312
-		}
313
-
314
-		if ($this->config->getSystemValueBool('installed', false) === false) {
315
-			// if not installed just return the version hash
316
-			return '?v=' . self::$versionHash;
317
-		}
318
-
319
-		$hash = false;
320
-		// Try the web-root first
321
-		if ($path !== '') {
322
-			$hash = $this->getVersionHashByPath($path);
323
-		}
324
-		// If not found try the file
325
-		if ($hash === false && $file !== '') {
326
-			$hash = $this->getVersionHashByPath($file);
327
-		}
328
-		// As a last resort we use the server version hash
329
-		if ($hash === false) {
330
-			$hash = self::$versionHash;
331
-		}
332
-
333
-		// The theming app is force-enabled thus the cache buster is always available
334
-		$themingSuffix = '-' . $this->config->getAppValue('theming', 'cachebuster', '0');
335
-
336
-		return '?v=' . $hash . $themingSuffix;
337
-	}
338
-
339
-	private function getVersionHashByPath(string $path): string|false {
340
-		if (array_key_exists($path, self::$cacheBusterCache) === false) {
341
-			// Not yet cached, so lets find the cache buster string
342
-			$appId = $this->getAppNamefromPath($path);
343
-			if ($appId === false) {
344
-				// No app Id could be guessed
345
-				return false;
346
-			}
347
-
348
-			if ($appId === 'core') {
349
-				// core is not a real app but the server itself
350
-				$hash = self::$versionHash;
351
-			} else {
352
-				$appVersion = $this->appManager->getAppVersion($appId);
353
-				// For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
354
-				if ($this->appManager->isShipped($appId)) {
355
-					$appVersion .= '-' . self::$versionHash;
356
-				}
357
-
358
-				$hash = substr(md5($appVersion), 0, 8);
359
-			}
360
-			self::$cacheBusterCache[$path] = $hash;
361
-		}
362
-
363
-		return self::$cacheBusterCache[$path];
364
-	}
365
-
366
-	public static function findStylesheetFiles(array $styles): array {
367
-		if (!self::$cssLocator) {
368
-			self::$cssLocator = \OCP\Server::get(CSSResourceLocator::class);
369
-		}
370
-		self::$cssLocator->find($styles);
371
-		return self::$cssLocator->getResources();
372
-	}
373
-
374
-	public function getAppNamefromPath(string $path): string|false {
375
-		if ($path !== '') {
376
-			$pathParts = explode('/', $path);
377
-			if ($pathParts[0] === 'css') {
378
-				// This is a scss request
379
-				return $pathParts[1];
380
-			} elseif ($pathParts[0] === 'core') {
381
-				return 'core';
382
-			}
383
-			return end($pathParts);
384
-		}
385
-		return false;
386
-	}
387
-
388
-	public static function findJavascriptFiles(array $scripts): array {
389
-		if (!self::$jsLocator) {
390
-			self::$jsLocator = \OCP\Server::get(JSResourceLocator::class);
391
-		}
392
-		self::$jsLocator->find($scripts);
393
-		return self::$jsLocator->getResources();
394
-	}
395
-
396
-	/**
397
-	 * Converts the absolute file path to a relative path from \OC::$SERVERROOT
398
-	 * @param string $filePath Absolute path
399
-	 * @return string Relative path
400
-	 * @throws \Exception If $filePath is not under \OC::$SERVERROOT
401
-	 */
402
-	public static function convertToRelativePath(string $filePath) {
403
-		$relativePath = explode(\OC::$SERVERROOT, $filePath);
404
-		if (count($relativePath) !== 2) {
405
-			throw new \Exception('$filePath is not under the \OC::$SERVERROOT');
406
-		}
407
-
408
-		return $relativePath[1];
409
-	}
39
+    private static string $versionHash = '';
40
+    /** @var string[] */
41
+    private static array $cacheBusterCache = [];
42
+
43
+    public static ?CSSResourceLocator $cssLocator = null;
44
+    public static ?JSResourceLocator $jsLocator = null;
45
+
46
+    public function __construct(
47
+        private IConfig $config,
48
+        private readonly IAppConfig $appConfig,
49
+        private IAppManager $appManager,
50
+        private InitialStateService $initialState,
51
+        private INavigationManager $navigationManager,
52
+        private ITemplateManager $templateManager,
53
+        private ServerVersion $serverVersion,
54
+    ) {
55
+    }
56
+
57
+    public function getPageTemplate(string $renderAs, string $appId): ITemplate {
58
+        // Add fallback theming variables if not rendered as user
59
+        if ($renderAs !== TemplateResponse::RENDER_AS_USER) {
60
+            // TODO cache generated default theme if enabled for fallback if server is erroring ?
61
+            Util::addStyle('theming', 'default');
62
+        }
63
+
64
+        // Decide which page we show
65
+        switch ($renderAs) {
66
+            case TemplateResponse::RENDER_AS_USER:
67
+                $page = $this->templateManager->getTemplate('core', 'layout.user');
68
+                if (in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) {
69
+                    $page->assign('bodyid', 'body-settings');
70
+                } else {
71
+                    $page->assign('bodyid', 'body-user');
72
+                }
73
+
74
+                $this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
75
+                $this->initialState->provideInitialState('core', 'apps', array_values($this->navigationManager->getAll()));
76
+
77
+                if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) {
78
+                    $this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
79
+                    $this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1));
80
+                    $this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
81
+                    Util::addScript('core', 'legacy-unified-search', 'core');
82
+                } else {
83
+                    Util::addScript('core', 'unified-search', 'core');
84
+                }
85
+
86
+                // Set logo link target
87
+                $logoUrl = $this->config->getSystemValueString('logo_url', '');
88
+                $page->assign('logoUrl', $logoUrl);
89
+
90
+                // Set default entry name
91
+                $defaultEntryId = $this->navigationManager->getDefaultEntryIdForUser();
92
+                $defaultEntry = $this->navigationManager->get($defaultEntryId);
93
+                $page->assign('defaultAppName', $defaultEntry['name'] ?? '');
94
+
95
+                // Add navigation entry
96
+                $page->assign('application', '');
97
+                $page->assign('appid', $appId);
98
+
99
+                $navigation = $this->navigationManager->getAll();
100
+                $page->assign('navigation', $navigation);
101
+                $settingsNavigation = $this->navigationManager->getAll('settings');
102
+                $this->initialState->provideInitialState('core', 'settingsNavEntries', $settingsNavigation);
103
+
104
+                foreach ($navigation as $entry) {
105
+                    if ($entry['active']) {
106
+                        $page->assign('application', $entry['name']);
107
+                        break;
108
+                    }
109
+                }
110
+
111
+                foreach ($settingsNavigation as $entry) {
112
+                    if ($entry['active']) {
113
+                        $page->assign('application', $entry['name']);
114
+                        break;
115
+                    }
116
+                }
117
+
118
+                $user = Server::get(IUserSession::class)->getUser();
119
+
120
+                if ($user === null) {
121
+                    $page->assign('user_uid', false);
122
+                    $page->assign('user_displayname', false);
123
+                    $page->assign('userAvatarSet', false);
124
+                    $page->assign('userStatus', false);
125
+                } else {
126
+                    $page->assign('user_uid', $user->getUID());
127
+                    $page->assign('user_displayname', $user->getDisplayName());
128
+                    $page->assign('userAvatarSet', true);
129
+                    $page->assign('userAvatarVersion', $this->config->getUserValue($user->getUID(), 'avatar', 'version', 0));
130
+                }
131
+                break;
132
+            case TemplateResponse::RENDER_AS_ERROR:
133
+                $page = $this->templateManager->getTemplate('core', 'layout.guest', '', false);
134
+                $page->assign('bodyid', 'body-login');
135
+                $page->assign('user_displayname', '');
136
+                $page->assign('user_uid', '');
137
+                break;
138
+            case TemplateResponse::RENDER_AS_GUEST:
139
+                $page = $this->templateManager->getTemplate('core', 'layout.guest');
140
+                Util::addStyle('guest');
141
+                $page->assign('bodyid', 'body-login');
142
+
143
+                $userDisplayName = false;
144
+                $user = Server::get(IUserSession::class)->getUser();
145
+                if ($user) {
146
+                    $userDisplayName = $user->getDisplayName();
147
+                }
148
+
149
+                $page->assign('user_displayname', $userDisplayName);
150
+                $page->assign('user_uid', \OC_User::getUser());
151
+                break;
152
+            case TemplateResponse::RENDER_AS_PUBLIC:
153
+                $page = $this->templateManager->getTemplate('core', 'layout.public');
154
+                $page->assign('appid', $appId);
155
+                $page->assign('bodyid', 'body-public');
156
+
157
+                // Set logo link target
158
+                $logoUrl = $this->config->getSystemValueString('logo_url', '');
159
+                $page->assign('logoUrl', $logoUrl);
160
+
161
+                $subscription = Server::get(IRegistry::class);
162
+                $showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true);
163
+                if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) {
164
+                    $showSimpleSignup = false;
165
+                }
166
+
167
+                $defaultSignUpLink = 'https://nextcloud.com/signup/';
168
+                $signUpLink = $this->config->getSystemValueString('registration_link', $defaultSignUpLink);
169
+                if ($signUpLink !== $defaultSignUpLink) {
170
+                    $showSimpleSignup = true;
171
+                }
172
+
173
+                if ($this->appManager->isEnabledForUser('registration')) {
174
+                    $urlGenerator = Server::get(IURLGenerator::class);
175
+                    $signUpLink = $urlGenerator->getAbsoluteURL('/index.php/apps/registration/');
176
+                }
177
+
178
+                $page->assign('showSimpleSignUpLink', $showSimpleSignup);
179
+                $page->assign('signUpLink', $signUpLink);
180
+                break;
181
+            default:
182
+                $page = $this->templateManager->getTemplate('core', 'layout.base');
183
+                break;
184
+        }
185
+        // Send the language, locale, and direction to our layouts
186
+        $l10nFactory = Server::get(IFactory::class);
187
+        $lang = $l10nFactory->findLanguage();
188
+        $locale = $l10nFactory->findLocale($lang);
189
+        $direction = $l10nFactory->getLanguageDirection($lang);
190
+
191
+        $lang = str_replace('_', '-', $lang);
192
+        $page->assign('language', $lang);
193
+        $page->assign('locale', $locale);
194
+        $page->assign('direction', $direction);
195
+
196
+        // Set body data-theme
197
+        try {
198
+            $themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
199
+        } catch (\Exception) {
200
+            $themesService = null;
201
+        }
202
+        $page->assign('enabledThemes', $themesService?->getEnabledThemes() ?? []);
203
+
204
+        if ($this->config->getSystemValueBool('installed', false)) {
205
+            if (empty(self::$versionHash)) {
206
+                $v = $this->appManager->getAppInstalledVersions(true);
207
+                $v['core'] = implode('.', $this->serverVersion->getVersion());
208
+                self::$versionHash = substr(md5(implode(',', $v)), 0, 8);
209
+            }
210
+        } else {
211
+            self::$versionHash = md5('not installed');
212
+        }
213
+
214
+        // Add the js files
215
+        $jsFiles = self::findJavascriptFiles(Util::getScripts());
216
+        $page->assign('jsfiles', []);
217
+        if ($this->config->getSystemValueBool('installed', false) && $renderAs != TemplateResponse::RENDER_AS_ERROR) {
218
+            // this is on purpose outside of the if statement below so that the initial state is prefilled (done in the getConfig() call)
219
+            // see https://github.com/nextcloud/server/pull/22636 for details
220
+            $jsConfigHelper = new JSConfigHelper(
221
+                $this->serverVersion,
222
+                \OCP\Util::getL10N('lib'),
223
+                \OCP\Server::get(Defaults::class),
224
+                $this->appManager,
225
+                \OC::$server->getSession(),
226
+                \OC::$server->getUserSession()->getUser(),
227
+                $this->config,
228
+                $this->appConfig,
229
+                \OC::$server->getGroupManager(),
230
+                \OC::$server->get(IniGetWrapper::class),
231
+                \OC::$server->getURLGenerator(),
232
+                \OC::$server->get(CapabilitiesManager::class),
233
+                \OCP\Server::get(IInitialStateService::class),
234
+                \OCP\Server::get(IProvider::class),
235
+                \OCP\Server::get(FilenameValidator::class),
236
+            );
237
+            $config = $jsConfigHelper->getConfig();
238
+            if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) {
239
+                $page->assign('inline_ocjs', $config);
240
+            } else {
241
+                $page->append('jsfiles', \OC::$server->getURLGenerator()->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
242
+            }
243
+        }
244
+        foreach ($jsFiles as $info) {
245
+            $web = $info[1];
246
+            $file = $info[2];
247
+            $page->append('jsfiles', $web . '/' . $file . $this->getVersionHashSuffix());
248
+        }
249
+
250
+        $request = \OCP\Server::get(IRequest::class);
251
+
252
+        try {
253
+            $pathInfo = $request->getPathInfo();
254
+        } catch (\Exception $e) {
255
+            $pathInfo = '';
256
+        }
257
+
258
+        // Do not initialise scss appdata until we have a fully installed instance
259
+        // Do not load scss for update, errors, installation or login page
260
+        if ($this->config->getSystemValueBool('installed', false)
261
+            && !\OCP\Util::needUpgrade()
262
+            && $pathInfo !== ''
263
+            && !preg_match('/^\/login/', $pathInfo)
264
+            && $renderAs !== TemplateResponse::RENDER_AS_ERROR
265
+        ) {
266
+            $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
267
+        } else {
268
+            // If we ignore the scss compiler,
269
+            // we need to load the guest css fallback
270
+            Util::addStyle('guest');
271
+            $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
272
+        }
273
+
274
+        $page->assign('cssfiles', []);
275
+        $page->assign('printcssfiles', []);
276
+        $this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
277
+        foreach ($cssFiles as $info) {
278
+            $web = $info[1];
279
+            $file = $info[2];
280
+
281
+            if (str_ends_with($file, 'print.css')) {
282
+                $page->append('printcssfiles', $web . '/' . $file . $this->getVersionHashSuffix());
283
+            } else {
284
+                $suffix = $this->getVersionHashSuffix($web, $file);
285
+
286
+                if (!str_contains($file, '?v=')) {
287
+                    $page->append('cssfiles', $web . '/' . $file . $suffix);
288
+                } else {
289
+                    $page->append('cssfiles', $web . '/' . $file . '-' . substr($suffix, 3));
290
+                }
291
+            }
292
+        }
293
+
294
+        if ($request->isUserAgent([Request::USER_AGENT_CLIENT_IOS, Request::USER_AGENT_SAFARI, Request::USER_AGENT_SAFARI_MOBILE])) {
295
+            // Prevent auto zoom with iOS but still allow user zoom
296
+            // On chrome (and others) this does not work (will also disable user zoom)
297
+            $page->assign('viewport_maximum_scale', '1.0');
298
+        }
299
+
300
+        $page->assign('initialStates', $this->initialState->getInitialStates());
301
+
302
+        $page->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
303
+        $page->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
304
+
305
+        return $page;
306
+    }
307
+
308
+    protected function getVersionHashSuffix(string $path = '', string $file = ''): string {
309
+        if ($this->config->getSystemValueBool('debug', false)) {
310
+            // allows chrome workspace mapping in debug mode
311
+            return '';
312
+        }
313
+
314
+        if ($this->config->getSystemValueBool('installed', false) === false) {
315
+            // if not installed just return the version hash
316
+            return '?v=' . self::$versionHash;
317
+        }
318
+
319
+        $hash = false;
320
+        // Try the web-root first
321
+        if ($path !== '') {
322
+            $hash = $this->getVersionHashByPath($path);
323
+        }
324
+        // If not found try the file
325
+        if ($hash === false && $file !== '') {
326
+            $hash = $this->getVersionHashByPath($file);
327
+        }
328
+        // As a last resort we use the server version hash
329
+        if ($hash === false) {
330
+            $hash = self::$versionHash;
331
+        }
332
+
333
+        // The theming app is force-enabled thus the cache buster is always available
334
+        $themingSuffix = '-' . $this->config->getAppValue('theming', 'cachebuster', '0');
335
+
336
+        return '?v=' . $hash . $themingSuffix;
337
+    }
338
+
339
+    private function getVersionHashByPath(string $path): string|false {
340
+        if (array_key_exists($path, self::$cacheBusterCache) === false) {
341
+            // Not yet cached, so lets find the cache buster string
342
+            $appId = $this->getAppNamefromPath($path);
343
+            if ($appId === false) {
344
+                // No app Id could be guessed
345
+                return false;
346
+            }
347
+
348
+            if ($appId === 'core') {
349
+                // core is not a real app but the server itself
350
+                $hash = self::$versionHash;
351
+            } else {
352
+                $appVersion = $this->appManager->getAppVersion($appId);
353
+                // For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
354
+                if ($this->appManager->isShipped($appId)) {
355
+                    $appVersion .= '-' . self::$versionHash;
356
+                }
357
+
358
+                $hash = substr(md5($appVersion), 0, 8);
359
+            }
360
+            self::$cacheBusterCache[$path] = $hash;
361
+        }
362
+
363
+        return self::$cacheBusterCache[$path];
364
+    }
365
+
366
+    public static function findStylesheetFiles(array $styles): array {
367
+        if (!self::$cssLocator) {
368
+            self::$cssLocator = \OCP\Server::get(CSSResourceLocator::class);
369
+        }
370
+        self::$cssLocator->find($styles);
371
+        return self::$cssLocator->getResources();
372
+    }
373
+
374
+    public function getAppNamefromPath(string $path): string|false {
375
+        if ($path !== '') {
376
+            $pathParts = explode('/', $path);
377
+            if ($pathParts[0] === 'css') {
378
+                // This is a scss request
379
+                return $pathParts[1];
380
+            } elseif ($pathParts[0] === 'core') {
381
+                return 'core';
382
+            }
383
+            return end($pathParts);
384
+        }
385
+        return false;
386
+    }
387
+
388
+    public static function findJavascriptFiles(array $scripts): array {
389
+        if (!self::$jsLocator) {
390
+            self::$jsLocator = \OCP\Server::get(JSResourceLocator::class);
391
+        }
392
+        self::$jsLocator->find($scripts);
393
+        return self::$jsLocator->getResources();
394
+    }
395
+
396
+    /**
397
+     * Converts the absolute file path to a relative path from \OC::$SERVERROOT
398
+     * @param string $filePath Absolute path
399
+     * @return string Relative path
400
+     * @throws \Exception If $filePath is not under \OC::$SERVERROOT
401
+     */
402
+    public static function convertToRelativePath(string $filePath) {
403
+        $relativePath = explode(\OC::$SERVERROOT, $filePath);
404
+        if (count($relativePath) !== 2) {
405
+            throw new \Exception('$filePath is not under the \OC::$SERVERROOT');
406
+        }
407
+
408
+        return $relativePath[1];
409
+    }
410 410
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +1984 added lines, -1984 removed lines patch added patch discarded remove patch
@@ -62,2005 +62,2005 @@
 block discarded – undo
62 62
  */
63 63
 class Manager implements IManager {
64 64
 
65
-	private ?IL10N $l;
66
-	private LegacyHooks $legacyHooks;
67
-
68
-	public function __construct(
69
-		private LoggerInterface $logger,
70
-		private IConfig $config,
71
-		private ISecureRandom $secureRandom,
72
-		private IHasher $hasher,
73
-		private IMountManager $mountManager,
74
-		private IGroupManager $groupManager,
75
-		private IFactory $l10nFactory,
76
-		private IProviderFactory $factory,
77
-		private IUserManager $userManager,
78
-		private IRootFolder $rootFolder,
79
-		private IMailer $mailer,
80
-		private IURLGenerator $urlGenerator,
81
-		private \OC_Defaults $defaults,
82
-		private IEventDispatcher $dispatcher,
83
-		private IUserSession $userSession,
84
-		private KnownUserService $knownUserService,
85
-		private ShareDisableChecker $shareDisableChecker,
86
-		private IDateTimeZone $dateTimeZone,
87
-		private IAppConfig $appConfig,
88
-	) {
89
-		$this->l = $this->l10nFactory->get('lib');
90
-		// The constructor of LegacyHooks registers the listeners of share events
91
-		// do not remove if those are not properly migrated
92
-		$this->legacyHooks = new LegacyHooks($this->dispatcher);
93
-	}
94
-
95
-	/**
96
-	 * Convert from a full share id to a tuple (providerId, shareId)
97
-	 *
98
-	 * @param string $id
99
-	 * @return string[]
100
-	 */
101
-	private function splitFullId($id) {
102
-		return explode(':', $id, 2);
103
-	}
104
-
105
-	/**
106
-	 * Verify if a password meets all requirements
107
-	 *
108
-	 * @param string $password
109
-	 * @throws HintException
110
-	 */
111
-	protected function verifyPassword($password) {
112
-		if ($password === null) {
113
-			// No password is set, check if this is allowed.
114
-			if ($this->shareApiLinkEnforcePassword()) {
115
-				throw new \InvalidArgumentException($this->l->t('Passwords are enforced for link and mail shares'));
116
-			}
117
-
118
-			return;
119
-		}
120
-
121
-		// Let others verify the password
122
-		try {
123
-			$event = new ValidatePasswordPolicyEvent($password, PasswordContext::SHARING);
124
-			$this->dispatcher->dispatchTyped($event);
125
-		} catch (HintException $e) {
126
-			/* Wrap in a 400 bad request error */
127
-			throw new HintException($e->getMessage(), $e->getHint(), 400, $e);
128
-		}
129
-	}
130
-
131
-	/**
132
-	 * Check for generic requirements before creating a share
133
-	 *
134
-	 * @param IShare $share
135
-	 * @throws \InvalidArgumentException
136
-	 * @throws GenericShareException
137
-	 *
138
-	 * @suppress PhanUndeclaredClassMethod
139
-	 */
140
-	protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
141
-		if ($share->getShareType() === IShare::TYPE_USER) {
142
-			// We expect a valid user as sharedWith for user shares
143
-			if (!$this->userManager->userExists($share->getSharedWith())) {
144
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid user'));
145
-			}
146
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
147
-			// We expect a valid group as sharedWith for group shares
148
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
149
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid group'));
150
-			}
151
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
152
-			// No check for TYPE_EMAIL here as we have a recipient for them
153
-			if ($share->getSharedWith() !== null) {
154
-				throw new \InvalidArgumentException($this->l->t('Share recipient should be empty'));
155
-			}
156
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
157
-			if ($share->getSharedWith() === null) {
158
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
159
-			}
160
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
161
-			if ($share->getSharedWith() === null) {
162
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
163
-			}
164
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
165
-			if ($share->getSharedWith() === null) {
166
-				throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
167
-			}
168
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
169
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
170
-			if ($circle === null) {
171
-				throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid circle'));
172
-			}
173
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
174
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
175
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
176
-		} else {
177
-			// We cannot handle other types yet
178
-			throw new \InvalidArgumentException($this->l->t('Unknown share type'));
179
-		}
180
-
181
-		// Verify the initiator of the share is set
182
-		if ($share->getSharedBy() === null) {
183
-			throw new \InvalidArgumentException($this->l->t('Share initiator must be set'));
184
-		}
185
-
186
-		// Cannot share with yourself
187
-		if ($share->getShareType() === IShare::TYPE_USER
188
-			&& $share->getSharedWith() === $share->getSharedBy()) {
189
-			throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
190
-		}
191
-
192
-		// The path should be set
193
-		if ($share->getNode() === null) {
194
-			throw new \InvalidArgumentException($this->l->t('Shared path must be set'));
195
-		}
196
-
197
-		// And it should be a file or a folder
198
-		if (!($share->getNode() instanceof \OCP\Files\File)
199
-			&& !($share->getNode() instanceof \OCP\Files\Folder)) {
200
-			throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
201
-		}
202
-
203
-		// And you cannot share your rootfolder
204
-		if ($this->userManager->userExists($share->getSharedBy())) {
205
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
206
-		} else {
207
-			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
208
-		}
209
-		if ($userFolder->getId() === $share->getNode()->getId()) {
210
-			throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
211
-		}
212
-
213
-		// Check if we actually have share permissions
214
-		if (!$share->getNode()->isShareable()) {
215
-			throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
216
-		}
217
-
218
-		// Permissions should be set
219
-		if ($share->getPermissions() === null) {
220
-			throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
221
-		}
222
-
223
-		// Permissions must be valid
224
-		if ($share->getPermissions() < 0 || $share->getPermissions() > \OCP\Constants::PERMISSION_ALL) {
225
-			throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
226
-		}
227
-
228
-		// Single file shares should never have delete or create permissions
229
-		if (($share->getNode() instanceof File)
230
-			&& (($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE)) !== 0)) {
231
-			throw new \InvalidArgumentException($this->l->t('File shares cannot have create or delete permissions'));
232
-		}
233
-
234
-		$permissions = 0;
235
-		$nodesForUser = $userFolder->getById($share->getNodeId());
236
-		foreach ($nodesForUser as $node) {
237
-			if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
238
-				// for the root of non-movable mount, the permissions we see if limited by the mount itself,
239
-				// so we instead use the "raw" permissions from the storage
240
-				$permissions |= $node->getStorage()->getPermissions('');
241
-			} else {
242
-				$permissions |= $node->getPermissions();
243
-			}
244
-		}
245
-
246
-		// Check that we do not share with more permissions than we have
247
-		if ($share->getPermissions() & ~$permissions) {
248
-			$path = $userFolder->getRelativePath($share->getNode()->getPath());
249
-			throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
250
-		}
251
-
252
-		// Check that read permissions are always set
253
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
254
-		$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
255
-			|| $share->getShareType() === IShare::TYPE_EMAIL;
256
-		if (!$noReadPermissionRequired
257
-			&& ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
258
-			throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
259
-		}
260
-
261
-		if ($share->getNode() instanceof \OCP\Files\File) {
262
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
263
-				throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
264
-			}
265
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
266
-				throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
267
-			}
268
-		}
269
-	}
270
-
271
-	/**
272
-	 * Validate if the expiration date fits the system settings
273
-	 *
274
-	 * @param IShare $share The share to validate the expiration date of
275
-	 * @return IShare The modified share object
276
-	 * @throws GenericShareException
277
-	 * @throws \InvalidArgumentException
278
-	 * @throws \Exception
279
-	 */
280
-	protected function validateExpirationDateInternal(IShare $share) {
281
-		$isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
282
-
283
-		$expirationDate = $share->getExpirationDate();
284
-
285
-		if ($isRemote) {
286
-			$defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
287
-			$defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
288
-			$configProp = 'remote_defaultExpDays';
289
-			$isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
290
-		} else {
291
-			$defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
292
-			$defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
293
-			$configProp = 'internal_defaultExpDays';
294
-			$isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
295
-		}
296
-
297
-		// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
298
-		// Then skip expiration date validation as null is accepted
299
-		if (!$share->getNoExpirationDate() || $isEnforced) {
300
-			if ($expirationDate !== null) {
301
-				$expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
302
-				$expirationDate->setTime(0, 0, 0);
303
-
304
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
305
-				$date->setTime(0, 0, 0);
306
-				if ($date >= $expirationDate) {
307
-					throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
308
-				}
309
-			}
310
-
311
-			// If expiredate is empty set a default one if there is a default
312
-			$fullId = null;
313
-			try {
314
-				$fullId = $share->getFullId();
315
-			} catch (\UnexpectedValueException $e) {
316
-				// This is a new share
317
-			}
318
-
319
-			if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
320
-				$expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
321
-				$expirationDate->setTime(0, 0, 0);
322
-				$days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
323
-				if ($days > $defaultExpireDays) {
324
-					$days = $defaultExpireDays;
325
-				}
326
-				$expirationDate->add(new \DateInterval('P' . $days . 'D'));
327
-			}
328
-
329
-			// If we enforce the expiration date check that is does not exceed
330
-			if ($isEnforced) {
331
-				if (empty($expirationDate)) {
332
-					throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
333
-				}
334
-
335
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
336
-				$date->setTime(0, 0, 0);
337
-				$date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
338
-				if ($date < $expirationDate) {
339
-					throw new GenericShareException($this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays), code: 404);
340
-				}
341
-			}
342
-		}
343
-
344
-		$accepted = true;
345
-		$message = '';
346
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
347
-			'expirationDate' => &$expirationDate,
348
-			'accepted' => &$accepted,
349
-			'message' => &$message,
350
-			'passwordSet' => $share->getPassword() !== null,
351
-		]);
352
-
353
-		if (!$accepted) {
354
-			throw new \Exception($message);
355
-		}
356
-
357
-		$share->setExpirationDate($expirationDate);
358
-
359
-		return $share;
360
-	}
361
-
362
-	/**
363
-	 * Validate if the expiration date fits the system settings
364
-	 *
365
-	 * @param IShare $share The share to validate the expiration date of
366
-	 * @return IShare The modified share object
367
-	 * @throws GenericShareException
368
-	 * @throws \InvalidArgumentException
369
-	 * @throws \Exception
370
-	 */
371
-	protected function validateExpirationDateLink(IShare $share) {
372
-		$expirationDate = $share->getExpirationDate();
373
-		$isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
374
-
375
-		// If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
376
-		// Then skip expiration date validation as null is accepted
377
-		if (!($share->getNoExpirationDate() && !$isEnforced)) {
378
-			if ($expirationDate !== null) {
379
-				$expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
380
-				$expirationDate->setTime(0, 0, 0);
381
-
382
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
383
-				$date->setTime(0, 0, 0);
384
-				if ($date >= $expirationDate) {
385
-					throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
386
-				}
387
-			}
388
-
389
-			// If expiredate is empty set a default one if there is a default
390
-			$fullId = null;
391
-			try {
392
-				$fullId = $share->getFullId();
393
-			} catch (\UnexpectedValueException $e) {
394
-				// This is a new share
395
-			}
396
-
397
-			if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
398
-				$expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
399
-				$expirationDate->setTime(0, 0, 0);
400
-
401
-				$days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
402
-				if ($days > $this->shareApiLinkDefaultExpireDays()) {
403
-					$days = $this->shareApiLinkDefaultExpireDays();
404
-				}
405
-				$expirationDate->add(new \DateInterval('P' . $days . 'D'));
406
-			}
407
-
408
-			// If we enforce the expiration date check that is does not exceed
409
-			if ($isEnforced) {
410
-				if (empty($expirationDate)) {
411
-					throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
412
-				}
413
-
414
-				$date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
415
-				$date->setTime(0, 0, 0);
416
-				$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
417
-				if ($date < $expirationDate) {
418
-					throw new GenericShareException(
419
-						$this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays()),
420
-						code: 404,
421
-					);
422
-				}
423
-			}
424
-
425
-		}
426
-
427
-		$accepted = true;
428
-		$message = '';
429
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
430
-			'expirationDate' => &$expirationDate,
431
-			'accepted' => &$accepted,
432
-			'message' => &$message,
433
-			'passwordSet' => $share->getPassword() !== null,
434
-		]);
435
-
436
-		if (!$accepted) {
437
-			throw new \Exception($message);
438
-		}
439
-
440
-		$share->setExpirationDate($expirationDate);
441
-
442
-		return $share;
443
-	}
444
-
445
-	/**
446
-	 * Check for pre share requirements for user shares
447
-	 *
448
-	 * @param IShare $share
449
-	 * @throws \Exception
450
-	 */
451
-	protected function userCreateChecks(IShare $share) {
452
-		// Check if we can share with group members only
453
-		if ($this->shareWithGroupMembersOnly()) {
454
-			$sharedBy = $this->userManager->get($share->getSharedBy());
455
-			$sharedWith = $this->userManager->get($share->getSharedWith());
456
-			// Verify we can share with this user
457
-			$groups = array_intersect(
458
-				$this->groupManager->getUserGroupIds($sharedBy),
459
-				$this->groupManager->getUserGroupIds($sharedWith)
460
-			);
461
-
462
-			// optional excluded groups
463
-			$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
464
-			$groups = array_diff($groups, $excludedGroups);
465
-
466
-			if (empty($groups)) {
467
-				throw new \Exception($this->l->t('Sharing is only allowed with group members'));
468
-			}
469
-		}
470
-
471
-		/*
65
+    private ?IL10N $l;
66
+    private LegacyHooks $legacyHooks;
67
+
68
+    public function __construct(
69
+        private LoggerInterface $logger,
70
+        private IConfig $config,
71
+        private ISecureRandom $secureRandom,
72
+        private IHasher $hasher,
73
+        private IMountManager $mountManager,
74
+        private IGroupManager $groupManager,
75
+        private IFactory $l10nFactory,
76
+        private IProviderFactory $factory,
77
+        private IUserManager $userManager,
78
+        private IRootFolder $rootFolder,
79
+        private IMailer $mailer,
80
+        private IURLGenerator $urlGenerator,
81
+        private \OC_Defaults $defaults,
82
+        private IEventDispatcher $dispatcher,
83
+        private IUserSession $userSession,
84
+        private KnownUserService $knownUserService,
85
+        private ShareDisableChecker $shareDisableChecker,
86
+        private IDateTimeZone $dateTimeZone,
87
+        private IAppConfig $appConfig,
88
+    ) {
89
+        $this->l = $this->l10nFactory->get('lib');
90
+        // The constructor of LegacyHooks registers the listeners of share events
91
+        // do not remove if those are not properly migrated
92
+        $this->legacyHooks = new LegacyHooks($this->dispatcher);
93
+    }
94
+
95
+    /**
96
+     * Convert from a full share id to a tuple (providerId, shareId)
97
+     *
98
+     * @param string $id
99
+     * @return string[]
100
+     */
101
+    private function splitFullId($id) {
102
+        return explode(':', $id, 2);
103
+    }
104
+
105
+    /**
106
+     * Verify if a password meets all requirements
107
+     *
108
+     * @param string $password
109
+     * @throws HintException
110
+     */
111
+    protected function verifyPassword($password) {
112
+        if ($password === null) {
113
+            // No password is set, check if this is allowed.
114
+            if ($this->shareApiLinkEnforcePassword()) {
115
+                throw new \InvalidArgumentException($this->l->t('Passwords are enforced for link and mail shares'));
116
+            }
117
+
118
+            return;
119
+        }
120
+
121
+        // Let others verify the password
122
+        try {
123
+            $event = new ValidatePasswordPolicyEvent($password, PasswordContext::SHARING);
124
+            $this->dispatcher->dispatchTyped($event);
125
+        } catch (HintException $e) {
126
+            /* Wrap in a 400 bad request error */
127
+            throw new HintException($e->getMessage(), $e->getHint(), 400, $e);
128
+        }
129
+    }
130
+
131
+    /**
132
+     * Check for generic requirements before creating a share
133
+     *
134
+     * @param IShare $share
135
+     * @throws \InvalidArgumentException
136
+     * @throws GenericShareException
137
+     *
138
+     * @suppress PhanUndeclaredClassMethod
139
+     */
140
+    protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
141
+        if ($share->getShareType() === IShare::TYPE_USER) {
142
+            // We expect a valid user as sharedWith for user shares
143
+            if (!$this->userManager->userExists($share->getSharedWith())) {
144
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid user'));
145
+            }
146
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
147
+            // We expect a valid group as sharedWith for group shares
148
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
149
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid group'));
150
+            }
151
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
152
+            // No check for TYPE_EMAIL here as we have a recipient for them
153
+            if ($share->getSharedWith() !== null) {
154
+                throw new \InvalidArgumentException($this->l->t('Share recipient should be empty'));
155
+            }
156
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
157
+            if ($share->getSharedWith() === null) {
158
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
159
+            }
160
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
161
+            if ($share->getSharedWith() === null) {
162
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
163
+            }
164
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
165
+            if ($share->getSharedWith() === null) {
166
+                throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty'));
167
+            }
168
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
169
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
170
+            if ($circle === null) {
171
+                throw new \InvalidArgumentException($this->l->t('Share recipient is not a valid circle'));
172
+            }
173
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
174
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
175
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
176
+        } else {
177
+            // We cannot handle other types yet
178
+            throw new \InvalidArgumentException($this->l->t('Unknown share type'));
179
+        }
180
+
181
+        // Verify the initiator of the share is set
182
+        if ($share->getSharedBy() === null) {
183
+            throw new \InvalidArgumentException($this->l->t('Share initiator must be set'));
184
+        }
185
+
186
+        // Cannot share with yourself
187
+        if ($share->getShareType() === IShare::TYPE_USER
188
+            && $share->getSharedWith() === $share->getSharedBy()) {
189
+            throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
190
+        }
191
+
192
+        // The path should be set
193
+        if ($share->getNode() === null) {
194
+            throw new \InvalidArgumentException($this->l->t('Shared path must be set'));
195
+        }
196
+
197
+        // And it should be a file or a folder
198
+        if (!($share->getNode() instanceof \OCP\Files\File)
199
+            && !($share->getNode() instanceof \OCP\Files\Folder)) {
200
+            throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
201
+        }
202
+
203
+        // And you cannot share your rootfolder
204
+        if ($this->userManager->userExists($share->getSharedBy())) {
205
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
206
+        } else {
207
+            $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
208
+        }
209
+        if ($userFolder->getId() === $share->getNode()->getId()) {
210
+            throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
211
+        }
212
+
213
+        // Check if we actually have share permissions
214
+        if (!$share->getNode()->isShareable()) {
215
+            throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
216
+        }
217
+
218
+        // Permissions should be set
219
+        if ($share->getPermissions() === null) {
220
+            throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
221
+        }
222
+
223
+        // Permissions must be valid
224
+        if ($share->getPermissions() < 0 || $share->getPermissions() > \OCP\Constants::PERMISSION_ALL) {
225
+            throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
226
+        }
227
+
228
+        // Single file shares should never have delete or create permissions
229
+        if (($share->getNode() instanceof File)
230
+            && (($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE)) !== 0)) {
231
+            throw new \InvalidArgumentException($this->l->t('File shares cannot have create or delete permissions'));
232
+        }
233
+
234
+        $permissions = 0;
235
+        $nodesForUser = $userFolder->getById($share->getNodeId());
236
+        foreach ($nodesForUser as $node) {
237
+            if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
238
+                // for the root of non-movable mount, the permissions we see if limited by the mount itself,
239
+                // so we instead use the "raw" permissions from the storage
240
+                $permissions |= $node->getStorage()->getPermissions('');
241
+            } else {
242
+                $permissions |= $node->getPermissions();
243
+            }
244
+        }
245
+
246
+        // Check that we do not share with more permissions than we have
247
+        if ($share->getPermissions() & ~$permissions) {
248
+            $path = $userFolder->getRelativePath($share->getNode()->getPath());
249
+            throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
250
+        }
251
+
252
+        // Check that read permissions are always set
253
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
254
+        $noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
255
+            || $share->getShareType() === IShare::TYPE_EMAIL;
256
+        if (!$noReadPermissionRequired
257
+            && ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
258
+            throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
259
+        }
260
+
261
+        if ($share->getNode() instanceof \OCP\Files\File) {
262
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
263
+                throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
264
+            }
265
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
266
+                throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
267
+            }
268
+        }
269
+    }
270
+
271
+    /**
272
+     * Validate if the expiration date fits the system settings
273
+     *
274
+     * @param IShare $share The share to validate the expiration date of
275
+     * @return IShare The modified share object
276
+     * @throws GenericShareException
277
+     * @throws \InvalidArgumentException
278
+     * @throws \Exception
279
+     */
280
+    protected function validateExpirationDateInternal(IShare $share) {
281
+        $isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
282
+
283
+        $expirationDate = $share->getExpirationDate();
284
+
285
+        if ($isRemote) {
286
+            $defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
287
+            $defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
288
+            $configProp = 'remote_defaultExpDays';
289
+            $isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
290
+        } else {
291
+            $defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
292
+            $defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
293
+            $configProp = 'internal_defaultExpDays';
294
+            $isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
295
+        }
296
+
297
+        // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
298
+        // Then skip expiration date validation as null is accepted
299
+        if (!$share->getNoExpirationDate() || $isEnforced) {
300
+            if ($expirationDate !== null) {
301
+                $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
302
+                $expirationDate->setTime(0, 0, 0);
303
+
304
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
305
+                $date->setTime(0, 0, 0);
306
+                if ($date >= $expirationDate) {
307
+                    throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
308
+                }
309
+            }
310
+
311
+            // If expiredate is empty set a default one if there is a default
312
+            $fullId = null;
313
+            try {
314
+                $fullId = $share->getFullId();
315
+            } catch (\UnexpectedValueException $e) {
316
+                // This is a new share
317
+            }
318
+
319
+            if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
320
+                $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
321
+                $expirationDate->setTime(0, 0, 0);
322
+                $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
323
+                if ($days > $defaultExpireDays) {
324
+                    $days = $defaultExpireDays;
325
+                }
326
+                $expirationDate->add(new \DateInterval('P' . $days . 'D'));
327
+            }
328
+
329
+            // If we enforce the expiration date check that is does not exceed
330
+            if ($isEnforced) {
331
+                if (empty($expirationDate)) {
332
+                    throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
333
+                }
334
+
335
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
336
+                $date->setTime(0, 0, 0);
337
+                $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
338
+                if ($date < $expirationDate) {
339
+                    throw new GenericShareException($this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays), code: 404);
340
+                }
341
+            }
342
+        }
343
+
344
+        $accepted = true;
345
+        $message = '';
346
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
347
+            'expirationDate' => &$expirationDate,
348
+            'accepted' => &$accepted,
349
+            'message' => &$message,
350
+            'passwordSet' => $share->getPassword() !== null,
351
+        ]);
352
+
353
+        if (!$accepted) {
354
+            throw new \Exception($message);
355
+        }
356
+
357
+        $share->setExpirationDate($expirationDate);
358
+
359
+        return $share;
360
+    }
361
+
362
+    /**
363
+     * Validate if the expiration date fits the system settings
364
+     *
365
+     * @param IShare $share The share to validate the expiration date of
366
+     * @return IShare The modified share object
367
+     * @throws GenericShareException
368
+     * @throws \InvalidArgumentException
369
+     * @throws \Exception
370
+     */
371
+    protected function validateExpirationDateLink(IShare $share) {
372
+        $expirationDate = $share->getExpirationDate();
373
+        $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
374
+
375
+        // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
376
+        // Then skip expiration date validation as null is accepted
377
+        if (!($share->getNoExpirationDate() && !$isEnforced)) {
378
+            if ($expirationDate !== null) {
379
+                $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
380
+                $expirationDate->setTime(0, 0, 0);
381
+
382
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
383
+                $date->setTime(0, 0, 0);
384
+                if ($date >= $expirationDate) {
385
+                    throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
386
+                }
387
+            }
388
+
389
+            // If expiredate is empty set a default one if there is a default
390
+            $fullId = null;
391
+            try {
392
+                $fullId = $share->getFullId();
393
+            } catch (\UnexpectedValueException $e) {
394
+                // This is a new share
395
+            }
396
+
397
+            if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
398
+                $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
399
+                $expirationDate->setTime(0, 0, 0);
400
+
401
+                $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
402
+                if ($days > $this->shareApiLinkDefaultExpireDays()) {
403
+                    $days = $this->shareApiLinkDefaultExpireDays();
404
+                }
405
+                $expirationDate->add(new \DateInterval('P' . $days . 'D'));
406
+            }
407
+
408
+            // If we enforce the expiration date check that is does not exceed
409
+            if ($isEnforced) {
410
+                if (empty($expirationDate)) {
411
+                    throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
412
+                }
413
+
414
+                $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
415
+                $date->setTime(0, 0, 0);
416
+                $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
417
+                if ($date < $expirationDate) {
418
+                    throw new GenericShareException(
419
+                        $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays()),
420
+                        code: 404,
421
+                    );
422
+                }
423
+            }
424
+
425
+        }
426
+
427
+        $accepted = true;
428
+        $message = '';
429
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
430
+            'expirationDate' => &$expirationDate,
431
+            'accepted' => &$accepted,
432
+            'message' => &$message,
433
+            'passwordSet' => $share->getPassword() !== null,
434
+        ]);
435
+
436
+        if (!$accepted) {
437
+            throw new \Exception($message);
438
+        }
439
+
440
+        $share->setExpirationDate($expirationDate);
441
+
442
+        return $share;
443
+    }
444
+
445
+    /**
446
+     * Check for pre share requirements for user shares
447
+     *
448
+     * @param IShare $share
449
+     * @throws \Exception
450
+     */
451
+    protected function userCreateChecks(IShare $share) {
452
+        // Check if we can share with group members only
453
+        if ($this->shareWithGroupMembersOnly()) {
454
+            $sharedBy = $this->userManager->get($share->getSharedBy());
455
+            $sharedWith = $this->userManager->get($share->getSharedWith());
456
+            // Verify we can share with this user
457
+            $groups = array_intersect(
458
+                $this->groupManager->getUserGroupIds($sharedBy),
459
+                $this->groupManager->getUserGroupIds($sharedWith)
460
+            );
461
+
462
+            // optional excluded groups
463
+            $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
464
+            $groups = array_diff($groups, $excludedGroups);
465
+
466
+            if (empty($groups)) {
467
+                throw new \Exception($this->l->t('Sharing is only allowed with group members'));
468
+            }
469
+        }
470
+
471
+        /*
472 472
 		 * TODO: Could be costly, fix
473 473
 		 *
474 474
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
475 475
 		 */
476
-		$provider = $this->factory->getProviderForType(IShare::TYPE_USER);
477
-		$existingShares = $provider->getSharesByPath($share->getNode());
478
-		foreach ($existingShares as $existingShare) {
479
-			// Ignore if it is the same share
480
-			try {
481
-				if ($existingShare->getFullId() === $share->getFullId()) {
482
-					continue;
483
-				}
484
-			} catch (\UnexpectedValueException $e) {
485
-				//Shares are not identical
486
-			}
487
-
488
-			// Identical share already exists
489
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
490
-				throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
491
-			}
492
-
493
-			// The share is already shared with this user via a group share
494
-			if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
495
-				$group = $this->groupManager->get($existingShare->getSharedWith());
496
-				if (!is_null($group)) {
497
-					$user = $this->userManager->get($share->getSharedWith());
498
-
499
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
500
-						throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
501
-					}
502
-				}
503
-			}
504
-		}
505
-	}
506
-
507
-	/**
508
-	 * Check for pre share requirements for group shares
509
-	 *
510
-	 * @param IShare $share
511
-	 * @throws \Exception
512
-	 */
513
-	protected function groupCreateChecks(IShare $share) {
514
-		// Verify group shares are allowed
515
-		if (!$this->allowGroupSharing()) {
516
-			throw new \Exception($this->l->t('Group sharing is now allowed'));
517
-		}
518
-
519
-		// Verify if the user can share with this group
520
-		if ($this->shareWithGroupMembersOnly()) {
521
-			$sharedBy = $this->userManager->get($share->getSharedBy());
522
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
523
-
524
-			// optional excluded groups
525
-			$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
526
-			if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
527
-				throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
528
-			}
529
-		}
530
-
531
-		/*
476
+        $provider = $this->factory->getProviderForType(IShare::TYPE_USER);
477
+        $existingShares = $provider->getSharesByPath($share->getNode());
478
+        foreach ($existingShares as $existingShare) {
479
+            // Ignore if it is the same share
480
+            try {
481
+                if ($existingShare->getFullId() === $share->getFullId()) {
482
+                    continue;
483
+                }
484
+            } catch (\UnexpectedValueException $e) {
485
+                //Shares are not identical
486
+            }
487
+
488
+            // Identical share already exists
489
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
490
+                throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
491
+            }
492
+
493
+            // The share is already shared with this user via a group share
494
+            if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
495
+                $group = $this->groupManager->get($existingShare->getSharedWith());
496
+                if (!is_null($group)) {
497
+                    $user = $this->userManager->get($share->getSharedWith());
498
+
499
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
500
+                        throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
501
+                    }
502
+                }
503
+            }
504
+        }
505
+    }
506
+
507
+    /**
508
+     * Check for pre share requirements for group shares
509
+     *
510
+     * @param IShare $share
511
+     * @throws \Exception
512
+     */
513
+    protected function groupCreateChecks(IShare $share) {
514
+        // Verify group shares are allowed
515
+        if (!$this->allowGroupSharing()) {
516
+            throw new \Exception($this->l->t('Group sharing is now allowed'));
517
+        }
518
+
519
+        // Verify if the user can share with this group
520
+        if ($this->shareWithGroupMembersOnly()) {
521
+            $sharedBy = $this->userManager->get($share->getSharedBy());
522
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
523
+
524
+            // optional excluded groups
525
+            $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
526
+            if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
527
+                throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
528
+            }
529
+        }
530
+
531
+        /*
532 532
 		 * TODO: Could be costly, fix
533 533
 		 *
534 534
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
535 535
 		 */
536
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
537
-		$existingShares = $provider->getSharesByPath($share->getNode());
538
-		foreach ($existingShares as $existingShare) {
539
-			try {
540
-				if ($existingShare->getFullId() === $share->getFullId()) {
541
-					continue;
542
-				}
543
-			} catch (\UnexpectedValueException $e) {
544
-				//It is a new share so just continue
545
-			}
546
-
547
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
548
-				throw new AlreadySharedException($this->l->t('Path is already shared with this group'), $existingShare);
549
-			}
550
-		}
551
-	}
552
-
553
-	/**
554
-	 * Check for pre share requirements for link shares
555
-	 *
556
-	 * @param IShare $share
557
-	 * @throws \Exception
558
-	 */
559
-	protected function linkCreateChecks(IShare $share) {
560
-		// Are link shares allowed?
561
-		if (!$this->shareApiAllowLinks()) {
562
-			throw new \Exception($this->l->t('Link sharing is not allowed'));
563
-		}
564
-
565
-		// Check if public upload is allowed
566
-		if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
567
-			&& ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
568
-			throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
569
-		}
570
-	}
571
-
572
-	/**
573
-	 * To make sure we don't get invisible link shares we set the parent
574
-	 * of a link if it is a reshare. This is a quick word around
575
-	 * until we can properly display multiple link shares in the UI
576
-	 *
577
-	 * See: https://github.com/owncloud/core/issues/22295
578
-	 *
579
-	 * FIXME: Remove once multiple link shares can be properly displayed
580
-	 *
581
-	 * @param IShare $share
582
-	 */
583
-	protected function setLinkParent(IShare $share) {
584
-		$storage = $share->getNode()->getStorage();
585
-		if ($storage->instanceOfStorage(SharedStorage::class)) {
586
-			/** @var \OCA\Files_Sharing\SharedStorage $storage */
587
-			$share->setParent((int)$storage->getShareId());
588
-		}
589
-	}
590
-
591
-	/**
592
-	 * @param File|Folder $path
593
-	 */
594
-	protected function pathCreateChecks($path) {
595
-		// Make sure that we do not share a path that contains a shared mountpoint
596
-		if ($path instanceof \OCP\Files\Folder) {
597
-			$mounts = $this->mountManager->findIn($path->getPath());
598
-			foreach ($mounts as $mount) {
599
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
600
-					// Using a flat sharing model ensures the file owner can always see who has access.
601
-					// Allowing parent folder sharing would require tracking inherited access, which adds complexity
602
-					// and hurts performance/scalability.
603
-					// So we forbid sharing a parent folder of a share you received.
604
-					throw new \InvalidArgumentException($this->l->t('You cannot share a folder that contains other shares'));
605
-				}
606
-			}
607
-		}
608
-	}
609
-
610
-	/**
611
-	 * Check if the user that is sharing can actually share
612
-	 *
613
-	 * @param IShare $share
614
-	 * @throws \Exception
615
-	 */
616
-	protected function canShare(IShare $share) {
617
-		if (!$this->shareApiEnabled()) {
618
-			throw new \Exception($this->l->t('Sharing is disabled'));
619
-		}
620
-
621
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
622
-			throw new \Exception($this->l->t('Sharing is disabled for you'));
623
-		}
624
-	}
625
-
626
-	/**
627
-	 * Share a path
628
-	 *
629
-	 * @param IShare $share
630
-	 * @return IShare The share object
631
-	 * @throws \Exception
632
-	 *
633
-	 * TODO: handle link share permissions or check them
634
-	 */
635
-	public function createShare(IShare $share) {
636
-		$this->canShare($share);
637
-
638
-		$this->generalCreateChecks($share);
639
-
640
-		// Verify if there are any issues with the path
641
-		$this->pathCreateChecks($share->getNode());
642
-
643
-		/*
536
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
537
+        $existingShares = $provider->getSharesByPath($share->getNode());
538
+        foreach ($existingShares as $existingShare) {
539
+            try {
540
+                if ($existingShare->getFullId() === $share->getFullId()) {
541
+                    continue;
542
+                }
543
+            } catch (\UnexpectedValueException $e) {
544
+                //It is a new share so just continue
545
+            }
546
+
547
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
548
+                throw new AlreadySharedException($this->l->t('Path is already shared with this group'), $existingShare);
549
+            }
550
+        }
551
+    }
552
+
553
+    /**
554
+     * Check for pre share requirements for link shares
555
+     *
556
+     * @param IShare $share
557
+     * @throws \Exception
558
+     */
559
+    protected function linkCreateChecks(IShare $share) {
560
+        // Are link shares allowed?
561
+        if (!$this->shareApiAllowLinks()) {
562
+            throw new \Exception($this->l->t('Link sharing is not allowed'));
563
+        }
564
+
565
+        // Check if public upload is allowed
566
+        if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
567
+            && ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
568
+            throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
569
+        }
570
+    }
571
+
572
+    /**
573
+     * To make sure we don't get invisible link shares we set the parent
574
+     * of a link if it is a reshare. This is a quick word around
575
+     * until we can properly display multiple link shares in the UI
576
+     *
577
+     * See: https://github.com/owncloud/core/issues/22295
578
+     *
579
+     * FIXME: Remove once multiple link shares can be properly displayed
580
+     *
581
+     * @param IShare $share
582
+     */
583
+    protected function setLinkParent(IShare $share) {
584
+        $storage = $share->getNode()->getStorage();
585
+        if ($storage->instanceOfStorage(SharedStorage::class)) {
586
+            /** @var \OCA\Files_Sharing\SharedStorage $storage */
587
+            $share->setParent((int)$storage->getShareId());
588
+        }
589
+    }
590
+
591
+    /**
592
+     * @param File|Folder $path
593
+     */
594
+    protected function pathCreateChecks($path) {
595
+        // Make sure that we do not share a path that contains a shared mountpoint
596
+        if ($path instanceof \OCP\Files\Folder) {
597
+            $mounts = $this->mountManager->findIn($path->getPath());
598
+            foreach ($mounts as $mount) {
599
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
600
+                    // Using a flat sharing model ensures the file owner can always see who has access.
601
+                    // Allowing parent folder sharing would require tracking inherited access, which adds complexity
602
+                    // and hurts performance/scalability.
603
+                    // So we forbid sharing a parent folder of a share you received.
604
+                    throw new \InvalidArgumentException($this->l->t('You cannot share a folder that contains other shares'));
605
+                }
606
+            }
607
+        }
608
+    }
609
+
610
+    /**
611
+     * Check if the user that is sharing can actually share
612
+     *
613
+     * @param IShare $share
614
+     * @throws \Exception
615
+     */
616
+    protected function canShare(IShare $share) {
617
+        if (!$this->shareApiEnabled()) {
618
+            throw new \Exception($this->l->t('Sharing is disabled'));
619
+        }
620
+
621
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
622
+            throw new \Exception($this->l->t('Sharing is disabled for you'));
623
+        }
624
+    }
625
+
626
+    /**
627
+     * Share a path
628
+     *
629
+     * @param IShare $share
630
+     * @return IShare The share object
631
+     * @throws \Exception
632
+     *
633
+     * TODO: handle link share permissions or check them
634
+     */
635
+    public function createShare(IShare $share) {
636
+        $this->canShare($share);
637
+
638
+        $this->generalCreateChecks($share);
639
+
640
+        // Verify if there are any issues with the path
641
+        $this->pathCreateChecks($share->getNode());
642
+
643
+        /*
644 644
 		 * On creation of a share the owner is always the owner of the path
645 645
 		 * Except for mounted federated shares.
646 646
 		 */
647
-		$storage = $share->getNode()->getStorage();
648
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
649
-			$parent = $share->getNode()->getParent();
650
-			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
651
-				$parent = $parent->getParent();
652
-			}
653
-			$share->setShareOwner($parent->getOwner()->getUID());
654
-		} else {
655
-			if ($share->getNode()->getOwner()) {
656
-				$share->setShareOwner($share->getNode()->getOwner()->getUID());
657
-			} else {
658
-				$share->setShareOwner($share->getSharedBy());
659
-			}
660
-		}
661
-
662
-		try {
663
-			// Verify share type
664
-			if ($share->getShareType() === IShare::TYPE_USER) {
665
-				$this->userCreateChecks($share);
666
-
667
-				// Verify the expiration date
668
-				$share = $this->validateExpirationDateInternal($share);
669
-			} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
670
-				$this->groupCreateChecks($share);
671
-
672
-				// Verify the expiration date
673
-				$share = $this->validateExpirationDateInternal($share);
674
-			} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
675
-				// Verify the expiration date
676
-				$share = $this->validateExpirationDateInternal($share);
677
-			} elseif ($share->getShareType() === IShare::TYPE_LINK
678
-				|| $share->getShareType() === IShare::TYPE_EMAIL) {
679
-				$this->linkCreateChecks($share);
680
-				$this->setLinkParent($share);
681
-
682
-				$token = $this->generateToken();
683
-				// Set the unique token
684
-				$share->setToken($token);
685
-
686
-				// Verify the expiration date
687
-				$share = $this->validateExpirationDateLink($share);
688
-
689
-				// Verify the password
690
-				$this->verifyPassword($share->getPassword());
691
-
692
-				// If a password is set. Hash it!
693
-				if ($share->getShareType() === IShare::TYPE_LINK
694
-					&& $share->getPassword() !== null) {
695
-					$share->setPassword($this->hasher->hash($share->getPassword()));
696
-				}
697
-			}
698
-
699
-			// Cannot share with the owner
700
-			if ($share->getShareType() === IShare::TYPE_USER
701
-				&& $share->getSharedWith() === $share->getShareOwner()) {
702
-				throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
703
-			}
704
-
705
-			// Generate the target
706
-			$shareFolder = $this->config->getSystemValue('share_folder', '/');
707
-			if ($share->getShareType() === IShare::TYPE_USER) {
708
-				$allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
709
-				if ($allowCustomShareFolder) {
710
-					$shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $shareFolder);
711
-				}
712
-			}
713
-
714
-			$target = $shareFolder . '/' . $share->getNode()->getName();
715
-			$target = \OC\Files\Filesystem::normalizePath($target);
716
-			$share->setTarget($target);
717
-
718
-			// Pre share event
719
-			$event = new Share\Events\BeforeShareCreatedEvent($share);
720
-			$this->dispatcher->dispatchTyped($event);
721
-			if ($event->isPropagationStopped() && $event->getError()) {
722
-				throw new \Exception($event->getError());
723
-			}
724
-
725
-			$oldShare = $share;
726
-			$provider = $this->factory->getProviderForType($share->getShareType());
727
-			$share = $provider->create($share);
728
-
729
-			// Reuse the node we already have
730
-			$share->setNode($oldShare->getNode());
731
-
732
-			// Reset the target if it is null for the new share
733
-			if ($share->getTarget() === '') {
734
-				$share->setTarget($target);
735
-			}
736
-		} catch (AlreadySharedException $e) {
737
-			// If a share for the same target already exists, dont create a new one,
738
-			// but do trigger the hooks and notifications again
739
-			$oldShare = $share;
740
-
741
-			// Reuse the node we already have
742
-			$share = $e->getExistingShare();
743
-			$share->setNode($oldShare->getNode());
744
-		}
745
-
746
-		// Post share event
747
-		$this->dispatcher->dispatchTyped(new ShareCreatedEvent($share));
748
-
749
-		// Send email if needed
750
-		if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)) {
751
-			if ($share->getMailSend()) {
752
-				$provider = $this->factory->getProviderForType($share->getShareType());
753
-				if ($provider instanceof IShareProviderWithNotification) {
754
-					$provider->sendMailNotification($share);
755
-				} else {
756
-					$this->logger->debug('Share notification not sent because the provider does not support it.', ['app' => 'share']);
757
-				}
758
-			} else {
759
-				$this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
760
-			}
761
-		} else {
762
-			$this->logger->debug('Share notification not sent because sharing notification emails is disabled.', ['app' => 'share']);
763
-		}
764
-
765
-		return $share;
766
-	}
767
-
768
-	/**
769
-	 * Update a share
770
-	 *
771
-	 * @param IShare $share
772
-	 * @return IShare The share object
773
-	 * @throws \InvalidArgumentException
774
-	 * @throws HintException
775
-	 */
776
-	public function updateShare(IShare $share, bool $onlyValid = true) {
777
-		$expirationDateUpdated = false;
778
-
779
-		$this->canShare($share);
780
-
781
-		try {
782
-			$originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
783
-		} catch (\UnexpectedValueException $e) {
784
-			throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
785
-		}
786
-
787
-		// We cannot change the share type!
788
-		if ($share->getShareType() !== $originalShare->getShareType()) {
789
-			throw new \InvalidArgumentException($this->l->t('Cannot change share type'));
790
-		}
791
-
792
-		// We can only change the recipient on user shares
793
-		if ($share->getSharedWith() !== $originalShare->getSharedWith()
794
-			&& $share->getShareType() !== IShare::TYPE_USER) {
795
-			throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
796
-		}
797
-
798
-		// Cannot share with the owner
799
-		if ($share->getShareType() === IShare::TYPE_USER
800
-			&& $share->getSharedWith() === $share->getShareOwner()) {
801
-			throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
802
-		}
803
-
804
-		$this->generalCreateChecks($share, true);
805
-
806
-		if ($share->getShareType() === IShare::TYPE_USER) {
807
-			$this->userCreateChecks($share);
808
-
809
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
810
-				// Verify the expiration date
811
-				$this->validateExpirationDateInternal($share);
812
-				$expirationDateUpdated = true;
813
-			}
814
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
815
-			$this->groupCreateChecks($share);
816
-
817
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
818
-				// Verify the expiration date
819
-				$this->validateExpirationDateInternal($share);
820
-				$expirationDateUpdated = true;
821
-			}
822
-		} elseif ($share->getShareType() === IShare::TYPE_LINK
823
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
824
-			$this->linkCreateChecks($share);
825
-
826
-			// The new password is not set again if it is the same as the old
827
-			// one, unless when switching from sending by Talk to sending by
828
-			// mail.
829
-			$plainTextPassword = $share->getPassword();
830
-			$updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
831
-
832
-			/**
833
-			 * Cannot enable the getSendPasswordByTalk if there is no password set
834
-			 */
835
-			if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
836
-				throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk with an empty password'));
837
-			}
838
-
839
-			/**
840
-			 * If we're in a mail share, we need to force a password change
841
-			 * as either the user is not aware of the password or is already (received by mail)
842
-			 * Thus the SendPasswordByTalk feature would not make sense
843
-			 */
844
-			if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
845
-				if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
846
-					throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk without setting a new password'));
847
-				}
848
-				if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
849
-					throw new \InvalidArgumentException($this->l->t('Cannot disable sending the password by Talk without setting a new password'));
850
-				}
851
-			}
852
-
853
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
854
-				// Verify the expiration date
855
-				$this->validateExpirationDateLink($share);
856
-				$expirationDateUpdated = true;
857
-			}
858
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
859
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
860
-				// Verify the expiration date
861
-				$this->validateExpirationDateInternal($share);
862
-				$expirationDateUpdated = true;
863
-			}
864
-		}
865
-
866
-		$this->pathCreateChecks($share->getNode());
867
-
868
-		// Now update the share!
869
-		$provider = $this->factory->getProviderForType($share->getShareType());
870
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
871
-			/** @var ShareByMailProvider $provider */
872
-			$share = $provider->update($share, $plainTextPassword);
873
-		} else {
874
-			$share = $provider->update($share);
875
-		}
876
-
877
-		if ($expirationDateUpdated === true) {
878
-			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
879
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
880
-				'itemSource' => $share->getNode()->getId(),
881
-				'date' => $share->getExpirationDate(),
882
-				'uidOwner' => $share->getSharedBy(),
883
-			]);
884
-		}
885
-
886
-		if ($share->getPassword() !== $originalShare->getPassword()) {
887
-			\OC_Hook::emit(Share::class, 'post_update_password', [
888
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
889
-				'itemSource' => $share->getNode()->getId(),
890
-				'uidOwner' => $share->getSharedBy(),
891
-				'token' => $share->getToken(),
892
-				'disabled' => is_null($share->getPassword()),
893
-			]);
894
-		}
895
-
896
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
897
-			if ($this->userManager->userExists($share->getShareOwner())) {
898
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
899
-			} else {
900
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
901
-			}
902
-			\OC_Hook::emit(Share::class, 'post_update_permissions', [
903
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
904
-				'itemSource' => $share->getNode()->getId(),
905
-				'shareType' => $share->getShareType(),
906
-				'shareWith' => $share->getSharedWith(),
907
-				'uidOwner' => $share->getSharedBy(),
908
-				'permissions' => $share->getPermissions(),
909
-				'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
910
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
911
-			]);
912
-		}
913
-
914
-		return $share;
915
-	}
916
-
917
-	/**
918
-	 * Accept a share.
919
-	 *
920
-	 * @param IShare $share
921
-	 * @param string $recipientId
922
-	 * @return IShare The share object
923
-	 * @throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
924
-	 * @since 9.0.0
925
-	 */
926
-	public function acceptShare(IShare $share, string $recipientId): IShare {
927
-		[$providerId,] = $this->splitFullId($share->getFullId());
928
-		$provider = $this->factory->getProvider($providerId);
929
-
930
-		if (!($provider instanceof IShareProviderSupportsAccept)) {
931
-			throw new \InvalidArgumentException($this->l->t('Share provider does not support accepting'));
932
-		}
933
-		/** @var IShareProvider&IShareProviderSupportsAccept $provider */
934
-		$provider->acceptShare($share, $recipientId);
935
-
936
-		$event = new ShareAcceptedEvent($share);
937
-		$this->dispatcher->dispatchTyped($event);
938
-
939
-		return $share;
940
-	}
941
-
942
-	/**
943
-	 * Updates the password of the given share if it is not the same as the
944
-	 * password of the original share.
945
-	 *
946
-	 * @param IShare $share the share to update its password.
947
-	 * @param IShare $originalShare the original share to compare its
948
-	 *                              password with.
949
-	 * @return boolean whether the password was updated or not.
950
-	 */
951
-	private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
952
-		$passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword())
953
-			&& (($share->getPassword() !== null && $originalShare->getPassword() === null)
954
-				|| ($share->getPassword() === null && $originalShare->getPassword() !== null)
955
-				|| ($share->getPassword() !== null && $originalShare->getPassword() !== null
956
-					&& !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
957
-
958
-		// Password updated.
959
-		if ($passwordsAreDifferent) {
960
-			// Verify the password
961
-			$this->verifyPassword($share->getPassword());
962
-
963
-			// If a password is set. Hash it!
964
-			if (!empty($share->getPassword())) {
965
-				$share->setPassword($this->hasher->hash($share->getPassword()));
966
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
967
-					// Shares shared by email have temporary passwords
968
-					$this->setSharePasswordExpirationTime($share);
969
-				}
970
-
971
-				return true;
972
-			} else {
973
-				// Empty string and null are seen as NOT password protected
974
-				$share->setPassword(null);
975
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
976
-					$share->setPasswordExpirationTime(null);
977
-				}
978
-				return true;
979
-			}
980
-		} else {
981
-			// Reset the password to the original one, as it is either the same
982
-			// as the "new" password or a hashed version of it.
983
-			$share->setPassword($originalShare->getPassword());
984
-		}
985
-
986
-		return false;
987
-	}
988
-
989
-	/**
990
-	 * Set the share's password expiration time
991
-	 */
992
-	private function setSharePasswordExpirationTime(IShare $share): void {
993
-		if (!$this->config->getSystemValueBool('sharing.enable_mail_link_password_expiration', false)) {
994
-			// Sets password expiration date to NULL
995
-			$share->setPasswordExpirationTime();
996
-			return;
997
-		}
998
-		// Sets password expiration date
999
-		$expirationTime = null;
1000
-		$now = new \DateTime();
1001
-		$expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1002
-		$expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1003
-		$share->setPasswordExpirationTime($expirationTime);
1004
-	}
1005
-
1006
-
1007
-	/**
1008
-	 * Delete all the children of this share
1009
-	 *
1010
-	 * @param IShare $share
1011
-	 * @return IShare[] List of deleted shares
1012
-	 */
1013
-	protected function deleteChildren(IShare $share) {
1014
-		$deletedShares = [];
1015
-
1016
-		$provider = $this->factory->getProviderForType($share->getShareType());
1017
-
1018
-		foreach ($provider->getChildren($share) as $child) {
1019
-			$this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($child));
1020
-
1021
-			$deletedChildren = $this->deleteChildren($child);
1022
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
1023
-
1024
-			$provider->delete($child);
1025
-			$this->dispatcher->dispatchTyped(new ShareDeletedEvent($child));
1026
-			$deletedShares[] = $child;
1027
-		}
1028
-
1029
-		return $deletedShares;
1030
-	}
1031
-
1032
-	/** Promote re-shares into direct shares so that target user keeps access */
1033
-	protected function promoteReshares(IShare $share): void {
1034
-		try {
1035
-			$node = $share->getNode();
1036
-		} catch (NotFoundException) {
1037
-			/* Skip if node not found */
1038
-			return;
1039
-		}
1040
-
1041
-		$userIds = [];
1042
-
1043
-		if ($share->getShareType() === IShare::TYPE_USER) {
1044
-			$userIds[] = $share->getSharedWith();
1045
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1046
-			$group = $this->groupManager->get($share->getSharedWith());
1047
-			$users = $group?->getUsers() ?? [];
1048
-
1049
-			foreach ($users as $user) {
1050
-				/* Skip share owner */
1051
-				if ($user->getUID() === $share->getShareOwner() || $user->getUID() === $share->getSharedBy()) {
1052
-					continue;
1053
-				}
1054
-				$userIds[] = $user->getUID();
1055
-			}
1056
-		} else {
1057
-			/* We only support user and group shares */
1058
-			return;
1059
-		}
1060
-
1061
-		$reshareRecords = [];
1062
-		$shareTypes = [
1063
-			IShare::TYPE_GROUP,
1064
-			IShare::TYPE_USER,
1065
-			IShare::TYPE_LINK,
1066
-			IShare::TYPE_REMOTE,
1067
-			IShare::TYPE_EMAIL,
1068
-		];
1069
-
1070
-		foreach ($userIds as $userId) {
1071
-			foreach ($shareTypes as $shareType) {
1072
-				try {
1073
-					$provider = $this->factory->getProviderForType($shareType);
1074
-				} catch (ProviderException $e) {
1075
-					continue;
1076
-				}
1077
-
1078
-				if ($node instanceof Folder) {
1079
-					/* We need to get all shares by this user to get subshares */
1080
-					$shares = $provider->getSharesBy($userId, $shareType, null, false, -1, 0);
1081
-
1082
-					foreach ($shares as $share) {
1083
-						try {
1084
-							$path = $share->getNode()->getPath();
1085
-						} catch (NotFoundException) {
1086
-							/* Ignore share of non-existing node */
1087
-							continue;
1088
-						}
1089
-						if ($node->getRelativePath($path) !== null) {
1090
-							/* If relative path is not null it means the shared node is the same or in a subfolder */
1091
-							$reshareRecords[] = $share;
1092
-						}
1093
-					}
1094
-				} else {
1095
-					$shares = $provider->getSharesBy($userId, $shareType, $node, false, -1, 0);
1096
-					foreach ($shares as $child) {
1097
-						$reshareRecords[] = $child;
1098
-					}
1099
-				}
1100
-			}
1101
-		}
1102
-
1103
-		foreach ($reshareRecords as $child) {
1104
-			try {
1105
-				/* Check if the share is still valid (means the resharer still has access to the file through another mean) */
1106
-				$this->generalCreateChecks($child);
1107
-			} catch (GenericShareException $e) {
1108
-				/* The check is invalid, promote it to a direct share from the sharer of parent share */
1109
-				$this->logger->debug('Promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1110
-				try {
1111
-					$child->setSharedBy($share->getSharedBy());
1112
-					$this->updateShare($child);
1113
-				} catch (GenericShareException|\InvalidArgumentException $e) {
1114
-					$this->logger->warning('Failed to promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1115
-				}
1116
-			}
1117
-		}
1118
-	}
1119
-
1120
-	/**
1121
-	 * Delete a share
1122
-	 *
1123
-	 * @param IShare $share
1124
-	 * @throws ShareNotFound
1125
-	 * @throws \InvalidArgumentException
1126
-	 */
1127
-	public function deleteShare(IShare $share) {
1128
-		try {
1129
-			$share->getFullId();
1130
-		} catch (\UnexpectedValueException $e) {
1131
-			throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
1132
-		}
1133
-
1134
-		$this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($share));
1135
-
1136
-		// Get all children and delete them as well
1137
-		$this->deleteChildren($share);
1138
-
1139
-		// Do the actual delete
1140
-		$provider = $this->factory->getProviderForType($share->getShareType());
1141
-		$provider->delete($share);
1142
-
1143
-		$this->dispatcher->dispatchTyped(new ShareDeletedEvent($share));
1144
-
1145
-		// Promote reshares of the deleted share
1146
-		$this->promoteReshares($share);
1147
-	}
1148
-
1149
-
1150
-	/**
1151
-	 * Unshare a file as the recipient.
1152
-	 * This can be different from a regular delete for example when one of
1153
-	 * the users in a groups deletes that share. But the provider should
1154
-	 * handle this.
1155
-	 *
1156
-	 * @param IShare $share
1157
-	 * @param string $recipientId
1158
-	 */
1159
-	public function deleteFromSelf(IShare $share, $recipientId) {
1160
-		[$providerId,] = $this->splitFullId($share->getFullId());
1161
-		$provider = $this->factory->getProvider($providerId);
1162
-
1163
-		$provider->deleteFromSelf($share, $recipientId);
1164
-		$event = new ShareDeletedFromSelfEvent($share);
1165
-		$this->dispatcher->dispatchTyped($event);
1166
-	}
1167
-
1168
-	public function restoreShare(IShare $share, string $recipientId): IShare {
1169
-		[$providerId,] = $this->splitFullId($share->getFullId());
1170
-		$provider = $this->factory->getProvider($providerId);
1171
-
1172
-		return $provider->restore($share, $recipientId);
1173
-	}
1174
-
1175
-	/**
1176
-	 * @inheritdoc
1177
-	 */
1178
-	public function moveShare(IShare $share, $recipientId) {
1179
-		if ($share->getShareType() === IShare::TYPE_LINK
1180
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1181
-			throw new \InvalidArgumentException($this->l->t('Cannot change target of link share'));
1182
-		}
1183
-
1184
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1185
-			throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1186
-		}
1187
-
1188
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1189
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1190
-			if (is_null($sharedWith)) {
1191
-				throw new \InvalidArgumentException($this->l->t('Group "%s" does not exist', [$share->getSharedWith()]));
1192
-			}
1193
-			$recipient = $this->userManager->get($recipientId);
1194
-			if (!$sharedWith->inGroup($recipient)) {
1195
-				throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1196
-			}
1197
-		}
1198
-
1199
-		[$providerId,] = $this->splitFullId($share->getFullId());
1200
-		$provider = $this->factory->getProvider($providerId);
1201
-
1202
-		return $provider->move($share, $recipientId);
1203
-	}
1204
-
1205
-	public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1206
-		$providers = $this->factory->getAllProviders();
1207
-		if (!$shallow) {
1208
-			throw new \Exception('non-shallow getSharesInFolder is no longer supported');
1209
-		}
1210
-
1211
-		$isOwnerless = $node->getMountPoint() instanceof IShareOwnerlessMount;
1212
-
1213
-		$shares = [];
1214
-		foreach ($providers as $provider) {
1215
-			if ($isOwnerless) {
1216
-				// If the provider does not implement the additional interface,
1217
-				// we lack a performant way of querying all shares and therefore ignore the provider.
1218
-				if ($provider instanceof IShareProviderSupportsAllSharesInFolder) {
1219
-					foreach ($provider->getAllSharesInFolder($node) as $fid => $data) {
1220
-						$shares[$fid] ??= [];
1221
-						$shares[$fid] = array_merge($shares[$fid], $data);
1222
-					}
1223
-				}
1224
-			} else {
1225
-				foreach ($provider->getSharesInFolder($userId, $node, $reshares) as $fid => $data) {
1226
-					$shares[$fid] ??= [];
1227
-					$shares[$fid] = array_merge($shares[$fid], $data);
1228
-				}
1229
-			}
1230
-		}
1231
-
1232
-		return $shares;
1233
-	}
1234
-
1235
-	/**
1236
-	 * @inheritdoc
1237
-	 */
1238
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
1239
-		if ($path !== null
1240
-			&& !($path instanceof \OCP\Files\File)
1241
-			&& !($path instanceof \OCP\Files\Folder)) {
1242
-			throw new \InvalidArgumentException($this->l->t('Invalid path'));
1243
-		}
1244
-
1245
-		try {
1246
-			$provider = $this->factory->getProviderForType($shareType);
1247
-		} catch (ProviderException $e) {
1248
-			return [];
1249
-		}
1250
-
1251
-		if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1252
-			$shares = array_filter($provider->getSharesByPath($path), static fn (IShare $share) => $share->getShareType() === $shareType);
1253
-		} else {
1254
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1255
-		}
1256
-
1257
-		/*
647
+        $storage = $share->getNode()->getStorage();
648
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
649
+            $parent = $share->getNode()->getParent();
650
+            while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
651
+                $parent = $parent->getParent();
652
+            }
653
+            $share->setShareOwner($parent->getOwner()->getUID());
654
+        } else {
655
+            if ($share->getNode()->getOwner()) {
656
+                $share->setShareOwner($share->getNode()->getOwner()->getUID());
657
+            } else {
658
+                $share->setShareOwner($share->getSharedBy());
659
+            }
660
+        }
661
+
662
+        try {
663
+            // Verify share type
664
+            if ($share->getShareType() === IShare::TYPE_USER) {
665
+                $this->userCreateChecks($share);
666
+
667
+                // Verify the expiration date
668
+                $share = $this->validateExpirationDateInternal($share);
669
+            } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
670
+                $this->groupCreateChecks($share);
671
+
672
+                // Verify the expiration date
673
+                $share = $this->validateExpirationDateInternal($share);
674
+            } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
675
+                // Verify the expiration date
676
+                $share = $this->validateExpirationDateInternal($share);
677
+            } elseif ($share->getShareType() === IShare::TYPE_LINK
678
+                || $share->getShareType() === IShare::TYPE_EMAIL) {
679
+                $this->linkCreateChecks($share);
680
+                $this->setLinkParent($share);
681
+
682
+                $token = $this->generateToken();
683
+                // Set the unique token
684
+                $share->setToken($token);
685
+
686
+                // Verify the expiration date
687
+                $share = $this->validateExpirationDateLink($share);
688
+
689
+                // Verify the password
690
+                $this->verifyPassword($share->getPassword());
691
+
692
+                // If a password is set. Hash it!
693
+                if ($share->getShareType() === IShare::TYPE_LINK
694
+                    && $share->getPassword() !== null) {
695
+                    $share->setPassword($this->hasher->hash($share->getPassword()));
696
+                }
697
+            }
698
+
699
+            // Cannot share with the owner
700
+            if ($share->getShareType() === IShare::TYPE_USER
701
+                && $share->getSharedWith() === $share->getShareOwner()) {
702
+                throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
703
+            }
704
+
705
+            // Generate the target
706
+            $shareFolder = $this->config->getSystemValue('share_folder', '/');
707
+            if ($share->getShareType() === IShare::TYPE_USER) {
708
+                $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
709
+                if ($allowCustomShareFolder) {
710
+                    $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $shareFolder);
711
+                }
712
+            }
713
+
714
+            $target = $shareFolder . '/' . $share->getNode()->getName();
715
+            $target = \OC\Files\Filesystem::normalizePath($target);
716
+            $share->setTarget($target);
717
+
718
+            // Pre share event
719
+            $event = new Share\Events\BeforeShareCreatedEvent($share);
720
+            $this->dispatcher->dispatchTyped($event);
721
+            if ($event->isPropagationStopped() && $event->getError()) {
722
+                throw new \Exception($event->getError());
723
+            }
724
+
725
+            $oldShare = $share;
726
+            $provider = $this->factory->getProviderForType($share->getShareType());
727
+            $share = $provider->create($share);
728
+
729
+            // Reuse the node we already have
730
+            $share->setNode($oldShare->getNode());
731
+
732
+            // Reset the target if it is null for the new share
733
+            if ($share->getTarget() === '') {
734
+                $share->setTarget($target);
735
+            }
736
+        } catch (AlreadySharedException $e) {
737
+            // If a share for the same target already exists, dont create a new one,
738
+            // but do trigger the hooks and notifications again
739
+            $oldShare = $share;
740
+
741
+            // Reuse the node we already have
742
+            $share = $e->getExistingShare();
743
+            $share->setNode($oldShare->getNode());
744
+        }
745
+
746
+        // Post share event
747
+        $this->dispatcher->dispatchTyped(new ShareCreatedEvent($share));
748
+
749
+        // Send email if needed
750
+        if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)) {
751
+            if ($share->getMailSend()) {
752
+                $provider = $this->factory->getProviderForType($share->getShareType());
753
+                if ($provider instanceof IShareProviderWithNotification) {
754
+                    $provider->sendMailNotification($share);
755
+                } else {
756
+                    $this->logger->debug('Share notification not sent because the provider does not support it.', ['app' => 'share']);
757
+                }
758
+            } else {
759
+                $this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
760
+            }
761
+        } else {
762
+            $this->logger->debug('Share notification not sent because sharing notification emails is disabled.', ['app' => 'share']);
763
+        }
764
+
765
+        return $share;
766
+    }
767
+
768
+    /**
769
+     * Update a share
770
+     *
771
+     * @param IShare $share
772
+     * @return IShare The share object
773
+     * @throws \InvalidArgumentException
774
+     * @throws HintException
775
+     */
776
+    public function updateShare(IShare $share, bool $onlyValid = true) {
777
+        $expirationDateUpdated = false;
778
+
779
+        $this->canShare($share);
780
+
781
+        try {
782
+            $originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
783
+        } catch (\UnexpectedValueException $e) {
784
+            throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
785
+        }
786
+
787
+        // We cannot change the share type!
788
+        if ($share->getShareType() !== $originalShare->getShareType()) {
789
+            throw new \InvalidArgumentException($this->l->t('Cannot change share type'));
790
+        }
791
+
792
+        // We can only change the recipient on user shares
793
+        if ($share->getSharedWith() !== $originalShare->getSharedWith()
794
+            && $share->getShareType() !== IShare::TYPE_USER) {
795
+            throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
796
+        }
797
+
798
+        // Cannot share with the owner
799
+        if ($share->getShareType() === IShare::TYPE_USER
800
+            && $share->getSharedWith() === $share->getShareOwner()) {
801
+            throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
802
+        }
803
+
804
+        $this->generalCreateChecks($share, true);
805
+
806
+        if ($share->getShareType() === IShare::TYPE_USER) {
807
+            $this->userCreateChecks($share);
808
+
809
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
810
+                // Verify the expiration date
811
+                $this->validateExpirationDateInternal($share);
812
+                $expirationDateUpdated = true;
813
+            }
814
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
815
+            $this->groupCreateChecks($share);
816
+
817
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
818
+                // Verify the expiration date
819
+                $this->validateExpirationDateInternal($share);
820
+                $expirationDateUpdated = true;
821
+            }
822
+        } elseif ($share->getShareType() === IShare::TYPE_LINK
823
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
824
+            $this->linkCreateChecks($share);
825
+
826
+            // The new password is not set again if it is the same as the old
827
+            // one, unless when switching from sending by Talk to sending by
828
+            // mail.
829
+            $plainTextPassword = $share->getPassword();
830
+            $updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
831
+
832
+            /**
833
+             * Cannot enable the getSendPasswordByTalk if there is no password set
834
+             */
835
+            if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
836
+                throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk with an empty password'));
837
+            }
838
+
839
+            /**
840
+             * If we're in a mail share, we need to force a password change
841
+             * as either the user is not aware of the password or is already (received by mail)
842
+             * Thus the SendPasswordByTalk feature would not make sense
843
+             */
844
+            if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
845
+                if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
846
+                    throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk without setting a new password'));
847
+                }
848
+                if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
849
+                    throw new \InvalidArgumentException($this->l->t('Cannot disable sending the password by Talk without setting a new password'));
850
+                }
851
+            }
852
+
853
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
854
+                // Verify the expiration date
855
+                $this->validateExpirationDateLink($share);
856
+                $expirationDateUpdated = true;
857
+            }
858
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
859
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
860
+                // Verify the expiration date
861
+                $this->validateExpirationDateInternal($share);
862
+                $expirationDateUpdated = true;
863
+            }
864
+        }
865
+
866
+        $this->pathCreateChecks($share->getNode());
867
+
868
+        // Now update the share!
869
+        $provider = $this->factory->getProviderForType($share->getShareType());
870
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
871
+            /** @var ShareByMailProvider $provider */
872
+            $share = $provider->update($share, $plainTextPassword);
873
+        } else {
874
+            $share = $provider->update($share);
875
+        }
876
+
877
+        if ($expirationDateUpdated === true) {
878
+            \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
879
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
880
+                'itemSource' => $share->getNode()->getId(),
881
+                'date' => $share->getExpirationDate(),
882
+                'uidOwner' => $share->getSharedBy(),
883
+            ]);
884
+        }
885
+
886
+        if ($share->getPassword() !== $originalShare->getPassword()) {
887
+            \OC_Hook::emit(Share::class, 'post_update_password', [
888
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
889
+                'itemSource' => $share->getNode()->getId(),
890
+                'uidOwner' => $share->getSharedBy(),
891
+                'token' => $share->getToken(),
892
+                'disabled' => is_null($share->getPassword()),
893
+            ]);
894
+        }
895
+
896
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
897
+            if ($this->userManager->userExists($share->getShareOwner())) {
898
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
899
+            } else {
900
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
901
+            }
902
+            \OC_Hook::emit(Share::class, 'post_update_permissions', [
903
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
904
+                'itemSource' => $share->getNode()->getId(),
905
+                'shareType' => $share->getShareType(),
906
+                'shareWith' => $share->getSharedWith(),
907
+                'uidOwner' => $share->getSharedBy(),
908
+                'permissions' => $share->getPermissions(),
909
+                'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
910
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
911
+            ]);
912
+        }
913
+
914
+        return $share;
915
+    }
916
+
917
+    /**
918
+     * Accept a share.
919
+     *
920
+     * @param IShare $share
921
+     * @param string $recipientId
922
+     * @return IShare The share object
923
+     * @throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
924
+     * @since 9.0.0
925
+     */
926
+    public function acceptShare(IShare $share, string $recipientId): IShare {
927
+        [$providerId,] = $this->splitFullId($share->getFullId());
928
+        $provider = $this->factory->getProvider($providerId);
929
+
930
+        if (!($provider instanceof IShareProviderSupportsAccept)) {
931
+            throw new \InvalidArgumentException($this->l->t('Share provider does not support accepting'));
932
+        }
933
+        /** @var IShareProvider&IShareProviderSupportsAccept $provider */
934
+        $provider->acceptShare($share, $recipientId);
935
+
936
+        $event = new ShareAcceptedEvent($share);
937
+        $this->dispatcher->dispatchTyped($event);
938
+
939
+        return $share;
940
+    }
941
+
942
+    /**
943
+     * Updates the password of the given share if it is not the same as the
944
+     * password of the original share.
945
+     *
946
+     * @param IShare $share the share to update its password.
947
+     * @param IShare $originalShare the original share to compare its
948
+     *                              password with.
949
+     * @return boolean whether the password was updated or not.
950
+     */
951
+    private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
952
+        $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword())
953
+            && (($share->getPassword() !== null && $originalShare->getPassword() === null)
954
+                || ($share->getPassword() === null && $originalShare->getPassword() !== null)
955
+                || ($share->getPassword() !== null && $originalShare->getPassword() !== null
956
+                    && !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
957
+
958
+        // Password updated.
959
+        if ($passwordsAreDifferent) {
960
+            // Verify the password
961
+            $this->verifyPassword($share->getPassword());
962
+
963
+            // If a password is set. Hash it!
964
+            if (!empty($share->getPassword())) {
965
+                $share->setPassword($this->hasher->hash($share->getPassword()));
966
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
967
+                    // Shares shared by email have temporary passwords
968
+                    $this->setSharePasswordExpirationTime($share);
969
+                }
970
+
971
+                return true;
972
+            } else {
973
+                // Empty string and null are seen as NOT password protected
974
+                $share->setPassword(null);
975
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
976
+                    $share->setPasswordExpirationTime(null);
977
+                }
978
+                return true;
979
+            }
980
+        } else {
981
+            // Reset the password to the original one, as it is either the same
982
+            // as the "new" password or a hashed version of it.
983
+            $share->setPassword($originalShare->getPassword());
984
+        }
985
+
986
+        return false;
987
+    }
988
+
989
+    /**
990
+     * Set the share's password expiration time
991
+     */
992
+    private function setSharePasswordExpirationTime(IShare $share): void {
993
+        if (!$this->config->getSystemValueBool('sharing.enable_mail_link_password_expiration', false)) {
994
+            // Sets password expiration date to NULL
995
+            $share->setPasswordExpirationTime();
996
+            return;
997
+        }
998
+        // Sets password expiration date
999
+        $expirationTime = null;
1000
+        $now = new \DateTime();
1001
+        $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1002
+        $expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1003
+        $share->setPasswordExpirationTime($expirationTime);
1004
+    }
1005
+
1006
+
1007
+    /**
1008
+     * Delete all the children of this share
1009
+     *
1010
+     * @param IShare $share
1011
+     * @return IShare[] List of deleted shares
1012
+     */
1013
+    protected function deleteChildren(IShare $share) {
1014
+        $deletedShares = [];
1015
+
1016
+        $provider = $this->factory->getProviderForType($share->getShareType());
1017
+
1018
+        foreach ($provider->getChildren($share) as $child) {
1019
+            $this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($child));
1020
+
1021
+            $deletedChildren = $this->deleteChildren($child);
1022
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
1023
+
1024
+            $provider->delete($child);
1025
+            $this->dispatcher->dispatchTyped(new ShareDeletedEvent($child));
1026
+            $deletedShares[] = $child;
1027
+        }
1028
+
1029
+        return $deletedShares;
1030
+    }
1031
+
1032
+    /** Promote re-shares into direct shares so that target user keeps access */
1033
+    protected function promoteReshares(IShare $share): void {
1034
+        try {
1035
+            $node = $share->getNode();
1036
+        } catch (NotFoundException) {
1037
+            /* Skip if node not found */
1038
+            return;
1039
+        }
1040
+
1041
+        $userIds = [];
1042
+
1043
+        if ($share->getShareType() === IShare::TYPE_USER) {
1044
+            $userIds[] = $share->getSharedWith();
1045
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1046
+            $group = $this->groupManager->get($share->getSharedWith());
1047
+            $users = $group?->getUsers() ?? [];
1048
+
1049
+            foreach ($users as $user) {
1050
+                /* Skip share owner */
1051
+                if ($user->getUID() === $share->getShareOwner() || $user->getUID() === $share->getSharedBy()) {
1052
+                    continue;
1053
+                }
1054
+                $userIds[] = $user->getUID();
1055
+            }
1056
+        } else {
1057
+            /* We only support user and group shares */
1058
+            return;
1059
+        }
1060
+
1061
+        $reshareRecords = [];
1062
+        $shareTypes = [
1063
+            IShare::TYPE_GROUP,
1064
+            IShare::TYPE_USER,
1065
+            IShare::TYPE_LINK,
1066
+            IShare::TYPE_REMOTE,
1067
+            IShare::TYPE_EMAIL,
1068
+        ];
1069
+
1070
+        foreach ($userIds as $userId) {
1071
+            foreach ($shareTypes as $shareType) {
1072
+                try {
1073
+                    $provider = $this->factory->getProviderForType($shareType);
1074
+                } catch (ProviderException $e) {
1075
+                    continue;
1076
+                }
1077
+
1078
+                if ($node instanceof Folder) {
1079
+                    /* We need to get all shares by this user to get subshares */
1080
+                    $shares = $provider->getSharesBy($userId, $shareType, null, false, -1, 0);
1081
+
1082
+                    foreach ($shares as $share) {
1083
+                        try {
1084
+                            $path = $share->getNode()->getPath();
1085
+                        } catch (NotFoundException) {
1086
+                            /* Ignore share of non-existing node */
1087
+                            continue;
1088
+                        }
1089
+                        if ($node->getRelativePath($path) !== null) {
1090
+                            /* If relative path is not null it means the shared node is the same or in a subfolder */
1091
+                            $reshareRecords[] = $share;
1092
+                        }
1093
+                    }
1094
+                } else {
1095
+                    $shares = $provider->getSharesBy($userId, $shareType, $node, false, -1, 0);
1096
+                    foreach ($shares as $child) {
1097
+                        $reshareRecords[] = $child;
1098
+                    }
1099
+                }
1100
+            }
1101
+        }
1102
+
1103
+        foreach ($reshareRecords as $child) {
1104
+            try {
1105
+                /* Check if the share is still valid (means the resharer still has access to the file through another mean) */
1106
+                $this->generalCreateChecks($child);
1107
+            } catch (GenericShareException $e) {
1108
+                /* The check is invalid, promote it to a direct share from the sharer of parent share */
1109
+                $this->logger->debug('Promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1110
+                try {
1111
+                    $child->setSharedBy($share->getSharedBy());
1112
+                    $this->updateShare($child);
1113
+                } catch (GenericShareException|\InvalidArgumentException $e) {
1114
+                    $this->logger->warning('Failed to promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
1115
+                }
1116
+            }
1117
+        }
1118
+    }
1119
+
1120
+    /**
1121
+     * Delete a share
1122
+     *
1123
+     * @param IShare $share
1124
+     * @throws ShareNotFound
1125
+     * @throws \InvalidArgumentException
1126
+     */
1127
+    public function deleteShare(IShare $share) {
1128
+        try {
1129
+            $share->getFullId();
1130
+        } catch (\UnexpectedValueException $e) {
1131
+            throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
1132
+        }
1133
+
1134
+        $this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($share));
1135
+
1136
+        // Get all children and delete them as well
1137
+        $this->deleteChildren($share);
1138
+
1139
+        // Do the actual delete
1140
+        $provider = $this->factory->getProviderForType($share->getShareType());
1141
+        $provider->delete($share);
1142
+
1143
+        $this->dispatcher->dispatchTyped(new ShareDeletedEvent($share));
1144
+
1145
+        // Promote reshares of the deleted share
1146
+        $this->promoteReshares($share);
1147
+    }
1148
+
1149
+
1150
+    /**
1151
+     * Unshare a file as the recipient.
1152
+     * This can be different from a regular delete for example when one of
1153
+     * the users in a groups deletes that share. But the provider should
1154
+     * handle this.
1155
+     *
1156
+     * @param IShare $share
1157
+     * @param string $recipientId
1158
+     */
1159
+    public function deleteFromSelf(IShare $share, $recipientId) {
1160
+        [$providerId,] = $this->splitFullId($share->getFullId());
1161
+        $provider = $this->factory->getProvider($providerId);
1162
+
1163
+        $provider->deleteFromSelf($share, $recipientId);
1164
+        $event = new ShareDeletedFromSelfEvent($share);
1165
+        $this->dispatcher->dispatchTyped($event);
1166
+    }
1167
+
1168
+    public function restoreShare(IShare $share, string $recipientId): IShare {
1169
+        [$providerId,] = $this->splitFullId($share->getFullId());
1170
+        $provider = $this->factory->getProvider($providerId);
1171
+
1172
+        return $provider->restore($share, $recipientId);
1173
+    }
1174
+
1175
+    /**
1176
+     * @inheritdoc
1177
+     */
1178
+    public function moveShare(IShare $share, $recipientId) {
1179
+        if ($share->getShareType() === IShare::TYPE_LINK
1180
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1181
+            throw new \InvalidArgumentException($this->l->t('Cannot change target of link share'));
1182
+        }
1183
+
1184
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1185
+            throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1186
+        }
1187
+
1188
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1189
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1190
+            if (is_null($sharedWith)) {
1191
+                throw new \InvalidArgumentException($this->l->t('Group "%s" does not exist', [$share->getSharedWith()]));
1192
+            }
1193
+            $recipient = $this->userManager->get($recipientId);
1194
+            if (!$sharedWith->inGroup($recipient)) {
1195
+                throw new \InvalidArgumentException($this->l->t('Invalid share recipient'));
1196
+            }
1197
+        }
1198
+
1199
+        [$providerId,] = $this->splitFullId($share->getFullId());
1200
+        $provider = $this->factory->getProvider($providerId);
1201
+
1202
+        return $provider->move($share, $recipientId);
1203
+    }
1204
+
1205
+    public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1206
+        $providers = $this->factory->getAllProviders();
1207
+        if (!$shallow) {
1208
+            throw new \Exception('non-shallow getSharesInFolder is no longer supported');
1209
+        }
1210
+
1211
+        $isOwnerless = $node->getMountPoint() instanceof IShareOwnerlessMount;
1212
+
1213
+        $shares = [];
1214
+        foreach ($providers as $provider) {
1215
+            if ($isOwnerless) {
1216
+                // If the provider does not implement the additional interface,
1217
+                // we lack a performant way of querying all shares and therefore ignore the provider.
1218
+                if ($provider instanceof IShareProviderSupportsAllSharesInFolder) {
1219
+                    foreach ($provider->getAllSharesInFolder($node) as $fid => $data) {
1220
+                        $shares[$fid] ??= [];
1221
+                        $shares[$fid] = array_merge($shares[$fid], $data);
1222
+                    }
1223
+                }
1224
+            } else {
1225
+                foreach ($provider->getSharesInFolder($userId, $node, $reshares) as $fid => $data) {
1226
+                    $shares[$fid] ??= [];
1227
+                    $shares[$fid] = array_merge($shares[$fid], $data);
1228
+                }
1229
+            }
1230
+        }
1231
+
1232
+        return $shares;
1233
+    }
1234
+
1235
+    /**
1236
+     * @inheritdoc
1237
+     */
1238
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
1239
+        if ($path !== null
1240
+            && !($path instanceof \OCP\Files\File)
1241
+            && !($path instanceof \OCP\Files\Folder)) {
1242
+            throw new \InvalidArgumentException($this->l->t('Invalid path'));
1243
+        }
1244
+
1245
+        try {
1246
+            $provider = $this->factory->getProviderForType($shareType);
1247
+        } catch (ProviderException $e) {
1248
+            return [];
1249
+        }
1250
+
1251
+        if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1252
+            $shares = array_filter($provider->getSharesByPath($path), static fn (IShare $share) => $share->getShareType() === $shareType);
1253
+        } else {
1254
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1255
+        }
1256
+
1257
+        /*
1258 1258
 		 * Work around so we don't return expired shares but still follow
1259 1259
 		 * proper pagination.
1260 1260
 		 */
1261 1261
 
1262
-		$shares2 = [];
1263
-
1264
-		while (true) {
1265
-			$added = 0;
1266
-			foreach ($shares as $share) {
1267
-				if ($onlyValid) {
1268
-					try {
1269
-						$this->checkShare($share);
1270
-					} catch (ShareNotFound $e) {
1271
-						// Ignore since this basically means the share is deleted
1272
-						continue;
1273
-					}
1274
-				}
1275
-
1276
-				$added++;
1277
-				$shares2[] = $share;
1278
-
1279
-				if (count($shares2) === $limit) {
1280
-					break;
1281
-				}
1282
-			}
1283
-
1284
-			// If we did not fetch more shares than the limit then there are no more shares
1285
-			if (count($shares) < $limit) {
1286
-				break;
1287
-			}
1288
-
1289
-			if (count($shares2) === $limit) {
1290
-				break;
1291
-			}
1292
-
1293
-			// If there was no limit on the select we are done
1294
-			if ($limit === -1) {
1295
-				break;
1296
-			}
1297
-
1298
-			$offset += $added;
1299
-
1300
-			// Fetch again $limit shares
1301
-			if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1302
-				// We already fetched all shares, so end here
1303
-				$shares = [];
1304
-			} else {
1305
-				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1306
-			}
1307
-
1308
-			// No more shares means we are done
1309
-			if (empty($shares)) {
1310
-				break;
1311
-			}
1312
-		}
1313
-
1314
-		$shares = $shares2;
1315
-
1316
-		return $shares;
1317
-	}
1318
-
1319
-	/**
1320
-	 * @inheritdoc
1321
-	 */
1322
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1323
-		try {
1324
-			$provider = $this->factory->getProviderForType($shareType);
1325
-		} catch (ProviderException $e) {
1326
-			return [];
1327
-		}
1328
-
1329
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1330
-
1331
-		// remove all shares which are already expired
1332
-		foreach ($shares as $key => $share) {
1333
-			try {
1334
-				$this->checkShare($share);
1335
-			} catch (ShareNotFound $e) {
1336
-				unset($shares[$key]);
1337
-			}
1338
-		}
1339
-
1340
-		return $shares;
1341
-	}
1342
-
1343
-	/**
1344
-	 * @inheritdoc
1345
-	 */
1346
-	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1347
-		$shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1348
-
1349
-		// Only get deleted shares
1350
-		$shares = array_filter($shares, function (IShare $share) {
1351
-			return $share->getPermissions() === 0;
1352
-		});
1353
-
1354
-		// Only get shares where the owner still exists
1355
-		$shares = array_filter($shares, function (IShare $share) {
1356
-			return $this->userManager->userExists($share->getShareOwner());
1357
-		});
1358
-
1359
-		return $shares;
1360
-	}
1361
-
1362
-	/**
1363
-	 * @inheritdoc
1364
-	 */
1365
-	public function getShareById($id, $recipient = null, bool $onlyValid = true) {
1366
-		if ($id === null) {
1367
-			throw new ShareNotFound();
1368
-		}
1369
-
1370
-		[$providerId, $id] = $this->splitFullId($id);
1371
-
1372
-		try {
1373
-			$provider = $this->factory->getProvider($providerId);
1374
-		} catch (ProviderException $e) {
1375
-			throw new ShareNotFound();
1376
-		}
1377
-
1378
-		$share = $provider->getShareById($id, $recipient);
1379
-
1380
-		if ($onlyValid) {
1381
-			$this->checkShare($share);
1382
-		}
1383
-
1384
-		return $share;
1385
-	}
1386
-
1387
-	/**
1388
-	 * Get all the shares for a given path
1389
-	 *
1390
-	 * @param \OCP\Files\Node $path
1391
-	 * @param int $page
1392
-	 * @param int $perPage
1393
-	 *
1394
-	 * @return Share[]
1395
-	 */
1396
-	public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1397
-		return [];
1398
-	}
1399
-
1400
-	/**
1401
-	 * Get the share by token possible with password
1402
-	 *
1403
-	 * @param string $token
1404
-	 * @return IShare
1405
-	 *
1406
-	 * @throws ShareNotFound
1407
-	 */
1408
-	public function getShareByToken($token) {
1409
-		// tokens cannot be valid local user names
1410
-		if ($this->userManager->userExists($token)) {
1411
-			throw new ShareNotFound();
1412
-		}
1413
-		$share = null;
1414
-		try {
1415
-			if ($this->shareApiAllowLinks()) {
1416
-				$provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1417
-				$share = $provider->getShareByToken($token);
1418
-			}
1419
-		} catch (ProviderException $e) {
1420
-		} catch (ShareNotFound $e) {
1421
-		}
1422
-
1423
-
1424
-		// If it is not a link share try to fetch a federated share by token
1425
-		if ($share === null) {
1426
-			try {
1427
-				$provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1428
-				$share = $provider->getShareByToken($token);
1429
-			} catch (ProviderException $e) {
1430
-			} catch (ShareNotFound $e) {
1431
-			}
1432
-		}
1433
-
1434
-		// If it is not a link share try to fetch a mail share by token
1435
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1436
-			try {
1437
-				$provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1438
-				$share = $provider->getShareByToken($token);
1439
-			} catch (ProviderException $e) {
1440
-			} catch (ShareNotFound $e) {
1441
-			}
1442
-		}
1443
-
1444
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1445
-			try {
1446
-				$provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1447
-				$share = $provider->getShareByToken($token);
1448
-			} catch (ProviderException $e) {
1449
-			} catch (ShareNotFound $e) {
1450
-			}
1451
-		}
1452
-
1453
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1454
-			try {
1455
-				$provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1456
-				$share = $provider->getShareByToken($token);
1457
-			} catch (ProviderException $e) {
1458
-			} catch (ShareNotFound $e) {
1459
-			}
1460
-		}
1461
-
1462
-		if ($share === null) {
1463
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1464
-		}
1465
-
1466
-		$this->checkShare($share);
1467
-
1468
-		/*
1262
+        $shares2 = [];
1263
+
1264
+        while (true) {
1265
+            $added = 0;
1266
+            foreach ($shares as $share) {
1267
+                if ($onlyValid) {
1268
+                    try {
1269
+                        $this->checkShare($share);
1270
+                    } catch (ShareNotFound $e) {
1271
+                        // Ignore since this basically means the share is deleted
1272
+                        continue;
1273
+                    }
1274
+                }
1275
+
1276
+                $added++;
1277
+                $shares2[] = $share;
1278
+
1279
+                if (count($shares2) === $limit) {
1280
+                    break;
1281
+                }
1282
+            }
1283
+
1284
+            // If we did not fetch more shares than the limit then there are no more shares
1285
+            if (count($shares) < $limit) {
1286
+                break;
1287
+            }
1288
+
1289
+            if (count($shares2) === $limit) {
1290
+                break;
1291
+            }
1292
+
1293
+            // If there was no limit on the select we are done
1294
+            if ($limit === -1) {
1295
+                break;
1296
+            }
1297
+
1298
+            $offset += $added;
1299
+
1300
+            // Fetch again $limit shares
1301
+            if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
1302
+                // We already fetched all shares, so end here
1303
+                $shares = [];
1304
+            } else {
1305
+                $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1306
+            }
1307
+
1308
+            // No more shares means we are done
1309
+            if (empty($shares)) {
1310
+                break;
1311
+            }
1312
+        }
1313
+
1314
+        $shares = $shares2;
1315
+
1316
+        return $shares;
1317
+    }
1318
+
1319
+    /**
1320
+     * @inheritdoc
1321
+     */
1322
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1323
+        try {
1324
+            $provider = $this->factory->getProviderForType($shareType);
1325
+        } catch (ProviderException $e) {
1326
+            return [];
1327
+        }
1328
+
1329
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1330
+
1331
+        // remove all shares which are already expired
1332
+        foreach ($shares as $key => $share) {
1333
+            try {
1334
+                $this->checkShare($share);
1335
+            } catch (ShareNotFound $e) {
1336
+                unset($shares[$key]);
1337
+            }
1338
+        }
1339
+
1340
+        return $shares;
1341
+    }
1342
+
1343
+    /**
1344
+     * @inheritdoc
1345
+     */
1346
+    public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1347
+        $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1348
+
1349
+        // Only get deleted shares
1350
+        $shares = array_filter($shares, function (IShare $share) {
1351
+            return $share->getPermissions() === 0;
1352
+        });
1353
+
1354
+        // Only get shares where the owner still exists
1355
+        $shares = array_filter($shares, function (IShare $share) {
1356
+            return $this->userManager->userExists($share->getShareOwner());
1357
+        });
1358
+
1359
+        return $shares;
1360
+    }
1361
+
1362
+    /**
1363
+     * @inheritdoc
1364
+     */
1365
+    public function getShareById($id, $recipient = null, bool $onlyValid = true) {
1366
+        if ($id === null) {
1367
+            throw new ShareNotFound();
1368
+        }
1369
+
1370
+        [$providerId, $id] = $this->splitFullId($id);
1371
+
1372
+        try {
1373
+            $provider = $this->factory->getProvider($providerId);
1374
+        } catch (ProviderException $e) {
1375
+            throw new ShareNotFound();
1376
+        }
1377
+
1378
+        $share = $provider->getShareById($id, $recipient);
1379
+
1380
+        if ($onlyValid) {
1381
+            $this->checkShare($share);
1382
+        }
1383
+
1384
+        return $share;
1385
+    }
1386
+
1387
+    /**
1388
+     * Get all the shares for a given path
1389
+     *
1390
+     * @param \OCP\Files\Node $path
1391
+     * @param int $page
1392
+     * @param int $perPage
1393
+     *
1394
+     * @return Share[]
1395
+     */
1396
+    public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1397
+        return [];
1398
+    }
1399
+
1400
+    /**
1401
+     * Get the share by token possible with password
1402
+     *
1403
+     * @param string $token
1404
+     * @return IShare
1405
+     *
1406
+     * @throws ShareNotFound
1407
+     */
1408
+    public function getShareByToken($token) {
1409
+        // tokens cannot be valid local user names
1410
+        if ($this->userManager->userExists($token)) {
1411
+            throw new ShareNotFound();
1412
+        }
1413
+        $share = null;
1414
+        try {
1415
+            if ($this->shareApiAllowLinks()) {
1416
+                $provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1417
+                $share = $provider->getShareByToken($token);
1418
+            }
1419
+        } catch (ProviderException $e) {
1420
+        } catch (ShareNotFound $e) {
1421
+        }
1422
+
1423
+
1424
+        // If it is not a link share try to fetch a federated share by token
1425
+        if ($share === null) {
1426
+            try {
1427
+                $provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1428
+                $share = $provider->getShareByToken($token);
1429
+            } catch (ProviderException $e) {
1430
+            } catch (ShareNotFound $e) {
1431
+            }
1432
+        }
1433
+
1434
+        // If it is not a link share try to fetch a mail share by token
1435
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1436
+            try {
1437
+                $provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1438
+                $share = $provider->getShareByToken($token);
1439
+            } catch (ProviderException $e) {
1440
+            } catch (ShareNotFound $e) {
1441
+            }
1442
+        }
1443
+
1444
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1445
+            try {
1446
+                $provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1447
+                $share = $provider->getShareByToken($token);
1448
+            } catch (ProviderException $e) {
1449
+            } catch (ShareNotFound $e) {
1450
+            }
1451
+        }
1452
+
1453
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1454
+            try {
1455
+                $provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1456
+                $share = $provider->getShareByToken($token);
1457
+            } catch (ProviderException $e) {
1458
+            } catch (ShareNotFound $e) {
1459
+            }
1460
+        }
1461
+
1462
+        if ($share === null) {
1463
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1464
+        }
1465
+
1466
+        $this->checkShare($share);
1467
+
1468
+        /*
1469 1469
 		 * Reduce the permissions for link or email shares if public upload is not enabled
1470 1470
 		 */
1471
-		if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1472
-			&& $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1473
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1474
-		}
1475
-
1476
-		return $share;
1477
-	}
1478
-
1479
-	/**
1480
-	 * Check expire date and disabled owner
1481
-	 *
1482
-	 * @throws ShareNotFound
1483
-	 */
1484
-	protected function checkShare(IShare $share): void {
1485
-		if ($share->isExpired()) {
1486
-			$this->deleteShare($share);
1487
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1488
-		}
1489
-		if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
1490
-			$uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
1491
-			foreach ($uids as $uid) {
1492
-				$user = $this->userManager->get($uid);
1493
-				if ($user?->isEnabled() === false) {
1494
-					throw new ShareNotFound($this->l->t('The requested share comes from a disabled user'));
1495
-				}
1496
-			}
1497
-		}
1498
-	}
1499
-
1500
-	/**
1501
-	 * Verify the password of a public share
1502
-	 *
1503
-	 * @param IShare $share
1504
-	 * @param ?string $password
1505
-	 * @return bool
1506
-	 */
1507
-	public function checkPassword(IShare $share, $password) {
1508
-
1509
-		// if there is no password on the share object / passsword is null, there is nothing to check
1510
-		if ($password === null || $share->getPassword() === null) {
1511
-			return false;
1512
-		}
1513
-
1514
-		// Makes sure password hasn't expired
1515
-		$expirationTime = $share->getPasswordExpirationTime();
1516
-		if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1517
-			return false;
1518
-		}
1519
-
1520
-		$newHash = '';
1521
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1522
-			return false;
1523
-		}
1524
-
1525
-		if (!empty($newHash)) {
1526
-			$share->setPassword($newHash);
1527
-			$provider = $this->factory->getProviderForType($share->getShareType());
1528
-			$provider->update($share);
1529
-		}
1530
-
1531
-		return true;
1532
-	}
1533
-
1534
-	/**
1535
-	 * @inheritdoc
1536
-	 */
1537
-	public function userDeleted($uid) {
1538
-		$types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1539
-
1540
-		foreach ($types as $type) {
1541
-			try {
1542
-				$provider = $this->factory->getProviderForType($type);
1543
-			} catch (ProviderException $e) {
1544
-				continue;
1545
-			}
1546
-			$provider->userDeleted($uid, $type);
1547
-		}
1548
-	}
1549
-
1550
-	/**
1551
-	 * @inheritdoc
1552
-	 */
1553
-	public function groupDeleted($gid) {
1554
-		foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1555
-			try {
1556
-				$provider = $this->factory->getProviderForType($type);
1557
-			} catch (ProviderException $e) {
1558
-				continue;
1559
-			}
1560
-			$provider->groupDeleted($gid);
1561
-		}
1562
-
1563
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1564
-		if ($excludedGroups === '') {
1565
-			return;
1566
-		}
1567
-
1568
-		$excludedGroups = json_decode($excludedGroups, true);
1569
-		if (json_last_error() !== JSON_ERROR_NONE) {
1570
-			return;
1571
-		}
1572
-
1573
-		$excludedGroups = array_diff($excludedGroups, [$gid]);
1574
-		$this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1575
-	}
1576
-
1577
-	/**
1578
-	 * @inheritdoc
1579
-	 */
1580
-	public function userDeletedFromGroup($uid, $gid) {
1581
-		foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1582
-			try {
1583
-				$provider = $this->factory->getProviderForType($type);
1584
-			} catch (ProviderException $e) {
1585
-				continue;
1586
-			}
1587
-			$provider->userDeletedFromGroup($uid, $gid);
1588
-		}
1589
-	}
1590
-
1591
-	/**
1592
-	 * Get access list to a path. This means
1593
-	 * all the users that can access a given path.
1594
-	 *
1595
-	 * Consider:
1596
-	 * -root
1597
-	 * |-folder1 (23)
1598
-	 *  |-folder2 (32)
1599
-	 *   |-fileA (42)
1600
-	 *
1601
-	 * fileA is shared with user1 and user1@server1 and email1@maildomain1
1602
-	 * folder2 is shared with group2 (user4 is a member of group2)
1603
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1604
-	 *                        and email2@maildomain2
1605
-	 *
1606
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1607
-	 * [
1608
-	 *  users  => [
1609
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1610
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1611
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1612
-	 *  ],
1613
-	 *  remote => [
1614
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1615
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1616
-	 *  ],
1617
-	 *  public => bool
1618
-	 *  mail => [
1619
-	 *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
1620
-	 *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
1621
-	 *  ]
1622
-	 * ]
1623
-	 *
1624
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1625
-	 * [
1626
-	 *  users  => ['user1', 'user2', 'user4'],
1627
-	 *  remote => bool,
1628
-	 *  public => bool
1629
-	 *  mail => ['email1@maildomain1', 'email2@maildomain2']
1630
-	 * ]
1631
-	 *
1632
-	 * This is required for encryption/activity
1633
-	 *
1634
-	 * @param \OCP\Files\Node $path
1635
-	 * @param bool $recursive Should we check all parent folders as well
1636
-	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1637
-	 * @return array
1638
-	 */
1639
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1640
-		$owner = $path->getOwner();
1641
-
1642
-		if ($owner === null) {
1643
-			return [];
1644
-		}
1645
-
1646
-		$owner = $owner->getUID();
1647
-
1648
-		if ($currentAccess) {
1649
-			$al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
1650
-		} else {
1651
-			$al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
1652
-		}
1653
-		if (!$this->userManager->userExists($owner)) {
1654
-			return $al;
1655
-		}
1656
-
1657
-		//Get node for the owner and correct the owner in case of external storage
1658
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1659
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1660
-			$path = $userFolder->getFirstNodeById($path->getId());
1661
-			if ($path === null || $path->getOwner() === null) {
1662
-				return [];
1663
-			}
1664
-			$owner = $path->getOwner()->getUID();
1665
-		}
1666
-
1667
-		$providers = $this->factory->getAllProviders();
1668
-
1669
-		/** @var Node[] $nodes */
1670
-		$nodes = [];
1671
-
1672
-
1673
-		if ($currentAccess) {
1674
-			$ownerPath = $path->getPath();
1675
-			$ownerPath = explode('/', $ownerPath, 4);
1676
-			if (count($ownerPath) < 4) {
1677
-				$ownerPath = '';
1678
-			} else {
1679
-				$ownerPath = $ownerPath[3];
1680
-			}
1681
-			$al['users'][$owner] = [
1682
-				'node_id' => $path->getId(),
1683
-				'node_path' => '/' . $ownerPath,
1684
-			];
1685
-		} else {
1686
-			$al['users'][] = $owner;
1687
-		}
1688
-
1689
-		// Collect all the shares
1690
-		while ($path->getPath() !== $userFolder->getPath()) {
1691
-			$nodes[] = $path;
1692
-			if (!$recursive) {
1693
-				break;
1694
-			}
1695
-			$path = $path->getParent();
1696
-		}
1697
-
1698
-		foreach ($providers as $provider) {
1699
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1700
-
1701
-			foreach ($tmp as $k => $v) {
1702
-				if (isset($al[$k])) {
1703
-					if (is_array($al[$k])) {
1704
-						if ($currentAccess) {
1705
-							$al[$k] += $v;
1706
-						} else {
1707
-							$al[$k] = array_merge($al[$k], $v);
1708
-							$al[$k] = array_unique($al[$k]);
1709
-							$al[$k] = array_values($al[$k]);
1710
-						}
1711
-					} else {
1712
-						$al[$k] = $al[$k] || $v;
1713
-					}
1714
-				} else {
1715
-					$al[$k] = $v;
1716
-				}
1717
-			}
1718
-		}
1719
-
1720
-		return $al;
1721
-	}
1722
-
1723
-	/**
1724
-	 * Create a new share
1725
-	 *
1726
-	 * @return IShare
1727
-	 */
1728
-	public function newShare() {
1729
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1730
-	}
1731
-
1732
-	/**
1733
-	 * Is the share API enabled
1734
-	 *
1735
-	 * @return bool
1736
-	 */
1737
-	public function shareApiEnabled() {
1738
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1739
-	}
1740
-
1741
-	/**
1742
-	 * Is public link sharing enabled
1743
-	 *
1744
-	 * @return bool
1745
-	 */
1746
-	public function shareApiAllowLinks() {
1747
-		if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1748
-			return false;
1749
-		}
1750
-
1751
-		$user = $this->userSession->getUser();
1752
-		if ($user) {
1753
-			$excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1754
-			if ($excludedGroups) {
1755
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1756
-				return !(bool)array_intersect($excludedGroups, $userGroups);
1757
-			}
1758
-		}
1759
-
1760
-		return true;
1761
-	}
1762
-
1763
-	/**
1764
-	 * Is password on public link requires
1765
-	 *
1766
-	 * @param bool Check group membership exclusion
1767
-	 * @return bool
1768
-	 */
1769
-	public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1770
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1771
-		if ($excludedGroups !== '' && $checkGroupMembership) {
1772
-			$excludedGroups = json_decode($excludedGroups);
1773
-			$user = $this->userSession->getUser();
1774
-			if ($user) {
1775
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1776
-				if ((bool)array_intersect($excludedGroups, $userGroups)) {
1777
-					return false;
1778
-				}
1779
-			}
1780
-		}
1781
-		return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
1782
-	}
1783
-
1784
-	/**
1785
-	 * Is default link expire date enabled
1786
-	 *
1787
-	 * @return bool
1788
-	 */
1789
-	public function shareApiLinkDefaultExpireDate() {
1790
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1791
-	}
1792
-
1793
-	/**
1794
-	 * Is default link expire date enforced
1795
-	 *`
1796
-	 *
1797
-	 * @return bool
1798
-	 */
1799
-	public function shareApiLinkDefaultExpireDateEnforced() {
1800
-		return $this->shareApiLinkDefaultExpireDate()
1801
-			&& $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1802
-	}
1803
-
1804
-
1805
-	/**
1806
-	 * Number of default link expire days
1807
-	 *
1808
-	 * @return int
1809
-	 */
1810
-	public function shareApiLinkDefaultExpireDays() {
1811
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1812
-	}
1813
-
1814
-	/**
1815
-	 * Is default internal expire date enabled
1816
-	 *
1817
-	 * @return bool
1818
-	 */
1819
-	public function shareApiInternalDefaultExpireDate(): bool {
1820
-		return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1821
-	}
1822
-
1823
-	/**
1824
-	 * Is default remote expire date enabled
1825
-	 *
1826
-	 * @return bool
1827
-	 */
1828
-	public function shareApiRemoteDefaultExpireDate(): bool {
1829
-		return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1830
-	}
1831
-
1832
-	/**
1833
-	 * Is default expire date enforced
1834
-	 *
1835
-	 * @return bool
1836
-	 */
1837
-	public function shareApiInternalDefaultExpireDateEnforced(): bool {
1838
-		return $this->shareApiInternalDefaultExpireDate()
1839
-			&& $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1840
-	}
1841
-
1842
-	/**
1843
-	 * Is default expire date enforced for remote shares
1844
-	 *
1845
-	 * @return bool
1846
-	 */
1847
-	public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1848
-		return $this->shareApiRemoteDefaultExpireDate()
1849
-			&& $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1850
-	}
1851
-
1852
-	/**
1853
-	 * Number of default expire days
1854
-	 *
1855
-	 * @return int
1856
-	 */
1857
-	public function shareApiInternalDefaultExpireDays(): int {
1858
-		return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1859
-	}
1860
-
1861
-	/**
1862
-	 * Number of default expire days for remote shares
1863
-	 *
1864
-	 * @return int
1865
-	 */
1866
-	public function shareApiRemoteDefaultExpireDays(): int {
1867
-		return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1868
-	}
1869
-
1870
-	/**
1871
-	 * Allow public upload on link shares
1872
-	 *
1873
-	 * @return bool
1874
-	 */
1875
-	public function shareApiLinkAllowPublicUpload() {
1876
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1877
-	}
1878
-
1879
-	/**
1880
-	 * check if user can only share with group members
1881
-	 *
1882
-	 * @return bool
1883
-	 */
1884
-	public function shareWithGroupMembersOnly() {
1885
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1886
-	}
1887
-
1888
-	/**
1889
-	 * If shareWithGroupMembersOnly is enabled, return an optional
1890
-	 * list of groups that must be excluded from the principle of
1891
-	 * belonging to the same group.
1892
-	 *
1893
-	 * @return array
1894
-	 */
1895
-	public function shareWithGroupMembersOnlyExcludeGroupsList() {
1896
-		if (!$this->shareWithGroupMembersOnly()) {
1897
-			return [];
1898
-		}
1899
-		$excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
1900
-		return json_decode($excludeGroups, true) ?? [];
1901
-	}
1902
-
1903
-	/**
1904
-	 * Check if users can share with groups
1905
-	 *
1906
-	 * @return bool
1907
-	 */
1908
-	public function allowGroupSharing() {
1909
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1910
-	}
1911
-
1912
-	public function allowEnumeration(): bool {
1913
-		return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1914
-	}
1915
-
1916
-	public function limitEnumerationToGroups(): bool {
1917
-		return $this->allowEnumeration()
1918
-			&& $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1919
-	}
1920
-
1921
-	public function limitEnumerationToPhone(): bool {
1922
-		return $this->allowEnumeration()
1923
-			&& $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1924
-	}
1925
-
1926
-	public function allowEnumerationFullMatch(): bool {
1927
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1928
-	}
1929
-
1930
-	public function matchEmail(): bool {
1931
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1932
-	}
1933
-
1934
-	public function ignoreSecondDisplayName(): bool {
1935
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1936
-	}
1937
-
1938
-	public function allowCustomTokens(): bool {
1939
-		return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_CUSTOM_TOKEN);
1940
-	}
1941
-
1942
-	public function allowViewWithoutDownload(): bool {
1943
-		return $this->appConfig->getValueBool('core', 'shareapi_allow_view_without_download', true);
1944
-	}
1945
-
1946
-	public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1947
-		if ($this->allowEnumerationFullMatch()) {
1948
-			return true;
1949
-		}
1950
-
1951
-		if (!$this->allowEnumeration()) {
1952
-			return false;
1953
-		}
1954
-
1955
-		if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1956
-			// Enumeration is enabled and not restricted: OK
1957
-			return true;
1958
-		}
1959
-
1960
-		if (!$currentUser instanceof IUser) {
1961
-			// Enumeration restrictions require an account
1962
-			return false;
1963
-		}
1964
-
1965
-		// Enumeration is limited to phone match
1966
-		if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1967
-			return true;
1968
-		}
1969
-
1970
-		// Enumeration is limited to groups
1971
-		if ($this->limitEnumerationToGroups()) {
1972
-			$currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
1973
-			$targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
1974
-			if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
1975
-				return true;
1976
-			}
1977
-		}
1978
-
1979
-		return false;
1980
-	}
1981
-
1982
-	/**
1983
-	 * Check if sharing is disabled for the current user
1984
-	 */
1985
-	public function sharingDisabledForUser(?string $userId): bool {
1986
-		return $this->shareDisableChecker->sharingDisabledForUser($userId);
1987
-	}
1988
-
1989
-	/**
1990
-	 * @inheritdoc
1991
-	 */
1992
-	public function outgoingServer2ServerSharesAllowed() {
1993
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1994
-	}
1995
-
1996
-	/**
1997
-	 * @inheritdoc
1998
-	 */
1999
-	public function outgoingServer2ServerGroupSharesAllowed() {
2000
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2001
-	}
2002
-
2003
-	/**
2004
-	 * @inheritdoc
2005
-	 */
2006
-	public function shareProviderExists($shareType) {
2007
-		try {
2008
-			$this->factory->getProviderForType($shareType);
2009
-		} catch (ProviderException $e) {
2010
-			return false;
2011
-		}
2012
-
2013
-		return true;
2014
-	}
2015
-
2016
-	public function registerShareProvider(string $shareProviderClass): void {
2017
-		$this->factory->registerProvider($shareProviderClass);
2018
-	}
2019
-
2020
-	public function getAllShares(): iterable {
2021
-		$providers = $this->factory->getAllProviders();
2022
-
2023
-		foreach ($providers as $provider) {
2024
-			yield from $provider->getAllShares();
2025
-		}
2026
-	}
2027
-
2028
-	public function generateToken(): string {
2029
-		// Initial token length
2030
-		$tokenLength = \OC\Share\Helper::getTokenLength();
2031
-
2032
-		do {
2033
-			$tokenExists = false;
2034
-
2035
-			for ($i = 0; $i <= 2; $i++) {
2036
-				// Generate a new token
2037
-				$token = $this->secureRandom->generate(
2038
-					$tokenLength,
2039
-					ISecureRandom::CHAR_HUMAN_READABLE,
2040
-				);
2041
-
2042
-				try {
2043
-					// Try to fetch a share with the generated token
2044
-					$this->getShareByToken($token);
2045
-					$tokenExists = true; // Token exists, we need to try again
2046
-				} catch (ShareNotFound $e) {
2047
-					// Token is unique, exit the loop
2048
-					$tokenExists = false;
2049
-					break;
2050
-				}
2051
-			}
2052
-
2053
-			// If we've reached the maximum attempts and the token still exists, increase the token length
2054
-			if ($tokenExists) {
2055
-				$tokenLength++;
2056
-
2057
-				// Check if the token length exceeds the maximum allowed length
2058
-				if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) {
2059
-					throw new ShareTokenException('Unable to generate a unique share token. Maximum token length exceeded.');
2060
-				}
2061
-			}
2062
-		} while ($tokenExists);
2063
-
2064
-		return $token;
2065
-	}
1471
+        if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1472
+            && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1473
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1474
+        }
1475
+
1476
+        return $share;
1477
+    }
1478
+
1479
+    /**
1480
+     * Check expire date and disabled owner
1481
+     *
1482
+     * @throws ShareNotFound
1483
+     */
1484
+    protected function checkShare(IShare $share): void {
1485
+        if ($share->isExpired()) {
1486
+            $this->deleteShare($share);
1487
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1488
+        }
1489
+        if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
1490
+            $uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
1491
+            foreach ($uids as $uid) {
1492
+                $user = $this->userManager->get($uid);
1493
+                if ($user?->isEnabled() === false) {
1494
+                    throw new ShareNotFound($this->l->t('The requested share comes from a disabled user'));
1495
+                }
1496
+            }
1497
+        }
1498
+    }
1499
+
1500
+    /**
1501
+     * Verify the password of a public share
1502
+     *
1503
+     * @param IShare $share
1504
+     * @param ?string $password
1505
+     * @return bool
1506
+     */
1507
+    public function checkPassword(IShare $share, $password) {
1508
+
1509
+        // if there is no password on the share object / passsword is null, there is nothing to check
1510
+        if ($password === null || $share->getPassword() === null) {
1511
+            return false;
1512
+        }
1513
+
1514
+        // Makes sure password hasn't expired
1515
+        $expirationTime = $share->getPasswordExpirationTime();
1516
+        if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1517
+            return false;
1518
+        }
1519
+
1520
+        $newHash = '';
1521
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1522
+            return false;
1523
+        }
1524
+
1525
+        if (!empty($newHash)) {
1526
+            $share->setPassword($newHash);
1527
+            $provider = $this->factory->getProviderForType($share->getShareType());
1528
+            $provider->update($share);
1529
+        }
1530
+
1531
+        return true;
1532
+    }
1533
+
1534
+    /**
1535
+     * @inheritdoc
1536
+     */
1537
+    public function userDeleted($uid) {
1538
+        $types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1539
+
1540
+        foreach ($types as $type) {
1541
+            try {
1542
+                $provider = $this->factory->getProviderForType($type);
1543
+            } catch (ProviderException $e) {
1544
+                continue;
1545
+            }
1546
+            $provider->userDeleted($uid, $type);
1547
+        }
1548
+    }
1549
+
1550
+    /**
1551
+     * @inheritdoc
1552
+     */
1553
+    public function groupDeleted($gid) {
1554
+        foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1555
+            try {
1556
+                $provider = $this->factory->getProviderForType($type);
1557
+            } catch (ProviderException $e) {
1558
+                continue;
1559
+            }
1560
+            $provider->groupDeleted($gid);
1561
+        }
1562
+
1563
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1564
+        if ($excludedGroups === '') {
1565
+            return;
1566
+        }
1567
+
1568
+        $excludedGroups = json_decode($excludedGroups, true);
1569
+        if (json_last_error() !== JSON_ERROR_NONE) {
1570
+            return;
1571
+        }
1572
+
1573
+        $excludedGroups = array_diff($excludedGroups, [$gid]);
1574
+        $this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1575
+    }
1576
+
1577
+    /**
1578
+     * @inheritdoc
1579
+     */
1580
+    public function userDeletedFromGroup($uid, $gid) {
1581
+        foreach ([IShare::TYPE_GROUP, IShare::TYPE_REMOTE_GROUP] as $type) {
1582
+            try {
1583
+                $provider = $this->factory->getProviderForType($type);
1584
+            } catch (ProviderException $e) {
1585
+                continue;
1586
+            }
1587
+            $provider->userDeletedFromGroup($uid, $gid);
1588
+        }
1589
+    }
1590
+
1591
+    /**
1592
+     * Get access list to a path. This means
1593
+     * all the users that can access a given path.
1594
+     *
1595
+     * Consider:
1596
+     * -root
1597
+     * |-folder1 (23)
1598
+     *  |-folder2 (32)
1599
+     *   |-fileA (42)
1600
+     *
1601
+     * fileA is shared with user1 and user1@server1 and email1@maildomain1
1602
+     * folder2 is shared with group2 (user4 is a member of group2)
1603
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1604
+     *                        and email2@maildomain2
1605
+     *
1606
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1607
+     * [
1608
+     *  users  => [
1609
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1610
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1611
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1612
+     *  ],
1613
+     *  remote => [
1614
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1615
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1616
+     *  ],
1617
+     *  public => bool
1618
+     *  mail => [
1619
+     *      'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
1620
+     *      'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
1621
+     *  ]
1622
+     * ]
1623
+     *
1624
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1625
+     * [
1626
+     *  users  => ['user1', 'user2', 'user4'],
1627
+     *  remote => bool,
1628
+     *  public => bool
1629
+     *  mail => ['email1@maildomain1', 'email2@maildomain2']
1630
+     * ]
1631
+     *
1632
+     * This is required for encryption/activity
1633
+     *
1634
+     * @param \OCP\Files\Node $path
1635
+     * @param bool $recursive Should we check all parent folders as well
1636
+     * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1637
+     * @return array
1638
+     */
1639
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1640
+        $owner = $path->getOwner();
1641
+
1642
+        if ($owner === null) {
1643
+            return [];
1644
+        }
1645
+
1646
+        $owner = $owner->getUID();
1647
+
1648
+        if ($currentAccess) {
1649
+            $al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
1650
+        } else {
1651
+            $al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
1652
+        }
1653
+        if (!$this->userManager->userExists($owner)) {
1654
+            return $al;
1655
+        }
1656
+
1657
+        //Get node for the owner and correct the owner in case of external storage
1658
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1659
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1660
+            $path = $userFolder->getFirstNodeById($path->getId());
1661
+            if ($path === null || $path->getOwner() === null) {
1662
+                return [];
1663
+            }
1664
+            $owner = $path->getOwner()->getUID();
1665
+        }
1666
+
1667
+        $providers = $this->factory->getAllProviders();
1668
+
1669
+        /** @var Node[] $nodes */
1670
+        $nodes = [];
1671
+
1672
+
1673
+        if ($currentAccess) {
1674
+            $ownerPath = $path->getPath();
1675
+            $ownerPath = explode('/', $ownerPath, 4);
1676
+            if (count($ownerPath) < 4) {
1677
+                $ownerPath = '';
1678
+            } else {
1679
+                $ownerPath = $ownerPath[3];
1680
+            }
1681
+            $al['users'][$owner] = [
1682
+                'node_id' => $path->getId(),
1683
+                'node_path' => '/' . $ownerPath,
1684
+            ];
1685
+        } else {
1686
+            $al['users'][] = $owner;
1687
+        }
1688
+
1689
+        // Collect all the shares
1690
+        while ($path->getPath() !== $userFolder->getPath()) {
1691
+            $nodes[] = $path;
1692
+            if (!$recursive) {
1693
+                break;
1694
+            }
1695
+            $path = $path->getParent();
1696
+        }
1697
+
1698
+        foreach ($providers as $provider) {
1699
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1700
+
1701
+            foreach ($tmp as $k => $v) {
1702
+                if (isset($al[$k])) {
1703
+                    if (is_array($al[$k])) {
1704
+                        if ($currentAccess) {
1705
+                            $al[$k] += $v;
1706
+                        } else {
1707
+                            $al[$k] = array_merge($al[$k], $v);
1708
+                            $al[$k] = array_unique($al[$k]);
1709
+                            $al[$k] = array_values($al[$k]);
1710
+                        }
1711
+                    } else {
1712
+                        $al[$k] = $al[$k] || $v;
1713
+                    }
1714
+                } else {
1715
+                    $al[$k] = $v;
1716
+                }
1717
+            }
1718
+        }
1719
+
1720
+        return $al;
1721
+    }
1722
+
1723
+    /**
1724
+     * Create a new share
1725
+     *
1726
+     * @return IShare
1727
+     */
1728
+    public function newShare() {
1729
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1730
+    }
1731
+
1732
+    /**
1733
+     * Is the share API enabled
1734
+     *
1735
+     * @return bool
1736
+     */
1737
+    public function shareApiEnabled() {
1738
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1739
+    }
1740
+
1741
+    /**
1742
+     * Is public link sharing enabled
1743
+     *
1744
+     * @return bool
1745
+     */
1746
+    public function shareApiAllowLinks() {
1747
+        if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1748
+            return false;
1749
+        }
1750
+
1751
+        $user = $this->userSession->getUser();
1752
+        if ($user) {
1753
+            $excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1754
+            if ($excludedGroups) {
1755
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1756
+                return !(bool)array_intersect($excludedGroups, $userGroups);
1757
+            }
1758
+        }
1759
+
1760
+        return true;
1761
+    }
1762
+
1763
+    /**
1764
+     * Is password on public link requires
1765
+     *
1766
+     * @param bool Check group membership exclusion
1767
+     * @return bool
1768
+     */
1769
+    public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1770
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1771
+        if ($excludedGroups !== '' && $checkGroupMembership) {
1772
+            $excludedGroups = json_decode($excludedGroups);
1773
+            $user = $this->userSession->getUser();
1774
+            if ($user) {
1775
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1776
+                if ((bool)array_intersect($excludedGroups, $userGroups)) {
1777
+                    return false;
1778
+                }
1779
+            }
1780
+        }
1781
+        return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
1782
+    }
1783
+
1784
+    /**
1785
+     * Is default link expire date enabled
1786
+     *
1787
+     * @return bool
1788
+     */
1789
+    public function shareApiLinkDefaultExpireDate() {
1790
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1791
+    }
1792
+
1793
+    /**
1794
+     * Is default link expire date enforced
1795
+     *`
1796
+     *
1797
+     * @return bool
1798
+     */
1799
+    public function shareApiLinkDefaultExpireDateEnforced() {
1800
+        return $this->shareApiLinkDefaultExpireDate()
1801
+            && $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1802
+    }
1803
+
1804
+
1805
+    /**
1806
+     * Number of default link expire days
1807
+     *
1808
+     * @return int
1809
+     */
1810
+    public function shareApiLinkDefaultExpireDays() {
1811
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1812
+    }
1813
+
1814
+    /**
1815
+     * Is default internal expire date enabled
1816
+     *
1817
+     * @return bool
1818
+     */
1819
+    public function shareApiInternalDefaultExpireDate(): bool {
1820
+        return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1821
+    }
1822
+
1823
+    /**
1824
+     * Is default remote expire date enabled
1825
+     *
1826
+     * @return bool
1827
+     */
1828
+    public function shareApiRemoteDefaultExpireDate(): bool {
1829
+        return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1830
+    }
1831
+
1832
+    /**
1833
+     * Is default expire date enforced
1834
+     *
1835
+     * @return bool
1836
+     */
1837
+    public function shareApiInternalDefaultExpireDateEnforced(): bool {
1838
+        return $this->shareApiInternalDefaultExpireDate()
1839
+            && $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1840
+    }
1841
+
1842
+    /**
1843
+     * Is default expire date enforced for remote shares
1844
+     *
1845
+     * @return bool
1846
+     */
1847
+    public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1848
+        return $this->shareApiRemoteDefaultExpireDate()
1849
+            && $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1850
+    }
1851
+
1852
+    /**
1853
+     * Number of default expire days
1854
+     *
1855
+     * @return int
1856
+     */
1857
+    public function shareApiInternalDefaultExpireDays(): int {
1858
+        return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1859
+    }
1860
+
1861
+    /**
1862
+     * Number of default expire days for remote shares
1863
+     *
1864
+     * @return int
1865
+     */
1866
+    public function shareApiRemoteDefaultExpireDays(): int {
1867
+        return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1868
+    }
1869
+
1870
+    /**
1871
+     * Allow public upload on link shares
1872
+     *
1873
+     * @return bool
1874
+     */
1875
+    public function shareApiLinkAllowPublicUpload() {
1876
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1877
+    }
1878
+
1879
+    /**
1880
+     * check if user can only share with group members
1881
+     *
1882
+     * @return bool
1883
+     */
1884
+    public function shareWithGroupMembersOnly() {
1885
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1886
+    }
1887
+
1888
+    /**
1889
+     * If shareWithGroupMembersOnly is enabled, return an optional
1890
+     * list of groups that must be excluded from the principle of
1891
+     * belonging to the same group.
1892
+     *
1893
+     * @return array
1894
+     */
1895
+    public function shareWithGroupMembersOnlyExcludeGroupsList() {
1896
+        if (!$this->shareWithGroupMembersOnly()) {
1897
+            return [];
1898
+        }
1899
+        $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
1900
+        return json_decode($excludeGroups, true) ?? [];
1901
+    }
1902
+
1903
+    /**
1904
+     * Check if users can share with groups
1905
+     *
1906
+     * @return bool
1907
+     */
1908
+    public function allowGroupSharing() {
1909
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1910
+    }
1911
+
1912
+    public function allowEnumeration(): bool {
1913
+        return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1914
+    }
1915
+
1916
+    public function limitEnumerationToGroups(): bool {
1917
+        return $this->allowEnumeration()
1918
+            && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1919
+    }
1920
+
1921
+    public function limitEnumerationToPhone(): bool {
1922
+        return $this->allowEnumeration()
1923
+            && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1924
+    }
1925
+
1926
+    public function allowEnumerationFullMatch(): bool {
1927
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1928
+    }
1929
+
1930
+    public function matchEmail(): bool {
1931
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1932
+    }
1933
+
1934
+    public function ignoreSecondDisplayName(): bool {
1935
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1936
+    }
1937
+
1938
+    public function allowCustomTokens(): bool {
1939
+        return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_CUSTOM_TOKEN);
1940
+    }
1941
+
1942
+    public function allowViewWithoutDownload(): bool {
1943
+        return $this->appConfig->getValueBool('core', 'shareapi_allow_view_without_download', true);
1944
+    }
1945
+
1946
+    public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1947
+        if ($this->allowEnumerationFullMatch()) {
1948
+            return true;
1949
+        }
1950
+
1951
+        if (!$this->allowEnumeration()) {
1952
+            return false;
1953
+        }
1954
+
1955
+        if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1956
+            // Enumeration is enabled and not restricted: OK
1957
+            return true;
1958
+        }
1959
+
1960
+        if (!$currentUser instanceof IUser) {
1961
+            // Enumeration restrictions require an account
1962
+            return false;
1963
+        }
1964
+
1965
+        // Enumeration is limited to phone match
1966
+        if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1967
+            return true;
1968
+        }
1969
+
1970
+        // Enumeration is limited to groups
1971
+        if ($this->limitEnumerationToGroups()) {
1972
+            $currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
1973
+            $targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
1974
+            if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
1975
+                return true;
1976
+            }
1977
+        }
1978
+
1979
+        return false;
1980
+    }
1981
+
1982
+    /**
1983
+     * Check if sharing is disabled for the current user
1984
+     */
1985
+    public function sharingDisabledForUser(?string $userId): bool {
1986
+        return $this->shareDisableChecker->sharingDisabledForUser($userId);
1987
+    }
1988
+
1989
+    /**
1990
+     * @inheritdoc
1991
+     */
1992
+    public function outgoingServer2ServerSharesAllowed() {
1993
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1994
+    }
1995
+
1996
+    /**
1997
+     * @inheritdoc
1998
+     */
1999
+    public function outgoingServer2ServerGroupSharesAllowed() {
2000
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2001
+    }
2002
+
2003
+    /**
2004
+     * @inheritdoc
2005
+     */
2006
+    public function shareProviderExists($shareType) {
2007
+        try {
2008
+            $this->factory->getProviderForType($shareType);
2009
+        } catch (ProviderException $e) {
2010
+            return false;
2011
+        }
2012
+
2013
+        return true;
2014
+    }
2015
+
2016
+    public function registerShareProvider(string $shareProviderClass): void {
2017
+        $this->factory->registerProvider($shareProviderClass);
2018
+    }
2019
+
2020
+    public function getAllShares(): iterable {
2021
+        $providers = $this->factory->getAllProviders();
2022
+
2023
+        foreach ($providers as $provider) {
2024
+            yield from $provider->getAllShares();
2025
+        }
2026
+    }
2027
+
2028
+    public function generateToken(): string {
2029
+        // Initial token length
2030
+        $tokenLength = \OC\Share\Helper::getTokenLength();
2031
+
2032
+        do {
2033
+            $tokenExists = false;
2034
+
2035
+            for ($i = 0; $i <= 2; $i++) {
2036
+                // Generate a new token
2037
+                $token = $this->secureRandom->generate(
2038
+                    $tokenLength,
2039
+                    ISecureRandom::CHAR_HUMAN_READABLE,
2040
+                );
2041
+
2042
+                try {
2043
+                    // Try to fetch a share with the generated token
2044
+                    $this->getShareByToken($token);
2045
+                    $tokenExists = true; // Token exists, we need to try again
2046
+                } catch (ShareNotFound $e) {
2047
+                    // Token is unique, exit the loop
2048
+                    $tokenExists = false;
2049
+                    break;
2050
+                }
2051
+            }
2052
+
2053
+            // If we've reached the maximum attempts and the token still exists, increase the token length
2054
+            if ($tokenExists) {
2055
+                $tokenLength++;
2056
+
2057
+                // Check if the token length exceeds the maximum allowed length
2058
+                if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) {
2059
+                    throw new ShareTokenException('Unable to generate a unique share token. Maximum token length exceeded.');
2060
+                }
2061
+            }
2062
+        } while ($tokenExists);
2063
+
2064
+        return $token;
2065
+    }
2066 2066
 }
Please login to merge, or discard this patch.
lib/private/Template/JSConfigHelper.php 1 patch
Indentation   +241 added lines, -241 removed lines patch added patch discarded remove patch
@@ -41,270 +41,270 @@
 block discarded – undo
41 41
 
42 42
 class JSConfigHelper {
43 43
 
44
-	/** @var array user back-ends excluded from password verification */
45
-	private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
44
+    /** @var array user back-ends excluded from password verification */
45
+    private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
46 46
 
47
-	public function __construct(
48
-		protected ServerVersion $serverVersion,
49
-		protected IL10N $l,
50
-		protected Defaults $defaults,
51
-		protected IAppManager $appManager,
52
-		protected ISession $session,
53
-		protected ?IUser $currentUser,
54
-		protected IConfig $config,
55
-		protected readonly IAppConfig $appConfig,
56
-		protected IGroupManager $groupManager,
57
-		protected IniGetWrapper $iniWrapper,
58
-		protected IURLGenerator $urlGenerator,
59
-		protected CapabilitiesManager $capabilitiesManager,
60
-		protected IInitialStateService $initialStateService,
61
-		protected IProvider $tokenProvider,
62
-		protected FilenameValidator $filenameValidator,
63
-	) {
64
-	}
47
+    public function __construct(
48
+        protected ServerVersion $serverVersion,
49
+        protected IL10N $l,
50
+        protected Defaults $defaults,
51
+        protected IAppManager $appManager,
52
+        protected ISession $session,
53
+        protected ?IUser $currentUser,
54
+        protected IConfig $config,
55
+        protected readonly IAppConfig $appConfig,
56
+        protected IGroupManager $groupManager,
57
+        protected IniGetWrapper $iniWrapper,
58
+        protected IURLGenerator $urlGenerator,
59
+        protected CapabilitiesManager $capabilitiesManager,
60
+        protected IInitialStateService $initialStateService,
61
+        protected IProvider $tokenProvider,
62
+        protected FilenameValidator $filenameValidator,
63
+    ) {
64
+    }
65 65
 
66
-	public function getConfig(): string {
67
-		$userBackendAllowsPasswordConfirmation = true;
68
-		if ($this->currentUser !== null) {
69
-			$uid = $this->currentUser->getUID();
66
+    public function getConfig(): string {
67
+        $userBackendAllowsPasswordConfirmation = true;
68
+        if ($this->currentUser !== null) {
69
+            $uid = $this->currentUser->getUID();
70 70
 
71
-			$backend = $this->currentUser->getBackend();
72
-			if ($backend instanceof IPasswordConfirmationBackend) {
73
-				$userBackendAllowsPasswordConfirmation = $backend->canConfirmPassword($uid) && $this->canUserValidatePassword();
74
-			} elseif (isset($this->excludedUserBackEnds[$this->currentUser->getBackendClassName()])) {
75
-				$userBackendAllowsPasswordConfirmation = false;
76
-			} else {
77
-				$userBackendAllowsPasswordConfirmation = $this->canUserValidatePassword();
78
-			}
79
-		} else {
80
-			$uid = null;
81
-		}
71
+            $backend = $this->currentUser->getBackend();
72
+            if ($backend instanceof IPasswordConfirmationBackend) {
73
+                $userBackendAllowsPasswordConfirmation = $backend->canConfirmPassword($uid) && $this->canUserValidatePassword();
74
+            } elseif (isset($this->excludedUserBackEnds[$this->currentUser->getBackendClassName()])) {
75
+                $userBackendAllowsPasswordConfirmation = false;
76
+            } else {
77
+                $userBackendAllowsPasswordConfirmation = $this->canUserValidatePassword();
78
+            }
79
+        } else {
80
+            $uid = null;
81
+        }
82 82
 
83
-		// Get the config
84
-		$apps_paths = [];
83
+        // Get the config
84
+        $apps_paths = [];
85 85
 
86
-		if ($this->currentUser === null) {
87
-			$apps = $this->appManager->getEnabledApps();
88
-		} else {
89
-			$apps = $this->appManager->getEnabledAppsForUser($this->currentUser);
90
-		}
86
+        if ($this->currentUser === null) {
87
+            $apps = $this->appManager->getEnabledApps();
88
+        } else {
89
+            $apps = $this->appManager->getEnabledAppsForUser($this->currentUser);
90
+        }
91 91
 
92
-		foreach ($apps as $app) {
93
-			try {
94
-				$apps_paths[$app] = $this->appManager->getAppWebPath($app);
95
-			} catch (AppPathNotFoundException $e) {
96
-				$apps_paths[$app] = false;
97
-			}
98
-		}
92
+        foreach ($apps as $app) {
93
+            try {
94
+                $apps_paths[$app] = $this->appManager->getAppWebPath($app);
95
+            } catch (AppPathNotFoundException $e) {
96
+                $apps_paths[$app] = false;
97
+            }
98
+        }
99 99
 
100
-		$enableLinkPasswordByDefault = $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT);
101
-		$defaultExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
102
-		$defaultExpireDate = $enforceDefaultExpireDate = null;
103
-		if ($defaultExpireDateEnabled) {
104
-			$defaultExpireDate = (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
105
-			$enforceDefaultExpireDate = $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
106
-		}
107
-		$outgoingServer2serverShareEnabled = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
100
+        $enableLinkPasswordByDefault = $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT);
101
+        $defaultExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
102
+        $defaultExpireDate = $enforceDefaultExpireDate = null;
103
+        if ($defaultExpireDateEnabled) {
104
+            $defaultExpireDate = (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
105
+            $enforceDefaultExpireDate = $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
106
+        }
107
+        $outgoingServer2serverShareEnabled = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
108 108
 
109
-		$defaultInternalExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
110
-		$defaultInternalExpireDate = $defaultInternalExpireDateEnforced = null;
111
-		if ($defaultInternalExpireDateEnabled) {
112
-			$defaultInternalExpireDate = (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
113
-			$defaultInternalExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
114
-		}
109
+        $defaultInternalExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
110
+        $defaultInternalExpireDate = $defaultInternalExpireDateEnforced = null;
111
+        if ($defaultInternalExpireDateEnabled) {
112
+            $defaultInternalExpireDate = (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
113
+            $defaultInternalExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
114
+        }
115 115
 
116
-		$defaultRemoteExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
117
-		$defaultRemoteExpireDate = $defaultRemoteExpireDateEnforced = null;
118
-		if ($defaultRemoteExpireDateEnabled) {
119
-			$defaultRemoteExpireDate = (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
120
-			$defaultRemoteExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
121
-		}
116
+        $defaultRemoteExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
117
+        $defaultRemoteExpireDate = $defaultRemoteExpireDateEnforced = null;
118
+        if ($defaultRemoteExpireDateEnabled) {
119
+            $defaultRemoteExpireDate = (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
120
+            $defaultRemoteExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
121
+        }
122 122
 
123
-		$countOfDataLocation = 0;
124
-		$dataLocation = str_replace(\OC::$SERVERROOT . '/', '', $this->config->getSystemValue('datadirectory', ''), $countOfDataLocation);
125
-		if ($countOfDataLocation !== 1 || $uid === null || !$this->groupManager->isAdmin($uid)) {
126
-			$dataLocation = false;
127
-		}
123
+        $countOfDataLocation = 0;
124
+        $dataLocation = str_replace(\OC::$SERVERROOT . '/', '', $this->config->getSystemValue('datadirectory', ''), $countOfDataLocation);
125
+        if ($countOfDataLocation !== 1 || $uid === null || !$this->groupManager->isAdmin($uid)) {
126
+            $dataLocation = false;
127
+        }
128 128
 
129
-		if ($this->currentUser instanceof IUser) {
130
-			if ($this->canUserValidatePassword()) {
131
-				$lastConfirmTimestamp = $this->session->get('last-password-confirm');
132
-				if (!is_int($lastConfirmTimestamp)) {
133
-					$lastConfirmTimestamp = 0;
134
-				}
135
-			} else {
136
-				$lastConfirmTimestamp = PHP_INT_MAX;
137
-			}
138
-		} else {
139
-			$lastConfirmTimestamp = 0;
140
-		}
129
+        if ($this->currentUser instanceof IUser) {
130
+            if ($this->canUserValidatePassword()) {
131
+                $lastConfirmTimestamp = $this->session->get('last-password-confirm');
132
+                if (!is_int($lastConfirmTimestamp)) {
133
+                    $lastConfirmTimestamp = 0;
134
+                }
135
+            } else {
136
+                $lastConfirmTimestamp = PHP_INT_MAX;
137
+            }
138
+        } else {
139
+            $lastConfirmTimestamp = 0;
140
+        }
141 141
 
142
-		$capabilities = $this->capabilitiesManager->getCapabilities(false, true);
142
+        $capabilities = $this->capabilitiesManager->getCapabilities(false, true);
143 143
 
144
-		$userFirstDay = $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, null);
145
-		$firstDay = (int)($userFirstDay ?? $this->l->l('firstday', null));
144
+        $userFirstDay = $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, null);
145
+        $firstDay = (int)($userFirstDay ?? $this->l->l('firstday', null));
146 146
 
147
-		$config = [
148
-			/** @deprecated 30.0.0 - use files capabilities instead */
149
-			'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX,
150
-			/** @deprecated 30.0.0 - use files capabilities instead */
151
-			'forbidden_filename_characters' => $this->filenameValidator->getForbiddenCharacters(),
147
+        $config = [
148
+            /** @deprecated 30.0.0 - use files capabilities instead */
149
+            'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX,
150
+            /** @deprecated 30.0.0 - use files capabilities instead */
151
+            'forbidden_filename_characters' => $this->filenameValidator->getForbiddenCharacters(),
152 152
 
153
-			'auto_logout' => $this->config->getSystemValue('auto_logout', false),
154
-			'loglevel' => $this->config->getSystemValue('loglevel_frontend',
155
-				$this->config->getSystemValue('loglevel', ILogger::WARN)
156
-			),
157
-			'lost_password_link' => $this->config->getSystemValue('lost_password_link', null),
158
-			'modRewriteWorking' => $this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true',
159
-			'no_unsupported_browser_warning' => $this->config->getSystemValue('no_unsupported_browser_warning', false),
160
-			'session_keepalive' => $this->config->getSystemValue('session_keepalive', true),
161
-			'session_lifetime' => min($this->config->getSystemValue('session_lifetime', $this->iniWrapper->getNumeric('session.gc_maxlifetime')), $this->iniWrapper->getNumeric('session.gc_maxlifetime')),
162
-			'sharing.maxAutocompleteResults' => max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)),
163
-			'sharing.minSearchStringLength' => $this->config->getSystemValueInt('sharing.minSearchStringLength', 0),
164
-			'version' => implode('.', $this->serverVersion->getVersion()),
165
-			'versionstring' => $this->serverVersion->getVersionString(),
166
-			'enable_non-accessible_features' => $this->config->getSystemValueBool('enable_non-accessible_features', true),
167
-		];
153
+            'auto_logout' => $this->config->getSystemValue('auto_logout', false),
154
+            'loglevel' => $this->config->getSystemValue('loglevel_frontend',
155
+                $this->config->getSystemValue('loglevel', ILogger::WARN)
156
+            ),
157
+            'lost_password_link' => $this->config->getSystemValue('lost_password_link', null),
158
+            'modRewriteWorking' => $this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true',
159
+            'no_unsupported_browser_warning' => $this->config->getSystemValue('no_unsupported_browser_warning', false),
160
+            'session_keepalive' => $this->config->getSystemValue('session_keepalive', true),
161
+            'session_lifetime' => min($this->config->getSystemValue('session_lifetime', $this->iniWrapper->getNumeric('session.gc_maxlifetime')), $this->iniWrapper->getNumeric('session.gc_maxlifetime')),
162
+            'sharing.maxAutocompleteResults' => max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)),
163
+            'sharing.minSearchStringLength' => $this->config->getSystemValueInt('sharing.minSearchStringLength', 0),
164
+            'version' => implode('.', $this->serverVersion->getVersion()),
165
+            'versionstring' => $this->serverVersion->getVersionString(),
166
+            'enable_non-accessible_features' => $this->config->getSystemValueBool('enable_non-accessible_features', true),
167
+        ];
168 168
 
169
-		$shareManager = Server::get(IShareManager::class);
169
+        $shareManager = Server::get(IShareManager::class);
170 170
 
171
-		$array = [
172
-			'_oc_debug' => $this->config->getSystemValue('debug', false) ? 'true' : 'false',
173
-			'_oc_isadmin' => $uid !== null && $this->groupManager->isAdmin($uid) ? 'true' : 'false',
174
-			'backendAllowsPasswordConfirmation' => $userBackendAllowsPasswordConfirmation ? 'true' : 'false',
175
-			'oc_dataURL' => is_string($dataLocation) ? '"' . $dataLocation . '"' : 'false',
176
-			'_oc_webroot' => '"' . \OC::$WEBROOT . '"',
177
-			'_oc_appswebroots' => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution
178
-			'datepickerFormatDate' => json_encode($this->l->l('jsdate', null)),
179
-			'nc_lastLogin' => $lastConfirmTimestamp,
180
-			'nc_pageLoad' => time(),
181
-			'dayNames' => json_encode([
182
-				$this->l->t('Sunday'),
183
-				$this->l->t('Monday'),
184
-				$this->l->t('Tuesday'),
185
-				$this->l->t('Wednesday'),
186
-				$this->l->t('Thursday'),
187
-				$this->l->t('Friday'),
188
-				$this->l->t('Saturday')
189
-			]),
190
-			'dayNamesShort' => json_encode([
191
-				$this->l->t('Sun.'),
192
-				$this->l->t('Mon.'),
193
-				$this->l->t('Tue.'),
194
-				$this->l->t('Wed.'),
195
-				$this->l->t('Thu.'),
196
-				$this->l->t('Fri.'),
197
-				$this->l->t('Sat.')
198
-			]),
199
-			'dayNamesMin' => json_encode([
200
-				$this->l->t('Su'),
201
-				$this->l->t('Mo'),
202
-				$this->l->t('Tu'),
203
-				$this->l->t('We'),
204
-				$this->l->t('Th'),
205
-				$this->l->t('Fr'),
206
-				$this->l->t('Sa')
207
-			]),
208
-			'monthNames' => json_encode([
209
-				$this->l->t('January'),
210
-				$this->l->t('February'),
211
-				$this->l->t('March'),
212
-				$this->l->t('April'),
213
-				$this->l->t('May'),
214
-				$this->l->t('June'),
215
-				$this->l->t('July'),
216
-				$this->l->t('August'),
217
-				$this->l->t('September'),
218
-				$this->l->t('October'),
219
-				$this->l->t('November'),
220
-				$this->l->t('December')
221
-			]),
222
-			'monthNamesShort' => json_encode([
223
-				$this->l->t('Jan.'),
224
-				$this->l->t('Feb.'),
225
-				$this->l->t('Mar.'),
226
-				$this->l->t('Apr.'),
227
-				$this->l->t('May.'),
228
-				$this->l->t('Jun.'),
229
-				$this->l->t('Jul.'),
230
-				$this->l->t('Aug.'),
231
-				$this->l->t('Sep.'),
232
-				$this->l->t('Oct.'),
233
-				$this->l->t('Nov.'),
234
-				$this->l->t('Dec.')
235
-			]),
236
-			'firstDay' => json_encode($firstDay),
237
-			'_oc_config' => json_encode($config),
238
-			'oc_appconfig' => json_encode([
239
-				'core' => [
240
-					'defaultExpireDateEnabled' => $defaultExpireDateEnabled,
241
-					'defaultExpireDate' => $defaultExpireDate,
242
-					'defaultExpireDateEnforced' => $enforceDefaultExpireDate,
243
-					'enforcePasswordForPublicLink' => Util::isPublicLinkPasswordRequired(),
244
-					'enableLinkPasswordByDefault' => $enableLinkPasswordByDefault,
245
-					'sharingDisabledForUser' => $shareManager->sharingDisabledForUser($uid),
246
-					'resharingAllowed' => Share::isResharingAllowed(),
247
-					'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
248
-					'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'),
249
-					'allowGroupSharing' => $shareManager->allowGroupSharing(),
250
-					'defaultInternalExpireDateEnabled' => $defaultInternalExpireDateEnabled,
251
-					'defaultInternalExpireDate' => $defaultInternalExpireDate,
252
-					'defaultInternalExpireDateEnforced' => $defaultInternalExpireDateEnforced,
253
-					'defaultRemoteExpireDateEnabled' => $defaultRemoteExpireDateEnabled,
254
-					'defaultRemoteExpireDate' => $defaultRemoteExpireDate,
255
-					'defaultRemoteExpireDateEnforced' => $defaultRemoteExpireDateEnforced,
256
-				]
257
-			]),
258
-			'_theme' => json_encode([
259
-				'entity' => $this->defaults->getEntity(),
260
-				'name' => $this->defaults->getName(),
261
-				'productName' => $this->defaults->getProductName(),
262
-				'title' => $this->defaults->getTitle(),
263
-				'baseUrl' => $this->defaults->getBaseUrl(),
264
-				'syncClientUrl' => $this->defaults->getSyncClientUrl(),
265
-				'docBaseUrl' => $this->defaults->getDocBaseUrl(),
266
-				'docPlaceholderUrl' => $this->defaults->buildDocLinkToKey('PLACEHOLDER'),
267
-				'slogan' => $this->defaults->getSlogan(),
268
-				'logoClaim' => '',
269
-				'folder' => \OC_Util::getTheme(),
270
-			]),
271
-		];
171
+        $array = [
172
+            '_oc_debug' => $this->config->getSystemValue('debug', false) ? 'true' : 'false',
173
+            '_oc_isadmin' => $uid !== null && $this->groupManager->isAdmin($uid) ? 'true' : 'false',
174
+            'backendAllowsPasswordConfirmation' => $userBackendAllowsPasswordConfirmation ? 'true' : 'false',
175
+            'oc_dataURL' => is_string($dataLocation) ? '"' . $dataLocation . '"' : 'false',
176
+            '_oc_webroot' => '"' . \OC::$WEBROOT . '"',
177
+            '_oc_appswebroots' => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution
178
+            'datepickerFormatDate' => json_encode($this->l->l('jsdate', null)),
179
+            'nc_lastLogin' => $lastConfirmTimestamp,
180
+            'nc_pageLoad' => time(),
181
+            'dayNames' => json_encode([
182
+                $this->l->t('Sunday'),
183
+                $this->l->t('Monday'),
184
+                $this->l->t('Tuesday'),
185
+                $this->l->t('Wednesday'),
186
+                $this->l->t('Thursday'),
187
+                $this->l->t('Friday'),
188
+                $this->l->t('Saturday')
189
+            ]),
190
+            'dayNamesShort' => json_encode([
191
+                $this->l->t('Sun.'),
192
+                $this->l->t('Mon.'),
193
+                $this->l->t('Tue.'),
194
+                $this->l->t('Wed.'),
195
+                $this->l->t('Thu.'),
196
+                $this->l->t('Fri.'),
197
+                $this->l->t('Sat.')
198
+            ]),
199
+            'dayNamesMin' => json_encode([
200
+                $this->l->t('Su'),
201
+                $this->l->t('Mo'),
202
+                $this->l->t('Tu'),
203
+                $this->l->t('We'),
204
+                $this->l->t('Th'),
205
+                $this->l->t('Fr'),
206
+                $this->l->t('Sa')
207
+            ]),
208
+            'monthNames' => json_encode([
209
+                $this->l->t('January'),
210
+                $this->l->t('February'),
211
+                $this->l->t('March'),
212
+                $this->l->t('April'),
213
+                $this->l->t('May'),
214
+                $this->l->t('June'),
215
+                $this->l->t('July'),
216
+                $this->l->t('August'),
217
+                $this->l->t('September'),
218
+                $this->l->t('October'),
219
+                $this->l->t('November'),
220
+                $this->l->t('December')
221
+            ]),
222
+            'monthNamesShort' => json_encode([
223
+                $this->l->t('Jan.'),
224
+                $this->l->t('Feb.'),
225
+                $this->l->t('Mar.'),
226
+                $this->l->t('Apr.'),
227
+                $this->l->t('May.'),
228
+                $this->l->t('Jun.'),
229
+                $this->l->t('Jul.'),
230
+                $this->l->t('Aug.'),
231
+                $this->l->t('Sep.'),
232
+                $this->l->t('Oct.'),
233
+                $this->l->t('Nov.'),
234
+                $this->l->t('Dec.')
235
+            ]),
236
+            'firstDay' => json_encode($firstDay),
237
+            '_oc_config' => json_encode($config),
238
+            'oc_appconfig' => json_encode([
239
+                'core' => [
240
+                    'defaultExpireDateEnabled' => $defaultExpireDateEnabled,
241
+                    'defaultExpireDate' => $defaultExpireDate,
242
+                    'defaultExpireDateEnforced' => $enforceDefaultExpireDate,
243
+                    'enforcePasswordForPublicLink' => Util::isPublicLinkPasswordRequired(),
244
+                    'enableLinkPasswordByDefault' => $enableLinkPasswordByDefault,
245
+                    'sharingDisabledForUser' => $shareManager->sharingDisabledForUser($uid),
246
+                    'resharingAllowed' => Share::isResharingAllowed(),
247
+                    'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
248
+                    'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'),
249
+                    'allowGroupSharing' => $shareManager->allowGroupSharing(),
250
+                    'defaultInternalExpireDateEnabled' => $defaultInternalExpireDateEnabled,
251
+                    'defaultInternalExpireDate' => $defaultInternalExpireDate,
252
+                    'defaultInternalExpireDateEnforced' => $defaultInternalExpireDateEnforced,
253
+                    'defaultRemoteExpireDateEnabled' => $defaultRemoteExpireDateEnabled,
254
+                    'defaultRemoteExpireDate' => $defaultRemoteExpireDate,
255
+                    'defaultRemoteExpireDateEnforced' => $defaultRemoteExpireDateEnforced,
256
+                ]
257
+            ]),
258
+            '_theme' => json_encode([
259
+                'entity' => $this->defaults->getEntity(),
260
+                'name' => $this->defaults->getName(),
261
+                'productName' => $this->defaults->getProductName(),
262
+                'title' => $this->defaults->getTitle(),
263
+                'baseUrl' => $this->defaults->getBaseUrl(),
264
+                'syncClientUrl' => $this->defaults->getSyncClientUrl(),
265
+                'docBaseUrl' => $this->defaults->getDocBaseUrl(),
266
+                'docPlaceholderUrl' => $this->defaults->buildDocLinkToKey('PLACEHOLDER'),
267
+                'slogan' => $this->defaults->getSlogan(),
268
+                'logoClaim' => '',
269
+                'folder' => \OC_Util::getTheme(),
270
+            ]),
271
+        ];
272 272
 
273
-		if ($this->currentUser !== null) {
274
-			$array['oc_userconfig'] = json_encode([
275
-				'avatar' => [
276
-					'version' => (int)$this->config->getUserValue($uid, 'avatar', 'version', 0),
277
-					'generated' => $this->config->getUserValue($uid, 'avatar', 'generated', 'true') === 'true',
278
-				]
279
-			]);
280
-		}
273
+        if ($this->currentUser !== null) {
274
+            $array['oc_userconfig'] = json_encode([
275
+                'avatar' => [
276
+                    'version' => (int)$this->config->getUserValue($uid, 'avatar', 'version', 0),
277
+                    'generated' => $this->config->getUserValue($uid, 'avatar', 'generated', 'true') === 'true',
278
+                ]
279
+            ]);
280
+        }
281 281
 
282
-		$this->initialStateService->provideInitialState('core', 'projects_enabled', $this->config->getSystemValueBool('projects.enabled', false));
282
+        $this->initialStateService->provideInitialState('core', 'projects_enabled', $this->config->getSystemValueBool('projects.enabled', false));
283 283
 
284
-		$this->initialStateService->provideInitialState('core', 'config', $config);
285
-		$this->initialStateService->provideInitialState('core', 'capabilities', $capabilities);
284
+        $this->initialStateService->provideInitialState('core', 'config', $config);
285
+        $this->initialStateService->provideInitialState('core', 'capabilities', $capabilities);
286 286
 
287
-		// Allow hooks to modify the output values
288
-		\OC_Hook::emit('\OCP\Config', 'js', ['array' => &$array]);
287
+        // Allow hooks to modify the output values
288
+        \OC_Hook::emit('\OCP\Config', 'js', ['array' => &$array]);
289 289
 
290
-		$result = '';
290
+        $result = '';
291 291
 
292
-		// Echo it
293
-		foreach ($array as $setting => $value) {
294
-			$result .= 'var ' . $setting . '=' . $value . ';' . PHP_EOL;
295
-		}
292
+        // Echo it
293
+        foreach ($array as $setting => $value) {
294
+            $result .= 'var ' . $setting . '=' . $value . ';' . PHP_EOL;
295
+        }
296 296
 
297
-		return $result;
298
-	}
297
+        return $result;
298
+    }
299 299
 
300
-	protected function canUserValidatePassword(): bool {
301
-		try {
302
-			$token = $this->tokenProvider->getToken($this->session->getId());
303
-		} catch (ExpiredTokenException|WipeTokenException|InvalidTokenException|SessionNotAvailableException) {
304
-			// actually we do not know, so we fall back to this statement
305
-			return true;
306
-		}
307
-		$scope = $token->getScopeAsArray();
308
-		return !isset($scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION]) || $scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION] === false;
309
-	}
300
+    protected function canUserValidatePassword(): bool {
301
+        try {
302
+            $token = $this->tokenProvider->getToken($this->session->getId());
303
+        } catch (ExpiredTokenException|WipeTokenException|InvalidTokenException|SessionNotAvailableException) {
304
+            // actually we do not know, so we fall back to this statement
305
+            return true;
306
+        }
307
+        $scope = $token->getScopeAsArray();
308
+        return !isset($scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION]) || $scope[IToken::SCOPE_SKIP_PASSWORD_VALIDATION] === false;
309
+    }
310 310
 }
Please login to merge, or discard this patch.
core/AppInfo/ConfigLexicon.php 1 patch
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -20,55 +20,55 @@
 block discarded – undo
20 20
  * Please Add & Manage your Config Keys in that file and keep the Lexicon up to date!
21 21
  */
22 22
 class ConfigLexicon implements ILexicon {
23
-	public const SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES = 'shareapi_allow_federation_on_public_shares';
24
-	public const SHARE_CUSTOM_TOKEN = 'shareapi_allow_custom_tokens';
25
-	public const SHARE_LINK_PASSWORD_DEFAULT = 'shareapi_enable_link_password_by_default';
26
-	public const SHARE_LINK_PASSWORD_ENFORCED = 'shareapi_enforce_links_password';
23
+    public const SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES = 'shareapi_allow_federation_on_public_shares';
24
+    public const SHARE_CUSTOM_TOKEN = 'shareapi_allow_custom_tokens';
25
+    public const SHARE_LINK_PASSWORD_DEFAULT = 'shareapi_enable_link_password_by_default';
26
+    public const SHARE_LINK_PASSWORD_ENFORCED = 'shareapi_enforce_links_password';
27 27
 
28
-	public const USER_LANGUAGE = 'lang';
29
-	public const LASTCRON_TIMESTAMP = 'lastcron';
28
+    public const USER_LANGUAGE = 'lang';
29
+    public const LASTCRON_TIMESTAMP = 'lastcron';
30 30
 
31
-	public function getStrictness(): Strictness {
32
-		return Strictness::IGNORE;
33
-	}
31
+    public function getStrictness(): Strictness {
32
+        return Strictness::IGNORE;
33
+    }
34 34
 
35
-	public function getAppConfigs(): array {
36
-		return [
37
-			new Entry(
38
-				key: self::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES,
39
-				type: ValueType::BOOL,
40
-				defaultRaw: true,
41
-				definition: 'adds share permission to public shares to allow adding them to your Nextcloud (federation)',
42
-				lazy: true,
43
-			),
44
-			new Entry(
45
-				key: self::SHARE_CUSTOM_TOKEN,
46
-				type: ValueType::BOOL,
47
-				defaultRaw: fn (Preset $p): bool => match ($p) {
48
-					Preset::FAMILY, Preset::PRIVATE => true,
49
-					default => false,
50
-				},
51
-				definition: 'Allow users to set custom share link tokens',
52
-				lazy: true,
53
-				note: 'Shares with guessable tokens may be accessed easily. Shares with custom tokens will continue to be accessible after this setting has been disabled.',
54
-			),
55
-			new Entry(self::SHARE_LINK_PASSWORD_DEFAULT, ValueType::BOOL, false, 'Ask for a password when sharing document by default'),
56
-			new Entry(
57
-				key: self::SHARE_LINK_PASSWORD_ENFORCED,
58
-				type: ValueType::BOOL,
59
-				defaultRaw: fn (Preset $p): bool => match ($p) {
60
-					Preset::SCHOOL, Preset::UNIVERSITY, Preset::SHARED, Preset::SMALL, Preset::MEDIUM, Preset::LARGE => true,
61
-					default => false,
62
-				},
63
-				definition: 'Enforce password protection when sharing document'
64
-			),
65
-			new Entry(self::LASTCRON_TIMESTAMP, ValueType::INT, 0, 'timestamp of last cron execution'),
66
-		];
67
-	}
35
+    public function getAppConfigs(): array {
36
+        return [
37
+            new Entry(
38
+                key: self::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES,
39
+                type: ValueType::BOOL,
40
+                defaultRaw: true,
41
+                definition: 'adds share permission to public shares to allow adding them to your Nextcloud (federation)',
42
+                lazy: true,
43
+            ),
44
+            new Entry(
45
+                key: self::SHARE_CUSTOM_TOKEN,
46
+                type: ValueType::BOOL,
47
+                defaultRaw: fn (Preset $p): bool => match ($p) {
48
+                    Preset::FAMILY, Preset::PRIVATE => true,
49
+                    default => false,
50
+                },
51
+                definition: 'Allow users to set custom share link tokens',
52
+                lazy: true,
53
+                note: 'Shares with guessable tokens may be accessed easily. Shares with custom tokens will continue to be accessible after this setting has been disabled.',
54
+            ),
55
+            new Entry(self::SHARE_LINK_PASSWORD_DEFAULT, ValueType::BOOL, false, 'Ask for a password when sharing document by default'),
56
+            new Entry(
57
+                key: self::SHARE_LINK_PASSWORD_ENFORCED,
58
+                type: ValueType::BOOL,
59
+                defaultRaw: fn (Preset $p): bool => match ($p) {
60
+                    Preset::SCHOOL, Preset::UNIVERSITY, Preset::SHARED, Preset::SMALL, Preset::MEDIUM, Preset::LARGE => true,
61
+                    default => false,
62
+                },
63
+                definition: 'Enforce password protection when sharing document'
64
+            ),
65
+            new Entry(self::LASTCRON_TIMESTAMP, ValueType::INT, 0, 'timestamp of last cron execution'),
66
+        ];
67
+    }
68 68
 
69
-	public function getUserConfigs(): array {
70
-		return [
71
-			new Entry(self::USER_LANGUAGE, ValueType::STRING, null, 'language'),
72
-		];
73
-	}
69
+    public function getUserConfigs(): array {
70
+        return [
71
+            new Entry(self::USER_LANGUAGE, ValueType::STRING, null, 'language'),
72
+        ];
73
+    }
74 74
 }
Please login to merge, or discard this patch.
core/Controller/OCJSController.php 1 patch
Indentation   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -33,57 +33,57 @@
 block discarded – undo
33 33
 
34 34
 #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
35 35
 class OCJSController extends Controller {
36
-	private JSConfigHelper $helper;
36
+    private JSConfigHelper $helper;
37 37
 
38
-	public function __construct(
39
-		string $appName,
40
-		IRequest $request,
41
-		IFactory $l10nFactory,
42
-		Defaults $defaults,
43
-		IAppManager $appManager,
44
-		ISession $session,
45
-		IUserSession $userSession,
46
-		IConfig $config,
47
-		IAppConfig $appConfig,
48
-		IGroupManager $groupManager,
49
-		IniGetWrapper $iniWrapper,
50
-		IURLGenerator $urlGenerator,
51
-		CapabilitiesManager $capabilitiesManager,
52
-		IInitialStateService $initialStateService,
53
-		IProvider $tokenProvider,
54
-		FilenameValidator $filenameValidator,
55
-		ServerVersion $serverVersion,
56
-	) {
57
-		parent::__construct($appName, $request);
38
+    public function __construct(
39
+        string $appName,
40
+        IRequest $request,
41
+        IFactory $l10nFactory,
42
+        Defaults $defaults,
43
+        IAppManager $appManager,
44
+        ISession $session,
45
+        IUserSession $userSession,
46
+        IConfig $config,
47
+        IAppConfig $appConfig,
48
+        IGroupManager $groupManager,
49
+        IniGetWrapper $iniWrapper,
50
+        IURLGenerator $urlGenerator,
51
+        CapabilitiesManager $capabilitiesManager,
52
+        IInitialStateService $initialStateService,
53
+        IProvider $tokenProvider,
54
+        FilenameValidator $filenameValidator,
55
+        ServerVersion $serverVersion,
56
+    ) {
57
+        parent::__construct($appName, $request);
58 58
 
59
-		$this->helper = new JSConfigHelper(
60
-			$serverVersion,
61
-			$l10nFactory->get('lib'),
62
-			$defaults,
63
-			$appManager,
64
-			$session,
65
-			$userSession->getUser(),
66
-			$config,
67
-			$appConfig,
68
-			$groupManager,
69
-			$iniWrapper,
70
-			$urlGenerator,
71
-			$capabilitiesManager,
72
-			$initialStateService,
73
-			$tokenProvider,
74
-			$filenameValidator,
75
-		);
76
-	}
59
+        $this->helper = new JSConfigHelper(
60
+            $serverVersion,
61
+            $l10nFactory->get('lib'),
62
+            $defaults,
63
+            $appManager,
64
+            $session,
65
+            $userSession->getUser(),
66
+            $config,
67
+            $appConfig,
68
+            $groupManager,
69
+            $iniWrapper,
70
+            $urlGenerator,
71
+            $capabilitiesManager,
72
+            $initialStateService,
73
+            $tokenProvider,
74
+            $filenameValidator,
75
+        );
76
+    }
77 77
 
78
-	/**
79
-	 * @NoTwoFactorRequired
80
-	 */
81
-	#[PublicPage]
82
-	#[NoCSRFRequired]
83
-	#[FrontpageRoute(verb: 'GET', url: '/core/js/oc.js')]
84
-	public function getConfig(): DataDisplayResponse {
85
-		$data = $this->helper->getConfig();
78
+    /**
79
+     * @NoTwoFactorRequired
80
+     */
81
+    #[PublicPage]
82
+    #[NoCSRFRequired]
83
+    #[FrontpageRoute(verb: 'GET', url: '/core/js/oc.js')]
84
+    public function getConfig(): DataDisplayResponse {
85
+        $data = $this->helper->getConfig();
86 86
 
87
-		return new DataDisplayResponse($data, Http::STATUS_OK, ['Content-type' => 'text/javascript']);
88
-	}
87
+        return new DataDisplayResponse($data, Http::STATUS_OK, ['Content-type' => 'text/javascript']);
88
+    }
89 89
 }
Please login to merge, or discard this patch.
apps/settings/lib/Settings/Admin/Sharing.php 2 patches
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -20,106 +20,106 @@
 block discarded – undo
20 20
 use OCP\Util;
21 21
 
22 22
 class Sharing implements IDelegatedSettings {
23
-	public function __construct(
24
-		private IConfig $config,
25
-		private IAppConfig $appConfig,
26
-		private IL10N $l,
27
-		private IManager $shareManager,
28
-		private IAppManager $appManager,
29
-		private IURLGenerator $urlGenerator,
30
-		private IInitialState $initialState,
31
-		private string $appName,
32
-	) {
33
-	}
23
+    public function __construct(
24
+        private IConfig $config,
25
+        private IAppConfig $appConfig,
26
+        private IL10N $l,
27
+        private IManager $shareManager,
28
+        private IAppManager $appManager,
29
+        private IURLGenerator $urlGenerator,
30
+        private IInitialState $initialState,
31
+        private string $appName,
32
+    ) {
33
+    }
34 34
 
35
-	/**
36
-	 * @return TemplateResponse
37
-	 */
38
-	public function getForm() {
39
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
40
-		$linksExcludedGroups = $this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '');
41
-		$excludedPasswordGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
42
-		$onlyShareWithGroupMembersExcludeGroupList = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
35
+    /**
36
+     * @return TemplateResponse
37
+     */
38
+    public function getForm() {
39
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
40
+        $linksExcludedGroups = $this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '');
41
+        $excludedPasswordGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
42
+        $onlyShareWithGroupMembersExcludeGroupList = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
43 43
 
44
-		$parameters = [
45
-			// Built-In Sharing
46
-			'enabled' => $this->getHumanBooleanConfig('core', 'shareapi_enabled', true),
47
-			'allowGroupSharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_group_sharing', true),
48
-			'allowLinks' => $this->getHumanBooleanConfig('core', 'shareapi_allow_links', true),
49
-			'allowLinksExcludeGroups' => json_decode($linksExcludedGroups, true) ?? [],
50
-			'allowPublicUpload' => $this->getHumanBooleanConfig('core', 'shareapi_allow_public_upload', true),
51
-			'allowResharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_resharing', true),
52
-			'allowShareDialogUserEnumeration' => $this->getHumanBooleanConfig('core', 'shareapi_allow_share_dialog_user_enumeration', true),
53
-			'allowFederationOnPublicShares' => $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES),
54
-			'restrictUserEnumerationToGroup' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_group'),
55
-			'restrictUserEnumerationToPhone' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_phone'),
56
-			'restrictUserEnumerationFullMatch' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match', true),
57
-			'restrictUserEnumerationFullMatchUserId' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_userid', true),
58
-			'restrictUserEnumerationFullMatchEmail' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_email', true),
59
-			'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn'),
60
-			'enforceLinksPassword' => Util::isPublicLinkPasswordRequired(false),
61
-			'enforceLinksPasswordExcludedGroups' => json_decode($excludedPasswordGroups) ?? [],
62
-			'enforceLinksPasswordExcludedGroupsEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false),
63
-			'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(),
64
-			'onlyShareWithGroupMembersExcludeGroupList' => json_decode($onlyShareWithGroupMembersExcludeGroupList) ?? [],
65
-			'defaultExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_expire_date'),
66
-			'expireAfterNDays' => $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'),
67
-			'enforceExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_expire_date'),
68
-			'excludeGroups' => $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no'),
69
-			'excludeGroupsList' => json_decode($excludedGroups, true) ?? [],
70
-			'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext'),
71
-			'enableLinkPasswordByDefault' => $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT),
72
-			'defaultPermissions' => (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL),
73
-			'defaultInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_internal_expire_date'),
74
-			'internalExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'),
75
-			'enforceInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_internal_expire_date'),
76
-			'defaultRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_remote_expire_date'),
77
-			'remoteExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7'),
78
-			'enforceRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_remote_expire_date'),
79
-			'allowCustomTokens' => $this->shareManager->allowCustomTokens(),
80
-			'allowViewWithoutDownload' => $this->shareManager->allowViewWithoutDownload(),
81
-		];
44
+        $parameters = [
45
+            // Built-In Sharing
46
+            'enabled' => $this->getHumanBooleanConfig('core', 'shareapi_enabled', true),
47
+            'allowGroupSharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_group_sharing', true),
48
+            'allowLinks' => $this->getHumanBooleanConfig('core', 'shareapi_allow_links', true),
49
+            'allowLinksExcludeGroups' => json_decode($linksExcludedGroups, true) ?? [],
50
+            'allowPublicUpload' => $this->getHumanBooleanConfig('core', 'shareapi_allow_public_upload', true),
51
+            'allowResharing' => $this->getHumanBooleanConfig('core', 'shareapi_allow_resharing', true),
52
+            'allowShareDialogUserEnumeration' => $this->getHumanBooleanConfig('core', 'shareapi_allow_share_dialog_user_enumeration', true),
53
+            'allowFederationOnPublicShares' => $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES),
54
+            'restrictUserEnumerationToGroup' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_group'),
55
+            'restrictUserEnumerationToPhone' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_phone'),
56
+            'restrictUserEnumerationFullMatch' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match', true),
57
+            'restrictUserEnumerationFullMatchUserId' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_userid', true),
58
+            'restrictUserEnumerationFullMatchEmail' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_email', true),
59
+            'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn'),
60
+            'enforceLinksPassword' => Util::isPublicLinkPasswordRequired(false),
61
+            'enforceLinksPasswordExcludedGroups' => json_decode($excludedPasswordGroups) ?? [],
62
+            'enforceLinksPasswordExcludedGroupsEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false),
63
+            'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(),
64
+            'onlyShareWithGroupMembersExcludeGroupList' => json_decode($onlyShareWithGroupMembersExcludeGroupList) ?? [],
65
+            'defaultExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_expire_date'),
66
+            'expireAfterNDays' => $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'),
67
+            'enforceExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_expire_date'),
68
+            'excludeGroups' => $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no'),
69
+            'excludeGroupsList' => json_decode($excludedGroups, true) ?? [],
70
+            'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext'),
71
+            'enableLinkPasswordByDefault' => $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT),
72
+            'defaultPermissions' => (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL),
73
+            'defaultInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_internal_expire_date'),
74
+            'internalExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'),
75
+            'enforceInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_internal_expire_date'),
76
+            'defaultRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_remote_expire_date'),
77
+            'remoteExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7'),
78
+            'enforceRemoteExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_remote_expire_date'),
79
+            'allowCustomTokens' => $this->shareManager->allowCustomTokens(),
80
+            'allowViewWithoutDownload' => $this->shareManager->allowViewWithoutDownload(),
81
+        ];
82 82
 
83
-		$this->initialState->provideInitialState('sharingAppEnabled', $this->appManager->isEnabledForUser('files_sharing'));
84
-		$this->initialState->provideInitialState('sharingDocumentation', $this->urlGenerator->linkToDocs('admin-sharing'));
85
-		$this->initialState->provideInitialState('sharingSettings', $parameters);
83
+        $this->initialState->provideInitialState('sharingAppEnabled', $this->appManager->isEnabledForUser('files_sharing'));
84
+        $this->initialState->provideInitialState('sharingDocumentation', $this->urlGenerator->linkToDocs('admin-sharing'));
85
+        $this->initialState->provideInitialState('sharingSettings', $parameters);
86 86
 
87
-		Util::addScript($this->appName, 'vue-settings-admin-sharing');
88
-		return new TemplateResponse($this->appName, 'settings/admin/sharing', [], '');
89
-	}
87
+        Util::addScript($this->appName, 'vue-settings-admin-sharing');
88
+        return new TemplateResponse($this->appName, 'settings/admin/sharing', [], '');
89
+    }
90 90
 
91
-	/**
92
-	 * Helper function to retrive boolean values from human readable strings ('yes' / 'no')
93
-	 */
94
-	private function getHumanBooleanConfig(string $app, string $key, bool $default = false): bool {
95
-		return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') === 'yes';
96
-	}
91
+    /**
92
+     * Helper function to retrive boolean values from human readable strings ('yes' / 'no')
93
+     */
94
+    private function getHumanBooleanConfig(string $app, string $key, bool $default = false): bool {
95
+        return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') === 'yes';
96
+    }
97 97
 
98
-	/**
99
-	 * @return string the section ID, e.g. 'sharing'
100
-	 */
101
-	public function getSection() {
102
-		return 'sharing';
103
-	}
98
+    /**
99
+     * @return string the section ID, e.g. 'sharing'
100
+     */
101
+    public function getSection() {
102
+        return 'sharing';
103
+    }
104 104
 
105
-	/**
106
-	 * @return int whether the form should be rather on the top or bottom of
107
-	 *             the admin section. The forms are arranged in ascending order of the
108
-	 *             priority values. It is required to return a value between 0 and 100.
109
-	 *
110
-	 * E.g.: 70
111
-	 */
112
-	public function getPriority() {
113
-		return 0;
114
-	}
105
+    /**
106
+     * @return int whether the form should be rather on the top or bottom of
107
+     *             the admin section. The forms are arranged in ascending order of the
108
+     *             priority values. It is required to return a value between 0 and 100.
109
+     *
110
+     * E.g.: 70
111
+     */
112
+    public function getPriority() {
113
+        return 0;
114
+    }
115 115
 
116
-	public function getAuthorizedAppConfig(): array {
117
-		return [
118
-			'core' => ['/shareapi_.*/'],
119
-		];
120
-	}
116
+    public function getAuthorizedAppConfig(): array {
117
+        return [
118
+            'core' => ['/shareapi_.*/'],
119
+        ];
120
+    }
121 121
 
122
-	public function getName(): ?string {
123
-		return null;
124
-	}
122
+    public function getName(): ?string {
123
+        return null;
124
+    }
125 125
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -69,7 +69,7 @@
 block discarded – undo
69 69
 			'excludeGroupsList' => json_decode($excludedGroups, true) ?? [],
70 70
 			'publicShareDisclaimerText' => $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext'),
71 71
 			'enableLinkPasswordByDefault' => $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT),
72
-			'defaultPermissions' => (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL),
72
+			'defaultPermissions' => (int) $this->config->getAppValue('core', 'shareapi_default_permissions', (string) Constants::PERMISSION_ALL),
73 73
 			'defaultInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_default_internal_expire_date'),
74 74
 			'internalExpireAfterNDays' => $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'),
75 75
 			'enforceInternalExpireDate' => $this->getHumanBooleanConfig('core', 'shareapi_enforce_internal_expire_date'),
Please login to merge, or discard this patch.
apps/settings/tests/Settings/Admin/SharingTest.php 2 patches
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -20,245 +20,245 @@
 block discarded – undo
20 20
 use Test\TestCase;
21 21
 
22 22
 class SharingTest extends TestCase {
23
-	private Sharing $admin;
23
+    private Sharing $admin;
24 24
 
25
-	private IConfig&MockObject $config;
26
-	private IAppConfig&MockObject $appConfig;
27
-	private IL10N&MockObject $l10n;
28
-	private IManager&MockObject $shareManager;
29
-	private IAppManager&MockObject $appManager;
30
-	private IURLGenerator&MockObject $urlGenerator;
31
-	private IInitialState&MockObject $initialState;
25
+    private IConfig&MockObject $config;
26
+    private IAppConfig&MockObject $appConfig;
27
+    private IL10N&MockObject $l10n;
28
+    private IManager&MockObject $shareManager;
29
+    private IAppManager&MockObject $appManager;
30
+    private IURLGenerator&MockObject $urlGenerator;
31
+    private IInitialState&MockObject $initialState;
32 32
 
33
-	protected function setUp(): void {
34
-		parent::setUp();
35
-		$this->config = $this->createMock(IConfig::class);
36
-		$this->appConfig = $this->createMock(IAppConfig::class);
37
-		$this->l10n = $this->createMock(IL10N::class);
33
+    protected function setUp(): void {
34
+        parent::setUp();
35
+        $this->config = $this->createMock(IConfig::class);
36
+        $this->appConfig = $this->createMock(IAppConfig::class);
37
+        $this->l10n = $this->createMock(IL10N::class);
38 38
 
39
-		$this->shareManager = $this->createMock(IManager::class);
40
-		$this->appManager = $this->createMock(IAppManager::class);
41
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
42
-		$this->initialState = $this->createMock(IInitialState::class);
39
+        $this->shareManager = $this->createMock(IManager::class);
40
+        $this->appManager = $this->createMock(IAppManager::class);
41
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
42
+        $this->initialState = $this->createMock(IInitialState::class);
43 43
 
44
-		$this->admin = new Sharing(
45
-			$this->config,
46
-			$this->appConfig,
47
-			$this->l10n,
48
-			$this->shareManager,
49
-			$this->appManager,
50
-			$this->urlGenerator,
51
-			$this->initialState,
52
-			'settings',
53
-		);
54
-	}
44
+        $this->admin = new Sharing(
45
+            $this->config,
46
+            $this->appConfig,
47
+            $this->l10n,
48
+            $this->shareManager,
49
+            $this->appManager,
50
+            $this->urlGenerator,
51
+            $this->initialState,
52
+            'settings',
53
+        );
54
+    }
55 55
 
56
-	public function testGetFormWithoutExcludedGroups(): void {
57
-		$this->appConfig
58
-			->method('getValueBool')
59
-			->willReturnMap([
60
-				['core', 'shareapi_allow_federation_on_public_shares', true],
61
-				['core', 'shareapi_enable_link_password_by_default', true],
62
-			]);
56
+    public function testGetFormWithoutExcludedGroups(): void {
57
+        $this->appConfig
58
+            ->method('getValueBool')
59
+            ->willReturnMap([
60
+                ['core', 'shareapi_allow_federation_on_public_shares', true],
61
+                ['core', 'shareapi_enable_link_password_by_default', true],
62
+            ]);
63 63
 
64
-		$this->config
65
-			->method('getAppValue')
66
-			->willReturnMap([
67
-				['core', 'shareapi_exclude_groups_list', '', ''],
68
-				['core', 'shareapi_allow_links_exclude_groups', '', ''],
69
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
70
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
71
-				['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
72
-				['core', 'shareapi_allow_resharing', 'yes', 'yes'],
73
-				['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
74
-				['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
75
-				['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
76
-				['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
77
-				['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'],
78
-				['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'],
79
-				['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'],
80
-				['core', 'shareapi_enabled', 'yes', 'yes'],
81
-				['core', 'shareapi_default_expire_date', 'no', 'no'],
82
-				['core', 'shareapi_expire_after_n_days', '7', '7'],
83
-				['core', 'shareapi_enforce_expire_date', 'no', 'no'],
84
-				['core', 'shareapi_exclude_groups', 'no', 'no'],
85
-				['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
86
-				['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
87
-				['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
88
-				['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
89
-				['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
90
-				['core', 'shareapi_default_remote_expire_date', 'no', 'no'],
91
-				['core', 'shareapi_remote_expire_after_n_days', '7', '7'],
92
-				['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'],
93
-				['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
94
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
95
-			]);
96
-		$this->shareManager->method('shareWithGroupMembersOnly')
97
-			->willReturn(false);
64
+        $this->config
65
+            ->method('getAppValue')
66
+            ->willReturnMap([
67
+                ['core', 'shareapi_exclude_groups_list', '', ''],
68
+                ['core', 'shareapi_allow_links_exclude_groups', '', ''],
69
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
70
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
71
+                ['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
72
+                ['core', 'shareapi_allow_resharing', 'yes', 'yes'],
73
+                ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
74
+                ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
75
+                ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
76
+                ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
77
+                ['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'],
78
+                ['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'],
79
+                ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'],
80
+                ['core', 'shareapi_enabled', 'yes', 'yes'],
81
+                ['core', 'shareapi_default_expire_date', 'no', 'no'],
82
+                ['core', 'shareapi_expire_after_n_days', '7', '7'],
83
+                ['core', 'shareapi_enforce_expire_date', 'no', 'no'],
84
+                ['core', 'shareapi_exclude_groups', 'no', 'no'],
85
+                ['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
86
+                ['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
87
+                ['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
88
+                ['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
89
+                ['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
90
+                ['core', 'shareapi_default_remote_expire_date', 'no', 'no'],
91
+                ['core', 'shareapi_remote_expire_after_n_days', '7', '7'],
92
+                ['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'],
93
+                ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
94
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
95
+            ]);
96
+        $this->shareManager->method('shareWithGroupMembersOnly')
97
+            ->willReturn(false);
98 98
 
99
-		$this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(false);
99
+        $this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(false);
100 100
 
101
-		$initialStateCalls = [];
102
-		$this->initialState
103
-			->expects($this->exactly(3))
104
-			->method('provideInitialState')
105
-			->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
106
-				$initialStateCalls[$key] = func_get_args();
107
-			});
101
+        $initialStateCalls = [];
102
+        $this->initialState
103
+            ->expects($this->exactly(3))
104
+            ->method('provideInitialState')
105
+            ->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
106
+                $initialStateCalls[$key] = func_get_args();
107
+            });
108 108
 
109
-		$expectedInitialStateCalls = [
110
-			'sharingAppEnabled' => false,
111
-			'sharingDocumentation' => '',
112
-			'sharingSettings' => [
113
-				'allowGroupSharing' => true,
114
-				'allowLinks' => true,
115
-				'allowPublicUpload' => true,
116
-				'allowResharing' => true,
117
-				'allowShareDialogUserEnumeration' => true,
118
-				'allowFederationOnPublicShares' => true,
119
-				'restrictUserEnumerationToGroup' => false,
120
-				'restrictUserEnumerationToPhone' => false,
121
-				'restrictUserEnumerationFullMatch' => true,
122
-				'restrictUserEnumerationFullMatchUserId' => true,
123
-				'restrictUserEnumerationFullMatchEmail' => true,
124
-				'restrictUserEnumerationFullMatchIgnoreSecondDN' => false,
125
-				'enforceLinksPassword' => false,
126
-				'onlyShareWithGroupMembers' => false,
127
-				'enabled' => true,
128
-				'defaultExpireDate' => false,
129
-				'expireAfterNDays' => '7',
130
-				'enforceExpireDate' => false,
131
-				'excludeGroups' => 'no',
132
-				'excludeGroupsList' => [],
133
-				'publicShareDisclaimerText' => 'Lorem ipsum',
134
-				'enableLinkPasswordByDefault' => true,
135
-				'defaultPermissions' => Constants::PERMISSION_ALL,
136
-				'defaultInternalExpireDate' => false,
137
-				'internalExpireAfterNDays' => '7',
138
-				'enforceInternalExpireDate' => false,
139
-				'defaultRemoteExpireDate' => false,
140
-				'remoteExpireAfterNDays' => '7',
141
-				'enforceRemoteExpireDate' => false,
142
-				'allowLinksExcludeGroups' => [],
143
-				'onlyShareWithGroupMembersExcludeGroupList' => [],
144
-				'enforceLinksPasswordExcludedGroups' => [],
145
-				'enforceLinksPasswordExcludedGroupsEnabled' => false,
146
-			]
147
-		];
109
+        $expectedInitialStateCalls = [
110
+            'sharingAppEnabled' => false,
111
+            'sharingDocumentation' => '',
112
+            'sharingSettings' => [
113
+                'allowGroupSharing' => true,
114
+                'allowLinks' => true,
115
+                'allowPublicUpload' => true,
116
+                'allowResharing' => true,
117
+                'allowShareDialogUserEnumeration' => true,
118
+                'allowFederationOnPublicShares' => true,
119
+                'restrictUserEnumerationToGroup' => false,
120
+                'restrictUserEnumerationToPhone' => false,
121
+                'restrictUserEnumerationFullMatch' => true,
122
+                'restrictUserEnumerationFullMatchUserId' => true,
123
+                'restrictUserEnumerationFullMatchEmail' => true,
124
+                'restrictUserEnumerationFullMatchIgnoreSecondDN' => false,
125
+                'enforceLinksPassword' => false,
126
+                'onlyShareWithGroupMembers' => false,
127
+                'enabled' => true,
128
+                'defaultExpireDate' => false,
129
+                'expireAfterNDays' => '7',
130
+                'enforceExpireDate' => false,
131
+                'excludeGroups' => 'no',
132
+                'excludeGroupsList' => [],
133
+                'publicShareDisclaimerText' => 'Lorem ipsum',
134
+                'enableLinkPasswordByDefault' => true,
135
+                'defaultPermissions' => Constants::PERMISSION_ALL,
136
+                'defaultInternalExpireDate' => false,
137
+                'internalExpireAfterNDays' => '7',
138
+                'enforceInternalExpireDate' => false,
139
+                'defaultRemoteExpireDate' => false,
140
+                'remoteExpireAfterNDays' => '7',
141
+                'enforceRemoteExpireDate' => false,
142
+                'allowLinksExcludeGroups' => [],
143
+                'onlyShareWithGroupMembersExcludeGroupList' => [],
144
+                'enforceLinksPasswordExcludedGroups' => [],
145
+                'enforceLinksPasswordExcludedGroupsEnabled' => false,
146
+            ]
147
+        ];
148 148
 
149
-		$expected = new TemplateResponse(
150
-			'settings',
151
-			'settings/admin/sharing',
152
-			[],
153
-			''
154
-		);
149
+        $expected = new TemplateResponse(
150
+            'settings',
151
+            'settings/admin/sharing',
152
+            [],
153
+            ''
154
+        );
155 155
 
156
-		$this->assertEquals($expected, $this->admin->getForm());
157
-		$this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match');
158
-	}
156
+        $this->assertEquals($expected, $this->admin->getForm());
157
+        $this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match');
158
+    }
159 159
 
160
-	public function testGetFormWithExcludedGroups(): void {
161
-		$this->config
162
-			->method('getAppValue')
163
-			->willReturnMap([
164
-				['core', 'shareapi_exclude_groups_list', '', '["NoSharers","OtherNoSharers"]'],
165
-				['core', 'shareapi_allow_links_exclude_groups', '', ''],
166
-				['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
167
-				['core', 'shareapi_allow_links', 'yes', 'yes'],
168
-				['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
169
-				['core', 'shareapi_allow_resharing', 'yes', 'yes'],
170
-				['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
171
-				['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
172
-				['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
173
-				['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
174
-				['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'],
175
-				['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'],
176
-				['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'],
177
-				['core', 'shareapi_enabled', 'yes', 'yes'],
178
-				['core', 'shareapi_default_expire_date', 'no', 'no'],
179
-				['core', 'shareapi_expire_after_n_days', '7', '7'],
180
-				['core', 'shareapi_enforce_expire_date', 'no', 'no'],
181
-				['core', 'shareapi_exclude_groups', 'no', 'yes'],
182
-				['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
183
-				['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'],
184
-				['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
185
-				['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
186
-				['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
187
-				['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
188
-				['core', 'shareapi_default_remote_expire_date', 'no', 'no'],
189
-				['core', 'shareapi_remote_expire_after_n_days', '7', '7'],
190
-				['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'],
191
-				['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
192
-				['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
193
-			]);
194
-		$this->shareManager->method('shareWithGroupMembersOnly')
195
-			->willReturn(false);
160
+    public function testGetFormWithExcludedGroups(): void {
161
+        $this->config
162
+            ->method('getAppValue')
163
+            ->willReturnMap([
164
+                ['core', 'shareapi_exclude_groups_list', '', '["NoSharers","OtherNoSharers"]'],
165
+                ['core', 'shareapi_allow_links_exclude_groups', '', ''],
166
+                ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
167
+                ['core', 'shareapi_allow_links', 'yes', 'yes'],
168
+                ['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
169
+                ['core', 'shareapi_allow_resharing', 'yes', 'yes'],
170
+                ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
171
+                ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
172
+                ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
173
+                ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
174
+                ['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'],
175
+                ['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'],
176
+                ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'],
177
+                ['core', 'shareapi_enabled', 'yes', 'yes'],
178
+                ['core', 'shareapi_default_expire_date', 'no', 'no'],
179
+                ['core', 'shareapi_expire_after_n_days', '7', '7'],
180
+                ['core', 'shareapi_enforce_expire_date', 'no', 'no'],
181
+                ['core', 'shareapi_exclude_groups', 'no', 'yes'],
182
+                ['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
183
+                ['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'],
184
+                ['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
185
+                ['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
186
+                ['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
187
+                ['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
188
+                ['core', 'shareapi_default_remote_expire_date', 'no', 'no'],
189
+                ['core', 'shareapi_remote_expire_after_n_days', '7', '7'],
190
+                ['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'],
191
+                ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
192
+                ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
193
+            ]);
194
+        $this->shareManager->method('shareWithGroupMembersOnly')
195
+            ->willReturn(false);
196 196
 
197
-		$this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(true);
197
+        $this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(true);
198 198
 
199
-		$initialStateCalls = [];
200
-		$this->initialState
201
-			->expects($this->exactly(3))
202
-			->method('provideInitialState')
203
-			->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
204
-				$initialStateCalls[$key] = func_get_args();
205
-			});
199
+        $initialStateCalls = [];
200
+        $this->initialState
201
+            ->expects($this->exactly(3))
202
+            ->method('provideInitialState')
203
+            ->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
204
+                $initialStateCalls[$key] = func_get_args();
205
+            });
206 206
 
207
-		$expectedInitialStateCalls = [
208
-			'sharingAppEnabled' => true,
209
-			'sharingDocumentation' => '',
210
-			'sharingSettings' => [
211
-				'allowGroupSharing' => true,
212
-				'allowLinks' => true,
213
-				'allowPublicUpload' => true,
214
-				'allowResharing' => true,
215
-				'allowShareDialogUserEnumeration' => true,
216
-				'restrictUserEnumerationToGroup' => false,
217
-				'restrictUserEnumerationToPhone' => false,
218
-				'restrictUserEnumerationFullMatch' => true,
219
-				'restrictUserEnumerationFullMatchUserId' => true,
220
-				'restrictUserEnumerationFullMatchEmail' => true,
221
-				'restrictUserEnumerationFullMatchIgnoreSecondDN' => false,
222
-				'enforceLinksPassword' => false,
223
-				'onlyShareWithGroupMembers' => false,
224
-				'enabled' => true,
225
-				'defaultExpireDate' => false,
226
-				'expireAfterNDays' => '7',
227
-				'enforceExpireDate' => false,
228
-				'excludeGroups' => 'yes',
229
-				'excludeGroupsList' => ['NoSharers','OtherNoSharers'],
230
-				'publicShareDisclaimerText' => 'Lorem ipsum',
231
-				'enableLinkPasswordByDefault' => true,
232
-				'defaultPermissions' => Constants::PERMISSION_ALL,
233
-				'defaultInternalExpireDate' => false,
234
-				'internalExpireAfterNDays' => '7',
235
-				'enforceInternalExpireDate' => false,
236
-				'defaultRemoteExpireDate' => false,
237
-				'remoteExpireAfterNDays' => '7',
238
-				'enforceRemoteExpireDate' => false,
239
-				'allowLinksExcludeGroups' => [],
240
-				'onlyShareWithGroupMembersExcludeGroupList' => [],
241
-				'enforceLinksPasswordExcludedGroups' => [],
242
-				'enforceLinksPasswordExcludedGroupsEnabled' => false,
243
-			],
244
-		];
207
+        $expectedInitialStateCalls = [
208
+            'sharingAppEnabled' => true,
209
+            'sharingDocumentation' => '',
210
+            'sharingSettings' => [
211
+                'allowGroupSharing' => true,
212
+                'allowLinks' => true,
213
+                'allowPublicUpload' => true,
214
+                'allowResharing' => true,
215
+                'allowShareDialogUserEnumeration' => true,
216
+                'restrictUserEnumerationToGroup' => false,
217
+                'restrictUserEnumerationToPhone' => false,
218
+                'restrictUserEnumerationFullMatch' => true,
219
+                'restrictUserEnumerationFullMatchUserId' => true,
220
+                'restrictUserEnumerationFullMatchEmail' => true,
221
+                'restrictUserEnumerationFullMatchIgnoreSecondDN' => false,
222
+                'enforceLinksPassword' => false,
223
+                'onlyShareWithGroupMembers' => false,
224
+                'enabled' => true,
225
+                'defaultExpireDate' => false,
226
+                'expireAfterNDays' => '7',
227
+                'enforceExpireDate' => false,
228
+                'excludeGroups' => 'yes',
229
+                'excludeGroupsList' => ['NoSharers','OtherNoSharers'],
230
+                'publicShareDisclaimerText' => 'Lorem ipsum',
231
+                'enableLinkPasswordByDefault' => true,
232
+                'defaultPermissions' => Constants::PERMISSION_ALL,
233
+                'defaultInternalExpireDate' => false,
234
+                'internalExpireAfterNDays' => '7',
235
+                'enforceInternalExpireDate' => false,
236
+                'defaultRemoteExpireDate' => false,
237
+                'remoteExpireAfterNDays' => '7',
238
+                'enforceRemoteExpireDate' => false,
239
+                'allowLinksExcludeGroups' => [],
240
+                'onlyShareWithGroupMembersExcludeGroupList' => [],
241
+                'enforceLinksPasswordExcludedGroups' => [],
242
+                'enforceLinksPasswordExcludedGroupsEnabled' => false,
243
+            ],
244
+        ];
245 245
 
246
-		$expected = new TemplateResponse(
247
-			'settings',
248
-			'settings/admin/sharing',
249
-			[],
250
-			''
251
-		);
246
+        $expected = new TemplateResponse(
247
+            'settings',
248
+            'settings/admin/sharing',
249
+            [],
250
+            ''
251
+        );
252 252
 
253
-		$this->assertEquals($expected, $this->admin->getForm());
254
-		$this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match');
255
-	}
253
+        $this->assertEquals($expected, $this->admin->getForm());
254
+        $this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match');
255
+    }
256 256
 
257
-	public function testGetSection(): void {
258
-		$this->assertSame('sharing', $this->admin->getSection());
259
-	}
257
+    public function testGetSection(): void {
258
+        $this->assertSame('sharing', $this->admin->getSection());
259
+    }
260 260
 
261
-	public function testGetPriority(): void {
262
-		$this->assertSame(0, $this->admin->getPriority());
263
-	}
261
+    public function testGetPriority(): void {
262
+        $this->assertSame(0, $this->admin->getPriority());
263
+    }
264 264
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
 				['core', 'shareapi_enforce_expire_date', 'no', 'no'],
84 84
 				['core', 'shareapi_exclude_groups', 'no', 'no'],
85 85
 				['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
86
-				['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
86
+				['core', 'shareapi_default_permissions', (string) Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
87 87
 				['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
88 88
 				['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
89 89
 				['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 		$this->initialState
103 103
 			->expects($this->exactly(3))
104 104
 			->method('provideInitialState')
105
-			->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
105
+			->willReturnCallback(function(string $key) use (&$initialStateCalls): void {
106 106
 				$initialStateCalls[$key] = func_get_args();
107 107
 			});
108 108
 
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
 				['core', 'shareapi_exclude_groups', 'no', 'yes'],
182 182
 				['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'],
183 183
 				['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'],
184
-				['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
184
+				['core', 'shareapi_default_permissions', (string) Constants::PERMISSION_ALL, Constants::PERMISSION_ALL],
185 185
 				['core', 'shareapi_default_internal_expire_date', 'no', 'no'],
186 186
 				['core', 'shareapi_internal_expire_after_n_days', '7', '7'],
187 187
 				['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'],
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
 		$this->initialState
201 201
 			->expects($this->exactly(3))
202 202
 			->method('provideInitialState')
203
-			->willReturnCallback(function (string $key) use (&$initialStateCalls): void {
203
+			->willReturnCallback(function(string $key) use (&$initialStateCalls): void {
204 204
 				$initialStateCalls[$key] = func_get_args();
205 205
 			});
206 206
 
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 				'expireAfterNDays' => '7',
227 227
 				'enforceExpireDate' => false,
228 228
 				'excludeGroups' => 'yes',
229
-				'excludeGroupsList' => ['NoSharers','OtherNoSharers'],
229
+				'excludeGroupsList' => ['NoSharers', 'OtherNoSharers'],
230 230
 				'publicShareDisclaimerText' => 'Lorem ipsum',
231 231
 				'enableLinkPasswordByDefault' => true,
232 232
 				'defaultPermissions' => Constants::PERMISSION_ALL,
Please login to merge, or discard this patch.
apps/files_sharing/lib/Capabilities.php 1 patch
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -21,173 +21,173 @@
 block discarded – undo
21 21
  * @package OCA\Files_Sharing
22 22
  */
23 23
 class Capabilities implements ICapability {
24
-	public function __construct(
25
-		private IConfig $config,
26
-		private readonly IAppConfig $appConfig,
27
-		private IManager $shareManager,
28
-		private IAppManager $appManager,
29
-	) {
30
-	}
31
-
32
-	/**
33
-	 * Return this classes capabilities
34
-	 *
35
-	 * @return array{
36
-	 *     files_sharing: array{
37
-	 *         api_enabled: bool,
38
-	 *         public: array{
39
-	 *             enabled: bool,
40
-	 *             password?: array{
41
-	 *                 enforced: bool,
42
-	 *                 askForOptionalPassword: bool
43
-	 *             },
44
-	 *     		   multiple_links?: bool,
45
-	 *             expire_date?: array{
46
-	 *                 enabled: bool,
47
-	 *                 days?: int,
48
-	 *                 enforced?: bool,
49
-	 *             },
50
-	 *             expire_date_internal?: array{
51
-	 *                 enabled: bool,
52
-	 *                 days?: int,
53
-	 *                 enforced?: bool,
54
-	 *             },
55
-	 *             expire_date_remote?: array{
56
-	 *                 enabled: bool,
57
-	 *                 days?: int,
58
-	 *                 enforced?: bool,
59
-	 *             },
60
-	 *             send_mail?: bool,
61
-	 *             upload?: bool,
62
-	 *             upload_files_drop?: bool,
63
-	 *             custom_tokens?: bool,
64
-	 *         },
65
-	 *         user: array{
66
-	 *             send_mail: bool,
67
-	 *             expire_date?: array{
68
-	 *                 enabled: bool,
69
-	 *             },
70
-	 *         },
71
-	 *         resharing: bool,
72
-	 *         group_sharing?: bool,
73
-	 *         group?: array{
74
-	 *             enabled: bool,
75
-	 *             expire_date?: array{
76
-	 *                 enabled: bool,
77
-	 *             },
78
-	 *         },
79
-	 *         default_permissions?: int,
80
-	 *         federation: array{
81
-	 *             outgoing: bool,
82
-	 *             incoming: bool,
83
-	 *             expire_date: array{
84
-	 *                 enabled: bool,
85
-	 *             },
86
-	 *             expire_date_supported: array{
87
-	 *                 enabled: bool,
88
-	 *             },
89
-	 *         },
90
-	 *         sharee: array{
91
-	 *             query_lookup_default: bool,
92
-	 *             always_show_unique: bool,
93
-	 *         },
94
-	 *	   },
95
-	 * }
96
-	 */
97
-	public function getCapabilities() {
98
-		$res = [];
99
-
100
-		if (!$this->shareManager->shareApiEnabled()) {
101
-			$res['api_enabled'] = false;
102
-			$res['public'] = ['enabled' => false];
103
-			$res['user'] = ['send_mail' => false];
104
-			$res['resharing'] = false;
105
-		} else {
106
-			$res['api_enabled'] = true;
107
-
108
-			$public = [];
109
-			$public['enabled'] = $this->shareManager->shareApiAllowLinks();
110
-			if ($public['enabled']) {
111
-				$public['password'] = [];
112
-				$public['password']['enforced'] = $this->shareManager->shareApiLinkEnforcePassword();
113
-
114
-				if ($public['password']['enforced']) {
115
-					$public['password']['askForOptionalPassword'] = false;
116
-				} else {
117
-					$public['password']['askForOptionalPassword'] = $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT);
118
-				}
119
-
120
-				$public['expire_date'] = [];
121
-				$public['multiple_links'] = true;
122
-				$public['expire_date']['enabled'] = $this->shareManager->shareApiLinkDefaultExpireDate();
123
-				if ($public['expire_date']['enabled']) {
124
-					$public['expire_date']['days'] = $this->shareManager->shareApiLinkDefaultExpireDays();
125
-					$public['expire_date']['enforced'] = $this->shareManager->shareApiLinkDefaultExpireDateEnforced();
126
-				}
127
-
128
-				$public['expire_date_internal'] = [];
129
-				$public['expire_date_internal']['enabled'] = $this->shareManager->shareApiInternalDefaultExpireDate();
130
-				if ($public['expire_date_internal']['enabled']) {
131
-					$public['expire_date_internal']['days'] = $this->shareManager->shareApiInternalDefaultExpireDays();
132
-					$public['expire_date_internal']['enforced'] = $this->shareManager->shareApiInternalDefaultExpireDateEnforced();
133
-				}
134
-
135
-				$public['expire_date_remote'] = [];
136
-				$public['expire_date_remote']['enabled'] = $this->shareManager->shareApiRemoteDefaultExpireDate();
137
-				if ($public['expire_date_remote']['enabled']) {
138
-					$public['expire_date_remote']['days'] = $this->shareManager->shareApiRemoteDefaultExpireDays();
139
-					$public['expire_date_remote']['enforced'] = $this->shareManager->shareApiRemoteDefaultExpireDateEnforced();
140
-				}
141
-
142
-				$public['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no') === 'yes';
143
-				$public['upload'] = $this->shareManager->shareApiLinkAllowPublicUpload();
144
-				$public['upload_files_drop'] = $public['upload'];
145
-				$public['custom_tokens'] = $this->shareManager->allowCustomTokens();
146
-			}
147
-			$res['public'] = $public;
148
-
149
-			$res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes';
150
-
151
-			$res['user']['send_mail'] = false;
152
-			$res['user']['expire_date']['enabled'] = true;
153
-
154
-			// deprecated in favour of 'group', but we need to keep it for now
155
-			// in order to stay compatible with older clients
156
-			$res['group_sharing'] = $this->shareManager->allowGroupSharing();
157
-
158
-			$res['group'] = [];
159
-			$res['group']['enabled'] = $this->shareManager->allowGroupSharing();
160
-			$res['group']['expire_date']['enabled'] = true;
161
-			$res['default_permissions'] = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
162
-		}
163
-
164
-		//Federated sharing
165
-		if ($this->appManager->isEnabledForAnyone('federation')) {
166
-			$res['federation'] = [
167
-				'outgoing' => $this->shareManager->outgoingServer2ServerSharesAllowed(),
168
-				'incoming' => $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'yes',
169
-				// old bogus one, expire_date was not working before, keeping for compatibility
170
-				'expire_date' => ['enabled' => true],
171
-				// the real deal, signifies that expiration date can be set on federated shares
172
-				'expire_date_supported' => ['enabled' => true],
173
-			];
174
-		} else {
175
-			$res['federation'] = [
176
-				'outgoing' => false,
177
-				'incoming' => false,
178
-				'expire_date' => ['enabled' => false],
179
-				'expire_date_supported' => ['enabled' => false],
180
-			];
181
-		}
182
-
183
-		// Sharee searches
184
-		$res['sharee'] = [
185
-			'query_lookup_default' => $this->config->getSystemValueBool('gs.enabled', false),
186
-			'always_show_unique' => $this->config->getAppValue('files_sharing', 'always_show_unique', 'yes') === 'yes',
187
-		];
188
-
189
-		return [
190
-			'files_sharing' => $res,
191
-		];
192
-	}
24
+    public function __construct(
25
+        private IConfig $config,
26
+        private readonly IAppConfig $appConfig,
27
+        private IManager $shareManager,
28
+        private IAppManager $appManager,
29
+    ) {
30
+    }
31
+
32
+    /**
33
+     * Return this classes capabilities
34
+     *
35
+     * @return array{
36
+     *     files_sharing: array{
37
+     *         api_enabled: bool,
38
+     *         public: array{
39
+     *             enabled: bool,
40
+     *             password?: array{
41
+     *                 enforced: bool,
42
+     *                 askForOptionalPassword: bool
43
+     *             },
44
+     *     		   multiple_links?: bool,
45
+     *             expire_date?: array{
46
+     *                 enabled: bool,
47
+     *                 days?: int,
48
+     *                 enforced?: bool,
49
+     *             },
50
+     *             expire_date_internal?: array{
51
+     *                 enabled: bool,
52
+     *                 days?: int,
53
+     *                 enforced?: bool,
54
+     *             },
55
+     *             expire_date_remote?: array{
56
+     *                 enabled: bool,
57
+     *                 days?: int,
58
+     *                 enforced?: bool,
59
+     *             },
60
+     *             send_mail?: bool,
61
+     *             upload?: bool,
62
+     *             upload_files_drop?: bool,
63
+     *             custom_tokens?: bool,
64
+     *         },
65
+     *         user: array{
66
+     *             send_mail: bool,
67
+     *             expire_date?: array{
68
+     *                 enabled: bool,
69
+     *             },
70
+     *         },
71
+     *         resharing: bool,
72
+     *         group_sharing?: bool,
73
+     *         group?: array{
74
+     *             enabled: bool,
75
+     *             expire_date?: array{
76
+     *                 enabled: bool,
77
+     *             },
78
+     *         },
79
+     *         default_permissions?: int,
80
+     *         federation: array{
81
+     *             outgoing: bool,
82
+     *             incoming: bool,
83
+     *             expire_date: array{
84
+     *                 enabled: bool,
85
+     *             },
86
+     *             expire_date_supported: array{
87
+     *                 enabled: bool,
88
+     *             },
89
+     *         },
90
+     *         sharee: array{
91
+     *             query_lookup_default: bool,
92
+     *             always_show_unique: bool,
93
+     *         },
94
+     *	   },
95
+     * }
96
+     */
97
+    public function getCapabilities() {
98
+        $res = [];
99
+
100
+        if (!$this->shareManager->shareApiEnabled()) {
101
+            $res['api_enabled'] = false;
102
+            $res['public'] = ['enabled' => false];
103
+            $res['user'] = ['send_mail' => false];
104
+            $res['resharing'] = false;
105
+        } else {
106
+            $res['api_enabled'] = true;
107
+
108
+            $public = [];
109
+            $public['enabled'] = $this->shareManager->shareApiAllowLinks();
110
+            if ($public['enabled']) {
111
+                $public['password'] = [];
112
+                $public['password']['enforced'] = $this->shareManager->shareApiLinkEnforcePassword();
113
+
114
+                if ($public['password']['enforced']) {
115
+                    $public['password']['askForOptionalPassword'] = false;
116
+                } else {
117
+                    $public['password']['askForOptionalPassword'] = $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_LINK_PASSWORD_DEFAULT);
118
+                }
119
+
120
+                $public['expire_date'] = [];
121
+                $public['multiple_links'] = true;
122
+                $public['expire_date']['enabled'] = $this->shareManager->shareApiLinkDefaultExpireDate();
123
+                if ($public['expire_date']['enabled']) {
124
+                    $public['expire_date']['days'] = $this->shareManager->shareApiLinkDefaultExpireDays();
125
+                    $public['expire_date']['enforced'] = $this->shareManager->shareApiLinkDefaultExpireDateEnforced();
126
+                }
127
+
128
+                $public['expire_date_internal'] = [];
129
+                $public['expire_date_internal']['enabled'] = $this->shareManager->shareApiInternalDefaultExpireDate();
130
+                if ($public['expire_date_internal']['enabled']) {
131
+                    $public['expire_date_internal']['days'] = $this->shareManager->shareApiInternalDefaultExpireDays();
132
+                    $public['expire_date_internal']['enforced'] = $this->shareManager->shareApiInternalDefaultExpireDateEnforced();
133
+                }
134
+
135
+                $public['expire_date_remote'] = [];
136
+                $public['expire_date_remote']['enabled'] = $this->shareManager->shareApiRemoteDefaultExpireDate();
137
+                if ($public['expire_date_remote']['enabled']) {
138
+                    $public['expire_date_remote']['days'] = $this->shareManager->shareApiRemoteDefaultExpireDays();
139
+                    $public['expire_date_remote']['enforced'] = $this->shareManager->shareApiRemoteDefaultExpireDateEnforced();
140
+                }
141
+
142
+                $public['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no') === 'yes';
143
+                $public['upload'] = $this->shareManager->shareApiLinkAllowPublicUpload();
144
+                $public['upload_files_drop'] = $public['upload'];
145
+                $public['custom_tokens'] = $this->shareManager->allowCustomTokens();
146
+            }
147
+            $res['public'] = $public;
148
+
149
+            $res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes';
150
+
151
+            $res['user']['send_mail'] = false;
152
+            $res['user']['expire_date']['enabled'] = true;
153
+
154
+            // deprecated in favour of 'group', but we need to keep it for now
155
+            // in order to stay compatible with older clients
156
+            $res['group_sharing'] = $this->shareManager->allowGroupSharing();
157
+
158
+            $res['group'] = [];
159
+            $res['group']['enabled'] = $this->shareManager->allowGroupSharing();
160
+            $res['group']['expire_date']['enabled'] = true;
161
+            $res['default_permissions'] = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
162
+        }
163
+
164
+        //Federated sharing
165
+        if ($this->appManager->isEnabledForAnyone('federation')) {
166
+            $res['federation'] = [
167
+                'outgoing' => $this->shareManager->outgoingServer2ServerSharesAllowed(),
168
+                'incoming' => $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'yes',
169
+                // old bogus one, expire_date was not working before, keeping for compatibility
170
+                'expire_date' => ['enabled' => true],
171
+                // the real deal, signifies that expiration date can be set on federated shares
172
+                'expire_date_supported' => ['enabled' => true],
173
+            ];
174
+        } else {
175
+            $res['federation'] = [
176
+                'outgoing' => false,
177
+                'incoming' => false,
178
+                'expire_date' => ['enabled' => false],
179
+                'expire_date_supported' => ['enabled' => false],
180
+            ];
181
+        }
182
+
183
+        // Sharee searches
184
+        $res['sharee'] = [
185
+            'query_lookup_default' => $this->config->getSystemValueBool('gs.enabled', false),
186
+            'always_show_unique' => $this->config->getAppValue('files_sharing', 'always_show_unique', 'yes') === 'yes',
187
+        ];
188
+
189
+        return [
190
+            'files_sharing' => $res,
191
+        ];
192
+    }
193 193
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/CapabilitiesTest.php 2 patches
Indentation   +321 added lines, -321 removed lines patch added patch discarded remove patch
@@ -36,325 +36,325 @@
 block discarded – undo
36 36
  */
37 37
 class CapabilitiesTest extends \Test\TestCase {
38 38
 
39
-	/**
40
-	 * Test for the general part in each return statement and assert.
41
-	 * Strip of the general part on the way.
42
-	 *
43
-	 * @param string[] $data Capabilities
44
-	 * @return string[]
45
-	 */
46
-	private function getFilesSharingPart(array $data) {
47
-		$this->assertArrayHasKey('files_sharing', $data);
48
-		return $data['files_sharing'];
49
-	}
50
-
51
-	/**
52
-	 * Create a mock config object and insert the values in $map to the getAppValue
53
-	 * function. Then obtain the capabilities and extract the first few
54
-	 * levels in the array
55
-	 *
56
-	 * @param (string[])[] $map Map of arguments to return types for the getAppValue function in the mock
57
-	 * @return string[]
58
-	 */
59
-	private function getResults(array $map, array $typedMap = [], bool $federationEnabled = true) {
60
-		$config = $this->getMockBuilder(IConfig::class)->disableOriginalConstructor()->getMock();
61
-		$appManager = $this->getMockBuilder(IAppManager::class)->disableOriginalConstructor()->getMock();
62
-		$config->method('getAppValue')->willReturnMap($map);
63
-		$appManager->method('isEnabledForAnyone')->with('federation')->willReturn($federationEnabled);
64
-
65
-		if (empty($typedMap)) {
66
-			$appConfig = $this->createMock(IAppConfig::class);
67
-		} else {
68
-			// hack to help transition from old IConfig to new IAppConfig
69
-			$appConfig = $this->getMockBuilder(IAppConfig::class)->disableOriginalConstructor()->getMock();
70
-			$appConfig->expects($this->any())->method('getValueBool')->willReturnCallback(function (...$args) use ($typedMap): bool {
71
-				foreach ($typedMap as $entry) {
72
-					if ($entry[0] !== $args[0] || $entry[1] !== $args[1]) {
73
-						continue;
74
-					}
75
-
76
-					return $entry[2];
77
-				}
78
-
79
-				return false;
80
-			});
81
-		}
82
-
83
-		$shareManager = new Manager(
84
-			$this->createMock(LoggerInterface::class),
85
-			$config,
86
-			$this->createMock(ISecureRandom::class),
87
-			$this->createMock(IHasher::class),
88
-			$this->createMock(IMountManager::class),
89
-			$this->createMock(IGroupManager::class),
90
-			$this->createMock(IFactory::class),
91
-			$this->createMock(IProviderFactory::class),
92
-			$this->createMock(IUserManager::class),
93
-			$this->createMock(IRootFolder::class),
94
-			$this->createMock(IMailer::class),
95
-			$this->createMock(IURLGenerator::class),
96
-			$this->createMock(\OC_Defaults::class),
97
-			$this->createMock(IEventDispatcher::class),
98
-			$this->createMock(IUserSession::class),
99
-			$this->createMock(KnownUserService::class),
100
-			$this->createMock(ShareDisableChecker::class),
101
-			$this->createMock(IDateTimeZone::class),
102
-			$appConfig,
103
-		);
104
-
105
-		$cap = new Capabilities($config, $appConfig, $shareManager, $appManager);
106
-		$result = $this->getFilesSharingPart($cap->getCapabilities());
107
-		return $result;
108
-	}
109
-
110
-	public function testEnabledSharingAPI(): void {
111
-		$map = [
112
-			['core', 'shareapi_enabled', 'yes', 'yes'],
113
-		];
114
-		$result = $this->getResults($map);
115
-		$this->assertTrue($result['api_enabled']);
116
-		$this->assertArrayHasKey('public', $result);
117
-		$this->assertArrayHasKey('user', $result);
118
-		$this->assertArrayHasKey('resharing', $result);
119
-	}
120
-
121
-	public function testDisabledSharingAPI(): void {
122
-		$map = [
123
-			['core', 'shareapi_enabled', 'yes', 'no'],
124
-		];
125
-		$result = $this->getResults($map);
126
-		$this->assertFalse($result['api_enabled']);
127
-		$this->assertFalse($result['public']['enabled']);
128
-		$this->assertFalse($result['user']['send_mail']);
129
-		$this->assertFalse($result['resharing']);
130
-	}
131
-
132
-	public function testNoLinkSharing(): void {
133
-		$map = [
134
-			['core', 'shareapi_enabled', 'yes', 'yes'],
135
-			['core', 'shareapi_allow_links', 'yes', 'no'],
136
-		];
137
-		$result = $this->getResults($map);
138
-		$this->assertIsArray($result['public']);
139
-		$this->assertFalse($result['public']['enabled']);
140
-	}
141
-
142
-	public function testOnlyLinkSharing(): void {
143
-		$map = [
144
-			['core', 'shareapi_enabled', 'yes', 'yes'],
145
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
146
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
147
-		];
148
-		$result = $this->getResults($map);
149
-		$this->assertIsArray($result['public']);
150
-		$this->assertTrue($result['public']['enabled']);
151
-	}
152
-
153
-	public function testLinkPassword(): void {
154
-		$map = [
155
-			['core', 'shareapi_enabled', 'yes', 'yes'],
156
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
157
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
158
-		];
159
-		$typedMap = [
160
-			['core', 'shareapi_enforce_links_password', true],
161
-		];
162
-		$result = $this->getResults($map, $typedMap);
163
-		$this->assertArrayHasKey('password', $result['public']);
164
-		$this->assertArrayHasKey('enforced', $result['public']['password']);
165
-		$this->assertTrue($result['public']['password']['enforced']);
166
-	}
167
-
168
-	public function testLinkNoPassword(): void {
169
-		$map = [
170
-			['core', 'shareapi_enabled', 'yes', 'yes'],
171
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
172
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
173
-			['core', 'shareapi_enforce_links_password', 'no', 'no'],
174
-		];
175
-		$result = $this->getResults($map);
176
-		$this->assertArrayHasKey('password', $result['public']);
177
-		$this->assertArrayHasKey('enforced', $result['public']['password']);
178
-		$this->assertFalse($result['public']['password']['enforced']);
179
-	}
180
-
181
-	public function testLinkNoExpireDate(): void {
182
-		$map = [
183
-			['core', 'shareapi_enabled', 'yes', 'yes'],
184
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
185
-			['core', 'shareapi_default_expire_date', 'no', 'no'],
186
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
187
-		];
188
-		$result = $this->getResults($map);
189
-		$this->assertArrayHasKey('expire_date', $result['public']);
190
-		$this->assertIsArray($result['public']['expire_date']);
191
-		$this->assertFalse($result['public']['expire_date']['enabled']);
192
-	}
193
-
194
-	public function testLinkExpireDate(): void {
195
-		$map = [
196
-			['core', 'shareapi_enabled', 'yes', 'yes'],
197
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
198
-			['core', 'shareapi_default_expire_date', 'no', 'yes'],
199
-			['core', 'shareapi_expire_after_n_days', '7', '7'],
200
-			['core', 'shareapi_enforce_expire_date', 'no', 'no'],
201
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
202
-		];
203
-		$result = $this->getResults($map);
204
-		$this->assertArrayHasKey('expire_date', $result['public']);
205
-		$this->assertIsArray($result['public']['expire_date']);
206
-		$this->assertTrue($result['public']['expire_date']['enabled']);
207
-		$this->assertArrayHasKey('days', $result['public']['expire_date']);
208
-		$this->assertFalse($result['public']['expire_date']['enforced']);
209
-	}
210
-
211
-	public function testLinkExpireDateEnforced(): void {
212
-		$map = [
213
-			['core', 'shareapi_enabled', 'yes', 'yes'],
214
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
215
-			['core', 'shareapi_default_expire_date', 'no', 'yes'],
216
-			['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
217
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
218
-		];
219
-		$result = $this->getResults($map);
220
-		$this->assertArrayHasKey('expire_date', $result['public']);
221
-		$this->assertIsArray($result['public']['expire_date']);
222
-		$this->assertTrue($result['public']['expire_date']['enforced']);
223
-	}
224
-
225
-	public function testLinkSendMail(): void {
226
-		$map = [
227
-			['core', 'shareapi_enabled', 'yes', 'yes'],
228
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
229
-			['core', 'shareapi_allow_public_notification', 'no', 'yes'],
230
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
231
-		];
232
-		$result = $this->getResults($map);
233
-		$this->assertTrue($result['public']['send_mail']);
234
-	}
235
-
236
-	public function testLinkNoSendMail(): void {
237
-		$map = [
238
-			['core', 'shareapi_enabled', 'yes', 'yes'],
239
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
240
-			['core', 'shareapi_allow_public_notification', 'no', 'no'],
241
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
242
-		];
243
-		$result = $this->getResults($map);
244
-		$this->assertFalse($result['public']['send_mail']);
245
-	}
246
-
247
-	public function testResharing(): void {
248
-		$map = [
249
-			['core', 'shareapi_enabled', 'yes', 'yes'],
250
-			['core', 'shareapi_allow_resharing', 'yes', 'yes'],
251
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
252
-		];
253
-		$result = $this->getResults($map);
254
-		$this->assertTrue($result['resharing']);
255
-	}
256
-
257
-	public function testNoResharing(): void {
258
-		$map = [
259
-			['core', 'shareapi_enabled', 'yes', 'yes'],
260
-			['core', 'shareapi_allow_resharing', 'yes', 'no'],
261
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
262
-		];
263
-		$result = $this->getResults($map);
264
-		$this->assertFalse($result['resharing']);
265
-	}
266
-
267
-	public function testLinkPublicUpload(): void {
268
-		$map = [
269
-			['core', 'shareapi_enabled', 'yes', 'yes'],
270
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
271
-			['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
272
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
273
-		];
274
-		$result = $this->getResults($map);
275
-		$this->assertTrue($result['public']['upload']);
276
-		$this->assertTrue($result['public']['upload_files_drop']);
277
-	}
278
-
279
-	public function testLinkNoPublicUpload(): void {
280
-		$map = [
281
-			['core', 'shareapi_enabled', 'yes', 'yes'],
282
-			['core', 'shareapi_allow_links', 'yes', 'yes'],
283
-			['core', 'shareapi_allow_public_upload', 'yes', 'no'],
284
-			['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
285
-		];
286
-		$result = $this->getResults($map);
287
-		$this->assertFalse($result['public']['upload']);
288
-		$this->assertFalse($result['public']['upload_files_drop']);
289
-	}
290
-
291
-	public function testNoGroupSharing(): void {
292
-		$map = [
293
-			['core', 'shareapi_enabled', 'yes', 'yes'],
294
-			['core', 'shareapi_allow_group_sharing', 'yes', 'no'],
295
-		];
296
-		$result = $this->getResults($map);
297
-		$this->assertFalse($result['group_sharing']);
298
-	}
299
-
300
-	public function testGroupSharing(): void {
301
-		$map = [
302
-			['core', 'shareapi_enabled', 'yes', 'yes'],
303
-			['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
304
-		];
305
-		$result = $this->getResults($map);
306
-		$this->assertTrue($result['group_sharing']);
307
-	}
308
-
309
-	public function testFederatedSharingIncoming(): void {
310
-		$map = [
311
-			['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'yes'],
312
-		];
313
-		$result = $this->getResults($map);
314
-		$this->assertArrayHasKey('federation', $result);
315
-		$this->assertTrue($result['federation']['incoming']);
316
-	}
317
-
318
-	public function testFederatedSharingNoIncoming(): void {
319
-		$map = [
320
-			['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'no'],
321
-		];
322
-		$result = $this->getResults($map);
323
-		$this->assertArrayHasKey('federation', $result);
324
-		$this->assertFalse($result['federation']['incoming']);
325
-	}
326
-
327
-	public function testFederatedSharingOutgoing(): void {
328
-		$map = [
329
-			['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'yes'],
330
-		];
331
-		$result = $this->getResults($map);
332
-		$this->assertArrayHasKey('federation', $result);
333
-		$this->assertTrue($result['federation']['outgoing']);
334
-	}
335
-
336
-	public function testFederatedSharingNoOutgoing(): void {
337
-		$map = [
338
-			['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'no'],
339
-		];
340
-		$result = $this->getResults($map);
341
-		$this->assertArrayHasKey('federation', $result);
342
-		$this->assertFalse($result['federation']['outgoing']);
343
-	}
344
-
345
-	public function testFederatedSharingExpirationDate(): void {
346
-		$result = $this->getResults([]);
347
-		$this->assertArrayHasKey('federation', $result);
348
-		$this->assertEquals(['enabled' => true], $result['federation']['expire_date']);
349
-		$this->assertEquals(['enabled' => true], $result['federation']['expire_date_supported']);
350
-	}
351
-
352
-	public function testFederatedSharingDisabled(): void {
353
-		$result = $this->getResults([], federationEnabled: false);
354
-		$this->assertArrayHasKey('federation', $result);
355
-		$this->assertFalse($result['federation']['incoming']);
356
-		$this->assertFalse($result['federation']['outgoing']);
357
-		$this->assertEquals(['enabled' => false], $result['federation']['expire_date']);
358
-		$this->assertEquals(['enabled' => false], $result['federation']['expire_date_supported']);
359
-	}
39
+    /**
40
+     * Test for the general part in each return statement and assert.
41
+     * Strip of the general part on the way.
42
+     *
43
+     * @param string[] $data Capabilities
44
+     * @return string[]
45
+     */
46
+    private function getFilesSharingPart(array $data) {
47
+        $this->assertArrayHasKey('files_sharing', $data);
48
+        return $data['files_sharing'];
49
+    }
50
+
51
+    /**
52
+     * Create a mock config object and insert the values in $map to the getAppValue
53
+     * function. Then obtain the capabilities and extract the first few
54
+     * levels in the array
55
+     *
56
+     * @param (string[])[] $map Map of arguments to return types for the getAppValue function in the mock
57
+     * @return string[]
58
+     */
59
+    private function getResults(array $map, array $typedMap = [], bool $federationEnabled = true) {
60
+        $config = $this->getMockBuilder(IConfig::class)->disableOriginalConstructor()->getMock();
61
+        $appManager = $this->getMockBuilder(IAppManager::class)->disableOriginalConstructor()->getMock();
62
+        $config->method('getAppValue')->willReturnMap($map);
63
+        $appManager->method('isEnabledForAnyone')->with('federation')->willReturn($federationEnabled);
64
+
65
+        if (empty($typedMap)) {
66
+            $appConfig = $this->createMock(IAppConfig::class);
67
+        } else {
68
+            // hack to help transition from old IConfig to new IAppConfig
69
+            $appConfig = $this->getMockBuilder(IAppConfig::class)->disableOriginalConstructor()->getMock();
70
+            $appConfig->expects($this->any())->method('getValueBool')->willReturnCallback(function (...$args) use ($typedMap): bool {
71
+                foreach ($typedMap as $entry) {
72
+                    if ($entry[0] !== $args[0] || $entry[1] !== $args[1]) {
73
+                        continue;
74
+                    }
75
+
76
+                    return $entry[2];
77
+                }
78
+
79
+                return false;
80
+            });
81
+        }
82
+
83
+        $shareManager = new Manager(
84
+            $this->createMock(LoggerInterface::class),
85
+            $config,
86
+            $this->createMock(ISecureRandom::class),
87
+            $this->createMock(IHasher::class),
88
+            $this->createMock(IMountManager::class),
89
+            $this->createMock(IGroupManager::class),
90
+            $this->createMock(IFactory::class),
91
+            $this->createMock(IProviderFactory::class),
92
+            $this->createMock(IUserManager::class),
93
+            $this->createMock(IRootFolder::class),
94
+            $this->createMock(IMailer::class),
95
+            $this->createMock(IURLGenerator::class),
96
+            $this->createMock(\OC_Defaults::class),
97
+            $this->createMock(IEventDispatcher::class),
98
+            $this->createMock(IUserSession::class),
99
+            $this->createMock(KnownUserService::class),
100
+            $this->createMock(ShareDisableChecker::class),
101
+            $this->createMock(IDateTimeZone::class),
102
+            $appConfig,
103
+        );
104
+
105
+        $cap = new Capabilities($config, $appConfig, $shareManager, $appManager);
106
+        $result = $this->getFilesSharingPart($cap->getCapabilities());
107
+        return $result;
108
+    }
109
+
110
+    public function testEnabledSharingAPI(): void {
111
+        $map = [
112
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
113
+        ];
114
+        $result = $this->getResults($map);
115
+        $this->assertTrue($result['api_enabled']);
116
+        $this->assertArrayHasKey('public', $result);
117
+        $this->assertArrayHasKey('user', $result);
118
+        $this->assertArrayHasKey('resharing', $result);
119
+    }
120
+
121
+    public function testDisabledSharingAPI(): void {
122
+        $map = [
123
+            ['core', 'shareapi_enabled', 'yes', 'no'],
124
+        ];
125
+        $result = $this->getResults($map);
126
+        $this->assertFalse($result['api_enabled']);
127
+        $this->assertFalse($result['public']['enabled']);
128
+        $this->assertFalse($result['user']['send_mail']);
129
+        $this->assertFalse($result['resharing']);
130
+    }
131
+
132
+    public function testNoLinkSharing(): void {
133
+        $map = [
134
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
135
+            ['core', 'shareapi_allow_links', 'yes', 'no'],
136
+        ];
137
+        $result = $this->getResults($map);
138
+        $this->assertIsArray($result['public']);
139
+        $this->assertFalse($result['public']['enabled']);
140
+    }
141
+
142
+    public function testOnlyLinkSharing(): void {
143
+        $map = [
144
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
145
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
146
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
147
+        ];
148
+        $result = $this->getResults($map);
149
+        $this->assertIsArray($result['public']);
150
+        $this->assertTrue($result['public']['enabled']);
151
+    }
152
+
153
+    public function testLinkPassword(): void {
154
+        $map = [
155
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
156
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
157
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
158
+        ];
159
+        $typedMap = [
160
+            ['core', 'shareapi_enforce_links_password', true],
161
+        ];
162
+        $result = $this->getResults($map, $typedMap);
163
+        $this->assertArrayHasKey('password', $result['public']);
164
+        $this->assertArrayHasKey('enforced', $result['public']['password']);
165
+        $this->assertTrue($result['public']['password']['enforced']);
166
+    }
167
+
168
+    public function testLinkNoPassword(): void {
169
+        $map = [
170
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
171
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
172
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
173
+            ['core', 'shareapi_enforce_links_password', 'no', 'no'],
174
+        ];
175
+        $result = $this->getResults($map);
176
+        $this->assertArrayHasKey('password', $result['public']);
177
+        $this->assertArrayHasKey('enforced', $result['public']['password']);
178
+        $this->assertFalse($result['public']['password']['enforced']);
179
+    }
180
+
181
+    public function testLinkNoExpireDate(): void {
182
+        $map = [
183
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
184
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
185
+            ['core', 'shareapi_default_expire_date', 'no', 'no'],
186
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
187
+        ];
188
+        $result = $this->getResults($map);
189
+        $this->assertArrayHasKey('expire_date', $result['public']);
190
+        $this->assertIsArray($result['public']['expire_date']);
191
+        $this->assertFalse($result['public']['expire_date']['enabled']);
192
+    }
193
+
194
+    public function testLinkExpireDate(): void {
195
+        $map = [
196
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
197
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
198
+            ['core', 'shareapi_default_expire_date', 'no', 'yes'],
199
+            ['core', 'shareapi_expire_after_n_days', '7', '7'],
200
+            ['core', 'shareapi_enforce_expire_date', 'no', 'no'],
201
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
202
+        ];
203
+        $result = $this->getResults($map);
204
+        $this->assertArrayHasKey('expire_date', $result['public']);
205
+        $this->assertIsArray($result['public']['expire_date']);
206
+        $this->assertTrue($result['public']['expire_date']['enabled']);
207
+        $this->assertArrayHasKey('days', $result['public']['expire_date']);
208
+        $this->assertFalse($result['public']['expire_date']['enforced']);
209
+    }
210
+
211
+    public function testLinkExpireDateEnforced(): void {
212
+        $map = [
213
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
214
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
215
+            ['core', 'shareapi_default_expire_date', 'no', 'yes'],
216
+            ['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
217
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
218
+        ];
219
+        $result = $this->getResults($map);
220
+        $this->assertArrayHasKey('expire_date', $result['public']);
221
+        $this->assertIsArray($result['public']['expire_date']);
222
+        $this->assertTrue($result['public']['expire_date']['enforced']);
223
+    }
224
+
225
+    public function testLinkSendMail(): void {
226
+        $map = [
227
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
228
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
229
+            ['core', 'shareapi_allow_public_notification', 'no', 'yes'],
230
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
231
+        ];
232
+        $result = $this->getResults($map);
233
+        $this->assertTrue($result['public']['send_mail']);
234
+    }
235
+
236
+    public function testLinkNoSendMail(): void {
237
+        $map = [
238
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
239
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
240
+            ['core', 'shareapi_allow_public_notification', 'no', 'no'],
241
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
242
+        ];
243
+        $result = $this->getResults($map);
244
+        $this->assertFalse($result['public']['send_mail']);
245
+    }
246
+
247
+    public function testResharing(): void {
248
+        $map = [
249
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
250
+            ['core', 'shareapi_allow_resharing', 'yes', 'yes'],
251
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
252
+        ];
253
+        $result = $this->getResults($map);
254
+        $this->assertTrue($result['resharing']);
255
+    }
256
+
257
+    public function testNoResharing(): void {
258
+        $map = [
259
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
260
+            ['core', 'shareapi_allow_resharing', 'yes', 'no'],
261
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
262
+        ];
263
+        $result = $this->getResults($map);
264
+        $this->assertFalse($result['resharing']);
265
+    }
266
+
267
+    public function testLinkPublicUpload(): void {
268
+        $map = [
269
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
270
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
271
+            ['core', 'shareapi_allow_public_upload', 'yes', 'yes'],
272
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
273
+        ];
274
+        $result = $this->getResults($map);
275
+        $this->assertTrue($result['public']['upload']);
276
+        $this->assertTrue($result['public']['upload_files_drop']);
277
+    }
278
+
279
+    public function testLinkNoPublicUpload(): void {
280
+        $map = [
281
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
282
+            ['core', 'shareapi_allow_links', 'yes', 'yes'],
283
+            ['core', 'shareapi_allow_public_upload', 'yes', 'no'],
284
+            ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
285
+        ];
286
+        $result = $this->getResults($map);
287
+        $this->assertFalse($result['public']['upload']);
288
+        $this->assertFalse($result['public']['upload_files_drop']);
289
+    }
290
+
291
+    public function testNoGroupSharing(): void {
292
+        $map = [
293
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
294
+            ['core', 'shareapi_allow_group_sharing', 'yes', 'no'],
295
+        ];
296
+        $result = $this->getResults($map);
297
+        $this->assertFalse($result['group_sharing']);
298
+    }
299
+
300
+    public function testGroupSharing(): void {
301
+        $map = [
302
+            ['core', 'shareapi_enabled', 'yes', 'yes'],
303
+            ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
304
+        ];
305
+        $result = $this->getResults($map);
306
+        $this->assertTrue($result['group_sharing']);
307
+    }
308
+
309
+    public function testFederatedSharingIncoming(): void {
310
+        $map = [
311
+            ['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'yes'],
312
+        ];
313
+        $result = $this->getResults($map);
314
+        $this->assertArrayHasKey('federation', $result);
315
+        $this->assertTrue($result['federation']['incoming']);
316
+    }
317
+
318
+    public function testFederatedSharingNoIncoming(): void {
319
+        $map = [
320
+            ['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'no'],
321
+        ];
322
+        $result = $this->getResults($map);
323
+        $this->assertArrayHasKey('federation', $result);
324
+        $this->assertFalse($result['federation']['incoming']);
325
+    }
326
+
327
+    public function testFederatedSharingOutgoing(): void {
328
+        $map = [
329
+            ['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'yes'],
330
+        ];
331
+        $result = $this->getResults($map);
332
+        $this->assertArrayHasKey('federation', $result);
333
+        $this->assertTrue($result['federation']['outgoing']);
334
+    }
335
+
336
+    public function testFederatedSharingNoOutgoing(): void {
337
+        $map = [
338
+            ['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'no'],
339
+        ];
340
+        $result = $this->getResults($map);
341
+        $this->assertArrayHasKey('federation', $result);
342
+        $this->assertFalse($result['federation']['outgoing']);
343
+    }
344
+
345
+    public function testFederatedSharingExpirationDate(): void {
346
+        $result = $this->getResults([]);
347
+        $this->assertArrayHasKey('federation', $result);
348
+        $this->assertEquals(['enabled' => true], $result['federation']['expire_date']);
349
+        $this->assertEquals(['enabled' => true], $result['federation']['expire_date_supported']);
350
+    }
351
+
352
+    public function testFederatedSharingDisabled(): void {
353
+        $result = $this->getResults([], federationEnabled: false);
354
+        $this->assertArrayHasKey('federation', $result);
355
+        $this->assertFalse($result['federation']['incoming']);
356
+        $this->assertFalse($result['federation']['outgoing']);
357
+        $this->assertEquals(['enabled' => false], $result['federation']['expire_date']);
358
+        $this->assertEquals(['enabled' => false], $result['federation']['expire_date_supported']);
359
+    }
360 360
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -67,7 +67,7 @@
 block discarded – undo
67 67
 		} else {
68 68
 			// hack to help transition from old IConfig to new IAppConfig
69 69
 			$appConfig = $this->getMockBuilder(IAppConfig::class)->disableOriginalConstructor()->getMock();
70
-			$appConfig->expects($this->any())->method('getValueBool')->willReturnCallback(function (...$args) use ($typedMap): bool {
70
+			$appConfig->expects($this->any())->method('getValueBool')->willReturnCallback(function(...$args) use ($typedMap): bool {
71 71
 				foreach ($typedMap as $entry) {
72 72
 					if ($entry[0] !== $args[0] || $entry[1] !== $args[1]) {
73 73
 						continue;
Please login to merge, or discard this patch.