Passed
Push — master ( f116c8...be892d )
by John
15:58 queued 12s
created
apps/theming/lib/Themes/CommonThemeTrait.php 1 patch
Indentation   +121 added lines, -121 removed lines patch added patch discarded remove patch
@@ -30,127 +30,127 @@
 block discarded – undo
30 30
 use OCA\Theming\Util;
31 31
 
32 32
 trait CommonThemeTrait {
33
-	public Util $util;
34
-
35
-	/**
36
-	 * Generate primary-related variables
37
-	 * This is shared between multiple themes because colorMainBackground and colorMainText
38
-	 * will change in between.
39
-	 */
40
-	protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array {
41
-		$colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
42
-		$colorPrimaryElement = $this->util->elementColor($this->primaryColor);
43
-		$colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
44
-
45
-		// primary related colours
46
-		return [
47
-			// invert filter if primary is too bright
48
-			// to be used for legacy reasons only. Use inline
49
-			// svg with proper css variable instead or material
50
-			// design icons.
51
-			// ⚠️ Using 'no' as a value to make sure we specify an
52
-			// invalid one with no fallback. 'unset' could here fallback to some
53
-			// other theme with media queries
54
-			'--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no',
55
-
56
-			'--color-primary' => $this->primaryColor,
57
-			'--color-primary-default' => $this->defaultPrimaryColor,
58
-			'--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
59
-			'--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
60
-			'--color-primary-light' => $colorPrimaryLight,
61
-			'--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20),
62
-			'--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
63
-			'--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
64
-
65
-			// used for buttons, inputs...
66
-			'--color-primary-element' => $colorPrimaryElement,
67
-			'--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
68
-			'--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
69
-			'--color-primary-element-light' => $colorPrimaryElementLight,
70
-			'--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20),
71
-			'--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
72
-			'--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
73
-
74
-			// to use like this: background-image: var(--gradient-primary-background);
75
-			'--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
76
-		];
77
-	}
78
-
79
-	/**
80
-	 * Generate admin theming background-related variables
81
-	 */
82
-	protected function generateGlobalBackgroundVariables(): array {
83
-		$backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor';
84
-		$hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader');
85
-
86
-		$variables = [];
87
-
88
-		// If primary as background has been request or if we have a custom primary colour
89
-		// let's not define the background image
90
-		if ($backgroundDeleted && $this->themingDefaults->isUserThemingDisabled()) {
91
-			$variables['--image-background-plain'] = 'true';
92
-			$variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary();
93
-		}
94
-
95
-		// Register image variables only if custom-defined
96
-		foreach (ImageManager::SupportedImageKeys as $image) {
97
-			if ($this->imageManager->hasImage($image)) {
98
-				$imageUrl = $this->imageManager->getImageUrl($image);
99
-				if ($image === 'background') {
100
-					// If background deleted is set, ignoring variable
101
-					if ($backgroundDeleted) {
102
-						continue;
103
-					}
104
-					$variables['--image-background-size'] = 'cover';
105
-				}
106
-				$variables["--image-$image"] = "url('" . $imageUrl . "')";
107
-			}
108
-		}
109
-
110
-		if ($hasCustomLogoHeader) {
111
-			$variables["--image-logoheader-custom"] = 'true';
112
-		}
113
-
114
-		return $variables;
115
-	}
116
-
117
-	/**
118
-	 * Generate user theming background-related variables
119
-	 */
120
-	protected function generateUserBackgroundVariables(): array {
121
-		$user = $this->userSession->getUser();
122
-		if ($user !== null
123
-			&& !$this->themingDefaults->isUserThemingDisabled()
124
-			&& $this->appManager->isEnabledForUser(Application::APP_ID)) {
125
-			$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
126
-			$currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
127
-
128
-			// The user uploaded a custom background
129
-			if ($themingBackground === 'custom') {
130
-				$cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
131
-				return [
132
-					'--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
133
-					// TODO: implement primary color from custom background --color-background-plain
134
-				];
135
-			}
33
+    public Util $util;
34
+
35
+    /**
36
+     * Generate primary-related variables
37
+     * This is shared between multiple themes because colorMainBackground and colorMainText
38
+     * will change in between.
39
+     */
40
+    protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array {
41
+        $colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80);
42
+        $colorPrimaryElement = $this->util->elementColor($this->primaryColor);
43
+        $colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80);
44
+
45
+        // primary related colours
46
+        return [
47
+            // invert filter if primary is too bright
48
+            // to be used for legacy reasons only. Use inline
49
+            // svg with proper css variable instead or material
50
+            // design icons.
51
+            // ⚠️ Using 'no' as a value to make sure we specify an
52
+            // invalid one with no fallback. 'unset' could here fallback to some
53
+            // other theme with media queries
54
+            '--primary-invert-if-bright' => $this->util->invertTextColor($this->primaryColor) ? 'invert(100%)' : 'no',
55
+
56
+            '--color-primary' => $this->primaryColor,
57
+            '--color-primary-default' => $this->defaultPrimaryColor,
58
+            '--color-primary-text' => $this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff',
59
+            '--color-primary-hover' => $this->util->mix($this->primaryColor, $colorMainBackground, 60),
60
+            '--color-primary-light' => $colorPrimaryLight,
61
+            '--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20),
62
+            '--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90),
63
+            '--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7),
64
+
65
+            // used for buttons, inputs...
66
+            '--color-primary-element' => $colorPrimaryElement,
67
+            '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff',
68
+            '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60),
69
+            '--color-primary-element-light' => $colorPrimaryElementLight,
70
+            '--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20),
71
+            '--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90),
72
+            '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7),
73
+
74
+            // to use like this: background-image: var(--gradient-primary-background);
75
+            '--gradient-primary-background' => 'linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%)',
76
+        ];
77
+    }
78
+
79
+    /**
80
+     * Generate admin theming background-related variables
81
+     */
82
+    protected function generateGlobalBackgroundVariables(): array {
83
+        $backgroundDeleted = $this->config->getAppValue(Application::APP_ID, 'backgroundMime', '') === 'backgroundColor';
84
+        $hasCustomLogoHeader = $this->imageManager->hasImage('logo') || $this->imageManager->hasImage('logoheader');
85
+
86
+        $variables = [];
87
+
88
+        // If primary as background has been request or if we have a custom primary colour
89
+        // let's not define the background image
90
+        if ($backgroundDeleted && $this->themingDefaults->isUserThemingDisabled()) {
91
+            $variables['--image-background-plain'] = 'true';
92
+            $variables['--color-background-plain'] = $this->themingDefaults->getColorPrimary();
93
+        }
94
+
95
+        // Register image variables only if custom-defined
96
+        foreach (ImageManager::SupportedImageKeys as $image) {
97
+            if ($this->imageManager->hasImage($image)) {
98
+                $imageUrl = $this->imageManager->getImageUrl($image);
99
+                if ($image === 'background') {
100
+                    // If background deleted is set, ignoring variable
101
+                    if ($backgroundDeleted) {
102
+                        continue;
103
+                    }
104
+                    $variables['--image-background-size'] = 'cover';
105
+                }
106
+                $variables["--image-$image"] = "url('" . $imageUrl . "')";
107
+            }
108
+        }
109
+
110
+        if ($hasCustomLogoHeader) {
111
+            $variables["--image-logoheader-custom"] = 'true';
112
+        }
113
+
114
+        return $variables;
115
+    }
116
+
117
+    /**
118
+     * Generate user theming background-related variables
119
+     */
120
+    protected function generateUserBackgroundVariables(): array {
121
+        $user = $this->userSession->getUser();
122
+        if ($user !== null
123
+            && !$this->themingDefaults->isUserThemingDisabled()
124
+            && $this->appManager->isEnabledForUser(Application::APP_ID)) {
125
+            $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', 'default');
126
+            $currentVersion = (int)$this->config->getUserValue($user->getUID(), Application::APP_ID, 'userCacheBuster', '0');
127
+
128
+            // The user uploaded a custom background
129
+            if ($themingBackground === 'custom') {
130
+                $cacheBuster = substr(sha1($user->getUID() . '_' . $currentVersion), 0, 8);
131
+                return [
132
+                    '--image-background' => "url('" . $this->urlGenerator->linkToRouteAbsolute('theming.userTheme.getBackground') . "?v=$cacheBuster')",
133
+                    // TODO: implement primary color from custom background --color-background-plain
134
+                ];
135
+            }
136 136
 			
137
-			// The user picked a shipped background
138
-			if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
139
-				return [
140
-					'--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
141
-					'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
142
-				];
143
-			}
137
+            // The user picked a shipped background
138
+            if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground])) {
139
+                return [
140
+                    '--image-background' => "url('" . $this->urlGenerator->linkTo(Application::APP_ID, "/img/background/$themingBackground") . "')",
141
+                    '--color-background-plain' => $this->themingDefaults->getColorPrimary(),
142
+                ];
143
+            }
144 144
 			
145
-			// The user picked a static colour
146
-			if (substr($themingBackground, 0, 1) === '#') {
147
-				return [
148
-					'--image-background' => 'no',
149
-					'--color-background-plain' => $this->themingDefaults->getColorPrimary(),
150
-				];
151
-			}
152
-		}
153
-
154
-		return [];
155
-	}
145
+            // The user picked a static colour
146
+            if (substr($themingBackground, 0, 1) === '#') {
147
+                return [
148
+                    '--image-background' => 'no',
149
+                    '--color-background-plain' => $this->themingDefaults->getColorPrimary(),
150
+                ];
151
+            }
152
+        }
153
+
154
+        return [];
155
+    }
156 156
 }
Please login to merge, or discard this patch.
apps/theming/lib/Themes/DefaultTheme.php 1 patch
Indentation   +170 added lines, -170 removed lines patch added patch discarded remove patch
@@ -36,174 +36,174 @@
 block discarded – undo
36 36
 use OCP\IUserSession;
37 37
 
38 38
 class DefaultTheme implements ITheme {
39
-	use CommonThemeTrait;
40
-
41
-	public Util $util;
42
-	public ThemingDefaults $themingDefaults;
43
-	public IUserSession $userSession;
44
-	public IURLGenerator $urlGenerator;
45
-	public ImageManager $imageManager;
46
-	public IConfig $config;
47
-	public IL10N $l;
48
-	public IAppManager $appManager;
49
-
50
-	public string $defaultPrimaryColor;
51
-	public string $primaryColor;
52
-
53
-	public function __construct(Util $util,
54
-								ThemingDefaults $themingDefaults,
55
-								IUserSession $userSession,
56
-								IURLGenerator $urlGenerator,
57
-								ImageManager $imageManager,
58
-								IConfig $config,
59
-								IL10N $l,
60
-								IAppManager $appManager) {
61
-		$this->util = $util;
62
-		$this->themingDefaults = $themingDefaults;
63
-		$this->userSession = $userSession;
64
-		$this->urlGenerator = $urlGenerator;
65
-		$this->imageManager = $imageManager;
66
-		$this->config = $config;
67
-		$this->l = $l;
68
-		$this->appManager = $appManager;
69
-
70
-		$this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
71
-		$this->primaryColor = $this->themingDefaults->getColorPrimary();
72
-
73
-		// Override default defaultPrimaryColor if set to improve accessibility
74
-		if ($this->primaryColor === BackgroundService::DEFAULT_COLOR) {
75
-			$this->primaryColor = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
76
-		}
77
-	}
78
-
79
-	public function getId(): string {
80
-		return 'default';
81
-	}
82
-
83
-	public function getType(): int {
84
-		return ITheme::TYPE_THEME;
85
-	}
86
-
87
-	public function getTitle(): string {
88
-		return $this->l->t('System default theme');
89
-	}
90
-
91
-	public function getEnableLabel(): string {
92
-		return $this->l->t('Enable the system default');
93
-	}
94
-
95
-	public function getDescription(): string {
96
-		return $this->l->t('Using the default system appearance.');
97
-	}
98
-
99
-	public function getMediaQuery(): string {
100
-		return '';
101
-	}
102
-
103
-	public function getCSSVariables(): array {
104
-		$colorMainText = '#222222';
105
-		$colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText));
106
-		$colorTextMaxcontrast = $this->util->lighten($colorMainText, 33);
107
-		$colorMainBackground = '#ffffff';
108
-		$colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
109
-		$colorBoxShadow = $this->util->darken($colorMainBackground, 70);
110
-		$colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
111
-
112
-		$variables = [
113
-			'--color-main-background' => $colorMainBackground,
114
-			'--color-main-background-rgb' => $colorMainBackgroundRGB,
115
-			'--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
116
-			'--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)',
117
-			'--filter-background-blur' => 'blur(25px)',
118
-
119
-			// to use like this: background-image: linear-gradient(0, var('--gradient-main-background));
120
-			'--gradient-main-background' => 'var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%',
121
-
122
-			// used for different active/hover/focus/disabled states
123
-			'--color-background-hover' => $this->util->darken($colorMainBackground, 4),
124
-			'--color-background-dark' => $this->util->darken($colorMainBackground, 7),
125
-			'--color-background-darker' => $this->util->darken($colorMainBackground, 14),
126
-
127
-			'--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
128
-			'--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
129
-
130
-			// max contrast for WCAG compliance
131
-			'--color-main-text' => $colorMainText,
132
-			'--color-text-maxcontrast' => $colorTextMaxcontrast,
133
-			'--color-text-maxcontrast-default' => $colorTextMaxcontrast,
134
-			'--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7),
135
-			'--color-text-light' => $colorMainText,
136
-			'--color-text-lighter' => $this->util->lighten($colorMainText, 33),
137
-
138
-			'--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)',
139
-
140
-			// info/warning/success feedback colours
141
-			'--color-error' => '#e9322d',
142
-			'--color-error-rgb' => join(',', $this->util->hexToRGB('#e9322d')),
143
-			'--color-error-hover' => $this->util->mix('#e9322d', $colorMainBackground, 60),
144
-			'--color-warning' => '#eca700',
145
-			'--color-warning-rgb' => join(',', $this->util->hexToRGB('#eca700')),
146
-			'--color-warning-hover' => $this->util->mix('#eca700', $colorMainBackground, 60),
147
-			'--color-success' => '#46ba61',
148
-			'--color-success-rgb' => join(',', $this->util->hexToRGB('#46ba61')),
149
-			'--color-success-hover' => $this->util->mix('#46ba61', $colorMainBackground, 60),
150
-
151
-			// used for the icon loading animation
152
-			'--color-loading-light' => '#cccccc',
153
-			'--color-loading-dark' => '#444444',
154
-
155
-			'--color-box-shadow-rgb' => $colorBoxShadowRGB,
156
-			'--color-box-shadow' => "rgba(var(--color-box-shadow-rgb), 0.5)",
157
-
158
-			'--color-border' => $this->util->darken($colorMainBackground, 7),
159
-			'--color-border-dark' => $this->util->darken($colorMainBackground, 14),
160
-
161
-			'--font-face' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
162
-			'--default-font-size' => '15px',
163
-
164
-			// TODO: support "(prefers-reduced-motion)"
165
-			'--animation-quick' => '100ms',
166
-			'--animation-slow' => '300ms',
167
-
168
-			// Default variables --------------------------------------------
169
-			'--border-radius' => '3px',
170
-			'--border-radius-large' => '10px',
171
-			// pill-style button, value is large so big buttons also have correct roundness
172
-			'--border-radius-pill' => '100px',
173
-
174
-			'--default-clickable-area' => '44px',
175
-			'--default-line-height' => '24px',
176
-			'--default-grid-baseline' => '4px',
177
-
178
-			// various structure data
179
-			'--header-height' => '50px',
180
-			'--navigation-width' => '300px',
181
-			'--sidebar-min-width' => '300px',
182
-			'--sidebar-max-width' => '500px',
183
-			'--list-min-width' => '200px',
184
-			'--list-max-width' => '300px',
185
-			'--header-menu-item-height' => '44px',
186
-			'--header-menu-profile-item-height' => '66px',
187
-
188
-			// mobile. Keep in sync with core/js/js.js
189
-			'--breakpoint-mobile' => '1024px',
190
-			'--background-invert-if-dark' => 'no',
191
-			'--background-invert-if-bright' => 'invert(100%)',
192
-
193
-			// Default last fallback values
194
-			'--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
195
-			'--color-background-plain' => $this->defaultPrimaryColor,
196
-		];
197
-
198
-		// Primary variables
199
-		$variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText));
200
-		$variables = array_merge($variables, $this->generateGlobalBackgroundVariables());
201
-		$variables = array_merge($variables, $this->generateUserBackgroundVariables());
202
-
203
-		return $variables;
204
-	}
205
-
206
-	public function getCustomCss(): string {
207
-		return '';
208
-	}
39
+    use CommonThemeTrait;
40
+
41
+    public Util $util;
42
+    public ThemingDefaults $themingDefaults;
43
+    public IUserSession $userSession;
44
+    public IURLGenerator $urlGenerator;
45
+    public ImageManager $imageManager;
46
+    public IConfig $config;
47
+    public IL10N $l;
48
+    public IAppManager $appManager;
49
+
50
+    public string $defaultPrimaryColor;
51
+    public string $primaryColor;
52
+
53
+    public function __construct(Util $util,
54
+                                ThemingDefaults $themingDefaults,
55
+                                IUserSession $userSession,
56
+                                IURLGenerator $urlGenerator,
57
+                                ImageManager $imageManager,
58
+                                IConfig $config,
59
+                                IL10N $l,
60
+                                IAppManager $appManager) {
61
+        $this->util = $util;
62
+        $this->themingDefaults = $themingDefaults;
63
+        $this->userSession = $userSession;
64
+        $this->urlGenerator = $urlGenerator;
65
+        $this->imageManager = $imageManager;
66
+        $this->config = $config;
67
+        $this->l = $l;
68
+        $this->appManager = $appManager;
69
+
70
+        $this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
71
+        $this->primaryColor = $this->themingDefaults->getColorPrimary();
72
+
73
+        // Override default defaultPrimaryColor if set to improve accessibility
74
+        if ($this->primaryColor === BackgroundService::DEFAULT_COLOR) {
75
+            $this->primaryColor = BackgroundService::DEFAULT_ACCESSIBLE_COLOR;
76
+        }
77
+    }
78
+
79
+    public function getId(): string {
80
+        return 'default';
81
+    }
82
+
83
+    public function getType(): int {
84
+        return ITheme::TYPE_THEME;
85
+    }
86
+
87
+    public function getTitle(): string {
88
+        return $this->l->t('System default theme');
89
+    }
90
+
91
+    public function getEnableLabel(): string {
92
+        return $this->l->t('Enable the system default');
93
+    }
94
+
95
+    public function getDescription(): string {
96
+        return $this->l->t('Using the default system appearance.');
97
+    }
98
+
99
+    public function getMediaQuery(): string {
100
+        return '';
101
+    }
102
+
103
+    public function getCSSVariables(): array {
104
+        $colorMainText = '#222222';
105
+        $colorMainTextRgb = join(',', $this->util->hexToRGB($colorMainText));
106
+        $colorTextMaxcontrast = $this->util->lighten($colorMainText, 33);
107
+        $colorMainBackground = '#ffffff';
108
+        $colorMainBackgroundRGB = join(',', $this->util->hexToRGB($colorMainBackground));
109
+        $colorBoxShadow = $this->util->darken($colorMainBackground, 70);
110
+        $colorBoxShadowRGB = join(',', $this->util->hexToRGB($colorBoxShadow));
111
+
112
+        $variables = [
113
+            '--color-main-background' => $colorMainBackground,
114
+            '--color-main-background-rgb' => $colorMainBackgroundRGB,
115
+            '--color-main-background-translucent' => 'rgba(var(--color-main-background-rgb), .97)',
116
+            '--color-main-background-blur' => 'rgba(var(--color-main-background-rgb), .8)',
117
+            '--filter-background-blur' => 'blur(25px)',
118
+
119
+            // to use like this: background-image: linear-gradient(0, var('--gradient-main-background));
120
+            '--gradient-main-background' => 'var(--color-main-background) 0%, var(--color-main-background-translucent) 85%, transparent 100%',
121
+
122
+            // used for different active/hover/focus/disabled states
123
+            '--color-background-hover' => $this->util->darken($colorMainBackground, 4),
124
+            '--color-background-dark' => $this->util->darken($colorMainBackground, 7),
125
+            '--color-background-darker' => $this->util->darken($colorMainBackground, 14),
126
+
127
+            '--color-placeholder-light' => $this->util->darken($colorMainBackground, 10),
128
+            '--color-placeholder-dark' => $this->util->darken($colorMainBackground, 20),
129
+
130
+            // max contrast for WCAG compliance
131
+            '--color-main-text' => $colorMainText,
132
+            '--color-text-maxcontrast' => $colorTextMaxcontrast,
133
+            '--color-text-maxcontrast-default' => $colorTextMaxcontrast,
134
+            '--color-text-maxcontrast-background-blur' => $this->util->darken($colorTextMaxcontrast, 7),
135
+            '--color-text-light' => $colorMainText,
136
+            '--color-text-lighter' => $this->util->lighten($colorMainText, 33),
137
+
138
+            '--color-scrollbar' => 'rgba(' . $colorMainTextRgb . ', .15)',
139
+
140
+            // info/warning/success feedback colours
141
+            '--color-error' => '#e9322d',
142
+            '--color-error-rgb' => join(',', $this->util->hexToRGB('#e9322d')),
143
+            '--color-error-hover' => $this->util->mix('#e9322d', $colorMainBackground, 60),
144
+            '--color-warning' => '#eca700',
145
+            '--color-warning-rgb' => join(',', $this->util->hexToRGB('#eca700')),
146
+            '--color-warning-hover' => $this->util->mix('#eca700', $colorMainBackground, 60),
147
+            '--color-success' => '#46ba61',
148
+            '--color-success-rgb' => join(',', $this->util->hexToRGB('#46ba61')),
149
+            '--color-success-hover' => $this->util->mix('#46ba61', $colorMainBackground, 60),
150
+
151
+            // used for the icon loading animation
152
+            '--color-loading-light' => '#cccccc',
153
+            '--color-loading-dark' => '#444444',
154
+
155
+            '--color-box-shadow-rgb' => $colorBoxShadowRGB,
156
+            '--color-box-shadow' => "rgba(var(--color-box-shadow-rgb), 0.5)",
157
+
158
+            '--color-border' => $this->util->darken($colorMainBackground, 7),
159
+            '--color-border-dark' => $this->util->darken($colorMainBackground, 14),
160
+
161
+            '--font-face' => "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', Arial, sans-serif, 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
162
+            '--default-font-size' => '15px',
163
+
164
+            // TODO: support "(prefers-reduced-motion)"
165
+            '--animation-quick' => '100ms',
166
+            '--animation-slow' => '300ms',
167
+
168
+            // Default variables --------------------------------------------
169
+            '--border-radius' => '3px',
170
+            '--border-radius-large' => '10px',
171
+            // pill-style button, value is large so big buttons also have correct roundness
172
+            '--border-radius-pill' => '100px',
173
+
174
+            '--default-clickable-area' => '44px',
175
+            '--default-line-height' => '24px',
176
+            '--default-grid-baseline' => '4px',
177
+
178
+            // various structure data
179
+            '--header-height' => '50px',
180
+            '--navigation-width' => '300px',
181
+            '--sidebar-min-width' => '300px',
182
+            '--sidebar-max-width' => '500px',
183
+            '--list-min-width' => '200px',
184
+            '--list-max-width' => '300px',
185
+            '--header-menu-item-height' => '44px',
186
+            '--header-menu-profile-item-height' => '66px',
187
+
188
+            // mobile. Keep in sync with core/js/js.js
189
+            '--breakpoint-mobile' => '1024px',
190
+            '--background-invert-if-dark' => 'no',
191
+            '--background-invert-if-bright' => 'invert(100%)',
192
+
193
+            // Default last fallback values
194
+            '--image-background' => "url('" . $this->urlGenerator->imagePath('core', 'app-background.jpg') . "')",
195
+            '--color-background-plain' => $this->defaultPrimaryColor,
196
+        ];
197
+
198
+        // Primary variables
199
+        $variables = array_merge($variables, $this->generatePrimaryVariables($colorMainBackground, $colorMainText));
200
+        $variables = array_merge($variables, $this->generateGlobalBackgroundVariables());
201
+        $variables = array_merge($variables, $this->generateUserBackgroundVariables());
202
+
203
+        return $variables;
204
+    }
205
+
206
+    public function getCustomCss(): string {
207
+        return '';
208
+    }
209 209
 }
Please login to merge, or discard this patch.
apps/theming/lib/Settings/Personal.php 1 patch
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -36,69 +36,69 @@
 block discarded – undo
36 36
 
37 37
 class Personal implements ISettings {
38 38
 
39
-	protected string $appName;
40
-	private IConfig $config;
41
-	private ThemesService $themesService;
42
-	private IInitialState $initialStateService;
43
-	private ThemingDefaults $themingDefaults;
39
+    protected string $appName;
40
+    private IConfig $config;
41
+    private ThemesService $themesService;
42
+    private IInitialState $initialStateService;
43
+    private ThemingDefaults $themingDefaults;
44 44
 
45
-	public function __construct(string $appName,
46
-								IConfig $config,
47
-								ThemesService $themesService,
48
-								IInitialState $initialStateService,
49
-								ThemingDefaults $themingDefaults) {
50
-		$this->appName = $appName;
51
-		$this->config = $config;
52
-		$this->themesService = $themesService;
53
-		$this->initialStateService = $initialStateService;
54
-		$this->themingDefaults = $themingDefaults;
55
-	}
45
+    public function __construct(string $appName,
46
+                                IConfig $config,
47
+                                ThemesService $themesService,
48
+                                IInitialState $initialStateService,
49
+                                ThemingDefaults $themingDefaults) {
50
+        $this->appName = $appName;
51
+        $this->config = $config;
52
+        $this->themesService = $themesService;
53
+        $this->initialStateService = $initialStateService;
54
+        $this->themingDefaults = $themingDefaults;
55
+    }
56 56
 
57
-	public function getForm(): TemplateResponse {
58
-		$enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
57
+    public function getForm(): TemplateResponse {
58
+        $enforcedTheme = $this->config->getSystemValueString('enforce_theme', '');
59 59
 
60
-		$themes = array_map(function($theme) {
61
-			return [
62
-				'id' => $theme->getId(),
63
-				'type' => $theme->getType(),
64
-				'title' => $theme->getTitle(),
65
-				'enableLabel' => $theme->getEnableLabel(),
66
-				'description' => $theme->getDescription(),
67
-				'enabled' => $this->themesService->isEnabled($theme),
68
-			];
69
-		}, $this->themesService->getThemes());
60
+        $themes = array_map(function($theme) {
61
+            return [
62
+                'id' => $theme->getId(),
63
+                'type' => $theme->getType(),
64
+                'title' => $theme->getTitle(),
65
+                'enableLabel' => $theme->getEnableLabel(),
66
+                'description' => $theme->getDescription(),
67
+                'enabled' => $this->themesService->isEnabled($theme),
68
+            ];
69
+        }, $this->themesService->getThemes());
70 70
 
71
-		if ($enforcedTheme !== '') {
72
-			$themes = array_filter($themes, function($theme) use ($enforcedTheme) {
73
-				return $theme['type'] !== ITheme::TYPE_THEME || $theme['id'] === $enforcedTheme;
74
-			});
75
-		}
71
+        if ($enforcedTheme !== '') {
72
+            $themes = array_filter($themes, function($theme) use ($enforcedTheme) {
73
+                return $theme['type'] !== ITheme::TYPE_THEME || $theme['id'] === $enforcedTheme;
74
+            });
75
+        }
76 76
 
77
-		$this->initialStateService->provideInitialState('themes', array_values($themes));
78
-		$this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme);
79
-		$this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled());
80
-		Util::addScript($this->appName, 'theming-settings');
77
+        $this->initialStateService->provideInitialState('themes', array_values($themes));
78
+        $this->initialStateService->provideInitialState('enforceTheme', $enforcedTheme);
79
+        $this->initialStateService->provideInitialState('isUserThemingDisabled', $this->themingDefaults->isUserThemingDisabled());
80
+        Util::addScript($this->appName, 'theming-settings');
81 81
 
82
-		return new TemplateResponse($this->appName, 'settings-personal');
83
-	}
82
+        return new TemplateResponse($this->appName, 'settings-personal');
83
+    }
84 84
 
85
-	/**
86
-	 * @return string the section ID, e.g. 'sharing'
87
-	 * @since 9.1
88
-	 */
89
-	public function getSection(): string {
90
-		return $this->appName;
91
-	}
85
+    /**
86
+     * @return string the section ID, e.g. 'sharing'
87
+     * @since 9.1
88
+     */
89
+    public function getSection(): string {
90
+        return $this->appName;
91
+    }
92 92
 
93
-	/**
94
-	 * @return int whether the form should be rather on the top or bottom of
95
-	 * the admin section. The forms are arranged in ascending order of the
96
-	 * priority values. It is required to return a value between 0 and 100.
97
-	 *
98
-	 * E.g.: 70
99
-	 * @since 9.1
100
-	 */
101
-	public function getPriority(): int {
102
-		return 40;
103
-	}
93
+    /**
94
+     * @return int whether the form should be rather on the top or bottom of
95
+     * the admin section. The forms are arranged in ascending order of the
96
+     * priority values. It is required to return a value between 0 and 100.
97
+     *
98
+     * E.g.: 70
99
+     * @since 9.1
100
+     */
101
+    public function getPriority(): int {
102
+        return 40;
103
+    }
104 104
 }
Please login to merge, or discard this patch.
apps/theming/lib/Settings/Admin.php 1 patch
Indentation   +71 added lines, -71 removed lines patch added patch discarded remove patch
@@ -36,83 +36,83 @@
 block discarded – undo
36 36
 use OCP\Settings\IDelegatedSettings;
37 37
 
38 38
 class Admin implements IDelegatedSettings {
39
-	private string $appName;
40
-	private IConfig $config;
41
-	private IL10N $l;
42
-	private ThemingDefaults $themingDefaults;
43
-	private IURLGenerator $urlGenerator;
44
-	private ImageManager $imageManager;
39
+    private string $appName;
40
+    private IConfig $config;
41
+    private IL10N $l;
42
+    private ThemingDefaults $themingDefaults;
43
+    private IURLGenerator $urlGenerator;
44
+    private ImageManager $imageManager;
45 45
 
46
-	public function __construct(string $appName,
47
-								IConfig $config,
48
-								IL10N $l,
49
-								ThemingDefaults $themingDefaults,
50
-								IURLGenerator $urlGenerator,
51
-								ImageManager $imageManager) {
52
-		$this->appName = $appName;
53
-		$this->config = $config;
54
-		$this->l = $l;
55
-		$this->themingDefaults = $themingDefaults;
56
-		$this->urlGenerator = $urlGenerator;
57
-		$this->imageManager = $imageManager;
58
-	}
46
+    public function __construct(string $appName,
47
+                                IConfig $config,
48
+                                IL10N $l,
49
+                                ThemingDefaults $themingDefaults,
50
+                                IURLGenerator $urlGenerator,
51
+                                ImageManager $imageManager) {
52
+        $this->appName = $appName;
53
+        $this->config = $config;
54
+        $this->l = $l;
55
+        $this->themingDefaults = $themingDefaults;
56
+        $this->urlGenerator = $urlGenerator;
57
+        $this->imageManager = $imageManager;
58
+    }
59 59
 
60
-	/**
61
-	 * @return TemplateResponse
62
-	 */
63
-	public function getForm(): TemplateResponse {
64
-		$themable = true;
65
-		$errorMessage = '';
66
-		$theme = $this->config->getSystemValue('theme', '');
67
-		if ($theme !== '') {
68
-			$themable = false;
69
-			$errorMessage = $this->l->t('You are already using a custom theme. Theming app settings might be overwritten by that.');
70
-		}
60
+    /**
61
+     * @return TemplateResponse
62
+     */
63
+    public function getForm(): TemplateResponse {
64
+        $themable = true;
65
+        $errorMessage = '';
66
+        $theme = $this->config->getSystemValue('theme', '');
67
+        if ($theme !== '') {
68
+            $themable = false;
69
+            $errorMessage = $this->l->t('You are already using a custom theme. Theming app settings might be overwritten by that.');
70
+        }
71 71
 
72
-		$parameters = [
73
-			'themable' => $themable,
74
-			'errorMessage' => $errorMessage,
75
-			'name' => $this->themingDefaults->getEntity(),
76
-			'url' => $this->themingDefaults->getBaseUrl(),
77
-			'slogan' => $this->themingDefaults->getSlogan(),
78
-			'color' => $this->themingDefaults->getDefaultColorPrimary(),
79
-			'uploadLogoRoute' => $this->urlGenerator->linkToRoute('theming.Theming.uploadImage'),
80
-			'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
81
-			'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
82
-			'images' => $this->imageManager->getCustomImages(),
83
-			'imprintUrl' => $this->themingDefaults->getImprintUrl(),
84
-			'privacyUrl' => $this->themingDefaults->getPrivacyUrl(),
85
-			'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(),
86
-		];
72
+        $parameters = [
73
+            'themable' => $themable,
74
+            'errorMessage' => $errorMessage,
75
+            'name' => $this->themingDefaults->getEntity(),
76
+            'url' => $this->themingDefaults->getBaseUrl(),
77
+            'slogan' => $this->themingDefaults->getSlogan(),
78
+            'color' => $this->themingDefaults->getDefaultColorPrimary(),
79
+            'uploadLogoRoute' => $this->urlGenerator->linkToRoute('theming.Theming.uploadImage'),
80
+            'canThemeIcons' => $this->imageManager->shouldReplaceIcons(),
81
+            'iconDocs' => $this->urlGenerator->linkToDocs('admin-theming-icons'),
82
+            'images' => $this->imageManager->getCustomImages(),
83
+            'imprintUrl' => $this->themingDefaults->getImprintUrl(),
84
+            'privacyUrl' => $this->themingDefaults->getPrivacyUrl(),
85
+            'userThemingDisabled' => $this->themingDefaults->isUserThemingDisabled(),
86
+        ];
87 87
 
88
-		return new TemplateResponse($this->appName, 'settings-admin', $parameters, '');
89
-	}
88
+        return new TemplateResponse($this->appName, 'settings-admin', $parameters, '');
89
+    }
90 90
 
91
-	/**
92
-	 * @return string the section ID, e.g. 'sharing'
93
-	 */
94
-	public function getSection(): string {
95
-		return $this->appName;
96
-	}
91
+    /**
92
+     * @return string the section ID, e.g. 'sharing'
93
+     */
94
+    public function getSection(): string {
95
+        return $this->appName;
96
+    }
97 97
 
98
-	/**
99
-	 * @return int whether the form should be rather on the top or bottom of
100
-	 * the admin section. The forms are arranged in ascending order of the
101
-	 * priority values. It is required to return a value between 0 and 100.
102
-	 *
103
-	 * E.g.: 70
104
-	 */
105
-	public function getPriority(): int {
106
-		return 5;
107
-	}
98
+    /**
99
+     * @return int whether the form should be rather on the top or bottom of
100
+     * the admin section. The forms are arranged in ascending order of the
101
+     * priority values. It is required to return a value between 0 and 100.
102
+     *
103
+     * E.g.: 70
104
+     */
105
+    public function getPriority(): int {
106
+        return 5;
107
+    }
108 108
 
109
-	public function getName(): ?string {
110
-		return null; // Only one setting in this section
111
-	}
109
+    public function getName(): ?string {
110
+        return null; // Only one setting in this section
111
+    }
112 112
 
113
-	public function getAuthorizedAppConfig(): array {
114
-		return [
115
-			$this->appName => '/.*/',
116
-		];
117
-	}
113
+    public function getAuthorizedAppConfig(): array {
114
+        return [
115
+            $this->appName => '/.*/',
116
+        ];
117
+    }
118 118
 }
Please login to merge, or discard this patch.
apps/theming/lib/ImageManager.php 1 patch
Indentation   +281 added lines, -281 removed lines patch added patch discarded remove patch
@@ -45,285 +45,285 @@
 block discarded – undo
45 45
 use OCP\IURLGenerator;
46 46
 
47 47
 class ImageManager {
48
-	public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
49
-
50
-	/** @var IConfig */
51
-	private $config;
52
-	/** @var IAppData */
53
-	private $appData;
54
-	/** @var IURLGenerator */
55
-	private $urlGenerator;
56
-	/** @var array */
57
-	/** @var ICacheFactory */
58
-	private $cacheFactory;
59
-	/** @var ILogger */
60
-	private $logger;
61
-	/** @var ITempManager */
62
-	private $tempManager;
63
-
64
-	public function __construct(IConfig $config,
65
-								IAppData $appData,
66
-								IURLGenerator $urlGenerator,
67
-								ICacheFactory $cacheFactory,
68
-								ILogger $logger,
69
-								ITempManager $tempManager) {
70
-		$this->config = $config;
71
-		$this->urlGenerator = $urlGenerator;
72
-		$this->cacheFactory = $cacheFactory;
73
-		$this->logger = $logger;
74
-		$this->tempManager = $tempManager;
75
-		$this->appData = $appData;
76
-	}
77
-
78
-	public function getImageUrl(string $key, bool $useSvg = true): string {
79
-		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
80
-		if ($this->hasImage($key)) {
81
-			return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
82
-		}
83
-
84
-		switch ($key) {
85
-			case 'logo':
86
-			case 'logoheader':
87
-			case 'favicon':
88
-				return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
89
-			case 'background':
90
-				return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
91
-		}
92
-		return '';
93
-	}
94
-
95
-	public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
96
-		return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
97
-	}
98
-
99
-	/**
100
-	 * @param string $key
101
-	 * @param bool $useSvg
102
-	 * @return ISimpleFile
103
-	 * @throws NotFoundException
104
-	 * @throws NotPermittedException
105
-	 */
106
-	public function getImage(string $key, bool $useSvg = true): ISimpleFile {
107
-		$logo = $this->config->getAppValue('theming', $key . 'Mime', '');
108
-		$folder = $this->getRootFolder()->getFolder('images');
109
-
110
-		if ($logo === '' || !$folder->fileExists($key)) {
111
-			throw new NotFoundException();
112
-		}
113
-
114
-		if (!$useSvg && $this->shouldReplaceIcons()) {
115
-			if (!$folder->fileExists($key . '.png')) {
116
-				try {
117
-					$finalIconFile = new \Imagick();
118
-					$finalIconFile->setBackgroundColor('none');
119
-					$finalIconFile->readImageBlob($folder->getFile($key)->getContent());
120
-					$finalIconFile->setImageFormat('png32');
121
-					$pngFile = $folder->newFile($key . '.png');
122
-					$pngFile->putContent($finalIconFile->getImageBlob());
123
-					return $pngFile;
124
-				} catch (\ImagickException $e) {
125
-					$this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
126
-				}
127
-			} else {
128
-				return $folder->getFile($key . '.png');
129
-			}
130
-		}
131
-
132
-		return $folder->getFile($key);
133
-	}
134
-
135
-	public function hasImage(string $key): bool {
136
-		$mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
137
-		return $mimeSetting !== '';
138
-	}
139
-
140
-	/**
141
-	 * @return array<string, array{mime: string, url: string}>
142
-	 */
143
-	public function getCustomImages(): array {
144
-		$images = [];
145
-		foreach ($this::SupportedImageKeys as $key) {
146
-			$images[$key] = [
147
-				'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
148
-				'url' => $this->getImageUrl($key),
149
-			];
150
-		}
151
-		return $images;
152
-	}
153
-
154
-	/**
155
-	 * Get folder for current theming files
156
-	 *
157
-	 * @return ISimpleFolder
158
-	 * @throws NotPermittedException
159
-	 */
160
-	public function getCacheFolder(): ISimpleFolder {
161
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
162
-		try {
163
-			$folder = $this->getRootFolder()->getFolder($cacheBusterValue);
164
-		} catch (NotFoundException $e) {
165
-			$folder = $this->getRootFolder()->newFolder($cacheBusterValue);
166
-			$this->cleanup();
167
-		}
168
-		return $folder;
169
-	}
170
-
171
-	/**
172
-	 * Get a file from AppData
173
-	 *
174
-	 * @param string $filename
175
-	 * @throws NotFoundException
176
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
177
-	 * @throws NotPermittedException
178
-	 */
179
-	public function getCachedImage(string $filename): ISimpleFile {
180
-		$currentFolder = $this->getCacheFolder();
181
-		return $currentFolder->getFile($filename);
182
-	}
183
-
184
-	/**
185
-	 * Store a file for theming in AppData
186
-	 *
187
-	 * @param string $filename
188
-	 * @param string $data
189
-	 * @return \OCP\Files\SimpleFS\ISimpleFile
190
-	 * @throws NotFoundException
191
-	 * @throws NotPermittedException
192
-	 */
193
-	public function setCachedImage(string $filename, string $data): ISimpleFile {
194
-		$currentFolder = $this->getCacheFolder();
195
-		if ($currentFolder->fileExists($filename)) {
196
-			$file = $currentFolder->getFile($filename);
197
-		} else {
198
-			$file = $currentFolder->newFile($filename);
199
-		}
200
-		$file->putContent($data);
201
-		return $file;
202
-	}
203
-
204
-	public function delete(string $key): void {
205
-		/* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
206
-		try {
207
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key);
208
-			$file->delete();
209
-		} catch (NotFoundException $e) {
210
-		} catch (NotPermittedException $e) {
211
-		}
212
-		try {
213
-			$file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
214
-			$file->delete();
215
-		} catch (NotFoundException $e) {
216
-		} catch (NotPermittedException $e) {
217
-		}
218
-	}
219
-
220
-	public function updateImage(string $key, string $tmpFile): string {
221
-		$this->delete($key);
222
-
223
-		try {
224
-			$folder = $this->getRootFolder()->getFolder('images');
225
-		} catch (NotFoundException $e) {
226
-			$folder = $this->getRootFolder()->newFolder('images');
227
-		}
228
-
229
-		$target = $folder->newFile($key);
230
-		$supportedFormats = $this->getSupportedUploadImageFormats($key);
231
-		$detectedMimeType = mime_content_type($tmpFile);
232
-		if (!in_array($detectedMimeType, $supportedFormats, true)) {
233
-			throw new \Exception('Unsupported image type');
234
-		}
235
-
236
-		if ($key === 'background' && strpos($detectedMimeType, 'image/svg') === false && strpos($detectedMimeType, 'image/gif') === false) {
237
-			// Optimize the image since some people may upload images that will be
238
-			// either to big or are not progressive rendering.
239
-			$newImage = @imagecreatefromstring(file_get_contents($tmpFile));
240
-
241
-			// Preserve transparency
242
-			imagesavealpha($newImage, true);
243
-			imagealphablending($newImage, true);
244
-
245
-			$tmpFile = $this->tempManager->getTemporaryFile();
246
-			$newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
247
-			$newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
248
-			$outputImage = imagescale($newImage, $newWidth, $newHeight);
249
-
250
-			imageinterlace($outputImage, 1);
251
-			imagepng($outputImage, $tmpFile, 8);
252
-			imagedestroy($outputImage);
253
-
254
-			$target->putContent(file_get_contents($tmpFile));
255
-		} else {
256
-			$target->putContent(file_get_contents($tmpFile));
257
-		}
258
-
259
-		return $detectedMimeType;
260
-	}
261
-
262
-	/**
263
-	 * Returns a list of supported mime types for image uploads.
264
-	 * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
265
-	 *
266
-	 * @param string $key The image key, e.g. "favicon"
267
-	 * @return string[]
268
-	 */
269
-	private function getSupportedUploadImageFormats(string $key): array {
270
-		$supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
271
-
272
-		if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
273
-			$supportedFormats[] = 'image/svg+xml';
274
-			$supportedFormats[] = 'image/svg';
275
-		}
276
-
277
-		if ($key === 'favicon') {
278
-			$supportedFormats[] = 'image/x-icon';
279
-			$supportedFormats[] = 'image/vnd.microsoft.icon';
280
-		}
281
-
282
-		return $supportedFormats;
283
-	}
284
-
285
-	/**
286
-	 * remove cached files that are not required any longer
287
-	 *
288
-	 * @throws NotPermittedException
289
-	 * @throws NotFoundException
290
-	 */
291
-	public function cleanup() {
292
-		$currentFolder = $this->getCacheFolder();
293
-		$folders = $this->getRootFolder()->getDirectoryListing();
294
-		foreach ($folders as $folder) {
295
-			if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
296
-				$folder->delete();
297
-			}
298
-		}
299
-	}
300
-
301
-	/**
302
-	 * Check if Imagemagick is enabled and if SVG is supported
303
-	 * otherwise we can't render custom icons
304
-	 *
305
-	 * @return bool
306
-	 */
307
-	public function shouldReplaceIcons() {
308
-		$cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
309
-		if ($value = $cache->get('shouldReplaceIcons')) {
310
-			return (bool)$value;
311
-		}
312
-		$value = false;
313
-		if (extension_loaded('imagick')) {
314
-			if (count(\Imagick::queryFormats('SVG')) >= 1) {
315
-				$value = true;
316
-			}
317
-		}
318
-		$cache->set('shouldReplaceIcons', $value);
319
-		return $value;
320
-	}
321
-
322
-	private function getRootFolder(): ISimpleFolder {
323
-		try {
324
-			return $this->appData->getFolder('global');
325
-		} catch (NotFoundException $e) {
326
-			return $this->appData->newFolder('global');
327
-		}
328
-	}
48
+    public const SupportedImageKeys = ['background', 'logo', 'logoheader', 'favicon'];
49
+
50
+    /** @var IConfig */
51
+    private $config;
52
+    /** @var IAppData */
53
+    private $appData;
54
+    /** @var IURLGenerator */
55
+    private $urlGenerator;
56
+    /** @var array */
57
+    /** @var ICacheFactory */
58
+    private $cacheFactory;
59
+    /** @var ILogger */
60
+    private $logger;
61
+    /** @var ITempManager */
62
+    private $tempManager;
63
+
64
+    public function __construct(IConfig $config,
65
+                                IAppData $appData,
66
+                                IURLGenerator $urlGenerator,
67
+                                ICacheFactory $cacheFactory,
68
+                                ILogger $logger,
69
+                                ITempManager $tempManager) {
70
+        $this->config = $config;
71
+        $this->urlGenerator = $urlGenerator;
72
+        $this->cacheFactory = $cacheFactory;
73
+        $this->logger = $logger;
74
+        $this->tempManager = $tempManager;
75
+        $this->appData = $appData;
76
+    }
77
+
78
+    public function getImageUrl(string $key, bool $useSvg = true): string {
79
+        $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
80
+        if ($this->hasImage($key)) {
81
+            return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => $key ]) . '?v=' . $cacheBusterCounter;
82
+        }
83
+
84
+        switch ($key) {
85
+            case 'logo':
86
+            case 'logoheader':
87
+            case 'favicon':
88
+                return $this->urlGenerator->imagePath('core', 'logo/logo.png') . '?v=' . $cacheBusterCounter;
89
+            case 'background':
90
+                return $this->urlGenerator->imagePath('core', 'background.png') . '?v=' . $cacheBusterCounter;
91
+        }
92
+        return '';
93
+    }
94
+
95
+    public function getImageUrlAbsolute(string $key, bool $useSvg = true): string {
96
+        return $this->urlGenerator->getAbsoluteURL($this->getImageUrl($key, $useSvg));
97
+    }
98
+
99
+    /**
100
+     * @param string $key
101
+     * @param bool $useSvg
102
+     * @return ISimpleFile
103
+     * @throws NotFoundException
104
+     * @throws NotPermittedException
105
+     */
106
+    public function getImage(string $key, bool $useSvg = true): ISimpleFile {
107
+        $logo = $this->config->getAppValue('theming', $key . 'Mime', '');
108
+        $folder = $this->getRootFolder()->getFolder('images');
109
+
110
+        if ($logo === '' || !$folder->fileExists($key)) {
111
+            throw new NotFoundException();
112
+        }
113
+
114
+        if (!$useSvg && $this->shouldReplaceIcons()) {
115
+            if (!$folder->fileExists($key . '.png')) {
116
+                try {
117
+                    $finalIconFile = new \Imagick();
118
+                    $finalIconFile->setBackgroundColor('none');
119
+                    $finalIconFile->readImageBlob($folder->getFile($key)->getContent());
120
+                    $finalIconFile->setImageFormat('png32');
121
+                    $pngFile = $folder->newFile($key . '.png');
122
+                    $pngFile->putContent($finalIconFile->getImageBlob());
123
+                    return $pngFile;
124
+                } catch (\ImagickException $e) {
125
+                    $this->logger->info('The image was requested to be no SVG file, but converting it to PNG failed: ' . $e->getMessage());
126
+                }
127
+            } else {
128
+                return $folder->getFile($key . '.png');
129
+            }
130
+        }
131
+
132
+        return $folder->getFile($key);
133
+    }
134
+
135
+    public function hasImage(string $key): bool {
136
+        $mimeSetting = $this->config->getAppValue('theming', $key . 'Mime', '');
137
+        return $mimeSetting !== '';
138
+    }
139
+
140
+    /**
141
+     * @return array<string, array{mime: string, url: string}>
142
+     */
143
+    public function getCustomImages(): array {
144
+        $images = [];
145
+        foreach ($this::SupportedImageKeys as $key) {
146
+            $images[$key] = [
147
+                'mime' => $this->config->getAppValue('theming', $key . 'Mime', ''),
148
+                'url' => $this->getImageUrl($key),
149
+            ];
150
+        }
151
+        return $images;
152
+    }
153
+
154
+    /**
155
+     * Get folder for current theming files
156
+     *
157
+     * @return ISimpleFolder
158
+     * @throws NotPermittedException
159
+     */
160
+    public function getCacheFolder(): ISimpleFolder {
161
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
162
+        try {
163
+            $folder = $this->getRootFolder()->getFolder($cacheBusterValue);
164
+        } catch (NotFoundException $e) {
165
+            $folder = $this->getRootFolder()->newFolder($cacheBusterValue);
166
+            $this->cleanup();
167
+        }
168
+        return $folder;
169
+    }
170
+
171
+    /**
172
+     * Get a file from AppData
173
+     *
174
+     * @param string $filename
175
+     * @throws NotFoundException
176
+     * @return \OCP\Files\SimpleFS\ISimpleFile
177
+     * @throws NotPermittedException
178
+     */
179
+    public function getCachedImage(string $filename): ISimpleFile {
180
+        $currentFolder = $this->getCacheFolder();
181
+        return $currentFolder->getFile($filename);
182
+    }
183
+
184
+    /**
185
+     * Store a file for theming in AppData
186
+     *
187
+     * @param string $filename
188
+     * @param string $data
189
+     * @return \OCP\Files\SimpleFS\ISimpleFile
190
+     * @throws NotFoundException
191
+     * @throws NotPermittedException
192
+     */
193
+    public function setCachedImage(string $filename, string $data): ISimpleFile {
194
+        $currentFolder = $this->getCacheFolder();
195
+        if ($currentFolder->fileExists($filename)) {
196
+            $file = $currentFolder->getFile($filename);
197
+        } else {
198
+            $file = $currentFolder->newFile($filename);
199
+        }
200
+        $file->putContent($data);
201
+        return $file;
202
+    }
203
+
204
+    public function delete(string $key): void {
205
+        /* ignore exceptions, since we don't want to fail hard if something goes wrong during cleanup */
206
+        try {
207
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key);
208
+            $file->delete();
209
+        } catch (NotFoundException $e) {
210
+        } catch (NotPermittedException $e) {
211
+        }
212
+        try {
213
+            $file = $this->getRootFolder()->getFolder('images')->getFile($key . '.png');
214
+            $file->delete();
215
+        } catch (NotFoundException $e) {
216
+        } catch (NotPermittedException $e) {
217
+        }
218
+    }
219
+
220
+    public function updateImage(string $key, string $tmpFile): string {
221
+        $this->delete($key);
222
+
223
+        try {
224
+            $folder = $this->getRootFolder()->getFolder('images');
225
+        } catch (NotFoundException $e) {
226
+            $folder = $this->getRootFolder()->newFolder('images');
227
+        }
228
+
229
+        $target = $folder->newFile($key);
230
+        $supportedFormats = $this->getSupportedUploadImageFormats($key);
231
+        $detectedMimeType = mime_content_type($tmpFile);
232
+        if (!in_array($detectedMimeType, $supportedFormats, true)) {
233
+            throw new \Exception('Unsupported image type');
234
+        }
235
+
236
+        if ($key === 'background' && strpos($detectedMimeType, 'image/svg') === false && strpos($detectedMimeType, 'image/gif') === false) {
237
+            // Optimize the image since some people may upload images that will be
238
+            // either to big or are not progressive rendering.
239
+            $newImage = @imagecreatefromstring(file_get_contents($tmpFile));
240
+
241
+            // Preserve transparency
242
+            imagesavealpha($newImage, true);
243
+            imagealphablending($newImage, true);
244
+
245
+            $tmpFile = $this->tempManager->getTemporaryFile();
246
+            $newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096);
247
+            $newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth));
248
+            $outputImage = imagescale($newImage, $newWidth, $newHeight);
249
+
250
+            imageinterlace($outputImage, 1);
251
+            imagepng($outputImage, $tmpFile, 8);
252
+            imagedestroy($outputImage);
253
+
254
+            $target->putContent(file_get_contents($tmpFile));
255
+        } else {
256
+            $target->putContent(file_get_contents($tmpFile));
257
+        }
258
+
259
+        return $detectedMimeType;
260
+    }
261
+
262
+    /**
263
+     * Returns a list of supported mime types for image uploads.
264
+     * "favicon" images are only allowed to be SVG when imagemagick with SVG support is available.
265
+     *
266
+     * @param string $key The image key, e.g. "favicon"
267
+     * @return string[]
268
+     */
269
+    private function getSupportedUploadImageFormats(string $key): array {
270
+        $supportedFormats = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
271
+
272
+        if ($key !== 'favicon' || $this->shouldReplaceIcons() === true) {
273
+            $supportedFormats[] = 'image/svg+xml';
274
+            $supportedFormats[] = 'image/svg';
275
+        }
276
+
277
+        if ($key === 'favicon') {
278
+            $supportedFormats[] = 'image/x-icon';
279
+            $supportedFormats[] = 'image/vnd.microsoft.icon';
280
+        }
281
+
282
+        return $supportedFormats;
283
+    }
284
+
285
+    /**
286
+     * remove cached files that are not required any longer
287
+     *
288
+     * @throws NotPermittedException
289
+     * @throws NotFoundException
290
+     */
291
+    public function cleanup() {
292
+        $currentFolder = $this->getCacheFolder();
293
+        $folders = $this->getRootFolder()->getDirectoryListing();
294
+        foreach ($folders as $folder) {
295
+            if ($folder->getName() !== 'images' && $folder->getName() !== $currentFolder->getName()) {
296
+                $folder->delete();
297
+            }
298
+        }
299
+    }
300
+
301
+    /**
302
+     * Check if Imagemagick is enabled and if SVG is supported
303
+     * otherwise we can't render custom icons
304
+     *
305
+     * @return bool
306
+     */
307
+    public function shouldReplaceIcons() {
308
+        $cache = $this->cacheFactory->createDistributed('theming-' . $this->urlGenerator->getBaseUrl());
309
+        if ($value = $cache->get('shouldReplaceIcons')) {
310
+            return (bool)$value;
311
+        }
312
+        $value = false;
313
+        if (extension_loaded('imagick')) {
314
+            if (count(\Imagick::queryFormats('SVG')) >= 1) {
315
+                $value = true;
316
+            }
317
+        }
318
+        $cache->set('shouldReplaceIcons', $value);
319
+        return $value;
320
+    }
321
+
322
+    private function getRootFolder(): ISimpleFolder {
323
+        try {
324
+            return $this->appData->getFolder('global');
325
+        } catch (NotFoundException $e) {
326
+            return $this->appData->newFolder('global');
327
+        }
328
+    }
329 329
 }
Please login to merge, or discard this patch.
apps/theming/lib/Controller/ThemingController.php 1 patch
Indentation   +315 added lines, -315 removed lines patch added patch discarded remove patch
@@ -65,340 +65,340 @@
 block discarded – undo
65 65
  * @package OCA\Theming\Controller
66 66
  */
67 67
 class ThemingController extends Controller {
68
-	private ThemingDefaults $themingDefaults;
69
-	private IL10N $l10n;
70
-	private IConfig $config;
71
-	private ITempManager $tempManager;
72
-	private IAppData $appData;
73
-	private IURLGenerator $urlGenerator;
74
-	private IAppManager $appManager;
75
-	private ImageManager $imageManager;
76
-	private ThemesService $themesService;
68
+    private ThemingDefaults $themingDefaults;
69
+    private IL10N $l10n;
70
+    private IConfig $config;
71
+    private ITempManager $tempManager;
72
+    private IAppData $appData;
73
+    private IURLGenerator $urlGenerator;
74
+    private IAppManager $appManager;
75
+    private ImageManager $imageManager;
76
+    private ThemesService $themesService;
77 77
 
78
-	public function __construct(
79
-		$appName,
80
-		IRequest $request,
81
-		IConfig $config,
82
-		ThemingDefaults $themingDefaults,
83
-		IL10N $l,
84
-		ITempManager $tempManager,
85
-		IAppData $appData,
86
-		IURLGenerator $urlGenerator,
87
-		IAppManager $appManager,
88
-		ImageManager $imageManager,
89
-		ThemesService $themesService
90
-	) {
91
-		parent::__construct($appName, $request);
78
+    public function __construct(
79
+        $appName,
80
+        IRequest $request,
81
+        IConfig $config,
82
+        ThemingDefaults $themingDefaults,
83
+        IL10N $l,
84
+        ITempManager $tempManager,
85
+        IAppData $appData,
86
+        IURLGenerator $urlGenerator,
87
+        IAppManager $appManager,
88
+        ImageManager $imageManager,
89
+        ThemesService $themesService
90
+    ) {
91
+        parent::__construct($appName, $request);
92 92
 
93
-		$this->themingDefaults = $themingDefaults;
94
-		$this->l10n = $l;
95
-		$this->config = $config;
96
-		$this->tempManager = $tempManager;
97
-		$this->appData = $appData;
98
-		$this->urlGenerator = $urlGenerator;
99
-		$this->appManager = $appManager;
100
-		$this->imageManager = $imageManager;
101
-		$this->themesService = $themesService;
102
-	}
93
+        $this->themingDefaults = $themingDefaults;
94
+        $this->l10n = $l;
95
+        $this->config = $config;
96
+        $this->tempManager = $tempManager;
97
+        $this->appData = $appData;
98
+        $this->urlGenerator = $urlGenerator;
99
+        $this->appManager = $appManager;
100
+        $this->imageManager = $imageManager;
101
+        $this->themesService = $themesService;
102
+    }
103 103
 
104
-	/**
105
-	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
106
-	 * @param string $setting
107
-	 * @param string $value
108
-	 * @return DataResponse
109
-	 * @throws NotPermittedException
110
-	 */
111
-	public function updateStylesheet($setting, $value) {
112
-		$value = trim($value);
113
-		$error = null;
114
-		switch ($setting) {
115
-			case 'name':
116
-				if (strlen($value) > 250) {
117
-					$error = $this->l10n->t('The given name is too long');
118
-				}
119
-				break;
120
-			case 'url':
121
-				if (strlen($value) > 500) {
122
-					$error = $this->l10n->t('The given web address is too long');
123
-				}
124
-				if (!$this->isValidUrl($value)) {
125
-					$error = $this->l10n->t('The given web address is not a valid URL');
126
-				}
127
-				break;
128
-			case 'imprintUrl':
129
-				if (strlen($value) > 500) {
130
-					$error = $this->l10n->t('The given legal notice address is too long');
131
-				}
132
-				if (!$this->isValidUrl($value)) {
133
-					$error = $this->l10n->t('The given legal notice address is not a valid URL');
134
-				}
135
-				break;
136
-			case 'privacyUrl':
137
-				if (strlen($value) > 500) {
138
-					$error = $this->l10n->t('The given privacy policy address is too long');
139
-				}
140
-				if (!$this->isValidUrl($value)) {
141
-					$error = $this->l10n->t('The given privacy policy address is not a valid URL');
142
-				}
143
-				break;
144
-			case 'slogan':
145
-				if (strlen($value) > 500) {
146
-					$error = $this->l10n->t('The given slogan is too long');
147
-				}
148
-				break;
149
-			case 'color':
150
-				if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
151
-					$error = $this->l10n->t('The given color is invalid');
152
-				}
153
-				break;
154
-			case 'disable-user-theming':
155
-				if ($value !== "yes" && $value !== "no") {
156
-					$error = $this->l10n->t('Disable-user-theming should be true or false');
157
-				}
158
-				break;
159
-		}
160
-		if ($error !== null) {
161
-			return new DataResponse([
162
-				'data' => [
163
-					'message' => $error,
164
-				],
165
-				'status' => 'error'
166
-			], Http::STATUS_BAD_REQUEST);
167
-		}
104
+    /**
105
+     * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
106
+     * @param string $setting
107
+     * @param string $value
108
+     * @return DataResponse
109
+     * @throws NotPermittedException
110
+     */
111
+    public function updateStylesheet($setting, $value) {
112
+        $value = trim($value);
113
+        $error = null;
114
+        switch ($setting) {
115
+            case 'name':
116
+                if (strlen($value) > 250) {
117
+                    $error = $this->l10n->t('The given name is too long');
118
+                }
119
+                break;
120
+            case 'url':
121
+                if (strlen($value) > 500) {
122
+                    $error = $this->l10n->t('The given web address is too long');
123
+                }
124
+                if (!$this->isValidUrl($value)) {
125
+                    $error = $this->l10n->t('The given web address is not a valid URL');
126
+                }
127
+                break;
128
+            case 'imprintUrl':
129
+                if (strlen($value) > 500) {
130
+                    $error = $this->l10n->t('The given legal notice address is too long');
131
+                }
132
+                if (!$this->isValidUrl($value)) {
133
+                    $error = $this->l10n->t('The given legal notice address is not a valid URL');
134
+                }
135
+                break;
136
+            case 'privacyUrl':
137
+                if (strlen($value) > 500) {
138
+                    $error = $this->l10n->t('The given privacy policy address is too long');
139
+                }
140
+                if (!$this->isValidUrl($value)) {
141
+                    $error = $this->l10n->t('The given privacy policy address is not a valid URL');
142
+                }
143
+                break;
144
+            case 'slogan':
145
+                if (strlen($value) > 500) {
146
+                    $error = $this->l10n->t('The given slogan is too long');
147
+                }
148
+                break;
149
+            case 'color':
150
+                if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
151
+                    $error = $this->l10n->t('The given color is invalid');
152
+                }
153
+                break;
154
+            case 'disable-user-theming':
155
+                if ($value !== "yes" && $value !== "no") {
156
+                    $error = $this->l10n->t('Disable-user-theming should be true or false');
157
+                }
158
+                break;
159
+        }
160
+        if ($error !== null) {
161
+            return new DataResponse([
162
+                'data' => [
163
+                    'message' => $error,
164
+                ],
165
+                'status' => 'error'
166
+            ], Http::STATUS_BAD_REQUEST);
167
+        }
168 168
 
169
-		$this->themingDefaults->set($setting, $value);
169
+        $this->themingDefaults->set($setting, $value);
170 170
 
171
-		return new DataResponse([
172
-			'data' => [
173
-				'message' => $this->l10n->t('Saved'),
174
-			],
175
-			'status' => 'success'
176
-		]);
177
-	}
171
+        return new DataResponse([
172
+            'data' => [
173
+                'message' => $this->l10n->t('Saved'),
174
+            ],
175
+            'status' => 'success'
176
+        ]);
177
+    }
178 178
 
179
-	/**
180
-	 * Check that a string is a valid http/https url
181
-	 */
182
-	private function isValidUrl(string $url): bool {
183
-		return ((strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) &&
184
-			filter_var($url, FILTER_VALIDATE_URL) !== false);
185
-	}
179
+    /**
180
+     * Check that a string is a valid http/https url
181
+     */
182
+    private function isValidUrl(string $url): bool {
183
+        return ((strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) &&
184
+            filter_var($url, FILTER_VALIDATE_URL) !== false);
185
+    }
186 186
 
187
-	/**
188
-	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
189
-	 * @return DataResponse
190
-	 * @throws NotPermittedException
191
-	 */
192
-	public function uploadImage(): DataResponse {
193
-		$key = $this->request->getParam('key');
194
-		$image = $this->request->getUploadedFile('image');
195
-		$error = null;
196
-		$phpFileUploadErrors = [
197
-			UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
198
-			UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
199
-			UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
200
-			UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
201
-			UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
202
-			UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
203
-			UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
204
-			UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
205
-		];
206
-		if (empty($image)) {
207
-			$error = $this->l10n->t('No file uploaded');
208
-		}
209
-		if (!empty($image) && array_key_exists('error', $image) && $image['error'] !== UPLOAD_ERR_OK) {
210
-			$error = $phpFileUploadErrors[$image['error']];
211
-		}
187
+    /**
188
+     * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
189
+     * @return DataResponse
190
+     * @throws NotPermittedException
191
+     */
192
+    public function uploadImage(): DataResponse {
193
+        $key = $this->request->getParam('key');
194
+        $image = $this->request->getUploadedFile('image');
195
+        $error = null;
196
+        $phpFileUploadErrors = [
197
+            UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
198
+            UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
199
+            UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
200
+            UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
201
+            UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
202
+            UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
203
+            UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
204
+            UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
205
+        ];
206
+        if (empty($image)) {
207
+            $error = $this->l10n->t('No file uploaded');
208
+        }
209
+        if (!empty($image) && array_key_exists('error', $image) && $image['error'] !== UPLOAD_ERR_OK) {
210
+            $error = $phpFileUploadErrors[$image['error']];
211
+        }
212 212
 
213
-		if ($error !== null) {
214
-			return new DataResponse(
215
-				[
216
-					'data' => [
217
-						'message' => $error
218
-					],
219
-					'status' => 'failure',
220
-				],
221
-				Http::STATUS_UNPROCESSABLE_ENTITY
222
-			);
223
-		}
213
+        if ($error !== null) {
214
+            return new DataResponse(
215
+                [
216
+                    'data' => [
217
+                        'message' => $error
218
+                    ],
219
+                    'status' => 'failure',
220
+                ],
221
+                Http::STATUS_UNPROCESSABLE_ENTITY
222
+            );
223
+        }
224 224
 
225
-		try {
226
-			$mime = $this->imageManager->updateImage($key, $image['tmp_name']);
227
-			$this->themingDefaults->set($key . 'Mime', $mime);
228
-		} catch (\Exception $e) {
229
-			return new DataResponse(
230
-				[
231
-					'data' => [
232
-						'message' => $e->getMessage()
233
-					],
234
-					'status' => 'failure',
235
-				],
236
-				Http::STATUS_UNPROCESSABLE_ENTITY
237
-			);
238
-		}
225
+        try {
226
+            $mime = $this->imageManager->updateImage($key, $image['tmp_name']);
227
+            $this->themingDefaults->set($key . 'Mime', $mime);
228
+        } catch (\Exception $e) {
229
+            return new DataResponse(
230
+                [
231
+                    'data' => [
232
+                        'message' => $e->getMessage()
233
+                    ],
234
+                    'status' => 'failure',
235
+                ],
236
+                Http::STATUS_UNPROCESSABLE_ENTITY
237
+            );
238
+        }
239 239
 
240
-		$name = $image['name'];
240
+        $name = $image['name'];
241 241
 
242
-		return new DataResponse(
243
-			[
244
-				'data' =>
245
-					[
246
-						'name' => $name,
247
-						'url' => $this->imageManager->getImageUrl($key),
248
-						'message' => $this->l10n->t('Saved'),
249
-					],
250
-				'status' => 'success'
251
-			]
252
-		);
253
-	}
242
+        return new DataResponse(
243
+            [
244
+                'data' =>
245
+                    [
246
+                        'name' => $name,
247
+                        'url' => $this->imageManager->getImageUrl($key),
248
+                        'message' => $this->l10n->t('Saved'),
249
+                    ],
250
+                'status' => 'success'
251
+            ]
252
+        );
253
+    }
254 254
 
255
-	/**
256
-	 * Revert setting to default value
257
-	 * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
258
-	 *
259
-	 * @param string $setting setting which should be reverted
260
-	 * @return DataResponse
261
-	 * @throws NotPermittedException
262
-	 */
263
-	public function undo(string $setting): DataResponse {
264
-		$value = $this->themingDefaults->undo($setting);
255
+    /**
256
+     * Revert setting to default value
257
+     * @AuthorizedAdminSetting(settings=OCA\Theming\Settings\Admin)
258
+     *
259
+     * @param string $setting setting which should be reverted
260
+     * @return DataResponse
261
+     * @throws NotPermittedException
262
+     */
263
+    public function undo(string $setting): DataResponse {
264
+        $value = $this->themingDefaults->undo($setting);
265 265
 
266
-		return new DataResponse(
267
-			[
268
-				'data' =>
269
-					[
270
-						'value' => $value,
271
-						'message' => $this->l10n->t('Saved'),
272
-					],
273
-				'status' => 'success'
274
-			]
275
-		);
276
-	}
266
+        return new DataResponse(
267
+            [
268
+                'data' =>
269
+                    [
270
+                        'value' => $value,
271
+                        'message' => $this->l10n->t('Saved'),
272
+                    ],
273
+                'status' => 'success'
274
+            ]
275
+        );
276
+    }
277 277
 
278
-	/**
279
-	 * @PublicPage
280
-	 * @NoCSRFRequired
281
-	 * @NoSameSiteCookieRequired
282
-	 *
283
-	 * @param string $key
284
-	 * @param bool $useSvg
285
-	 * @return FileDisplayResponse|NotFoundResponse
286
-	 * @throws NotPermittedException
287
-	 */
288
-	public function getImage(string $key, bool $useSvg = true) {
289
-		try {
290
-			$file = $this->imageManager->getImage($key, $useSvg);
291
-		} catch (NotFoundException $e) {
292
-			return new NotFoundResponse();
293
-		}
278
+    /**
279
+     * @PublicPage
280
+     * @NoCSRFRequired
281
+     * @NoSameSiteCookieRequired
282
+     *
283
+     * @param string $key
284
+     * @param bool $useSvg
285
+     * @return FileDisplayResponse|NotFoundResponse
286
+     * @throws NotPermittedException
287
+     */
288
+    public function getImage(string $key, bool $useSvg = true) {
289
+        try {
290
+            $file = $this->imageManager->getImage($key, $useSvg);
291
+        } catch (NotFoundException $e) {
292
+            return new NotFoundResponse();
293
+        }
294 294
 
295
-		$response = new FileDisplayResponse($file);
296
-		$csp = new Http\ContentSecurityPolicy();
297
-		$csp->allowInlineStyle();
298
-		$response->setContentSecurityPolicy($csp);
299
-		$response->cacheFor(3600);
300
-		$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
301
-		$response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"');
302
-		if (!$useSvg) {
303
-			$response->addHeader('Content-Type', 'image/png');
304
-		} else {
305
-			$response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
306
-		}
307
-		return $response;
308
-	}
295
+        $response = new FileDisplayResponse($file);
296
+        $csp = new Http\ContentSecurityPolicy();
297
+        $csp->allowInlineStyle();
298
+        $response->setContentSecurityPolicy($csp);
299
+        $response->cacheFor(3600);
300
+        $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
301
+        $response->addHeader('Content-Disposition', 'attachment; filename="' . $key . '"');
302
+        if (!$useSvg) {
303
+            $response->addHeader('Content-Type', 'image/png');
304
+        } else {
305
+            $response->addHeader('Content-Type', $this->config->getAppValue($this->appName, $key . 'Mime', ''));
306
+        }
307
+        return $response;
308
+    }
309 309
 
310
-	/**
311
-	 * @NoCSRFRequired
312
-	 * @PublicPage
313
-	 * @NoSameSiteCookieRequired
314
-	 * @NoTwoFactorRequired
315
-	 *
316
-	 * @return DataDisplayResponse|NotFoundResponse
317
-	 */
318
-	public function getThemeStylesheet(string $themeId, bool $plain = false, bool $withCustomCss = false) {
319
-		$themes = $this->themesService->getThemes();
320
-		if (!in_array($themeId, array_keys($themes))) {
321
-			return new NotFoundResponse();
322
-		}
310
+    /**
311
+     * @NoCSRFRequired
312
+     * @PublicPage
313
+     * @NoSameSiteCookieRequired
314
+     * @NoTwoFactorRequired
315
+     *
316
+     * @return DataDisplayResponse|NotFoundResponse
317
+     */
318
+    public function getThemeStylesheet(string $themeId, bool $plain = false, bool $withCustomCss = false) {
319
+        $themes = $this->themesService->getThemes();
320
+        if (!in_array($themeId, array_keys($themes))) {
321
+            return new NotFoundResponse();
322
+        }
323 323
 
324
-		$theme = $themes[$themeId];
325
-		$customCss  = $theme->getCustomCss();
324
+        $theme = $themes[$themeId];
325
+        $customCss  = $theme->getCustomCss();
326 326
 
327
-		// Generate variables
328
-		$variables = '';
329
-		foreach ($theme->getCSSVariables() as $variable => $value) {
330
-			$variables .= "$variable:$value; ";
331
-		};
327
+        // Generate variables
328
+        $variables = '';
329
+        foreach ($theme->getCSSVariables() as $variable => $value) {
330
+            $variables .= "$variable:$value; ";
331
+        };
332 332
 
333
-		// If plain is set, the browser decides of the css priority
334
-		if ($plain) {
335
-			$css = ":root { $variables } " . $customCss;
336
-		} else { 
337
-			// If not set, we'll rely on the body class
338
-			$compiler = new Compiler();
339
-			$compiledCss = $compiler->compileString("[data-theme-$themeId] { $variables $customCss }");
340
-			$css = $compiledCss->getCss();;
341
-		}
333
+        // If plain is set, the browser decides of the css priority
334
+        if ($plain) {
335
+            $css = ":root { $variables } " . $customCss;
336
+        } else { 
337
+            // If not set, we'll rely on the body class
338
+            $compiler = new Compiler();
339
+            $compiledCss = $compiler->compileString("[data-theme-$themeId] { $variables $customCss }");
340
+            $css = $compiledCss->getCss();;
341
+        }
342 342
 
343
-		try {
344
-			$response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
345
-			$response->cacheFor(86400);
346
-			return $response;
347
-		} catch (NotFoundException $e) {
348
-			return new NotFoundResponse();
349
-		}
350
-	}
343
+        try {
344
+            $response = new DataDisplayResponse($css, Http::STATUS_OK, ['Content-Type' => 'text/css']);
345
+            $response->cacheFor(86400);
346
+            return $response;
347
+        } catch (NotFoundException $e) {
348
+            return new NotFoundResponse();
349
+        }
350
+    }
351 351
 
352
-	/**
353
-	 * @NoCSRFRequired
354
-	 * @PublicPage
355
-	 *
356
-	 * @return Http\JSONResponse
357
-	 */
358
-	public function getManifest($app) {
359
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
360
-		if ($app === 'core' || $app === 'settings') {
361
-			$name = $this->themingDefaults->getName();
362
-			$shortName = $this->themingDefaults->getName();
363
-			$startUrl = $this->urlGenerator->getBaseUrl();
364
-			$description = $this->themingDefaults->getSlogan();
365
-		} else {
366
-			$info = $this->appManager->getAppInfo($app, false, $this->l10n->getLanguageCode());
367
-			$name = $info['name'] . ' - ' . $this->themingDefaults->getName();
368
-			$shortName = $info['name'];
369
-			if (strpos($this->request->getRequestUri(), '/index.php/') !== false) {
370
-				$startUrl = $this->urlGenerator->getBaseUrl() . '/index.php/apps/' . $app . '/';
371
-			} else {
372
-				$startUrl = $this->urlGenerator->getBaseUrl() . '/apps/' . $app . '/';
373
-			}
374
-			$description = $info['summary'] ?? '';
375
-		}
376
-		$responseJS = [
377
-			'name' => $name,
378
-			'short_name' => $shortName,
379
-			'start_url' => $startUrl,
380
-			'theme_color' => $this->themingDefaults->getColorPrimary(),
381
-			'background_color' => $this->themingDefaults->getColorPrimary(),
382
-			'description' => $description,
383
-			'icons' =>
384
-				[
385
-					[
386
-						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon',
387
-								['app' => $app]) . '?v=' . $cacheBusterValue,
388
-						'type' => 'image/png',
389
-						'sizes' => '512x512'
390
-					],
391
-					[
392
-						'src' => $this->urlGenerator->linkToRoute('theming.Icon.getFavicon',
393
-								['app' => $app]) . '?v=' . $cacheBusterValue,
394
-						'type' => 'image/svg+xml',
395
-						'sizes' => '16x16'
396
-					]
397
-				],
398
-			'display' => 'standalone'
399
-		];
400
-		$response = new Http\JSONResponse($responseJS);
401
-		$response->cacheFor(3600);
402
-		return $response;
403
-	}
352
+    /**
353
+     * @NoCSRFRequired
354
+     * @PublicPage
355
+     *
356
+     * @return Http\JSONResponse
357
+     */
358
+    public function getManifest($app) {
359
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
360
+        if ($app === 'core' || $app === 'settings') {
361
+            $name = $this->themingDefaults->getName();
362
+            $shortName = $this->themingDefaults->getName();
363
+            $startUrl = $this->urlGenerator->getBaseUrl();
364
+            $description = $this->themingDefaults->getSlogan();
365
+        } else {
366
+            $info = $this->appManager->getAppInfo($app, false, $this->l10n->getLanguageCode());
367
+            $name = $info['name'] . ' - ' . $this->themingDefaults->getName();
368
+            $shortName = $info['name'];
369
+            if (strpos($this->request->getRequestUri(), '/index.php/') !== false) {
370
+                $startUrl = $this->urlGenerator->getBaseUrl() . '/index.php/apps/' . $app . '/';
371
+            } else {
372
+                $startUrl = $this->urlGenerator->getBaseUrl() . '/apps/' . $app . '/';
373
+            }
374
+            $description = $info['summary'] ?? '';
375
+        }
376
+        $responseJS = [
377
+            'name' => $name,
378
+            'short_name' => $shortName,
379
+            'start_url' => $startUrl,
380
+            'theme_color' => $this->themingDefaults->getColorPrimary(),
381
+            'background_color' => $this->themingDefaults->getColorPrimary(),
382
+            'description' => $description,
383
+            'icons' =>
384
+                [
385
+                    [
386
+                        'src' => $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon',
387
+                                ['app' => $app]) . '?v=' . $cacheBusterValue,
388
+                        'type' => 'image/png',
389
+                        'sizes' => '512x512'
390
+                    ],
391
+                    [
392
+                        'src' => $this->urlGenerator->linkToRoute('theming.Icon.getFavicon',
393
+                                ['app' => $app]) . '?v=' . $cacheBusterValue,
394
+                        'type' => 'image/svg+xml',
395
+                        'sizes' => '16x16'
396
+                    ]
397
+                ],
398
+            'display' => 'standalone'
399
+        ];
400
+        $response = new Http\JSONResponse($responseJS);
401
+        $response->cacheFor(3600);
402
+        return $response;
403
+    }
404 404
 }
Please login to merge, or discard this patch.
apps/theming/lib/ThemingDefaults.php 1 patch
Indentation   +449 added lines, -449 removed lines patch added patch discarded remove patch
@@ -55,454 +55,454 @@
 block discarded – undo
55 55
 
56 56
 class ThemingDefaults extends \OC_Defaults {
57 57
 
58
-	private IConfig $config;
59
-	private IL10N $l;
60
-	private ImageManager $imageManager;
61
-	private IUserSession $userSession;
62
-	private IURLGenerator $urlGenerator;
63
-	private ICacheFactory $cacheFactory;
64
-	private Util $util;
65
-	private IAppManager $appManager;
66
-	private INavigationManager $navigationManager;
67
-
68
-	private string $name;
69
-	private string $title;
70
-	private string $entity;
71
-	private string $productName;
72
-	private string $url;
73
-	private string $color;
74
-
75
-	private string $iTunesAppId;
76
-	private string $iOSClientUrl;
77
-	private string $AndroidClientUrl;
78
-	private string $FDroidClientUrl;
79
-
80
-	/**
81
-	 * ThemingDefaults constructor.
82
-	 *
83
-	 * @param IConfig $config
84
-	 * @param IL10N $l
85
-	 * @param ImageManager $imageManager
86
-	 * @param IUserSession $userSession
87
-	 * @param IURLGenerator $urlGenerator
88
-	 * @param ICacheFactory $cacheFactory
89
-	 * @param Util $util
90
-	 * @param IAppManager $appManager
91
-	 */
92
-	public function __construct(IConfig $config,
93
-								IL10N $l,
94
-								IUserSession $userSession,
95
-								IURLGenerator $urlGenerator,
96
-								ICacheFactory $cacheFactory,
97
-								Util $util,
98
-								ImageManager $imageManager,
99
-								IAppManager $appManager,
100
-								INavigationManager $navigationManager
101
-	) {
102
-		parent::__construct();
103
-		$this->config = $config;
104
-		$this->l = $l;
105
-		$this->imageManager = $imageManager;
106
-		$this->userSession = $userSession;
107
-		$this->urlGenerator = $urlGenerator;
108
-		$this->cacheFactory = $cacheFactory;
109
-		$this->util = $util;
110
-		$this->appManager = $appManager;
111
-		$this->navigationManager = $navigationManager;
112
-
113
-		$this->name = parent::getName();
114
-		$this->title = parent::getTitle();
115
-		$this->entity = parent::getEntity();
116
-		$this->productName = parent::getProductName();
117
-		$this->url = parent::getBaseUrl();
118
-		$this->color = parent::getColorPrimary();
119
-		$this->iTunesAppId = parent::getiTunesAppId();
120
-		$this->iOSClientUrl = parent::getiOSClientUrl();
121
-		$this->AndroidClientUrl = parent::getAndroidClientUrl();
122
-		$this->FDroidClientUrl = parent::getFDroidClientUrl();
123
-	}
124
-
125
-	public function getName() {
126
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
127
-	}
128
-
129
-	public function getHTMLName() {
130
-		return $this->config->getAppValue('theming', 'name', $this->name);
131
-	}
132
-
133
-	public function getTitle() {
134
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
135
-	}
136
-
137
-	public function getEntity() {
138
-		return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
139
-	}
140
-
141
-	public function getProductName() {
142
-		return strip_tags($this->config->getAppValue('theming', 'productName', $this->productName));
143
-	}
144
-
145
-	public function getBaseUrl() {
146
-		return $this->config->getAppValue('theming', 'url', $this->url);
147
-	}
148
-
149
-	/**
150
-	 * We pass a string and sanitizeHTML will return a string too in that case
151
-	 * @psalm-suppress InvalidReturnStatement
152
-	 * @psalm-suppress InvalidReturnType
153
-	 */
154
-	public function getSlogan(?string $lang = null) {
155
-		return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan($lang)));
156
-	}
157
-
158
-	public function getImprintUrl() {
159
-		return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
160
-	}
161
-
162
-	public function getPrivacyUrl() {
163
-		return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
164
-	}
165
-
166
-	public function getShortFooter() {
167
-		$slogan = $this->getSlogan();
168
-		$baseUrl = $this->getBaseUrl();
169
-		if ($baseUrl !== '') {
170
-			$footer = '<a href="' . $baseUrl . '" target="_blank"' .
171
-				' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
172
-		} else {
173
-			$footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
174
-		}
175
-		$footer .= ($slogan !== '' ? ' – ' . $slogan : '');
176
-
177
-		$links = [
178
-			[
179
-				'text' => $this->l->t('Legal notice'),
180
-				'url' => (string)$this->getImprintUrl()
181
-			],
182
-			[
183
-				'text' => $this->l->t('Privacy policy'),
184
-				'url' => (string)$this->getPrivacyUrl()
185
-			],
186
-		];
187
-
188
-		$navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
189
-		$guestNavigation = array_map(function ($nav) {
190
-			return [
191
-				'text' => $nav['name'],
192
-				'url' => $nav['href']
193
-			];
194
-		}, $navigation);
195
-		$links = array_merge($links, $guestNavigation);
196
-
197
-		$legalLinks = '';
198
-		$divider = '';
199
-		foreach ($links as $link) {
200
-			if ($link['url'] !== ''
201
-				&& filter_var($link['url'], FILTER_VALIDATE_URL)
202
-			) {
203
-				$legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
204
-					' rel="noreferrer noopener">' . $link['text'] . '</a>';
205
-				$divider = ' · ';
206
-			}
207
-		}
208
-		if ($legalLinks !== '') {
209
-			$footer .= '<br/>' . $legalLinks;
210
-		}
211
-
212
-		return $footer;
213
-	}
214
-
215
-	/**
216
-	 * Color that is used for the header as well as for mail headers
217
-	 */
218
-	public function getColorPrimary(): string {
219
-		$user = $this->userSession->getUser();
220
-
221
-		// admin-defined primary color
222
-		$defaultColor = $this->getDefaultColorPrimary();
223
-
224
-		if ($this->isUserThemingDisabled()) {
225
-			return $defaultColor;
226
-		}
58
+    private IConfig $config;
59
+    private IL10N $l;
60
+    private ImageManager $imageManager;
61
+    private IUserSession $userSession;
62
+    private IURLGenerator $urlGenerator;
63
+    private ICacheFactory $cacheFactory;
64
+    private Util $util;
65
+    private IAppManager $appManager;
66
+    private INavigationManager $navigationManager;
67
+
68
+    private string $name;
69
+    private string $title;
70
+    private string $entity;
71
+    private string $productName;
72
+    private string $url;
73
+    private string $color;
74
+
75
+    private string $iTunesAppId;
76
+    private string $iOSClientUrl;
77
+    private string $AndroidClientUrl;
78
+    private string $FDroidClientUrl;
79
+
80
+    /**
81
+     * ThemingDefaults constructor.
82
+     *
83
+     * @param IConfig $config
84
+     * @param IL10N $l
85
+     * @param ImageManager $imageManager
86
+     * @param IUserSession $userSession
87
+     * @param IURLGenerator $urlGenerator
88
+     * @param ICacheFactory $cacheFactory
89
+     * @param Util $util
90
+     * @param IAppManager $appManager
91
+     */
92
+    public function __construct(IConfig $config,
93
+                                IL10N $l,
94
+                                IUserSession $userSession,
95
+                                IURLGenerator $urlGenerator,
96
+                                ICacheFactory $cacheFactory,
97
+                                Util $util,
98
+                                ImageManager $imageManager,
99
+                                IAppManager $appManager,
100
+                                INavigationManager $navigationManager
101
+    ) {
102
+        parent::__construct();
103
+        $this->config = $config;
104
+        $this->l = $l;
105
+        $this->imageManager = $imageManager;
106
+        $this->userSession = $userSession;
107
+        $this->urlGenerator = $urlGenerator;
108
+        $this->cacheFactory = $cacheFactory;
109
+        $this->util = $util;
110
+        $this->appManager = $appManager;
111
+        $this->navigationManager = $navigationManager;
112
+
113
+        $this->name = parent::getName();
114
+        $this->title = parent::getTitle();
115
+        $this->entity = parent::getEntity();
116
+        $this->productName = parent::getProductName();
117
+        $this->url = parent::getBaseUrl();
118
+        $this->color = parent::getColorPrimary();
119
+        $this->iTunesAppId = parent::getiTunesAppId();
120
+        $this->iOSClientUrl = parent::getiOSClientUrl();
121
+        $this->AndroidClientUrl = parent::getAndroidClientUrl();
122
+        $this->FDroidClientUrl = parent::getFDroidClientUrl();
123
+    }
124
+
125
+    public function getName() {
126
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->name));
127
+    }
128
+
129
+    public function getHTMLName() {
130
+        return $this->config->getAppValue('theming', 'name', $this->name);
131
+    }
132
+
133
+    public function getTitle() {
134
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->title));
135
+    }
136
+
137
+    public function getEntity() {
138
+        return strip_tags($this->config->getAppValue('theming', 'name', $this->entity));
139
+    }
140
+
141
+    public function getProductName() {
142
+        return strip_tags($this->config->getAppValue('theming', 'productName', $this->productName));
143
+    }
144
+
145
+    public function getBaseUrl() {
146
+        return $this->config->getAppValue('theming', 'url', $this->url);
147
+    }
148
+
149
+    /**
150
+     * We pass a string and sanitizeHTML will return a string too in that case
151
+     * @psalm-suppress InvalidReturnStatement
152
+     * @psalm-suppress InvalidReturnType
153
+     */
154
+    public function getSlogan(?string $lang = null) {
155
+        return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', parent::getSlogan($lang)));
156
+    }
157
+
158
+    public function getImprintUrl() {
159
+        return (string)$this->config->getAppValue('theming', 'imprintUrl', '');
160
+    }
161
+
162
+    public function getPrivacyUrl() {
163
+        return (string)$this->config->getAppValue('theming', 'privacyUrl', '');
164
+    }
165
+
166
+    public function getShortFooter() {
167
+        $slogan = $this->getSlogan();
168
+        $baseUrl = $this->getBaseUrl();
169
+        if ($baseUrl !== '') {
170
+            $footer = '<a href="' . $baseUrl . '" target="_blank"' .
171
+                ' rel="noreferrer noopener" class="entity-name">' . $this->getEntity() . '</a>';
172
+        } else {
173
+            $footer = '<span class="entity-name">' .$this->getEntity() . '</span>';
174
+        }
175
+        $footer .= ($slogan !== '' ? ' – ' . $slogan : '');
176
+
177
+        $links = [
178
+            [
179
+                'text' => $this->l->t('Legal notice'),
180
+                'url' => (string)$this->getImprintUrl()
181
+            ],
182
+            [
183
+                'text' => $this->l->t('Privacy policy'),
184
+                'url' => (string)$this->getPrivacyUrl()
185
+            ],
186
+        ];
187
+
188
+        $navigation = $this->navigationManager->getAll(INavigationManager::TYPE_GUEST);
189
+        $guestNavigation = array_map(function ($nav) {
190
+            return [
191
+                'text' => $nav['name'],
192
+                'url' => $nav['href']
193
+            ];
194
+        }, $navigation);
195
+        $links = array_merge($links, $guestNavigation);
196
+
197
+        $legalLinks = '';
198
+        $divider = '';
199
+        foreach ($links as $link) {
200
+            if ($link['url'] !== ''
201
+                && filter_var($link['url'], FILTER_VALIDATE_URL)
202
+            ) {
203
+                $legalLinks .= $divider . '<a href="' . $link['url'] . '" class="legal" target="_blank"' .
204
+                    ' rel="noreferrer noopener">' . $link['text'] . '</a>';
205
+                $divider = ' · ';
206
+            }
207
+        }
208
+        if ($legalLinks !== '') {
209
+            $footer .= '<br/>' . $legalLinks;
210
+        }
211
+
212
+        return $footer;
213
+    }
214
+
215
+    /**
216
+     * Color that is used for the header as well as for mail headers
217
+     */
218
+    public function getColorPrimary(): string {
219
+        $user = $this->userSession->getUser();
220
+
221
+        // admin-defined primary color
222
+        $defaultColor = $this->getDefaultColorPrimary();
223
+
224
+        if ($this->isUserThemingDisabled()) {
225
+            return $defaultColor;
226
+        }
227 227
 		
228
-		// user-defined primary color
229
-		$themingBackground = '';
230
-		if (!empty($user)) {
231
-			$themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
232
-			// If the user selected the default background
233
-			if ($themingBackground === '') {
234
-				return BackgroundService::DEFAULT_COLOR;
235
-			}
236
-
237
-			// If the user selected a specific colour
238
-			if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
239
-				return $themingBackground;
240
-			}
241
-
242
-			// if the user-selected background is a background reference
243
-			if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
244
-				return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
245
-			}
246
-		}
247
-
248
-		// If the default color is not valid, return the default background one
249
-		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
250
-			return BackgroundService::DEFAULT_COLOR;
251
-		}
252
-
253
-		// Finally, return the system global primary color
254
-		return $defaultColor;
255
-	}
256
-
257
-	/**
258
-	 * Return the default color primary
259
-	 */
260
-	public function getDefaultColorPrimary(): string {
261
-		$color = $this->config->getAppValue(Application::APP_ID, 'color');
262
-		if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
263
-			$color = '#0082c9';
264
-		}
265
-		return $color;
266
-	}
267
-
268
-	/**
269
-	 * Themed logo url
270
-	 *
271
-	 * @param bool $useSvg Whether to point to the SVG image or a fallback
272
-	 * @return string
273
-	 */
274
-	public function getLogo($useSvg = true): string {
275
-		$logo = $this->config->getAppValue('theming', 'logoMime', '');
276
-
277
-		// short cut to avoid setting up the filesystem just to check if the logo is there
278
-		//
279
-		// explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
280
-		// otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
281
-		// needs to be called then)
282
-		if ($useSvg === true && $logo !== false) {
283
-			$logoExists = true;
284
-		} else {
285
-			try {
286
-				$this->imageManager->getImage('logo', $useSvg);
287
-				$logoExists = true;
288
-			} catch (\Exception $e) {
289
-				$logoExists = false;
290
-			}
291
-		}
292
-
293
-		$cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
294
-
295
-		if (!$logo || !$logoExists) {
296
-			if ($useSvg) {
297
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
298
-			} else {
299
-				$logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
300
-			}
301
-			return $logo . '?v=' . $cacheBusterCounter;
302
-		}
303
-
304
-		return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
305
-	}
306
-
307
-	/**
308
-	 * Themed background image url
309
-	 *
310
-	 * @return string
311
-	 */
312
-	public function getBackground(): string {
313
-		return $this->imageManager->getImageUrl('background');
314
-	}
315
-
316
-	/**
317
-	 * @return string
318
-	 */
319
-	public function getiTunesAppId() {
320
-		return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
321
-	}
322
-
323
-	/**
324
-	 * @return string
325
-	 */
326
-	public function getiOSClientUrl() {
327
-		return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
328
-	}
329
-
330
-	/**
331
-	 * @return string
332
-	 */
333
-	public function getAndroidClientUrl() {
334
-		return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
335
-	}
336
-
337
-	/**
338
-	 * @return string
339
-	 */
340
-	public function getFDroidClientUrl() {
341
-		return $this->config->getAppValue('theming', 'FDroidClientUrl', $this->FDroidClientUrl);
342
-	}
343
-
344
-	/**
345
-	 * @return array scss variables to overwrite
346
-	 */
347
-	public function getScssVariables() {
348
-		$cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
349
-		$cache = $this->cacheFactory->createDistributed('theming-' . $cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
350
-		if ($value = $cache->get('getScssVariables')) {
351
-			return $value;
352
-		}
353
-
354
-		$variables = [
355
-			'theming-cachebuster' => "'" . $cacheBuster . "'",
356
-			'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
357
-			'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
358
-			'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
359
-			'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
360
-		];
361
-
362
-		$variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
363
-		$variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
364
-		$variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
365
-		$variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
366
-		$variables['image-login-plain'] = 'false';
367
-
368
-		if ($this->config->getAppValue('theming', 'color', '') !== '') {
369
-			$variables['color-primary'] = $this->getColorPrimary();
370
-			$variables['color-primary-text'] = $this->getTextColorPrimary();
371
-			$variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
372
-		}
373
-
374
-		if ($this->config->getAppValue('theming', 'backgroundMime', '') === 'backgroundColor') {
375
-			$variables['image-login-plain'] = 'true';
376
-		}
377
-
378
-		$variables['has-legal-links'] = 'false';
379
-		if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
380
-			$variables['has-legal-links'] = 'true';
381
-		}
382
-
383
-		$cache->set('getScssVariables', $variables);
384
-		return $variables;
385
-	}
386
-
387
-	/**
388
-	 * Check if the image should be replaced by the theming app
389
-	 * and return the new image location then
390
-	 *
391
-	 * @param string $app name of the app
392
-	 * @param string $image filename of the image
393
-	 * @return bool|string false if image should not replaced, otherwise the location of the image
394
-	 */
395
-	public function replaceImagePath($app, $image) {
396
-		if ($app === '' || $app === 'files_sharing') {
397
-			$app = 'core';
398
-		}
399
-		$cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
400
-
401
-		$route = false;
402
-		if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
403
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
404
-		}
405
-		if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
406
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
407
-		}
408
-		if ($image === 'manifest.json') {
409
-			try {
410
-				$appPath = $this->appManager->getAppPath($app);
411
-				if (file_exists($appPath . '/img/manifest.json')) {
412
-					return false;
413
-				}
414
-			} catch (AppPathNotFoundException $e) {
415
-			}
416
-			$route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
417
-		}
418
-		if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
419
-			$route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
420
-		}
421
-
422
-		if ($route) {
423
-			return $route . '?v=' . $cacheBusterValue;
424
-		}
425
-
426
-		return false;
427
-	}
428
-
429
-	protected function getCustomFavicon(): ?ISimpleFile {
430
-		try {
431
-			return $this->imageManager->getImage('favicon');
432
-		} catch (NotFoundException $e) {
433
-			return null;
434
-		}
435
-	}
436
-
437
-	/**
438
-	 * Increases the cache buster key
439
-	 */
440
-	public function increaseCacheBuster(): void {
441
-		$cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
442
-		$this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
443
-		$this->cacheFactory->createDistributed('theming-')->clear();
444
-		$this->cacheFactory->createDistributed('imagePath')->clear();
445
-	}
446
-
447
-	/**
448
-	 * Update setting in the database
449
-	 *
450
-	 * @param string $setting
451
-	 * @param string $value
452
-	 */
453
-	public function set($setting, $value) {
454
-		$this->config->setAppValue('theming', $setting, $value);
455
-		$this->increaseCacheBuster();
456
-	}
457
-
458
-	/**
459
-	 * Revert settings to the default value
460
-	 *
461
-	 * @param string $setting setting which should be reverted
462
-	 * @return string default value
463
-	 */
464
-	public function undo($setting) {
465
-		$this->config->deleteAppValue('theming', $setting);
466
-		$this->increaseCacheBuster();
467
-
468
-		$returnValue = '';
469
-		switch ($setting) {
470
-			case 'name':
471
-				$returnValue = $this->getEntity();
472
-				break;
473
-			case 'url':
474
-				$returnValue = $this->getBaseUrl();
475
-				break;
476
-			case 'slogan':
477
-				$returnValue = $this->getSlogan();
478
-				break;
479
-			case 'color':
480
-				$returnValue = $this->getColorPrimary();
481
-				break;
482
-			case 'logo':
483
-			case 'logoheader':
484
-			case 'background':
485
-			case 'favicon':
486
-				$this->imageManager->delete($setting);
487
-				break;
488
-		}
489
-
490
-		return $returnValue;
491
-	}
492
-
493
-	/**
494
-	 * Color of text in the header and primary buttons
495
-	 *
496
-	 * @return string
497
-	 */
498
-	public function getTextColorPrimary() {
499
-		return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
500
-	}
501
-
502
-	/**
503
-	 * Has the admin disabled user customization
504
-	 */
505
-	public function isUserThemingDisabled(): bool {
506
-		return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes';
507
-	}
228
+        // user-defined primary color
229
+        $themingBackground = '';
230
+        if (!empty($user)) {
231
+            $themingBackground = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'background', '');
232
+            // If the user selected the default background
233
+            if ($themingBackground === '') {
234
+                return BackgroundService::DEFAULT_COLOR;
235
+            }
236
+
237
+            // If the user selected a specific colour
238
+            if (preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $themingBackground)) {
239
+                return $themingBackground;
240
+            }
241
+
242
+            // if the user-selected background is a background reference
243
+            if (isset(BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'])) {
244
+                return BackgroundService::SHIPPED_BACKGROUNDS[$themingBackground]['primary_color'];
245
+            }
246
+        }
247
+
248
+        // If the default color is not valid, return the default background one
249
+        if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $defaultColor)) {
250
+            return BackgroundService::DEFAULT_COLOR;
251
+        }
252
+
253
+        // Finally, return the system global primary color
254
+        return $defaultColor;
255
+    }
256
+
257
+    /**
258
+     * Return the default color primary
259
+     */
260
+    public function getDefaultColorPrimary(): string {
261
+        $color = $this->config->getAppValue(Application::APP_ID, 'color');
262
+        if (!preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $color)) {
263
+            $color = '#0082c9';
264
+        }
265
+        return $color;
266
+    }
267
+
268
+    /**
269
+     * Themed logo url
270
+     *
271
+     * @param bool $useSvg Whether to point to the SVG image or a fallback
272
+     * @return string
273
+     */
274
+    public function getLogo($useSvg = true): string {
275
+        $logo = $this->config->getAppValue('theming', 'logoMime', '');
276
+
277
+        // short cut to avoid setting up the filesystem just to check if the logo is there
278
+        //
279
+        // explanation: if an SVG is requested and the app config value for logoMime is set then the logo is there.
280
+        // otherwise we need to check it and maybe also generate a PNG from the SVG (that's done in getImage() which
281
+        // needs to be called then)
282
+        if ($useSvg === true && $logo !== false) {
283
+            $logoExists = true;
284
+        } else {
285
+            try {
286
+                $this->imageManager->getImage('logo', $useSvg);
287
+                $logoExists = true;
288
+            } catch (\Exception $e) {
289
+                $logoExists = false;
290
+            }
291
+        }
292
+
293
+        $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0');
294
+
295
+        if (!$logo || !$logoExists) {
296
+            if ($useSvg) {
297
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.svg');
298
+            } else {
299
+                $logo = $this->urlGenerator->imagePath('core', 'logo/logo.png');
300
+            }
301
+            return $logo . '?v=' . $cacheBusterCounter;
302
+        }
303
+
304
+        return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]);
305
+    }
306
+
307
+    /**
308
+     * Themed background image url
309
+     *
310
+     * @return string
311
+     */
312
+    public function getBackground(): string {
313
+        return $this->imageManager->getImageUrl('background');
314
+    }
315
+
316
+    /**
317
+     * @return string
318
+     */
319
+    public function getiTunesAppId() {
320
+        return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId);
321
+    }
322
+
323
+    /**
324
+     * @return string
325
+     */
326
+    public function getiOSClientUrl() {
327
+        return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl);
328
+    }
329
+
330
+    /**
331
+     * @return string
332
+     */
333
+    public function getAndroidClientUrl() {
334
+        return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl);
335
+    }
336
+
337
+    /**
338
+     * @return string
339
+     */
340
+    public function getFDroidClientUrl() {
341
+        return $this->config->getAppValue('theming', 'FDroidClientUrl', $this->FDroidClientUrl);
342
+    }
343
+
344
+    /**
345
+     * @return array scss variables to overwrite
346
+     */
347
+    public function getScssVariables() {
348
+        $cacheBuster = $this->config->getAppValue('theming', 'cachebuster', '0');
349
+        $cache = $this->cacheFactory->createDistributed('theming-' . $cacheBuster . '-' . $this->urlGenerator->getBaseUrl());
350
+        if ($value = $cache->get('getScssVariables')) {
351
+            return $value;
352
+        }
353
+
354
+        $variables = [
355
+            'theming-cachebuster' => "'" . $cacheBuster . "'",
356
+            'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime') . "'",
357
+            'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime') . "'",
358
+            'theming-logoheader-mime' => "'" . $this->config->getAppValue('theming', 'logoheaderMime') . "'",
359
+            'theming-favicon-mime' => "'" . $this->config->getAppValue('theming', 'faviconMime') . "'"
360
+        ];
361
+
362
+        $variables['image-logo'] = "url('".$this->imageManager->getImageUrl('logo')."')";
363
+        $variables['image-logoheader'] = "url('".$this->imageManager->getImageUrl('logoheader')."')";
364
+        $variables['image-favicon'] = "url('".$this->imageManager->getImageUrl('favicon')."')";
365
+        $variables['image-login-background'] = "url('".$this->imageManager->getImageUrl('background')."')";
366
+        $variables['image-login-plain'] = 'false';
367
+
368
+        if ($this->config->getAppValue('theming', 'color', '') !== '') {
369
+            $variables['color-primary'] = $this->getColorPrimary();
370
+            $variables['color-primary-text'] = $this->getTextColorPrimary();
371
+            $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary());
372
+        }
373
+
374
+        if ($this->config->getAppValue('theming', 'backgroundMime', '') === 'backgroundColor') {
375
+            $variables['image-login-plain'] = 'true';
376
+        }
377
+
378
+        $variables['has-legal-links'] = 'false';
379
+        if ($this->getImprintUrl() !== '' || $this->getPrivacyUrl() !== '') {
380
+            $variables['has-legal-links'] = 'true';
381
+        }
382
+
383
+        $cache->set('getScssVariables', $variables);
384
+        return $variables;
385
+    }
386
+
387
+    /**
388
+     * Check if the image should be replaced by the theming app
389
+     * and return the new image location then
390
+     *
391
+     * @param string $app name of the app
392
+     * @param string $image filename of the image
393
+     * @return bool|string false if image should not replaced, otherwise the location of the image
394
+     */
395
+    public function replaceImagePath($app, $image) {
396
+        if ($app === '' || $app === 'files_sharing') {
397
+            $app = 'core';
398
+        }
399
+        $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0');
400
+
401
+        $route = false;
402
+        if ($image === 'favicon.ico' && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
403
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]);
404
+        }
405
+        if (($image === 'favicon-touch.png' || $image === 'favicon-fb.png') && ($this->imageManager->shouldReplaceIcons() || $this->getCustomFavicon() !== null)) {
406
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]);
407
+        }
408
+        if ($image === 'manifest.json') {
409
+            try {
410
+                $appPath = $this->appManager->getAppPath($app);
411
+                if (file_exists($appPath . '/img/manifest.json')) {
412
+                    return false;
413
+                }
414
+            } catch (AppPathNotFoundException $e) {
415
+            }
416
+            $route = $this->urlGenerator->linkToRoute('theming.Theming.getManifest', ['app' => $app ]);
417
+        }
418
+        if (strpos($image, 'filetypes/') === 0 && file_exists(\OC::$SERVERROOT . '/core/img/' . $image)) {
419
+            $route = $this->urlGenerator->linkToRoute('theming.Icon.getThemedIcon', ['app' => $app, 'image' => $image]);
420
+        }
421
+
422
+        if ($route) {
423
+            return $route . '?v=' . $cacheBusterValue;
424
+        }
425
+
426
+        return false;
427
+    }
428
+
429
+    protected function getCustomFavicon(): ?ISimpleFile {
430
+        try {
431
+            return $this->imageManager->getImage('favicon');
432
+        } catch (NotFoundException $e) {
433
+            return null;
434
+        }
435
+    }
436
+
437
+    /**
438
+     * Increases the cache buster key
439
+     */
440
+    public function increaseCacheBuster(): void {
441
+        $cacheBusterKey = (int)$this->config->getAppValue('theming', 'cachebuster', '0');
442
+        $this->config->setAppValue('theming', 'cachebuster', (string)($cacheBusterKey + 1));
443
+        $this->cacheFactory->createDistributed('theming-')->clear();
444
+        $this->cacheFactory->createDistributed('imagePath')->clear();
445
+    }
446
+
447
+    /**
448
+     * Update setting in the database
449
+     *
450
+     * @param string $setting
451
+     * @param string $value
452
+     */
453
+    public function set($setting, $value) {
454
+        $this->config->setAppValue('theming', $setting, $value);
455
+        $this->increaseCacheBuster();
456
+    }
457
+
458
+    /**
459
+     * Revert settings to the default value
460
+     *
461
+     * @param string $setting setting which should be reverted
462
+     * @return string default value
463
+     */
464
+    public function undo($setting) {
465
+        $this->config->deleteAppValue('theming', $setting);
466
+        $this->increaseCacheBuster();
467
+
468
+        $returnValue = '';
469
+        switch ($setting) {
470
+            case 'name':
471
+                $returnValue = $this->getEntity();
472
+                break;
473
+            case 'url':
474
+                $returnValue = $this->getBaseUrl();
475
+                break;
476
+            case 'slogan':
477
+                $returnValue = $this->getSlogan();
478
+                break;
479
+            case 'color':
480
+                $returnValue = $this->getColorPrimary();
481
+                break;
482
+            case 'logo':
483
+            case 'logoheader':
484
+            case 'background':
485
+            case 'favicon':
486
+                $this->imageManager->delete($setting);
487
+                break;
488
+        }
489
+
490
+        return $returnValue;
491
+    }
492
+
493
+    /**
494
+     * Color of text in the header and primary buttons
495
+     *
496
+     * @return string
497
+     */
498
+    public function getTextColorPrimary() {
499
+        return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff';
500
+    }
501
+
502
+    /**
503
+     * Has the admin disabled user customization
504
+     */
505
+    public function isUserThemingDisabled(): bool {
506
+        return $this->config->getAppValue('theming', 'disable-user-theming', 'no') === 'yes';
507
+    }
508 508
 }
Please login to merge, or discard this patch.
apps/theming/lib/Command/UpdateConfig.php 1 patch
Indentation   +106 added lines, -106 removed lines patch added patch discarded remove patch
@@ -32,110 +32,110 @@
 block discarded – undo
32 32
 use Symfony\Component\Console\Output\OutputInterface;
33 33
 
34 34
 class UpdateConfig extends Command {
35
-	public const SUPPORTED_KEYS = [
36
-		'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'disable-user-theming'
37
-	];
38
-
39
-	public const SUPPORTED_IMAGE_KEYS = [
40
-		'background', 'logo', 'favicon', 'logoheader'
41
-	];
42
-
43
-	private $themingDefaults;
44
-	private $imageManager;
45
-	private $config;
46
-
47
-	public function __construct(ThemingDefaults $themingDefaults, ImageManager $imageManager, IConfig $config) {
48
-		parent::__construct();
49
-
50
-		$this->themingDefaults = $themingDefaults;
51
-		$this->imageManager = $imageManager;
52
-		$this->config = $config;
53
-	}
54
-
55
-	protected function configure() {
56
-		$this
57
-			->setName('theming:config')
58
-			->setDescription('Set theming app config values')
59
-			->addArgument(
60
-				'key',
61
-				InputArgument::OPTIONAL,
62
-				'Key to update the theming app configuration (leave empty to get a list of all configured values)' . PHP_EOL .
63
-				'One of: ' . implode(', ', self::SUPPORTED_KEYS)
64
-			)
65
-			->addArgument(
66
-				'value',
67
-				InputArgument::OPTIONAL,
68
-				'Value to set (leave empty to obtain the current value)'
69
-			)
70
-			->addOption(
71
-				'reset',
72
-				'r',
73
-				InputOption::VALUE_NONE,
74
-				'Reset the given config key to default'
75
-			);
76
-	}
77
-
78
-
79
-	protected function execute(InputInterface $input, OutputInterface $output): int {
80
-		$key = $input->getArgument('key');
81
-		$value = $input->getArgument('value');
82
-		assert(is_string($value) || $value === null, 'At most one value should be provided.');
83
-
84
-		if ($key === null) {
85
-			$output->writeln('Current theming config:');
86
-			foreach (self::SUPPORTED_KEYS as $key) {
87
-				$value = $this->config->getAppValue('theming', $key, '');
88
-				$output->writeln('- ' . $key . ': ' . $value . '');
89
-			}
90
-			foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
91
-				$value = $this->config->getAppValue('theming', $key . 'Mime', '');
92
-				$output->writeln('- ' . $key . ': ' . $value . '');
93
-			}
94
-			return 0;
95
-		}
96
-
97
-		if (!in_array($key, self::SUPPORTED_KEYS, true) && !in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
98
-			$output->writeln('<error>Invalid config key provided</error>');
99
-			return 1;
100
-		}
101
-
102
-		if ($input->getOption('reset')) {
103
-			$defaultValue = $this->themingDefaults->undo($key);
104
-			$output->writeln('<info>Reset ' . $key . ' to ' . $defaultValue . '</info>');
105
-			return 0;
106
-		}
107
-
108
-		if ($value === null) {
109
-			$value = $this->config->getAppValue('theming', $key, '');
110
-			if ($value !== '') {
111
-				$output->writeln('<info>' . $key . ' is currently set to ' . $value . '</info>');
112
-			} else {
113
-				$output->writeln('<info>' . $key . ' is currently not set</info>');
114
-			}
115
-			return 0;
116
-		}
117
-
118
-		if (in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
119
-			if (strpos($value, '/') !== 0) {
120
-				$output->writeln('<error>The image file needs to be provided as an absolute path: ' . $value . '.</error>');
121
-				return 1;
122
-			}
123
-			if (!file_exists($value)) {
124
-				$output->writeln('<error>File could not be found: ' . $value . '.</error>');
125
-				return 1;
126
-			}
127
-			$value = $this->imageManager->updateImage($key, $value);
128
-			$key = $key . 'Mime';
129
-		}
130
-
131
-		if ($key === 'color' && !preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
132
-			$output->writeln('<error>The given color is invalid: ' . $value . '</error>');
133
-			return 1;
134
-		}
135
-
136
-		$this->themingDefaults->set($key, $value);
137
-		$output->writeln('<info>Updated ' . $key . ' to ' . $value . '</info>');
138
-
139
-		return 0;
140
-	}
35
+    public const SUPPORTED_KEYS = [
36
+        'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color', 'disable-user-theming'
37
+    ];
38
+
39
+    public const SUPPORTED_IMAGE_KEYS = [
40
+        'background', 'logo', 'favicon', 'logoheader'
41
+    ];
42
+
43
+    private $themingDefaults;
44
+    private $imageManager;
45
+    private $config;
46
+
47
+    public function __construct(ThemingDefaults $themingDefaults, ImageManager $imageManager, IConfig $config) {
48
+        parent::__construct();
49
+
50
+        $this->themingDefaults = $themingDefaults;
51
+        $this->imageManager = $imageManager;
52
+        $this->config = $config;
53
+    }
54
+
55
+    protected function configure() {
56
+        $this
57
+            ->setName('theming:config')
58
+            ->setDescription('Set theming app config values')
59
+            ->addArgument(
60
+                'key',
61
+                InputArgument::OPTIONAL,
62
+                'Key to update the theming app configuration (leave empty to get a list of all configured values)' . PHP_EOL .
63
+                'One of: ' . implode(', ', self::SUPPORTED_KEYS)
64
+            )
65
+            ->addArgument(
66
+                'value',
67
+                InputArgument::OPTIONAL,
68
+                'Value to set (leave empty to obtain the current value)'
69
+            )
70
+            ->addOption(
71
+                'reset',
72
+                'r',
73
+                InputOption::VALUE_NONE,
74
+                'Reset the given config key to default'
75
+            );
76
+    }
77
+
78
+
79
+    protected function execute(InputInterface $input, OutputInterface $output): int {
80
+        $key = $input->getArgument('key');
81
+        $value = $input->getArgument('value');
82
+        assert(is_string($value) || $value === null, 'At most one value should be provided.');
83
+
84
+        if ($key === null) {
85
+            $output->writeln('Current theming config:');
86
+            foreach (self::SUPPORTED_KEYS as $key) {
87
+                $value = $this->config->getAppValue('theming', $key, '');
88
+                $output->writeln('- ' . $key . ': ' . $value . '');
89
+            }
90
+            foreach (self::SUPPORTED_IMAGE_KEYS as $key) {
91
+                $value = $this->config->getAppValue('theming', $key . 'Mime', '');
92
+                $output->writeln('- ' . $key . ': ' . $value . '');
93
+            }
94
+            return 0;
95
+        }
96
+
97
+        if (!in_array($key, self::SUPPORTED_KEYS, true) && !in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
98
+            $output->writeln('<error>Invalid config key provided</error>');
99
+            return 1;
100
+        }
101
+
102
+        if ($input->getOption('reset')) {
103
+            $defaultValue = $this->themingDefaults->undo($key);
104
+            $output->writeln('<info>Reset ' . $key . ' to ' . $defaultValue . '</info>');
105
+            return 0;
106
+        }
107
+
108
+        if ($value === null) {
109
+            $value = $this->config->getAppValue('theming', $key, '');
110
+            if ($value !== '') {
111
+                $output->writeln('<info>' . $key . ' is currently set to ' . $value . '</info>');
112
+            } else {
113
+                $output->writeln('<info>' . $key . ' is currently not set</info>');
114
+            }
115
+            return 0;
116
+        }
117
+
118
+        if (in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) {
119
+            if (strpos($value, '/') !== 0) {
120
+                $output->writeln('<error>The image file needs to be provided as an absolute path: ' . $value . '.</error>');
121
+                return 1;
122
+            }
123
+            if (!file_exists($value)) {
124
+                $output->writeln('<error>File could not be found: ' . $value . '.</error>');
125
+                return 1;
126
+            }
127
+            $value = $this->imageManager->updateImage($key, $value);
128
+            $key = $key . 'Mime';
129
+        }
130
+
131
+        if ($key === 'color' && !preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
132
+            $output->writeln('<error>The given color is invalid: ' . $value . '</error>');
133
+            return 1;
134
+        }
135
+
136
+        $this->themingDefaults->set($key, $value);
137
+        $output->writeln('<info>Updated ' . $key . ' to ' . $value . '</info>');
138
+
139
+        return 0;
140
+    }
141 141
 }
Please login to merge, or discard this patch.