From fdd8f8850d6f39e797779c7faaaca56bf433dfff Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 13 Apr 2025 20:32:13 +0200 Subject: [PATCH] add user settings --- lib/Controller/AdminSettingController.php | 8 +- lib/Controller/PersonalSettingController.php | 78 ++++-- src/admin.js | 2 +- src/components/settings/AdminSaveButton.vue | 90 ++++++ src/components/settings/TableLabel.vue | 11 +- src/components/settings/TableRow.vue | 21 +- src/components/settings/UserSaveButton.vue | 97 +++++++ .../settings/form/FormAppPicker.vue | 2 +- src/components/settings/form/FormAppSort.vue | 2 +- src/components/settings/form/FormCatSort.vue | 5 +- src/menus/SimpleSideMenu.vue | 4 + src/pages/AdminSettings.vue | 60 ++-- src/pages/UserSettings.vue | 171 +++++++++++ src/scss/admin.scss | 24 +- src/scss/menu.scss | 5 - src/store/config.js | 28 +- src/user.js | 32 +++ templates/settings/personal-form.php | 249 +--------------- templates/settings/personal-form.php.bk | 265 ++++++++++++++++++ webpack.config.js | 1 + 20 files changed, 839 insertions(+), 316 deletions(-) create mode 100644 src/components/settings/AdminSaveButton.vue create mode 100644 src/components/settings/UserSaveButton.vue create mode 100644 src/pages/UserSettings.vue create mode 100644 src/user.js create mode 100644 templates/settings/personal-form.php.bk diff --git a/lib/Controller/AdminSettingController.php b/lib/Controller/AdminSettingController.php index a70b0f3..e9ba8f9 100644 --- a/lib/Controller/AdminSettingController.php +++ b/lib/Controller/AdminSettingController.php @@ -98,7 +98,6 @@ class AdminSettingController extends Controller 'add-logo-link', 'show-settings', 'loader-enabled', - 'top-menu-mouse-over-hidden-label', 'always-displayed', 'enabled', 'force', @@ -139,7 +138,6 @@ class AdminSettingController extends Controller 'add-logo-link' => '1', 'show-settings' => '0', 'loader-enabled' => '1', - 'top-menu-mouse-over-hidden-label' => '0', 'always-displayed' => '0', 'enabled' => '1', 'force' => '0', @@ -207,6 +205,12 @@ class AdminSettingController extends Controller } } + foreach ($defaults as $key => $default) { + if (!array_key_exists($key, $config)) { + $config[$key] = $default; + } + } + $config['langs'] = $this->langRepository->getUsedLangs(); return new JSONResponse($config); diff --git a/lib/Controller/PersonalSettingController.php b/lib/Controller/PersonalSettingController.php index e7d00c7..9b63576 100644 --- a/lib/Controller/PersonalSettingController.php +++ b/lib/Controller/PersonalSettingController.php @@ -25,6 +25,7 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\JSONResponse; use OCP\IConfig; use OCP\IRequest; use OCP\IUserSession; @@ -43,7 +44,7 @@ class PersonalSettingController extends Controller #[NoCSRFRequired] #[NoAdminRequired] - #[FrontpageRoute(verb: 'POST', url: '/personalSetting/valueSet')] + #[FrontpageRoute(verb: 'POST', url: '/user/valueSet')] public function valueSet($name, $value): array { $doSave = false; @@ -65,22 +66,7 @@ class PersonalSettingController extends Controller } } - if ('target-blank-apps' === $name) { - $doSave = true; - $data = json_decode($value, true); - - if (!is_array($data)) { - $doSave = false; - } else { - foreach ($data as $v) { - if (!is_string($v)) { - $doSave = false; - } - } - } - } - - if (in_array($name, ['top-menu-apps', 'top-side-menu-apps', 'apps-order'])) { + if (in_array($name, ['target-blank-apps', 'top-menu-apps', 'top-side-menu-apps', 'apps-order'])) { $doSave = true; $data = json_decode($value, true); @@ -110,4 +96,62 @@ class PersonalSettingController extends Controller return []; } + + #[NoCSRFRequired] + #[FrontpageRoute(verb: 'GET', url: '/user/config')] + public function configuration(): JSONResponse + { + $user = $this->userSession->getUser(); + $keys = $this->config->getUserKeys($user->getUid(), Application::APP_ID); + + $booleans = [ + 'enabled', + ]; + + $arrays = [ + 'apps-order', + 'target-blank-apps', + 'top-menu-apps', + 'top-side-menu-apps', + ]; + + $integers = [ + 'target-blank-mode', + ]; + + $defaults = [ + 'enabled' => '1', + 'target-blank-mode' => '1', + 'apps-order' => '[]', + 'target-blank-apps' => '[]', + 'top-menu-apps' => '[]', + 'top-side-menu-apps' => '[]', + ]; + + $config = []; + + foreach ($keys as $key) { + if (!isset($defaults[$key])) { + continue; + } + + if (in_array($key, $booleans)) { + $config[$key] = $this->configProxy->getUserValueBool($user, $key, $defaults[$key]); + } elseif (in_array($key, $arrays)) { + $config[$key] = $this->configProxy->getUserValueArray($user, $key, $defaults[$key]); + } elseif (in_array($key, $integers)) { + $config[$key] = $this->configProxy->getUserValueInt($user, $key, $defaults[$key]); + } else { + $config[$key] = $this->configProxy->getUserValue($user, $key, $defaults[$key]); + } + } + + foreach ($defaults as $key => $default) { + if (!array_key_exists($key, $config)) { + $config[$key] = $default; + } + } + + return new JSONResponse($config); + } } diff --git a/src/admin.js b/src/admin.js index 1749f6f..06cdbe5 100644 --- a/src/admin.js +++ b/src/admin.js @@ -21,7 +21,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import { waitContainer } from './lib/dom.js' -import AdminSettings from './pages/AdminSettings.vue' +import AdminSettings from './pages/AdminSettings' waitContainer('#side-menu-admin-settings').then((selector) => { const pinia = createPinia() diff --git a/src/components/settings/AdminSaveButton.vue b/src/components/settings/AdminSaveButton.vue new file mode 100644 index 0000000..6209dce --- /dev/null +++ b/src/components/settings/AdminSaveButton.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/settings/TableLabel.vue b/src/components/settings/TableLabel.vue index 2c3a215..e808fa1 100644 --- a/src/components/settings/TableLabel.vue +++ b/src/components/settings/TableLabel.vue @@ -27,7 +27,11 @@ along with this program. If not, see . + @@ -58,5 +62,10 @@ const { short, label } = defineProps({ required: false, default: null, }, + help2: { + type: [String, null], + required: false, + default: null, + }, }) diff --git a/src/components/settings/TableRow.vue b/src/components/settings/TableRow.vue index ac757fc..06c77fa 100644 --- a/src/components/settings/TableRow.vue +++ b/src/components/settings/TableRow.vue @@ -15,7 +15,26 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> + + + + diff --git a/src/components/settings/UserSaveButton.vue b/src/components/settings/UserSaveButton.vue new file mode 100644 index 0000000..7e764d7 --- /dev/null +++ b/src/components/settings/UserSaveButton.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/settings/form/FormAppPicker.vue b/src/components/settings/form/FormAppPicker.vue index ae18aaa..8cda708 100644 --- a/src/components/settings/form/FormAppPicker.vue +++ b/src/components/settings/form/FormAppPicker.vue @@ -74,7 +74,7 @@ const closeModal = () => { } onMounted(async () => { - apps.value = await navStore.getApps() + apps.value = await navStore.getCoreApps() }) diff --git a/src/components/settings/form/FormAppSort.vue b/src/components/settings/form/FormAppSort.vue index 3d2a3ed..9429c82 100644 --- a/src/components/settings/form/FormAppSort.vue +++ b/src/components/settings/form/FormAppSort.vue @@ -104,7 +104,7 @@ const update = () => { } onMounted(async () => { - const items = await navStore.getApps() + const items = await navStore.getCoreApps() window.setTimeout(() => { setApps(items) diff --git a/src/components/settings/form/FormCatSort.vue b/src/components/settings/form/FormCatSort.vue index ded9f04..4d92650 100644 --- a/src/components/settings/form/FormCatSort.vue +++ b/src/components/settings/form/FormCatSort.vue @@ -35,10 +35,9 @@ along with this program. If not, see . @end="update" > diff --git a/src/menus/SimpleSideMenu.vue b/src/menus/SimpleSideMenu.vue index 793c321..d1b58f7 100644 --- a/src/menus/SimpleSideMenu.vue +++ b/src/menus/SimpleSideMenu.vue @@ -123,6 +123,8 @@ watch( function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) { const data = [] + console.log(order) + items.forEach((item) => { if (topMenuApps.includes(item.id) && !topSideMenuApps.includes(item.id)) { return @@ -139,6 +141,8 @@ function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) { data.push(item) }) + console.log(data) + return data.sort((a, b) => { return a.order < b.order ? -1 : 1 }) diff --git a/src/pages/AdminSettings.vue b/src/pages/AdminSettings.vue index c0d9898..f2b31a5 100644 --- a/src/pages/AdminSettings.vue +++ b/src/pages/AdminSettings.vue @@ -46,7 +46,7 @@ along with this program. If not, see . /> - + . - + . - + . + @@ -143,6 +144,7 @@ along with this program. If not, see . /> + @@ -175,12 +177,13 @@ along with this program. If not, see . + - + . v-model="config['categories-order-type']" :options="[ { id: 'default', label: 'Name' }, - { id: 'custom', label: 'Customed' }, + { id: 'custom', label: 'Custom' }, ]" /> - - + + + + + + + . - - - - - - + @@ -272,13 +275,13 @@ along with this program. If not, see . + @@ -415,9 +418,14 @@ along with this program. If not, see . /> + +

+ {{ t('side_menu', 'Use the shortcut Ctrl+o to open and to hide the side menu. Use tab key to navigate.') }} +

+ @@ -450,6 +458,7 @@ along with this program. If not, see . +
@@ -557,6 +566,7 @@ along with this program. If not, see . + @@ -572,6 +582,7 @@ import TableRow from '../components/settings/TableRow' import TableLabel from '../components/settings/TableLabel' import TableValue from '../components/settings/TableValue' import SectionTitle from '../components/settings/SectionTitle' +import AdminSaveButton from '../components/settings/AdminSaveButton' import FormRange from '../components/settings/form/FormRange' import FormColorPicker from '../components/settings/form/FormColorPicker' import FormOpener from '../components/settings/form/FormOpener' @@ -587,11 +598,11 @@ import FormAppCategory from '../components/settings/form/FormAppCategory' const menu = [ { label: 'Global', section: 'global' }, { label: 'Panel', section: 'panel' }, - { label: 'Top menu', section: 'topMenu' }, { label: 'Colors', section: 'colors' }, { label: 'Opener', section: 'opener' }, { label: 'Applications', section: 'apps' }, { label: 'Categories', section: 'cats' }, + { label: 'Top menu', section: 'topMenu' }, { label: 'Support', section: 'support' }, ] @@ -599,9 +610,11 @@ const config = ref(null) const showConfig = ref(false) const configCopied = ref(false) const configStore = useConfigStore() -const section = ref(menu[0].section) +const section = ref(null) const setSection = (value) => { + sessionStorage.setItem('side_menu_section', value) + section.value = value } @@ -629,7 +642,7 @@ const filterConfig = (value) => { const result = {} for (let key in value) { - if (['cache-categories', 'cache'].includes(key) === false) { + if (['cache-categories', 'cache', 'langs', 'enabled'].includes(key) === false) { result[key] = value[key] } } @@ -639,6 +652,8 @@ const filterConfig = (value) => { onMounted(async () => { config.value = await configStore.getAppConfig() + + setSection(sessionStorage.getItem('side_menu_section') ?? menu[0].section) }) @@ -670,4 +685,9 @@ onMounted(async () => { display: inline-block; margin-right: 5px; } + +.save { + display: block; + float: right +} diff --git a/src/pages/UserSettings.vue b/src/pages/UserSettings.vue new file mode 100644 index 0000000..987979a --- /dev/null +++ b/src/pages/UserSettings.vue @@ -0,0 +1,171 @@ + + + + + + diff --git a/src/scss/admin.scss b/src/scss/admin.scss index 4438837..8a050d7 100644 --- a/src/scss/admin.scss +++ b/src/scss/admin.scss @@ -120,16 +120,10 @@ .side-menu-setting-row { display: table; margin-bottom: 10px; -} -.side-menu-setting-row code { - margin-left: 2px; - margin-bottom: 1px; - padding: 3px 10px; - border-radius: 5px; - display: inline-block; - right: 2px; - border: 1px solid var(--color-border-dark); + &--disabled { + opacity: 0.5; + } } .side-menu-setting-label { @@ -256,6 +250,10 @@ .side-menu-setting-app { display: flex; + + &--user { + display: block; + } } .side-menu-setting { @@ -264,3 +262,11 @@ flex-direction: column; gap: 6px; } + +.side-menu-setting-save { + margin-top: 30px; +} + +.side-menu-tips { + margin-bottom: 15px; +} diff --git a/src/scss/menu.scss b/src/scss/menu.scss index 6d92c24..d362658 100644 --- a/src/scss/menu.scss +++ b/src/scss/menu.scss @@ -76,11 +76,6 @@ padding: 10px 0; } } - - &.side-menu-big, - &.side-menu-with-categories { - height: auto; - } } #header { diff --git a/src/store/config.js b/src/store/config.js index 3454202..744c23d 100644 --- a/src/store/config.js +++ b/src/store/config.js @@ -21,31 +21,37 @@ import axios from '@nextcloud/axios' import { generateUrl } from '@nextcloud/router' export const useConfigStore = defineStore('config', () => { - const config = ref(null) - const appConfig = ref(null) + let config = null + let appConfig = null + let userConfig = null async function getConfig() { - if (config.value !== null) { - return config.value + if (config === null) { + config = await axios.get(generateUrl('/apps/side_menu/js/config')).then((response) => response.data) } - config.value = await axios.get(generateUrl('/apps/side_menu/js/config')).then((response) => response.data) - - return config.value + return config } async function getAppConfig() { - if (appConfig.value !== null) { - return appConfig.value + if (appConfig === null) { + appConfig = await axios.get(generateUrl('/apps/side_menu/admin/config')).then((response) => response.data) } - appConfig.value = await axios.get(generateUrl('/apps/side_menu/admin/config')).then((response) => response.data) + return appConfig + } - return appConfig.value + async function getUserConfig() { + if (userConfig === null) { + userConfig = await axios.get(generateUrl('/apps/side_menu/user/config')).then((response) => response.data) + } + + return userConfig } return { getConfig, getAppConfig, + getUserConfig, } }) diff --git a/src/user.js b/src/user.js new file mode 100644 index 0000000..0f4b27a --- /dev/null +++ b/src/user.js @@ -0,0 +1,32 @@ +/** + * @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 . + */ + +import './scss/admin.scss' + +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import { waitContainer } from './lib/dom.js' + +import UserSettings from './pages/UserSettings' + +waitContainer('#side-menu-user-settings').then((selector) => { + const pinia = createPinia() + const app = createApp(UserSettings) + app.use(pinia) + app.mixin({ methods: { t, n } }) + app.mount(selector) +}) diff --git a/templates/settings/personal-form.php b/templates/settings/personal-form.php index 793feec..e2ff8e4 100644 --- a/templates/settings/personal-form.php +++ b/templates/settings/personal-form.php @@ -16,250 +16,11 @@ * along with this program. If not, see . */ -vendor_script('side_menu', 'html5sortable.min'); -script('side_menu', 'admin'); -style('side_menu', 'admin'); +use OCP\IURLGenerator; +use OCP\IConfig; +use OCA\SideMenu\AppInfo\Application; -$choicesYesNo = [ - 'No' => '0', - 'Yes' => '1', -]; - - -$labelShowHideApps = 'Show and hide the list of applications'; -$labelReset = 'Reset to default'; +script('side_menu', 'side_menu-user'); ?> -
- -
-

- t('Menu')); ?> -

-

- t('You do not have permission to change the settings.'); ?> -

-
- -
-

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

- -
-
-
- t('Enable the custom menu')); ?> -
-
- -
-
-
- -
-
-
- t('Open apps in new tab')); ?> -
-
- '1', - 'Use my selection' => '2', - ]; ?> - - - -

- - 🖱️ t($labelShowHideApps)); ?> - -

- - -
-
-
-
- -
-

- t('Top menu')); ?> -

- -
-
-
- t('Applications kept in the top menu')); ?> -

- - t('If there is no selection then the global configuration is applied.')); ?> - -

-
-
-

- - 🖱️ t($labelShowHideApps)); ?> - -

- - -
-
-
- -
-
-
- t('Applications kept in the top menu but also shown in side menu')); ?> -

- - t('These applications must be selected in the previous option.')); ?>
- t('If there is no selection then the global configuration is applied.')); ?> -
-

-
-
-

- - 🖱️ t($labelShowHideApps)); ?> - -

- - -
-
-
-
-
-

- t('Applications')); ?> -

- -
-
-
- t('Customize sorting')); ?> -
-
- - 🖱️ t($labelShowHideApps)); ?> - - -
- - - - ' name="apps-order" class="side-menu-setting" id="side-menu-apps-order" data-personal> -
-
-
-
- - -
- - - - - -
- - -
- - t('You like this app and you want to support me?')); ?> - - - - - -
-
-
+
diff --git a/templates/settings/personal-form.php.bk b/templates/settings/personal-form.php.bk new file mode 100644 index 0000000..793feec --- /dev/null +++ b/templates/settings/personal-form.php.bk @@ -0,0 +1,265 @@ +. + */ + +vendor_script('side_menu', 'html5sortable.min'); +script('side_menu', 'admin'); +style('side_menu', 'admin'); + +$choicesYesNo = [ + 'No' => '0', + 'Yes' => '1', +]; + + +$labelShowHideApps = 'Show and hide the list of applications'; +$labelReset = 'Reset to default'; +?> +
+ +
+

+ t('Menu')); ?> +

+ +

+ t('You do not have permission to change the settings.'); ?> +

+
+ +
+

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

+ +
+
+
+ t('Enable the custom menu')); ?> +
+
+ +
+
+
+ +
+
+
+ t('Open apps in new tab')); ?> +
+
+ '1', + 'Use my selection' => '2', + ]; ?> + + + +

+ + 🖱️ t($labelShowHideApps)); ?> + +

+ + +
+
+
+
+ +
+

+ t('Top menu')); ?> +

+ +
+
+
+ t('Applications kept in the top menu')); ?> +

+ + t('If there is no selection then the global configuration is applied.')); ?> + +

+
+
+

+ + 🖱️ t($labelShowHideApps)); ?> + +

+ + +
+
+
+ +
+
+
+ t('Applications kept in the top menu but also shown in side menu')); ?> +

+ + t('These applications must be selected in the previous option.')); ?>
+ t('If there is no selection then the global configuration is applied.')); ?> +
+

+
+
+

+ + 🖱️ t($labelShowHideApps)); ?> + +

+ + +
+
+
+
+
+

+ t('Applications')); ?> +

+ +
+
+
+ t('Customize sorting')); ?> +
+
+ + 🖱️ t($labelShowHideApps)); ?> + + +
+ + + + ' name="apps-order" class="side-menu-setting" id="side-menu-apps-order" data-personal> +
+
+
+
+ + +
+ + + + + +
+ + +
+ + t('You like this app and you want to support me?')); ?> + + + + + +
+
+
diff --git a/webpack.config.js b/webpack.config.js index db0a2c5..d9876e8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,6 +17,7 @@ module.exports = { entry: { menu: path.resolve(path.join('src', 'menu.js')), admin: path.resolve(path.join('src', 'admin.js')), + user: path.resolve(path.join('src', 'user.js')), }, output: { path: path.resolve('./js'),