diff --git a/CHANGELOG.md b/CHANGELOG.md index 845ba97..161d55a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ ## [Unreleased] +## 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 +- fix categories order in large menu ## 2.1.0 ### Added 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 126024d..e65d4cd 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.2.0 + 2.3.0 agpl Simon Vieille SideMenu 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/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/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/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 e7a2bab..d0a532b 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -28,6 +28,7 @@ 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 { @@ -66,6 +67,11 @@ class Admin implements ISettings */ protected $color; + /** + * @var LangRepository + */ + protected $langRepository; + public function __construct( IL10N $l, ILogger $logger, @@ -73,7 +79,8 @@ class Admin implements ISettings AppRepository $appRepository, CategoryRepository $categoryRepository, ThemingDefaults $theming, - Color $color + Color $color, + LangRepository $langRepository ) { $this->l = $l; $this->logger = $logger; @@ -82,6 +89,7 @@ class Admin implements ISettings $this->categoryRepository = $categoryRepository; $this->theming = $theming; $this->color = $color; + $this->langRepository = $langRepository; } /** @@ -139,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 e87767d..f38078f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@nextcloud/axios": "^1.8.0", - "@nextcloud/vue": "^1.4.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/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/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 043eb58..85ed332 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -52,7 +52,7 @@ 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 { 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')); ?> - -

- -