diff --git a/CHANGELOG.md b/CHANGELOG.md index 24d5413..51ed6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ ## [Unreleased] +## 2.3.2 +### Fixed +- fix hidden menu + +## 2.3.1 +### Fixed +- fix #88: does not work with default menu + +## 2.3.0 +### Added +- fix #82: add an option to keep visible an app in both menus +- fix #83: add custom categories +- add auto-reload when settings are saved + +## 2.2.0 +### Added +- fix #84: update icons +- fix #85: use Nextcloud colors by default + +### Fixed +- fix categories order in large menu + +## 2.1.0 +### Added +- add compatibility with Nextcloud 23 + ## 2.0.1 ### Fixed - fix #78: Top menu is broken - invisible apps are shown diff --git a/README.md b/README.md index 2e359f4..62f0ad9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ =============================== Allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top. -You can also define apps that must be displayed in the top menu. Fully customisable. +You can also add and sort custom categories, define apps that must be displayed in the top menu, etc. Fully customisable. This application is rather suitable for instances that activate a lot of applications. diff --git a/appinfo/info.xml b/appinfo/info.xml index d48d084..881366c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 2.0.1 + 2.3.2 agpl Simon Vieille SideMenu @@ -46,7 +46,7 @@ If you like this application and if you want to support the development: https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png - + diff --git a/css/admin.css b/css/admin.css index a15aa64..82c6b31 100644 --- a/css/admin.css +++ b/css/admin.css @@ -97,6 +97,10 @@ margin-top: -1px; } +#apps-categories-custom-list select { + width: 100%; +} + .side-menu-setting-table { display: table; @@ -115,7 +119,7 @@ .side-menu-setting-form { display: table-cell; - width: 300px; + min-width: 300px; } .side-menu-setting-label-short { diff --git a/img/side-menu-opener-closer.svg b/img/side-menu-opener-closer.svg index 3774c6d..0aa965f 100644 --- a/img/side-menu-opener-closer.svg +++ b/img/side-menu-opener-closer.svg @@ -1,6 +1 @@ - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener-dark.svg b/img/side-menu-opener-dark.svg index 7b56e7a..c6a026c 100644 --- a/img/side-menu-opener-dark.svg +++ b/img/side-menu-opener-dark.svg @@ -1,173 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener-hamburger-2-dark.svg b/img/side-menu-opener-hamburger-2-dark.svg index 505ddee..504e037 100644 --- a/img/side-menu-opener-hamburger-2-dark.svg +++ b/img/side-menu-opener-hamburger-2-dark.svg @@ -1,102 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener-hamburger-2.svg b/img/side-menu-opener-hamburger-2.svg index b5eb64c..e7034ae 100644 --- a/img/side-menu-opener-hamburger-2.svg +++ b/img/side-menu-opener-hamburger-2.svg @@ -1,101 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener-hamburger-dark.svg b/img/side-menu-opener-hamburger-dark.svg index 5d60f54..3585049 100644 --- a/img/side-menu-opener-hamburger-dark.svg +++ b/img/side-menu-opener-hamburger-dark.svg @@ -1,92 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener-hamburger.svg b/img/side-menu-opener-hamburger.svg index c1b1325..09ae9db 100644 --- a/img/side-menu-opener-hamburger.svg +++ b/img/side-menu-opener-hamburger.svg @@ -1,91 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - + \ No newline at end of file diff --git a/img/side-menu-opener.svg b/img/side-menu-opener.svg index 32abf78..6294ecb 100644 --- a/img/side-menu-opener.svg +++ b/img/side-menu-opener.svg @@ -1,172 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/lib/Controller/CssController.php b/lib/Controller/CssController.php index cdbf11a..028ac11 100644 --- a/lib/Controller/CssController.php +++ b/lib/Controller/CssController.php @@ -27,6 +27,8 @@ use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\IRequest; use OCP\IUserSession; +use OCA\Theming\ThemingDefaults; +use OCA\SideMenu\Service\Color; class CssController extends Controller { @@ -40,12 +42,30 @@ class CssController extends Controller */ protected $user; - public function __construct(string $appName, IRequest $request, ConfigProxy $config) + /** + * @var ThemingDefaults + */ + protected $theming; + + /** + * @var Color + */ + protected $color; + + public function __construct( + string $appName, + IRequest $request, + ConfigProxy $config, + ThemingDefaults $theming, + Color $color + ) { parent::__construct($appName, $request); $this->user = OC::$server[IUserSession::class]->getUser(); $this->config = $config; + $this->theming = $theming; + $this->color = $color; } /** @@ -67,6 +87,7 @@ class CssController extends Controller { $isForced = $this->config->getAppValueBool('force', '0'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); + $topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]'); $isAccessibilityAppEnabled = $this->config->getAppValueBool('enabled', '0', 'accessibility'); $isBreezeDarkAppEnabled = $this->config->getAppValueBool('enabled', '0', 'breezedark'); @@ -74,11 +95,16 @@ class CssController extends Controller if ($this->user) { $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); + $userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]'); if (!empty($userTopMenuApps) && !$isForced) { $topMenuApps = $userTopMenuApps; } + if (!empty($userTopSideMenuApps) && !$isForced) { + $topSideMenuApps = $userTopSideMenuApps; + } + $isDarkThemeUserEnabled = $this->config->getUserValue($this->user, 'theme', '', 'accessibility') === 'dark'; $isBreezeDarkUserEnabled = $this->config->getUserValue($this->user, 'theme_enabled', '', 'breezedark'); @@ -90,23 +116,29 @@ class CssController extends Controller $isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled) || ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled); + $primaryColor = $this->theming->getColorPrimary(); + $lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2); + $darkenPrimaryColor = $this->color->adjustBrightness($primaryColor, -0.2); + $darkenPrimaryColor2 = $this->color->adjustBrightness($primaryColor, -0.3); + $textColor = $this->theming->getTextColorPrimary(); + if ($isDarkMode) { - $backgroundColor = $this->config->getAppValue('dark-mode-background-color', '#333333'); - $backgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $backgroundColor); - $currentAppBackgroundColor = $this->config->getAppValue('dark-mode-current-app-background-color', '#444444'); - $loaderColor = $this->config->getAppValue('dark-mode-loader-color', '#cccccc'); - $textColor = $this->config->getAppValue('dark-mode-text-color', '#FFFFFF'); + $backgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor); + $backgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkenPrimaryColor); + $currentAppBackgroundColor = $this->config->getAppValue('dark-mode-current-app-background-color', $darkenPrimaryColor2); + $loaderColor = $this->config->getAppValue('dark-mode-loader-color', $lightenPrimaryColor); + $textColor = $this->config->getAppValue('dark-mode-text-color', $textColor); $iconInvertFilter = abs($this->config->getAppValueInt('dark-mode-icon-invert-filter', '0')).'%'; $iconOpacity = abs($this->config->getAppValueInt('dark-mode-icon-opacity', '100') / 100); $opener = $this->config->getAppValue('dark-mode-opener', 'side-menu-opener'); $backgroundOpacity = dechex($this->config->getAppValueInt('dark-mode-background-color-opacity', '100') * 255 / 100); } else { - $backgroundColor = $this->config->getAppValue('background-color', '#333333'); - $backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor); - $currentAppBackgroundColor = $this->config->getAppValue('current-app-background-color', '#444444'); - $loaderColor = $this->config->getAppValue('loader-color', '#0e75ac'); - $textColor = $this->config->getAppValue('text-color', '#FFFFFF'); + $backgroundColor = $this->config->getAppValue('background-color', $darkenPrimaryColor); + $backgroundColorTo = $this->config->getAppValue('background-color-to', $darkenPrimaryColor); + $currentAppBackgroundColor = $this->config->getAppValue('current-app-background-color', $darkenPrimaryColor2); + $loaderColor = $this->config->getAppValue('loader-color', $lightenPrimaryColor); + $textColor = $this->config->getAppValue('text-color', $textColor); $iconInvertFilter = abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%'; $iconOpacity = abs($this->config->getAppValueInt('icon-opacity', '100') / 100); $opener = $this->config->getAppValue('opener', 'side-menu-opener'); @@ -136,6 +168,7 @@ class CssController extends Controller 'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'), 'big-menu' => $this->config->getAppValueBool('big-menu', '0'), 'top-menu-apps' => $topMenuApps, + 'top-side-menu-apps' => $topSideMenuApps, ]; } } diff --git a/lib/Controller/JsController.php b/lib/Controller/JsController.php index 135b1ab..c873826 100644 --- a/lib/Controller/JsController.php +++ b/lib/Controller/JsController.php @@ -94,6 +94,7 @@ class JsController extends Controller protected function getConfig(): array { $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); + $topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]'); $targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]'); $useAvatar = $this->config->getAppValueBool('use-avatar', '0'); $isForced = $this->config->getAppValueBool('force', '0'); @@ -103,11 +104,16 @@ class JsController extends Controller if ($this->user) { $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); + $userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]'); if (!empty($userTopMenuApps) && !$isForced) { $topMenuApps = $userTopMenuApps; } + if (!empty($userTopSideMenuApps) && !$isForced) { + $topSideMenuApps = $userTopSideMenuApps; + } + $userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1'); $userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]'); @@ -157,6 +163,7 @@ class JsController extends Controller 'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'), 'avatar' => $avatar, 'top-menu-apps' => $topMenuApps, + 'top-side-menu-apps' => $topSideMenuApps, 'target-blank-apps' => $targetBlankApps, 'settings' => $settings, 'logo' => $this->themingDefaults->getLogo(), diff --git a/lib/Controller/NavController.php b/lib/Controller/NavController.php index 4f6625e..41aff0c 100644 --- a/lib/Controller/NavController.php +++ b/lib/Controller/NavController.php @@ -187,9 +187,18 @@ class NavController extends Controller usort($items, function ($a, $b) use ($categoriesLabels) { foreach ($categoriesLabels as $key => $value) { + if ($a['categoryId'] === 'other') { + return -1; + } + + if ($b['categoryId'] === 'other') { + return 1; + } + if ($a['categoryId'] === $key) { return -1; } + if ($b['categoryId'] === $key) { return 1; } diff --git a/lib/Controller/PersonalSettingController.php b/lib/Controller/PersonalSettingController.php index 90fb758..bdc9fca 100644 --- a/lib/Controller/PersonalSettingController.php +++ b/lib/Controller/PersonalSettingController.php @@ -97,7 +97,7 @@ class PersonalSettingController extends Controller } } - if ('top-menu-apps' === $name) { + if (in_array($name, ['top-menu-apps', 'top-side-menu-apps'])) { $doSave = true; $data = json_decode($value, true); diff --git a/lib/Service/AppRepository.php b/lib/Service/AppRepository.php index 93c8f9c..d91ec75 100644 --- a/lib/Service/AppRepository.php +++ b/lib/Service/AppRepository.php @@ -21,10 +21,27 @@ class AppRepository */ protected $l10nFactory; - public function __construct(\OC_App $ocApp, IFactory $l10nFactory) + /** + * @var ConfigProxy + */ + protected $config; + + /** + * @var CategoryRepository + */ + protected $categoryRepository; + + public function __construct( + \OC_App $ocApp, + IFactory $l10nFactory, + ConfigProxy $config, + CategoryRepository $categoryRepository + ) { $this->ocApp = $ocApp; $this->l10nFactory = $l10nFactory; + $this->config = $config; + $this->categoryRepository = $categoryRepository; } /** @@ -35,6 +52,9 @@ class AppRepository public function getVisibleApps() { $navigation = $this->ocApp->getNavigation(); + $appCategoriesCustom = $this->config->getAppValueArray('apps-categories-custom', '[]'); + $categoriesCustom = $this->config->getAppValueArray('categories-custom', '[]'); + $categories = $this->categoryRepository->getOrderedCategories(); $apps = $this->ocApp->listAllApps(); $visibleApps = []; @@ -74,6 +94,12 @@ class AppRepository } } + foreach ($visibleApps as $id => $app) { + if (isset($appCategoriesCustom[$id], $categories[$appCategoriesCustom[$id]])) { + $visibleApps[$id]['category'] = [$appCategoriesCustom[$id]]; + } + } + usort($visibleApps, function ($a, $b) { return ($a['name'] < $b['name']) ? -1 : 1; }); diff --git a/lib/Service/CategoryRepository.php b/lib/Service/CategoryRepository.php index 3e67e63..7559854 100644 --- a/lib/Service/CategoryRepository.php +++ b/lib/Service/CategoryRepository.php @@ -5,6 +5,7 @@ namespace OCA\SideMenu\Service; use OC\App\AppStore\Fetcher\CategoryFetcher; use OCA\SideMenu\AppInfo\Application; use OCP\IConfig; +use OCP\IUserSession; use OCP\L10N\IFactory; /** @@ -34,16 +35,23 @@ class CategoryRepository */ protected $iConfig; + /** + * @var IUserSession + */ + protected $userSession; + public function __construct( CategoryFetcher $categoryFetcher, ConfigProxy $config, IConfig $iConfig, - IFactory $l10nFactory + IFactory $l10nFactory, + IUserSession $userSession ) { $this->categoryFetcher = $categoryFetcher; $this->l10nFactory = $l10nFactory; $this->config = $config; $this->iConfig = $iConfig; + $this->userSession = $userSession; } /** @@ -56,8 +64,8 @@ class CategoryRepository $currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2); $type = $this->config->getAppValue('categories-order-type', 'default'); $order = $this->config->getAppValueArray('categories-order', '[]'); - $categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]'); + $customCategories = $this->config->getAppValueArray('categories-custom', '[]'); if (empty($categoriesLabels)) { $categoriesLabels = $this->categoryFetcher->get(); @@ -74,6 +82,18 @@ class CategoryRepository $categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites'); $categoriesLabels['other'] = ''; + $user = $this->userSession->getUser(); + + if ($user) { + $lang = $this->iConfig->getUserValue($user->getUid(), 'core', 'lang'); + } else { + $lang = 'en'; + } + + foreach ($customCategories as $category) { + $categoriesLabels[$category['id']] = $category[$lang] ?? $category['en']; + } + asort($categoriesLabels); if ('custom' === $type) { diff --git a/lib/Service/Color.php b/lib/Service/Color.php new file mode 100644 index 0000000..cf90dd6 --- /dev/null +++ b/lib/Service/Color.php @@ -0,0 +1,34 @@ + + */ +class Color +{ + /** + * @thanks https://stackoverflow.com/posts/54393956/revision + */ + public function adjustBrightness(string $hexCode, float $adjustPercent): string + { + $hexCode = ltrim($hexCode, '#'); + + if (3 == strlen($hexCode)) { + $hexCode = $hexCode[0].$hexCode[0].$hexCode[1].$hexCode[1].$hexCode[2].$hexCode[2]; + } + + $hexCode = array_map('hexdec', str_split($hexCode, 2)); + + foreach ($hexCode as &$color) { + $adjustableLimit = $adjustPercent < 0 ? $color : 255 - $color; + $adjustAmount = ceil($adjustableLimit * $adjustPercent); + + $color = str_pad(dechex($color + $adjustAmount), 2, '0', STR_PAD_LEFT); + } + + return '#'.implode($hexCode); + } +} diff --git a/lib/Service/LangRepository.php b/lib/Service/LangRepository.php new file mode 100644 index 0000000..c998a7e --- /dev/null +++ b/lib/Service/LangRepository.php @@ -0,0 +1,43 @@ + + */ +class LangRepository +{ + /** + * @var IDBConnection + */ + protected $db; + + public function __construct(IDBConnection $db) + { + $this->db = $db; + } + + public function getUsedLangs(): array + { + $qb = $this->db->getQueryBuilder(); + + $qb->select($qb->createFunction('DISTINCT configvalue')) + ->where('configkey="lang" and appid="core" and configvalue<>"en"') + ->from('preferences') + ; + + $stmt = $qb->execute(); + + $langs = ['en']; + + foreach ($stmt->fetchAll() as $result) { + $langs[] = $result['configvalue']; + } + + return $langs; + } +} diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 8885c13..d0a532b 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -26,6 +26,9 @@ use OCP\AppFramework\Http\TemplateResponse; use OCP\IL10N; use OCP\ILogger; use OCP\Settings\ISettings; +use OCA\Theming\ThemingDefaults; +use OCA\SideMenu\Service\Color; +use OCA\SideMenu\Service\LangRepository; class Admin implements ISettings { @@ -54,18 +57,39 @@ class Admin implements ISettings */ private $categoryRepository; + /** + * @var ThemingDefaults + */ + protected $theming; + + /** + * @var Color + */ + protected $color; + + /** + * @var LangRepository + */ + protected $langRepository; + public function __construct( IL10N $l, ILogger $logger, ConfigProxy $config, AppRepository $appRepository, - CategoryRepository $categoryRepository + CategoryRepository $categoryRepository, + ThemingDefaults $theming, + Color $color, + LangRepository $langRepository ) { $this->l = $l; $this->logger = $logger; $this->config = $config; $this->appRepository = $appRepository; $this->categoryRepository = $categoryRepository; + $this->theming = $theming; + $this->color = $color; + $this->langRepository = $langRepository; } /** @@ -73,29 +97,35 @@ class Admin implements ISettings */ public function getForm() { - $backgroundColor = $this->config->getAppValue('background-color', '#333333'); - $backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor); + $primaryColor = $this->theming->getColorPrimary(); + $lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2); + $darkenPrimaryColor = $this->color->adjustBrightness($primaryColor, -0.2); + $darkenPrimaryColor2 = $this->color->adjustBrightness($primaryColor, -0.3); + $textColor = $this->theming->getTextColorPrimary(); - $darkModeBackgroundColor = $this->config->getAppValue('dark-mode-background-color', '#333333'); - $darkModeBackgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkModeBackgroundColor); + $backgroundColor = $this->config->getAppValue('background-color', $darkenPrimaryColor); + $backgroundColorTo = $this->config->getAppValue('background-color-to', $darkenPrimaryColor); + + $darkModeBackgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor); + $darkModeBackgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkenPrimaryColor); $parameters = [ 'background-color' => $backgroundColor, 'background-color-to' => $backgroundColorTo, 'background-color-opacity' => $this->config->getAppValueInt('background-color-opacity', '100'), - 'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'), - 'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'), + 'current-app-background-color' => $this->config->getAppValue('current-app-background-color', $darkenPrimaryColor2), + 'loader-color' => $this->config->getAppValue('loader-color', $lightenPrimaryColor), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'), - 'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'), + 'text-color' => $this->config->getAppValue('text-color', $textColor), 'dark-mode-background-color' => $darkModeBackgroundColor, 'dark-mode-background-color-to' => $darkModeBackgroundColorTo, 'dark-mode-background-color-opacity' => $this->config->getAppValueInt('dark-mode-background-color-opacity', '100'), - 'dark-mode-current-app-background-color' => $this->config->getAppValue('dark-mode-current-app-background-color', '#444444'), - 'dark-mode-loader-color' => $this->config->getAppValue('dark-mode-loader-color', '#cccccc'), + 'dark-mode-current-app-background-color' => $this->config->getAppValue('dark-mode-current-app-background-color', $darkenPrimaryColor2), + 'dark-mode-loader-color' => $this->config->getAppValue('dark-mode-loader-color', $textColor), 'dark-mode-icon-invert-filter' => $this->config->getAppValueInt('dark-mode-icon-invert-filter', '0'), 'dark-mode-icon-opacity' => $this->config->getAppValueInt('dark-mode-icon-opacity', '100'), - 'dark-mode-text-color' => $this->config->getAppValue('dark-mode-text-color', '#FFFFFF'), + 'dark-mode-text-color' => $this->config->getAppValue('dark-mode-text-color', $textColor), 'dark-mode-opener' => $this->config->getAppValue('dark-mode-opener', 'side-menu-opener'), 'opener' => $this->config->getAppValue('opener', 'side-menu-opener'), 'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'), @@ -117,11 +147,15 @@ class Admin implements ISettings 'force' => $this->config->getAppValue('force', '0'), 'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'), 'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'), + 'top-side-menu-apps' => $this->config->getAppValueArray('top-side-menu-apps', '[]'), 'default-enabled' => $this->config->getAppValue('default-enabled', '1'), + 'apps' => $this->appRepository->getVisibleApps(), + 'apps-categories-custom' => $this->config->getAppValueArray('apps-categories-custom', '[]'), 'categories-order-type' => $this->config->getAppValue('categories-order-type', 'default'), 'categories-order' => $this->config->getAppValueArray('categories-order', '[]'), - 'apps' => $this->appRepository->getVisibleApps(), + 'categories-custom' => $this->config->getAppValueArray('categories-custom', '[]'), 'categories' => $this->categoryRepository->getOrderedCategories(), + 'langs' => $this->langRepository->getUsedLangs(), ]; return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, ''); diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index 2b34bb2..9ac24c7 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -78,6 +78,7 @@ class Personal implements ISettings $this->config->getAppValue('default-enabled', '1') ), 'top-menu-apps' => $this->config->getUserValueArray($user, 'top-menu-apps', '[]'), + 'top-side-menu-apps' => $this->config->getUserValueArray($user, 'top-side-menu-apps', '[]'), 'target-blank-mode' => $this->config->getUserValue($user, 'target-blank-mode', '1'), 'target-blank-apps' => $this->config->getUserValueArray($user, 'target-blank-apps', '[]'), 'apps' => $this->appRepository->getVisibleApps(), diff --git a/package.json b/package.json index 38edace..f38078f 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "stylelint:fix": "stylelint src --fix" }, "dependencies": { - "@nextcloud/axios": "^1.3.2", - "@nextcloud/vue": "^1.4.0", - "axios": "^0.19.2", + "@nextcloud/axios": "^1.8.0", + "@nextcloud/vue": "^1.5.0", + "axios": "^0.24.0", "trim": "0.0.1", "vue": "^2.6.11" }, diff --git a/src/AdminCategoriesCustom.vue b/src/AdminCategoriesCustom.vue new file mode 100644 index 0000000..909580d --- /dev/null +++ b/src/AdminCategoriesCustom.vue @@ -0,0 +1,182 @@ + + + + + + diff --git a/src/SideMenu.vue b/src/SideMenu.vue index 7ab3cab..e47df87 100644 --- a/src/SideMenu.vue +++ b/src/SideMenu.vue @@ -100,7 +100,7 @@ export default { var dataId = parent.getAttribute('data-id') dataId = dataId !== null ? dataId : '' - if (!parent.classList.contains('app-hidden') && !menuIsHidden) { + if (!parent.classList.contains('app-top-side-menu') && !parent.classList.contains('app-hidden') && !menuIsHidden) { continue } diff --git a/src/admin.js b/src/admin.js index 2d63eca..b7b5982 100644 --- a/src/admin.js +++ b/src/admin.js @@ -15,6 +15,12 @@ * along with this program. If not, see . */ +import AdminCategoriesCustom from './AdminCategoriesCustom.vue' +import Vue from 'vue' + +Vue.prototype.OC = window.OC +Vue.prototype.OCA = window.OCA + let elements = [] const selector = '#side-menu-message' @@ -80,10 +86,11 @@ const saveSettings = (key) => { t('side_menu', (key + 1) + '/' + size) ) - if (key < size) { + if (key < size - 1) { saveSettings(key + 1) } else { - OC.msg.finishedSuccess(selector, t('side_menu', 'Saved')) + OC.msg.finishedSuccess(selector, t('side_menu', 'Saved! Page is reloading...')) + location.reload() } }, error: () => { @@ -108,7 +115,29 @@ const elementToggler = (element) => { element.style.display = display } +const updateAppsCategoriesCustom = () => { + let values = {} + + for (let item of document.querySelectorAll('.apps-categories-custom')) { + let app = item.getAttribute('data-app') + let value = item.value + + if (value) { + values[app] = value + } + } + + document.querySelector('#apps-categories-custom').value = JSON.stringify(values) +} + document.addEventListener('DOMContentLoaded', () => { + if (document.querySelector('#side-menu-categories-custom')) { + const View = Vue.extend(AdminCategoriesCustom) + const adminCategoriesCustom = new View({}) + + adminCategoriesCustom.$mount('#side-menu-categories-custom') + } + elements = document.querySelectorAll('.side-menu-setting') document.querySelector('#side-menu-save').addEventListener('click', (event) => { @@ -134,6 +163,12 @@ document.addEventListener('DOMContentLoaded', () => { }) } + for (let item of document.querySelectorAll('.apps-categories-custom')) { + item.addEventListener('change', (event) => { + updateAppsCategoriesCustom() + }) + } + for (let item of document.querySelectorAll('.side-menu-setting-live')) { item.addEventListener('change', (event) => { const target = event.target @@ -196,6 +231,7 @@ document.addEventListener('DOMContentLoaded', () => { let value = [] for (let item of document.querySelectorAll('#categories-list .side-menu-setting-list-item')) { + console.log(item.getAttribute('data-id')) value.push(item.getAttribute('data-id')) } diff --git a/src/l10n/fixtures/cs.yaml b/src/l10n/fixtures/cs.yaml index 215c0e3..11fc4b1 100644 --- a/src/l10n/fixtures/cs.yaml +++ b/src/l10n/fixtures/cs.yaml @@ -77,3 +77,8 @@ "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Tyto parametry jsou použity v případě, že je zapnutý (Breeze) tmavý motiv vzhledu." "Dark mode colors": "Barvy tmavého režimu" "With categories": "S kategoriemi" +"Custom categories": "Vlastní kategorie" +"Customize application categories": "Personnaliser les catégories des applications" +"Customize application categories": "Přizpůsobte kategorie aplikací" +"Apps only visible in the top menu": "Aplikace jsou viditelné pouze v horní nabídce " +"Apps visible in the top and side menus": "Aplikace viditelné v horní a boční nabídce" diff --git a/src/l10n/fixtures/de.yaml b/src/l10n/fixtures/de.yaml index 5d8353a..eb30f55 100644 --- a/src/l10n/fixtures/de.yaml +++ b/src/l10n/fixtures/de.yaml @@ -77,3 +77,7 @@ "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Optionen werden auf Dark Theme oder Breeze Dark Theme angewendet." "Dark mode colors": "Farben für den dunklen Modus" "With categories": "Mit Kategorien" +"Custom categories": "Benutzerdefinierte Kategorien" +"Customize application categories": "Anwendungskategorien anpassen" +"Apps only visible in the top menu": "Apps nur im oberen Menü sichtbar " +"Apps visible in the top and side menus": "Apps im oberen und seitlichen Menü sichtbar" diff --git a/src/l10n/fixtures/fr.yaml b/src/l10n/fixtures/fr.yaml index 56b286e..9b6a6c0 100644 --- a/src/l10n/fixtures/fr.yaml +++ b/src/l10n/fixtures/fr.yaml @@ -77,3 +77,7 @@ "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Ces paramètres sont utilisés lorsque le thème sombre ou le thème Breeze Dark sont activés." "Dark mode colors": "Couleurs du mode sombre" "With categories": "Avec les catégories" +"Custom categories": "Catégories personnalisées" +"Customize application categories": "Personnaliser les catégories des applications" +"Apps only visible in the top menu": "Applications visibles uniquement dans le menu supérieur" +"Apps visible in the top and side menus": "Applications visibles dans le menus supérieur et latéral" diff --git a/src/l10n/fixtures/zh_CN.yaml b/src/l10n/fixtures/zh_CN.yaml index 969583d..294157a 100644 --- a/src/l10n/fixtures/zh_CN.yaml +++ b/src/l10n/fixtures/zh_CN.yaml @@ -77,3 +77,7 @@ "This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "此参数将应用于暗黑主题激活时。" "Dark mode colors": "暗黑模式颜色" "With categories": "有类别" +"Custom categories": "自定义类别" +"Customize application categories": "自定义应用程序类别" +"Apps only visible in the top menu": "应用程序仅在顶部菜单中可见" +"Apps visible in the top and side menus": "顶部和侧边菜单中可见的应用程序" diff --git a/templates/css/stylesheet.php b/templates/css/stylesheet.php index e8b6a7e..379fb42 100644 --- a/templates/css/stylesheet.php +++ b/templates/css/stylesheet.php @@ -8,7 +8,7 @@ } - + #appmenu { display: none; } diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 043eb58..1cee506 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -52,13 +52,17 @@ const updateTopMenu = function() { continue } - if (topMenuApps.indexOf(dataId) === -1) { + if (topMenuApps.indexOf(dataId) === -1 && topSideMenuApps.indexOf(dataId) === -1) { app.classList.add('hidden') app.classList.add('app-hidden') } else { app.classList.remove('hidden') app.classList.add('app-external-site') + if (topSideMenuApps.indexOf(dataId) !== -1) { + app.classList.add('app-top-side-menu') + } + appShown.push(app) navigationAppsHtml = navigationAppsHtml + app.outerHTML diff --git a/templates/js/script.php b/templates/js/script.php index 8f22477..e133d10 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -186,8 +186,9 @@ if ($_['always-displayed']) { nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) - - const topMenuApps = + + const topMenuApps = + const topSideMenuApps = diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 9b53d7d..1aa2d94 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -497,7 +497,6 @@ $choicesSizes = [

t('Not compatible with touch screens.')); ?>

@@ -731,7 +730,7 @@ $choicesSizes = [
- t('Apps that not must be moved in the side menu')); ?> + t('Apps only visible in the top menu')); ?>
+ + @@ -790,6 +822,61 @@ $choicesSizes = [ +
+
+ t('Custom categories')); ?> +
+
+ + +
+
+
+
+ +
+
+ t('Customize application categories')); ?> + t('Experimental')); ?> +
+
+ + 🖱️ t('Show and hide the list of applications')); ?> + + + + + +
+
+
t('Customize sorting')); ?> diff --git a/templates/settings/personal-form.php b/templates/settings/personal-form.php index 69cd7dd..2af925f 100644 --- a/templates/settings/personal-form.php +++ b/templates/settings/personal-form.php @@ -43,69 +43,73 @@ $choicesYesNo = [ t('Menu')); ?> -
- -
-

t('Use the shortcut Ctrl+o to open and to hide the side menu. Use tab to navigate.'); ?>

-
- -
- -
- -
- -
- '1', - 'Use my selection' => '2', - ]; ?> - - -
- -

- - 🖱️ t('Show and hide the list of applications')); ?> - -

- -