Compare commits

..

219 Commits

Author SHA1 Message Date
Simon Vieille b208c8cb19 release v1.19.1 2020-10-21 18:06:00 +02:00
Simon Vieille 0e5cd135b4 fix issue #47 2020-10-21 18:05:36 +02:00
Simon Vieille 854beb8078 release v1.19.0 2020-10-19 18:26:17 +02:00
Simon Vieille 709f938f48 update screenshots 2020-10-19 18:24:08 +02:00
Simon Vieille 80700cac32 add option: the menu is enabled by default for users 2020-10-19 13:44:07 +02:00
Simon Vieille a71a8c7208 add documentation 2020-10-16 13:42:23 +02:00
Simon Vieille cf80c3512c add licence 2020-10-16 11:46:25 +02:00
Simon Vieille a5d88317e6 update screenshots 2020-10-16 11:18:48 +02:00
Simon Vieille b2103a012c release v1.18.0 2020-10-16 11:17:44 +02:00
Simon Vieille 847b3696cb Merge branch 'feature/ref' into develop 2020-10-16 11:08:37 +02:00
Simon Vieille c67bfa0ce4 indentation 2020-10-16 11:07:45 +02:00
Simon Vieille 7edd6d6d84 refactor menus with separated components 2020-10-16 11:04:03 +02:00
Simon Vieille ae148aba02 Merge branch 'feature/issue44' into develop 2020-10-15 16:44:57 +02:00
Simon Vieille 575f963902 indentation 2020-10-15 15:47:28 +02:00
Simon Vieille ae74e745db add menu element: link to settings 2020-10-15 15:46:59 +02:00
Simon Vieille b749a9be2e add option: show link to settings 2020-10-15 15:46:43 +02:00
Simon Vieille d279fbd813 change typo 2020-10-14 16:23:09 +02:00
Simon Vieille 0d98a40536 release v1.17.0 2020-10-14 16:16:06 +02:00
Simon Vieille 5e894f9f08 add admin actions: remove cache, export configuration 2020-10-14 16:14:50 +02:00
Simon Vieille 731d4f826b release v1.16.4 2020-10-14 10:00:21 +02:00
Simon Vieille 4225b157e0 app:check compliance 2020-10-14 09:57:42 +02:00
Simon Vieille 0eefcd23cd app:check compliance (info.xml) 2020-10-14 09:33:19 +02:00
Simon Vieille ead4ce9a6f release v1.16.3 2020-10-13 14:33:07 +02:00
Simon Vieille baa2a0748e fix links that must be opened in new window 2020-10-13 14:32:15 +02:00
Simon Vieille 5b5daa92b9 remove useless call 2020-10-13 13:26:08 +02:00
Simon Vieille b19911b02f release v1.16.2 2020-10-13 11:09:56 +02:00
Simon Vieille a57017e80b fix issue with personal settings when global settings are forced 2020-10-13 11:09:14 +02:00
Simon Vieille dfe6ad7bd4 release v1.16.1 2020-10-12 09:11:28 +02:00
Simon Vieille 2653fbbfa7 add cache and loader for big menu 2020-10-11 17:06:34 +02:00
Simon Vieille 62e5860531 fix side menu covers apps left menu 2020-10-11 16:15:51 +02:00
Simon Vieille 46af933a4e fix side menu covers apps left menu 2020-10-09 09:53:37 +02:00
Simon Vieille 9de22bf5e9 update the discussion link 2020-10-06 13:33:02 +02:00
Simon Vieille b41d413059 update properties of controller 2020-09-29 11:25:31 +02:00
Simon Vieille cc0fac6486 release v1.16.0 2020-09-24 14:29:12 +02:00
Simon Vieille 4d1ec41a9b release v1.16.0 2020-09-24 14:27:35 +02:00
Simon Vieille 034b24369a Merge branch 'feature/issue38' into develop 2020-09-24 14:26:46 +02:00
Simon Vieille b5874792f1 update screenshots 2020-09-24 14:26:43 +02:00
Simon Vieille c7f9fd1b8b add an option to force settings to users 2020-09-24 14:26:25 +02:00
Simon Vieille 4b6a1f2989 release v1.15.0 2020-09-23 14:34:59 +02:00
Simon Vieille 87dc460ed0 Merge branch 'feature/issue35' into develop 2020-09-23 14:34:17 +02:00
Simon Vieille 508d9f71d1 fix #36: always displayed is not expanding 2020-09-23 14:32:25 +02:00
Simon Vieille fcb2d2a385 update translations 2020-09-23 13:21:22 +02:00
Simon Vieille c9645a4f85 change 'Side menu' with 'Custom menu' 2020-09-23 13:17:13 +02:00
Simon Vieille a6397d2f24 update app icon 2020-09-23 13:16:58 +02:00
Simon Vieille b0b2ae44f5 update template for issue 2020-09-23 12:57:45 +02:00
Simon Vieille 9d2eab42d8 fix args type 2020-09-23 12:51:55 +02:00
Simon Vieille 12c115d6d6 fix #37: update template for issue 2020-09-23 12:51:32 +02:00
Simon Vieille 30ed6c1b54 release v1.14.0 2020-09-22 15:04:05 +02:00
Simon Vieille bd7cefa6b6 release v1.14.0 2020-09-22 14:59:33 +02:00
Simon Vieille 240a6b1aa4 PSR 2 compliance 2020-09-22 14:35:09 +02:00
Simon Vieille 40e883841e add config proxy 2020-09-22 14:24:53 +02:00
Simon Vieille 7b5e92f83e add config proxy 2020-09-22 14:24:48 +02:00
Simon Vieille 9a67c165bd add constante for the app id and the app name 2020-09-21 11:28:55 +02:00
Simon Vieille 78c95c58ff fix regression with the logo and the avatar display 2020-09-21 09:49:09 +02:00
Simon Vieille 98f74bfe33 remove useless attributes 2020-09-21 09:45:51 +02:00
Simon Vieille a9b4283e76 Merge branch 'feature/conf' into develop 2020-09-20 20:57:02 +02:00
Simon Vieille 85c15a1967 add PHP doc 2020-09-20 20:56:59 +02:00
Simon Vieille 45abf1f09d SideMenu and SideMenuBig use the new controller to get configuration 2020-09-20 20:56:36 +02:00
Simon Vieille 5153bee7c6 - add specific methods to get the config
- add a controller to retrieve most of the configuration
2020-09-20 20:53:03 +02:00
Simon Vieille cd294273cb update description 2020-09-19 15:51:56 +02:00
Simon Vieille be17c0e232 update translations 2020-09-18 13:02:23 +02:00
Simon Vieille 41ca035299 release v1.13.0 2020-09-18 11:12:47 +02:00
Simon Vieille 110ac5b130 update screenshots 2020-09-18 11:12:19 +02:00
Simon Vieille 266ba1453e Merge branch 'feature/issue34' into develop 2020-09-18 11:10:22 +02:00
Simon Vieille ba6d6fdd8f PSR 2 compliance 2020-09-18 11:09:14 +02:00
Simon Vieille e5dd893774 add option to show the avatar instead of the logo 2020-09-18 11:09:01 +02:00
Simon Vieille 85c25f0fc7 update description 2020-09-18 09:14:05 +02:00
Simon Vieille ae130388a5 release v1.12.3 2020-09-18 09:05:06 +02:00
Simon Vieille e653214b91 add a delay before moving elements (fix #33) 2020-09-17 15:19:42 +02:00
Simon Vieille 9ca6a3a995 update screenshots 2020-09-14 14:56:09 +02:00
Simon Vieille 7303dbae41 release v1.12.2 2020-09-14 14:51:39 +02:00
Simon Vieille 511f5a0c45 fix issue #31 2020-09-14 14:40:14 +02:00
Simon Vieille 9ea5c1b59b release v1.12.1 2020-09-14 11:31:50 +02:00
Simon Vieille ade56062e3 fix typo 2020-09-14 11:31:03 +02:00
Simon Vieille 481a2d2606 Merge pull request 'src/l10n/fixtures/de.yaml ändern' (#32) from JuxLe/side_menu:juxle-patch-1 into develop
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/32
2020-09-14 09:01:09 +02:00
JuxLe 3a809bb92e „src/l10n/fixtures/de.yaml“ ändern
some translation improvements
2020-09-13 03:18:09 +02:00
Simon Vieille cc2ab04035 release v1.12.0 2020-09-12 16:44:05 +02:00
Simon Vieille 17a57f3685 Merge branch 'feature/issue30-tab' into develop 2020-09-12 16:40:43 +02:00
Simon Vieille d41ecbbd9a add translations 2020-09-12 16:35:30 +02:00
Simon Vieille b6c6fc2486 add target _blank 2020-09-12 16:32:10 +02:00
Simon Vieille 918c55da3e add options to select apps that must be opend on new tab (UI) 2020-09-12 16:10:48 +02:00
Simon Vieille 385c27c691 add documentation 2020-09-11 13:46:48 +02:00
Simon Vieille 54d702ba9f add documentation 2020-09-11 13:46:25 +02:00
Simon Vieille c816c40c19 fix typo 2020-09-11 13:39:41 +02:00
Simon Vieille aad27cd8b4 release v1.11.0 2020-09-11 13:26:01 +02:00
Simon Vieille 96fb33b80b Merge branch 'feature/issue29-tab' into develop 2020-09-11 13:25:09 +02:00
Simon Vieille e10a5f1f62 fix issue with side menu header 2020-09-11 13:23:52 +02:00
Simon Vieille f133932884 add target _blank when the option is enabled 2020-09-11 13:23:40 +02:00
Simon Vieille e287e27375 add translations 2020-09-11 13:06:43 +02:00
Simon Vieille 9ae4d7ee48 add new option: open apps in new tab 2020-09-11 13:03:37 +02:00
Simon Vieille 624bb52ec4 release v1.10.0 2020-09-04 18:14:49 +02:00
Simon Vieille ba34a3738a update display images 2020-09-04 18:05:08 +02:00
Simon Vieille 18c1cce715 update translations 2020-09-04 18:03:40 +02:00
Simon Vieille 7a906fb033 update screenshots 2020-09-04 16:45:17 +02:00
Simon Vieille c8536ae264 add live preview of settings 2020-09-04 16:43:27 +02:00
Simon Vieille 3703791a28 replace $ by jQuery 2020-09-04 15:34:09 +02:00
Simon Vieille 6dff33f9d7 improve admin ux 2020-09-04 15:32:30 +02:00
Simon Vieille 05bd7c29e9 fix typo 2020-09-03 14:50:10 +02:00
Simon Vieille 8380a4bf17 release v1.9.3 2020-09-03 12:47:32 +02:00
Simon Vieille ad7b5b7c52 release v1.9.2-b2 2020-09-03 12:44:19 +02:00
Simon Vieille 871cf1f5b4 fix regression 2020-09-03 12:42:33 +02:00
Simon Vieille a2b615752b release v1.9.2 2020-09-03 10:43:40 +02:00
Simon Vieille aec89ce9fa release v1.9.1 2020-09-03 10:43:08 +02:00
Simon Vieille 02e526d717 release v1.9.1 2020-09-03 10:39:25 +02:00
Simon Vieille ebb485ffba release v1.9.0 2020-09-03 10:31:11 +02:00
Simon Vieille 80221c3ea2 release v1.9.0 2020-09-03 10:19:18 +02:00
Simon Vieille f8aebf94d2 release v1.9.0 2020-09-03 10:17:08 +02:00
Simon Vieille 97da542bd8 prepare release 1.9.0 2020-09-03 10:11:09 +02:00
Simon Vieille 612a94fd06 Merge branch 'feature/fixed-left-menu' into develop 2020-09-03 09:49:41 +02:00
Simon Vieille a7bcc7da0f replace $ by jQuery 2020-09-03 09:49:30 +02:00
Simon Vieille 3da1830f58 fix #21: add on option to always display the menu on the left 2020-09-03 09:46:39 +02:00
Simon Vieille 8a9c68c702 fix #21: add on option to always display the menu on the left 2020-09-03 09:46:36 +02:00
Simon Vieille e55419eb8b Merge branch 'develop' into feature/fixed-left-menu 2020-09-02 19:13:03 +02:00
Simon Vieille 2f0e47dcec remove experimental notices 2020-09-02 19:12:50 +02:00
Simon Vieille 361ad9211c poc #21: add a fixed menu on the left 2020-09-02 19:09:37 +02:00
Simon Vieille 2fc9a4effe update translations 2020-08-29 14:46:04 +02:00
Simon Vieille 6a5ecfa115 add translations: de 2020-08-29 14:30:46 +02:00
Simon Vieille aab26bad32 add documentation 2020-08-29 14:04:52 +02:00
Simon Vieille f3e657dcdb add gitignore 2020-08-29 14:04:45 +02:00
Simon Vieille e1ca610256 update release task 2020-08-28 20:17:58 +02:00
Simon Vieille acf2022938 update release task 2020-08-28 20:13:10 +02:00
Simon Vieille 707dbf6810 indentation 2020-08-28 17:30:08 +02:00
Simon Vieille f8dd123851 indentation 2020-08-28 17:29:03 +02:00
Simon Vieille 518f4bb70c update documentation 2020-08-28 17:28:12 +02:00
Simon Vieille c8b3258819 update update 2020-08-28 17:28:03 +02:00
Simon Vieille 186ee4d344 add Makefile 2020-08-28 17:11:12 +02:00
Simon Vieille 64ee6fb223 add gitignore 2020-08-28 17:09:24 +02:00
Simon Vieille c2662cdaae Merge branch 'feature/translations' into develop 2020-08-28 16:46:45 +02:00
Simon Vieille d3ee4120c9 add gitignore 2020-08-28 16:46:38 +02:00
Simon Vieille 4fd11f2deb add l10n generators and fr translations 2020-08-28 16:45:19 +02:00
Simon Vieille 72dc0c7d01 update forms to handle translations 2020-08-28 16:45:02 +02:00
Simon Vieille 8c38382eae remove l10n files 2020-08-28 16:44:36 +02:00
Simon Vieille 1567a8e82f fix #28: menu items invisible after 1.8.4 2020-08-25 19:06:01 +02:00
Simon Vieille e1ae5d21e2 add compatibility with NC 20 2020-08-25 13:33:02 +02:00
Simon Vieille 4370738625 fix #27: disable side menu on public pages - broken in 1.8.3 2020-08-25 12:18:39 +02:00
Simon Vieille acdd299573 release v1.8.3 2020-08-20 16:29:41 +02:00
Simon Vieille fae21390dd fix #26: alphabetic order of apps 2020-08-20 16:27:35 +02:00
Simon Vieille 4f0fdd91f9 release v1.8.2 2020-08-19 11:44:05 +02:00
Simon Vieille d084cfed86 fix #25: Icon x for closing the big menu 2020-08-19 11:41:10 +02:00
Simon Vieille 3db0c1ccd2 fix #23: hidden icons in the top menu 2020-08-19 11:29:21 +02:00
Simon Vieille e4f9765247 fix #24: missing apps 2020-08-19 09:18:29 +02:00
Simon Vieille e7d319bbf7 fix 't.apps is not iterable' 2020-08-18 14:39:45 +02:00
Simon Vieille cf0f459aeb release v1.8.1 2020-08-14 13:31:13 +02:00
Simon Vieille b5ce8b92e1 fix issue with links 2020-08-14 13:30:48 +02:00
Simon Vieille 39aede4a73 release v1.8.0 2020-08-13 23:15:03 +02:00
Simon Vieille d9ea37ba62 fix cache and config key 2020-08-13 23:02:28 +02:00
Simon Vieille aad3038003 admin ui, remove debugging code 2020-08-13 22:24:41 +02:00
Simon Vieille 011d24d3bd add icon opacity 2020-08-13 22:07:09 +02:00
Simon Vieille 9eec0ce3ef add icon color filter range 2020-08-13 21:22:10 +02:00
Simon Vieille 831d2d81fc add documentation 2020-08-13 16:51:41 +02:00
Simon Vieille c6a43f755d fix issue with empty cat 2020-08-13 16:33:05 +02:00
Simon Vieille d30603c7d1 update the big-menu size when screen < 1024px 2020-08-13 16:13:31 +02:00
Simon Vieille aa9ba60680 fix issue with the classic display 2020-08-13 13:05:38 +02:00
Simon Vieille d3b7b99aae Merge branch 'feature/issue22-favorite-apps' into develop 2020-08-13 11:34:43 +02:00
Simon Vieille 3a431f16e7 release v1.8.0-rc1 2020-08-13 11:34:40 +02:00
Simon Vieille e4abc5b43f add PSR 2 compliance 2020-08-13 11:21:59 +02:00
Simon Vieille ef72c00fbb fix translations 2020-08-13 11:21:06 +02:00
Simon Vieille e1c6784d41 update visible apps order 2020-08-12 16:32:26 +02:00
Simon Vieille 3f2a307226 refactoring 2020-08-12 16:27:43 +02:00
Simon Vieille deca4b2cba fix issue with 'top-menu-apps' 2020-08-12 16:16:50 +02:00
Simon Vieille a1b1396b63 add add top-menu-apps option in the personal settings 2020-08-12 15:59:51 +02:00
Simon Vieille 9d9afff08d fix compatibility with the option 'hide-when-no-apps' 2020-08-12 14:06:52 +02:00
Simon Vieille ffab77aff2 add options to select apps that must be displayed in the top menu (replace exteral sites option) 2020-08-12 10:51:49 +02:00
Simon Vieille caa9c092ef add external links 2020-08-11 15:52:15 +02:00
Simon Vieille 8aa08dbc6b add compatibility with basic side menu options 2020-08-11 15:26:40 +02:00
Simon Vieille 460dac5faf update admin and personal pages 2020-08-11 15:26:17 +02:00
Simon Vieille ce5c806524 update ui of the big menu, add 'files' app 2020-08-11 13:34:36 +02:00
Simon Vieille 307f69906b add links 2020-08-11 10:40:31 +02:00
Simon Vieille 5a86e16861 update ui of the big menu 2020-08-11 10:34:06 +02:00
Simon Vieille 8c3dd2a4f6 remove personal setting for the big menu 2020-08-11 10:33:48 +02:00
Simon Vieille 8e0be738a6 add base of the big menu 2020-08-06 15:00:59 +02:00
Simon Vieille ddb7cc40ce refactor appinfo 2020-08-06 15:00:24 +02:00
Simon Vieille cdcc4ddd51 refactor js 2020-08-04 09:15:16 +02:00
Simon Vieille e1d450218c release v1.7.0 2020-05-19 15:23:25 +02:00
Simon Vieille 93c8ade21a release v1.6.4 2020-05-19 15:21:44 +02:00
Simon Vieille dcc19f5b23 add loader 2020-05-19 15:17:54 +02:00
Simon Vieille 6418e79d49 compatibility with nextcloud 19 2020-05-15 16:59:52 +02:00
Simon Vieille 9b4aecaf41 add documentation 2020-05-14 17:03:09 +02:00
Simon Vieille 46148167e9 add documentation 2020-05-14 17:02:23 +02:00
Simon Vieille a0f4eddab0 release v1.6.3 2020-05-14 15:31:20 +02:00
Simon Vieille 24bf8496f3 fix issue #20: add shortcut to open and to hide the menu 2020-05-14 15:30:34 +02:00
Simon Vieille 8368562ed0 release v1.6.2 2020-05-14 13:28:39 +02:00
Simon Vieille 9a8f5517b6 add a cache for the top menu update (#19) 2020-05-13 12:25:11 +02:00
Simon Vieille bddd8e9c0c release v1.6.1 2020-05-12 19:48:44 +02:00
Simon Vieille 03dbe8eeed fix issue #19: add a hack to show external sites in the top menu with navigation 2020-05-12 19:45:46 +02:00
Simon Vieille 321e5b1178 add title on links 2020-05-12 09:25:43 +02:00
Simon Vieille 594b818515 release v1.6.0 2020-05-11 22:57:55 +02:00
Simon Vieille 7ac19c7bec add personal settings page to enabled (or not) the side menu 2020-05-11 22:51:27 +02:00
Simon Vieille 375ed7d2f1 indentation 2020-05-09 16:37:17 +02:00
Simon Vieille bbd4403c3e release v1.5.0 2020-05-09 16:05:17 +02:00
Simon Vieille c52079e882 fix #19: add option to keep external sites in the top menu 2020-05-09 15:53:25 +02:00
Simon Vieille 7864e16ef3 add option to force light icons 2020-05-09 14:30:24 +02:00
Simon Vieille 4b06b633b8 fix #16: add dark icons 2020-05-09 14:00:24 +02:00
Simon Vieille 12abd19936 fix #16: add dark icons and handle main menu svg filters 2020-05-09 14:00:11 +02:00
Simon Vieille 281e7e8de1 release v1.4.1 2020-05-05 18:53:04 +02:00
Simon Vieille d325d583d4 fix issue #15 and harmonization render with chrome browsers 2020-05-05 18:52:04 +02:00
Simon Vieille 63b03ae11e release v1.4.0 2020-04-29 09:07:07 +02:00
Simon Vieille ec50328f78 Merge branch 'feature/issue-12' into develop 2020-04-29 09:03:11 +02:00
Simon Vieille 4146e9c2a1 fix #12: add an option to hide the opener and the panel when there is no application 2020-04-29 09:03:03 +02:00
Simon Vieille 76ecd6aeaa Merge branch 'develop' into feature/issue-12 2020-04-29 08:44:10 +02:00
Simon Vieille ef22990f4b release v1.3.4 2020-04-27 08:48:48 +02:00
Simon Vieille c84ce6a615 release v1.4.0 2020-04-26 23:46:38 +02:00
Simon Vieille 9b060d1a4b fix #13: add option to show only the opener 2020-04-26 23:45:33 +02:00
Simon Vieille 26e737e2d7 fix #13: add alternate hamburger icon 2020-04-26 23:32:30 +02:00
Simon Vieille 67f27e0ef4 fix #13: add alternate hamburger icon 2020-04-26 23:30:43 +02:00
Simon Vieille a392bb07c0 first tests to solve issue #12 2020-04-26 21:36:54 +02:00
Simon Vieille 58661508d4 indentation 2020-04-25 16:29:52 +02:00
Simon Vieille 5509b69908 fix #11 2020-04-25 16:04:54 +02:00
Simon Vieille 51eccde659 add gitignore 2020-04-25 16:03:51 +02:00
Simon Vieille d0c072bee5 release v1.3.3 2020-04-25 14:30:02 +02:00
Simon Vieille 95f7eb5980 add options to select the size of icons and texts or hide them 2020-04-25 14:25:51 +02:00
Simon Vieille 18314b260f add 2nd background color 2020-04-25 13:25:41 +02:00
Simon Vieille e3c159b4d6 add 2nd background color 2020-04-25 13:05:01 +02:00
Simon Vieille a48611edf4 indentation 2020-04-22 19:35:56 +02:00
Simon Vieille 5dde7ca850 add padding on icons 2020-04-22 19:30:04 +02:00
Simon Vieille db2f7becae add documentation 2020-04-22 00:42:47 +02:00
Simon Vieille 1715ae5d52 release v1.3.2 2020-04-20 19:52:02 +02:00
Simon Vieille 847fa23345 add support message 2020-04-20 19:51:54 +02:00
Simon Vieille 0495046d02 fix #9: apply a patch to correctly generate an image path 2020-04-20 19:24:37 +02:00
63 changed files with 4373 additions and 361 deletions
+6
View File
@@ -0,0 +1,6 @@
/js
/node_modules
/l10n/*
/releases
/package-lock.json
!/l10n/.gitkeep
+35
View File
@@ -0,0 +1,35 @@
## Feature
### Description
...
### Benefits
...
---
## Issue
### Environment
* Side menu version: ...
* Nextcloud version: ...
* PHP version: ...
* Web server (Nginx, Apache2): ...
* Web browser and version (Firefox 80, Google Chrome 74, etc): ...
### Steps to reproduce
* ...
* ...
* ...
### Observed Results
...
### Expected Results
...
+24
View File
@@ -0,0 +1,24 @@
npm-build:
npm run build
npm-watch:
npm run watch
.ONESHELL:
release: npm-build translations
if [ -z "$$VERSION" ]; then
echo "VERSION required"
exit 1
fi
test -d releases/$$VERSION && rm -fr releases/$$VERSION
mkdir -p releases/$$VERSION/side_menu
cp -r README.md appinfo css lib img l10n js src templates screenshots releases/$$VERSION/side_menu
cd releases/$$VERSION
zip -r side_menu_v$$VERSION.zip side_menu
tar cvzf side_menu_v$$VERSION.tar.gz side_menu
rm -fr side_menu
openssl dgst -sha512 -sign $$HOME/.nextcloud/certificates/side_menu.key side_menu_v$$VERSION.tar.gz | openssl base64 > side_menu_v$$VERSION.sig
translations:
php bin/generate_l10n.php
+31 -29
View File
@@ -1,52 +1,54 @@
🤙 Nextcloud app / Side menu 🎨 🤙 Nextcloud app / Custom menu 🎨
=============================== ===============================
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.
This application moves the top menu of Nextcloud to the left side. This application is rather suitable for instances that activate a lot of applications.
Comptatible with AppOrder. Comptatible with AppOrder.
Installation * [Installation and upgrade](#installation-and-upgrade)
------------ * [How to contribute?](#how-to-contribute)
* [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/)
Side menu is availabe from the app store. You like this app and you want to support me? [Buy me a coffee](https://www.buymeacoffee.com/deblan) ☕
If you want to install it from source: go to https://gitnet.fr/deblan/side_menu/releases and download the last release (side_menu_vX.Y.Z.zip). Installation and upgrade
------------------------
Copy the archive to your Nextcloud server. Custom menu is available from the app store.
```
$ cd /path/to/nextcloud
$ php occ app:install side_menu
```
If you want to install it from source, go to https://gitnet.fr/deblan/side_menu/releases and copy the link to the last release (side_menu_vX.Y.Z.tar.gz). Then:
``` ```
$ cd /path/to/nextcloud/apps $ cd /path/to/nextcloud/apps
$ unzip -d side_menu /path/to/side_menu_vX.Y.Z.zip $ curl -sS https://gitnet.fr/attachments/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | tar xvfz -
``` ```
Upgrade Administrators can edit many settings using the administration page.
-------
Side menu can be upgraded from the app store. Users can disable the menu using the page of personal settings.
If you want to install it from source: go to https://gitnet.fr/deblan/side_menu/releases and download the last release (side_menu_vX.Y.Z.zip).
Copy the archive to your Nextcloud server.
```
$ cd /path/to/nextcloud/apps
$ rm -frv side_menu
$ unzip -d side_menu /path/to/side_menu_vX.Y.Z.zip
```
![](https://upload.deblan.org/u/2020-03/5e7fab2b.jpg)
![](https://upload.deblan.org/u/2020-03/5e81b219.jpg)
![](https://upload.deblan.org/u/2020-04/5e9d9058.png)
Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate.
How to contribute? How to contribute?
------------------ ------------------
Fork the repository, go to `apps/`, clone your repository and run `npm install` inside. You can report a bug or request a feature by opening an issue: https://gitnet.fr/deblan/side_menu/issues
Build javascripts using `webpack --config ./webpack.js` (add `-w` to build them in real time). If you are a developer:
* fork the repository
* install an instance of Nextcloud
* go to `apps/` and clone your repository
* go to `apps/side_menu` and run `npm install`
Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time).
Then commit and create a pull request. Then commit and create a pull request.
+6 -58
View File
@@ -1,62 +1,10 @@
<?php <?php
/** use OCA\SideMenu\AppInfo\Application;
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Appinfo; $app = new Application();
use OC\Security\CSP\ContentSecurityPolicy; if ($app->isEnabled()) {
use OCP\Util; $app->registerAssets();
$app->registerServices();
$config = \OC::$server->getConfig(); }
$cspnm = \OC::$server->getContentSecurityPolicyNonceManager();
Util::addScript('side_menu', 'sideMenu');
Util::addStyle('side_menu', 'sideMenu');
$stylesheet = \OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Css.stylesheet',
[
'v' => $config->getAppValue('side_menu', 'cache', '0'),
]
);
$script = \OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Js.script',
[
'v' => $config->getAppValue('side_menu', 'cache', '0'),
]
);
Util::addHeader(
'link',
[
'href' => $stylesheet,
'rel' => 'stylesheet'
],
''
);
Util::addHeader(
'script',
[
'src' => $script,
'nonce' => $cspnm->getNonce(),
],
''
);
+40 -14
View File
@@ -1,25 +1,51 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<info> <info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>side_menu</id> <id>side_menu</id>
<name>Side menu</name> <name>Custom menu</name>
<summary>Move the top menu to the left side.</summary> <summary>Modify the display of the menu.</summary>
<description>Move the top menu to the left side.</description> <description><![CDATA[Custom menu 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.
This application is rather suitable for instances that activate a lot of applications.
Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate.
Comptatible with AppOrder.
You can report a bug or request a feature by opening an issue.
If you like this application and want to support the development:
* ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan)
* 😃 [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments)
]]></description>
<version>1.19.1</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="dev+sidemenu@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author> <author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author>
<version>1.3.1</version>
<namespace>SideMenu</namespace> <namespace>SideMenu</namespace>
<documentation>
<admin>https://gitnet.fr/deblan/side_menu/src/branch/master/README.md</admin>
<developer>https://gitnet.fr/deblan/side_menu/src/branch/master/README.md</developer>
</documentation>
<category>customization</category> <category>customization</category>
<website>https://gitnet.fr/deblan/side_menu</website> <website>https://gitnet.fr/deblan/side_menu</website>
<discussion>https://matrix.to/#/!GhfQePnOSPYpYEunWM:neutralnetwork.org</discussion>
<bugs>https://gitnet.fr/deblan/side_menu/issues</bugs> <bugs>https://gitnet.fr/deblan/side_menu/issues</bugs>
<repository type="git">https://gitnet.fr/deblan/side_menu</repository> <repository type="git">https://gitnet.fr/deblan/side_menu</repository>
<screenshot>https://upload.deblan.org/u/2020-03/5e7fab2b.jpg</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-03/5e81b219.jpg</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/admin_settings.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-04/5e9d9058.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/n19_big_menu.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc18_menu_always_displayed.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot>
<dependencies> <dependencies>
<nextcloud min-version="18" max-version="18"/> <nextcloud min-version="18" max-version="20"/>
</dependencies> </dependencies>
<settings> <settings>
<admin>OCA\SideMenu\Settings\Admin</admin> <admin>OCA\SideMenu\Settings\Admin</admin>
<admin-section>OCA\SideMenu\Settings\Section</admin-section> <admin-section>OCA\SideMenu\Settings\AdminSection</admin-section>
</settings> <personal>OCA\SideMenu\Settings\Personal</personal>
<personal-section>OCA\SideMenu\Settings\PersonalSection</personal-section>
</settings>
</info> </info>
+7 -2
View File
@@ -19,7 +19,12 @@
return [ return [
'routes' => [ 'routes' => [
['name' => 'Css#stylesheet', 'url' => '/css/stylesheet', 'verb' => 'GET'], ['name' => 'Css#stylesheet', 'url' => '/css/stylesheet', 'verb' => 'GET'],
['name' => 'Js#script', 'url' => '/js/script', 'verb' => 'GET'], ['name' => 'Js#script', 'url' => '/js/script', 'verb' => 'GET'],
['name' => 'Js#config', 'url' => '/js/config', 'verb' => 'GET'],
['name' => 'Nav#items', 'url' => '/nav/items', 'verb' => 'GET'],
['name' => 'PersonalSetting#valueSet', 'url' => '/personalSetting/valueSet', 'verb' => 'POST'],
['name' => 'AdminSetting#removeCache', 'url' => '/admin/cache/remove', 'verb' => 'GET'],
['name' => 'AdminSetting#exportConfiguration', 'url' => '/admin/config/export', 'verb' => 'GET'],
], ],
]; ];
+33
View File
@@ -0,0 +1,33 @@
<?php
function generateJsContent($translations)
{
$json = json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
return <<< EOF
OC.L10N.register("side_menu", {$json}, "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
EOF;
}
function generateJsonContent($translations)
{
$datas = [
'translations' => $translations,
'pluralForm' => 'nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;',
];
return json_encode(
$datas,
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
);
}
chdir(__DIR__.'/../');
foreach (glob('src/l10n/fixtures/*.yaml') as $file) {
$lang = str_replace('.yaml', '', basename($file));
$translations = yaml_parse(file_get_contents($file));
file_put_contents('l10n/'.$lang.'.js', generateJsContent($translations));
file_put_contents('l10n/'.$lang.'.json', generateJsonContent($translations));
}
+45
View File
@@ -20,6 +20,51 @@
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
} }
#side-menu-section input[type="checkbox"] {
vertical-align: middle;
}
#side-menu-section input[type="range"] {
vertical-align: middle;
}
#side-menu-section select { #side-menu-section select {
margin: 10px 0 10px 0; margin: 10px 0 10px 0;
} }
.keyboard-key {
padding: 1px 9px;
margin: 0 2px;
background: #eee;
border: 1px solid #aaa;
color: #555;
border-radius: 3px;
}
.side-menu-display {
padding: 10px;
border: 2px solid transparent;
max-width: 100%;
cursor: pointer;
}
.side-menu-display.is-active {
border: 2px solid #91cb7f;
}
.info {
margin-top: 8px;
padding: 5px;
background: #91cb7f;
color: #fff;
border-radius: var(--border-radius);
}
#side-menu-section h2 small {
font-size: 11px;
font-weight: normal;
}
.side-menu-toggler {
cursor: pointer;
}
+205 -10
View File
@@ -15,10 +15,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#appmenu, #appmenu + nav {
display: none;
}
#side-menu { #side-menu {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -26,7 +22,7 @@
height: 100vh; height: 100vh;
width: 100%; width: 100%;
max-width: 250px; max-width: 250px;
background: var(--side-menu-background-color, #333); background: linear-gradient(90deg, var(--side-menu-background-color, #333) 0%, var(--side-menu-background-color-to, #333) 100%);
z-index: 3000; z-index: 3000;
color: var(--side-menu-text-color, #fff); color: var(--side-menu-text-color, #fff);
box-shadow: rgba(0, 0, 0, 0.22) 0px 25.6px 57.6px 0px, rgba(0, 0, 0, 0.18) 0px 4.8px 14.4px 0px; box-shadow: rgba(0, 0, 0, 0.22) 0px 25.6px 57.6px 0px, rgba(0, 0, 0, 0.18) 0px 4.8px 14.4px 0px;
@@ -37,6 +33,38 @@
display: block; display: block;
} }
#header .side-menu-opener {
margin-left: 5px;
}
.side-menu-settings {
margin-right: 9px;
margin-top: 2px;
float: right;
line-height: 34px;
height: 28px;
display: none;
}
.side-menu-settings a {
color: var(--side-menu-text-color, #fff);
display: block;
padding: 4px 7px;
}
.side-menu-settings:hover a, .side-menu-settings a:active, .side-menu-settings a:focus {
background: var(--side-menu-current-app-background-color, #444);
}
.side-menu-settings img {
vertical-align: bottom;
margin-left: 3px;
}
#side-menu.open .side-menu-settings {
display: block;
}
.side-menu-opener { .side-menu-opener {
background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); background: var(--side-menu-opener, url('../img/side-menu-opener.svg'));
height: 40px; height: 40px;
@@ -45,10 +73,15 @@
border: 0; border: 0;
} }
#side-menu.hide-opener .side-menu-opener { .side-menu-closer {
background: url('../img/side-menu-opener-closer.svg');
display: none; display: none;
} }
#side-menu.hide-opener .side-menu-opener, .side-menu-opener.hide, #side-menu.hide {
display: none !important;
}
.side-menu-apps-list { .side-menu-apps-list {
height: calc(100vh - 150px); height: calc(100vh - 150px);
z-index: 2200; z-index: 2200;
@@ -61,21 +94,28 @@
.side-menu-app-icon { .side-menu-app-icon {
width: 20px; width: 20px;
vertical-align: middle; vertical-align: top;
margin-right: 10px; margin-right: 10px;
filter: invert(var(--side-menu-icon-invert-filter, 0%));
opacity: var(--side-menu-icon-opacity, 1);
}
.side-menu-app-icon svg {
vertical-align: middle;
margin-top: -3px;
} }
.side-menu-app a { .side-menu-app a {
line-height: 30px; line-height: 30px;
color: var(--side-menu-text-color, #fff); color: var(--side-menu-text-color, #fff);
display: block; display: block;
padding: 7px 0 7px 15px; padding: 7px 0 5px 15px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.side-menu-app a:hover, .side-menu-app.active a { .side-menu-app a:hover, .side-menu-app.active, .side-menu-app a:focus {
background: var(--side-menu-current-app-background-color, #444); background: var(--side-menu-current-app-background-color, #444);
} }
@@ -93,12 +133,167 @@
width: 100%; width: 100%;
z-index: 2300; z-index: 2300;
max-width: 250px; max-width: 250px;
background: var(--side-menu-background-color, #333);
position: fixed; position: fixed;
padding-top: 2px; padding-top: 2px;
padding-left: 5px;
top: 0; top: 0;
} }
#side-menu.hide-opener .side-menu-logo { #side-menu.hide-opener .side-menu-logo {
margin-top: 20px; margin-top: 20px;
} }
#side-menu-loader {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 3001;
}
#side-menu-loader-bar {
height: 4px;
background: var(--side-menu-loader-color, #0e75ac);
width: 0;
transition-property: width;
}
#side-menu.side-menu-big {
max-width: 100%;
height: auto;
}
.side-menu-big .side-menu-header {
height: auto;
}
.side-menu-big .side-menu-apps-list {
height: auto;
position: static;
max-width: 100vw;
overflow: auto;
}
.side-menu-big .side-menu-app a {
padding: 7px 0 7px 7px;
}
.side-menu-categories-wrapper {
padding-bottom: 70px;
}
.side-menu-categories {
max-height: calc(100vh - 50px);
overflow: auto;
position: relative;
top: 50px;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 0 10% 0 10%;
}
.side-menu-category {
padding: 10px 20px;
flex: 1 1 auto;
}
.side-menu-category-title {
padding-left: 10px;
color: var(--side-menu-text-color, #fff);
}
.side-menu-loader {
text-align: center;
}
.side-menu-loader svg {
width: 38px;
margin: auto;
stroke: var(--side-menu-text-color, #fff);
}
.side-menu-big .side-menu-app-icon {
vertical-align: middle;
margin-top: -2px;
}
.side-menu-always-displayed #header,
.side-menu-always-displayed body {
width: calc(100% - 50px) !important;
}
.side-menu-always-displayed body {
position: absolute;
left: 50px;
}
.side-menu-always-displayed #side-menu {
display: block;
}
.side-menu-always-displayed .side-menu-apps-list {
height: calc(100vh - 49px);
top: 49px;
}
.side-menu-always-displayed #side-menu,
.side-menu-always-displayed .side-menu-header,
.side-menu-always-displayed .side-menu-apps-list {
width: 50px;
}
.side-menu-always-displayed #side-menu .side-menu-app-text,
.side-menu-always-displayed #header .side-menu-opener,
.side-menu-always-displayed .side-menu-logo {
display: none;
}
.side-menu-always-displayed #side-menu .side-menu-header {
height: 49px;
}
.side-menu-always-displayed #side-menu.open,
.side-menu-always-displayed #side-menu.open .side-menu-apps-list,
.side-menu-always-displayed #side-menu.open .side-menu-header {
width: 100%;
}
.side-menu-always-displayed #side-menu.open .side-menu-app-text {
display: inline;
}
.side-menu-always-displayed .app-navigation--close {
transform: translateX(calc(-100% + 50px)) !important;
}
@media screen and (max-width: 1024px) {
#side-menu.side-menu-big {
max-width: 290px;
}
.side-menu-categories {
display: block;
padding: 0;
}
.side-menu-category {
padding: 10px 0;
}
#side-menu.side-menu-big {
height: 100vh;
}
}
@media screen and (min-width: 1024px) {
.side-menu-closer {
display: block;
float: right;
margin-right: 9px;
}
.side-menu-big .side-menu-header {
max-width: 100%;
}
}
+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="571.907" height="156.921" viewBox="0 0 151.317 41.519">
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18H58.71v41.16H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18H58.71v5.062H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h6.128v41.16H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M1.496 1.034h3.496V4.39H1.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M2.297 7.455H4.19v1.81H2.297zM2.297 10.997H4.19v1.81H2.297zM2.297 14.404H4.19v1.81H2.297zM2.297 17.946H4.19v1.81H2.297zM2.297 21.353H4.19v1.81H2.297zM2.297 24.895H4.19v1.81H2.297zM2.297 28.302H4.19v1.81H2.297zM2.297 31.844H4.19v1.81H2.297zM7.136 1.034h6.102V4.39H7.136z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M24.44 21.35l5.056-5.056M23.3 29.835l18.15-18.151M36.267 25.225l3.585-3.586"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.865.18h58.532v41.16H62.865z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.865.18h58.532v5.062H62.865z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.865.18h58.532v41.16H62.865z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M84.197 21.35l5.055-5.056M83.056 29.835l18.152-18.151M96.024 25.225l3.585-3.586"/>
</g>
<g color="#000" stroke-width=".359" stroke-linecap="square">
<path style="marker:none" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" paint-order="fill markers stroke" d="M62.866.18h20.829v41.16h-20.83z"/>
<path style="marker:none" overflow="visible" fill="#666" stroke="#ccc" paint-order="fill markers stroke" d="M64.044 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" overflow="visible" fill="#e6e6e6" stroke="#ccc" paint-order="fill markers stroke" d="M64.578 8.076h1.893v1.81h-1.893zM67.507 8.407H79.97v1.148H67.507zM64.578 11.617h1.893v1.81h-1.893zM67.507 11.948h10.592v1.148H67.507zM64.578 15.025h1.893v1.81h-1.893zM67.507 15.356h12.997v1.148H67.507zM64.578 18.566h1.893v1.81h-1.893zM67.507 18.897h7.585v1.148h-7.585zM64.578 21.974h1.893v1.81h-1.893zM67.507 22.305h11.46v1.148h-11.46zM64.578 25.515h1.893v1.81h-1.893zM67.507 25.846h9.322v1.148h-9.322zM64.578 28.923h1.893v1.81h-1.893zM67.507 29.254h11.126v1.148H67.507zM64.578 32.464h1.893v1.81h-1.893zM67.507 32.795H80.37v1.148H67.507z"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.856.18h25.281v41.16h-25.28z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.856.18h25.257v5.062h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.856.18h25.257v41.16h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.856.18h20.83v41.16h-20.83z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.034 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.568 8.076h1.893v1.81h-1.893zM130.497 8.407h12.463v1.148h-12.463zM127.568 11.617h1.893v1.81h-1.893zM130.497 11.948h10.592v1.148h-10.592zM127.568 15.025h1.893v1.81h-1.893zM130.497 15.356h12.997v1.148h-12.997zM127.568 18.566h1.893v1.81h-1.893zM130.497 18.897h7.585v1.148h-7.585zM127.568 21.974h1.893v1.81h-1.893zM130.497 22.305h11.46v1.148h-11.46zM127.568 25.515h1.893v1.81h-1.893zM130.497 25.846h9.322v1.148h-9.322zM127.568 28.923h1.893v1.81h-1.893zM130.497 29.254h11.126v1.148h-11.126zM127.568 32.464h1.893v1.81h-1.893zM130.497 32.795h12.863v1.148h-12.863z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

+30
View File
@@ -0,0 +1,30 @@
<svg xmlns="http://www.w3.org/2000/svg" width="571.907" height="156.921" viewBox="0 0 151.317 41.519">
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h58.532v41.16H.18zM62.735.18h58.532v41.16H62.735z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h58.532v25.123H62.735z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h58.532v41.16H62.735z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M63.738 1.034h3.496V4.39h-3.496z"/>
<g stroke-width=".401" color="#000" fill="#e6e6e6" stroke="#ccc" stroke-linecap="square">
<path style="marker:none" overflow="visible" paint-order="fill markers stroke" d="M38.862 110.138h1.893v1.81h-1.893zM41.792 110.469h12.463v1.148H41.792zM38.862 113.679h1.893v1.81h-1.893zM41.792 114.01h10.592v1.148H41.792zM38.862 117.087h1.893v1.81h-1.893zM41.792 117.418h12.997v1.148H41.792zM38.862 120.628h1.893v1.81h-1.893zM41.792 120.959h7.585v1.148h-7.585z" transform="matrix(.89632 0 0 .8943 38.412 -87.8)"/>
</g>
<g stroke-width=".401" color="#000" fill="#e6e6e6" stroke="#ccc" stroke-linecap="square">
<path style="marker:none" overflow="visible" paint-order="fill markers stroke" d="M65.723 110.272h1.893v1.81h-1.893zM68.652 110.602h11.46v1.148h-11.46zM65.723 113.813h1.893v1.81h-1.893zM68.652 114.144h9.322v1.148h-9.322zM65.723 117.221h1.893v1.81h-1.893zM68.652 117.551h11.126v1.148H68.652zM65.723 120.762h1.893v1.81h-1.893zM68.652 121.093h12.863v1.148H68.652z" transform="matrix(.89632 0 0 .8943 34.262 -87.919)"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M116.257 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M72.715 6.186h11.518v1.148H72.715zM92.452 6.186h9.344v1.148h-9.344z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h58.532v5.063H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h58.532v41.16H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M1.357 1.034h3.496V4.39H1.357z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M6.068 1.034h6.102V4.39H6.068z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.28v41.16h-25.28z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.257v5.063h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.257v41.16h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h20.829v41.16h-20.83z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.034 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.569 10.155h1.893v1.81h-1.893zM130.498 10.486h12.463v1.148h-12.463zM127.569 13.696h1.893v1.81h-1.893zM130.498 14.027h10.592v1.148h-10.592zM127.569 17.104h1.893v1.81h-1.893zM130.498 17.435h12.997v1.148h-12.997zM127.569 24.708h1.893v1.81h-1.893zM130.498 25.04h7.585v1.147h-7.585zM127.569 28.116h1.893v1.81h-1.893zM130.498 28.447h11.46v1.148h-11.46zM127.569 31.657h1.893v1.81h-1.893zM130.498 31.988h9.322v1.148h-9.322zM127.569 35.065h1.893v1.81h-1.893zM130.498 35.396h11.126v1.148h-11.126zM127.616 6.848h11.518v1.148h-11.518zM127.569 21.311h9.344v1.148h-9.344z"/>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" width="571.907" height="156.921" viewBox="0 0 151.317 41.519">
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h58.532v41.16H62.735zM.18.18h58.532v41.16H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h58.532v5.063H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M.18.18h58.532v41.16H.18z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M1.357 1.034h3.496V4.39H1.357z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M6.068 1.034h6.102V4.39H6.068z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h58.532v5.063H62.735z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h58.532v41.16H62.735z"/>
<g fill="#b3b3b3" stroke="#ccc" fill-rule="evenodd" stroke-width="1.465" stroke-linecap="round">
<path d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"/>
</g>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M62.735.18h20.829v41.16h-20.83z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M63.912 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M64.447 8.076h1.893v1.81h-1.893zM67.376 8.407h12.463v1.148H67.376zM64.447 11.617h1.893v1.81h-1.893zM67.376 11.948h10.592v1.148H67.376zM64.447 15.025h1.893v1.81h-1.893zM67.376 15.356h12.997v1.148H67.376zM64.447 18.566h1.893v1.81h-1.893zM67.376 18.897h7.585v1.148h-7.585zM64.447 21.974h1.893v1.81h-1.893zM67.376 22.305h11.46v1.148h-11.46zM64.447 25.515h1.893v1.81h-1.893zM67.376 25.846h9.322v1.148h-9.322zM64.447 28.923h1.893v1.81h-1.893zM67.376 29.254h11.126v1.148H67.376zM64.447 32.464h1.893v1.81h-1.893zM67.376 32.795h12.863v1.148H67.376z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#fff" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.28v41.16h-25.28z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#f9f9f9" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.257v5.063h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="none" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h25.257v41.16h-25.257z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#4d4d4d" stroke="#b3b3b3" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M125.857.18h20.829v41.16h-20.83z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#666" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.034 1.034h3.496V4.39h-3.496z"/>
<path style="marker:none" color="#000" overflow="visible" fill="#e6e6e6" stroke="#ccc" stroke-width=".359" stroke-linecap="square" paint-order="fill markers stroke" d="M127.569 8.076h1.893v1.81h-1.893zM130.498 8.407h12.463v1.148h-12.463zM127.569 11.617h1.893v1.81h-1.893zM130.498 11.948h10.592v1.148h-10.592zM127.569 15.025h1.893v1.81h-1.893zM130.498 15.356h12.997v1.148h-12.997zM127.569 18.566h1.893v1.81h-1.893zM130.498 18.897h7.585v1.148h-7.585zM127.569 21.974h1.893v1.81h-1.893zM130.498 22.305h11.46v1.148h-11.46zM127.569 25.515h1.893v1.81h-1.893zM130.498 25.846h9.322v1.148h-9.322zM127.569 28.923h1.893v1.81h-1.893zM130.498 29.254h11.126v1.148h-11.126zM127.569 32.464h1.893v1.81h-1.893zM130.498 32.795h12.863v1.148h-12.863z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

+113
View File
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="37.099998mm"
height="37.099998mm"
viewBox="0 0 37.099998 37.099998"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="icon.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="242.1839"
inkscape:cy="32.026866"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1918"
inkscape:window-height="1027"
inkscape:window-x="1920"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:snap-nodes="false" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-59.922223,-92.669069)">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.46500015;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-2-7"
width="34.535"
height="34.535"
x="61.204723"
y="93.951569"
rx="5.4806547"
ry="5.4806547" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#ececec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.46500015;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-7"
width="20.357224"
height="33.40107"
x="61.677193"
y="94.518532"
rx="0"
ry="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 66.59458,111.16177 h 9.997359"
id="path873-5"
inkscape:connector-curvature="0" />
<g
id="g1018"
transform="translate(2.6458333)"
style="stroke:#333333;stroke-opacity:1">
<g
id="g1013"
style="stroke:#333333;stroke-opacity:1">
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 63.948747,103.80467 H 75.349244"
id="path873"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#333333;stroke-width:3.16499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 63.948747,118.51887 H 75.349244"
id="path873-5-3"
inkscape:connector-curvature="0" />
</g>
</g>
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#333333;stroke-width:2.56500006;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;enable-background:accumulate"
id="rect815-6-2"
width="34.535"
height="34.535"
x="61.204723"
y="93.951569"
rx="5.4806547"
ry="5.4806547" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+17
View File
@@ -0,0 +1,17 @@
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 694 B

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583">
<g color="#000" fill="#fff">
<rect transform="rotate(135)" ry=".545" rx=".545" y="-8.209" x="-3.608" height="1.451" width="7.216" style="marker:none" overflow="visible" paint-order="stroke markers fill"/>
<rect transform="rotate(-135)" ry=".545" rx=".545" y="-.726" x="-11.091" height="1.451" width="7.216" style="marker:none" overflow="visible" paint-order="stroke markers fill"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 498 B

+173
View File
@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg935"
sodipodi:docname="side-menu-opener-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata941">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs939" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview937"
showgrid="false"
inkscape:zoom="8.34386"
inkscape:cx="83.025045"
inkscape:cy="14.304895"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg935" />
<g
transform="translate(.3 -286.074)"
fill="#fff"
color="#000"
fill-opacity=".855"
id="g933"
style="fill:#000000;fill-opacity:0.89855075">
<rect
ry="0"
rx="0"
y="288.474"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect909" />
<g
transform="translate(-.13)"
id="g915"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect911" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect913" />
</g>
<rect
ry="0"
rx="0"
y="290.631"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect917" />
<g
transform="translate(-.13 2.157)"
id="g923"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect919" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect921" />
</g>
<rect
ry="0"
rx="0"
y="292.808"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#000000;fill-opacity:0.89855075"
overflow="visible"
paint-order="stroke markers fill"
id="rect925" />
<g
transform="translate(-.13 4.334)"
id="g931"
style="fill:#000000;fill-opacity:0.89855075">
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect927" />
<rect
style="marker:none;fill:#000000;fill-opacity:0.89855075"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect929" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+102
View File
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1090"
sodipodi:docname="side-menu-opener-hamburger-2-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1096">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1094" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1092"
showgrid="false"
inkscape:zoom="8.34386"
inkscape:cx="84.523455"
inkscape:cy="12.270318"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1090" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="2.787"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1082" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="4.655"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1084" />
<rect
overflow="visible"
style="marker:none;fill:#000000;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="6.54"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1086" />
<path
style="marker:none;fill:#000000;fill-opacity:1"
d="M1.243.71A.469.469 0 0 0 .881.88a.469.469 0 0 0-.171.362V9.34c0 .146.067.275.171.362a.469.469 0 0 0 .362.171H9.34a.469.469 0 0 0 .362-.171.469.469 0 0 0 .171-.362V1.243a.469.469 0 0 0-.171-.362A.469.469 0 0 0 9.34.71zm.723 1.256h6.65v6.65h-6.65z"
color="#000"
overflow="visible"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="path1088" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+101
View File
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1443"
sodipodi:docname="side-menu-opener-hamburger-2.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1449">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1447" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1445"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="90.508475"
inkscape:cy="4.5762712"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1443" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="2.787"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1435" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="4.655"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1437" />
<rect
overflow="visible"
style="marker:none;fill:#ffffff;fill-opacity:1"
width="4.806"
height="1.256"
x="2.888"
y="6.54"
rx="0"
ry=".472"
color="#000"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="rect1439" />
<path
style="marker:none;fill:#ffffff;fill-opacity:1"
d="M1.243.71A.469.469 0 0 0 .881.88a.469.469 0 0 0-.171.362V9.34c0 .146.067.275.171.362a.469.469 0 0 0 .362.171H9.34a.469.469 0 0 0 .362-.171.469.469 0 0 0 .171-.362V1.243a.469.469 0 0 0-.171-.362A.469.469 0 0 0 9.34.71zm.723 1.256h6.65v6.65h-6.65z"
color="#000"
overflow="visible"
fill="#fff"
fill-opacity=".855"
paint-order="stroke markers fill"
id="path1441" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1168"
sodipodi:docname="side-menu-opener-hamburger-dark.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1174">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1172" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1170"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="112.71186"
inkscape:cy="0.16949153"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1168" />
<g
color="#000"
fill="#fff"
fill-opacity=".855"
transform="translate(0 -286.417)"
id="g1166"
style="fill:#000000;fill-opacity:0.99637681">
<rect
ry=".545"
rx="0"
y="288.816"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1160" />
<rect
ry=".545"
rx="0"
y="290.973"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1162" />
<rect
ry=".545"
rx="0"
y="293.15"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#000000;fill-opacity:0.99637681"
overflow="visible"
paint-order="stroke markers fill"
id="rect1164" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+89 -5
View File
@@ -1,7 +1,91 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<g color="#000" fill="#fff" fill-opacity=".855" transform="translate(0 -286.417)"> <svg
<rect ry=".545" rx="0" y="288.816" x="2.516" height="1.451" width="5.551" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> xmlns:dc="http://purl.org/dc/elements/1.1/"
<rect ry=".545" rx="0" y="290.973" x="2.516" height="1.451" width="5.551" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> xmlns:cc="http://creativecommons.org/ns#"
<rect ry=".545" rx="0" y="293.15" x="2.516" height="1.451" width="5.551" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1382"
sodipodi:docname="side-menu-opener-hamburger.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1388">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1386" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1384"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="108.13559"
inkscape:cy="3.8983051"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1382" />
<g
color="#000"
fill="#fff"
fill-opacity=".855"
transform="translate(0 -286.417)"
id="g1380"
style="fill:#ffffff;fill-opacity:1">
<rect
ry=".545"
rx="0"
y="288.816"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1374" />
<rect
ry=".545"
rx="0"
y="290.973"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1376" />
<rect
ry=".545"
rx="0"
y="293.15"
x="2.516"
height="1.451"
width="5.551"
style="marker:none;fill:#ffffff;fill-opacity:1"
overflow="visible"
paint-order="stroke markers fill"
id="rect1378" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 2.4 KiB

+167 -14
View File
@@ -1,19 +1,172 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 10.583 10.583"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<g transform="translate(.3 -286.074)" fill="#fff" color="#000" fill-opacity=".855"> <svg
<rect ry="0" rx="0" y="288.474" x="4.266" height="1.451" width="1.451" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> xmlns:dc="http://purl.org/dc/elements/1.1/"
<g transform="translate(-.13)"> xmlns:cc="http://creativecommons.org/ns#"
<rect style="marker:none" width="1.451" height="1.451" x="6.445" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
<rect style="marker:none" width="1.451" height="1.451" x="2.345" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="40"
height="40"
viewBox="0 0 10.583 10.583"
version="1.1"
id="svg1321"
sodipodi:docname="side-menu-opener.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata1327">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1325" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1008"
id="namedview1323"
showgrid="false"
inkscape:zoom="5.9"
inkscape:cx="78.983051"
inkscape:cy="5.5932203"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0"
inkscape:current-layer="svg1321" />
<g
transform="translate(.3 -286.074)"
fill="#fff"
color="#000"
fill-opacity=".855"
id="g1319"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
ry="0"
rx="0"
y="288.474"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1295" />
<g
transform="translate(-.13)"
id="g1301"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1297" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1299" />
</g> </g>
<rect ry="0" rx="0" y="290.631" x="4.266" height="1.451" width="1.451" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> <rect
<g transform="translate(-.13 2.157)"> ry="0"
<rect style="marker:none" width="1.451" height="1.451" x="6.445" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> rx="0"
<rect style="marker:none" width="1.451" height="1.451" x="2.345" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> y="290.631"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1303" />
<g
transform="translate(-.13 2.157)"
id="g1309"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1305" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1307" />
</g> </g>
<rect ry="0" rx="0" y="292.808" x="4.266" height="1.451" width="1.451" style="marker:none" overflow="visible" paint-order="stroke markers fill"/> <rect
<g transform="translate(-.13 4.334)"> ry="0"
<rect style="marker:none" width="1.451" height="1.451" x="6.445" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> rx="0"
<rect style="marker:none" width="1.451" height="1.451" x="2.345" y="288.474" rx="0" ry="0" overflow="visible" paint-order="stroke markers fill"/> y="292.808"
x="4.266"
height="1.451"
width="1.451"
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
overflow="visible"
paint-order="stroke markers fill"
id="rect1311" />
<g
transform="translate(-.13 4.334)"
id="g1317"
style="fill:#ffffff;fill-opacity:0.94565219">
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="6.445"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1313" />
<rect
style="marker:none;fill:#ffffff;fill-opacity:0.94565219"
width="1.451"
height="1.451"
x="2.345"
y="288.474"
rx="0"
ry="0"
overflow="visible"
paint-order="stroke markers fill"
id="rect1315" />
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace OCA\SideMenu\AppInfo;
use OC;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\User\User;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\App;
use OCP\IUserSession;
use OCP\Util;
use Psr\Container\ContainerInterface;
/**
* class Application.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class Application extends App
{
public const APP_ID = 'side_menu';
public const APP_NAME = 'Custom menu';
/**
* @var OC\AllConfig
*/
protected $config;
/**
* @var ContentSecurityPolicyNonceManager
*/
protected $cspnm;
/**
* @var User
*/
protected $user;
/**
* {@inheritdoc}
*/
public function __construct(array $urlParams = [])
{
parent::__construct(self::APP_ID, $urlParams);
$this->config = OC::$server->getConfig();
$this->cspnm = OC::$server->getContentSecurityPolicyNonceManager();
$this->user = OC::$server[IUserSession::class]->getUser();
}
/**
* Checks if this app is enabled.
*/
public function isEnabled(): bool
{
$enabled = true;
$isForced = (bool) $this->config->getAppValue(self::APP_ID, 'force', '0');
if (null !== $this->user && !$isForced) {
$enabled = (bool) $this->config->getUserValue(
$this->user->getUid(),
self::APP_ID,
'enabled',
$this->config->getAppValue(
self::APP_ID,
'default-enabled',
'1'
)
);
}
return $enabled;
}
/**
* Registes services.
*/
public function registerServices()
{
$container = $this->getContainer();
$container->registerService('AppRepository', function (ContainerInterface $c) {
return new AppRepository();
});
$container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config);
});
}
/**
* Registers assets.
*/
public function registerAssets()
{
Util::addScript(self::APP_ID, 'sideMenu');
Util::addStyle(self::APP_ID, 'sideMenu');
$stylesheet = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Css.stylesheet',
[
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
]
);
$script = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Js.script',
[
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
]
);
Util::addHeader(
'link',
[
'href' => $stylesheet,
'rel' => 'stylesheet',
],
''
);
Util::addHeader(
'script',
[
'src' => $script,
'nonce' => $this->cspnm->getNonce(),
],
''
);
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Controller;
use OCA\SideMenu\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IURLGenerator;
class AdminSettingController extends Controller
{
/**
* @var IConfig
*/
protected $config;
/**
* @var IURLGenerator
*/
protected $urlGenerator;
public function __construct($appName, IRequest $request, IConfig $config, IURLGenerator $urlGenerator)
{
parent::__construct($appName, $request);
$this->config = $config;
$this->urlGenerator = $urlGenerator;
}
/**
* @NoCSRFRequired
*
* @return RedirectResponse
*/
public function removeCache()
{
$this->config->setAppValue(Application::APP_ID, 'cache-categories', '[]');
return new RedirectResponse($this->urlGenerator->linkToRoute('settings.AdminSettings.index', [
'section' => Application::APP_ID,
]).'#more');
}
/**
* @NoCSRFRequired
*
* @return Response
*/
public function exportConfiguration()
{
$keys = $this->config->getAppKeys(Application::APP_ID);
$config = [];
$excludedKeys = [
'cache',
'cache-categories',
];
foreach ($keys as $key) {
if (in_array($key, $excludedKeys)) {
continue;
}
$config[$key] = $this->config->getAppValue(Application::APP_ID, $key);
}
return new DataDownloadResponse(
json_encode($config, JSON_PRETTY_PRINT),
'config.json',
'text/json'
);
}
}
+52 -24
View File
@@ -18,27 +18,33 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OC;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Response;
use OCP\IConfig; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession;
class CssController extends Controller class CssController extends Controller
{ {
/**
/** @var \OCP\IConfig */ * @var ConfigProxy
*/
protected $config; protected $config;
/** /**
* @param string $appName * @var User
* @param IRequest $request
* @param IConfig $config
*/ */
public function __construct($appName, IRequest $request, IConfig $config) protected $user;
public function __construct(string $appName, IRequest $request, ConfigProxy $config)
{ {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config; $this->config = $config;
} }
@@ -51,24 +57,46 @@ class CssController extends Controller
*/ */
public function stylesheet() public function stylesheet()
{ {
$requestUri = $this->request->server['REQUEST_URI']; $response = new TemplateResponse(Application::APP_ID, 'css/stylesheet', $this->getConfig(), 'blank');
$assetsRoot = str_replace('/index.php', '', $requestUri);
$assetsRoot = preg_replace('#apps/side_menu/.*#s', 'apps/side_menu', $assetsRoot);
$parameters = [
'vars' => [
'background-color' => $this->config->getAppValue('side_menu', 'background-color', '#333333'),
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'),
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'),
],
'display-logo' => (bool) $this->config->getAppValue('side_menu', 'display-logo', 1),
'assetsRoot' => $assetsRoot,
];
$response = new TemplateResponse('side_menu', 'css/stylesheet', $parameters, 'blank');
$response->addHeader('Content-Type', 'text/css'); $response->addHeader('Content-Type', 'text/css');
return $response; return $response;
} }
protected function getConfig(): array
{
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$isForced = $this->config->getAppValueBool('force', '0');
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps;
}
}
return [
'vars' => [
'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'),
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'icon-invert-filter' => abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%',
'icon-opacity' => abs($this->config->getAppValueInt('icon-opacity', '100') / 100),
],
'display-logo' => $this->config->getAppValueBool('display-logo', '1'),
'opener-only' => $this->config->getAppValueBool('opener-only', '0'),
'external-sites-in-top-menu' => $this->config->getAppValueBool('external-sites-in-top-menu', '0'),
'size-icon' => $this->config->getAppValue('size-icon', 'normal'),
'size-text' => $this->config->getAppValue('size-text', 'normal'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'top-menu-apps' => $topMenuApps,
];
}
} }
+114 -18
View File
@@ -18,47 +18,143 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OC;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCA\Theming\ThemingDefaults;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class JsController extends Controller class JsController extends Controller
{ {
/**
/** @var \OCP\IConfig */ * @var ConfigProxy
*/
protected $config; protected $config;
/** /**
* @param string $appName * @var User
* @param IRequest $request
* @param IConfig $config
*/ */
public function __construct($appName, IRequest $request, IConfig $config) protected $user;
{
/**
* @var ThemingDefaults
*/
protected $themingDefaults;
/**
* @var IFactory
*/
protected $l10nFactory;
public function __construct(
string $appName,
IRequest $request,
ConfigProxy $config,
ThemingDefaults $themingDefaults,
IFactory $l10nFactory
) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->themingDefaults = $themingDefaults;
$this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config; $this->config = $config;
$this->l10nFactory = $l10nFactory;
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
* @PublicPage * @PublicPage
*
* @return Response
*/ */
public function script() public function script(): TemplateResponse
{ {
$parameters = [ $response = new TemplateResponse(Application::APP_ID, 'js/script', $this->getConfig(), 'blank');
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'),
'opener-hover' => (bool) $this->config->getAppValue('side_menu', 'opener-hover', '0'),
];
$response = new TemplateResponse('side_menu', 'js/script', $parameters, 'blank');
$response->addHeader('Content-Type', 'text/javascript'); $response->addHeader('Content-Type', 'text/javascript');
return $response; return $response;
} }
/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*/
public function config(): JSONResponse
{
return new JSONResponse($this->getConfig());
}
protected function getConfig(): array
{
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]');
$useAvatar = $this->config->getAppValueBool('use-avatar', '0');
$isForced = $this->config->getAppValueBool('force', '0');
$avatar = null;
$settings = null;
if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps;
}
$userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1');
$userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]');
if (2 === $userTargetBlankMode && !$isForced) {
$targetBlankApps = $userTargetBlankApps;
}
$isAvatarSet = OC::$server->getAvatarManager()->getAvatar($this->user->getUid())->exists();
if ($useAvatar && $isAvatarSet) {
$avatar = OC::$server->getURLGenerator()->linkToRoute('core.avatar.getAvatar', [
'userId' => $this->user->getUid(),
'size' => 128,
'v' => $this->config->getUserValueInt($this->user, 'avatar', 'version', 0),
]);
}
if ($this->config->getAppValueBool('show-settings', '0')) {
$settingsNav = OC::$server->getNavigationManager()->getAll('settings');
if (isset($settingsNav['settings'])) {
$settings = [
'href' => $settingsNav['settings']['href'],
'name' => $settingsNav['settings']['name'],
'avatar' => OC::$server->getURLGenerator()->linkToRoute('core.avatar.getAvatar', [
'userId' => $this->user->getUid(),
'size' => 32,
'v' => $this->config->getUserValueInt($this->user, 'avatar', 'version', 0),
]),
];
}
}
}
return [
'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'),
'external-sites-in-top-menu' => $this->config->getAppValueBool('external-sites-in-top-menu', '0'),
'force-light-icon' => $this->config->getAppValueBool('force-light-icon', '0'),
'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'),
'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'avatar' => $avatar,
'top-menu-apps' => $topMenuApps,
'target-blank-apps' => $targetBlankApps,
'settings' => $settings,
'logo' => $this->themingDefaults->getLogo(),
];
}
} }
+222
View File
@@ -0,0 +1,222 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Controller;
use OC;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class NavController extends Controller
{
/**
* @var ConfigProxy
*/
protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/**
* @var AppRepository
*/
protected $appRepository;
/**
* @var IFactory
*/
protected $l10nFactory;
/**
* @var CategoryFetcher
*/
protected $categoryFetcher;
/**
* @var URLGenerator
*/
protected $router;
public function __construct(
string $appName,
IRequest $request,
IConfig $iConfig,
ConfigProxy $config,
AppRepository $appRepository,
CategoryFetcher $categoryFetcher,
URLGenerator $router,
IL10N $trans,
IFactory $l10nFactory
) {
parent::__construct($appName, $request);
$this->config = $config;
$this->iConfig = $iConfig;
$this->appRepository = $appRepository;
$this->categoryFetcher = $categoryFetcher;
$this->l10nFactory = $l10nFactory;
$this->router = $router;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*
* @return JSONResponse
*/
public function items()
{
$apps = $this->appRepository->getVisibleApps();
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$externalSitesInTopMenu = $this->config->getAppValueBool('external-sites-in-top-menu', '0');
$user = OC::$server[IUserSession::class]->getUser();
$isForced = $this->config->getAppValueBool('force', '0');
$appsCategories = [];
$categoriesAppsCount = [];
$items = [];
if (!$user) {
return new JSONResponse([
'items' => $items,
]);
}
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]');
if (empty($categoriesLabels)) {
$categoriesLabels = $this->categoryFetcher->get();
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
if (!$isForced && !empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
foreach ($categoriesLabels as $k => $category) {
$categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'];
unset($categoriesLabels[$k]);
}
$categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites');
$items['other'] = [
'name' => '',
'apps' => [],
];
foreach ($apps as $app) {
if (in_array($app['id'], $topMenuApps)) {
continue;
}
$categories = (array) $app['category'];
$appsCategories[$app['id']] = [];
if (empty($categories)) {
$categories = ['other'];
}
foreach ($categories as $category) {
if (!isset($items[$category])) {
$items[$category] = [
'name' => $categoriesLabels[$category] ?? $category,
'apps' => [],
];
}
if (!isset($categoriesAppsCount[$category])) {
$categoriesAppsCount[$category] = 0;
}
++$categoriesAppsCount[$category];
$appsCategories[$app['id']][] = $category;
$items[$category]['apps'][$app['id']] = [
'name' => $app['name'],
'href' => $app['href'],
'icon' => $app['icon'],
];
}
}
arsort($categoriesAppsCount);
$keys = array_keys($categoriesAppsCount);
foreach ($appsCategories as $app => $appCategories) {
$smallerIndex = count($categoriesAppsCount) - 1;
foreach ($appCategories as $appCategory) {
$appKey = array_keys($keys, $appCategory)[0];
if ($appKey < $smallerIndex) {
$smallerIndex = $appKey;
}
}
$category = $keys[$smallerIndex];
foreach ($items as $itemCategory => $value) {
if ($itemCategory !== $category && isset($value['apps'][$app])) {
unset($items[$itemCategory]['apps'][$app]);
if (empty($items[$itemCategory]['apps'])) {
unset($items[$itemCategory]);
}
}
}
}
foreach ($items as $category => $value) {
if (empty($items[$category]['apps'])) {
unset($items[$category]);
} else {
uasort($items[$category]['apps'], function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1;
});
}
}
usort($items, function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1;
});
return new JSONResponse([
'items' => $items,
]);
}
}
@@ -0,0 +1,129 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
class PersonalSettingController extends Controller
{
/**
* @var IConfig
*/
protected $config;
/**
* @var ConfigProxy
*/
protected $configProxy;
/**
* @var IUserSession
*/
protected $userSession;
public function __construct($appName, IRequest $request, IConfig $config, ConfigProxy $configProxy, IUserSession $userSession)
{
parent::__construct($appName, $request);
$this->config = $config;
$this->configProxy = $configProxy;
$this->userSession = $userSession;
}
/**
* @NoAdminRequired
*
* @param mixed $name
* @param mixed $value
*
* @return Response
*/
public function valueSet($name, $value)
{
$doSave = false;
$user = $this->userSession->getUser();
if ('enabled' === $name) {
$doSave = true;
if (!in_array($value, ['0', '1'])) {
$value = '1';
}
}
if ('target-blank-mode' === $name) {
$doSave = true;
if (!in_array($value, ['1', '2'])) {
$value = '1';
}
}
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 ('top-menu-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 ($this->configProxy->getAppValueBool('force', '0')) {
$doSave = false;
}
if ($doSave) {
$this->config->setUserValue($user->getUid(), Application::APP_ID, $name, $value);
return [
'name' => $name,
'value' => $value,
];
}
return [];
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
namespace OCA\SideMenu\Service;
use OCP\L10N\IFactory;
/**
* class AppRepository.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class AppRepository
{
/**
* @var \OC_App
*/
protected $ocApp;
/**
* @var IFactory
*/
protected $l10nFactory;
public function __construct(\OC_App $ocApp, IFactory $l10nFactory)
{
$this->ocApp = $ocApp;
$this->l10nFactory = $l10nFactory;
}
/**
* Retrieves visibles apps.
*
* @return array
*/
public function getVisibleApps()
{
$navigation = $this->ocApp->getNavigation();
$apps = $this->ocApp->listAllApps();
$visibleApps = [];
foreach ($apps as $app) {
$id = $app['id'];
foreach ([$app['id'], $app['id'].'_index'] as $id) {
if (isset($navigation[$id])) {
$app['name'] = $this->l10nFactory->get($id)->t($app['name']);
$app['href'] = $navigation[$id]['href'];
$app['icon'] = $navigation[$id]['icon'];
$visibleApps[$id] = $app;
}
}
}
foreach ($navigation as $app) {
if ('external_index' === substr($app['id'], 0, 14)) {
$visibleApps[$app['id']] = [
'id' => $app['id'],
'name' => $this->l10nFactory->get($app['id'])->t($app['name']),
'href' => $app['href'],
'icon' => $app['icon'],
'category' => [
'external_links',
],
];
} elseif ('files' === $app['id']) {
$visibleApps[$app['id']] = [
'id' => $app['id'],
'name' => $this->l10nFactory->get($app['id'])->t($app['name']),
'href' => $app['href'],
'icon' => $app['icon'],
'category' => [],
];
}
}
usort($visibleApps, function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1;
});
return $visibleApps;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace OCA\SideMenu\Service;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCP\IConfig;
/**
* class Config.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class ConfigProxy
{
/**
* @var IConfig
*/
protected $config;
public function __construct(IConfig $config)
{
$this->config = $config;
}
public function getAppValue(string $name, string $default): string
{
return (string) $this->config->getAppValue(Application::APP_ID, $name, $default);
}
public function getUserValue(User $user, string $name, string $default): string
{
return (string) $this->config->getUserValue($user->getUid(), Application::APP_ID, $name, $default);
}
public function getAppValueBool(string $name, string $default): bool
{
return (bool) $this->getAppValue($name, $default);
}
public function getAppValueArray(string $name, string $default): array
{
return (array) json_decode($this->getAppValue($name, $default), true);
}
public function getAppValueInt(string $name, string $default): int
{
return (int) $this->getAppValue($name, $default);
}
public function getUserValueBool(User $user, string $name, string $default): bool
{
return (bool) $this->getUserValue($user, $name, $default);
}
public function getUserValueArray(User $user, string $name, string $default): array
{
return (array) json_decode($this->getUserValue($user, $name, $default), true);
}
public function getUserValueInt(User $user, string $name, string $default): int
{
return (int) $this->getUserValue($user, $name, $default);
}
}
+52 -17
View File
@@ -18,28 +18,42 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger; use OCP\ILogger;
use OCP\Settings\ISettings; use OCP\Settings\ISettings;
use OCP\IConfig;
class Admin implements ISettings class Admin implements ISettings
{ {
/** @var IL10N */ /**
* @var IL10N
*/
private $l; private $l;
/** @var ILogger */ /**
* @var ILogger
*/
private $logger; private $logger;
/** @var IConfig */ /**
* @var ConfigProxy
*/
private $config; private $config;
public function __construct(IL10N $l, ILogger $logger, IConfig $config) /**
* @var AppRepository
*/
private $appRepository;
public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, AppRepository $appRepository)
{ {
$this->l = $l; $this->l = $l;
$this->logger = $logger; $this->logger = $logger;
$this->config = $config; $this->config = $config;
$this->appRepository = $appRepository;
} }
/** /**
@@ -47,18 +61,39 @@ class Admin implements ISettings
*/ */
public function getForm() public function getForm()
{ {
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$parameters = [ $parameters = [
'background-color' => $this->config->getAppValue('side_menu', 'background-color', '#333333'), 'background-color' => $backgroundColor,
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'), 'background-color-to' => $backgroundColorTo,
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'), 'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'),
'cache' => $this->config->getAppValue('side_menu', 'cache', '0'), 'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'),
'display-logo' => $this->config->getAppValue('side_menu', 'display-logo', '1'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'),
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'), 'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'opener-hover' => $this->config->getAppValue('side_menu', 'opener-hover', '0'), 'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'),
'cache' => $this->config->getAppValue('cache', '0'),
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'always-displayed' => $this->config->getAppValue('always-displayed', '0'),
'big-menu' => $this->config->getAppValue('big-menu', '0'),
'display-logo' => $this->config->getAppValue('display-logo', '1'),
'use-avatar' => $this->config->getAppValue('use-avatar', '0'),
'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValue('opener-hover', '0'),
'opener-only' => $this->config->getAppValue('opener-only', '0'),
'show-settings' => $this->config->getAppValue('show-settings', '0'),
'hide-when-no-apps' => $this->config->getAppValue('hide-when-no-apps', '0'),
'size-icon' => $this->config->getAppValue('size-icon', 'normal'),
'size-text' => $this->config->getAppValue('size-text', 'normal'),
'force' => $this->config->getAppValue('force', '0'),
'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'),
'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'),
'default-enabled' => $this->config->getAppValue('default-enabled', '1'),
'apps' => $this->appRepository->getVisibleApps(),
]; ];
return new TemplateResponse('side_menu', 'settings/admin-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
} }
/** /**
@@ -66,13 +101,13 @@ class Admin implements ISettings
*/ */
public function getSection() public function getSection()
{ {
return 'side_menu'; return Application::APP_ID;
} }
/** /**
* @return int whether the form should be rather on the top or bottom of * @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 * 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. * priority values. It is required to return a value between 0 and 100.
* *
* E.g.: 70 * E.g.: 70
*/ */
@@ -21,13 +21,18 @@ namespace OCA\SideMenu\Settings;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Settings\IIconSection; use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class Section implements IIconSection class AdminSection implements IIconSection
{ {
/** @var IL10N */ /**
* @var IL10N
*/
private $l; private $l;
/** @var IURLGenerator */ /**
* @var IURLGenerator
*/
private $url; private $url;
/** /**
@@ -48,7 +53,7 @@ class Section implements IIconSection
*/ */
public function getID() public function getID()
{ {
return 'side_menu'; return Application::APP_ID;
} }
/** /**
@@ -59,7 +64,7 @@ class Section implements IIconSection
*/ */
public function getName() public function getName()
{ {
return $this->l->t('Side menu'); return $this->l->t(Application::APP_NAME);
} }
/** /**
@@ -79,6 +84,6 @@ class Section implements IIconSection
*/ */
public function getIcon() public function getIcon()
{ {
return $this->url->imagePath('theming', 'app-dark.svg'); return $this->url->imagePath(Application::APP_ID, 'icon.svg');
} }
} }
+108
View File
@@ -0,0 +1,108 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IUserSession;
use OCP\Settings\ISettings;
class Personal implements ISettings
{
/**
* @var IL10N
*/
private $l;
/**
* @var ILogger
*/
private $logger;
/**
* @var ConfigProxy
*/
private $config;
/**
* @var IUserSession
*/
private $userSession;
/**
* @var AppRepository
*/
private $appRepository;
public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, IUserSession $userSession, AppRepository $appRepository)
{
$this->l = $l;
$this->logger = $logger;
$this->config = $config;
$this->userSession = $userSession;
$this->appRepository = $appRepository;
}
/**
* @return TemplateResponse
*/
public function getForm()
{
$user = $this->userSession->getUser();
$parameters = [
'force' => $this->config->getAppValueBool('force', '0'),
'enabled' => $this->config->getUserValue(
$user,
'enabled',
$this->config->getAppValue('default-enabled', '1')
),
'top-menu-apps' => $this->config->getUserValueArray($user, 'top-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(),
];
return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, '');
}
/**
* @return string the section ID, e.g. 'sharing'
*/
public function getSection()
{
return Application::APP_ID;
}
/**
* @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
*/
public function getPriority()
{
return 70;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
namespace OCA\SideMenu\Settings;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class PersonalSection implements IIconSection
{
/**
* @var IL10N
*/
private $l;
/**
* @var IURLGenerator
*/
private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
public function __construct(IURLGenerator $url, IL10N $l)
{
$this->url = $url;
$this->l = $l;
}
/**
* returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap'
*
* @returns string
*/
public function getID()
{
return Application::APP_ID;
}
/**
* returns the translated name as it should be displayed, e.g. 'LDAP / AD
* integration'. Use the L10N service to translate it.
*
* @return string
*/
public function getName()
{
return $this->l->t(Application::APP_NAME);
}
/**
* @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
*/
public function getPriority()
{
return 70;
}
/**
* {@inheritdoc}
*/
public function getIcon()
{
return $this->url->imagePath(Application::APP_ID, 'icon.svg');
}
}
+2
View File
@@ -13,6 +13,8 @@
"dependencies": { "dependencies": {
"@nextcloud/axios": "^1.3.2", "@nextcloud/axios": "^1.3.2",
"@nextcloud/vue": "^1.4.0", "@nextcloud/vue": "^1.4.0",
"axios": "^0.19.2",
"trim": "0.0.1",
"vue": "^2.6.11" "vue": "^2.6.11"
}, },
"browserslist": [ "browserslist": [
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

+25
View File
@@ -0,0 +1,25 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<button class="side-menu-opener side-menu-closer"></button>
</template>
<script>
export default {
name: 'CloserButton',
}
</script>
+42
View File
@@ -0,0 +1,42 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<div class="side-menu-loader">
<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
</div>
</template>
<script>
export default {
name: 'Loader',
}
</script>
+37
View File
@@ -0,0 +1,37 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<div v-bind:class="classes">
<img v-bind:src="image">
</div>
</template>
<script>
export default {
name: 'Logo',
props: {
image: {
type: String,
required: true
},
classes: {
type: Object,
required: true
},
},
}
</script>
+25
View File
@@ -0,0 +1,25 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<button class="side-menu-opener"></button>
</template>
<script>
export default {
name: 'OpenerButton',
}
</script>
+47
View File
@@ -0,0 +1,47 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<div class="side-menu-settings">
<a v-bind:href="href">
{{ label }}
<span class="avatardiv avatardiv-shown">
<img v-bind:src="avatar" v-bind:alt="name" v-bind:title="name">
</span>
</a>
</div>
</template>
<script>
export default {
name: 'SettingsButton',
props: {
label: {
type: String,
required: true
},
href: {
type: String,
required: true
},
avatar: {
type: String,
required: true
},
},
}
</script>
+14 -3
View File
@@ -17,19 +17,30 @@
import Vue from 'vue' import Vue from 'vue'
import SideMenu from './SideMenu.vue' import SideMenu from './SideMenu.vue'
import SideMenuBig from './SideMenuBig.vue'
// Vue.prototype.t = t // Vue.prototype.t = t
// Vue.prototype.OC = OC Vue.prototype.OC = OC
// Vue.prototype.OC = OCP // Vue.prototype.OC = OCP
const View = Vue.extend(SideMenu)
const sideMenu = new View({})
const mountSideMenuComponent = () => { const mountSideMenuComponent = () => {
const sideMenuContainer = document.querySelector('#side-menu') const sideMenuContainer = document.querySelector('#side-menu')
if (sideMenuContainer) { if (sideMenuContainer) {
let component
if (sideMenuContainer.getAttribute('data-bigmenu')) {
component = SideMenuBig
} else {
component = SideMenu
}
const View = Vue.extend(component)
const sideMenu = new View({})
sideMenu.$mount('#side-menu') sideMenu.$mount('#side-menu')
$('body').trigger('side-menu.ready') $('body').trigger('side-menu.ready')
} else { } else {
window.setTimeout(mountSideMenuComponent, 50) window.setTimeout(mountSideMenuComponent, 50)
+97 -33
View File
@@ -17,75 +17,139 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template> <template>
<div id="side-menu"> <div id="side-menu">
<div class="side-menu-header"> <div class="side-menu-header">
<button class="side-menu-opener"></button> <SettingsButton
v-if="settings"
v-bind:href="settings.href"
v-bind:label="settings.name"
v-bind:avatar="settings.avatar" />
<div v-if="logo" class="side-menu-logo"> <OpenerButton />
<img v-bind:src="logo">
</div> <Logo v-if="!avatar && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}" v-bind:image="logo" />
<Logo v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}" v-bind:image="avatar" />
</div> </div>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
<li v-for="app in apps" v-bind:class="{'side-menu-app': true, 'active': app.active}"> <SideMenuApp
<a v-bind:href="app.href"> v-for="app in apps"
<span class="side-menu-app-icon" v-html="app.icon"></span> v-bind:classes="{'side-menu-app': true, 'active': app.active}"
v-bind:icon="app.icon"
{{ app.name }} v-bind:label="app.name"
</a> v-bind:href="app.href"
</li> v-bind:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
/>
</ul> </ul>
</div> </div>
</template> </template>
<script> <script>
import trim from 'trim'
import axios from 'axios'
import OpenerButton from './OpenerButton'
import SettingsButton from './SettingsButton'
import SideMenuApp from './SideMenuApp'
import Logo from './Logo'
export default { export default {
name: 'SideMenu', name: 'SideMenu',
components: {
SettingsButton,
OpenerButton,
SideMenuApp,
Logo,
},
data() { data() {
return { return {
apps: [], apps: [],
logo: null logo: null,
avatar: null,
forceLightIcon: false,
targetBlankApps: [],
settings: null,
} }
}, },
methods: { methods: {
retrieveApps() { retrieveApps() {
this.apps = []; this.apps = []
const links = document.querySelectorAll('#appmenu a'); const links = document.querySelectorAll('#appmenu a')
const menu = document.querySelector('#appmenu')
let menuIsHidden = true
if (menu) {
menuIsHidden = window.getComputedStyle(menu, null).getPropertyValue('display') === 'none'
}
for (let element of links) { for (let element of links) {
let href = element.getAttribute('href') let href = element.getAttribute('href')
var parent = element.parentNode
if (!parent) {
continue
}
var dataId = parent.getAttribute('data-id')
dataId = dataId !== null ? dataId : ''
if (!parent.classList.contains('app-hidden') && !menuIsHidden) {
continue
}
if (href !== '#') { if (href !== '#') {
let svg = element.querySelector('svg').outerHTML
svg = svg
.replace(/(height|width)="20"/, '')
.replace('id="invertMenuMain', 'id="invertSideMenu')
.replace('url(#invertMenuMain', 'url(#invertSideMenu')
if (this.forceLightIcon) {
svg = svg.replace(/filter="url[^"]+"/, '')
}
this.apps.push({ this.apps.push({
id: dataId,
href: href, href: href,
name: element.querySelector('span').innerHTML, name: trim(element.querySelector('span').innerHTML),
icon: element.querySelector('svg').outerHTML, icon: svg,
active: element.classList.contains('active') active: element.classList.contains('active')
}); });
} }
} }
(function(apps) {
window.setTimeout(function() {
jQuery('body').trigger('side-menu.apps', [apps])
}, 1000)
})(this.apps)
}, },
retrieveLogo() {
const ncLogo = document.querySelector('#nextcloud .logo')
if (ncLogo) { retrieveConfig() {
const url = window.getComputedStyle(ncLogo, null) let that = this
.getPropertyValue('background-image')
.replace('url("', '')
.replace('")', '')
if (url && url !== 'none') { axios
this.logo = url .get(OC.generateUrl('/apps/side_menu/js/config'))
} .then(function(response) {
} const config = response.data
that.targetBlankApps = config['target-blank-apps']
that.forceLightIcon = config['force-light-icon']
that.avatar = config['avatar']
that.logo = config['logo']
that.settings = config['settings']
});
}, },
}, },
created() { mounted() {
this.retrieveApps(); this.retrieveConfig()
this.retrieveLogo(); this.retrieveApps()
const menu = document.querySelector('#appmenu') const menu = document.querySelector('#appmenu')
const config = {attributes: true, childList: true, subtree: true};
const observer = new MutationObserver(this.retrieveApps); if (menu) {
observer.observe(menu, config); const config = {attributes: true, childList: true, subtree: true}
const observer = new MutationObserver(this.retrieveApps)
observer.observe(menu, config)
}
} }
} }
</script> </script>
+52
View File
@@ -0,0 +1,52 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<li v-bind:class="classes">
<a v-bind:href="href" :target="target" v-bind:title="label">
<span class="side-menu-app-icon" v-html="icon"></span>
<span class="side-menu-app-text" v-html="label"></span>
</a>
</li>
</template>
<script>
export default {
name: 'SideMenuApp',
props: {
label: {
type: String,
required: true
},
icon: {
type: String,
required: true
},
href: {
type: String,
required: true
},
classes: {
type: Object,
required: true
},
target: {
type: String,
required: false
},
},
}
</script>
+128
View File
@@ -0,0 +1,128 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<div id="side-menu" class="side-menu-big">
<div class="side-menu-header">
<CloserButton />
<SettingsButton
v-if="settings"
v-bind:href="settings.href"
v-bind:label="settings.name"
v-bind:avatar="settings.avatar"
/>
<OpenerButton />
</div>
<div class="side-menu-categories-wrapper">
<div class="side-menu-categories">
<Loader v-if="!items.length" />
<div class="side-menu-category" v-for="category in items">
<h2 class="side-menu-category-title" v-if="category.name != ''" v-html="category.name"></h2>
<ul class="side-menu-apps-list">
<SideMenuBigApp
v-for="(app, appId) in category.apps"
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
v-bind:icon="app.icon"
v-bind:label="app.name"
v-bind:href="app.href"
v-bind:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
/>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import trim from 'trim'
import axios from 'axios'
import OpenerButton from './OpenerButton'
import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton'
import Loader from './Loader'
import SideMenuBigApp from './SideMenuBigApp'
export default {
name: 'SideMenuBig',
components: {
SettingsButton,
OpenerButton,
CloserButton,
Loader,
SideMenuBigApp,
},
data() {
return {
items: [],
activeApp: null,
targetBlank: false,
targetBlankApps: [],
settings: null,
}
},
methods: {
retrieveApps() {
this.apps = []
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/nav/items'))
.then(function(response) {
that.items = response.data.items
let apps = []
for (let category of that.items) {
for (let a in category.apps) {
apps.push(category.apps[a])
}
}
jQuery('body').trigger('side-menu.apps', [apps])
});
},
retrieveActiveApp() {
let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
},
retrieveConfig() {
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then(function(response) {
const config = response.data
that.targetBlankApps = config['target-blank-apps']
that.settings = config['settings']
});
},
},
mounted() {
this.retrieveConfig()
this.retrieveApps()
this.retrieveActiveApp()
}
}
</script>
+52
View File
@@ -0,0 +1,52 @@
<!--
@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 <http://www.gnu.org/licenses/>.
-->
<template>
<li v-bind:class="classes">
<a v-bind:href="href" :target="target" v-bind:title="label">
<img class="side-menu-app-icon" v-bind:src="icon"></span>
<span class="side-menu-app-text" v-html="label"></span>
</a>
</li>
</template>
<script>
export default {
name: 'SideMenuBigApp',
props: {
label: {
type: String,
required: true
},
icon: {
type: String,
required: true
},
href: {
type: String,
required: true
},
classes: {
type: Object,
required: true
},
target: {
type: String,
required: false
},
},
}
</script>
+86 -21
View File
@@ -15,30 +15,48 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
const elements = [ let elements = []
'side-menu-background-color',
'side-menu-current-app-background-color',
'side-menu-text-color',
'side-menu-opener',
'side-menu-opener-position',
'side-menu-opener-hover',
'side-menu-display-logo',
'side-menu-cache',
];
const selector = '#side-menu-message'; const selector = '#side-menu-message';
const saveSettings = (key) => { const userConfig = (name, value, callbacks) => {
const element = elements[key] const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet')
const name = $('#' + element).attr('name')
let value = $('#' + element).val()
const size = elements.length
if (element === 'side-menu-cache') { $.post(url, {name: name, value: value}, callbacks.success)
value++ .fail(callbacks.error)
}
const appConfig = (name, value, callbacks) => {
OCP.AppConfig.setValue('side_menu', name, value, callbacks)
}
const saveSettings = (key) => {
const element = elements.get(key)
let value
let name
if (jQuery(element).is('[data-checkbox]')) {
name = jQuery(element).attr('data-name')
const inputs = jQuery('input[name="' + name + '[]"]:checked')
value = []
inputs.each((i, v) => {
value.push(v.value)
})
value = JSON.stringify(value)
} else {
name = jQuery(element).attr('name')
value = jQuery(element).val()
} }
OCP.AppConfig.setValue('side_menu', name, value, { const size = elements.length
if (name === 'cache') {
++value
}
const callbacks = {
success: () => { success: () => {
OC.msg.finishedSuccess( OC.msg.finishedSuccess(
selector, selector,
@@ -54,14 +72,61 @@ const saveSettings = (key) => {
error: () => { error: () => {
OC.msg.finishedError(selector, t('side_menu', 'Error while saving "' + element + '"')) OC.msg.finishedError(selector, t('side_menu', 'Error while saving "' + element + '"'))
} }
}); }
if (jQuery(element).is('[data-personal]')) {
userConfig(name, value, callbacks)
} else {
appConfig(name, value, callbacks)
}
} }
$(document).ready(() => { const elementToggler = (element) => {
$('#side-menu-save').on('click', (event) => { jQuery(element).toggle()
}
jQuery(document).ready(() => {
elements = jQuery('.side-menu-setting')
jQuery('#side-menu-save').on('click', (event) => {
event.preventDefault() event.preventDefault()
OC.msg.startSaving(selector) OC.msg.startSaving(selector)
saveSettings(0) saveSettings(0)
}); });
jQuery('.side-menu-display').on('click', (event) => {
var target = jQuery(event.target)
jQuery('.side-menu-display').removeClass('is-active')
target.addClass('is-active')
jQuery('#side-menu-always-displayed').val(target.attr('data-alwaysdiplayed'))
jQuery('#side-menu-big-menu').val(target.attr('data-bigmenu'))
})
jQuery('.side-menu-setting-live').on('change', (event) => {
var target = jQuery(event.target)
var name = target.attr('name')
var value = target.val()
if (name === 'opener') {
var url = OC.generateUrl(`/apps/side_menu/img/${value}.svg`).replace('/index.php', '')
value = `url(${url})`;
}
if (name === 'icon-invert-filter' || name === 'icon-opacity') {
value/=100;
}
document.documentElement.style.setProperty('--side-menu-' + name, value)
})
jQuery('.side-menu-toggler').on('click', (event) => {
var target = jQuery(event.target)
var element = target.attr('data-target')
elementToggler(element)
})
}); });
+66
View File
@@ -0,0 +1,66 @@
"Custom menu": "Benutzerdefiniertes Menü"
"Enable the custom menu": "Aktiviere das Benutzerdefiniertes Menü"
"No": "Nein"
"Yes": "Ja"
"Menu": "Menü"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Verwende die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü ein- und auszublenden. Verwende <span class="keyboard-key">tab</span> zum Navigieren.'
"Top menu": "Hauptmenü"
"Apps that not must be moved in the side menu": "Apps, die nicht ins Seitenmenü verschoben werden müssen"
"If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet."
"Experimental": "Experimentell"
"Save": "Speichern"
"You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?"
"Buy me a coffee ☕": "Gib mir einen Kaffee ☕"
"Hidden": "Versteckt"
"Small": "Klein"
"Normal": "Normal"
"Big": "Groß"
"Colors": "Farben"
"Background color": "Hintergrundfarbe"
"Background color of current app": "Hintergrundfarbe der aktuellen App"
"Text color": "Textfarbe"
"Loader": "Ladestandanzeige"
"Icon": "Symbol"
"Same color": "Selbe Farbe"
"Opposite color": "Gegenfarbe"
"Transparent": "Transparent"
"Opaque": "Undurchsichtig"
"Opener": "Öffner"
"Default": "Standard"
"Default (dark)": "Standard (dunkel)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (dunkel)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (dunkel)"
"Before the logo": "Vor dem Logo"
"After the logo": "Nach dem Logo"
"Position": "Position"
"Show only the opener (hidden logo)": "Nur den Öffner anzeigen (verstecktes Logo)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)."
"Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über den Öffner bewegt wird (auf Touchscreens automatisch deaktiviert)."
"Display the big menu": "Großes Menü anzeigen"
"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel."
"Display the logo": "Logo anzeigen"
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit <code>großes Menü</code> kompatibel."
"Icons and texts": "Symbole und Texte"
"Loader enabled": "Loader aktiviert"
"Tips": "Tipps"
"Always displayed": "Wird immer angezeigt"
"The logo will be hidden when the menu is always displayed.": "Das Logo wird ausgeblendet, wenn das Menü immer angezeigt wird."
"This is the automatic behavior when the menu is always displayed.": "Dies ist das automatische Verhalten, wenn das Menü immer angezeigt wird."
"Not compatible with touch screens.": "Nicht kompatibel mit Touchscreens."
"Big menu": "Großes Menü"
"Live preview": "Live Vorschau"
"Open apps in new tab": "Öffne Apps in einem neuen Tab"
"Use the global setting": "Verwende die globale Einstellung"
"Use my selection": "Verwende meine Auswahl"
"Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste"
"Use the avatar instead of the logo": "Verwenden Sie den Avatar anstelle des Logos"
"You do not have permission to change the settings.": "Sie haben keine Berechtigung zum Ändern der Einstellungen."
"Force this configuration to users": "Erzwingen Sie diese Konfiguration für Benutzer"
"Export the configuration": "Exportieren Sie die Konfiguration"
"Purge the cache": "Leeren Sie den Cache"
"Show the link to settings": "Zeigen Sie den Link zu den Einstellungen an"
"The menu is enabled by default for users": "Das Menü ist standardmäßig für Benutzer aktiviert"
"Except when the configuration is forced.": "Außer wenn die Konfiguration erzwungen wird."
+66
View File
@@ -0,0 +1,66 @@
"Custom menu": "Menu personnalisé"
"Enable the custom menu": "Activer le menu personnalisé"
"No": "Non"
"Yes": "Oui"
"Menu": "Menu"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Utiliser le raccourcis clavier <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> pour ouvrir et fermer le menu latéral. Utiliser <span class="keyboard-key">tab</span> pour naviguer.'
"Top menu": "Menu supérieur"
"Apps that not must be moved in the side menu": "Les applications qui ne doivent pas être affichées dans le menu latéral"
"If there is no selection then the global configuration is applied.": "Si il n'y a aucune sélection alors la configuration globale sera appliquée"
"Experimental": "Expérimental"
"Save": "Sauvegarder"
"You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?"
"Buy me a coffee ☕": "Offrez moi un café ☕"
"Hidden": "Caché"
"Small": "Petit"
"Normal": "Normal"
"Big": "Gros"
"Colors": "Couleurs"
"Background color": "Couleur de fond"
"Background color of current app": "Couleur de fond de l'application en cours"
"Text color": "Couleur du texte"
"Loader": "Indicateur de chargement"
"Icon": "Icône"
"Same color": "Même couleur"
"Opposite color": "Couleur opposée"
"Transparent": "Transparent"
"Opaque": "Opaque"
"Opener": "Bouton d'ouverture"
"Default": "Par défaut"
"Default (dark)": "Par défaut (sombre)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (sombre)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (sombre)"
"Before the logo": "Avant le logo"
"After the logo": "Après le logo"
"Position": "Position"
"Show only the opener (hidden logo)": "Afficher uniquement le bouton d'ouverture (masquer le logo)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Ne pas afficher le menu latéral et le bouton d'ouverture s'il n'y a aucune application (exemple : page publiques)."
"Panel": "Panneau"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Ouvrir le menu au passage de la souris (automatiquement désactivé sur les écrans tactiles)"
"Display the big menu": "Afficher le menu large"
"The big menu is not compatible with AppOrder.": "Le menu large n'est pas compatible avec l'application AppOrder"
"Display the logo": "Afficher le logo"
"This feature is not compatible with the <code>big menu</code> display.": "Cette fonctionnalité n'est pas compatible avec l'affichage du menu large."
"Icons and texts": "Icônes et textes"
"Loader enabled": "Activation de l'indicateur de chargement"
"Tips": "Astuces"
"Always displayed": "Toujours affiché"
"The logo will be hidden when the menu is always displayed.": "Le logo sera masque si le menu est toujours affiché."
"This is the automatic behavior when the menu is always displayed.": "C'est le comportement automatique lorsque le menu est toujours affiché."
"Not compatible with touch screens.": "Incompatible avec les écrans tactiles."
"Big menu": "Menu large"
"Live preview": "Aperçu en direct"
"Open apps in new tab": "Ouvrir les applications dans un nouvel onglet"
"Use the global setting": "Utiliser la configuration globale"
"Use my selection": "Utiliser ma sélection"
"Show and hide the list of applications": "Afficher et masquer la liste des applications"
"Use the avatar instead of the logo": "Utiliser l'avatar à la place du logo"
"You do not have permission to change the settings.": "Vous n'avez pas la permission de changer les paramètres."
"Force this configuration to users": "Forcer cette configuration aux utilisateurs"
"Export the configuration": "Exporter la configuration"
"Purge the cache": "Purger le cache"
"Show the link to settings": "Afficher le lien vers les paramètres"
"The menu is enabled by default for users": "Le menu est activé par défaut pour les utilisateurs"
"Except when the configuration is forced.": "Sauf lorsque la configuration est forcée."
+105 -22
View File
@@ -1,33 +1,116 @@
:root { :root {
<?php foreach ($_['vars'] as $key => $value): ?> <?php foreach ($_['vars'] as $key => $value): ?>
<?php if ($key === 'opener'): ?> <?php if ($key === 'opener'): ?>
--side-menu-<?php echo $key ?>: url('<?php echo $_['assetsRoot'] ?>/img/<?php echo $value ?>.svg'); --side-menu-<?php echo $key ?>: url('<?php print_unescaped(image_path('side_menu', $value.'.svg')); ?>');
<?php else: ?> <?php else: ?>
--side-menu-<?php echo $key ?>: <?php echo $value ?>; --side-menu-<?php echo $key ?>: <?php echo $value ?>;
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
} }
<?php if ($_['display-logo'] === false): ?> <?php if (empty($_['top-menu-apps'])): ?>
.side-menu-logo { #appmenu {
display: none; display: none;
} }
.side-menu-header { #appmenu + nav {
height: 50px; display: none;
} }
<?php else: ?>
.side-menu-apps-list { .app-hidden {
height: calc(100vh - 60px); opacity: 0;
top: 60px; }
} <?php endif; ?>
#side-menu.hide-opener .side-menu-header { <?php if ($_['opener-only']): ?>
display: none; #nextcloud {
} display: none;
}
#side-menu.hide-opener .side-menu-apps-list { <?php endif; ?>
height: calc(100vh);
top: 0; <?php if (!$_['display-logo']): ?>
} .side-menu-logo {
display: none;
}
.side-menu-header {
height: 50px;
}
.side-menu-apps-list {
height: calc(100vh - 49px);
top: 49px;
}
#side-menu.hide-opener .side-menu-header {
visibility: hidden;
}
<?php if ($_['size-text'] === 'hidden'): ?>
#side-menu, .side-menu-apps-list {
<?php if ($_['size-icon'] === 'big'): ?>
width: 55px;
<?php else: ?>
width: 52px;
<?php endif; ?>
}
#side-menu .side-menu-opener {
<?php if ($_['size-icon'] === 'big'): ?>
margin-left: 1px;
<?php else: ?>
margin-left: 0px;
<?php endif; ?>
}
<?php endif; ?>
<?php endif; ?>
<?php if ($_['size-icon'] === 'hidden'): ?>
.side-menu-app-icon {
display: none;
}
<?php elseif ($_['size-icon'] === 'small'): ?>
.side-menu-app-icon svg {
width: 15px;
height: 15px;
}
img.side-menu-app-icon {
width: 15px;
height: 15px;
}
<?php elseif ($_['size-icon'] === 'normal'): ?>
.side-menu-app-icon svg {
width: 20px;
height: 20px;
}
img.side-menu-app-icon {
width: 20px;
height: 20px;
}
<?php elseif ($_['size-icon'] === 'big'): ?>
.side-menu-app-icon svg {
width: 23px;
height: 23px;
}
img.side-menu-app-icon {
width: 23px;
height: 23px;
}
<?php endif; ?>
<?php if ($_['size-text'] === 'hidden'): ?>
.side-menu-app-text {
display: none;
}
<?php elseif ($_['size-text'] === 'small'): ?>
.side-menu-app-text {
font-size: 12px;
}
<?php elseif ($_['size-text'] === 'big'): ?>
.side-menu-app-text {
font-size: 16px;
}
<?php endif; ?> <?php endif; ?>
+74
View File
@@ -0,0 +1,74 @@
var alwaysDisplayed = function() {
var elements = document.querySelectorAll('*');
var fixedElements = []
for (var i in elements) {
var element = elements[i]
if (typeof element !== 'object') {
continue
}
var position = window.getComputedStyle(element, null).getPropertyValue('position');
if (position !== 'fixed') {
continue
}
var id = element.getAttribute('id')
if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') {
continue
}
if (element.classList.contains('oc-dialog')) {
continue
}
if (jQuery(element).parents('#side-menu').length) {
continue
}
fixedElements.push(element)
}
for (var i in fixedElements) {
var element = fixedElements[i]
var computedStyle = window.getComputedStyle(element, null)
var left = computedStyle.getPropertyValue('left')
var right = computedStyle.getPropertyValue('right')
if (right !== '0px') {
var intValue = parseInt(left.replace('px', ''))
element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)')
}
}
}
let content = document.getElementById('content')
if (content && content.classList.contains('app-settings')) {
let loaded = false
const config = {
attributes: false,
childList: true,
subtree: true
}
const observer = new MutationObserver(() => {
if (loaded) {
return;
}
const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul')
if (element) {
loaded = true
alwaysDisplayed()
}
})
observer.observe(content, config)
} else {
window.setTimeout(alwaysDisplayed, 200)
}
+15
View File
@@ -0,0 +1,15 @@
var pageLoader = jQuery('<div id="side-menu-loader">')
var pageLoaderBar = jQuery('<div id="side-menu-loader-bar">')
body.append(pageLoader)
pageLoader.append(pageLoaderBar)
var pageLoaderValue = 0
$(window).on('beforeunload', function() {
setInterval(function() {
pageLoaderBar.width(pageLoaderValue.toString() + '%')
pageLoaderValue = Math.min(pageLoaderValue + .2, 100)
}, 25)
})
+172
View File
@@ -0,0 +1,172 @@
var menuCache = null
var updateTopMenu = function() {
var breakpointMobileWidth = 1024
var menu = jQuery('#appmenu')
var apps = menu.find('li')
var minAppsDesktop = 8
var usePercentualAppMenuLimit = 0.8
var isMobile = jQuery(window).width() < breakpointMobileWidth
var lastShownApp = null
var appShown = []
var moreApps = jQuery('#more-apps')
var navigation = jQuery('#navigation')
var navigationApps = jQuery('#apps ul')
var appCount = null
var currentMenuCache = menu.html() + menu.next().html()
if (currentMenuCache === menuCache) {
return
}
navigationApps.html('')
apps.each(function(i, app) {
var dataId = app.getAttribute('data-id')
if (dataId === null) {
return
}
if (topMenuApps.indexOf(dataId) === -1) {
app.classList.add('hidden')
app.classList.add('app-hidden')
} else {
app.classList.remove('hidden')
app.classList.add('app-external-site')
appShown.push(app)
navigationApps.append(app.outerHTML)
}
if (targetBlankApps.indexOf(dataId) !== -1) {
jQuery(app).children('a').attr('target', '_blank');
}
})
var rightHeaderWidth = jQuery('.header-right').outerWidth()
var headerWidth = jQuery('header').outerWidth()
var availableWidth = headerWidth - jQuery('#nextcloud').outerWidth()
- jQuery('#header .side-menu-opener').outerWidth()
- (rightHeaderWidth > 230 ? rightHeaderWidth : 230)
if (!isMobile) {
availableWidth = availableWidth * usePercentualAppMenuLimit
}
appCount = Math.floor(availableWidth / jQuery('#appmenu li').width())
if (isMobile && appCount > minAppsDesktop) {
appCount = minAppsDesktop
} else if (!isMobile && appCount < minAppsDesktop) {
appCount = minAppsDesktop
}
if (appCount === 0) {
menu.addClass('hidden')
}
menu.removeClass('hidden')
menu.css('opacity', 1)
if (appShown.length - 1 - appCount >= 1) {
appCount--
}
moreApps.find('a').removeClass('active')
var k = 0
var notInHeader = 0
var name
jQuery(appShown).each(function(i, app) {
app = jQuery(app)
name = app.data('id')
if (k < appCount && appCount > 0) {
app.removeClass('hidden')
lastShownApp = app
jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header')
} else {
app.addClass('hidden')
notInHeader++
jQuery('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header')
if (appCount > 0 && app.children('a').hasClass('active')) {
lastShownApp.addClass('hidden')
app.removeClass('hidden')
notInHeader++
jQuery('#apps li[data-id=' + name + '].app-external-site')
.removeClass('in-header')
.addClass('in-header')
}
}
k++
})
// Hack for https://github.com/nextcloud/server/blob/23b0b63c213f5b31eecae817ffd4a9e26f6624d0/core/src/components/MainMenu.js#L74-L96
menu.undelegate('li:not(#more-apps) > a', 'click')
menu.delegate('li:not(#more-apps) > a', 'click', function(e) {
var a = $(e.target)
if (!a.is('a')) {
a = a.closest('a')
}
if (a.attr('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey && a.parent('#more-apps').length === 0) {
a.find('svg').remove()
a.find('div').remove()
a.prepend(jQuery('<div/>').addClass(
OCA.Theming && OCA.Theming.inverted
? 'icon-loading-small'
: 'icon-loading-small-dark'
))
window.location.href = a.attr('href')
}
})
jQuery('#apps li.app-external-site').each(function(i, app) {
app = jQuery(app)
var appId = app.attr('data-id')
if (app.hasClass('in-header')) {
app.find('svg').find('defs').remove()
} else {
var svg = app.find('svg');
if (svg.find('defs').length > 0) {
return;
}
var defs = `
<defs>
<filter id="invertMenuMore-${appId}">
<feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix>
</filter>
</defs>`
svg.prepend(defs)
svg.find('image').attr('filter', `url(#invertMenuMore-${appId})`)
var html = svg.get(0).innerHTML.replace(/fecolormatrix/g, 'feColorMatrix');
svg.html(html)
}
})
if (notInHeader === 0) {
moreApps.hide()
navigation.hide()
} else {
moreApps.show()
}
menuCache = menu.html() + menu.next().html()
}
setInterval(updateTopMenu, 50)
+111 -24
View File
@@ -1,28 +1,67 @@
(function() { (function() {
var sideMenuContainer = $('<div id="side-menu-container">') var sideMenuContainer = jQuery('<div id="side-menu-container">')
var sideMenuOpener = $('<button class="side-menu-opener"></button>') var sideMenuOpener = jQuery('<button class="side-menu-opener"></button>')
var sideMenu = $('<div id="side-menu">') var sideMenu = jQuery('<div id="side-menu">')
var body = $('body') var body = jQuery('body')
var html = jQuery('html')
body.append(sideMenuContainer)
sideMenuContainer.append(sideMenu)
<?php if ($_['opener-position'] === 'before'): ?>
sideMenuOpener.insertBefore('#nextcloud')
<?php else: ?>
sideMenuOpener.insertAfter('#nextcloud')
<?php endif; ?>
var isTouchDevice = window.matchMedia("(pointer: coarse)").matches var isTouchDevice = window.matchMedia("(pointer: coarse)").matches
<?php if ($_['big-menu']): ?>
sideMenu.attr('data-bigmenu', '1')
<?php endif; ?>
var targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>;
body.on('side-menu.apps', function(e, apps) {
<?php if ($_['hide-when-no-apps']): ?>
sideMenu = jQuery('#side-menu')
if (apps.length === 0) {
sideMenu.removeClass('open')
sideMenu.addClass('hide')
sideMenuOpener.addClass('hide')
} else {
sideMenu.removeClass('hide')
sideMenuOpener.removeClass('hide')
}
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
if (apps.length === 0) {
html.removeClass('side-menu-always-displayed');
} else {
html.addClass('side-menu-always-displayed');
}
<?php endif; ?>
<?php else: ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
if (apps.length === 0) {
html.removeClass('side-menu-always-displayed');
} else {
html.addClass('side-menu-always-displayed');
}
<?php endif; ?>
<?php endif; ?>
})
body.on('side-menu.ready', function() { body.on('side-menu.ready', function() {
sideMenu = $('#side-menu') sideMenu = jQuery('#side-menu')
var headerMenuOpener = $('#header .side-menu-opener') var headerMenuOpener = jQuery('#header .side-menu-opener')
var sideMenuOpener = $('#side-menu .side-menu-opener') var sideMenuOpener = jQuery('#side-menu .side-menu-opener')
<?php if ($_['opener-hover']): ?> sideMenuFocus = function() {
var a = sideMenu.find('.side-menu-app.active a')
if (a.length === 0) {
a = sideMenu.find('.side-menu-app:first-child a')
}
if (a.length > 0) {
a.focus()
}
}
<?php if ($_['opener-hover'] || ($_['always-displayed'] && !$_['big-menu'])): ?>
var sideMenuMouseLeave = function() { var sideMenuMouseLeave = function() {
sideMenu sideMenu
.removeClass('open') .removeClass('open')
@@ -37,21 +76,69 @@
sideMenu sideMenu
.addClass('open') .addClass('open')
.on('mouseenter', sideMenuMouseEnter) .on('mouseenter', sideMenuMouseEnter)
sideMenuFocus()
} }
if (!isTouchDevice) { if (!isTouchDevice) {
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter) <?php if ($_['opener-hover']): ?>
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter)
sideMenu.addClass('hide-opener') sideMenu.addClass('hide-opener')
<?php endif ?>
sideMenu.on('mouseleave', sideMenuMouseLeave)
sideMenu.on('mouseenter', sideMenuOpenerMouseEnter)
} }
<?php endif; ?> <?php endif; ?>
headerMenuOpener.on('click', function() { headerMenuOpener.on('click', function() {
sideMenu.addClass('open'); sideMenu.addClass('open')
sideMenu.find('.side-menu-app.active a').focus()
}) })
sideMenuOpener.on('click', function() { <?php if ($_['always-displayed'] && !$_['big-menu']): ?>
sideMenu.removeClass('open'); sideMenuOpener.on('click', function() {
sideMenu.toggleClass('open')
})
<?php else: ?>
sideMenuOpener.on('click', function() {
sideMenu.removeClass('open')
})
<?php endif; ?>
jQuery(document).keydown(function(e) {
var key = e.key || e.keyCode
if ((key === 'o' || key === 79) && e.ctrlKey === true) {
e.preventDefault()
sideMenu.toggleClass('open')
sideMenuFocus()
}
}) })
}) })
body.append(sideMenuContainer)
sideMenuContainer.append(sideMenu)
<?php if ($_['loader-enabled'] === true): ?>
<?php require_once __DIR__.'/_loaderEnabled.js'; ?>
<?php endif; ?>
<?php if ($_['opener-position'] === 'before'): ?>
sideMenuOpener.insertBefore('#nextcloud')
<?php else: ?>
sideMenuOpener.insertAfter('#nextcloud')
<?php endif; ?>
<?php if (!empty($_['top-menu-apps'])): ?>
var topMenuApps = <?php echo json_encode($_['top-menu-apps']); ?>;
<?php require_once __DIR__.'/_topMenuApps.js'; ?>
<?php endif; ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
<?php require_once __DIR__.'/_alwaysDisplayed.js'; ?>
<?php endif; ?>
})(); })();
+410 -14
View File
@@ -16,23 +16,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
use OCP\IURLGenerator;
use OCP\IConfig;
use OCA\SideMenu\AppInfo\Application;
script('side_menu', 'admin'); script('side_menu', 'admin');
style('side_menu', 'admin'); style('side_menu', 'admin');
$urlGenerator = \OC::$server[IURLGenerator::class];
$cacheSize = floor(mb_strlen(\OC::$server[IConfig::class]->getAppValue(Application::APP_ID, 'cache-categories', ''), '8bit') / 1024);
$choicesYesNo = [ $choicesYesNo = [
'No' => '0', 'No' => '0',
'Yes' => '1', 'Yes' => '1',
]; ];
$choicesSizes = [
'Hidden' => 'hidden',
'Small' => 'small',
'Normal' => 'normal',
'Big' => 'big',
];
?> ?>
<div id="side-menu-section"> <div id="side-menu-section">
<div class="section"> <div class="section">
<h2> <h2>
<?php p($l->t('Colors')); ?> <?php p($l->t('Colors')); ?>
<small><span class="info"><?php p($l->t('Live preview')); ?></span></small>
</h2> </h2>
<div> <div>
<label for="side-menu-background-color" class="settings-hint"> <label for="side-menu-background-color">
<?php p($l->t('Background color')); ?> <?php p($l->t('Background color')); ?>
</label> </label>
</div> </div>
@@ -41,11 +58,18 @@ $choicesYesNo = [
id="side-menu-background-color" id="side-menu-background-color"
name="background-color" name="background-color"
type="color" type="color"
class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['background-color']); ?>"> value="<?php print_unescaped($_['background-color']); ?>">
<input
id="side-menu-background-color-to"
name="background-color-to"
type="color"
class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['background-color-to']); ?>">
</div> </div>
<div> <div>
<label for="side-menu-current-app-background-color" class="settings-hint"> <label for="side-menu-current-app-background-color">
<?php p($l->t('Background color of current app')); ?> <?php p($l->t('Background color of current app')); ?>
</label> </label>
</div> </div>
@@ -54,11 +78,12 @@ $choicesYesNo = [
id="side-menu-current-app-background-color" id="side-menu-current-app-background-color"
name="current-app-background-color" name="current-app-background-color"
type="color" type="color"
class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['current-app-background-color']); ?>"> value="<?php print_unescaped($_['current-app-background-color']); ?>">
</div> </div>
<div> <div>
<label for="side-menu-text-color" class="settings-hint"> <label for="side-menu-text-color">
<?php p($l->t('Text color')); ?> <?php p($l->t('Text color')); ?>
</label> </label>
</div> </div>
@@ -67,10 +92,69 @@ $choicesYesNo = [
id="side-menu-text-color" id="side-menu-text-color"
name="text-color" name="text-color"
type="color" type="color"
class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['text-color']); ?>"> value="<?php print_unescaped($_['text-color']); ?>">
</div> </div>
<div>
<label for="side-menu-text-color">
<?php p($l->t('Loader')); ?>
</label>
</div>
<div>
<input
id="side-menu-loader-color"
name="loader-color"
type="color"
class="side-menu-setting"
value="<?php print_unescaped($_['loader-color']); ?>">
</div>
<div>
<label for="side-menu-icon-invert-filter">
<?php p($l->t('Icon')); ?>
</label>
</div>
<div>
<em>
<?php p($l->t('Same color')); ?>
</em>
<input
type="range"
min="0" max="100"
id="side-menu-icon-invert-filter"
name="icon-invert-filter"
class="side-menu-setting side-menu-setting-live"
value="<?php echo $_['icon-invert-filter'] ?>"
>
<em>
<?php p($l->t('Opposite color')); ?>
</em>
</div>
<div>
<em>
<?php p($l->t('Transparent')); ?>
</em>
<input
type="range"
min="0" max="100"
id="side-menu-icon-opacity"
name="icon-opacity"
class="side-menu-setting side-menu-setting-live"
value="<?php echo $_['icon-opacity'] ?>"
>
<em>
<?php p($l->t('Opaque')); ?>
</em>
</div>
</div> </div>
<div class="section"> <div class="section">
<h2> <h2>
<?php p($l->t('Opener')); ?> <?php p($l->t('Opener')); ?>
@@ -79,18 +163,24 @@ $choicesYesNo = [
<?php <?php
$choices = [ $choices = [
'Default' => 'side-menu-opener', 'Default' => 'side-menu-opener',
'Default (dark)' => 'side-menu-opener-dark',
'Hamburger' => 'side-menu-opener-hamburger', 'Hamburger' => 'side-menu-opener-hamburger',
'Hamburger (dark)' => 'side-menu-opener-hamburger-dark',
'Hamburger 2' => 'side-menu-opener-hamburger-2',
'Hamburger 2 (dark)' => 'side-menu-opener-hamburger-2-dark',
]; ];
?> ?>
<div> <div>
<label for="side-menu-opener" class="settings-hint"> <label for="side-menu-opener">
<?php p($l->t('Icon')); ?> <?php p($l->t('Icon')); ?>
<small><span class="info"><?php p($l->t('Live preview')); ?></span></small>
</label> </label>
</div> </div>
<div> <div>
<select id="side-menu-opener" name="opener"> <select id="side-menu-opener" name="opener" class="side-menu-setting side-menu-setting-live">
<?php foreach ($choices as $label => $value): ?> <?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['opener']): ?>selected<?php endif; ?>> <option value="<?php echo $value ?>" <?php if ($value === $_['opener']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
@@ -107,13 +197,13 @@ $choicesYesNo = [
?> ?>
<div> <div>
<label for="side-menu-opener-position" class="settings-hint"> <label for="side-menu-opener-position">
<?php p($l->t('Position')); ?> <?php p($l->t('Position')); ?>
</label> </label>
</div> </div>
<div> <div>
<select id="side-menu-opener-position" name="opener-position"> <select id="side-menu-opener-position" name="opener-position" class="side-menu-setting">
<?php foreach ($choices as $label => $value): ?> <?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['opener-position']): ?>selected<?php endif; ?>> <option value="<?php echo $value ?>" <?php if ($value === $_['opener-position']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
@@ -121,6 +211,38 @@ $choicesYesNo = [
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div>
<label for="side-menu-opener-only">
<?php p($l->t('Show only the opener (hidden logo)')); ?>
</label>
</div>
<div>
<select id="side-menu-opener-only" name="opener-only" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['opener-only']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-hide-when-no-apps">
<?php p($l->t('Do not display the side menu and the opener if there is no application (eg: public pages).')); ?>
</label>
</div>
<div>
<select id="side-menu-hide-when-no-apps" name="hide-when-no-apps" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['hide-when-no-apps']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div> </div>
<div class="section"> <div class="section">
@@ -129,13 +251,77 @@ $choicesYesNo = [
</h2> </h2>
<div> <div>
<label for="side-menu-opener" class="settings-hint"> <label>
<?php p($l->t('Default')); ?>
</label>
</div>
<?php
$displays = [
'default' => !$_['always-displayed'] && !$_['big-menu'],
'always-displayed' => $_['always-displayed'] && !$_['big-menu'],
'big-menu' => $_['big-menu'],
];
?>
<p>
<img
class="side-menu-display <?php echo $displays['default'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-default.svg')); ?>" alt="<?php p($l->t('Default')); ?>">
</p>
<div>
<label for="side-menu-opener">
<?php p($l->t('Big menu')); ?>
</label>
</div>
<p><em><?php p($l->t('The big menu is not compatible with AppOrder.')); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['big-menu'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="0"
data-bigmenu="1"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-big-menu.svg')); ?>" alt="<?php p($l->t('Big menu')); ?>">
</p>
<br>
<div>
<label for="side-menu-opener">
<?php p($l->t('Always displayed')); ?>
<small><span class="warning"><?php p($l->t('Experimental')); ?></span></small>
</label>
</div>
<p><em><?php p($l->t('Not compatible with touch screens.')); ?></em></p>
<p>
<img
class="side-menu-display <?php echo $displays['always-displayed'] ? 'is-active' : '' ?>"
data-alwaysdiplayed="1"
data-bigmenu="0"
src="<?php print_unescaped(image_path('side_menu', 'admin/layout-always-displayed.svg')); ?>" alt="<?php p($l->t('Always displayed')); ?>">
</p>
<input type="hidden" class="side-menu-setting" name="always-displayed" id="side-menu-always-displayed" value="<?php echo (int) $_['always-displayed'] ?>">
<input type="hidden" class="side-menu-setting" name="big-menu" id="side-menu-big-menu" value="<?php echo (int) $_['big-menu'] ?>">
<br>
<div>
<label for="side-menu-opener">
<?php p($l->t('Open the menu when the mouse is hover the opener (automatically disabled on touch screens)')); ?> <?php p($l->t('Open the menu when the mouse is hover the opener (automatically disabled on touch screens)')); ?>
</label> </label>
</div> </div>
<p><em><?php p($l->t('This is the automatic behavior when the menu is always displayed.')); ?></em></p>
<div> <div>
<select id="side-menu-opener-hover" name="opener-hover"> <select id="side-menu-opener-hover" name="opener-hover" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?> <?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['opener-hover']): ?>selected<?php endif; ?>> <option value="<?php echo $value ?>" <?php if ($value === $_['opener-hover']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
@@ -145,13 +331,16 @@ $choicesYesNo = [
</div> </div>
<div> <div>
<label for="side-menu-opener" class="settings-hint"> <label for="side-menu-opener">
<?php p($l->t('Display the logo')); ?> <?php p($l->t('Display the logo')); ?>
</label> </label>
</div> </div>
<p><em><?php echo $l->t('This feature is not compatible with the <code>big menu</code> display.'); ?></em></p>
<p><em><?php p($l->t('The logo will be hidden when the menu is always displayed.')); ?></em></p>
<div> <div>
<select id="side-menu-display-logo" name="display-logo"> <select id="side-menu-display-logo" name="display-logo" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?> <?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['display-logo']): ?>selected<?php endif; ?>> <option value="<?php echo $value ?>" <?php if ($value === $_['display-logo']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
@@ -159,13 +348,220 @@ $choicesYesNo = [
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<p><em><?php p($l->t('Use the avatar instead of the logo')); ?></em></p>
<div>
<select id="side-menu-use-avatar" name="use-avatar" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['use-avatar']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-show-settings">
<?php p($l->t('Show the link to settings')); ?>
</label>
</div>
<div>
<select id="side-menu-show-settings" name="show-settings" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['show-settings']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label>
<?php p($l->t('Icons and texts')); ?>
</label>
</div>
<div>
<select id="side-menu-size-icon" name="size-icon" class="side-menu-setting">
<?php foreach ($choicesSizes as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['size-icon']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> icon
</option>
<?php endforeach; ?>
</select>
<select id="side-menu-size-text" name="size-text" class="side-menu-setting">
<?php foreach ($choicesSizes as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['size-text']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> text
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-target-blank">
<?php p($l->t('Open apps in new tab')); ?>
</label>
</div>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div>
<?php endforeach; ?>
</div>
<br>
<div>
<label for="side-menu-loader-enabled">
<?php p($l->t('Loader enabled')); ?>
</label>
</div>
<div>
<select id="side-menu-loader-enabled" name="loader-enabled" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['loader-enabled']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div> </div>
<div class="section"> <div class="section">
<input type="hidden" id="side-menu-cache" name="cache" value="<?php print_unescaped($_['cache']); ?>"> <h2>
<?php p($l->t('Top menu')); ?>
</h2>
<div>
<label for="side-menu-top-menu-apps">
<?php p($l->t('Apps that not must be moved in the side menu')); ?>
</label>
</div>
<button id="side-menu-save" class="btn btn-primary"><?php p($l->t('Save')); ?></button> <p>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" id="top-menu-apps" data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="top-menu-apps[]"
value="<?php echo $app['id'] ?>"
id="top-menu-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['top-menu-apps'])): ?>checked<?php endif; ?>
/>
<label for="top-menu-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="section">
<h2>
<?php p($l->t('Tips')); ?>
</h2>
<p>
<em><?php echo $l->t('Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.'); ?></em>
</p>
</div>
<div class="section">
<div>
<label for="side-menu-default-enabled">
<?php p($l->t('The menu is enabled by default for users')); ?>
</label>
<p><em><?php p($l->t('Except when the configuration is forced.')); ?></em></p>
<select id="side-menu-default-enabled" name="default-enabled" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['default-enabled']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-opener-only">
<?php p($l->t('Force this configuration to users')); ?>
</label>
</div>
<div>
<select id="side-menu-force" name="force" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['force']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<input type="hidden" id="side-menu-cache" name="cache" value="<?php print_unescaped($_['cache']); ?>" class="side-menu-setting">
<button id="side-menu-save" class="btn btn-info">
<?php p($l->t('Save')); ?>
</button>
<span id="side-menu-message" class="msg"></span> <span id="side-menu-message" class="msg"></span>
</div> </div>
<div class="section" id="more">
<a href="<?php echo $urlGenerator->linkToRoute('side_menu.AdminSetting.exportConfiguration') ?>" target="_blank">
<button class="btn btn-primary" >
<?php p($l->t('Export the configuration')); ?>
</button>
</a>
<a href="<?php echo $urlGenerator->linkToRoute('side_menu.AdminSetting.removeCache') ?>">
<button class="btn btn-primary" >
<?php p($l->t('Purge the cache')); ?> (<?php echo $cacheSize ?> Kb)
</button>
</a>
<div style="height: 30px"></div>
<div>
<span for="side-menu-opener">
<?php p($l->t('You like this app and you want to support me?')); ?>
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button>
<?php p($l->t('Buy me a coffee ☕')); ?>
</button>
</a>
</span>
</div>
</div>
</div> </div>
+178
View File
@@ -0,0 +1,178 @@
<?php
/**
* @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 <http://www.gnu.org/licenses/>.
*/
script('side_menu', 'admin');
style('side_menu', 'admin');
$choicesYesNo = [
'No' => '0',
'Yes' => '1',
];
?>
<div id="side-menu-section">
<?php if ($_['force']): ?>
<div class="section">
<h2>
<?php p($l->t('Menu')); ?>
</h2>
<p>
<em><?php echo $l->t('You do not have permission to change the settings.'); ?></em>
</p>
</div>
<?php else: ?>
<div class="section">
<h2>
<?php p($l->t('Menu')); ?>
</h2>
<div>
<label for="side-menu-enabled">
<?php p($l->t('Enable the custom menu')); ?>
</label>
</div>
<p>
<em><?php echo $l->t('Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.'); ?></em>
</p>
<div>
<select id="side-menu-enabled" name="enabled" class="side-menu-setting" data-personal>
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['enabled']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-target-blank">
<?php p($l->t('Open apps in new tab')); ?>
</label>
</div>
<div>
<?php $choices = [
'Use the global setting' => '1',
'Use my selection' => '2',
]; ?>
<select id="side-menu-loader-enabled" name="target-blank-mode" class="side-menu-setting" data-personal>
<?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['target-blank-mode']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-personal data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="section">
<h2>
<?php p($l->t('Top menu')); ?>
</h2>
<div>
<label for="side-menu-top-menu-apps">
<?php p($l->t('Apps that not must be moved in the side menu')); ?>
</label>
</div>
<p>
<em>
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
</em>
</p>
<p>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal id="top-menu-apps" style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="top-menu-apps[]"
value="<?php echo $app['id'] ?>"
id="top-menu-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['top-menu-apps'])): ?>checked<?php endif; ?>
/>
<label for="top-menu-app-<?php echo $app['id'] ?>">
<?php echo $app['name'] ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif ?>
<div class="section">
<?php if (!$_['force']): ?>
<button id="side-menu-save" class="btn btn-primary"><?php p($l->t('Save')); ?></button>
<span id="side-menu-message" class="msg"></span>
<div style="height: 30px"></div>
<?php endif ?>
<div>
<span for="side-menu-opener">
<?php p($l->t('You like this app and you want to support me?')); ?>
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button>
<?php p($l->t('Buy me a coffee ☕')); ?>
</button>
</a>
</span>
</div>
</div>
</div>
+47 -47
View File
@@ -3,51 +3,51 @@ const { VueLoaderPlugin } = require('vue-loader')
const StyleLintPlugin = require('stylelint-webpack-plugin') const StyleLintPlugin = require('stylelint-webpack-plugin')
module.exports = { module.exports = {
entry: { entry: {
'admin': path.join(__dirname, 'src', 'admin.js'), 'admin': path.join(__dirname, 'src', 'admin.js'),
'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'), 'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'),
}, },
output: { output: {
path: path.resolve(__dirname, './js'), path: path.resolve(__dirname, './js'),
publicPath: '/js', publicPath: '/js',
filename: '[name].js?v=[hash]', filename: '[name].js?v=[hash]',
chunkFilename: 'chunks/[name]-[hash].js', chunkFilename: 'chunks/[name]-[hash].js',
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.css$/, test: /\.css$/,
use: ['vue-style-loader', 'css-loader'], use: ['vue-style-loader', 'css-loader'],
}, },
{ {
test: /\.scss$/, test: /\.scss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader'], use: ['vue-style-loader', 'css-loader', 'sass-loader'],
}, },
{ {
test: /\.vue$/, test: /\.vue$/,
loader: 'vue-loader', loader: 'vue-loader',
}, },
{ {
test: /\.js$/, test: /\.js$/,
loader: 'babel-loader', loader: 'babel-loader',
exclude: /node_modules/, exclude: /node_modules/,
}, },
{ {
test: /\.(png|jpg|gif|svg)$/, test: /\.(png|jpg|gif|svg)$/,
loader: 'url-loader', loader: 'url-loader',
options: { options: {
name: '[name].[ext]?[hash]', name: '[name].[ext]?[hash]',
limit: 8192, limit: 8192,
}, },
}, },
], ],
}, },
plugins: [ plugins: [
new VueLoaderPlugin(), new VueLoaderPlugin(),
new StyleLintPlugin(), new StyleLintPlugin(),
], ],
resolve: { resolve: {
extensions: ['*', '.js', '.vue'], extensions: ['*', '.js', '.vue'],
symlinks: false, symlinks: false,
}, },
} }