Compare commits

..

102 Commits

Author SHA1 Message Date
Simon Vieille 70caa80af6 Merge pull request 'update makefile for assets build' (#75) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/75
2021-11-14 17:33:55 +01:00
Simon Vieille 48f403882a update makefile for assets build 2021-11-14 17:32:54 +01:00
Simon Vieille 6296c1c807 Merge pull request 'Release v2.0.0' (#74) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/74
2021-11-14 17:22:49 +01:00
Simon Vieille 71b9bc4766 release v2.0.0 2021-11-14 17:17:46 +01:00
Simon Vieille 228f07ea25 Merge branch 'feature/removing-jquery' into develop 2021-11-14 17:03:04 +01:00
Simon Vieille fd2ae59d16 remove background and border from opener (#73) 2021-11-14 16:44:52 +01:00
Simon Vieille 9124de3c81 fix the code of conduct 2021-11-06 20:11:53 +01:00
Simon Vieille 5e308b610d update the build task to include vendor/ 2021-11-05 20:20:42 +01:00
Simon Vieille db2220e745 replace the sortable jquery plugin with html5sortable 2021-11-05 20:19:08 +01:00
Simon Vieille 1143843a5b fix ci/cd conf 2021-11-03 23:02:57 +01:00
Simon Vieille 74d26106dc Merge pull request 'add ci/cd conf' (#72) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/72
2021-11-03 20:04:03 +01:00
Simon Vieille 539a81c07e add ci/cd conf 2021-11-03 20:01:54 +01:00
Simon Vieille d978da2c72 Merge branch 'develop' into feature/removing-jquery 2021-11-03 19:52:55 +01:00
Simon Vieille 544ed1a1fa add ci/cd conf (drone) 2021-11-03 19:52:24 +01:00
Simon Vieille ff5ba04217 Merge pull request 'add ci/cd conf (drone)' (#71) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/71
2021-11-03 19:20:36 +01:00
Simon Vieille 56e87d470d add ci/cd conf (drone) 2021-11-03 19:17:29 +01:00
Simon Vieille 9173ca37c7 add ci/cd conf (drone) 2021-11-02 20:48:54 +01:00
Simon Vieille 240b564e22 Merge pull request 'update documentation' (#70) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/70
2021-10-31 19:44:17 +01:00
Simon Vieille 11ccdceb98 update documentation 2021-10-31 19:43:25 +01:00
Simon Vieille 0608011d80 Merge pull request 'update metroline conf file' (#69) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/69
2021-10-31 18:45:36 +01:00
Simon Vieille 3d3da18322 update metroline conf file 2021-10-31 18:44:30 +01:00
Simon Vieille 3a999b22b0 Merge branch 'develop' into feature/removing-jquery 2021-10-31 17:48:06 +01:00
Simon Vieille 1544b28b57 Merge pull request 'add ci configuration' (#68) from develop into master
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/68
2021-10-31 17:47:47 +01:00
Simon Vieille e72d0555db update metroline conf file 2021-10-31 17:44:21 +01:00
Simon Vieille 7866089532 add metroline conf file 2021-10-31 17:42:22 +01:00
Simon Vieille ed2531db55 add fetch to make request 2021-10-31 17:27:08 +01:00
Simon Vieille c53221d112 fix indentation 2021-10-31 17:02:09 +01:00
Simon Vieille 9a95151156 remove console log 2021-10-31 16:56:52 +01:00
Simon Vieille 80369a39e1 remove jquery excepted for sorting categories 2021-10-31 16:55:43 +01:00
Simon Vieille 7471e93847 fix null setting update 2021-10-31 16:55:04 +01:00
Simon Vieille d1faf881da add timeout 2021-10-31 16:54:16 +01:00
Simon Vieille 96ffdf93ea remove useless vars 2021-10-29 15:30:22 +02:00
Simon Vieille 6f0c4e98e0 fix top menu generation; fix issue #66 by removing usage of setInterval 2021-10-29 13:56:48 +02:00
Simon Vieille c2e6d24b95 remove ';' on endline 2021-10-29 11:27:45 +02:00
Simon Vieille 5d93d02960 refactoring of alwaysDisplayed function 2021-10-29 11:24:01 +02:00
Simon Vieille a074d747c1 remove most of jquery from top menu 2021-10-29 11:14:27 +02:00
Simon Vieille cc7c9f6ed5 remove most of jquery 2021-10-28 15:47:03 +02:00
Simon Vieille f26adb6ac1 remove jquery from components 2021-10-28 13:50:16 +02:00
Simon Vieille 1e64b34cff Merge branch 'develop' 2021-10-28 13:17:04 +02:00
Simon Vieille 3b6b8ad54a add missing svg 2021-10-28 13:16:55 +02:00
Simon Vieille cc71fda3b0 Merge branch 'develop' 2021-10-21 21:43:51 +02:00
Simon Vieille 64c4d7a1e1 release v1.28.0 2021-10-21 21:43:30 +02:00
Simon Vieille fd367fe2d4 Merge branch 'develop' 2021-10-21 21:37:40 +02:00
Simon Vieille 3975f3be28 fix #63: add a new side menu with categories 2021-10-21 21:37:32 +02:00
Simon Vieille 3f590a97bc Merge branch 'develop' 2021-10-21 20:06:41 +02:00
Simon Vieille 1896bb399d fix #62: hide app notification icon 2021-10-21 20:06:17 +02:00
Simon Vieille d77c2ef9ae release v1.27.1 2021-08-11 14:43:52 +02:00
Simon Vieille db0fa60ce8 fix German translation render 2021-08-11 14:43:04 +02:00
Simon Vieille aac9f2cb16 Merge branch 'develop' 2021-08-11 11:58:25 +02:00
Simon Vieille 33d7c6a7f0 release v1.27.0 2021-08-11 11:58:15 +02:00
Simon Vieille a72bb87aa2 hide personal settings access when settings are forced by the administrator 2021-08-11 11:57:43 +02:00
Simon Vieille 5e4a7bcae4 improved German translations 2021-08-11 11:57:01 +02:00
Simon Vieille f7d4a8284b Merge branch 'develop' 2021-08-02 21:49:02 +02:00
Simon Vieille b900ed7a4d release v1.26.0 2021-08-02 21:48:58 +02:00
Simon Vieille 4a10338588 Merge pull request 'Add Czech localization' (#61) from Pavelb/side_menu:develop into develop
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/61
2021-08-02 21:35:34 +02:00
Pavelb 88ecd2ceaf Add Czech localization 2021-07-23 14:45:45 +02:00
Simon Vieille 04d161ad9b Merge branch 'develop' 2021-07-09 12:55:07 +02:00
Simon Vieille 1dcf1b7938 update changelog 2021-07-09 12:55:05 +02:00
Simon Vieille dab5566be4 Merge branch 'develop' 2021-07-09 12:54:32 +02:00
Simon Vieille 143ece3be8 update changelog 2021-07-09 12:54:28 +02:00
Simon Vieille d735e1cfa6 Merge branch 'develop' 2021-07-09 12:43:01 +02:00
Simon Vieille f86ec1de52 update changelog 2021-07-09 12:42:51 +02:00
Simon Vieille b780f53e0b add PHP version as dependency 2021-07-09 12:42:43 +02:00
Simon Vieille 96a4235a10 Merge branch 'develop' 2021-07-09 11:52:28 +02:00
Simon Vieille 86be38af6c add translations: zh_CN 2021-07-09 11:52:05 +02:00
Simon Vieille 4413623618 merge develop 2021-07-07 21:36:02 +02:00
Simon Vieille 7887a441b3 add compatibility with Nextcloud 22, add CHANGELOG.md, update app icon 2021-07-07 21:35:25 +02:00
Simon Vieille 44478af3a2 release v1.24.0 2021-07-03 20:33:42 +02:00
Simon Vieille 7306a31a7d release v1.24.0 2021-07-03 20:32:39 +02:00
Simon Vieille d2a8b05772 add background opacity (fix #58) 2021-07-03 20:23:39 +02:00
Simon Vieille fba980416d fix translations 2021-03-02 19:16:53 +01:00
Simon Vieille 95c840ae83 release v1.23.1 2021-03-02 19:16:44 +01:00
Simon Vieille f021c9f0cd fix issue with opener selector 2021-03-02 19:16:37 +01:00
Simon Vieille 58e03e903c release v1.23.0 2021-02-27 14:28:02 +01:00
Simon Vieille 9747820cb8 add dark mode colors 2021-02-27 14:21:55 +01:00
Simon Vieille d8d6aededa add compatibility with nextcloud 21 2021-02-24 15:16:29 +01:00
Simon Vieille a27e723657 Merge branch 'feature/issue55' into develop 2021-02-09 16:51:29 +01:00
Simon Vieille c7db75cea3 release v1.22.2 2021-02-09 16:51:27 +01:00
Simon Vieille 4cd5421e53 fix regression: apps does not open in the tab 2021-02-09 16:50:16 +01:00
Simon Vieille f2d1de1cbe release v1.22.1 2021-02-05 17:57:46 +01:00
Simon Vieille 6be679c569 fix regression: apps does not open in the tab 2021-02-05 17:54:50 +01:00
Simon Vieille 0893ddd5fe release v1.22.0 2021-02-03 15:23:07 +01:00
Simon Vieille c893391065 update ui of admin and fix #53 2021-02-03 15:21:08 +01:00
Simon Vieille b06df8ad9f update ui of admin lists 2021-02-03 10:27:08 +01:00
Simon Vieille 13f7e63514 PSR 2 compliance 2021-02-03 10:15:59 +01:00
Simon Vieille 64da45e676 add feature: order categories 2021-02-03 10:14:03 +01:00
Simon Vieille 77e27bcc1a add code of conduct and update chat room link 2020-12-23 13:39:43 +01:00
Simon Vieille 99858d86c1 release v1.21.0 2020-12-03 13:34:57 +01:00
Simon Vieille 6984efdafd fix issue #51: add link on the logo 2020-12-03 13:33:28 +01:00
Simon Vieille 48d80edb0a update issue template 2020-11-08 12:32:19 +01:00
Simon Vieille b57743c61a add donation link 2020-11-06 18:17:17 +01:00
Simon Vieille a58ca57e46 release v1.20.1 2020-11-03 13:00:26 +01:00
Simon Vieille dc6ec4eb89 fix typo and translations 2020-11-03 13:00:09 +01:00
Simon Vieille 8588f1510f release v1.20.0 2020-11-03 12:20:01 +01:00
Simon Vieille 02825cee67 add option to hide apps (big menu display) 2020-11-03 12:16:36 +01:00
Simon Vieille 10e90df4b8 update issue templates 2020-11-01 14:13:54 +01:00
Simon Vieille 99590447e5 update issue templates 2020-11-01 13:53:19 +01:00
Simon Vieille b5e41ff96e update issue templates 2020-11-01 13:49:04 +01:00
Simon Vieille 680587e75c add documentation (related to #48) 2020-10-24 14:29:32 +02:00
Simon Vieille 9f7d6c931e add a script to import config 2020-10-23 18:25:08 +02:00
Simon Vieille 205a72f74d add documentation 2020-10-23 18:24:48 +02:00
Simon Vieille ef00aae51f update issue template 2020-10-23 11:51:28 +02:00
45 changed files with 2772 additions and 916 deletions
+42
View File
@@ -0,0 +1,42 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: dependencies
image: deblan/devenv
commands:
- npm install
- name: build
image: deblan/devenv
commands:
- make npm-build
when:
branch:
- develop
- feature/*
- name: release
image: deblan/devenv
volumes:
- name: artifacts
path: /artifacts
environment:
APP_CERTIFICATE:
from_secret: app_certificate
commands:
- mkdir -p "$HOME/.nextcloud/certificates"
- echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key"
- export VERSION=$(grep "<version>" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never)
- export RELEASE_DIRECTORY="/artifacts/deblan/side_menu"
- make release
when:
branch:
- master
volumes:
- name: artifacts
host:
path: /var/www/html/artifacts
+16
View File
@@ -0,0 +1,16 @@
---
name: "New feature"
about: "Use this template if you want to request a feature"
title: "[FEATURE] "
labels:
- enhancement
---
## Feature
### Description
...
### Benefits
...
+32
View File
@@ -0,0 +1,32 @@
---
name: "New issue"
about: "Use this template if you have a bug"
title: "[BUG] "
labels:
- bug
---
## Issue
### Environment
* Custom menu version:
* Nextcloud version:
* PHP version:
* Web server (Nginx, Apache2):
* Web browser and version (Firefox 80, Google Chrome 74, etc):
```
Insert your configuration here. You can export the configuration using the admin page.
```
### Steps to reproduce
...
### Observed Results
...
### Expected Results
...
+7
View File
@@ -0,0 +1,7 @@
version: '1'
image: deblan/devenv
jobs:
build:
script:
- npm install
- make npm-build
+292
View File
@@ -0,0 +1,292 @@
## [Unreleased]
## 2.0.0
### Fixed
- fix #66: removing usage of setInterval
- fix #73: icon background
### Changed
- fix #67: replace jQuery with Vanilla JS
### Removed
- Nextcloud 18 is not supported anymore
## 1.28.0
### Added
- fix #63: add a new side menu with categories
## 1.27.2
### Fixed
- fix #62: hide app notification icon
## 1.27.1
### Fixed
- fix German translation render
## 1.27.0
### Added
- hide personal settings access when settings are forced by the administrator
### Fixed
- improve German translations
## 1.26.0
### Added
- add Czech translation
## 1.25.2
### Fixed
- fix CHANGELOG
## 1.25.1
### Added
- add PHP version as dependency
- add chinese translation
### Fixed
- fix CHANGELOG
## 1.25.0
### Added
- add compatibility with Nextcloud 22
- add CHANGELOG.md (fix #59)
- update app icon
## 1.24.0
### Added
- add option to define the background opacity (fix #53)
- add missing translations
## 1.23.1
### Fixed
- fix but wih dark mode opener option
## 1.23.0
### Added
- add support of dark Theme and Breeze Dark
- add support of Nextcloud 21
## 1.22.2
### Fixed
- fix regression: apps does not open in new tab (fix #55)
## 1.22.1
### Fixed
- fix regression: apps does not open in new tab
## 1.22.0
### Added
- Add option to sort categories (fix #53)
- Update admin UI
## 1.21.0
### Added
- [FEATURE] Logo in the menu links to main page of installation (#51)
## 1.20.1
### Fixed
- Fix translations
## 1.20.0
### Added
- [FEATURE] Ability to remove apps from the Big Menu (#49)
## 1.19.1
### Fixed
- fix #47: setting for list/grid view in files app flashes and dissapears
## 1.19.0
### Added
- add option: the menu is enabled by default for users (fix #46)
## 1.18.0
### Added
- add option to show link to settings (fix #44)
- refactor menus using several components
## 1.17.0
### Added
- compliance with the app checker
- add an action to export the configuration
- add an action to purge the cache
## 1.16.3
### Fixed
- fix links that must be opened in new window (https://help.nextcloud.com/t/external-petit-probleme-concernant-lapplication-external-sites-ou-sites-externes/94884/11)
## 1.16.2
### Fixed
- fix issue with personal settings when global settings are forced
## 1.16.1
### Fixed
- fix #42: add cache to manage failures to access apps.nextcloud.com
- fix #41: side menu was hover apps list
## 1.16.0
### Added
- add an option to force settings to users (fix #38)
## 1.15.0
### Changed
- New name
### Fixed
- Fix #36: always displayed is not expanding
## 1.14.0
### Added
- add an api accessed by components
- add a config proxy in controllers
### Fixed
- fix translations
## 1.13.0
### Added
- add an option to display the avatar instead of the logo (fix #34)
## 1.12.3
### Added
- add a delay before moving elements (fix #33)
## 1.12.2
### Fixed
- fix #30: `Always displayed` menu can not be close using touchscreens
## 1.12.1
### Fixed
- fix typo
- fix translations
## 1.12.0
### Fixed
- fix #30: administrators and users can select what apps must be opened in new tab
- fix typo
## 1.11.0
### Added
- add the option for opening apps in new tab (fix #29)
### Fixed
- fix issue with the header of the always displayed menu
## 1.10.0
### Added
- add images to select the display of the menu
- add live preview
- update translations
- update app info
## 1.9.3
### Fixed
- fix regression with logo display
## 1.9.2
### Added
- add the option “always displayed” which fixes the position of the menu to the left and always displays the application icons (fix #21, fix #2)
## 1.8.6
### Added
- add translations: `fr` and `de`
- improve and publish the `Makefile`
- update documentation
## 1.8.5
### Fixed
- fix #28: menu items invisible after 1.8.4
## 1.8.4
### Fixed
- fix #27: disable side menu on public pages - broken in 1.8.3
## 1.8.3
### Fixed
- fix alphabetic order of apps (#26)
## 1.8.2
### Added
- add icon for closing the big menu (fix #25)
### Fixed
- fix hidden icons in the top menu (fix #23)
- fix missing apps (fix #24)
## 1.8.1
### Fixed
- fix issue with links
- fix missing l10n files
## 1.8.0
### Added
- add a `big menu` display (fix #22)
- add the possibility to choose what apps are displayed in the top menu (fix #22)
- add icon color filter
- add icon opacity filter
## 1.8.0-rc2
## 1.7.0
### Added
- add a loader when the page is unloading
- add compatibility with Nextcloud 19
## 1.6.3
### Fixed
- fix #20: add a shortcut to open and to hide the menu
## 1.6.2
### Fixed
- fix #19: add a cache to limit flashes
## 1.6.1
### Fixed
- fix #19: add a hack to show external sites in the top menu with navigation
## 1.6.0
### Added
- Add a page of personal settings
- Add an option to disable the side menu as user
- Refactoring of javascripts
## 1.5.0
### Added
- add option to force light icons instead of dark icons
### Fixed
- fix #19: add option to keep external sites in the top menu
- fix #16 #17: add dark icons and handle the svg filters
## 1.4.1
### Added
- Rendering harmonization with browsers
### Fixed
- FIX #15: make menu start after icon
## 1.4.0
### Fixed
- Fix #12: add an option to hide the opener and the panel when there is no application
## 1.3.4
### Fixed
- FIX #14: add option to show only the opener
- FIX #13: add alternate hamburger icon
- FIX #11: remove error in the console
## 1.3.3
### Added
- Add a second background color to create a background gradiant
- Update documentation
### Fixed
- FIX #10: add options to select the size of the icons and the text, or hide them
## 1.3.2
### Fixed
- FIX #9: opener icon not visible with multiple apps_paths
## 1.3.1
### Fixed
- FIX #3: Add an opition the choose the position of the opener (after or before the logo)
- FIX #8: Remove extension of dynamic asset's routes (js, css)
## 1.3.0
### Added
- `main.js` is replaced by a controller and a template
### Fixed
- FIX #2: add option to open the menu by hovering over opener
## 1.2.4
### Fixed
- FIX #7: Opener icon not visible in Safari
+5
View File
@@ -0,0 +1,5 @@
# Contributor Code of Conduct
This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.
For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.
-35
View File
@@ -1,35 +0,0 @@
## Feature
### Description
...
### Benefits
...
---
## Issue
### Environment
* Side menu version: ...
* Nextcloud version: ...
* PHP version: ...
* Web server (Nginx, Apache2): ...
* Web browser and version (Firefox 80, Google Chrome 74, etc): ...
### Steps to reproduce
* ...
* ...
* ...
### Observed Results
...
### Expected Results
...
+8 -4
View File
@@ -11,10 +11,14 @@ release: npm-build translations
exit 1
fi
test -d releases/$$VERSION && rm -fr releases/$$VERSION
mkdir -p releases/$$VERSION/side_menu
cp -r README.md appinfo css lib img l10n js src templates screenshots releases/$$VERSION/side_menu
cd releases/$$VERSION
if [ -z "$$RELEASE_DIRECTORY" ]; then
RELEASE_DIRECTORY=releases
fi
test -d $$RELEASE_DIRECTORY/$$VERSION && rm -fr $$RELEASE_DIRECTORY/$$VERSION
mkdir -p $$RELEASE_DIRECTORY/$$VERSION/side_menu
cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots vendor $$RELEASE_DIRECTORY/$$VERSION/side_menu
cd $$RELEASE_DIRECTORY/$$VERSION
zip -r side_menu_v$$VERSION.zip side_menu
tar cvzf side_menu_v$$VERSION.tar.gz side_menu
rm -fr side_menu
+14 -2
View File
@@ -6,13 +6,20 @@ You can also define apps that must be displayed in the top menu. Fully customisa
This application is rather suitable for instances that activate a lot of applications.
Comptatible with AppOrder.
You can customize colors depending of the theme (Dark theme and Breeze Dark). Comptatible with AppOrder.
* [Installation and upgrade](#installation-and-upgrade)
* [How to contribute?](#how-to-contribute)
* [Support](#support)
* [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) ☕
You like this app and you want to support me? ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan) or [Donate with liberapay](https://liberapay.com/deblan)
Requirements
------------
* PHP >= 7.3
* App `theming` enabled
Installation and upgrade
------------------------
@@ -52,3 +59,8 @@ If you are a developer:
Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time).
Then commit and create a pull request.
Support
-------
You can join the official room on Matrix: [#custommenu:neutralnetwork.org](https://matrix.to/#/#custommenu:neutralnetwork.org).
+14 -7
View File
@@ -11,16 +11,22 @@ This application is rather suitable for instances that activate a lot of applica
Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate.
Comptatible with AppOrder.
You can customize colors depending of the theme (Dark theme and Breeze Dark). Comptatible with AppOrder.
You can report a bug or request a feature by opening an issue.
If you like this application and want to support the development:
Requirements:
* ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan)
* 😃 [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments)
* PHP >= 7.3
* App `theming` enabled
If you like this application and if you want to support the development:
* [Buy me a coffee](https://www.buymeacoffee.com/deblan)
* [Donate with liberapay](https://liberapay.com/deblan)
* [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments)
]]></description>
<version>1.19.1</version>
<version>2.0.0</version>
<licence>agpl</licence>
<author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author>
<namespace>SideMenu</namespace>
@@ -30,7 +36,7 @@ If you like this application and want to support the development:
</documentation>
<category>customization</category>
<website>https://gitnet.fr/deblan/side_menu</website>
<discussion>https://matrix.to/#/!GhfQePnOSPYpYEunWM:neutralnetwork.org</discussion>
<discussion>https://matrix.to/#/!TFPucDATKODpHNVAtu:neutralnetwork.org?via=neutralnetwork.org</discussion>
<bugs>https://gitnet.fr/deblan/side_menu/issues</bugs>
<repository type="git">https://gitnet.fr/deblan/side_menu</repository>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot>
@@ -40,7 +46,8 @@ If you like this application and want to support the development:
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot>
<dependencies>
<nextcloud min-version="18" max-version="20"/>
<nextcloud min-version="19" max-version="22"/>
<php min-version="7.3"/>
</dependencies>
<settings>
<admin>OCA\SideMenu\Settings\Admin</admin>
+6
View File
@@ -1,4 +1,10 @@
<?php
/**
* Generates l10n files using Yaml.
*
* Usage:
* php bin/generate_l10n.php
*/
function generateJsContent($translations)
{
+24
View File
@@ -0,0 +1,24 @@
<?php
/**
* Imports a json configuration into a sqlite database.
*
* Usage:
* php bin/import_config.php /path/to/config.json /path/to/owncloud.db
*/
$configFile = $argv[1];
$databaseFile = $argv[2];
$content = file_get_contents($configFile);
$config = json_decode($content, true);
$pdo = new \Pdo(sprintf('sqlite:%s', $databaseFile));
$stmt = $pdo->prepare('UPDATE oc_appconfig SET configvalue=:value WHERE configkey=:key and appid=:appId');
foreach ($config as $key => $value) {
$stmt->execute([
'appId' => 'side_menu',
'key' => $key,
'value' => $value,
]);
}
+58 -1
View File
@@ -20,7 +20,7 @@
margin: 10px 0 10px 0;
}
#side-menu-section input[type="checkbox"] {
#-dropside-menu-section input[type="checkbox"] {
vertical-align: middle;
}
@@ -68,3 +68,60 @@
.side-menu-toggler {
cursor: pointer;
}
.side-menu-setting-list {
margin: 10px 4px 4px 0px;
}
.side-menu-setting-list-item {
padding: 5px 10px;
border: 1px solid var(--color-border-dark);
max-width: 300px;
margin: -1px 0 0 0;
cursor: pointer;
}
.side-menu-setting-list-drop {
background: yellow;
border-color: yellow;
height: 34px;
}
.side-menu-setting.arrow {
color: #ccc;
padding-right: 5px;
}
.side-menu-setting-list-item input {
min-height: auto;
margin-top: -1px;
}
.side-menu-setting-table {
display: table;
width: 100%;
}
.side-menu-setting-row {
display: table;
}
.side-menu-setting-label {
display: table-cell;
width: 400px;
padding-right: 20px;
}
.side-menu-setting-form {
display: table-cell;
width: 300px;
}
.side-menu-setting-label-short {
width: 300px;
}
.side-menu-setting-form-long {
width: 400px;
}
+37 -11
View File
@@ -34,7 +34,7 @@
}
#header .side-menu-opener {
margin-left: 5px;
margin-left: 0px;
}
.side-menu-settings {
@@ -67,10 +67,19 @@
.side-menu-opener {
background: var(--side-menu-opener, url('../img/side-menu-opener.svg'));
height: 40px;
width: 40px;
border-radius: 0;
border: 0;
background-color: transparent !important;
height: 40px !important;
width: 40px !important;
border-radius: 0 !important;
border: 0 !important;
padding-right: 12px !important;
padding-left: 12px !important;
margin-left: 5px !important;
margin-left: 3px !important;
}
.side-menu-opener:active {
background-color: var(--color-background-dark) !important;
}
.side-menu-closer {
@@ -105,6 +114,10 @@
margin-top: -3px;
}
.side-menu-app-icon .app-icon-notification {
display: none;
}
.side-menu-app a {
line-height: 30px;
color: var(--side-menu-text-color, #fff);
@@ -135,7 +148,6 @@
max-width: 250px;
position: fixed;
padding-top: 2px;
padding-left: 5px;
top: 0;
}
@@ -158,23 +170,23 @@
transition-property: width;
}
#side-menu.side-menu-big {
#side-menu.side-menu-big, #side-menu.side-menu-with-categories {
max-width: 100%;
height: auto;
}
.side-menu-big .side-menu-header {
.side-menu-big .side-menu-header, .side-menu-with-categories .side-menu-header {
height: auto;
}
.side-menu-big .side-menu-apps-list {
.side-menu-big .side-menu-apps-list, .side-menu-with-categories .side-menu-apps-list {
height: auto;
position: static;
max-width: 100vw;
overflow: auto;
}
.side-menu-big .side-menu-app a {
.side-menu-big .side-menu-app a, .side-menu-with-categories .side-menu-app a {
padding: 7px 0 7px 7px;
}
@@ -213,7 +225,7 @@
stroke: var(--side-menu-text-color, #fff);
}
.side-menu-big .side-menu-app-icon {
.side-menu-with-categories .side-menu-app-icon, .side-menu-big .side-menu-app-icon {
vertical-align: middle;
margin-top: -2px;
}
@@ -267,6 +279,20 @@
transform: translateX(calc(-100% + 50px)) !important;
}
#side-menu.side-menu-with-categories {
max-width: 290px;
height: 100vh;
}
.side-menu-with-categories .side-menu-categories {
display: block;
padding: 0;
}
.side-menu-with-categories .side-menu-category {
padding: 10px 0;
}
@media screen and (max-width: 1024px) {
#side-menu.side-menu-big {
max-width: 290px;
@@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="571.907"
height="156.921"
viewBox="0 0 151.317 41.519"
version="1.1"
id="svg901"
sodipodi:docname="layout-side-menu-with-categories.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1036"
id="namedview903"
showgrid="false"
inkscape:zoom="1.6086532"
inkscape:cx="455.61054"
inkscape:cy="16.694081"
inkscape:window-x="0"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:current-layer="svg901"
showguides="false" />
<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"
id="path853" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g857">
<path
d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"
id="path855" />
</g>
<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"
id="path861" />
<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"
id="path877" />
<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"
id="path879" />
<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"
id="path881" />
<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"
id="path883" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g887">
<path
d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"
id="path885" />
</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"
id="path889" />
<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"
id="path891" />
<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"
id="path893" />
<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"
id="path895" />
<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"
id="path897" />
<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"
id="path899" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#4d4d4d;stroke:#b3b3b3;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 62.728587,0.17999881 h 20.829 V 41.34 h -20.83 z"
id="path895-5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#666666;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 63.905587,1.0339988 h 3.496 V 4.389999 h -3.496 z"
id="path897-3" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#e6e6e6;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 64.440587,10.154999 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.463 v 1.148 h -12.463 z m -2.929,3.21 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 10.592 v 1.148 h -10.592 z m -2.929,3.077 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.997 v 1.148 h -12.997 z m -2.929,7.273 h 1.893 V 26.518 h -1.893 z m 2.929,0.332 h 7.585 v 1.147 h -7.585 z m -2.929,3.076001 h 1.893 v 1.81 h -1.893 z m 2.929,0.330999 h 11.46 v 1.147999 h -11.46 z m -2.929,3.21 h 1.893 v 1.809999 h -1.893 z m 2.929,0.330999 h 9.322 v 1.148001 h -9.322 z m -2.929,3.077001 h 1.893 v 1.809999 h -1.893 z m 2.929,0.331001 h 11.126 v 1.147999 h -11.126 z m -2.882,-28.5480012 h 11.518 v 1.1480001 h -11.518 z m -0.047,14.4630002 h 9.344 v 1.148 h -9.344 z"
id="path899-5" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

+223
View File
@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="571.907"
height="156.921"
viewBox="0 0 151.317 41.519"
version="1.1"
id="svg901"
sodipodi:docname="layout-side-menu-with-categories.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata907">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs905" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1918"
inkscape:window-height="1036"
id="namedview903"
showgrid="false"
inkscape:zoom="1.6086532"
inkscape:cx="455.61054"
inkscape:cy="16.694081"
inkscape:window-x="0"
inkscape:window-y="21"
inkscape:window-maximized="0"
inkscape:current-layer="svg901" />
<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"
id="path853" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g857">
<path
d="M84.066 21.35l5.055-5.056M82.925 29.835l18.152-18.151M95.893 25.225l3.585-3.586"
id="path855" />
</g>
<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"
id="path861" />
<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"
id="path877" />
<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"
id="path879" />
<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"
id="path881" />
<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"
id="path883" />
<g
fill="#b3b3b3"
stroke="#ccc"
fill-rule="evenodd"
stroke-width="1.465"
stroke-linecap="round"
id="g887">
<path
d="M21.51 21.35l5.056-5.056M20.37 29.835L38.52 11.684M33.337 25.225l3.586-3.586"
id="path885" />
</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"
id="path889" />
<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"
id="path891" />
<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"
id="path893" />
<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"
id="path895" />
<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"
id="path897" />
<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"
id="path899" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#4d4d4d;stroke:#b3b3b3;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 62.728587,0.17999881 h 20.829 V 41.34 h -20.83 z"
id="path895-5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#666666;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 63.905587,1.0339988 h 3.496 V 4.389999 h -3.496 z"
id="path897-3" />
<path
inkscape:connector-curvature="0"
style="color:#000000;overflow:visible;fill:#e6e6e6;stroke:#cccccc;stroke-width:0.359;stroke-linecap:square;marker:none;paint-order:fill markers stroke"
overflow="visible"
d="m 64.440587,10.154999 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.463 v 1.148 h -12.463 z m -2.929,3.21 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 10.592 v 1.148 h -10.592 z m -2.929,3.077 h 1.893 v 1.81 h -1.893 z m 2.929,0.331 h 12.997 v 1.148 h -12.997 z m -2.929,7.273 h 1.893 V 26.518 h -1.893 z m 2.929,0.332 h 7.585 v 1.147 h -7.585 z m -2.929,3.076001 h 1.893 v 1.81 h -1.893 z m 2.929,0.330999 h 11.46 v 1.147999 h -11.46 z m -2.929,3.21 h 1.893 v 1.809999 h -1.893 z m 2.929,0.330999 h 9.322 v 1.148001 h -9.322 z m -2.929,3.077001 h 1.893 v 1.809999 h -1.893 z m 2.929,0.331001 h 11.126 v 1.147999 h -11.126 z m -2.882,-28.5480012 h 11.518 v 1.1480001 h -11.518 z m -0.047,14.4630002 h 9.344 v 1.148 h -9.344 z"
id="path899-5" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

+1 -113
View File
@@ -1,113 +1 @@
<?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>
<svg xmlns="http://www.w3.org/2000/svg" width="145.889" height="145.889" viewBox="0 0 38.6 38.6"><g transform="translate(-59.172 -91.92)" stroke="#000" fill="none"><path d="M66.595 111.162h9.997M66.595 103.805h11.4m-11.4 14.714h11.4" stroke-width="3.165"/><rect width="34.535" height="34.535" x="61.205" y="93.952" overflow="visible" rx="5.481" ry="5.481" style="marker:none" color="#000" stroke-width="4.065" stroke-linecap="round" paint-order="fill markers stroke"/></g></svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 478 B

+6 -1
View File
@@ -6,6 +6,7 @@ use OC;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\User\User;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\App;
use OCP\IUserSession;
@@ -84,8 +85,12 @@ class Application extends App
return new AppRepository();
});
$container->registerService('CategoryRepository', function (ContainerInterface $c) {
return new CategoryRepository();
});
$container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config);
return new ConfigProxy();
});
}
+47 -8
View File
@@ -65,29 +65,68 @@ class CssController extends Controller
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', '[]');
$isAccessibilityAppEnabled = $this->config->getAppValueBool('enabled', '0', 'accessibility');
$isBreezeDarkAppEnabled = $this->config->getAppValueBool('enabled', '0', 'breezedark');
$isBreezeDarkGlobalEnabled = $this->config->getAppValueBool('theme_enabled', '0', 'breezedark');
if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps;
}
$isDarkThemeUserEnabled = $this->config->getUserValue($this->user, 'theme', '', 'accessibility') === 'dark';
$isBreezeDarkUserEnabled = $this->config->getUserValue($this->user, 'theme_enabled', '', 'breezedark');
$isBreezeDarkUserEnabled = $isBreezeDarkUserEnabled === '1' || ($isBreezeDarkGlobalEnabled && $isBreezeDarkUserEnabled === '');
} else {
$isDarkThemeUserEnabled = false;
$isBreezeDarkUserEnabled = false;
}
$isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled) || ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled);
if ($isDarkMode) {
$backgroundColor = $this->config->getAppValue('dark-mode-background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $backgroundColor);
$currentAppBackgroundColor = $this->config->getAppValue('dark-mode-current-app-background-color', '#444444');
$loaderColor = $this->config->getAppValue('dark-mode-loader-color', '#cccccc');
$textColor = $this->config->getAppValue('dark-mode-text-color', '#FFFFFF');
$iconInvertFilter = abs($this->config->getAppValueInt('dark-mode-icon-invert-filter', '0')).'%';
$iconOpacity = abs($this->config->getAppValueInt('dark-mode-icon-opacity', '100') / 100);
$opener = $this->config->getAppValue('dark-mode-opener', 'side-menu-opener');
$backgroundOpacity = dechex($this->config->getAppValueInt('dark-mode-background-color-opacity', '100') * 255 / 100);
} else {
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$currentAppBackgroundColor = $this->config->getAppValue('current-app-background-color', '#444444');
$loaderColor = $this->config->getAppValue('loader-color', '#0e75ac');
$textColor = $this->config->getAppValue('text-color', '#FFFFFF');
$iconInvertFilter = abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%';
$iconOpacity = abs($this->config->getAppValueInt('icon-opacity', '100') / 100);
$opener = $this->config->getAppValue('opener', 'side-menu-opener');
$backgroundOpacity = dechex($this->config->getAppValueInt('background-color-opacity', '100') * 255 / 100);
}
$backgroundColor .= $backgroundOpacity;
$backgroundColorTo .= $backgroundOpacity;
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),
'current-app-background-color' => $currentAppBackgroundColor,
'loader-color' => $loaderColor,
'text-color' => $textColor,
'opener' => $opener,
'icon-invert-filter' => $iconInvertFilter,
'icon-opacity' => $iconOpacity,
],
'display-logo' => $this->config->getAppValueBool('display-logo', '1'),
'opener-only' => $this->config->getAppValueBool('opener-only', '0'),
+6
View File
@@ -97,6 +97,7 @@ class JsController extends Controller
$targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]');
$useAvatar = $this->config->getAppValueBool('use-avatar', '0');
$isForced = $this->config->getAppValueBool('force', '0');
$addLogoLink = $this->config->getAppValueBool('add-logo-link', '1');
$avatar = null;
$settings = null;
@@ -141,6 +142,8 @@ class JsController extends Controller
}
}
$indexUrl = OC::$server->getURLGenerator()->linkTo('', 'index.php');
return [
'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'),
@@ -149,12 +152,15 @@ class JsController extends Controller
'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'),
'side-with-categories' => $this->config->getAppValueBool('side-with-categories', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'avatar' => $avatar,
'top-menu-apps' => $topMenuApps,
'target-blank-apps' => $targetBlankApps,
'settings' => $settings,
'logo' => $this->themingDefaults->getLogo(),
'logo-link' => $addLogoLink ? $indexUrl : null,
];
}
}
+25 -42
View File
@@ -21,13 +21,11 @@ namespace OCA\SideMenu\Controller;
use OC;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
@@ -39,11 +37,6 @@ class NavController extends Controller
*/
protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/**
* @var AppRepository
*/
@@ -67,20 +60,17 @@ class NavController extends Controller
public function __construct(
string $appName,
IRequest $request,
IConfig $iConfig,
ConfigProxy $config,
AppRepository $appRepository,
CategoryFetcher $categoryFetcher,
CategoryRepository $categoryRepository,
URLGenerator $router,
IL10N $trans,
IFactory $l10nFactory
) {
parent::__construct($appName, $request);
$this->config = $config;
$this->iConfig = $iConfig;
$this->appRepository = $appRepository;
$this->categoryFetcher = $categoryFetcher;
$this->categoryRepository = $categoryRepository;
$this->l10nFactory = $l10nFactory;
$this->router = $router;
}
@@ -94,13 +84,7 @@ class NavController extends Controller
*/
public function items()
{
$apps = $this->appRepository->getVisibleApps();
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$externalSitesInTopMenu = $this->config->getAppValueBool('external-sites-in-top-menu', '0');
$user = OC::$server[IUserSession::class]->getUser();
$isForced = $this->config->getAppValueBool('force', '0');
$appsCategories = [];
$categoriesAppsCount = [];
$items = [];
if (!$user) {
@@ -109,39 +93,28 @@ class NavController extends Controller
]);
}
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]');
if (empty($categoriesLabels)) {
$categoriesLabels = $this->categoryFetcher->get();
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
$apps = $this->appRepository->getVisibleApps();
$categoriesLabels = $this->categoryRepository->getOrderedCategories();
$hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]');
$isForced = $this->config->getAppValueBool('force', '0');
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
$appsCategories = [];
$categoriesAppsCount = [];
if (!$isForced && !empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
foreach ($categoriesLabels as $k => $category) {
$categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'];
unset($categoriesLabels[$k]);
}
$categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites');
$items['other'] = [
'name' => '',
'apps' => [],
];
foreach ($apps as $app) {
if (in_array($app['id'], $topMenuApps)) {
continue;
}
if (in_array($app['id'], $hiddenApps)) {
continue;
}
$categories = (array) $app['category'];
$appsCategories[$app['id']] = [];
@@ -153,6 +126,7 @@ class NavController extends Controller
if (!isset($items[$category])) {
$items[$category] = [
'name' => $categoriesLabels[$category] ?? $category,
'categoryId' => $category,
'apps' => [],
];
}
@@ -211,8 +185,17 @@ class NavController extends Controller
}
}
usort($items, function ($a, $b) {
return ($a['name'] < $b['name']) ? -1 : 1;
usort($items, function ($a, $b) use ($categoriesLabels) {
foreach ($categoriesLabels as $key => $value) {
if ($a['categoryId'] === $key) {
return -1;
}
if ($b['categoryId'] === $key) {
return 1;
}
}
return 0;
});
return new JSONResponse([
+2 -2
View File
@@ -18,13 +18,13 @@
namespace OCA\SideMenu\Controller;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
class PersonalSettingController extends Controller
{
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace OCA\SideMenu\Service;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OCA\SideMenu\AppInfo\Application;
use OCP\IConfig;
use OCP\L10N\IFactory;
/**
* class CategoryRepository.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class CategoryRepository
{
/**
* @var CategoryFetcher
*/
protected $categoryFetcher;
/**
* @var IFactory
*/
protected $l10nFactory;
/**
* @var ConfigProxy
*/
protected $config;
/**
* @var IConfig
*/
protected $iConfig;
public function __construct(
CategoryFetcher $categoryFetcher,
ConfigProxy $config,
IConfig $iConfig,
IFactory $l10nFactory
) {
$this->categoryFetcher = $categoryFetcher;
$this->l10nFactory = $l10nFactory;
$this->config = $config;
$this->iConfig = $iConfig;
}
/**
* Retrieves categories.
*
* @return array
*/
public function getOrderedCategories()
{
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$type = $this->config->getAppValue('categories-order-type', 'default');
$order = $this->config->getAppValueArray('categories-order', '[]');
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]');
if (empty($categoriesLabels)) {
$categoriesLabels = $this->categoryFetcher->get();
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
foreach ($categoriesLabels as $k => $category) {
$categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name'];
unset($categoriesLabels[$k]);
}
$categoriesLabels['external_links'] = $this->l10nFactory->get('external')->t('External sites');
$categoriesLabels['other'] = '';
asort($categoriesLabels);
if ('custom' === $type) {
$ordered = [];
foreach ($order as $id) {
if (isset($categoriesLabels[$id])) {
$ordered[$id] = $categoriesLabels[$id];
}
}
foreach ($categoriesLabels as $id => $value) {
if (!isset($ordered[$id])) {
$ordered[$id] = $value;
}
}
$categoriesLabels = $ordered;
}
return $categoriesLabels;
}
}
+20 -16
View File
@@ -23,43 +23,47 @@ class ConfigProxy
$this->config = $config;
}
public function getAppValue(string $name, string $default): string
public function getAppValue(string $name, string $default, $appId = null): string
{
return (string) $this->config->getAppValue(Application::APP_ID, $name, $default);
return (string) $this->config->getAppValue($appId ?? Application::APP_ID, $name, $default);
}
public function getUserValue(User $user, string $name, string $default): string
public function getUserValue(User $user, string $name, string $default, $appId = null): string
{
return (string) $this->config->getUserValue($user->getUid(), Application::APP_ID, $name, $default);
return (string) $this->config->getUserValue($user->getUid(), $appId ?? Application::APP_ID, $name, $default);
}
public function getAppValueBool(string $name, string $default): bool
public function getAppValueBool(string $name, string $default, $appId = null): bool
{
return (bool) $this->getAppValue($name, $default);
return (bool) $this->getAppValue($name, $default, $appId);
}
public function getAppValueArray(string $name, string $default): array
public function getAppValueArray(string $name, string $default, $appId = null): array
{
return (array) json_decode($this->getAppValue($name, $default), true);
return (array) json_decode($this->getAppValue($name, $default, $appId), true);
}
public function getAppValueInt(string $name, string $default): int
public function getAppValueInt(string $name, string $default, $appId = null): int
{
return (int) $this->getAppValue($name, $default);
return (int) $this->getAppValue($name, $default, $appId);
}
public function getUserValueBool(User $user, string $name, string $default): bool
public function getUserValueBool(User $user, string $name, string $default, $appId = null): bool
{
return (bool) $this->getUserValue($user, $name, $default);
return (bool) str_replace(
['yes', 'no'],
['1', '0'],
$this->getUserValue($user, $name, $default, $appId)
);
}
public function getUserValueArray(User $user, string $name, string $default): array
public function getUserValueArray(User $user, string $name, string $default, $appId = null): array
{
return (array) json_decode($this->getUserValue($user, $name, $default), true);
return (array) json_decode($this->getUserValue($user, $name, $default, $appId), true);
}
public function getUserValueInt(User $user, string $name, string $default): int
public function getUserValueInt(User $user, string $name, string $default, $appId = null): int
{
return (int) $this->getUserValue($user, $name, $default);
return (int) $this->getUserValue($user, $name, $default, $appId);
}
}
+35 -4
View File
@@ -20,6 +20,7 @@ namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
@@ -48,12 +49,23 @@ class Admin implements ISettings
*/
private $appRepository;
public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, AppRepository $appRepository)
{
/**
* @var CategoryRepository
*/
private $categoryRepository;
public function __construct(
IL10N $l,
ILogger $logger,
ConfigProxy $config,
AppRepository $appRepository,
CategoryRepository $categoryRepository
) {
$this->l = $l;
$this->logger = $logger;
$this->config = $config;
$this->appRepository = $appRepository;
$this->categoryRepository = $categoryRepository;
}
/**
@@ -64,20 +76,36 @@ class Admin implements ISettings
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$darkModeBackgroundColor = $this->config->getAppValue('dark-mode-background-color', '#333333');
$darkModeBackgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkModeBackgroundColor);
$parameters = [
'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo,
'background-color-opacity' => $this->config->getAppValueInt('background-color-opacity', '100'),
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'),
'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'),
'cache' => $this->config->getAppValue('cache', '0'),
'dark-mode-background-color' => $darkModeBackgroundColor,
'dark-mode-background-color-to' => $darkModeBackgroundColorTo,
'dark-mode-background-color-opacity' => $this->config->getAppValueInt('dark-mode-background-color-opacity', '100'),
'dark-mode-current-app-background-color' => $this->config->getAppValue('dark-mode-current-app-background-color', '#444444'),
'dark-mode-loader-color' => $this->config->getAppValue('dark-mode-loader-color', '#cccccc'),
'dark-mode-icon-invert-filter' => $this->config->getAppValueInt('dark-mode-icon-invert-filter', '0'),
'dark-mode-icon-opacity' => $this->config->getAppValueInt('dark-mode-icon-opacity', '100'),
'dark-mode-text-color' => $this->config->getAppValue('dark-mode-text-color', '#FFFFFF'),
'dark-mode-opener' => $this->config->getAppValue('dark-mode-opener', 'side-menu-opener'),
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'cache' => $this->config->getAppValue('cache', '0'),
'always-displayed' => $this->config->getAppValue('always-displayed', '0'),
'big-menu' => $this->config->getAppValue('big-menu', '0'),
'side-with-categories' => $this->config->getAppValue('side-with-categories', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'display-logo' => $this->config->getAppValue('display-logo', '1'),
'add-logo-link' => $this->config->getAppValue('add-logo-link', '1'),
'use-avatar' => $this->config->getAppValue('use-avatar', '0'),
'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValue('opener-hover', '0'),
@@ -90,7 +118,10 @@ class Admin implements ISettings
'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'),
'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'),
'default-enabled' => $this->config->getAppValue('default-enabled', '1'),
'categories-order-type' => $this->config->getAppValue('categories-order-type', 'default'),
'categories-order' => $this->config->getAppValueArray('categories-order', '[]'),
'apps' => $this->appRepository->getVisibleApps(),
'categories' => $this->categoryRepository->getOrderedCategories(),
];
return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
+4 -8
View File
@@ -18,10 +18,10 @@
namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class AdminSection implements IIconSection
{
@@ -35,10 +35,6 @@ class AdminSection implements IIconSection
*/
private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
public function __construct(IURLGenerator $url, IL10N $l)
{
$this->url = $url;
@@ -47,7 +43,7 @@ class AdminSection implements IIconSection
/**
* returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap'
* e.g. 'ldap'.
*
* @returns string
*/
@@ -69,8 +65,8 @@ class AdminSection implements IIconSection
/**
* @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99.
* the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99.
*
* E.g.: 70
*/
+22 -7
View File
@@ -18,10 +18,11 @@
namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class PersonalSection implements IIconSection
{
@@ -36,23 +37,29 @@ class PersonalSection implements IIconSection
private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
* @var ConfigProxy
*/
public function __construct(IURLGenerator $url, IL10N $l)
private $configProxy;
public function __construct(IURLGenerator $url, IL10N $l, ConfigProxy $configProxy)
{
$this->url = $url;
$this->l = $l;
$this->configProxy = $configProxy;
}
/**
* returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap'
* e.g. 'ldap'.
*
* @returns string
*/
public function getID()
{
if ($this->configProxy->getAppValueBool('force', '0')) {
return '';
}
return Application::APP_ID;
}
@@ -64,18 +71,26 @@ class PersonalSection implements IIconSection
*/
public function getName()
{
if ($this->configProxy->getAppValueBool('force', '0')) {
return '';
}
return $this->l->t(Application::APP_NAME);
}
/**
* @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99.
* the settings navigation. The sections are arranged in ascending order of
* the priority values. It is required to return a value between 0 and 99.
*
* E.g.: 70
*/
public function getPriority()
{
if ($this->configProxy->getAppValueBool('force', '0')) {
return null;
}
return 70;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 195 KiB

+8 -1
View File
@@ -16,7 +16,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div v-bind:class="classes">
<img v-bind:src="image">
<a v-if="link !== null" v-bind:href="link">
<img v-bind:src="image">
</a>
<img v-else v-bind:src="image">
</div>
</template>
@@ -28,6 +31,10 @@ export default {
type: String,
required: true
},
link: {
type: String,
required: false
},
classes: {
type: Object,
required: true
+4 -4
View File
@@ -18,11 +18,9 @@
import Vue from 'vue'
import SideMenu from './SideMenu.vue'
import SideMenuBig from './SideMenuBig.vue'
import SideMenuWithCategories from './SideMenuWithCategories.vue'
// Vue.prototype.t = t
Vue.prototype.OC = OC
// Vue.prototype.OC = OCP
const mountSideMenuComponent = () => {
const sideMenuContainer = document.querySelector('#side-menu')
@@ -32,6 +30,8 @@ const mountSideMenuComponent = () => {
if (sideMenuContainer.getAttribute('data-bigmenu')) {
component = SideMenuBig
} else if(sideMenuContainer.getAttribute('data-sidewithcategories')) {
component = SideMenuWithCategories
} else {
component = SideMenu
}
@@ -41,7 +41,7 @@ const mountSideMenuComponent = () => {
sideMenu.$mount('#side-menu')
$('body').trigger('side-menu.ready')
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready'))
} else {
window.setTimeout(mountSideMenuComponent, 50)
}
+18 -5
View File
@@ -25,8 +25,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<OpenerButton />
<Logo v-if="!avatar && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}" v-bind:image="logo" />
<Logo v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}" v-bind:image="avatar" />
<Logo
v-if="!avatar && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}"
v-bind:image="logo"
v-bind:link="logoLink"
/>
<Logo
v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}"
v-bind:image="avatar"
v-bind:link="logoLink"
/>
</div>
<ul class="side-menu-apps-list">
@@ -62,6 +71,7 @@ export default {
return {
apps: [],
logo: null,
logoLink: null,
avatar: null,
forceLightIcon: false,
targetBlankApps: [],
@@ -112,13 +122,15 @@ export default {
name: trim(element.querySelector('span').innerHTML),
icon: svg,
active: element.classList.contains('active')
});
})
}
}
(function(apps) {
window.setTimeout(function() {
jQuery('body').trigger('side-menu.apps', [apps])
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: apps},
}))
}, 1000)
})(this.apps)
},
@@ -135,8 +147,9 @@ export default {
that.forceLightIcon = config['force-light-icon']
that.avatar = config['avatar']
that.logo = config['logo']
that.logoLink = config['logo-link']
that.settings = config['settings']
});
})
},
},
mounted() {
+6 -4
View File
@@ -43,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
v-bind:icon="app.icon"
v-bind:label="app.name"
v-bind:href="app.href"
v-bind:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
/>
</ul>
</div>
@@ -97,8 +97,10 @@ export default {
}
}
jQuery('body').trigger('side-menu.apps', [apps])
});
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: apps},
}))
})
},
retrieveActiveApp() {
@@ -116,7 +118,7 @@ export default {
that.targetBlankApps = config['target-blank-apps']
that.settings = config['settings']
});
})
},
},
mounted() {
+128
View File
@@ -0,0 +1,128 @@
<!--
@license GNU AGPL version 3 or any later version
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template>
<div id="side-menu" class="side-menu-with-categories">
<div class="side-menu-header">
<SettingsButton
v-if="settings"
v-bind:href="settings.href"
v-bind:label="settings.name"
v-bind:avatar="settings.avatar"
/>
<OpenerButton />
</div>
<div class="side-menu-categories-wrapper">
<div class="side-menu-categories">
<Loader v-if="!items.length" />
<div class="side-menu-category" v-for="category in items">
<h2 class="side-menu-category-title" v-if="category.name != ''" v-html="category.name"></h2>
<ul class="side-menu-apps-list">
<SideMenuBigApp
v-for="(app, appId) in category.apps"
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
v-bind:icon="app.icon"
v-bind:label="app.name"
v-bind:href="app.href"
v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
/>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import trim from 'trim'
import axios from 'axios'
import OpenerButton from './OpenerButton'
import CloserButton from './CloserButton'
import SettingsButton from './SettingsButton'
import Loader from './Loader'
import SideMenuBigApp from './SideMenuBigApp'
export default {
name: 'SideMenuWithCategories',
components: {
SettingsButton,
OpenerButton,
CloserButton,
Loader,
SideMenuBigApp,
},
data() {
return {
items: [],
activeApp: null,
targetBlank: false,
targetBlankApps: [],
settings: null,
}
},
methods: {
retrieveApps() {
this.apps = []
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/nav/items'))
.then(function(response) {
that.items = response.data.items
let apps = []
for (let category of that.items) {
for (let a in category.apps) {
apps.push(category.apps[a])
}
}
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
detail: {apps: apps},
}))
})
},
retrieveActiveApp() {
let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
},
retrieveConfig() {
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then(function(response) {
const config = response.data
that.targetBlankApps = config['target-blank-apps']
that.settings = config['settings']
})
},
},
mounted() {
this.retrieveConfig()
this.retrieveApps()
this.retrieveActiveApp()
}
}
</script>
+119 -49
View File
@@ -17,13 +17,23 @@
let elements = []
const selector = '#side-menu-message';
const selector = '#side-menu-message'
const userConfig = (name, value, callbacks) => {
const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet')
const formData = new FormData()
formData.append('name', name)
formData.append('value', value)
$.post(url, {name: name, value: value}, callbacks.success)
.fail(callbacks.error)
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData
})
.then(callbacks.success)
.catch(callbacks.error)
}
const appConfig = (name, value, callbacks) => {
@@ -31,23 +41,29 @@ const appConfig = (name, value, callbacks) => {
}
const saveSettings = (key) => {
const element = elements.get(key)
const element = elements[key]
if (!element) {
return
}
let value
let name
if (jQuery(element).is('[data-checkbox]')) {
name = jQuery(element).attr('data-name')
const inputs = jQuery('input[name="' + name + '[]"]:checked')
if (element.hasAttribute('data-checkbox')) {
name = element.getAttribute('data-name')
value = []
inputs.each((i, v) => {
value.push(v.value)
})
const inputs = document.querySelectorAll('input[name="' + name + '[]"]:checked')
for (let input of inputs) {
value.push(input.value)
}
value = JSON.stringify(value)
} else {
name = jQuery(element).attr('name')
value = jQuery(element).val()
name = element.getAttribute('name')
value = element.value
}
const size = elements.length
@@ -63,8 +79,8 @@ const saveSettings = (key) => {
t('side_menu', (key + 1) + '/' + size)
)
if (key < size - 1) {
saveSettings(++key)
if (key < size) {
saveSettings(key + 1)
} else {
OC.msg.finishedSuccess(selector, t('side_menu', 'Saved'))
}
@@ -74,7 +90,7 @@ const saveSettings = (key) => {
}
}
if (jQuery(element).is('[data-personal]')) {
if (element.hasAttribute('data-personal')) {
userConfig(name, value, callbacks)
} else {
appConfig(name, value, callbacks)
@@ -82,51 +98,105 @@ const saveSettings = (key) => {
}
const elementToggler = (element) => {
jQuery(element).toggle()
let display = 'none'
if (window.getComputedStyle(element).display === 'none') {
display = 'block'
}
element.style.display = display
}
jQuery(document).ready(() => {
elements = jQuery('.side-menu-setting')
document.addEventListener('DOMContentLoaded', () => {
elements = document.querySelectorAll('.side-menu-setting')
jQuery('#side-menu-save').on('click', (event) => {
document.querySelector('#side-menu-save').addEventListener('click', (event) => {
event.preventDefault()
OC.msg.startSaving(selector)
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()
const displays = document.querySelectorAll('.side-menu-display')
if (name === 'opener') {
var url = OC.generateUrl(`/apps/side_menu/img/${value}.svg`).replace('/index.php', '')
for (let display of displays) {
display.addEventListener('click', (event) => {
const target = event.target
value = `url(${url})`;
for (let d of displays) {
d.classList.toggle('is-active', d === display)
}
document.querySelector('#side-menu-always-displayed').value = target.getAttribute('data-alwaysdiplayed')
document.querySelector('#side-menu-big-menu').value = target.getAttribute('data-bigmenu')
document.querySelector('#side-menu-side-with-categories').value = target.getAttribute('data-sidewithcategories')
})
}
for (let item of document.querySelectorAll('.side-menu-setting-live')) {
item.addEventListener('change', (event) => {
const target = event.target
const name = target.getAttribute('name')
let value = target.value
let id = null
if (name === 'background-color-opacity') {
id = '#side-menu-background-color, #side-menu-background-color-to'
} else if (name === 'dark-mode-background-color-opacity') {
id = '#side-menu-dark-mode-background-color, #side-menu-dark-mode-background-color-to'
}
if (id) {
document.querySelector(id).dispatchEvent(new CustomEvent('change'))
return
}
if (name === 'opener') {
const 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
}
if (['dark-mode-background-color', 'dark-mode-background-color-to'].indexOf(name) > -1) {
const opacity = parseInt(document.querySelector('#side-menu-dark-mode-background-color-opacity').value * 255 / 100)
value = [value, opacity.toString(16)].join('')
} else if (['background-color', 'background-color-to'].indexOf(name) > -1) {
const opacity = parseInt(document.querySelector('#side-menu-background-color-opacity').value * 255 / 100)
value = [value, opacity.toString(16)].join('')
}
document.documentElement.style.setProperty('--side-menu-' + name, value)
})
}
for (let toggler of document.querySelectorAll('.side-menu-toggler')) {
toggler.addEventListener('click', (event) => {
const target = event.target
const element = document.querySelector(target.getAttribute('data-target'))
elementToggler(element)
})
}
sortable('#categories-list .side-menu-setting-list', {
placeholderClass: 'side-menu-setting-list-drop'
})
sortable('#categories-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => {
let value = []
for (let item of document.querySelectorAll('#categories-list .side-menu-setting-list-item')) {
value.push(item.getAttribute('data-id'))
}
if (name === 'icon-invert-filter' || name === 'icon-opacity') {
value/=100;
}
document.documentElement.style.setProperty('--side-menu-' + name, value)
document.querySelector('input[name="categories-order"]').value = JSON.stringify(value)
})
jQuery('.side-menu-toggler').on('click', (event) => {
var target = jQuery(event.target)
var element = target.attr('data-target')
elementToggler(element)
})
});
})
+79
View File
@@ -0,0 +1,79 @@
"Custom menu": "Uživatelsky určená nabídka"
"Enable the custom menu": "Zapnout uživatelsky určenou nabídku"
"No": "Ne"
"Yes": "Ano"
"Menu": "Nabídka"
'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.': 'Pro otevření/skrytí postranní nabídky použijte zkratku <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span>. Pro pohyb po použijte klávesu <span class="keyboard-key">tab</span>.'
"Top menu": "Hlavní nabídka"
"Apps that not must be moved in the side menu": "Aplikace, které nepřesouvat do postranní nabídky"
"If there is no selection then the global configuration is applied.": "Pokud neexistuje žádný výběr, je uplatněno globální nastavení."
"Experimental": "Experimentální"
"Save": "Uložit"
"You like this app and you want to support me?": "Líbí se vám tato aplikace a chcete podpořit její vývoj?"
"Buy me a coffee ☕": "Kupte mi kafe ☕"
"Hidden": "Skryté"
"Small": "Malé"
"Normal": "Normální"
"Big": "Velké"
"Colors": "Barvy"
"Background color": "Barva pozadí"
"Background color of current app": "Barva pozadí stávající aplikace"
"Text color": "Barva textu"
"Loader": "Nástroj pro načítání"
"Icon": "Ikona"
"Same color": "Stejná barva"
"Opposite color": "Doplňková barva"
"Transparent": "Průhledné"
"Opaque": "Neprůhledné"
"Opener": "Tlačítko pro otevření"
"Default": "Výchozí"
"Default (dark)": "Výchozí (tmavé)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (tmavé)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (tmavé)"
"Before the logo": "Před logem"
"After the logo": "Za logem"
"Position": "Pozice"
"Show only the opener (hidden logo)": "Zobrazovat pouze otevírací tlačítko (logo skryto)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Nezobrazovat postranní nabídku a její otevírací tlačítko pokud nejsou dostupné žádné aplikace (např. na veřejných stránkách)."
"Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Otevřít nabídku při najetím ukazatelem na tlačítko nabídky (automaticky vypnuto pro dotykové obrazovky)."
"Display the big menu": "Zobrazit velkou nabídku"
"This menu is not compatible with AppOrder.": "Nabídka není kompatibilní s jinou aplikací (doplňkem) „Pořadí aplikací“."
"Display the logo": "Zobrazit logo"
"This feature is not compatible with the <code>big menu</code> display.": "Tato funkce není kompatibilní se zobrazením <code>velké nabídky</code>."
"Icons and texts": "Ikony a texty"
"Loader enabled": "Načítání zapnuto"
"Tips": "Tipy"
"Always displayed": "Vždy zobrazeno"
"The logo will be hidden when the menu is always displayed.": "Pokud je nabídka zobrazena trvale, logo bude skryto."
"This is the automatic behavior when the menu is always displayed.": "Toto je automatické chování, kdy je nabídka vždy zobrazena."
"Not compatible with touch screens.": "Nekompatibilní s dotykovými obrazovkami."
"Big menu": "Velká nabídka"
"Live preview": "Živý náhled"
"Open apps in new tab": "Otevírat aplikace v novém panelu"
"Use the global setting": "Použít globální nastavení"
"Use my selection": "Použít můj výběr"
"Show and hide the list of applications": "Zobrazit/skrýt seznam aplikací"
"Use the avatar instead of the logo": "Použít namísto loga profilový obrázek uživatele"
"You do not have permission to change the settings.": "Nemáte oprávnění měnit nastavení."
"Force this configuration to users": "Vynutit uplatnění těchto nastavení uživatelům"
"Export the configuration": "Exportovat nastavení"
"Purge the cache": "Vyprázdnit mezipaměť"
"Show the link to settings": "Zobrazit odkaz na nastavení"
"The menu is enabled by default for users": "Nabídka je ve výchozím stavu zapnutá pro uživatele"
"Except when the configuration is forced.": "S výjimkou, kdy je nastavení vynuceno."
"Apps that should not be displayed in the menu": "Aplikace, které by neměly být v nabídce zobrazeny"
"This feature is only compatible with the <code>big menu</code> display.": "Tato funkce je kompatibilní pouze s <code>velkou nabídkou</ code>."
"The logo is a link to the default app": "Logo je odkaz na výchozí aplikaci"
"Others": "Ostatní"
"Categories": "Kategorie"
"Customize sorting": "Přizpůsobit si řazení"
"Order by": "Řadit podle"
"Name": "Název"
"Customed": "Přizpůsobeno"
"Show and hide the list of categories": "Zobrazit/skrýt seznam kategorií"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Tyto parametry jsou použity v případě, že je zapnutý (Breeze) tmavý motiv vzhledu."
"Dark mode colors": "Barvy tmavého režimu"
"With categories": "S kategoriemi"
+39 -26
View File
@@ -1,31 +1,31 @@
"Custom menu": "Benutzerdefiniertes Menü"
"Enable the custom menu": "Aktiviere das Benutzerdefiniertes Menü"
"Enable the custom menu": "Benutzerdefiniertes Menü aktivieren"
"No": "Nein"
"Yes": "Ja"
"Menu": "Menü"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Verwende die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü ein- und auszublenden. Verwende <span class="keyboard-key">tab</span> zum Navigieren.'
"Top menu": "Hauptmenü"
"Apps that not must be moved in the side menu": "Apps, die nicht ins Seitenmenü verschoben werden müssen"
"Top menu": "Obere Navigationsleiste"
"Apps that not must be moved in the side menu": "Anwendungen, die nicht ins Seitenmenü verschoben werden sollen"
"If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet."
"Experimental": "Experimentell"
"Save": "Speichern"
"You like this app and you want to support me?": "Du magst diese App und möchtest mich unterstützen?"
"Buy me a coffee ☕": "Gib mir einen Kaffee ☕"
"Hidden": "Versteckt"
"You like this app and you want to support me?": "Du magst diese Anwendung und möchtest mich unterstützen?"
"Buy me a coffee ☕": "Gib mir einen Kaffee aus ☕"
"Hidden": "Ausblenden"
"Small": "Klein"
"Normal": "Normal"
"Big": "Groß"
"Colors": "Farben"
"Background color": "Hintergrundfarbe"
"Background color of current app": "Hintergrundfarbe der aktuellen App"
"Background color of current app": "Hintergrundfarbe der aktuellen Anwendung"
"Text color": "Textfarbe"
"Loader": "Ladestandanzeige"
"Icon": "Symbol"
"Same color": "Selbe Farbe"
"Opposite color": "Gegenfarbe"
"Transparent": "Transparent"
"Opaque": "Undurchsichtig"
"Opener": "Öffner"
"Opaque": "Nicht transparent"
"Opener": "Menü-Symbol"
"Default": "Standard"
"Default (dark)": "Standard (dunkel)"
"Hamburger": "Hamburger"
@@ -35,32 +35,45 @@
"Before the logo": "Vor dem Logo"
"After the logo": "Nach dem Logo"
"Position": "Position"
"Show only the opener (hidden logo)": "Nur den Öffner anzeigen (verstecktes Logo)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)."
"Show only the opener (hidden logo)": "Nur das Menü-Symbol anzeigen (Logo wird ausgeblendet)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und das Menü-Symbol nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)."
"Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über den Öffner bewegt wird (auf Touchscreens automatisch deaktiviert)."
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über das Menü-Symbol bewegt wird (auf Touchscreens automatisch deaktiviert)."
"Display the big menu": "Großes Menü anzeigen"
"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel."
"This menu is not compatible with AppOrder.": "Dieses Menü ist nicht mit <code>AppOrder</code> kompatibel."
"Display the logo": "Logo anzeigen"
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit <code>großes Menü</code> kompatibel."
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit dem <code>großen Menü</code> kompatibel."
"Icons and texts": "Symbole und Texte"
"Loader enabled": "Loader aktiviert"
"Loader enabled": "Ladestandanzeige aktiviert"
"Tips": "Tipps"
"Always displayed": "Wird immer angezeigt"
"Always displayed": "Immer anzeigen"
"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"
"Live preview": "Live-Vorschau"
"Open apps in new tab": "Öffne Anwendungen in einem neuen Tab"
"Use the global setting": "Verwende die globale Einstellung"
"Use my selection": "Verwende meine Auswahl"
"Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste"
"Use the avatar instead of the logo": "Verwenden Sie den Avatar anstelle des Logos"
"You do not have permission to change the settings.": "Sie haben keine Berechtigung zum Ändern der Einstellungen."
"Force this configuration to users": "Erzwingen Sie diese Konfiguration für Benutzer"
"Export the configuration": "Exportieren Sie die Konfiguration"
"Purge the cache": "Leeren Sie den Cache"
"Show the link to settings": "Zeigen Sie den Link zu den Einstellungen an"
"The menu is enabled by default for users": "Das Menü ist standardmäßig für Benutzer aktiviert"
"Except when the configuration is forced.": "Außer wenn die Konfiguration erzwungen wird."
"Use the avatar instead of the logo": "Avatar anstelle des Logos anzeigen"
"You do not have permission to change the settings.": "Du hast keine Berechtigung, die Einstellungen dieser Anwendung zu ändern."
"Force this configuration to users": "Konfiguration für alle Benutzer erzwingen"
"Export the configuration": "Konfiguration exportieren"
"Purge the cache": "Cache leeren"
"Show the link to settings": "Link zu den Einstellungen anzeigen"
"The menu is enabled by default for users": "Das Menü ist standardmäßig für alle Benutzer aktiviert"
"Except when the configuration is forced.": "Gilt nicht, wenn die Konfiguration erzwungen wird."
"Apps that should not be displayed in the menu": "Anwendungen, die nicht im Menü angezeigt werden sollen"
"This feature is only compatible with the <code>big menu</code> display.": "Kompatibel mit dem <code>großen Menü</code>."
"The logo is a link to the default app": "Das Logo ist ein Link zur Standard-App"
"Others": "Andere"
"Categories": "Kategorien"
"Customize sorting": "Sortierung anpassen"
"Order by": "Sortieren nach"
"Name": "Name"
"Customed": "Benutzerdefiniert"
"Show and hide the list of categories": "Liste der Kategorien ein- und ausblenden"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Diese Optionen werden auf <code>Dark Theme</code> oder <code>Breeze Dark Theme</code> angewendet."
"Dark mode colors": "Farben für den dunklen Modus"
"With categories": "Mit Kategorien"
+14 -1
View File
@@ -40,7 +40,7 @@
"Panel": "Panneau"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Ouvrir le menu au passage de la souris (automatiquement désactivé sur les écrans tactiles)"
"Display the big menu": "Afficher le menu large"
"The big menu is not compatible with AppOrder.": "Le menu large n'est pas compatible avec l'application AppOrder"
"This menu is not compatible with AppOrder.": "Ce menu n'est pas compatible avec l'application AppOrder"
"Display the logo": "Afficher le logo"
"This feature is not compatible with the <code>big menu</code> display.": "Cette fonctionnalité n'est pas compatible avec l'affichage du menu large."
"Icons and texts": "Icônes et textes"
@@ -64,3 +64,16 @@
"Show the link to settings": "Afficher le lien vers les paramètres"
"The menu is enabled by default for users": "Le menu est activé par défaut pour les utilisateurs"
"Except when the configuration is forced.": "Sauf lorsque la configuration est forcée."
"Apps that should not be displayed in the menu": "Applications qui ne doivent pas être affichées dans le menu"
"This feature is only compatible with the <code>big menu</code> display.": "Compatible avec l'affichage <code>Menu large</code>."
"The logo is a link to the default app": "Le logo est un lien vers l'application par défaut"
"Others": "Autres"
"Categories": "Catégories"
"Customize sorting": "Personnaliser le tri"
"Order by": "Trier par"
"Name": "Nom"
"Customed": "Personnalisé"
"Show and hide the list of categories": "Afficher et masquer la liste des catégories"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "Ces paramètres sont utilisés lorsque le thème sombre ou le thème Breeze Dark sont activés."
"Dark mode colors": "Couleurs du mode sombre"
"With categories": "Avec les catégories"
+79
View File
@@ -0,0 +1,79 @@
"Custom menu": "自定义菜单"
"Enable the custom menu": "激活自定义菜单"
"No": "取消"
"Yes": "确定"
"Menu": "菜单"
"Use the shortcut <span class=\"keyboard-key\">Ctrl<\/span>+<span class=\"keyboard-key\">o<\/span> to open and to hide the side menu. Use <span class=\"keyboard-key\">tab<\/span> to navigate.": "使用快捷键 <span class=\"keyboard-key\">Ctrl<\/span>+<span class=\"keyboard-key\">o<\/span> 打开或隐藏侧边栏菜单。使用<span class=\"keyboard-key\">tab<\/span> 来导航。"
"Top menu": "顶部菜单"
"Apps that not must be moved in the side menu": "禁止在侧边栏菜单移动的应用"
"If there is no selection then the global configuration is applied.": "如不选择,将应用全局设定。"
"Experimental": "实验性"
"Save": "保存"
"You like this app and you want to support me?": "喜欢本应用并支持我一下?"
"Buy me a coffee ☕": "赏一杯咖啡 ☕ 给我"
"Hidden": "隐藏"
"Small": "小型"
"Normal": "标准"
"Big": "大型"
"Colors": "颜色"
"Background color": "背景颜色"
"Background color of current app": "当前应用的背景色"
"Text color": "文字颜色"
"Loader": "菜单指示器"
"Icon": "图标"
"Same color": "相同颜色"
"Opposite color": "相反颜色"
"Transparent": "透明"
"Opaque": "不透明"
"Opener": "容器"
"Default": "默认"
"Default (dark)": "默认(深色)"
"Hamburger": "Hamburger"
"Hamburger (dark)": "Hamburger (深色)"
"Hamburger 2": "Hamburger 2"
"Hamburger 2 (dark)": "Hamburger 2 (深色)"
"Before the logo": "在logo前"
"After the logo": "在logo后"
"Position": "位置"
"Show only the opener (hidden logo)": "只显示容器 (隐藏logo)"
"Do not display the side menu and the opener if there is no application (eg: public pages).": "N如果没有应用,不显示侧边栏菜单和容器 (例如 : 公共页面)。"
"Panel": "面板"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "鼠标悬停时打开菜单 (触摸屏时将自动禁用)"
"Display the big menu": "显示大型菜单"
"This menu is not compatible with AppOrder.": "型菜单与应用顺序不兼容"
"Display the logo": "显示logo"
"This feature is not compatible with the <code>big menu<\/code> display.": "此功能与显示<code>大型菜单<\/code>不兼容。"
"Icons and texts": "图标与文字"
"Loader enabled": "菜单指示器已激活"
"Tips": "技巧"
"Always displayed": "一直显示"
"The logo will be hidden when the menu is always displayed.": "一直显示菜单时logo将被隐藏。"
"This is the automatic behavior when the menu is always displayed.": "一直显示菜单时的自动动作。"
"Not compatible with touch screens.": "与触屏不兼容。"
"Big menu": "大型菜单"
"Live preview": "实时预览"
"Open apps in new tab": "在新标签中打开应用"
"Use the global setting": "使用全局设定"
"Use my selection": "使用自定义设定"
"Show and hide the list of applications": "显示或隐藏应用列表"
"Use the avatar instead of the logo": "使用头像代替logo"
"You do not have permission to change the settings.": "没有更改设置的权限。"
"Force this configuration to users": "强制用户使用此设置"
"Export the configuration": "导出设置"
"Purge the cache": "清除缓存"
"Show the link to settings": "显示设置链接"
"The menu is enabled by default for users": "用户的默认菜单已激活"
"Except when the configuration is forced.": "除非设置被强制使用。"
"Apps that should not be displayed in the menu": "禁止在菜单中显示的应用"
"This feature is only compatible with the <code>big menu<\/code> display.": "此功能只和<code>大型菜单<\/code>兼容。"
"The logo is a link to the default app": "logo链接到默认应用"
"Others": "其他"
"Categories": "类别"
"Customize sorting": "自定义顺序"
"Order by": "排序规则"
"Name": "名称"
"Customed": "自定义"
"Show and hide the list of categories": "显示或隐藏类别列表"
"This parameters are used when Dark theme or Breeze Dark Theme are enabled.": "此参数将应用于暗黑主题激活时。"
"Dark mode colors": "暗黑模式颜色"
"With categories": "有类别"
+29 -17
View File
@@ -1,21 +1,19 @@
var alwaysDisplayed = function() {
var elements = document.querySelectorAll('*');
var fixedElements = []
for (var i in elements) {
var element = elements[i]
const alwaysDisplayed = function() {
const elements = querySelectorAll('*')
const fixedElements = []
for (var element of elements) {
if (typeof element !== 'object') {
continue
}
var position = window.getComputedStyle(element, null).getPropertyValue('position');
const position = window.getComputedStyle(element, null).getPropertyValue('position')
if (position !== 'fixed') {
continue
}
var id = element.getAttribute('id')
const id = element.getAttribute('id')
if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') {
continue
@@ -25,7 +23,21 @@ var alwaysDisplayed = function() {
continue
}
if (jQuery(element).parents('#side-menu').length) {
let elementIsInSideMenu = false
let parent = element.parentNode
while (parent && !elementIsInSideMenu) {
try {
if (parent.getAttribute('id') === 'side-menu') {
elementIsInSideMenu = true
}
} catch (e) {
}
parent = parent.parentNode
}
if (elementIsInSideMenu) {
continue
}
@@ -33,19 +45,19 @@ var alwaysDisplayed = function() {
}
for (var i in fixedElements) {
var element = fixedElements[i]
var computedStyle = window.getComputedStyle(element, null)
var left = computedStyle.getPropertyValue('left')
var right = computedStyle.getPropertyValue('right')
const element = fixedElements[i]
const computedStyle = window.getComputedStyle(element, null)
const left = computedStyle.getPropertyValue('left')
const right = computedStyle.getPropertyValue('right')
if (right !== '0px') {
var intValue = parseInt(left.replace('px', ''))
element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)')
const intValue = parseInt(left.replace('px', '')) + 50
element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)')
}
}
}
let content = document.getElementById('content')
const content = querySelector('#content')
if (content && content.classList.contains('app-settings')) {
let loaded = false
@@ -56,7 +68,7 @@ if (content && content.classList.contains('app-settings')) {
}
const observer = new MutationObserver(() => {
if (loaded) {
return;
return
}
const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul')
+8 -9
View File
@@ -1,15 +1,14 @@
var pageLoader = jQuery('<div id="side-menu-loader">')
var pageLoaderBar = jQuery('<div id="side-menu-loader-bar">')
let pageLoader = createElement('div', {id: 'side-menu-loader'})
let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'})
body.append(pageLoader)
pageLoader.append(pageLoaderBar)
pageLoader.appendChild(pageLoaderBar)
querySelector('body').appendChild(pageLoader)
var pageLoaderValue = 0
$(window).on('beforeunload', function() {
setInterval(function() {
pageLoaderBar.width(pageLoaderValue.toString() + '%')
let pageLoaderValue = 0
window.addEventListener('beforeunload', () => {
setInterval(() => {
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
pageLoaderValue = Math.min(pageLoaderValue + .2, 100)
}, 25)
})
+140 -98
View File
@@ -1,32 +1,55 @@
var menuCache = null
let menuCache = null
var updateTopMenu = function() {
var breakpointMobileWidth = 1024
var menu = jQuery('#appmenu')
var apps = menu.find('li')
var minAppsDesktop = 8
var usePercentualAppMenuLimit = 0.8
var isMobile = jQuery(window).width() < breakpointMobileWidth
var lastShownApp = null
var appShown = []
var moreApps = jQuery('#more-apps')
var navigation = jQuery('#navigation')
var navigationApps = jQuery('#apps ul')
var appCount = null
const breakpointMobileWidth = 1024
const usePercentualAppMenuLimit = 0.8
const minAppsDesktop = 8
var currentMenuCache = menu.html() + menu.next().html()
const handleMenuClick = (e, icon) => {
let element = e.target
if (currentMenuCache === menuCache) {
while (element.tagName !== 'LI') {
element = element.parentNode
}
const a = querySelector('a', element)
if (a.getAttribute('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey) {
for (let tag of ['svg', 'div']) {
let el = querySelector(tag, element)
if (el) {
el.remove()
}
}
const loader = createElement('div', {'class': icon})
a.insertBefore(loader, querySelector('span', a))
}
}
const updateTopMenu = function() {
const isMobile = window.innerWidth < breakpointMobileWidth
const menu = querySelector('#appmenu')
const moreApps = querySelector('#more-apps')
const navigation = querySelector('#navigation')
const navigationApps = querySelector('#apps ul')
let apps = querySelectorAll('li', menu)
let lastShownApp = null
let appShown = []
if ((menu.innerHTML + menu.nextSibling.innerHTML) === menuCache) {
return
}
navigationApps.html('')
navigationAppsHtml = ''
apps.each(function(i, app) {
var dataId = app.getAttribute('data-id')
for (let app of apps) {
const dataId = app.getAttribute('data-id')
if (dataId === null) {
return
continue
}
if (topMenuApps.indexOf(dataId) === -1) {
@@ -35,26 +58,31 @@ var updateTopMenu = function() {
} else {
app.classList.remove('hidden')
app.classList.add('app-external-site')
appShown.push(app)
navigationApps.append(app.outerHTML)
navigationAppsHtml = navigationAppsHtml + app.outerHTML
}
if (targetBlankApps.indexOf(dataId) !== -1) {
jQuery(app).children('a').attr('target', '_blank');
querySelector('a', app).setAttribute('target', '_blank')
}
})
var rightHeaderWidth = jQuery('.header-right').outerWidth()
var headerWidth = jQuery('header').outerWidth()
var availableWidth = headerWidth - jQuery('#nextcloud').outerWidth()
- jQuery('#header .side-menu-opener').outerWidth()
- (rightHeaderWidth > 230 ? rightHeaderWidth : 230)
if (!isMobile) {
availableWidth = availableWidth * usePercentualAppMenuLimit
}
appCount = Math.floor(availableWidth / jQuery('#appmenu li').width())
navigationApps.innerHTML = navigationAppsHtml
const rightHeaderWidth = querySelector('.header-right').offsetWidth
const headerWidth = querySelector('header').offsetWidth
let availableWidth = headerWidth
availableWidth -= nextcloud.offsetWidth
availableWidth -= querySelector('#header .side-menu-opener').offsetWidth
availableWidth -= rightHeaderWidth > 230 ? rightHeaderWidth : 230
availableWidth *= isMobile ? usePercentualAppMenuLimit : 1
let appCount = Math.floor(availableWidth / querySelector('#appmenu li:not(.hidden)').offsetWidth)
if (isMobile && appCount > minAppsDesktop) {
appCount = minAppsDesktop
@@ -62,111 +90,125 @@ var updateTopMenu = function() {
appCount = minAppsDesktop
}
if (appCount === 0) {
menu.addClass('hidden')
}
menu.removeClass('hidden')
menu.css('opacity', 1)
menu.style.opacity = 1
if (appShown.length - 1 - appCount >= 1) {
appCount--
}
moreApps.find('a').removeClass('active')
for (let item of querySelectorAll('a', moreApps)) {
item.classList.remove('active')
}
var k = 0
var notInHeader = 0
var name
let k = 0
let notInHeader = 0
jQuery(appShown).each(function(i, app) {
app = jQuery(app)
name = app.data('id')
for (let app of appShown) {
const name = app.getAttribute('data-id')
const li = querySelector('#apps li[data-id=' + name + '].app-external-site')
if (k < appCount && appCount > 0) {
app.removeClass('hidden')
lastShownApp = app
app.classList.remove('hidden')
li.classList.add('in-header')
jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header')
lastShownApp = app
} else {
app.addClass('hidden')
app.classList.add('hidden')
li.classList.remove('in-header')
notInHeader++
jQuery('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header')
const a = querySelector('a', app)
if (appCount > 0 && app.children('a').hasClass('active')) {
lastShownApp.addClass('hidden')
app.removeClass('hidden')
if (appCount > 0 && a.classList.contains('active')) {
lastShownApp.classList.add('hidden')
app.classList.remove('hidden')
notInHeader++
jQuery('#apps li[data-id=' + name + '].app-external-site')
.removeClass('in-header')
.addClass('in-header')
li.classList.add('in-header')
}
}
k++
})
}
// Hack for https://github.com/nextcloud/server/blob/23b0b63c213f5b31eecae817ffd4a9e26f6624d0/core/src/components/MainMenu.js#L74-L96
menu.undelegate('li:not(#more-apps) > a', 'click')
menu.delegate('li:not(#more-apps) > a', 'click', function(e) {
var a = $(e.target)
// Hack for:
// - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
// - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119
jQuery(menu).undelegate('li:not(#more-apps) > a', 'click')
jQuery(navigation).undelegate('a', 'click')
if (!a.is('a')) {
a = a.closest('a')
const confs = [
{
items: querySelectorAll('#navigation li'),
icon: 'icon-loading-small'
},
{
items: querySelectorAll('li:not(#more-apps)', menu),
icon: OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark'
},
]
for (let conf of confs) {
for (let item of conf.items) {
item.addEventListener('click', (e) => {
handleMenuClick(e, conf.icon)
})
}
}
if (a.attr('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey && a.parent('#more-apps').length === 0) {
a.find('svg').remove()
a.find('div').remove()
a.prepend(jQuery('<div/>').addClass(
OCA.Theming && OCA.Theming.inverted
? 'icon-loading-small'
: 'icon-loading-small-dark'
))
for (let app of querySelectorAll('#apps li.app-external-site')) {
const appId = app.getAttribute('data-id')
window.location.href = a.attr('href')
}
})
jQuery('#apps li.app-external-site').each(function(i, app) {
app = jQuery(app)
var appId = app.attr('data-id')
if (app.hasClass('in-header')) {
app.find('svg').find('defs').remove()
if (app.classList.contains('in-header')) {
for (let defs of querySelectorAll('svg defs', app)) {
defs.remove()
}
} else {
var svg = app.find('svg');
const svg = querySelector('svg', app)
if (svg.find('defs').length > 0) {
return;
if (querySelectorAll('svg defs', app).length > 0) {
continue
}
var defs = `
const defs = `
<defs>
<filter id="invertMenuMore-${appId}">
<feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix>
</filter>
</defs>`
svg.prepend(defs)
svg.find('image').attr('filter', `url(#invertMenuMore-${appId})`)
svg.innerHTML = defs + svg.innerHTML
var html = svg.get(0).innerHTML.replace(/fecolormatrix/g, 'feColorMatrix');
for (let image of querySelectorAll('image', svg)) {
image.setAttribute('filter', `url(#invertMenuMore-${appId})`)
}
svg.html(html)
svg.innerHTML = svg.innerHTML.replace(/fecolormatrix/g, 'feColorMatrix')
}
})
if (notInHeader === 0) {
moreApps.hide()
navigation.hide()
} else {
moreApps.show()
}
menuCache = menu.html() + menu.next().html()
if (notInHeader === 0) {
moreApps.style.display = 'none'
navigation.style.display = 'none'
} else {
moreApps.style.display = 'flex'
}
menuCache = menu.innerHTML + menu.nextSibling.innerHTML
}
setInterval(updateTopMenu, 50)
for (let timeout of [300, 500, 700, 900, 1100]) {
setTimeout(updateTopMenu, timeout)
}
let resizeTimeout = null;
window.addEventListener('resize', () => {
if (resizeTimeout !== null) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(updateTopMenu, 100)
})
+118 -64
View File
@@ -1,59 +1,110 @@
(function() {
var sideMenuContainer = jQuery('<div id="side-menu-container">')
var sideMenuOpener = jQuery('<button class="side-menu-opener"></button>')
var sideMenu = jQuery('<div id="side-menu">')
var body = jQuery('body')
var html = jQuery('html')
var isTouchDevice = window.matchMedia("(pointer: coarse)").matches
<?php
<?php if ($_['big-menu']): ?>
sideMenu.attr('data-bigmenu', '1')
$display = 'default';
if ($_['always-displayed']) {
$display = 'always-displayed';
} elseif ($_['big-menu']) {
$display = 'big-menu';
} elseif ($_['side-with-categories']) {
$display = 'side-with-categories';
}
?>
(function() {
const querySelector = function(selector, element) {
if (element) {
return element.querySelector(selector)
}
return document.querySelector(selector)
}
const querySelectorAll = function(selector, element) {
if (element) {
return element.querySelectorAll(selector)
}
return document.querySelectorAll(selector)
}
const createElement = function(tagName, attributes) {
const element = document.createElement(tagName)
if (typeof attributes === 'object') {
for (let i in attributes) {
element.setAttribute(i, attributes[i])
}
}
return element
}
const sideMenuContainer = createElement('div', {id: 'side-menu-container'})
const sideMenuOpener = createElement('button', {'class': 'side-menu-opener'})
const sideMenu = createElement('div', {id: 'side-menu'})
const body = querySelector('body')
const html = querySelector('html')
const nextcloud = querySelector('#nextcloud')
const isTouchDevice = window.matchMedia("(pointer: coarse)").matches
const targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>
<?php if ($display === 'big-menu'): ?>
sideMenu.setAttribute('data-bigmenu', '1')
<?php elseif ($display === 'side-with-categories'): ?>
sideMenu.setAttribute('data-sidewithcategories', '1')
<?php endif; ?>
var targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>;
querySelector('body').addEventListener('side-menu.apps', (e) => {
const apps = e.detail.apps;
body.on('side-menu.apps', function(e, apps) {
<?php if ($_['hide-when-no-apps']): ?>
sideMenu = jQuery('#side-menu')
const sideMenu = querySelector('#side-menu')
if (apps.length === 0) {
sideMenu.removeClass('open')
sideMenu.addClass('hide')
sideMenuOpener.addClass('hide')
sideMenu.classList.remove('open')
sideMenu.classList.add('hide')
sideMenuOpener.classList.add('hide')
} else {
sideMenu.removeClass('hide')
sideMenuOpener.removeClass('hide')
sideMenu.classList.remove('hide')
sideMenuOpener.classList.remove('hide')
}
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
<?php if ($display === 'always-displayed'): ?>
if (apps.length === 0) {
html.removeClass('side-menu-always-displayed');
html.classList.remove('side-menu-always-displayed')
} else {
html.addClass('side-menu-always-displayed');
html.classList.add('side-menu-always-displayed')
}
<?php endif; ?>
<?php else: ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
<?php if ($display === 'always-displayed'): ?>
if (apps.length === 0) {
html.removeClass('side-menu-always-displayed');
html.classList.remove('side-menu-always-displayed')
} else {
html.addClass('side-menu-always-displayed');
html.classList.add('side-menu-always-displayed')
}
<?php endif; ?>
<?php endif; ?>
})
body.on('side-menu.ready', function() {
sideMenu = jQuery('#side-menu')
body.addEventListener('side-menu.ready', () => {
const sideMenu = querySelector('#side-menu')
const headerMenuOpener = querySelector('#header .side-menu-opener')
const sideMenuOpener = querySelectorAll('#side-menu .side-menu-opener')
var headerMenuOpener = jQuery('#header .side-menu-opener')
var sideMenuOpener = jQuery('#side-menu .side-menu-opener')
sideMenuFocus = () => {
let a = querySelector('.side-menu-app.active a', sideMenu)
sideMenuFocus = function() {
var a = sideMenu.find('.side-menu-app.active a')
if (!a) {
return
}
if (a.length === 0) {
a = sideMenu.find('.side-menu-app:first-child a')
a = querySelector('.side-menu-app:first-child a', sideMenu)
}
if (a.length > 0) {
@@ -61,84 +112,87 @@
}
}
<?php if ($_['opener-hover'] || ($_['always-displayed'] && !$_['big-menu'])): ?>
var sideMenuMouseLeave = function() {
sideMenu
.removeClass('open')
.off('mouseleave', sideMenuMouseLeave)
<?php if ($_['opener-hover']): ?>
const sideMenuMouseLeave = () => {
sideMenu.classList.remove('open')
sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave)
}
var sideMenuMouseEnter = function() {
sideMenu.on('mouseleave', sideMenuMouseLeave)
const sideMenuMouseEnter = () => {
sideMenu.addEventListener('mouseleave', sideMenuMouseLeave)
}
var sideMenuOpenerMouseEnter = function() {
sideMenu
.addClass('open')
.on('mouseenter', sideMenuMouseEnter)
const sideMenuOpenerMouseEnter = () => {
sideMenu.classList.add('open')
sideMenu.addEventListener('mouseenter', sideMenuMouseEnter)
sideMenuFocus()
}
if (!isTouchDevice) {
<?php if ($_['opener-hover']): ?>
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter)
headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter)
sideMenu.addClass('hide-opener')
sideMenu.classList.add('hide-opener')
<?php endif ?>
sideMenu.on('mouseleave', sideMenuMouseLeave)
sideMenu.on('mouseenter', sideMenuOpenerMouseEnter)
sideMenu.addEventListener('mouseleave', sideMenuMouseLeave)
sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter)
}
<?php endif; ?>
headerMenuOpener.on('click', function() {
sideMenu.addClass('open')
sideMenu.find('.side-menu-app.active a').focus()
headerMenuOpener.addEventListener('click', () => {
sideMenu.classList.add('open')
const a = querySelector('.side-menu-app.active a', sideMenu)
if (a !== null) {
a.focus()
}
})
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
sideMenuOpener.on('click', function() {
sideMenu.toggleClass('open')
for (let opener of sideMenuOpener) {
opener.addEventListener('click', () => {
<?php if ($display === 'always-displayed'): ?>
sideMenu.classList.toggle('open')
<?php else: ?>
sideMenu.classList.remove('open')
<?php endif; ?>
})
<?php else: ?>
sideMenuOpener.on('click', function() {
sideMenu.removeClass('open')
})
<?php endif; ?>
}
jQuery(document).keydown(function(e) {
document.addEventListener('keydown', (e) => {
var key = e.key || e.keyCode
if ((key === 'o' || key === 79) && e.ctrlKey === true) {
e.preventDefault()
sideMenu.toggleClass('open')
sideMenu.classList.toggle('open')
sideMenuFocus()
}
})
})
body.append(sideMenuContainer)
sideMenuContainer.append(sideMenu)
body.appendChild(sideMenuContainer)
sideMenuContainer.appendChild(sideMenu)
<?php if ($_['loader-enabled'] === true): ?>
<?php require_once __DIR__.'/_loaderEnabled.js'; ?>
<?php endif; ?>
<?php if ($_['opener-position'] === 'before'): ?>
sideMenuOpener.insertBefore('#nextcloud')
nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud)
<?php else: ?>
sideMenuOpener.insertAfter('#nextcloud')
nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling)
<?php endif; ?>
<?php if (!empty($_['top-menu-apps'])): ?>
var topMenuApps = <?php echo json_encode($_['top-menu-apps']); ?>;
const topMenuApps = <?php echo json_encode($_['top-menu-apps']); ?>
<?php require_once __DIR__.'/_topMenuApps.js'; ?>
<?php endif; ?>
<?php if ($_['always-displayed'] && !$_['big-menu']): ?>
<?php if ($display === 'always-displayed'): ?>
<?php require_once __DIR__.'/_alwaysDisplayed.js'; ?>
<?php endif; ?>
})();
File diff suppressed because it is too large Load Diff
-2
View File
@@ -44,8 +44,6 @@ $choicesYesNo = [
<?php p($l->t('Menu')); ?>
</h2>
<div>
<label for="side-menu-enabled">
<?php p($l->t('Enable the custom menu')); ?>
+2
View File
File diff suppressed because one or more lines are too long