diff --git a/apps/accessibility/css/style.scss b/apps/accessibility/css/style.scss deleted file mode 100644 index 9dd2e428d80..00000000000 --- a/apps/accessibility/css/style.scss +++ /dev/null @@ -1,73 +0,0 @@ -// Rules we could port to the rest of Nextcloud too - -// Proper highlight for links and focus feedback -#accessibility a { - font-weight: bold; - - &:hover, - &:focus { - text-decoration: underline; - } -} - -// Highlight checkbox label in bold for focus feedback -// Drawback: Text width increases a bit -#accessibility .checkbox:focus + label { - font-weight: bold; -} - -// Limit width of settings sections for readability -#accessibility.section p { - max-width: 800px; -} - -// End of rules we could port to rest of Nextcloud - - - -.preview-list { - display: flex; - flex-direction: column; - max-width: 800px; -} - -.preview { - display: flex; - justify-content: flex-start; - margin-top: 3em; - position: relative; - - &, - * { - user-select: none; - } - - .preview-image { - flex-basis: 200px; - flex-shrink: 0; - margin-right: 1em; - background-position: top left; - background-size: cover; - background-repeat: no-repeat; - border-radius: var(--border-radius); - } - - .preview-description { - display: flex; - flex-direction: column; - - label { - padding: 12px 0; - } - } -} - -@media (max-width: ($breakpoint-mobile / 2)) { - .app-settings #accessibility .preview-list .preview { - display: unset; - - .preview-image { - height: 150px; - } - } -} diff --git a/apps/theming/appinfo/info.xml b/apps/theming/appinfo/info.xml index 3d7cabe7213..7b6e20a781f 100644 --- a/apps/theming/appinfo/info.xml +++ b/apps/theming/appinfo/info.xml @@ -23,8 +23,11 @@ OCA\Theming\Settings\Admin - OCA\Theming\Settings\Section + OCA\Theming\Settings\AdminSection + OCA\Theming\Settings\Personal + OCA\Theming\Settings\PersonalSection + OCA\Theming\Command\UpdateConfig diff --git a/apps/theming/appinfo/routes.php b/apps/theming/appinfo/routes.php index 358f6a39ad4..c9a99a409ef 100644 --- a/apps/theming/appinfo/routes.php +++ b/apps/theming/appinfo/routes.php @@ -27,54 +27,68 @@ * along with this program. If not, see . * */ -return ['routes' => [ - [ - 'name' => 'Theming#updateStylesheet', - 'url' => '/ajax/updateStylesheet', - 'verb' => 'POST' +return [ + 'routes' => [ + [ + 'name' => 'Theming#updateStylesheet', + 'url' => '/ajax/updateStylesheet', + 'verb' => 'POST' + ], + [ + 'name' => 'Theming#undo', + 'url' => '/ajax/undoChanges', + 'verb' => 'POST' + ], + [ + 'name' => 'Theming#uploadImage', + 'url' => '/ajax/uploadImage', + 'verb' => 'POST' + ], + [ + 'name' => 'Theming#getThemeVariables', + 'url' => '/theme/{themeId}.css', + 'verb' => 'GET', + ], + [ + 'name' => 'Theming#getImage', + 'url' => '/image/{key}', + 'verb' => 'GET', + ], + [ + 'name' => 'Theming#getManifest', + 'url' => '/manifest/{app}', + 'verb' => 'GET', + 'defaults' => ['app' => 'core'] + ], + [ + 'name' => 'Icon#getFavicon', + 'url' => '/favicon/{app}', + 'verb' => 'GET', + 'defaults' => ['app' => 'core'], + ], + [ + 'name' => 'Icon#getTouchIcon', + 'url' => '/icon/{app}', + 'verb' => 'GET', + 'defaults' => ['app' => 'core'], + ], + [ + 'name' => 'Icon#getThemedIcon', + 'url' => '/img/{app}/{image}', + 'verb' => 'GET', + 'requirements' => ['image' => '.+'] + ], ], - [ - 'name' => 'Theming#undo', - 'url' => '/ajax/undoChanges', - 'verb' => 'POST' - ], - [ - 'name' => 'Theming#uploadImage', - 'url' => '/ajax/uploadImage', - 'verb' => 'POST' - ], - [ - 'name' => 'Theming#getThemeVariables', - 'url' => '/theme/{themeId}.css', - 'verb' => 'GET', - ], - [ - 'name' => 'Theming#getImage', - 'url' => '/image/{key}', - 'verb' => 'GET', - ], - [ - 'name' => 'Theming#getManifest', - 'url' => '/manifest/{app}', - 'verb' => 'GET', - 'defaults' => ['app' => 'core'] - ], - [ - 'name' => 'Icon#getFavicon', - 'url' => '/favicon/{app}', - 'verb' => 'GET', - 'defaults' => ['app' => 'core'], - ], - [ - 'name' => 'Icon#getTouchIcon', - 'url' => '/icon/{app}', - 'verb' => 'GET', - 'defaults' => ['app' => 'core'], - ], - [ - 'name' => 'Icon#getThemedIcon', - 'url' => '/img/{app}/{image}', - 'verb' => 'GET', - 'requirements' => ['image' => '.+'] - ], -]]; + 'ocs' => [ + [ + 'name' => 'userTheme#enableTheme', + 'url' => '/api/v1/theme/{themeId}/enable', + 'verb' => 'PUT', + ], + [ + 'name' => 'userTheme#disableTheme', + 'url' => '/api/v1/theme/{themeId}', + 'verb' => 'DELETE', + ], + ] +]; diff --git a/apps/theming/img/dark.jpg b/apps/theming/img/dark.jpg new file mode 100644 index 00000000000..b207c390cfa Binary files /dev/null and b/apps/theming/img/dark.jpg differ diff --git a/apps/theming/img/default.jpg b/apps/theming/img/default.jpg new file mode 100644 index 00000000000..ad3fafd96f2 Binary files /dev/null and b/apps/theming/img/default.jpg differ diff --git a/apps/theming/img/highcontrast.jpg b/apps/theming/img/highcontrast.jpg new file mode 100644 index 00000000000..8c55a7358b9 Binary files /dev/null and b/apps/theming/img/highcontrast.jpg differ diff --git a/apps/theming/img/opendyslexic.jpg b/apps/theming/img/opendyslexic.jpg new file mode 100644 index 00000000000..db8e60f3658 Binary files /dev/null and b/apps/theming/img/opendyslexic.jpg differ diff --git a/apps/theming/lib/Controller/UserThemeController.php b/apps/theming/lib/Controller/UserThemeController.php new file mode 100644 index 00000000000..ec379d2e6fa --- /dev/null +++ b/apps/theming/lib/Controller/UserThemeController.php @@ -0,0 +1,111 @@ + + * @copyright Copyright (c) 2019 Janis Köhr + * + * @author Christoph Wurst + * @author Daniel Kesselberg + * @author Janis Köhr + * @author John Molakvoæ + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Theming\Controller; + +use OCA\Theming\Service\ThemesService; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCSController; +use OCP\IConfig; +use OCP\IRequest; +use OCP\IUserSession; +use OCP\PreConditionNotMetException; + +class UserThemeController extends OCSController { + + protected string $userId; + private IConfig $config; + private IUserSession $userSession; + private ThemesService $themesService; + + /** + * Config constructor. + */ + public function __construct(string $appName, + IRequest $request, + IConfig $config, + IUserSession $userSession, + ThemesService $themesService) { + parent::__construct($appName, $request); + $this->config = $config; + $this->userSession = $userSession; + $this->themesService = $themesService; + $this->userId = $userSession->getUser()->getUID(); + } + + /** + * @NoAdminRequired + * + * Enable theme + * + * @param string $themeId the theme ID + * @return DataResponse + * @throws OCSBadRequestException|PreConditionNotMetException + */ + public function enableTheme(string $themeId): DataResponse { + if ($themeId === '' || !$themeId) { + throw new OCSBadRequestException('Invalid theme id: ' . $themeId); + } + + $themes = $this->themesService->getThemes(); + if (!isset($themes[$themeId])) { + throw new OCSBadRequestException('Invalid theme id: ' . $themeId); + } + + // Enable selected theme + $this->themesService->enableTheme($themes[$themeId]); + return new DataResponse(); + } + + /** + * @NoAdminRequired + * + * Disable theme + * + * @param string $themeId the theme ID + * @return DataResponse + * @throws OCSBadRequestException|PreConditionNotMetException + */ + public function disableTheme(string $themeId): DataResponse { + if ($themeId === '' || !$themeId) { + throw new OCSBadRequestException('Invalid theme id: ' . $themeId); + } + + $themes = $this->themesService->getThemes(); + if (!isset($themes[$themeId])) { + throw new OCSBadRequestException('Invalid theme id: ' . $themeId); + } + + // Enable selected theme + $this->themesService->disableTheme($themes[$themeId]); + return new DataResponse(); + } +} diff --git a/apps/theming/lib/ITheme.php b/apps/theming/lib/ITheme.php index 7f3e49075ca..20508fac4e8 100644 --- a/apps/theming/lib/ITheme.php +++ b/apps/theming/lib/ITheme.php @@ -30,12 +30,46 @@ namespace OCA\Theming; */ interface ITheme { + const TYPE_THEME = 1; + const TYPE_FONT = 2; + /** * Unique theme id + * Will be used to search for ID.png in the img folder + * * @since 25.0.0 */ public function getId(): string; + /** + * Theme type + * TYPE_THEME or TYPE_FONT + * + * @since 25.0.0 + */ + public function getType(): int; + + /** + * The theme translated title + * + * @since 25.0.0 + */ + public function getTitle(): string; + + /** + * The theme enable checkbox translated label + * + * @since 25.0.0 + */ + public function getEnableLabel(): string; + + /** + * The theme translated description + * + * @since 25.0.0 + */ + public function getDescription(): string; + /** * Get the media query triggering this theme * Optional, ignored if falsy diff --git a/apps/theming/lib/Service/ThemesService.php b/apps/theming/lib/Service/ThemesService.php index 832c443a2e1..8b39da6bb5d 100644 --- a/apps/theming/lib/Service/ThemesService.php +++ b/apps/theming/lib/Service/ThemesService.php @@ -23,18 +23,18 @@ namespace OCA\Theming\Service; use OCA\Theming\AppInfo\Application; -use OCA\Theming\Themes\DefaultTheme; -use OCA\Theming\Themes\DarkTheme; -use OCA\Theming\Themes\DarkHighContrastTheme; -use OCA\Theming\Themes\HighContrastTheme; use OCA\Theming\ITheme; -use OCP\IAppConfig; +use OCA\Theming\Themes\DarkHighContrastTheme; +use OCA\Theming\Themes\DarkTheme; +use OCA\Theming\Themes\DefaultTheme; +use OCA\Theming\Themes\DyslexiaFont; +use OCA\Theming\Themes\HighContrastTheme; use OCP\IConfig; use OCP\IUser; use OCP\IUserSession; class ThemesService { - private IUserSession $session; + private IUserSession $userSession; private IConfig $config; /** @var ITheme[] */ @@ -45,7 +45,8 @@ class ThemesService { DefaultTheme $defaultTheme, DarkTheme $darkTheme, DarkHighContrastTheme $darkHighContrastTheme, - HighContrastTheme $highContrastTheme) { + HighContrastTheme $highContrastTheme, + DyslexiaFont $dyslexiaFont) { $this->userSession = $userSession; $this->config = $config; @@ -53,28 +54,57 @@ class ThemesService { $this->themesProviders = [ $defaultTheme->getId() => $defaultTheme, $darkTheme->getId() => $darkTheme, - $darkHighContrastTheme->getId() => $darkHighContrastTheme, $highContrastTheme->getId() => $highContrastTheme, + $darkHighContrastTheme->getId() => $darkHighContrastTheme, + $dyslexiaFont->getId() => $dyslexiaFont, ]; } + /** + * Get the list of all registered themes + * + * @return ITheme[] + */ public function getThemes(): array { return $this->themesProviders; } - public function getThemeVariables(string $id): array { - return $this->themesProviders[$id]->getCSSVariables(); - } - + /** + * Enable a theme for the logged-in user + * + * @param ITheme $theme the theme to enable + */ public function enableTheme(ITheme $theme): void { - $themes = $this->getEnabledThemes(); - array_push($themes, $theme->getId()); - $this->setEnabledThemes($themes); + $themesIds = $this->getEnabledThemes(); + + /** @var ITheme[] */ + $themes = array_map(function($themeId) { + return $this->getThemes()[$themeId]; + }, $themesIds); + + // Filtering all themes with the same type + $filteredThemes = array_filter($themes, function($t) use ($theme) { + return $theme->getType() === $t->getType(); + }); + + // Disable all the other themes of the same type + // as there can only be one enabled at the same time + foreach ($filteredThemes as $t) { + $this->disableTheme($t); + } + + $this->setEnabledThemes([...$this->getEnabledThemes(), $theme->getId()]); } + /** + * Disable a theme for the logged-in user + * + * @param ITheme $theme the theme to disable + */ public function disableTheme(ITheme $theme): void { // Using keys as it's faster $themes = $this->getEnabledThemes(); + // If enabled, removing it if (in_array($theme->getId(), $themes)) { $this->setEnabledThemes(array_filter($themes, function($themeId) use ($theme) { @@ -83,6 +113,12 @@ class ThemesService { } } + /** + * Check whether a theme is enabled or not + * for the logged-in user + * + * @return bool + */ public function isEnabled(ITheme $theme): bool { $user = $this->userSession->getUser(); if ($user instanceof IUser) { @@ -92,12 +128,27 @@ class ThemesService { } } + /** + * Get the list of all enabled themes IDs + * for the logged-in user + * + * @return string[] + */ public function getEnabledThemes(): array { $user = $this->userSession->getUser(); - $enabledThemes = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', '[]'); - return json_decode($enabledThemes); + try { + return json_decode($this->config->getUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', '[]')); + } catch (\Exception $e) { + return []; + } } + /** + * Set the list of enabled themes + * for the logged-in user + * + * @param string[] $themes the list of enabled themes IDs + */ private function setEnabledThemes(array $themes): void { $user = $this->userSession->getUser(); $this->config->setUserValue($user->getUID(), Application::APP_ID, 'enabled-themes', json_encode(array_unique($themes))); diff --git a/apps/theming/lib/Settings/Admin.php b/apps/theming/lib/Settings/Admin.php index 045f0b3fe77..6caa174d99b 100644 --- a/apps/theming/lib/Settings/Admin.php +++ b/apps/theming/lib/Settings/Admin.php @@ -36,22 +36,20 @@ use OCP\IURLGenerator; use OCP\Settings\IDelegatedSettings; class Admin implements IDelegatedSettings { - /** @var IConfig */ - private $config; - /** @var IL10N */ - private $l; - /** @var ThemingDefaults */ - private $themingDefaults; - /** @var IURLGenerator */ - private $urlGenerator; - /** @var ImageManager */ - private $imageManager; + private string $appName; + private IConfig $config; + private IL10N $l; + private ThemingDefaults $themingDefaults; + private IURLGenerator $urlGenerator; + private ImageManager $imageManager; - public function __construct(IConfig $config, + public function __construct(string $appName, + IConfig $config, IL10N $l, ThemingDefaults $themingDefaults, IURLGenerator $urlGenerator, ImageManager $imageManager) { + $this->appName = $appName; $this->config = $config; $this->l = $l; $this->themingDefaults = $themingDefaults; @@ -86,14 +84,14 @@ class Admin implements IDelegatedSettings { 'privacyUrl' => $this->themingDefaults->getPrivacyUrl(), ]; - return new TemplateResponse('theming', 'settings-admin', $parameters, ''); + return new TemplateResponse($this->appName, 'settings-admin', $parameters, ''); } /** * @return string the section ID, e.g. 'sharing' */ public function getSection(): string { - return 'theming'; + return $this->appName; } /** @@ -113,7 +111,7 @@ class Admin implements IDelegatedSettings { public function getAuthorizedAppConfig(): array { return [ - 'theming' => '/.*/', + $this->appName => '/.*/', ]; } } diff --git a/apps/theming/lib/Settings/Section.php b/apps/theming/lib/Settings/AdminSection.php similarity index 84% rename from apps/theming/lib/Settings/Section.php rename to apps/theming/lib/Settings/AdminSection.php index fe2cc9243bb..2fcc81a9279 100644 --- a/apps/theming/lib/Settings/Section.php +++ b/apps/theming/lib/Settings/AdminSection.php @@ -26,17 +26,13 @@ use OCP\IL10N; use OCP\IURLGenerator; use OCP\Settings\IIconSection; -class Section implements IIconSection { - /** @var IL10N */ - private $l; - /** @var IURLGenerator */ - private $url; +class AdminSection implements IIconSection { + private string $appName; + private IL10N $l; + private IURLGenerator $url; - /** - * @param IURLGenerator $url - * @param IL10N $l - */ - public function __construct(IURLGenerator $url, IL10N $l) { + public function __construct(string $appName, IURLGenerator $url, IL10N $l) { + $this->appName = $appName; $this->url = $url; $this->l = $l; } @@ -48,7 +44,7 @@ class Section implements IIconSection { * @returns string */ public function getID() { - return 'theming'; + return $this->appName; } /** @@ -76,6 +72,6 @@ class Section implements IIconSection { * {@inheritdoc} */ public function getIcon() { - return $this->url->imagePath('theming', 'app-dark.svg'); + return $this->url->imagePath($this->appName, 'app-dark.svg'); } } diff --git a/apps/theming/lib/Settings/Personal.php b/apps/theming/lib/Settings/Personal.php new file mode 100644 index 00000000000..6dd865b9cf6 --- /dev/null +++ b/apps/theming/lib/Settings/Personal.php @@ -0,0 +1,93 @@ + + * @copyright Copyright (c) 2019 Janis Köhr + * + * @author Christoph Wurst + * @author John Molakvoæ + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Theming\Settings; + +use OCA\Theming\Service\ThemesService; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IConfig; +use OCP\IUserSession; +use OCP\Settings\ISettings; +use OCP\Util; + +class Personal implements ISettings { + + protected string $appName; + private IConfig $config; + private IUserSession $userSession; + private ThemesService $themesService; + private IInitialState $initialStateService; + + public function __construct(string $appName, + IConfig $config, + IUserSession $userSession, + ThemesService $themesService, + IInitialState $initialStateService) { + $this->appName = $appName; + $this->config = $config; + $this->userSession = $userSession; + $this->themesService = $themesService; + $this->initialStateService = $initialStateService; + } + + public function getForm(): TemplateResponse { + $themes = array_map(function($theme) { + return [ + 'id' => $theme->getId(), + 'type' => $theme->getType(), + 'title' => $theme->getTitle(), + 'enableLabel' => $theme->getEnableLabel(), + 'description' => $theme->getDescription(), + 'enabled' => $this->themesService->isEnabled($theme), + ]; + }, $this->themesService->getThemes()); + + $this->initialStateService->provideInitialState('themes', array_values($themes)); + Util::addScript($this->appName, 'theming-settings'); + + return new TemplateResponse($this->appName, 'settings-personal'); + } + + /** + * @return string the section ID, e.g. 'sharing' + * @since 9.1 + */ + public function getSection(): string { + return $this->appName; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + * @since 9.1 + */ + public function getPriority(): int { + return 40; + } +} diff --git a/apps/theming/lib/Settings/PersonalSection.php b/apps/theming/lib/Settings/PersonalSection.php new file mode 100644 index 00000000000..821708e3970 --- /dev/null +++ b/apps/theming/lib/Settings/PersonalSection.php @@ -0,0 +1,100 @@ + + * + * @author Christoph Wurst + * @author John Molakvoæ + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Theming\Settings; + +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IIconSection; + +class PersonalSection implements IIconSection { + + /** @var string */ + protected $appName; + + /** @var IURLGenerator */ + private $urlGenerator; + + /** @var IL10N */ + private $l; + + /** + * Personal Section constructor. + * + * @param string $appName + * @param IURLGenerator $urlGenerator + * @param IL10N $l + */ + public function __construct(string $appName, + IURLGenerator $urlGenerator, + IL10N $l) { + $this->appName = $appName; + $this->urlGenerator = $urlGenerator; + $this->l = $l; + } + + /** + * returns the relative path to an 16*16 icon describing the section. + * e.g. '/core/img/places/files.svg' + * + * @returns string + * @since 13.0.0 + */ + public function getIcon() { + return $this->urlGenerator->imagePath($this->appName, 'app-dark.svg'); + } + + /** + * returns the ID of the section. It is supposed to be a lower case string, + * e.g. 'ldap' + * + * @returns string + * @since 9.1 + */ + public function getID() { + return $this->appName; + } + + /** + * returns the translated name as it should be displayed, e.g. 'LDAP / AD + * integration'. Use the L10N service to translate it. + * + * @return string + * @since 9.1 + */ + public function getName() { + return $this->l->t('Appearance and accessibility'); + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the settings navigation. The sections are arranged in ascending order of + * the priority values. It is required to return a value between 0 and 99. + * + * E.g.: 70 + * @since 9.1 + */ + public function getPriority() { + return 15; + } +} diff --git a/apps/theming/lib/Themes/DarkHighContrastTheme.php b/apps/theming/lib/Themes/DarkHighContrastTheme.php index 1f00990c7de..8d0b134c75f 100644 --- a/apps/theming/lib/Themes/DarkHighContrastTheme.php +++ b/apps/theming/lib/Themes/DarkHighContrastTheme.php @@ -36,6 +36,18 @@ class DarkHighContrastTheme extends HighContrastTheme implements ITheme { return '(prefers-color-scheme: dark) and (prefers-contrast: more)'; } + public function getTitle(): string { + return $this->l->t('Dark theme with high contrast mode'); + } + + public function getEnableLabel(): string { + return $this->l->t('Enable dark high contrast mode'); + } + + public function getDescription(): string { + return $this->l->t('Similar to the high contrast mode, but with dark colours.'); + } + public function getCSSVariables(): array { $variables = parent::getCSSVariables(); diff --git a/apps/theming/lib/Themes/DarkTheme.php b/apps/theming/lib/Themes/DarkTheme.php index b7ec16aa56b..c00f8a7ea4d 100644 --- a/apps/theming/lib/Themes/DarkTheme.php +++ b/apps/theming/lib/Themes/DarkTheme.php @@ -36,6 +36,18 @@ class DarkTheme extends DefaultTheme implements ITheme { return '(prefers-color-scheme: dark)'; } + public function getTitle(): string { + return $this->l->t('Dark theme'); + } + + public function getEnableLabel(): string { + return $this->l->t('Enable dark theme'); + } + + public function getDescription(): string { + return $this->l->t('A dark theme to ease your eyes by reducing the overall luminosity and brightness. It is still under development, so please report any issues you may find.'); + } + public function getCSSVariables(): array { $defaultVariables = parent::getCSSVariables(); diff --git a/apps/theming/lib/Themes/DefaultTheme.php b/apps/theming/lib/Themes/DefaultTheme.php index 990b011bae9..3b194a36546 100644 --- a/apps/theming/lib/Themes/DefaultTheme.php +++ b/apps/theming/lib/Themes/DefaultTheme.php @@ -29,6 +29,7 @@ use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; use OCA\Theming\ITheme; use OCP\IConfig; +use OCP\IL10N; use OCP\IURLGenerator; class DefaultTheme implements ITheme { @@ -37,6 +38,7 @@ class DefaultTheme implements ITheme { public IURLGenerator $urlGenerator; public ImageManager $imageManager; public IConfig $config; + public IL10N $l; public string $primaryColor; @@ -44,12 +46,14 @@ class DefaultTheme implements ITheme { ThemingDefaults $themingDefaults, IURLGenerator $urlGenerator, ImageManager $imageManager, - IConfig $config) { + IConfig $config, + IL10N $l) { $this->util = $util; $this->themingDefaults = $themingDefaults; $this->urlGenerator = $urlGenerator; $this->imageManager = $imageManager; $this->config = $config; + $this->l = $l; $this->primaryColor = $this->themingDefaults->getColorPrimary(); } @@ -58,6 +62,22 @@ class DefaultTheme implements ITheme { return 'default'; } + public function getType(): int { + return ITheme::TYPE_THEME; + } + + public function getTitle(): string { + return $this->l->t('Light theme'); + } + + public function getEnableLabel(): string { + return $this->l->t('Enable the default light theme'); + } + + public function getDescription(): string { + return $this->l->t('The default light appearance.'); + } + public function getMediaQuery(): string { return ''; } diff --git a/apps/theming/lib/Themes/DyslexiaFont.php b/apps/theming/lib/Themes/DyslexiaFont.php new file mode 100644 index 00000000000..460147b9fa3 --- /dev/null +++ b/apps/theming/lib/Themes/DyslexiaFont.php @@ -0,0 +1,75 @@ + + * + * @author Joas Schilling + * @author John Molakvoæ + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Theming\Themes; + +use OCA\Theming\ITheme; + +class DyslexiaFont extends DefaultTheme implements ITheme { + + public function getId(): string { + return 'opendyslexic'; + } + + public function getType(): int { + return ITheme::TYPE_FONT; + } + + public function getTitle(): string { + return $this->l->t('Dyslexia font'); + } + + public function getEnableLabel(): string { + return $this->l->t('Enable dyslexia font'); + } + + public function getDescription(): string { + return $this->l->t('OpenDyslexic is a free typeface/font designed to mitigate some of the common reading errors caused by dyslexia.'); + } + + public function getCSSVariables(): array { + $variables = parent::getCSSVariables(); + $originalFontFace = $variables['--font-face']; + + $variables = [ + '--font-face' => 'OpenDyslexic, ' . $originalFontFace + ]; + + return $variables; + } +} + +// @font-face { +// font-family: 'OpenDyslexic'; +// font-style: normal; +// font-weight: 400; +// src: url('../fonts/OpenDyslexic-Regular.woff') format('woff'); +// } + +// @font-face { +// font-family: 'OpenDyslexic'; +// font-style: normal; +// font-weight: 700; +// src: url('../fonts/OpenDyslexic-Bold.woff') format('woff'); +// } diff --git a/apps/theming/lib/Themes/HighContrastTheme.php b/apps/theming/lib/Themes/HighContrastTheme.php index cae7cc5be98..67276e4ef00 100644 --- a/apps/theming/lib/Themes/HighContrastTheme.php +++ b/apps/theming/lib/Themes/HighContrastTheme.php @@ -36,6 +36,18 @@ class HighContrastTheme extends DefaultTheme implements ITheme { return '(prefers-contrast: more)'; } + public function getTitle(): string { + return $this->l->t('High contrast mode'); + } + + public function getEnableLabel(): string { + return $this->l->t('Enable high contrast mode'); + } + + public function getDescription(): string { + return $this->l->t('A high contrast mode to ease your navigation. Visual quality will be reduced but clarity will be increased.'); + } + public function getCSSVariables(): array { $variables = parent::getCSSVariables(); diff --git a/apps/theming/src/UserThemes.vue b/apps/theming/src/UserThemes.vue new file mode 100644 index 00000000000..78115021412 --- /dev/null +++ b/apps/theming/src/UserThemes.vue @@ -0,0 +1,175 @@ + + + + diff --git a/apps/theming/src/components/ItemPreview.vue b/apps/theming/src/components/ItemPreview.vue new file mode 100644 index 00000000000..997d66a037e --- /dev/null +++ b/apps/theming/src/components/ItemPreview.vue @@ -0,0 +1,121 @@ + + + + diff --git a/apps/theming/src/settings.js b/apps/theming/src/settings.js new file mode 100644 index 00000000000..94ae6fd6314 --- /dev/null +++ b/apps/theming/src/settings.js @@ -0,0 +1,32 @@ +/** + * @copyright Copyright (c) 2018 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import Vue from 'vue' +import App from './UserThemes.vue' + +// bind to window +Vue.prototype.OC = OC +Vue.prototype.t = t + +const View = Vue.extend(App) +const accessibility = new View() +accessibility.$mount('#theming') diff --git a/apps/theming/templates/settings-personal.php b/apps/theming/templates/settings-personal.php new file mode 100644 index 00000000000..4ba1aa47e6f --- /dev/null +++ b/apps/theming/templates/settings-personal.php @@ -0,0 +1,26 @@ + + * + * @author John Molakvoæ + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +?> + + \ No newline at end of file diff --git a/webpack.modules.js b/webpack.modules.js index 593c201a08d..90c4a2b550c 100644 --- a/webpack.modules.js +++ b/webpack.modules.js @@ -87,6 +87,9 @@ module.exports = { systemtags: { systemtags: path.join(__dirname, 'apps/systemtags/src', 'systemtags.js'), }, + theming: { + 'theming-settings': path.join(__dirname, 'apps/theming/src', 'settings.js'), + }, twofactor_backupcodes: { settings: path.join(__dirname, 'apps/twofactor_backupcodes/src', 'settings.js'), },