Compare commits

...

88 Commits

Author SHA1 Message Date
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
40 changed files with 1211 additions and 339 deletions
+2
View File
@@ -15,8 +15,10 @@
### Environment ### Environment
* Side menu version: ... * Side menu version: ...
* Nextcloud version: ...
* PHP version: ... * PHP version: ...
* Web server (Nginx, Apache2): ... * Web server (Nginx, Apache2): ...
* Web browser and version (Firefox 80, Google Chrome 74, etc): ...
### Steps to reproduce ### Steps to reproduce
+1 -1
View File
@@ -13,7 +13,7 @@ release: npm-build translations
test -d releases/$$VERSION && rm -fr releases/$$VERSION test -d releases/$$VERSION && rm -fr releases/$$VERSION
mkdir -p releases/$$VERSION/side_menu mkdir -p releases/$$VERSION/side_menu
cp -r README.md appinfo css lib img l10n js src templates releases/$$VERSION/side_menu cp -r README.md appinfo css lib img l10n js src templates screenshots releases/$$VERSION/side_menu
cd releases/$$VERSION cd releases/$$VERSION
zip -r side_menu_v$$VERSION.zip side_menu zip -r side_menu_v$$VERSION.zip side_menu
tar cvzf side_menu_v$$VERSION.tar.gz side_menu tar cvzf side_menu_v$$VERSION.tar.gz side_menu
+6 -19
View File
@@ -1,7 +1,7 @@
🤙 Nextcloud app / Side menu 🎨 🤙 Nextcloud app / Custom menu 🎨
=============================== ===============================
Side 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. Allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top.
You can also define apps that must be displayed in the top menu. Fully customisable. You can also 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. This application is rather suitable for instances that activate a lot of applications.
@@ -10,12 +10,14 @@ Comptatible with AppOrder.
* [Installation and upgrade](#installation-and-upgrade) * [Installation and upgrade](#installation-and-upgrade)
* [How to contribute?](#how-to-contribute) * [How to contribute?](#how-to-contribute)
* [Preview](#preview) * [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/)
You like this app and you want to support me? [Buy me a coffee](https://www.buymeacoffee.com/deblan) ☕
Installation and upgrade Installation and upgrade
------------------------ ------------------------
Side menu is availabe from the app store. 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 content into `apps`. Custom menu is available from the app store. 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 content into `apps`.
``` ```
$ cd /path/to/nextcloud/apps $ cd /path/to/nextcloud/apps
@@ -43,18 +45,3 @@ If you are a developer:
Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time). 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.
Previews
--------
![](https://upload.deblan.org/u/2020-03/5e81b219.jpg)
![](https://upload.deblan.org/u/2020-03/5e7fab2b.jpg)
![](https://upload.deblan.org/u/2020-08/5f3551d1.png)
![](https://upload.deblan.org/u/2020-08/5f3551f0.png)
![](https://upload.deblan.org/u/2020-08/5f35aae1.png)
![](https://upload.deblan.org/u/2020-08/5f35ab0f.png)
+25 -16
View File
@@ -1,9 +1,9 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<info> <info>
<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><![CDATA[Side 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. <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. 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. This application is rather suitable for instances that activate a lot of applications.
@@ -14,28 +14,37 @@ Comptatible with AppOrder.
You can report a bug or request a feature by opening an issue. 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> ]]></description>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="contact@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.8.6</version> <version>1.16.1</version>
<namespace>SideMenu</namespace> <namespace>SideMenu</namespace>
<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/5e81b219.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/5e7fab2b.jpg</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/admin_settings.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-08/5f3551d1.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/n19_big_menu.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-08/5f3551f0.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc18_menu_always_displayed.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-08/5f35aae1.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png</screenshot>
<screenshot>https://upload.deblan.org/u/2020-08/5f35ab0f.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot>
<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>
<dependencies> <dependencies>
<nextcloud min-version="18" max-version="20"/> <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\AdminSection</admin-section> <admin-section>OCA\SideMenu\Settings\AdminSection</admin-section>
<personal>OCA\SideMenu\Settings\Personal</personal> <personal>OCA\SideMenu\Settings\Personal</personal>
<personal-section>OCA\SideMenu\Settings\PersonalSection</personal-section> <personal-section>OCA\SideMenu\Settings\PersonalSection</personal-section>
</settings> </settings>
</info> </info>
+1
View File
@@ -21,6 +21,7 @@ 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' => 'Nav#items', 'url' => '/nav/items', 'verb' => 'GET'],
['name' => 'PersonalSetting#valueSet', 'url' => '/personalSetting/valueSet', 'verb' => 'POST'], ['name' => 'PersonalSetting#valueSet', 'url' => '/personalSetting/valueSet', 'verb' => 'POST'],
], ],
+26
View File
@@ -41,4 +41,30 @@
border-radius: 3px; 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;
}
+61 -2
View File
@@ -50,8 +50,8 @@
display: none; display: none;
} }
#side-menu.hide-opener .side-menu-opener, .side-menu-opener.hide { #side-menu.hide-opener .side-menu-opener, .side-menu-opener.hide, #side-menu.hide {
display: none; display: none !important;
} }
.side-menu-apps-list { .side-menu-apps-list {
@@ -175,11 +175,70 @@
color: var(--side-menu-text-color, #fff); 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 { .side-menu-big .side-menu-app-icon {
vertical-align: middle; vertical-align: middle;
margin-top: -2px; 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) { @media screen and (max-width: 1024px) {
#side-menu.side-menu-big { #side-menu.side-menu-big {
max-width: 290px; max-width: 290px;
+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

+16 -7
View File
@@ -6,6 +6,7 @@ use OC;
use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\User\User; use OC\User\User;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\IUserSession; use OCP\IUserSession;
use OCP\Util; use OCP\Util;
@@ -18,6 +19,9 @@ use Psr\Container\ContainerInterface;
*/ */
class Application extends App class Application extends App
{ {
public const APP_ID = 'side_menu';
public const APP_NAME = 'Custom menu';
/** /**
* @var OC\AllConfig * @var OC\AllConfig
*/ */
@@ -38,7 +42,7 @@ class Application extends App
*/ */
public function __construct(array $urlParams = []) public function __construct(array $urlParams = [])
{ {
parent::__construct('side_menu', $urlParams); parent::__construct(self::APP_ID, $urlParams);
$this->config = OC::$server->getConfig(); $this->config = OC::$server->getConfig();
$this->cspnm = OC::$server->getContentSecurityPolicyNonceManager(); $this->cspnm = OC::$server->getContentSecurityPolicyNonceManager();
@@ -51,9 +55,10 @@ class Application extends App
public function isEnabled(): bool public function isEnabled(): bool
{ {
$enabled = true; $enabled = true;
$isForced = (bool) $this->config->getAppValue(self::APP_ID, 'force', '0');
if (null !== $this->user) { if (null !== $this->user && !$isForced) {
$enabled = (bool) $this->config->getUserValue($this->user->getUid(), 'side_menu', 'enabled', '1'); $enabled = (bool) $this->config->getUserValue($this->user->getUid(), self::APP_ID, 'enabled', '1');
} }
return $enabled; return $enabled;
@@ -69,6 +74,10 @@ class Application extends App
$container->registerService('AppRepository', function (ContainerInterface $c) { $container->registerService('AppRepository', function (ContainerInterface $c) {
return new AppRepository(new OC_App()); return new AppRepository(new OC_App());
}); });
$container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config);
});
} }
/** /**
@@ -76,20 +85,20 @@ class Application extends App
*/ */
public function registerAssets() public function registerAssets()
{ {
Util::addScript('side_menu', 'sideMenu'); Util::addScript(self::APP_ID, 'sideMenu');
Util::addStyle('side_menu', 'sideMenu'); Util::addStyle(self::APP_ID, 'sideMenu');
$stylesheet = OC::$server->getURLGenerator()->linkToRoute( $stylesheet = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Css.stylesheet', 'side_menu.Css.stylesheet',
[ [
'v' => $this->config->getAppValue('side_menu', 'cache', '0'), 'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
] ]
); );
$script = OC::$server->getURLGenerator()->linkToRoute( $script = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Js.script', 'side_menu.Js.script',
[ [
'v' => $this->config->getAppValue('side_menu', 'cache', '0'), 'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
] ]
); );
+47 -39
View File
@@ -19,27 +19,32 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OC; 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\Response; use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; 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
*/ */
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;
} }
@@ -52,43 +57,46 @@ class CssController extends Controller
*/ */
public function stylesheet() public function stylesheet()
{ {
$backgroundColor = $this->config->getAppValue('side_menu', 'background-color', '#333333'); $response = new TemplateResponse(Application::APP_ID, 'css/stylesheet', $this->getConfig(), 'blank');
$backgroundColorTo = $this->config->getAppValue('side_menu', 'background-color-to', $backgroundColor);
$user = OC::$server[IUserSession::class]->getUser();
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true);
if ($user) {
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
}
$parameters = [
'vars' => [
'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('side_menu', 'loader-color', '#0e75ac'),
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'),
'icon-invert-filter' => abs((int) $this->config->getAppValue('side_menu', 'icon-invert-filter', '0')).'%',
'icon-opacity' => abs((int) $this->config->getAppValue('side_menu', 'icon-opacity', '100') / 100),
],
'display-logo' => (bool) $this->config->getAppValue('side_menu', 'display-logo', 1),
'opener-only' => (bool) $this->config->getAppValue('side_menu', 'opener-only', 0),
'external-sites-in-top-menu' => (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0),
'size-icon' => $this->config->getAppValue('side_menu', 'size-icon', 'normal'),
'size-text' => $this->config->getAppValue('side_menu', 'size-text', 'normal'),
'big-menu' => (bool) $this->config->getAppValue('side_menu', 'big-menu', '0'),
'top-menu-apps' => $topMenuApps,
];
$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,
];
}
} }
+81 -31
View File
@@ -19,27 +19,40 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OC; 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\Response; use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
class JsController extends Controller class JsController extends Controller
{ {
/** /**
* @var \OCP\IConfig * @var ConfigProxy
*/ */
protected $config; protected $config;
/** /**
* @param string $appName * @var User
*/ */
public function __construct($appName, IRequest $request, IConfig $config) protected $user;
/**
* @var ThemingDefaults
*/
protected $themingDefaults;
public function __construct(string $appName, IRequest $request, ConfigProxy $config, ThemingDefaults $themingDefaults)
{ {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->themingDefaults = $themingDefaults;
$this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config; $this->config = $config;
} }
@@ -47,36 +60,73 @@ class JsController extends Controller
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
* @PublicPage * @PublicPage
*
* @return Response
*/ */
public function script() public function script(): TemplateResponse
{ {
$user = OC::$server[IUserSession::class]->getUser(); $response = new TemplateResponse(Application::APP_ID, 'js/script', $this->getConfig(), 'blank');
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true);
if ($user) {
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
}
$parameters = [
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'),
'opener-hover' => (bool) $this->config->getAppValue('side_menu', 'opener-hover', '0'),
'external-sites-in-top-menu' => (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0),
'force-light-icon' => (bool) $this->config->getAppValue('side_menu', 'force-light-icon', '0'),
'hide-when-no-apps' => (bool) $this->config->getAppValue('side_menu', 'hide-when-no-apps', '0'),
'loader-enabled' => (bool) $this->config->getAppValue('side_menu', 'loader-enabled', '1'),
'big-menu' => (bool) $this->config->getAppValue('side_menu', 'big-menu', '0'),
'top-menu-apps' => $topMenuApps,
];
$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;
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),
]
);
}
}
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,
'logo' => $this->themingDefaults->getLogo(),
];
}
} }
+24 -11
View File
@@ -21,7 +21,9 @@ namespace OCA\SideMenu\Controller;
use OC; use OC;
use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator; use OC\URLGenerator;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig; use OCP\IConfig;
@@ -33,10 +35,15 @@ use OCP\L10N\IFactory;
class NavController extends Controller class NavController extends Controller
{ {
/** /**
* @var IConfig * @var ConfigProxy
*/ */
protected $config; protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/** /**
* @var AppRepository * @var AppRepository
*/ */
@@ -57,13 +64,11 @@ class NavController extends Controller
*/ */
protected $router; protected $router;
/**
* @param string $appName
*/
public function __construct( public function __construct(
$appName, string $appName,
IRequest $request, IRequest $request,
IConfig $config, IConfig $iConfig,
ConfigProxy $config,
AppRepository $appRepository, AppRepository $appRepository,
CategoryFetcher $categoryFetcher, CategoryFetcher $categoryFetcher,
URLGenerator $router, URLGenerator $router,
@@ -73,6 +78,7 @@ class NavController extends Controller
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->config = $config; $this->config = $config;
$this->iConfig = $iConfig;
$this->appRepository = $appRepository; $this->appRepository = $appRepository;
$this->categoryFetcher = $categoryFetcher; $this->categoryFetcher = $categoryFetcher;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
@@ -90,8 +96,7 @@ class NavController extends Controller
{ {
$apps = $this->appRepository->getVisibleApps(); $apps = $this->appRepository->getVisibleApps();
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2); $currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$categoriesLabels = $this->categoryFetcher->get(); $externalSitesInTopMenu = $this->config->getAppValueBool('external-sites-in-top-menu', '0');
$externalSitesInTopMenu = (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0);
$user = OC::$server[IUserSession::class]->getUser(); $user = OC::$server[IUserSession::class]->getUser();
$appsCategories = []; $appsCategories = [];
$categoriesAppsCount = []; $categoriesAppsCount = [];
@@ -103,8 +108,16 @@ class NavController extends Controller
]); ]);
} }
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true); $categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]');
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
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 (!empty($userTopMenuApps)) { if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps; $topMenuApps = $userTopMenuApps;
@@ -138,7 +151,7 @@ class NavController extends Controller
foreach ($categories as $category) { foreach ($categories as $category) {
if (!isset($items[$category])) { if (!isset($items[$category])) {
$items[$category] = [ $items[$category] = [
'name' => $categoriesLabels[$category], 'name' => $categoriesLabels[$category] ?? $category,
'apps' => [], 'apps' => [],
]; ];
} }
+39 -4
View File
@@ -23,24 +23,32 @@ use OCP\AppFramework\Http\Response;
use OCP\IConfig; use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
class PersonalSettingController extends Controller class PersonalSettingController extends Controller
{ {
/** /**
* @var \OCP\IConfig * @var IConfig
*/ */
protected $config; protected $config;
/**
* @var ConfigProxy
*/
protected $configProxy;
/** /**
* @var IUserSession * @var IUserSession
*/ */
private $userSession; protected $userSession;
public function __construct($appName, IRequest $request, IConfig $config, IUserSession $userSession) public function __construct($appName, IRequest $request, IConfig $config, ConfigProxy $configProxy, IUserSession $userSession)
{ {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->config = $config; $this->config = $config;
$this->configProxy = $configProxy;
$this->userSession = $userSession; $this->userSession = $userSession;
} }
@@ -65,6 +73,29 @@ class PersonalSettingController extends Controller
} }
} }
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) { if ('top-menu-apps' === $name) {
$doSave = true; $doSave = true;
$data = json_decode($value, true); $data = json_decode($value, true);
@@ -80,8 +111,12 @@ class PersonalSettingController extends Controller
} }
} }
if ($this->configProxy->getAppValueBool('force', '0')) {
$doSave = false;
}
if ($doSave) { if ($doSave) {
$this->config->setUserValue($user->getUid(), 'side_menu', $name, $value); $this->config->setUserValue($user->getUid(), Application::APP_ID, $name, $value);
return [ return [
'name' => $name, 'name' => $name,
+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);
}
}
+29 -24
View File
@@ -18,9 +18,10 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger; use OCP\ILogger;
use OCP\Settings\ISettings; use OCP\Settings\ISettings;
@@ -38,7 +39,7 @@ class Admin implements ISettings
private $logger; private $logger;
/** /**
* @var IConfig * @var ConfigProxy
*/ */
private $config; private $config;
@@ -47,7 +48,7 @@ class Admin implements ISettings
*/ */
private $appRepository; private $appRepository;
public function __construct(IL10N $l, ILogger $logger, IConfig $config, AppRepository $appRepository) public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, AppRepository $appRepository)
{ {
$this->l = $l; $this->l = $l;
$this->logger = $logger; $this->logger = $logger;
@@ -60,33 +61,37 @@ class Admin implements ISettings
*/ */
public function getForm() public function getForm()
{ {
$backgroundColor = $this->config->getAppValue('side_menu', 'background-color', '#333333'); $backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('side_menu', 'background-color-to', $backgroundColor); $backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$parameters = [ $parameters = [
'background-color' => $backgroundColor, 'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo, 'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'), 'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('side_menu', 'loader-color', '#0e75ac'), 'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'icon-invert-filter' => (int) $this->config->getAppValue('side_menu', 'icon-invert-filter', '0'), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'),
'icon-opacity' => (int) $this->config->getAppValue('side_menu', 'icon-opacity', '100'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue('side_menu', 'loader-enabled', '1'), 'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'), 'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'),
'cache' => $this->config->getAppValue('side_menu', 'cache', '0'), 'cache' => $this->config->getAppValue('cache', '0'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'), 'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'big-menu' => $this->config->getAppValue('side_menu', 'big-menu', '0'), 'always-displayed' => $this->config->getAppValue('always-displayed', '0'),
'display-logo' => $this->config->getAppValue('side_menu', 'display-logo', '1'), 'big-menu' => $this->config->getAppValue('big-menu', '0'),
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'), 'display-logo' => $this->config->getAppValue('display-logo', '1'),
'opener-hover' => $this->config->getAppValue('side_menu', 'opener-hover', '0'), 'use-avatar' => $this->config->getAppValue('use-avatar', '0'),
'opener-only' => $this->config->getAppValue('side_menu', 'opener-only', '0'), 'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'hide-when-no-apps' => $this->config->getAppValue('side_menu', 'hide-when-no-apps', '0'), 'opener-hover' => $this->config->getAppValue('opener-hover', '0'),
'size-icon' => $this->config->getAppValue('side_menu', 'size-icon', 'normal'), 'opener-only' => $this->config->getAppValue('opener-only', '0'),
'size-text' => $this->config->getAppValue('side_menu', 'size-text', 'normal'), 'hide-when-no-apps' => $this->config->getAppValue('hide-when-no-apps', '0'),
'top-menu-apps' => (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true), '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', '[]'),
'apps' => $this->appRepository->getVisibleApps(), 'apps' => $this->appRepository->getVisibleApps(),
]; ];
return new TemplateResponse('side_menu', 'settings/admin-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
} }
/** /**
@@ -94,7 +99,7 @@ class Admin implements ISettings
*/ */
public function getSection() public function getSection()
{ {
return 'side_menu'; return Application::APP_ID;
} }
/** /**
+4 -3
View File
@@ -21,6 +21,7 @@ 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 AdminSection implements IIconSection class AdminSection implements IIconSection
{ {
@@ -52,7 +53,7 @@ class AdminSection implements IIconSection
*/ */
public function getID() public function getID()
{ {
return 'side_menu'; return Application::APP_ID;
} }
/** /**
@@ -63,7 +64,7 @@ class AdminSection implements IIconSection
*/ */
public function getName() public function getName()
{ {
return $this->l->t('Side menu'); return $this->l->t(Application::APP_NAME);
} }
/** /**
@@ -83,6 +84,6 @@ class AdminSection 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');
} }
} }
+15 -11
View File
@@ -18,13 +18,14 @@
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\IConfig;
use OCP\IUserSession; use OCP\IUserSession;
use OCA\SideMenu\Service\AppRepository; use OCP\Settings\ISettings;
class Personal implements ISettings class Personal implements ISettings
{ {
@@ -39,7 +40,7 @@ class Personal implements ISettings
private $logger; private $logger;
/** /**
* @var IConfig * @var ConfigProxy
*/ */
private $config; private $config;
@@ -53,7 +54,7 @@ class Personal implements ISettings
*/ */
private $appRepository; private $appRepository;
public function __construct(IL10N $l, ILogger $logger, IConfig $config, IUserSession $userSession, AppRepository $appRepository) public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, IUserSession $userSession, AppRepository $appRepository)
{ {
$this->l = $l; $this->l = $l;
$this->logger = $logger; $this->logger = $logger;
@@ -70,12 +71,15 @@ class Personal implements ISettings
$user = $this->userSession->getUser(); $user = $this->userSession->getUser();
$parameters = [ $parameters = [
'enabled' => $this->config->getUserValue($user->getUid(), 'side_menu', 'enabled', '1'), 'force' => $this->config->getAppValueBool('force', '0'),
'top-menu-apps' => (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true), 'enabled' => $this->config->getUserValue($user, '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(), 'apps' => $this->appRepository->getVisibleApps(),
]; ];
return new TemplateResponse('side_menu', 'settings/personal-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, '');
} }
/** /**
@@ -83,13 +87,13 @@ class Personal 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
*/ */
+4 -3
View File
@@ -21,6 +21,7 @@ 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 PersonalSection implements IIconSection class PersonalSection implements IIconSection
{ {
@@ -52,7 +53,7 @@ class PersonalSection implements IIconSection
*/ */
public function getID() public function getID()
{ {
return 'side_menu'; return Application::APP_ID;
} }
/** /**
@@ -63,7 +64,7 @@ class PersonalSection implements IIconSection
*/ */
public function getName() public function getName()
{ {
return $this->l->t('Side menu'); return $this->l->t(Application::APP_NAME);
} }
/** /**
@@ -83,6 +84,6 @@ class PersonalSection 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');
} }
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 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

+22 -15
View File
@@ -19,14 +19,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="side-menu-header"> <div class="side-menu-header">
<button class="side-menu-opener"></button> <button class="side-menu-opener"></button>
<div v-if="logo" class="side-menu-logo"> <div v-if="!avatar && logo" v-bind:class="{'side-menu-logo': true, 'avatardiv': false}">
<img v-bind:src="logo"> <img v-bind:src="logo">
</div> </div>
<div v-if="avatar" v-bind:class="{'side-menu-logo': true, 'avatardiv': true}">
<img v-bind:src="avatar">
</div>
</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}"> <li v-for="app in apps" v-bind:class="{'side-menu-app': true, 'active': app.active}">
<a v-bind:href="app.href" v-bind:title="app.name"> <a v-bind:href="app.href" :target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined" v-bind:title="app.name">
<span class="side-menu-app-icon" v-html="app.icon"></span> <span class="side-menu-app-icon" v-html="app.icon"></span>
<span class="side-menu-app-text" v-html="app.name"></span> <span class="side-menu-app-text" v-html="app.name"></span>
</a> </a>
@@ -37,6 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script> <script>
import trim from 'trim' import trim from 'trim'
import axios from 'axios'
export default { export default {
name: 'SideMenu', name: 'SideMenu',
@@ -44,7 +48,9 @@ export default {
return { return {
apps: [], apps: [],
logo: null, logo: null,
avatar: null,
forceLightIcon: false, forceLightIcon: false,
targetBlankApps: [],
} }
}, },
methods: { methods: {
@@ -86,6 +92,7 @@ export default {
} }
this.apps.push({ this.apps.push({
id: dataId,
href: href, href: href,
name: trim(element.querySelector('span').innerHTML), name: trim(element.querySelector('span').innerHTML),
icon: svg, icon: svg,
@@ -100,25 +107,25 @@ export default {
}, 1000) }, 1000)
})(this.apps) })(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']
});
}, },
}, },
mounted() { mounted() {
this.retrieveConfig()
this.retrieveApps() this.retrieveApps()
this.retrieveLogo()
this.forceLightIcon = document.querySelector('#side-menu-container').getAttribute('data-forcelighticon') == 1;
const menu = document.querySelector('#appmenu') const menu = document.querySelector('#appmenu')
+37 -2
View File
@@ -18,17 +18,36 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div id="side-menu" class="side-menu-big"> <div id="side-menu" class="side-menu-big">
<div class="side-menu-header"> <div class="side-menu-header">
<button class="side-menu-opener side-menu-closer"></button> <button class="side-menu-opener side-menu-closer"></button>
<button class="side-menu-opener"></button> <button class="side-menu-opener"></button>
</div> </div>
<div class="side-menu-categories-wrapper"> <div class="side-menu-categories-wrapper">
<div class="side-menu-categories"> <div class="side-menu-categories">
<div v-if="!items.length" 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>
<div class="side-menu-category" v-for="category in items"> <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> <h2 class="side-menu-category-title" v-if="category.name != ''" v-html="category.name"></h2>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
<li v-bind:class="{'side-menu-app': true, 'active': activeApp === appId}" v-for="(app, appId) in category.apps"> <li v-bind:class="{'side-menu-app': true, 'active': activeApp === appId}" v-for="(app, appId) in category.apps">
<a v-bind:href="app.href" v-bind:title="app.name"> <a v-bind:href="app.href" :target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined" v-bind:title="app.name">
<img class="side-menu-app-icon" v-bind:src="app.icon"></span> <img class="side-menu-app-icon" v-bind:src="app.icon"></span>
<span class="side-menu-app-text" v-html="app.name"></span> <span class="side-menu-app-text" v-html="app.name"></span>
</a> </a>
@@ -50,6 +69,8 @@ export default {
return { return {
items: [], items: [],
activeApp: null, activeApp: null,
targetBlank: false,
targetBlankApps: [],
} }
}, },
methods: { methods: {
@@ -73,12 +94,26 @@ export default {
jQuery('body').trigger('side-menu.apps', [apps]) jQuery('body').trigger('side-menu.apps', [apps])
}); });
}, },
retrieveActiveApp() { retrieveActiveApp() {
let activeAppLink = document.querySelector('#appmenu a.active') let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null 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']
});
},
}, },
mounted() { mounted() {
this.retrieveConfig()
this.retrieveApps() this.retrieveApps()
this.retrieveActiveApp() this.retrieveActiveApp()
} }
+48 -9
View File
@@ -35,9 +35,9 @@ const saveSettings = (key) => {
let value let value
let name let name
if ($(element).is('[data-checkbox]')) { if (jQuery(element).is('[data-checkbox]')) {
name = $(element).attr('data-name') name = jQuery(element).attr('data-name')
const inputs = $('input[name="' + name + '[]"]:checked') const inputs = jQuery('input[name="' + name + '[]"]:checked')
value = [] value = []
inputs.each((i, v) => { inputs.each((i, v) => {
@@ -46,8 +46,8 @@ const saveSettings = (key) => {
value = JSON.stringify(value) value = JSON.stringify(value)
} else { } else {
name = $(element).attr('name') name = jQuery(element).attr('name')
value = $(element).val() value = jQuery(element).val()
} }
const size = elements.length const size = elements.length
@@ -74,20 +74,59 @@ const saveSettings = (key) => {
} }
} }
if ($(element).is('[data-personal]')) { if (jQuery(element).is('[data-personal]')) {
userConfig(name, value, callbacks) userConfig(name, value, callbacks)
} else { } else {
appConfig(name, value, callbacks) appConfig(name, value, callbacks)
} }
} }
$(document).ready(() => { const elementToggler = (element) => {
elements = $('.side-menu-setting') jQuery(element).toggle()
}
$('#side-menu-save').on('click', (event) => { 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)
})
}); });
+25 -12
View File
@@ -1,16 +1,16 @@
"Side menu": "Seitenmenü" "Custom menu": "Benutzerdefiniertes Menü"
"Enable the side menu": "Aktivieren Sie das Seitenmenü" "Enable the custom menu": "Aktiviere das Benutzerdefiniertes Menü"
"No": "Nein" "No": "Nein"
"Yes": "Ja" "Yes": "Ja"
"Menu": "Speisekarte" "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.': 'Verwenden Sie die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü zu öffnen und auszublenden. Verwenden Sie zum Navigieren die Registerkarte <span class="keyboard-key">tab</span>.' '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ü" "Top menu": "Hauptmenü"
"Apps that not must be moved in the side menu": "Apps, die nicht im Seitenmenü verschoben werden müssen" "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." "If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet."
"Experimental": "Experimental" "Experimental": "Experimentell"
"Save": "Sparen" "Save": "Speichern"
"You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?" "You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?"
"Buy me a coffe ☕": "Gib mir einen Kaffee ☕" "Buy me a coffee ☕": "Gib mir einen Kaffee ☕"
"Hidden": "Versteckt" "Hidden": "Versteckt"
"Small": "Klein" "Small": "Klein"
"Normal": "Normal" "Normal": "Normal"
@@ -36,13 +36,26 @@
"After the logo": "Nach dem Logo" "After the logo": "Nach dem Logo"
"Position": "Position" "Position": "Position"
"Show only the opener (hidden logo)": "Nur den Öffner anzeigen (verstecktes Logo)" "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).": "Zeigen Sie das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z. B. öffentliche Seiten)." "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" "Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffnen Sie das Menü, wenn Sie die Maus über den Öffner bewegen (auf Touchscreens automatisch deaktiviert)." "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" "Display the big menu": "Großes Menü anzeigen"
"The big menu is not compatible with AppOrder.": "Le menu large n'est pas compatible avec l'application AppOrder" "The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel."
"Display the logo": "Logo anzeigen" "Display the logo": "Logo anzeigen"
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit der großen Menüanzeige kompatibel." "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" "Icons and texts": "Symbole und Texte"
"Loader enabled": "Loader aktiviert" "Loader enabled": "Loader aktiviert"
"Tips": "Tipps" "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"
+16 -3
View File
@@ -1,5 +1,5 @@
"Side menu": "Menu latéral" "Custom menu": "Menu personnalisé"
"Enable the side menu": "Activer le menu latéral" "Enable the custom menu": "Activer le menu personnalisé"
"No": "Non" "No": "Non"
"Yes": "Oui" "Yes": "Oui"
"Menu": "Menu" "Menu": "Menu"
@@ -10,7 +10,7 @@
"Experimental": "Expérimental" "Experimental": "Expérimental"
"Save": "Sauvegarder" "Save": "Sauvegarder"
"You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?" "You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?"
"Buy me a coffe ☕": "Offrez moi un café ☕" "Buy me a coffee ☕": "Offrez moi un café ☕"
"Hidden": "Caché" "Hidden": "Caché"
"Small": "Petit" "Small": "Petit"
"Normal": "Normal" "Normal": "Normal"
@@ -46,3 +46,16 @@
"Icons and texts": "Icônes et textes" "Icons and texts": "Icônes et textes"
"Loader enabled": "Activation de l'indicateur de chargement" "Loader enabled": "Activation de l'indicateur de chargement"
"Tips": "Astuces" "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"
+2 -2
View File
@@ -22,13 +22,13 @@
} }
<?php endif; ?> <?php endif; ?>
<?php if ($_['opener-only'] === true): ?> <?php if ($_['opener-only']): ?>
#nextcloud { #nextcloud {
display: none; display: none;
} }
<?php endif; ?> <?php endif; ?>
<?php if ($_['display-logo'] === false): ?> <?php if (!$_['display-logo']): ?>
.side-menu-logo { .side-menu-logo {
display: none; display: none;
} }
+68
View File
@@ -0,0 +1,68 @@
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 value = window.getComputedStyle(element, null).getPropertyValue('left')
var intValue = parseInt(value.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)
}
+2 -2
View File
@@ -1,5 +1,5 @@
var pageLoader = $('<div id="side-menu-loader">') var pageLoader = jQuery('<div id="side-menu-loader">')
var pageLoaderBar = $('<div id="side-menu-loader-bar">') var pageLoaderBar = jQuery('<div id="side-menu-loader-bar">')
body.append(pageLoader) body.append(pageLoader)
pageLoader.append(pageLoaderBar) pageLoader.append(pageLoaderBar)
+21 -17
View File
@@ -2,16 +2,16 @@ var menuCache = null
var updateTopMenu = function() { var updateTopMenu = function() {
var breakpointMobileWidth = 1024 var breakpointMobileWidth = 1024
var menu = $('#appmenu') var menu = jQuery('#appmenu')
var apps = menu.find('li') var apps = menu.find('li')
var minAppsDesktop = 8 var minAppsDesktop = 8
var usePercentualAppMenuLimit = 0.8 var usePercentualAppMenuLimit = 0.8
var isMobile = $(window).width() < breakpointMobileWidth var isMobile = jQuery(window).width() < breakpointMobileWidth
var lastShownApp = null var lastShownApp = null
var appShown = [] var appShown = []
var moreApps = $('#more-apps') var moreApps = jQuery('#more-apps')
var navigation = $('#navigation') var navigation = jQuery('#navigation')
var navigationApps = $('#apps ul') var navigationApps = jQuery('#apps ul')
var appCount = null var appCount = null
var currentMenuCache = menu.html() + menu.next().html() var currentMenuCache = menu.html() + menu.next().html()
@@ -38,19 +38,23 @@ var updateTopMenu = function() {
appShown.push(app) appShown.push(app)
navigationApps.append(app.outerHTML) navigationApps.append(app.outerHTML)
} }
if (targetBlankApps.indexOf(dataId) !== -1) {
jQuery(app).children('a').attr('target', '_blank');
}
}) })
var rightHeaderWidth = $('.header-right').outerWidth() var rightHeaderWidth = jQuery('.header-right').outerWidth()
var headerWidth = $('header').outerWidth() var headerWidth = jQuery('header').outerWidth()
var availableWidth = headerWidth - $('#nextcloud').outerWidth() var availableWidth = headerWidth - jQuery('#nextcloud').outerWidth()
- $('#header .side-menu-opener').outerWidth() - jQuery('#header .side-menu-opener').outerWidth()
- (rightHeaderWidth > 230 ? rightHeaderWidth : 230) - (rightHeaderWidth > 230 ? rightHeaderWidth : 230)
if (!isMobile) { if (!isMobile) {
availableWidth = availableWidth * usePercentualAppMenuLimit availableWidth = availableWidth * usePercentualAppMenuLimit
} }
appCount = Math.floor(availableWidth / $('#appmenu li').width()) appCount = Math.floor(availableWidth / jQuery('#appmenu li').width())
if (isMobile && appCount > minAppsDesktop) { if (isMobile && appCount > minAppsDesktop) {
appCount = minAppsDesktop appCount = minAppsDesktop
@@ -75,27 +79,27 @@ var updateTopMenu = function() {
var notInHeader = 0 var notInHeader = 0
var name var name
$(appShown).each(function(i, app) { jQuery(appShown).each(function(i, app) {
app = $(app) app = jQuery(app)
name = app.data('id') name = app.data('id')
if (k < appCount && appCount > 0) { if (k < appCount && appCount > 0) {
app.removeClass('hidden') app.removeClass('hidden')
lastShownApp = app lastShownApp = app
$('#apps li[data-id=' + name + '].app-external-site').addClass('in-header') jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header')
} else { } else {
app.addClass('hidden') app.addClass('hidden')
notInHeader++ notInHeader++
$('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header') jQuery('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header')
if (appCount > 0 && app.children('a').hasClass('active')) { if (appCount > 0 && app.children('a').hasClass('active')) {
lastShownApp.addClass('hidden') lastShownApp.addClass('hidden')
app.removeClass('hidden') app.removeClass('hidden')
notInHeader++ notInHeader++
$('#apps li[data-id=' + name + '].app-external-site') jQuery('#apps li[data-id=' + name + '].app-external-site')
.removeClass('in-header') .removeClass('in-header')
.addClass('in-header') .addClass('in-header')
} }
@@ -104,8 +108,8 @@ var updateTopMenu = function() {
k++ k++
}) })
$('#apps li.app-external-site').each(function(i, app) { jQuery('#apps li.app-external-site').each(function(i, app) {
app = $(app) app = jQuery(app)
var appId = app.attr('data-id') var appId = app.attr('data-id')
if (app.hasClass('in-header')) { if (app.hasClass('in-header')) {
+50 -18
View File
@@ -1,36 +1,53 @@
(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')
var isTouchDevice = window.matchMedia("(pointer: coarse)").matches var isTouchDevice = window.matchMedia("(pointer: coarse)").matches
<?php if ($_['big-menu']): ?> <?php if ($_['big-menu']): ?>
sideMenu.attr('data-bigmenu', '1') sideMenu.attr('data-bigmenu', '1')
<?php endif; ?> <?php endif; ?>
<?php if ($_['force-light-icon']): ?> var targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>;
sideMenuContainer.attr('data-forcelighticon', '1')
<?php endif; ?>
body.on('side-menu.apps', function(e, apps) { body.on('side-menu.apps', function(e, apps) {
<?php if ($_['hide-when-no-apps']): ?> <?php if ($_['hide-when-no-apps']): ?>
sideMenu = $('#side-menu') sideMenu = jQuery('#side-menu')
if (apps.length === 0) { if (apps.length === 0) {
sideMenu.removeClass('open') sideMenu.removeClass('open')
sideMenu.addClass('hide')
sideMenuOpener.addClass('hide') sideMenuOpener.addClass('hide')
} else { } else {
sideMenu.removeClass('hide')
sideMenuOpener.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; ?> <?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')
sideMenuFocus = function() { sideMenuFocus = function() {
var a = sideMenu.find('.side-menu-app.active a') var a = sideMenu.find('.side-menu-app.active a')
@@ -44,7 +61,7 @@
} }
} }
<?php if ($_['opener-hover']): ?> <?php if ($_['opener-hover'] || ($_['always-displayed'] && !$_['big-menu'])): ?>
var sideMenuMouseLeave = function() { var sideMenuMouseLeave = function() {
sideMenu sideMenu
.removeClass('open') .removeClass('open')
@@ -64,9 +81,14 @@
} }
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; ?>
@@ -75,11 +97,17 @@
sideMenu.find('.side-menu-app.active a').focus() 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; ?>
$(document).keydown(function(e) { jQuery(document).keydown(function(e) {
var key = e.key || e.keyCode var key = e.key || e.keyCode
if ((key === 'o' || key === 79) && e.ctrlKey === true) { if ((key === 'o' || key === 79) && e.ctrlKey === true) {
@@ -109,4 +137,8 @@
<?php require_once __DIR__.'/_topMenuApps.js'; ?> <?php require_once __DIR__.'/_topMenuApps.js'; ?>
<?php endif; ?> <?php endif; ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
<?php require_once __DIR__.'/_alwaysDisplayed.js'; ?>
<?php endif; ?>
})(); })();
+137 -29
View File
@@ -37,6 +37,8 @@ $choicesSizes = [
<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>
@@ -49,13 +51,13 @@ $choicesSizes = [
id="side-menu-background-color" id="side-menu-background-color"
name="background-color" name="background-color"
type="color" type="color"
class="side-menu-setting" class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['background-color']); ?>"> value="<?php print_unescaped($_['background-color']); ?>">
<input <input
id="side-menu-background-color-to" id="side-menu-background-color-to"
name="background-color-to" name="background-color-to"
type="color" type="color"
class="side-menu-setting" class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['background-color-to']); ?>"> value="<?php print_unescaped($_['background-color-to']); ?>">
</div> </div>
@@ -69,7 +71,7 @@ $choicesSizes = [
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" 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>
@@ -83,7 +85,7 @@ $choicesSizes = [
id="side-menu-text-color" id="side-menu-text-color"
name="text-color" name="text-color"
type="color" type="color"
class="side-menu-setting" class="side-menu-setting side-menu-setting-live"
value="<?php print_unescaped($_['text-color']); ?>"> value="<?php print_unescaped($_['text-color']); ?>">
</div> </div>
@@ -117,7 +119,7 @@ $choicesSizes = [
min="0" max="100" min="0" max="100"
id="side-menu-icon-invert-filter" id="side-menu-icon-invert-filter"
name="icon-invert-filter" name="icon-invert-filter"
class="side-menu-setting" class="side-menu-setting side-menu-setting-live"
value="<?php echo $_['icon-invert-filter'] ?>" value="<?php echo $_['icon-invert-filter'] ?>"
> >
@@ -136,7 +138,7 @@ $choicesSizes = [
min="0" max="100" min="0" max="100"
id="side-menu-icon-opacity" id="side-menu-icon-opacity"
name="icon-opacity" name="icon-opacity"
class="side-menu-setting" class="side-menu-setting side-menu-setting-live"
value="<?php echo $_['icon-opacity'] ?>" value="<?php echo $_['icon-opacity'] ?>"
> >
@@ -165,11 +167,13 @@ $choicesSizes = [
<div> <div>
<label for="side-menu-opener"> <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" class="side-menu-setting"> <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); ?>
@@ -239,15 +243,75 @@ $choicesSizes = [
<?php p($l->t('Panel')); ?> <?php p($l->t('Panel')); ?>
</h2> </h2>
<div>
<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> <div>
<label for="side-menu-opener"> <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> <p><em><?php p($l->t('This is the automatic behavior when the menu is always displayed.')); ?></em></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>
<select id="side-menu-opener-hover" name="opener-hover" class="side-menu-setting"> <select id="side-menu-opener-hover" name="opener-hover" class="side-menu-setting">
@@ -260,37 +324,30 @@ $choicesSizes = [
</div> </div>
<div> <div>
<label for="side-menu-big-menu"> <label for="side-menu-opener">
<?php p($l->t('Display the big menu')); ?> <?php p($l->t('Display the logo')); ?>
<small><span class="warning"><?php p($l->t('Experimental')); ?></span></small>
</label> </label>
</div> </div>
<p><em><?php p($l->t('The big menu is not compatible with AppOrder.')); ?></em></p> <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-big-menu" name="big-menu" class="side-menu-setting"> <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 === $_['big-menu']): ?>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); ?>
</option> </option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div> <p><em><?php p($l->t('Use the avatar instead of the logo')); ?></em></p>
<label for="side-menu-opener">
<?php p($l->t('Display the logo')); ?>
</label>
</div>
<p><em><?php p($l->t('This feature is not compatible with the <code>big menu</code> display.')); ?></em></p>
<div> <div>
<select id="side-menu-display-logo" name="display-logo" class="side-menu-setting"> <select id="side-menu-use-avatar" name="use-avatar" 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 === $_['use-avatar']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?> <?php echo $l->t($label); ?>
</option> </option>
<?php endforeach; ?> <?php endforeach; ?>
@@ -321,6 +378,38 @@ $choicesSizes = [
</select> </select>
</div> </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> <div>
<label for="side-menu-loader-enabled"> <label for="side-menu-loader-enabled">
<?php p($l->t('Loader enabled')); ?> <?php p($l->t('Loader enabled')); ?>
@@ -345,11 +434,16 @@ $choicesSizes = [
<div> <div>
<label for="side-menu-top-menu-apps"> <label for="side-menu-top-menu-apps">
<?php p($l->t('Apps that not must be moved in the side menu')); ?> <?php p($l->t('Apps that not must be moved in the side menu')); ?>
<small><span class="warning">Experimental</span></small>
</label> </label>
</div> </div>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox> <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): ?> <?php foreach ($_['apps'] as $app): ?>
<div> <div>
<input <input
@@ -379,6 +473,20 @@ $choicesSizes = [
</div> </div>
<div class="section"> <div class="section">
<div>
<label for="side-menu-opener-only">
<?php p($l->t('Force this configuration to users')); ?>
</label>
<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"> <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-primary"><?php p($l->t('Save')); ?></button> <button id="side-menu-save" class="btn btn-primary"><?php p($l->t('Save')); ?></button>
@@ -393,7 +501,7 @@ $choicesSizes = [
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan"> <a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button> <button>
<?php p($l->t('Buy me a coffe ☕')); ?> <?php p($l->t('Buy me a coffee ☕')); ?>
</button> </button>
</a> </a>
</span> </span>
+126 -59
View File
@@ -27,75 +27,142 @@ $choicesYesNo = [
?> ?>
<div id="side-menu-section"> <div id="side-menu-section">
<div class="section"> <?php if ($_['force']): ?>
<h2> <div class="section">
<?php p($l->t('Menu')); ?> <h2>
</h2> <?php p($l->t('Menu')); ?>
</h2>
<div> <p>
<label for="side-menu-enabled"> <em><?php echo $l->t('You do not have permission to change the settings.'); ?></em>
<?php p($l->t('Enable the side menu')); ?> </p>
</label>
</div> </div>
<?php else: ?>
<div class="section">
<h2>
<?php p($l->t('Menu')); ?>
</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>
<select id="side-menu-enabled" name="enabled" class="side-menu-setting" data-personal> <div>
<?php foreach ($choicesYesNo as $label => $value): ?> <label for="side-menu-enabled">
<option value="<?php echo $value ?>" <?php if ($value === $_['enabled']): ?>selected<?php endif; ?>> <?php p($l->t('Enable the custom menu')); ?>
<?php echo $l->t($label); ?> </label>
</option> </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; ?> <?php endforeach; ?>
</select> </div>
</div> </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"> <div class="section">
<h2> <?php if (!$_['force']): ?>
<?php p($l->t('Top menu')); ?> <button id="side-menu-save" class="btn btn-primary"><?php p($l->t('Save')); ?></button>
</h2>
<div>
<label for="side-menu-top-menu-apps">
<?php p($l->t('Apps that not must be moved in the side menu')); ?>
<small><span class="warning"><?php p($l->t('Experimental')); ?></span></small>
</label>
</div>
<p> <span id="side-menu-message" class="msg"></span>
<em>
<?php p($l->t('If there is no selection then the global configuration is applied.')); ?>
</em>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal> <div style="height: 30px"></div>
<?php foreach ($_['apps'] as $app): ?> <?php endif ?>
<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>
<div class="section">
<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>
<div> <div>
<span for="side-menu-opener"> <span for="side-menu-opener">
@@ -103,7 +170,7 @@ $choicesYesNo = [
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan"> <a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button> <button>
<?php p($l->t('Buy me a coffe ☕')); ?> <?php p($l->t('Buy me a coffee ☕')); ?>
</button> </button>
</a> </a>
</span> </span>