Completed
Push — master ( d8ff05...9bc946 )
by
unknown
40:53 queued 20:39
created
apps/theming/lib/Service/ThemesService.php 1 patch
Indentation   +165 added lines, -165 removed lines patch added patch discarded remove patch
@@ -19,169 +19,169 @@
 block discarded – undo
19 19
 use Psr\Log\LoggerInterface;
20 20
 
21 21
 class ThemesService {
22
-	/** @var ITheme[] */
23
-	private array $themesProviders;
24
-
25
-	public function __construct(
26
-		private IUserSession $userSession,
27
-		private IConfig $config,
28
-		private LoggerInterface $logger,
29
-		private DefaultTheme $defaultTheme,
30
-		LightTheme $lightTheme,
31
-		private DarkTheme $darkTheme,
32
-		HighContrastTheme $highContrastTheme,
33
-		DarkHighContrastTheme $darkHighContrastTheme,
34
-		DyslexiaFont $dyslexiaFont,
35
-	) {
36
-
37
-		// Register themes
38
-		$this->themesProviders = [
39
-			$defaultTheme->getId() => $defaultTheme,
40
-			$lightTheme->getId() => $lightTheme,
41
-			$darkTheme->getId() => $darkTheme,
42
-			$highContrastTheme->getId() => $highContrastTheme,
43
-			$darkHighContrastTheme->getId() => $darkHighContrastTheme,
44
-			$dyslexiaFont->getId() => $dyslexiaFont,
45
-		];
46
-	}
47
-
48
-	/**
49
-	 * Get the list of all registered themes
50
-	 *
51
-	 * @return ITheme[]
52
-	 */
53
-	public function getThemes(): array {
54
-		// Enforced theme if configured
55
-		$enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
56
-		if ($enforcedTheme !== '') {
57
-			if (!isset($this->themesProviders[$enforcedTheme])) {
58
-				$this->logger->error('Enforced theme not found', ['theme' => $enforcedTheme]);
59
-				return $this->themesProviders;
60
-			}
61
-
62
-			$defaultTheme = $this->themesProviders[$this->defaultTheme->getId()];
63
-			$darkTheme = $this->themesProviders[$this->darkTheme->getId()];
64
-			$theme = $this->themesProviders[$enforcedTheme];
65
-			return [
66
-				// Leave the default theme as a fallback
67
-				$defaultTheme->getId() => $defaultTheme,
68
-				// Make sure we also have the dark theme to allow apps
69
-				// to scope sections of their UI to the dark theme
70
-				$darkTheme->getId() => $darkTheme,
71
-				// Finally, the enforced theme
72
-				$theme->getId() => $theme,
73
-			];
74
-		}
75
-
76
-		return $this->themesProviders;
77
-	}
78
-
79
-	/**
80
-	 * Enable a theme for the logged-in user
81
-	 *
82
-	 * @param ITheme $theme the theme to enable
83
-	 * @return string[] the enabled themes
84
-	 */
85
-	public function enableTheme(ITheme $theme): array {
86
-		$themesIds = $this->getEnabledThemes();
87
-
88
-		// If already enabled, ignore
89
-		if (in_array($theme->getId(), $themesIds)) {
90
-			return $themesIds;
91
-		}
92
-
93
-		/** @var ITheme[] */
94
-		$themes = array_filter(array_map(function ($themeId) {
95
-			return $this->getThemes()[$themeId];
96
-		}, $themesIds));
97
-
98
-		// Filtering all themes with the same type
99
-		$filteredThemes = array_filter($themes, function (ITheme $t) use ($theme) {
100
-			return $theme->getType() === $t->getType();
101
-		});
102
-
103
-		// Retrieve IDs only
104
-		/** @var string[] */
105
-		$filteredThemesIds = array_map(function (ITheme $t) {
106
-			return $t->getId();
107
-		}, array_values($filteredThemes));
108
-
109
-		$enabledThemes = array_merge(array_diff($themesIds, $filteredThemesIds), [$theme->getId()]);
110
-		$this->setEnabledThemes($enabledThemes);
111
-
112
-		return $enabledThemes;
113
-	}
114
-
115
-	/**
116
-	 * Disable a theme for the logged-in user
117
-	 *
118
-	 * @param ITheme $theme the theme to disable
119
-	 * @return string[] the enabled themes
120
-	 */
121
-	public function disableTheme(ITheme $theme): array {
122
-		$themesIds = $this->getEnabledThemes();
123
-
124
-		// If enabled, removing it
125
-		if (in_array($theme->getId(), $themesIds)) {
126
-			$enabledThemes = array_diff($themesIds, [$theme->getId()]);
127
-			$this->setEnabledThemes($enabledThemes);
128
-			return $enabledThemes;
129
-		}
130
-
131
-		return $themesIds;
132
-	}
133
-
134
-	/**
135
-	 * Check whether a theme is enabled or not
136
-	 * for the logged-in user
137
-	 *
138
-	 * @return bool
139
-	 */
140
-	public function isEnabled(ITheme $theme): bool {
141
-		$user = $this->userSession->getUser();
142
-		if ($user instanceof IUser) {
143
-			// Using keys as it's faster
144
-			$themes = $this->getEnabledThemes();
145
-			return in_array($theme->getId(), $themes);
146
-		}
147
-		return false;
148
-	}
149
-
150
-	/**
151
-	 * Get the list of all enabled themes IDs for the current user.
152
-	 *
153
-	 * @return string[]
154
-	 */
155
-	public function getEnabledThemes(): array {
156
-		$enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
157
-		$user = $this->userSession->getUser();
158
-		if ($user === null) {
159
-			if ($enforcedTheme !== '') {
160
-				return [$enforcedTheme];
161
-			}
162
-			return [];
163
-		}
164
-
165
-		$enabledThemes = json_decode($this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', '["default"]'));
166
-		if ($enforcedTheme !== '') {
167
-			return array_merge([$enforcedTheme], $enabledThemes);
168
-		}
169
-
170
-		try {
171
-			return $enabledThemes;
172
-		} catch (\Exception $e) {
173
-			return [];
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * Set the list of enabled themes
179
-	 * for the logged-in user
180
-	 *
181
-	 * @param string[] $themes the list of enabled themes IDs
182
-	 */
183
-	private function setEnabledThemes(array $themes): void {
184
-		$user = $this->userSession->getUser();
185
-		$this->config->setUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', json_encode(array_values(array_unique($themes))));
186
-	}
22
+    /** @var ITheme[] */
23
+    private array $themesProviders;
24
+
25
+    public function __construct(
26
+        private IUserSession $userSession,
27
+        private IConfig $config,
28
+        private LoggerInterface $logger,
29
+        private DefaultTheme $defaultTheme,
30
+        LightTheme $lightTheme,
31
+        private DarkTheme $darkTheme,
32
+        HighContrastTheme $highContrastTheme,
33
+        DarkHighContrastTheme $darkHighContrastTheme,
34
+        DyslexiaFont $dyslexiaFont,
35
+    ) {
36
+
37
+        // Register themes
38
+        $this->themesProviders = [
39
+            $defaultTheme->getId() => $defaultTheme,
40
+            $lightTheme->getId() => $lightTheme,
41
+            $darkTheme->getId() => $darkTheme,
42
+            $highContrastTheme->getId() => $highContrastTheme,
43
+            $darkHighContrastTheme->getId() => $darkHighContrastTheme,
44
+            $dyslexiaFont->getId() => $dyslexiaFont,
45
+        ];
46
+    }
47
+
48
+    /**
49
+     * Get the list of all registered themes
50
+     *
51
+     * @return ITheme[]
52
+     */
53
+    public function getThemes(): array {
54
+        // Enforced theme if configured
55
+        $enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
56
+        if ($enforcedTheme !== '') {
57
+            if (!isset($this->themesProviders[$enforcedTheme])) {
58
+                $this->logger->error('Enforced theme not found', ['theme' => $enforcedTheme]);
59
+                return $this->themesProviders;
60
+            }
61
+
62
+            $defaultTheme = $this->themesProviders[$this->defaultTheme->getId()];
63
+            $darkTheme = $this->themesProviders[$this->darkTheme->getId()];
64
+            $theme = $this->themesProviders[$enforcedTheme];
65
+            return [
66
+                // Leave the default theme as a fallback
67
+                $defaultTheme->getId() => $defaultTheme,
68
+                // Make sure we also have the dark theme to allow apps
69
+                // to scope sections of their UI to the dark theme
70
+                $darkTheme->getId() => $darkTheme,
71
+                // Finally, the enforced theme
72
+                $theme->getId() => $theme,
73
+            ];
74
+        }
75
+
76
+        return $this->themesProviders;
77
+    }
78
+
79
+    /**
80
+     * Enable a theme for the logged-in user
81
+     *
82
+     * @param ITheme $theme the theme to enable
83
+     * @return string[] the enabled themes
84
+     */
85
+    public function enableTheme(ITheme $theme): array {
86
+        $themesIds = $this->getEnabledThemes();
87
+
88
+        // If already enabled, ignore
89
+        if (in_array($theme->getId(), $themesIds)) {
90
+            return $themesIds;
91
+        }
92
+
93
+        /** @var ITheme[] */
94
+        $themes = array_filter(array_map(function ($themeId) {
95
+            return $this->getThemes()[$themeId];
96
+        }, $themesIds));
97
+
98
+        // Filtering all themes with the same type
99
+        $filteredThemes = array_filter($themes, function (ITheme $t) use ($theme) {
100
+            return $theme->getType() === $t->getType();
101
+        });
102
+
103
+        // Retrieve IDs only
104
+        /** @var string[] */
105
+        $filteredThemesIds = array_map(function (ITheme $t) {
106
+            return $t->getId();
107
+        }, array_values($filteredThemes));
108
+
109
+        $enabledThemes = array_merge(array_diff($themesIds, $filteredThemesIds), [$theme->getId()]);
110
+        $this->setEnabledThemes($enabledThemes);
111
+
112
+        return $enabledThemes;
113
+    }
114
+
115
+    /**
116
+     * Disable a theme for the logged-in user
117
+     *
118
+     * @param ITheme $theme the theme to disable
119
+     * @return string[] the enabled themes
120
+     */
121
+    public function disableTheme(ITheme $theme): array {
122
+        $themesIds = $this->getEnabledThemes();
123
+
124
+        // If enabled, removing it
125
+        if (in_array($theme->getId(), $themesIds)) {
126
+            $enabledThemes = array_diff($themesIds, [$theme->getId()]);
127
+            $this->setEnabledThemes($enabledThemes);
128
+            return $enabledThemes;
129
+        }
130
+
131
+        return $themesIds;
132
+    }
133
+
134
+    /**
135
+     * Check whether a theme is enabled or not
136
+     * for the logged-in user
137
+     *
138
+     * @return bool
139
+     */
140
+    public function isEnabled(ITheme $theme): bool {
141
+        $user = $this->userSession->getUser();
142
+        if ($user instanceof IUser) {
143
+            // Using keys as it's faster
144
+            $themes = $this->getEnabledThemes();
145
+            return in_array($theme->getId(), $themes);
146
+        }
147
+        return false;
148
+    }
149
+
150
+    /**
151
+     * Get the list of all enabled themes IDs for the current user.
152
+     *
153
+     * @return string[]
154
+     */
155
+    public function getEnabledThemes(): array {
156
+        $enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
157
+        $user = $this->userSession->getUser();
158
+        if ($user === null) {
159
+            if ($enforcedTheme !== '') {
160
+                return [$enforcedTheme];
161
+            }
162
+            return [];
163
+        }
164
+
165
+        $enabledThemes = json_decode($this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', '["default"]'));
166
+        if ($enforcedTheme !== '') {
167
+            return array_merge([$enforcedTheme], $enabledThemes);
168
+        }
169
+
170
+        try {
171
+            return $enabledThemes;
172
+        } catch (\Exception $e) {
173
+            return [];
174
+        }
175
+    }
176
+
177
+    /**
178
+     * Set the list of enabled themes
179
+     * for the logged-in user
180
+     *
181
+     * @param string[] $themes the list of enabled themes IDs
182
+     */
183
+    private function setEnabledThemes(array $themes): void {
184
+        $user = $this->userSession->getUser();
185
+        $this->config->setUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', json_encode(array_values(array_unique($themes))));
186
+    }
187 187
 }
Please login to merge, or discard this patch.
lib/private/TemplateLayout.php 1 patch
Indentation   +371 added lines, -371 removed lines patch added patch discarded remove patch
@@ -35,375 +35,375 @@
 block discarded – undo
35 35
 use OCP\Util;
36 36
 
37 37
 class TemplateLayout {
38
-	private static string $versionHash = '';
39
-	/** @var string[] */
40
-	private static array $cacheBusterCache = [];
41
-
42
-	public static ?CSSResourceLocator $cssLocator = null;
43
-	public static ?JSResourceLocator $jsLocator = null;
44
-
45
-	public function __construct(
46
-		private IConfig $config,
47
-		private IAppManager $appManager,
48
-		private InitialStateService $initialState,
49
-		private INavigationManager $navigationManager,
50
-		private ITemplateManager $templateManager,
51
-		private ServerVersion $serverVersion,
52
-	) {
53
-	}
54
-
55
-	public function getPageTemplate(string $renderAs, string $appId): ITemplate {
56
-		// Add fallback theming variables if not rendered as user
57
-		if ($renderAs !== TemplateResponse::RENDER_AS_USER) {
58
-			// TODO cache generated default theme if enabled for fallback if server is erroring ?
59
-			Util::addStyle('theming', 'default');
60
-		}
61
-
62
-		// Decide which page we show
63
-		switch ($renderAs) {
64
-			case TemplateResponse::RENDER_AS_USER:
65
-				$page = $this->templateManager->getTemplate('core', 'layout.user');
66
-				if (in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) {
67
-					$page->assign('bodyid', 'body-settings');
68
-				} else {
69
-					$page->assign('bodyid', 'body-user');
70
-				}
71
-
72
-				$this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
73
-				$this->initialState->provideInitialState('core', 'apps', array_values($this->navigationManager->getAll()));
74
-
75
-				if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) {
76
-					$this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
77
-					$this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1));
78
-					$this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
79
-					Util::addScript('core', 'legacy-unified-search', 'core');
80
-				} else {
81
-					Util::addScript('core', 'unified-search', 'core');
82
-				}
83
-
84
-				// Set logo link target
85
-				$logoUrl = $this->config->getSystemValueString('logo_url', '');
86
-				$page->assign('logoUrl', $logoUrl);
87
-
88
-				// Set default entry name
89
-				$defaultEntryId = $this->navigationManager->getDefaultEntryIdForUser();
90
-				$defaultEntry = $this->navigationManager->get($defaultEntryId);
91
-				$page->assign('defaultAppName', $defaultEntry['name'] ?? '');
92
-
93
-				// Add navigation entry
94
-				$page->assign('application', '');
95
-				$page->assign('appid', $appId);
96
-
97
-				$navigation = $this->navigationManager->getAll();
98
-				$page->assign('navigation', $navigation);
99
-				$settingsNavigation = $this->navigationManager->getAll('settings');
100
-				$this->initialState->provideInitialState('core', 'settingsNavEntries', $settingsNavigation);
101
-
102
-				foreach ($navigation as $entry) {
103
-					if ($entry['active']) {
104
-						$page->assign('application', $entry['name']);
105
-						break;
106
-					}
107
-				}
108
-
109
-				foreach ($settingsNavigation as $entry) {
110
-					if ($entry['active']) {
111
-						$page->assign('application', $entry['name']);
112
-						break;
113
-					}
114
-				}
115
-
116
-				$user = Server::get(IUserSession::class)->getUser();
117
-
118
-				if ($user === null) {
119
-					$page->assign('user_uid', false);
120
-					$page->assign('user_displayname', false);
121
-					$page->assign('userAvatarSet', false);
122
-					$page->assign('userStatus', false);
123
-				} else {
124
-					$page->assign('user_uid', $user->getUID());
125
-					$page->assign('user_displayname', $user->getDisplayName());
126
-					$page->assign('userAvatarSet', true);
127
-					$page->assign('userAvatarVersion', $this->config->getUserValue($user->getUID(), 'avatar', 'version', 0));
128
-				}
129
-				break;
130
-			case TemplateResponse::RENDER_AS_ERROR:
131
-				$page = $this->templateManager->getTemplate('core', 'layout.guest', '', false);
132
-				$page->assign('bodyid', 'body-login');
133
-				$page->assign('user_displayname', '');
134
-				$page->assign('user_uid', '');
135
-				break;
136
-			case TemplateResponse::RENDER_AS_GUEST:
137
-				$page = $this->templateManager->getTemplate('core', 'layout.guest');
138
-				Util::addStyle('guest');
139
-				$page->assign('bodyid', 'body-login');
140
-
141
-				$userDisplayName = false;
142
-				$user = Server::get(IUserSession::class)->getUser();
143
-				if ($user) {
144
-					$userDisplayName = $user->getDisplayName();
145
-				}
146
-
147
-				$page->assign('enabledThemes', []);
148
-				if ($this->appManager->isEnabledForUser('theming') && class_exists('\OCA\Theming\Service\ThemesService')) {
149
-					$themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
150
-					$page->assign('enabledThemes', $themesService->getEnabledThemes());
151
-				}
152
-
153
-				$page->assign('user_displayname', $userDisplayName);
154
-				$page->assign('user_uid', \OC_User::getUser());
155
-				break;
156
-			case TemplateResponse::RENDER_AS_PUBLIC:
157
-				$page = $this->templateManager->getTemplate('core', 'layout.public');
158
-				$page->assign('appid', $appId);
159
-				$page->assign('bodyid', 'body-public');
160
-
161
-				// Set logo link target
162
-				$logoUrl = $this->config->getSystemValueString('logo_url', '');
163
-				$page->assign('logoUrl', $logoUrl);
164
-
165
-				$subscription = Server::get(IRegistry::class);
166
-				$showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true);
167
-				if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) {
168
-					$showSimpleSignup = false;
169
-				}
170
-
171
-				$defaultSignUpLink = 'https://nextcloud.com/signup/';
172
-				$signUpLink = $this->config->getSystemValueString('registration_link', $defaultSignUpLink);
173
-				if ($signUpLink !== $defaultSignUpLink) {
174
-					$showSimpleSignup = true;
175
-				}
176
-
177
-				if ($this->appManager->isEnabledForUser('registration')) {
178
-					$urlGenerator = Server::get(IURLGenerator::class);
179
-					$signUpLink = $urlGenerator->getAbsoluteURL('/index.php/apps/registration/');
180
-				}
181
-
182
-				$page->assign('showSimpleSignUpLink', $showSimpleSignup);
183
-				$page->assign('signUpLink', $signUpLink);
184
-				break;
185
-			default:
186
-				$page = $this->templateManager->getTemplate('core', 'layout.base');
187
-				break;
188
-		}
189
-		// Send the language, locale, and direction to our layouts
190
-		$l10nFactory = Server::get(IFactory::class);
191
-		$lang = $l10nFactory->findLanguage();
192
-		$locale = $l10nFactory->findLocale($lang);
193
-		$direction = $l10nFactory->getLanguageDirection($lang);
194
-
195
-		$lang = str_replace('_', '-', $lang);
196
-		$page->assign('language', $lang);
197
-		$page->assign('locale', $locale);
198
-		$page->assign('direction', $direction);
199
-
200
-		// Set body data-theme
201
-		$themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
202
-		$page->assign('enabledThemes', $themesService->getEnabledThemes());
203
-
204
-		if ($this->config->getSystemValueBool('installed', false)) {
205
-			if (empty(self::$versionHash)) {
206
-				$v = $this->appManager->getAppInstalledVersions();
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
-				\OC::$server->getGroupManager(),
229
-				\OC::$server->get(IniGetWrapper::class),
230
-				\OC::$server->getURLGenerator(),
231
-				\OC::$server->get(CapabilitiesManager::class),
232
-				\OCP\Server::get(IInitialStateService::class),
233
-				\OCP\Server::get(IProvider::class),
234
-				\OCP\Server::get(FilenameValidator::class),
235
-			);
236
-			$config = $jsConfigHelper->getConfig();
237
-			if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) {
238
-				$page->assign('inline_ocjs', $config);
239
-			} else {
240
-				$page->append('jsfiles', \OC::$server->getURLGenerator()->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
241
-			}
242
-		}
243
-		foreach ($jsFiles as $info) {
244
-			$web = $info[1];
245
-			$file = $info[2];
246
-			$page->append('jsfiles', $web . '/' . $file . $this->getVersionHashSuffix());
247
-		}
248
-
249
-		$request = \OCP\Server::get(IRequest::class);
250
-
251
-		try {
252
-			$pathInfo = $request->getPathInfo();
253
-		} catch (\Exception $e) {
254
-			$pathInfo = '';
255
-		}
256
-
257
-		// Do not initialise scss appdata until we have a fully installed instance
258
-		// Do not load scss for update, errors, installation or login page
259
-		if ($this->config->getSystemValueBool('installed', false)
260
-			&& !\OCP\Util::needUpgrade()
261
-			&& $pathInfo !== ''
262
-			&& !preg_match('/^\/login/', $pathInfo)
263
-			&& $renderAs !== TemplateResponse::RENDER_AS_ERROR
264
-		) {
265
-			$cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
266
-		} else {
267
-			// If we ignore the scss compiler,
268
-			// we need to load the guest css fallback
269
-			Util::addStyle('guest');
270
-			$cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
271
-		}
272
-
273
-		$page->assign('cssfiles', []);
274
-		$page->assign('printcssfiles', []);
275
-		$this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
276
-		foreach ($cssFiles as $info) {
277
-			$web = $info[1];
278
-			$file = $info[2];
279
-
280
-			if (str_ends_with($file, 'print.css')) {
281
-				$page->append('printcssfiles', $web . '/' . $file . $this->getVersionHashSuffix());
282
-			} else {
283
-				$suffix = $this->getVersionHashSuffix($web, $file);
284
-
285
-				if (!str_contains($file, '?v=')) {
286
-					$page->append('cssfiles', $web . '/' . $file . $suffix);
287
-				} else {
288
-					$page->append('cssfiles', $web . '/' . $file . '-' . substr($suffix, 3));
289
-				}
290
-			}
291
-		}
292
-
293
-		if ($request->isUserAgent([Request::USER_AGENT_CLIENT_IOS, Request::USER_AGENT_SAFARI, Request::USER_AGENT_SAFARI_MOBILE])) {
294
-			// Prevent auto zoom with iOS but still allow user zoom
295
-			// On chrome (and others) this does not work (will also disable user zoom)
296
-			$page->assign('viewport_maximum_scale', '1.0');
297
-		}
298
-
299
-		$page->assign('initialStates', $this->initialState->getInitialStates());
300
-
301
-		$page->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
302
-		$page->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
303
-
304
-		return $page;
305
-	}
306
-
307
-	protected function getVersionHashSuffix(string $path = '', string $file = ''): string {
308
-		if ($this->config->getSystemValueBool('debug', false)) {
309
-			// allows chrome workspace mapping in debug mode
310
-			return '';
311
-		}
312
-
313
-		if ($this->config->getSystemValueBool('installed', false) === false) {
314
-			// if not installed just return the version hash
315
-			return '?v=' . self::$versionHash;
316
-		}
317
-
318
-		$hash = false;
319
-		// Try the web-root first
320
-		if ($path !== '') {
321
-			$hash = $this->getVersionHashByPath($path);
322
-		}
323
-		// If not found try the file
324
-		if ($hash === false && $file !== '') {
325
-			$hash = $this->getVersionHashByPath($file);
326
-		}
327
-		// As a last resort we use the server version hash
328
-		if ($hash === false) {
329
-			$hash = self::$versionHash;
330
-		}
331
-
332
-		// The theming app is force-enabled thus the cache buster is always available
333
-		$themingSuffix = '-' . $this->config->getAppValue('theming', 'cachebuster', '0');
334
-
335
-		return '?v=' . $hash . $themingSuffix;
336
-	}
337
-
338
-	private function getVersionHashByPath(string $path): string|false {
339
-		if (array_key_exists($path, self::$cacheBusterCache) === false) {
340
-			// Not yet cached, so lets find the cache buster string
341
-			$appId = $this->getAppNamefromPath($path);
342
-			if ($appId === false) {
343
-				// No app Id could be guessed
344
-				return false;
345
-			}
346
-
347
-			if ($appId === 'core') {
348
-				// core is not a real app but the server itself
349
-				$hash = self::$versionHash;
350
-			} else {
351
-				$appVersion = $this->appManager->getAppVersion($appId);
352
-				// For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
353
-				if ($this->appManager->isShipped($appId)) {
354
-					$appVersion .= '-' . self::$versionHash;
355
-				}
356
-
357
-				$hash = substr(md5($appVersion), 0, 8);
358
-			}
359
-			self::$cacheBusterCache[$path] = $hash;
360
-		}
361
-
362
-		return self::$cacheBusterCache[$path];
363
-	}
364
-
365
-	public static function findStylesheetFiles(array $styles): array {
366
-		if (!self::$cssLocator) {
367
-			self::$cssLocator = \OCP\Server::get(CSSResourceLocator::class);
368
-		}
369
-		self::$cssLocator->find($styles);
370
-		return self::$cssLocator->getResources();
371
-	}
372
-
373
-	public function getAppNamefromPath(string $path): string|false {
374
-		if ($path !== '') {
375
-			$pathParts = explode('/', $path);
376
-			if ($pathParts[0] === 'css') {
377
-				// This is a scss request
378
-				return $pathParts[1];
379
-			} elseif ($pathParts[0] === 'core') {
380
-				return 'core';
381
-			}
382
-			return end($pathParts);
383
-		}
384
-		return false;
385
-	}
386
-
387
-	public static function findJavascriptFiles(array $scripts): array {
388
-		if (!self::$jsLocator) {
389
-			self::$jsLocator = \OCP\Server::get(JSResourceLocator::class);
390
-		}
391
-		self::$jsLocator->find($scripts);
392
-		return self::$jsLocator->getResources();
393
-	}
394
-
395
-	/**
396
-	 * Converts the absolute file path to a relative path from \OC::$SERVERROOT
397
-	 * @param string $filePath Absolute path
398
-	 * @return string Relative path
399
-	 * @throws \Exception If $filePath is not under \OC::$SERVERROOT
400
-	 */
401
-	public static function convertToRelativePath(string $filePath) {
402
-		$relativePath = explode(\OC::$SERVERROOT, $filePath);
403
-		if (count($relativePath) !== 2) {
404
-			throw new \Exception('$filePath is not under the \OC::$SERVERROOT');
405
-		}
406
-
407
-		return $relativePath[1];
408
-	}
38
+    private static string $versionHash = '';
39
+    /** @var string[] */
40
+    private static array $cacheBusterCache = [];
41
+
42
+    public static ?CSSResourceLocator $cssLocator = null;
43
+    public static ?JSResourceLocator $jsLocator = null;
44
+
45
+    public function __construct(
46
+        private IConfig $config,
47
+        private IAppManager $appManager,
48
+        private InitialStateService $initialState,
49
+        private INavigationManager $navigationManager,
50
+        private ITemplateManager $templateManager,
51
+        private ServerVersion $serverVersion,
52
+    ) {
53
+    }
54
+
55
+    public function getPageTemplate(string $renderAs, string $appId): ITemplate {
56
+        // Add fallback theming variables if not rendered as user
57
+        if ($renderAs !== TemplateResponse::RENDER_AS_USER) {
58
+            // TODO cache generated default theme if enabled for fallback if server is erroring ?
59
+            Util::addStyle('theming', 'default');
60
+        }
61
+
62
+        // Decide which page we show
63
+        switch ($renderAs) {
64
+            case TemplateResponse::RENDER_AS_USER:
65
+                $page = $this->templateManager->getTemplate('core', 'layout.user');
66
+                if (in_array(\OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) {
67
+                    $page->assign('bodyid', 'body-settings');
68
+                } else {
69
+                    $page->assign('bodyid', 'body-user');
70
+                }
71
+
72
+                $this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
73
+                $this->initialState->provideInitialState('core', 'apps', array_values($this->navigationManager->getAll()));
74
+
75
+                if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) {
76
+                    $this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
77
+                    $this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1));
78
+                    $this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
79
+                    Util::addScript('core', 'legacy-unified-search', 'core');
80
+                } else {
81
+                    Util::addScript('core', 'unified-search', 'core');
82
+                }
83
+
84
+                // Set logo link target
85
+                $logoUrl = $this->config->getSystemValueString('logo_url', '');
86
+                $page->assign('logoUrl', $logoUrl);
87
+
88
+                // Set default entry name
89
+                $defaultEntryId = $this->navigationManager->getDefaultEntryIdForUser();
90
+                $defaultEntry = $this->navigationManager->get($defaultEntryId);
91
+                $page->assign('defaultAppName', $defaultEntry['name'] ?? '');
92
+
93
+                // Add navigation entry
94
+                $page->assign('application', '');
95
+                $page->assign('appid', $appId);
96
+
97
+                $navigation = $this->navigationManager->getAll();
98
+                $page->assign('navigation', $navigation);
99
+                $settingsNavigation = $this->navigationManager->getAll('settings');
100
+                $this->initialState->provideInitialState('core', 'settingsNavEntries', $settingsNavigation);
101
+
102
+                foreach ($navigation as $entry) {
103
+                    if ($entry['active']) {
104
+                        $page->assign('application', $entry['name']);
105
+                        break;
106
+                    }
107
+                }
108
+
109
+                foreach ($settingsNavigation as $entry) {
110
+                    if ($entry['active']) {
111
+                        $page->assign('application', $entry['name']);
112
+                        break;
113
+                    }
114
+                }
115
+
116
+                $user = Server::get(IUserSession::class)->getUser();
117
+
118
+                if ($user === null) {
119
+                    $page->assign('user_uid', false);
120
+                    $page->assign('user_displayname', false);
121
+                    $page->assign('userAvatarSet', false);
122
+                    $page->assign('userStatus', false);
123
+                } else {
124
+                    $page->assign('user_uid', $user->getUID());
125
+                    $page->assign('user_displayname', $user->getDisplayName());
126
+                    $page->assign('userAvatarSet', true);
127
+                    $page->assign('userAvatarVersion', $this->config->getUserValue($user->getUID(), 'avatar', 'version', 0));
128
+                }
129
+                break;
130
+            case TemplateResponse::RENDER_AS_ERROR:
131
+                $page = $this->templateManager->getTemplate('core', 'layout.guest', '', false);
132
+                $page->assign('bodyid', 'body-login');
133
+                $page->assign('user_displayname', '');
134
+                $page->assign('user_uid', '');
135
+                break;
136
+            case TemplateResponse::RENDER_AS_GUEST:
137
+                $page = $this->templateManager->getTemplate('core', 'layout.guest');
138
+                Util::addStyle('guest');
139
+                $page->assign('bodyid', 'body-login');
140
+
141
+                $userDisplayName = false;
142
+                $user = Server::get(IUserSession::class)->getUser();
143
+                if ($user) {
144
+                    $userDisplayName = $user->getDisplayName();
145
+                }
146
+
147
+                $page->assign('enabledThemes', []);
148
+                if ($this->appManager->isEnabledForUser('theming') && class_exists('\OCA\Theming\Service\ThemesService')) {
149
+                    $themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
150
+                    $page->assign('enabledThemes', $themesService->getEnabledThemes());
151
+                }
152
+
153
+                $page->assign('user_displayname', $userDisplayName);
154
+                $page->assign('user_uid', \OC_User::getUser());
155
+                break;
156
+            case TemplateResponse::RENDER_AS_PUBLIC:
157
+                $page = $this->templateManager->getTemplate('core', 'layout.public');
158
+                $page->assign('appid', $appId);
159
+                $page->assign('bodyid', 'body-public');
160
+
161
+                // Set logo link target
162
+                $logoUrl = $this->config->getSystemValueString('logo_url', '');
163
+                $page->assign('logoUrl', $logoUrl);
164
+
165
+                $subscription = Server::get(IRegistry::class);
166
+                $showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true);
167
+                if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) {
168
+                    $showSimpleSignup = false;
169
+                }
170
+
171
+                $defaultSignUpLink = 'https://nextcloud.com/signup/';
172
+                $signUpLink = $this->config->getSystemValueString('registration_link', $defaultSignUpLink);
173
+                if ($signUpLink !== $defaultSignUpLink) {
174
+                    $showSimpleSignup = true;
175
+                }
176
+
177
+                if ($this->appManager->isEnabledForUser('registration')) {
178
+                    $urlGenerator = Server::get(IURLGenerator::class);
179
+                    $signUpLink = $urlGenerator->getAbsoluteURL('/index.php/apps/registration/');
180
+                }
181
+
182
+                $page->assign('showSimpleSignUpLink', $showSimpleSignup);
183
+                $page->assign('signUpLink', $signUpLink);
184
+                break;
185
+            default:
186
+                $page = $this->templateManager->getTemplate('core', 'layout.base');
187
+                break;
188
+        }
189
+        // Send the language, locale, and direction to our layouts
190
+        $l10nFactory = Server::get(IFactory::class);
191
+        $lang = $l10nFactory->findLanguage();
192
+        $locale = $l10nFactory->findLocale($lang);
193
+        $direction = $l10nFactory->getLanguageDirection($lang);
194
+
195
+        $lang = str_replace('_', '-', $lang);
196
+        $page->assign('language', $lang);
197
+        $page->assign('locale', $locale);
198
+        $page->assign('direction', $direction);
199
+
200
+        // Set body data-theme
201
+        $themesService = Server::get(\OCA\Theming\Service\ThemesService::class);
202
+        $page->assign('enabledThemes', $themesService->getEnabledThemes());
203
+
204
+        if ($this->config->getSystemValueBool('installed', false)) {
205
+            if (empty(self::$versionHash)) {
206
+                $v = $this->appManager->getAppInstalledVersions();
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
+                \OC::$server->getGroupManager(),
229
+                \OC::$server->get(IniGetWrapper::class),
230
+                \OC::$server->getURLGenerator(),
231
+                \OC::$server->get(CapabilitiesManager::class),
232
+                \OCP\Server::get(IInitialStateService::class),
233
+                \OCP\Server::get(IProvider::class),
234
+                \OCP\Server::get(FilenameValidator::class),
235
+            );
236
+            $config = $jsConfigHelper->getConfig();
237
+            if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) {
238
+                $page->assign('inline_ocjs', $config);
239
+            } else {
240
+                $page->append('jsfiles', \OC::$server->getURLGenerator()->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
241
+            }
242
+        }
243
+        foreach ($jsFiles as $info) {
244
+            $web = $info[1];
245
+            $file = $info[2];
246
+            $page->append('jsfiles', $web . '/' . $file . $this->getVersionHashSuffix());
247
+        }
248
+
249
+        $request = \OCP\Server::get(IRequest::class);
250
+
251
+        try {
252
+            $pathInfo = $request->getPathInfo();
253
+        } catch (\Exception $e) {
254
+            $pathInfo = '';
255
+        }
256
+
257
+        // Do not initialise scss appdata until we have a fully installed instance
258
+        // Do not load scss for update, errors, installation or login page
259
+        if ($this->config->getSystemValueBool('installed', false)
260
+            && !\OCP\Util::needUpgrade()
261
+            && $pathInfo !== ''
262
+            && !preg_match('/^\/login/', $pathInfo)
263
+            && $renderAs !== TemplateResponse::RENDER_AS_ERROR
264
+        ) {
265
+            $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
266
+        } else {
267
+            // If we ignore the scss compiler,
268
+            // we need to load the guest css fallback
269
+            Util::addStyle('guest');
270
+            $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
271
+        }
272
+
273
+        $page->assign('cssfiles', []);
274
+        $page->assign('printcssfiles', []);
275
+        $this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
276
+        foreach ($cssFiles as $info) {
277
+            $web = $info[1];
278
+            $file = $info[2];
279
+
280
+            if (str_ends_with($file, 'print.css')) {
281
+                $page->append('printcssfiles', $web . '/' . $file . $this->getVersionHashSuffix());
282
+            } else {
283
+                $suffix = $this->getVersionHashSuffix($web, $file);
284
+
285
+                if (!str_contains($file, '?v=')) {
286
+                    $page->append('cssfiles', $web . '/' . $file . $suffix);
287
+                } else {
288
+                    $page->append('cssfiles', $web . '/' . $file . '-' . substr($suffix, 3));
289
+                }
290
+            }
291
+        }
292
+
293
+        if ($request->isUserAgent([Request::USER_AGENT_CLIENT_IOS, Request::USER_AGENT_SAFARI, Request::USER_AGENT_SAFARI_MOBILE])) {
294
+            // Prevent auto zoom with iOS but still allow user zoom
295
+            // On chrome (and others) this does not work (will also disable user zoom)
296
+            $page->assign('viewport_maximum_scale', '1.0');
297
+        }
298
+
299
+        $page->assign('initialStates', $this->initialState->getInitialStates());
300
+
301
+        $page->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
302
+        $page->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
303
+
304
+        return $page;
305
+    }
306
+
307
+    protected function getVersionHashSuffix(string $path = '', string $file = ''): string {
308
+        if ($this->config->getSystemValueBool('debug', false)) {
309
+            // allows chrome workspace mapping in debug mode
310
+            return '';
311
+        }
312
+
313
+        if ($this->config->getSystemValueBool('installed', false) === false) {
314
+            // if not installed just return the version hash
315
+            return '?v=' . self::$versionHash;
316
+        }
317
+
318
+        $hash = false;
319
+        // Try the web-root first
320
+        if ($path !== '') {
321
+            $hash = $this->getVersionHashByPath($path);
322
+        }
323
+        // If not found try the file
324
+        if ($hash === false && $file !== '') {
325
+            $hash = $this->getVersionHashByPath($file);
326
+        }
327
+        // As a last resort we use the server version hash
328
+        if ($hash === false) {
329
+            $hash = self::$versionHash;
330
+        }
331
+
332
+        // The theming app is force-enabled thus the cache buster is always available
333
+        $themingSuffix = '-' . $this->config->getAppValue('theming', 'cachebuster', '0');
334
+
335
+        return '?v=' . $hash . $themingSuffix;
336
+    }
337
+
338
+    private function getVersionHashByPath(string $path): string|false {
339
+        if (array_key_exists($path, self::$cacheBusterCache) === false) {
340
+            // Not yet cached, so lets find the cache buster string
341
+            $appId = $this->getAppNamefromPath($path);
342
+            if ($appId === false) {
343
+                // No app Id could be guessed
344
+                return false;
345
+            }
346
+
347
+            if ($appId === 'core') {
348
+                // core is not a real app but the server itself
349
+                $hash = self::$versionHash;
350
+            } else {
351
+                $appVersion = $this->appManager->getAppVersion($appId);
352
+                // For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
353
+                if ($this->appManager->isShipped($appId)) {
354
+                    $appVersion .= '-' . self::$versionHash;
355
+                }
356
+
357
+                $hash = substr(md5($appVersion), 0, 8);
358
+            }
359
+            self::$cacheBusterCache[$path] = $hash;
360
+        }
361
+
362
+        return self::$cacheBusterCache[$path];
363
+    }
364
+
365
+    public static function findStylesheetFiles(array $styles): array {
366
+        if (!self::$cssLocator) {
367
+            self::$cssLocator = \OCP\Server::get(CSSResourceLocator::class);
368
+        }
369
+        self::$cssLocator->find($styles);
370
+        return self::$cssLocator->getResources();
371
+    }
372
+
373
+    public function getAppNamefromPath(string $path): string|false {
374
+        if ($path !== '') {
375
+            $pathParts = explode('/', $path);
376
+            if ($pathParts[0] === 'css') {
377
+                // This is a scss request
378
+                return $pathParts[1];
379
+            } elseif ($pathParts[0] === 'core') {
380
+                return 'core';
381
+            }
382
+            return end($pathParts);
383
+        }
384
+        return false;
385
+    }
386
+
387
+    public static function findJavascriptFiles(array $scripts): array {
388
+        if (!self::$jsLocator) {
389
+            self::$jsLocator = \OCP\Server::get(JSResourceLocator::class);
390
+        }
391
+        self::$jsLocator->find($scripts);
392
+        return self::$jsLocator->getResources();
393
+    }
394
+
395
+    /**
396
+     * Converts the absolute file path to a relative path from \OC::$SERVERROOT
397
+     * @param string $filePath Absolute path
398
+     * @return string Relative path
399
+     * @throws \Exception If $filePath is not under \OC::$SERVERROOT
400
+     */
401
+    public static function convertToRelativePath(string $filePath) {
402
+        $relativePath = explode(\OC::$SERVERROOT, $filePath);
403
+        if (count($relativePath) !== 2) {
404
+            throw new \Exception('$filePath is not under the \OC::$SERVERROOT');
405
+        }
406
+
407
+        return $relativePath[1];
408
+    }
409 409
 }
Please login to merge, or discard this patch.