diff --git a/.gitea/issue_template/FEATURE_TEMPLATE.md b/.gitea/issue_template/FEATURE_TEMPLATE.md deleted file mode 100644 index ed2269a..0000000 --- a/.gitea/issue_template/FEATURE_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: "New feature" -about: "Use this template if you want to request a feature" -title: "[FEATURE] " -labels: - - enhancement ---- -## Feature - -### Description - -... - -### Benefits - -... diff --git a/.gitea/issue_template/FEATURE_TEMPLATE.yml b/.gitea/issue_template/FEATURE_TEMPLATE.yml new file mode 100644 index 0000000..872865f --- /dev/null +++ b/.gitea/issue_template/FEATURE_TEMPLATE.yml @@ -0,0 +1,34 @@ +name: New feature +about: Use this template if you want to request a feature +title: "[FEATURE] " +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! + + - type: textarea + id: description + attributes: + label: Description + description: Describe the feature. + validations: + required: true + + - type: textarea + id: benefits + attributes: + label: Benefits + description: Describe the benefits of this feature. + validations: + required: true + + - type: textarea + id: extra + attributes: + label: More informations + description: If you want to share more things, this is here! + validations: + required: false diff --git a/.gitea/issue_template/ISSUE_TEMPLATE.md b/.gitea/issue_template/ISSUE_TEMPLATE.md deleted file mode 100644 index f970300..0000000 --- a/.gitea/issue_template/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -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 - -... diff --git a/.gitea/issue_template/ISSUE_TEMPLATE.yml b/.gitea/issue_template/ISSUE_TEMPLATE.yml new file mode 100644 index 0000000..96e6c28 --- /dev/null +++ b/.gitea/issue_template/ISSUE_TEMPLATE.yml @@ -0,0 +1,69 @@ +name: New issue +about: Use this template if you have a bug +title: "[Bug] " +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + - type: textarea + id: environment + attributes: + label: Environment + value: | + * Custom menu version: + * Nextcloud version: + * PHP version: + * Web server (Nginx, Apache2): + * Web browser and version (Firefox 80, Google Chrome 74, etc): + validations: + required: true + + - type: textarea + id: configuration + attributes: + label: Configuration + description: Export the configuration using the admin page and copy/paste here ([documentation](https://deblan.gitnet.page/side_menu_doc/tips/#export-the-configuration)). + value: | + ``` + { + ... + } + ``` + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: How reproduce the bug? + validations: + required: false + + - type: textarea + id: resuts + attributes: + label: Observed Results + description: What happened? + validations: + required: false + + - type: textarea + id: expected + attributes: + label: Expected Results + description: What should happen? + validations: + required: false + + - type: textarea + id: extra + attributes: + label: More informations + description: If you want to share more things, this is here! + validations: + required: false diff --git a/.gitignore b/.gitignore index bfc782e..d10e458 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /js -/assets /node_modules /l10n/* /releases diff --git a/.woodpecker.yml b/.woodpecker.yml index aee34a9..34f13eb 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,25 +1,78 @@ pipeline: dependencies: - image: gitnet.fr/deblan/devenv + image: node:16 + pull: true commands: - - make dep + - npm i when: event: [tag, push, pull_request] - branch: [master, develop, feature/*] + branch: [master, develop, feature/*, fix/*, translations] - build: - image: gitnet.fr/deblan/devenv + osv-detector: + image: gitnet.fr/deblan/osv-detector:v0.10 commands: - - make build - when: - event: [push, pull_request] + - osv-detector package-lock.json + failure: ignore - package: - image: gitnet.fr/deblan/devenv + build-js: + image: node:16 + commands: + - npm run build + when: + branch: [master, develop, feature/*, fix/*, translations] + event: [tag, push, pull_request] + + build-translations: + image: deblan/php:8.0 + commands: + - php bin/generate_l10n.php + when: + branch: [master, develop, feature/*, fix/*, translations] + event: [tag, push, pull_request] + + create-signature: + image: nextcloud:25 + secrets: [app_certificate, app_public_certificate] + environment: + SQLITE_DATABASE: /var/www/data/data.db + NEXTCLOUD_ADMIN_USER: admin + NEXTCLOUD_ADMIN_PASSWORD: admin + commands: + - echo "$APP_CERTIFICATE" > "/tmp/side_menu.key" + - echo "$APP_PUBLIC_CERTIFICATE" > "/tmp/side_menu.crt" + - mkdir /tmp/app + - cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots vendor /tmp/app + - /usr/src/nextcloud/occ integrity:sign-app + --privateKey=/tmp/side_menu.key + --certificate=/tmp/side_menu.crt + --path=/tmp/app + - mv /tmp/app/appinfo/signature.json appinfo/ + when: + event: [tag] + + # check-code-quality: + # image: sonarsource/sonar-scanner-cli + # secrets: [sonar_token, sonar_host, sonar_project] + # commands: + # - sonar-scanner + # -Dsonar.projectKey=$SONAR_PROJECT + # -Dsonar.sources=. + # -Dsonar.host.url=$SONAR_HOST + # -Dsonar.pullrequest.key=$CI_COMMIT_PULL_REQUEST + # -Dsonar.pullrequest.branch=$CI_COMMIT_SOURCE_BRANCH + # -Dsonar.pullrequest.base=$CI_COMMIT_TARGET_BRANCH + # failure: ignore + # when: + # event: [pull_request] + + create-package: + image: deblan/php:8.0 volumes: - /var/www/html/artifacts:/var/www/html/artifacts secrets: [app_certificate] commands: + - apt-get update + - apt-get install -y zip make - mkdir -p "$HOME/.nextcloud/certificates" - echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key" - export VERSION=$(grep "" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never) @@ -28,7 +81,7 @@ pipeline: when: event: [tag] - release: + push-release: image: plugins/gitea-release volumes: - /var/www/html/artifacts:/var/www/html/artifacts diff --git a/CHANGELOG.md b/CHANGELOG.md index e834f32..d59a7d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,98 @@ ## [Unreleased] +## 3.9.1 +### Fixed +* fix fixed menu on dashboard (#262) + +## 3.9.0 +### Added +* add compatibility with NC27 +### Fixed +* fix app redirect (#261) + +## 3.8.0 +### Added +* add option to show hovered label only on top menu (fix #253) + +## 3.7.4 +### Fixed +* fix Integrity failed (#247) + +## 3.7.3 +### Fixed +* fix #244: use app href for redirection +### Added +* add signature on build + +## 3.7.2 +### Added +* update pipeline conditions allowing `fix/*` +### Fixed +* fix #233: load configuration and then retrieve apps in default side menu display + +## 3.7.1 +### Fixed +* fix build process (#230) + +## 3.7.0 +### Added +* add translations (thanks to AHOHNMYC) +* add compatibility with NC26 + +## 3.6.0 +### Added +* add hidden apps compatible with default menu (#219) + +## 3.5.2 +### Fixed +* add check if menu exists before adding event listeners (#210) + +## 3.5.1 +### Added +* add translations (thanks to p-bo adn gallegonovato) +### Fixed +* fix #189: sorting not applied on mobile + +## 3.5.0 +### Added +* add dependency check (ci) +* add code quality check (ci) +* add translations (thanks to gallegonovato) +* add option to disable the display labels in the top menu (#194) +### Fixed +* fix missing img alt (settings image) +* fix code quality alerts + +## 3.4.1 +### Added +* add translations (thanks to zonorti, jorisvandijk, jak2k) +### Fixed +* fix #183: hide custom categories list when empty (admin page) + +## 3.4.0 +### Added +* add translations (thanks to Pavelb, nier, Timur, p-bo) +* add possibility to define Custom Menu as default app and redirect to the first top menu app (#177) + +## 3.3.2 +### Fixed +* fix #173: reduce the height of categories list + +## 3.3.1 +### Fixed +* fix #162: top and side apps does work correctly + +## 3.3.0 +### Added +* add documentation in admin page +* add app sorter in user config side (#160) +### Fixed +* fix #164: open apps in new tab does not work +* fix #162 #159: top and side apps does work correctly + ## 3.2.1 ### Fixed -* fix #150: Active app is not visible has active in menu (except in default menu) +* fix #150: active app is not visible has active in menu (except in default menu) * fix #151: opener position ## 3.2.0 diff --git a/Makefile b/Makefile index 966581f..e5b6267 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ dep: npm link @nextcloud/vue || sudo npm link @nextcloud/vue .ONESHELL: -release: build translations +release: if [ -z "$$VERSION" ]; then echo "VERSION required" exit 1 @@ -34,4 +34,4 @@ translations: .ONESHELL: run-code-quality-analysis: export SONAR_TOKEN="$$SONAR_TOKEN_DEBLAN_SIDE_MENU" - sonar-scanner -Dsonar.projectKey=deblan-side_menu -Dsonar.sources=. -Dsonar.host.url=https://cq.gitnet.fr + sonar-scanner -Dsonar.projectKey=deblan-side_menu -Dsonar.sources=. -Dsonar.host.url=$$SONAR_SERVER -Dsonar.branch.name=$$(git branch --show-current) diff --git a/README.md b/README.md index b1686e0..bf8abbb 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://ci.gitnet.fr/api/badges/deblan/side_menu/status.svg)](https://ci.gitnet.fr/deblan/side_menu) [![Translations](https://translate.codeberg.org/widgets/custom-menu/-/application/svg-badge.svg)](https://translate.codeberg.org/engage/custom-menu/) +![Downloads](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.K_downloads&suffix=K&url=https%3A%2F%2Fapi-side-menu.deblan.org%2Fdownloads.php) Allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top. You can also add and sort custom categories, define apps that must be displayed in the top menu, etc. Fully customisable. @@ -18,10 +19,12 @@ You can customize colors depending of the theme (Dark theme and Breeze Dark). 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) +## [📘 Read the documentation](https://deblan.gitnet.page/side_menu_doc/) + Requirements ------------ -* PHP >= 7.4 +* PHP >= 8.0 * App `theming` enabled Installation and upgrade @@ -47,6 +50,16 @@ Users can disable the menu using the page of personal settings. Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate. +### Use first top menu app as default app + +You can easily let Custom Menu redirect to the first app in the top menu by changing the following parameter in your `config/config.php`: + +``` +'defaultapp' => 'side_menu', +``` + +If the top menu is empty then it redirects to files. + How to contribute? ------------------ diff --git a/appinfo/info.xml b/appinfo/info.xml index 6e60309..4acf6f4 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -17,7 +17,7 @@ You can report a bug or request a feature by opening an issue. Requirements: -* PHP >= 7.4 +* PHP >= 8.0 * App `theming` enabled If you like this application and if you want to support the development: @@ -32,12 +32,12 @@ Notice Because I believe in a free and decentralized Internet, [Gitnet](https://gitnet.fr) is **self-hosted at home**. In case of downtime, you can download **Custom Menu** from [here](https://kim.deblan.fr/~side_menu/). ]]> - 3.2.1 + 3.9.1 agpl Simon Vieille SideMenu - https://gitnet.fr/deblan/side_menu/src/branch/master/README.md + https://deblan.gitnet.page/side_menu_doc/ https://gitnet.fr/deblan/side_menu/src/branch/master/README.md customization @@ -54,7 +54,7 @@ In case of downtime, you can download **Custom Menu** from [here](https://kim.de https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc25_big_menu.png https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc25_default_menu.png - + diff --git a/appinfo/routes.php b/appinfo/routes.php index 1efd4b6..bdc3fba 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -19,6 +19,7 @@ return [ 'routes' => [ + ['name' => 'App#index', 'url' => '/', 'verb' => 'GET'], ['name' => 'Css#stylesheet', 'url' => '/css/stylesheet', 'verb' => 'GET'], ['name' => 'Js#script', 'url' => '/js/script', 'verb' => 'GET'], ['name' => 'Js#config', 'url' => '/js/config', 'verb' => 'GET'], diff --git a/src/css/admin.scss b/css/admin.css similarity index 83% rename from src/css/admin.scss rename to css/admin.css index 086d0d7..b3d0b7c 100644 --- a/src/css/admin.scss +++ b/css/admin.css @@ -73,7 +73,7 @@ .side-menu-setting-list { margin: 10px 4px 4px 0px; - border: 1px solid var(--color-border-dark); + border: 2px solid var(--color-border-dark); border-radius: 15px; } @@ -137,6 +137,10 @@ padding-right: 20px; } +.side-menu-setting-label--top { + vertical-align: top; +} + .side-menu-setting-form { display: table-cell; min-width: 300px; @@ -175,3 +179,42 @@ .btn-reset--progress { transform: rotate(-359deg); } + +.badges { + margin-bottom: 14px; + margin-top: 4px; +} + +.badge { + border-width: 1px; + padding: 2px 8px; + margin-right: 2px; + margin-bottom: 5px; + display: inline-block; + border-radius: 4px; + font-size: 13px; +} + +.badge-1 { + background: #d4ce14; + border-color: #cad413; + color: #373a05; +} + +.badge-2 { + background: #96d47f; + border-color: #7ed49b; + color: #333; +} + +.badge-3 { + background: #d4540a; + border-color: #d4700c; + color: #fff; +} + +.badge-4 { + background: #9d81d4; + border-color: #c681d4; + color: #fff; +} diff --git a/src/css/menu.scss b/css/sideMenu.css similarity index 94% rename from src/css/menu.scss rename to css/sideMenu.css index 5e73d40..7fc71a2 100644 --- a/src/css/menu.scss +++ b/css/sideMenu.css @@ -73,7 +73,7 @@ } .side-menu-opener { - background: var(--side-menu-opener, url('../../img/side-menu-opener.svg')); + background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); background-color: transparent !important; height: 40px !important; width: 40px !important; @@ -90,7 +90,7 @@ } .side-menu-closer { - background: url('../../img/side-menu-opener-closer.svg'); + background: url('../img/side-menu-opener-closer.svg'); display: none; } @@ -113,7 +113,7 @@ vertical-align: middle; margin-top: -4px; margin-right: 10px; - /*filter: invert(var(--side-menu-icon-invert-filter, 0%));*/ + filter: invert(var(--side-menu-icon-invert-filter, 0%)); opacity: var(--side-menu-icon-opacity, 1); } @@ -198,7 +198,7 @@ } .side-menu-categories { - max-height: calc(100vh - 5px); + max-height: calc(100vh - 55px); overflow: auto; position: relative; top: 5px; @@ -235,21 +235,27 @@ .side-menu-always-displayed body { width: calc(100% - 50px) !important; -} - -.side-menu-always-displayed body { position: absolute; left: 50px; } +.side-menu-always-displayed #header { + position: absolute !important; +} + .side-menu-always-displayed #side-menu { display: block; } .side-menu-always-displayed .side-menu-apps-list { + height: 100vh; + top: 0; + overflow: hidden; +} + +.side-menu-always-displayed .side-menu-apps-list--with-settings { height: calc(100vh - 49px); top: 49px; - overflow: hidden; } .side-menu-always-displayed .side-menu-apps-list:hover { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 6336b88..b812fd7 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -8,13 +8,13 @@ use OC\User\User; use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\CategoryRepository; use OCA\SideMenu\Service\ConfigProxy; -use OCA\SideMenu\Util\AssetUtil; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\IUserSession; use OCP\Util; +use Psr\Container\ContainerInterface; /** * class Application. @@ -72,27 +72,21 @@ class Application extends App implements IBootstrap protected function addAssets() { - AssetUtil::getInstance() - // Add runtime.js - ->addEntrypointAsset('menu', 'js', 0) - // Add menu.js - ->addEntrypointAsset('menu', 'js', 1) - // Add menu.css - ->addEntrypointAsset('menu', 'css', 0) - ; + Util::addScript(self::APP_ID, 'sideMenu'); + Util::addStyle(self::APP_ID, 'sideMenu'); $assets = [ - [ - 'type' => 'link', + 'stylesheet' => [ 'route' => 'side_menu.Css.stylesheet', + 'type' => 'link', 'route_attr' => 'href', 'attr' => [ 'rel' => 'stylesheet', ], ], - [ - 'type' => 'script', + 'script' => [ 'route' => 'side_menu.Js.script', + 'type' => 'script', 'route_attr' => 'src', 'attr' => [ 'nonce' => $this->cspnm->getNonce(), @@ -105,6 +99,7 @@ class Application extends App implements IBootstrap foreach ($assets as $value) { $route = OC::$server->getURLGenerator()->linkToRoute($value['route'], ['v' => $cache]); $value['attr'][$value['route_attr']] = $route; + Util::addHeader($value['type'], $value['attr'], ''); } } diff --git a/lib/Controller/AppController.php b/lib/Controller/AppController.php new file mode 100644 index 0000000..588911f --- /dev/null +++ b/lib/Controller/AppController.php @@ -0,0 +1,110 @@ +. + */ + +namespace OCA\SideMenu\Controller; + +use OC; +use OCA\SideMenu\Service\AppRepository; +use OCA\SideMenu\Service\ConfigProxy; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\RedirectResponse; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\IUserSession; + +class AppController extends Controller +{ + /** + * @var ConfigProxy + */ + protected $config; + + /** + * @var AppRepository + */ + protected $appRepository; + + public function __construct( + string $appName, + IRequest $request, + AppRepository $appRepository, + IURLGenerator $urlGenerator, + ConfigProxy $config + ) { + parent::__construct($appName, $request); + + $this->appRepository = $appRepository; + $this->urlGenerator = $urlGenerator; + $this->config = $config; + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function index(): RedirectResponse + { + $user = OC::$server[IUserSession::class]->getUser(); + $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); + $hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]'); + $isForced = $this->config->getAppValueBool('force', '0'); + $userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]'); + $apps = $this->appRepository->getOrderedApps($user); + + if (!$isForced && !empty($userTopMenuApps)) { + $topMenuApps = $userTopMenuApps; + } + + foreach ($apps as $app) { + $inTopMenuApps = in_array($app['id'], $topMenuApps); + $inHiddenApps = in_array($app['id'], $hiddenApps); + + if (!$inTopMenuApps && $inHiddenApps) { + continue; + } + + return $this->redirectToApp($app, true); + } + + return $this->redirectToApp('files'); + } + + protected function redirectToApp($app, bool $isHref = false): RedirectResponse + { + if (!$isHref) { + $isIgnoreFrontController = true === OC::$server->getConfig()->getSystemValue( + 'htaccess.IgnoreFrontController', + false + ); + + $isFrontControllerActive = 'true' === getenv('front_controller_active'); + + if ($isIgnoreFrontController || $isFrontControllerActive) { + $path = '/apps/%s/'; + } else { + $path = '/index.php/apps/%s/'; + } + + $url = $this->urlGenerator->getAbsoluteURL(sprintf($path, $app)); + } else { + $url = $app['href']; + } + + return new RedirectResponse($url); + } +} diff --git a/lib/Controller/CssController.php b/lib/Controller/CssController.php index 028ac11..06f7ae8 100644 --- a/lib/Controller/CssController.php +++ b/lib/Controller/CssController.php @@ -21,14 +21,14 @@ namespace OCA\SideMenu\Controller; use OC; use OC\User\User; use OCA\SideMenu\AppInfo\Application; +use OCA\SideMenu\Service\Color; use OCA\SideMenu\Service\ConfigProxy; +use OCA\Theming\ThemingDefaults; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\IRequest; use OCP\IUserSession; -use OCA\Theming\ThemingDefaults; -use OCA\SideMenu\Service\Color; class CssController extends Controller { @@ -58,8 +58,7 @@ class CssController extends Controller ConfigProxy $config, ThemingDefaults $theming, Color $color - ) - { + ) { parent::__construct($appName, $request); $this->user = OC::$server[IUserSession::class]->getUser(); @@ -105,16 +104,18 @@ class CssController extends Controller $topSideMenuApps = $userTopSideMenuApps; } - $isDarkThemeUserEnabled = $this->config->getUserValue($this->user, 'theme', '', 'accessibility') === 'dark'; + $isDarkThemeUserEnabled = 'dark' === $this->config->getUserValue($this->user, 'theme', '', 'accessibility'); $isBreezeDarkUserEnabled = $this->config->getUserValue($this->user, 'theme_enabled', '', 'breezedark'); - $isBreezeDarkUserEnabled = $isBreezeDarkUserEnabled === '1' || ($isBreezeDarkGlobalEnabled && $isBreezeDarkUserEnabled === ''); + $isBreezeDarkUserEnabled = '1' === $isBreezeDarkUserEnabled || + ($isBreezeDarkGlobalEnabled && '' === $isBreezeDarkUserEnabled); } else { $isDarkThemeUserEnabled = false; $isBreezeDarkUserEnabled = false; } - $isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled) || ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled); + $isDarkMode = ($isAccessibilityAppEnabled && $isDarkThemeUserEnabled) || + ($isBreezeDarkAppEnabled && $isBreezeDarkUserEnabled); $primaryColor = $this->theming->getColorPrimary(); $lightenPrimaryColor = $this->color->adjustBrightness($primaryColor, 0.2); @@ -125,25 +126,33 @@ class CssController extends Controller if ($isDarkMode) { $backgroundColor = $this->config->getAppValue('dark-mode-background-color', $darkenPrimaryColor); $backgroundColorTo = $this->config->getAppValue('dark-mode-background-color-to', $darkenPrimaryColor); - $currentAppBackgroundColor = $this->config->getAppValue('dark-mode-current-app-background-color', $darkenPrimaryColor2); + $currentAppBackgroundColor = $this->config->getAppValue( + 'dark-mode-current-app-background-color', + $darkenPrimaryColor2 + ); $loaderColor = $this->config->getAppValue('dark-mode-loader-color', $lightenPrimaryColor); $textColor = $this->config->getAppValue('dark-mode-text-color', $textColor); $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); + $opacity = $this->config->getAppValueInt('dark-mode-background-color-opacity', '100'); + $backgroundOpacity = dechex($opacity * 255 / 100); } else { $backgroundColor = $this->config->getAppValue('background-color', $darkenPrimaryColor); $backgroundColorTo = $this->config->getAppValue('background-color-to', $darkenPrimaryColor); - $currentAppBackgroundColor = $this->config->getAppValue('current-app-background-color', $darkenPrimaryColor2); + $currentAppBackgroundColor = $this->config->getAppValue( + 'current-app-background-color', + $darkenPrimaryColor2 + ); $loaderColor = $this->config->getAppValue('loader-color', $lightenPrimaryColor); $textColor = $this->config->getAppValue('text-color', $textColor); $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); + $opacity = $this->config->getAppValueInt('background-color-opacity', '100'); + $backgroundOpacity = dechex($opacity * 255 / 100); } $backgroundColor .= $backgroundOpacity; diff --git a/lib/Controller/JsController.php b/lib/Controller/JsController.php index 32cd630..13d803e 100644 --- a/lib/Controller/JsController.php +++ b/lib/Controller/JsController.php @@ -95,15 +95,16 @@ class JsController extends Controller { $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]'); - $topMenuAppsOrder = $this->config->getAppValueArray('top-menu-apps-order', '[]'); $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'); + $appsOrder = $this->config->getAppValueArray('apps-order', '[]'); $avatar = null; $settings = null; if ($this->user) { + $userAppsOrder = $this->config->getUserValueArray($this->user, 'apps-order', '[]'); $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); $userTopSideMenuApps = $this->config->getUserValueArray($this->user, 'top-side-menu-apps', '[]'); @@ -115,6 +116,10 @@ class JsController extends Controller $topSideMenuApps = $userTopSideMenuApps; } + if (!empty($userAppsOrder) && !$isForced) { + $appsOrder = $userAppsOrder; + } + $userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1'); $userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]'); @@ -162,10 +167,14 @@ class JsController extends Controller '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', '[]'), - 'apps-order' => $this->config->getAppValueArray('apps-order', '[]'), + 'apps-order' => $appsOrder, 'avatar' => $avatar, 'top-menu-apps' => $topMenuApps, 'top-side-menu-apps' => $topSideMenuApps, + 'top-menu-mouse-over-hidden-label' => $this->config->getAppValueInt( + 'top-menu-mouse-over-hidden-label', + '0' + ), 'target-blank-apps' => $targetBlankApps, 'settings' => $settings, 'logo' => $this->themingDefaults->getLogo(), diff --git a/lib/Controller/NavController.php b/lib/Controller/NavController.php index ac766a5..50efc4c 100644 --- a/lib/Controller/NavController.php +++ b/lib/Controller/NavController.php @@ -93,11 +93,13 @@ class NavController extends Controller ]); } - $apps = $this->appRepository->getOrderedApps(); + $apps = $this->appRepository->getOrderedApps($user); $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', '[]'); + $topSideMenuApps = $this->config->getAppValueArray('top-side-menu-apps', '[]'); + $userTopSideMenuApps = $this->config->getUserValueArray($user, 'top-side-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]'); $appsCategories = []; $categoriesAppsCount = []; @@ -106,12 +108,16 @@ class NavController extends Controller $topMenuApps = $userTopMenuApps; } - foreach ($apps as $app) { - if (in_array($app['id'], $topMenuApps)) { - continue; - } + if (!$isForced && !empty($userTopSideMenuApps)) { + $topSideMenuApps = $userTopSideMenuApps; + } - if (in_array($app['id'], $hiddenApps)) { + foreach ($apps as $app) { + $inTopMenuApps = in_array($app['id'], $topMenuApps); + $inTopSideMenuApps = in_array($app['id'], $topSideMenuApps); + $inHiddenApps = in_array($app['id'], $hiddenApps); + + if (($inTopMenuApps && !$inTopSideMenuApps) || $inHiddenApps) { continue; } diff --git a/lib/Controller/PersonalSettingController.php b/lib/Controller/PersonalSettingController.php index bdc9fca..fc30c35 100644 --- a/lib/Controller/PersonalSettingController.php +++ b/lib/Controller/PersonalSettingController.php @@ -43,8 +43,13 @@ class PersonalSettingController extends Controller */ protected $userSession; - public function __construct($appName, IRequest $request, IConfig $config, ConfigProxy $configProxy, IUserSession $userSession) - { + public function __construct( + $appName, + IRequest $request, + IConfig $config, + ConfigProxy $configProxy, + IUserSession $userSession + ) { parent::__construct($appName, $request); $this->config = $config; @@ -97,7 +102,7 @@ class PersonalSettingController extends Controller } } - if (in_array($name, ['top-menu-apps', 'top-side-menu-apps'])) { + if (in_array($name, ['top-menu-apps', 'top-side-menu-apps', 'apps-order'])) { $doSave = true; $data = json_decode($value, true); diff --git a/lib/Service/AppRepository.php b/lib/Service/AppRepository.php index 2ae59d5..ce96107 100644 --- a/lib/Service/AppRepository.php +++ b/lib/Service/AppRepository.php @@ -2,6 +2,7 @@ namespace OCA\SideMenu\Service; +use OC\User\User; use OCP\L10N\IFactory; /** @@ -110,11 +111,19 @@ class AppRepository ); } - public function getOrderedApps() + public function getOrderedApps(?User $user = null) { $apps = $this->getVisibleApps(); $orders = $this->config->getAppValueArray('apps-order', '[]'); + if (null !== $user && !$this->config->getAppValueBool('force', '0')) { + $userOrders = $this->config->getUserValueArray($user, 'apps-order', '[]'); + + if (!empty($userOrders)) { + $orders = $userOrders; + } + } + usort($apps, function ($a, $b) use ($orders) { $ak = array_keys($orders, $a['id'])[0] ?? null; $bk = array_keys($orders, $b['id'])[0] ?? null; diff --git a/lib/Service/CategoryRepository.php b/lib/Service/CategoryRepository.php index 7559854..c146609 100644 --- a/lib/Service/CategoryRepository.php +++ b/lib/Service/CategoryRepository.php @@ -74,7 +74,8 @@ class CategoryRepository } foreach ($categoriesLabels as $k => $category) { - $categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? $category['translations']['en']['name']; + $categoriesLabels[$category['id']] = $category['translations'][$currentLanguage]['name'] ?? + $category['translations']['en']['name']; unset($categoriesLabels[$k]); } diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 12be78a..86ddac4 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -125,15 +125,24 @@ class Admin implements ISettings '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', $darkenPrimaryColor2), + 'current-app-background-color' => $this->config->getAppValue( + 'current-app-background-color', + $darkenPrimaryColor2 + ), 'loader-color' => $this->config->getAppValue('loader-color', $lightenPrimaryColor), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'), 'text-color' => $this->config->getAppValue('text-color', $textColor), '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', $darkenPrimaryColor2), + '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', + $darkenPrimaryColor2 + ), 'dark-mode-loader-color' => $this->config->getAppValue('dark-mode-loader-color', $textColor), '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'), @@ -159,6 +168,10 @@ class Admin implements ISettings 'force' => $this->config->getAppValue('force', '0'), 'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'), 'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'), + 'top-menu-mouse-over-hidden-label' => $this->config->getAppValue( + 'top-menu-mouse-over-hidden-label', + '0' + ), 'apps-order' => $this->config->getAppValueArray('apps-order', '[]'), 'ordered-apps' => $this->appRepository->getOrderedApps(), 'top-side-menu-apps' => $this->config->getAppValueArray('top-side-menu-apps', '[]'), diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index 9ac24c7..57112d0 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -54,8 +54,13 @@ class Personal implements ISettings */ private $appRepository; - public function __construct(IL10N $l, ILogger $logger, ConfigProxy $config, IUserSession $userSession, AppRepository $appRepository) - { + public function __construct( + IL10N $l, + ILogger $logger, + ConfigProxy $config, + IUserSession $userSession, + AppRepository $appRepository + ) { $this->l = $l; $this->logger = $logger; $this->config = $config; @@ -81,7 +86,9 @@ class Personal implements ISettings 'top-side-menu-apps' => $this->config->getUserValueArray($user, 'top-side-menu-apps', '[]'), 'target-blank-mode' => $this->config->getUserValue($user, 'target-blank-mode', '1'), 'target-blank-apps' => $this->config->getUserValueArray($user, 'target-blank-apps', '[]'), + 'apps-order' => $this->config->getUserValueArray($user, 'apps-order', '[]'), 'apps' => $this->appRepository->getVisibleApps(), + 'ordered-apps' => $this->appRepository->getOrderedApps($user), ]; return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, ''); diff --git a/lib/Util/AssetUtil.php b/lib/Util/AssetUtil.php deleted file mode 100644 index 9232d10..0000000 --- a/lib/Util/AssetUtil.php +++ /dev/null @@ -1,64 +0,0 @@ - - */ -class AssetUtil -{ - protected array $entrypoints; - protected static ?AssetUtil $instance = null; - - public function __construct() - { - $this->entrypoints = json_decode( - file_get_contents(__DIR__.'/../../assets/entrypoints.json'), - true - )['entrypoints']; - } - - public static function getInstance(): self - { - if (null === self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - public function addEntrypointAsset($entry, $type, $key): self - { - $file = basename($this->entrypoints[$entry][$type][$key]); - $path = $this->generatePath('assets', $file); - - return $this->addAsset($type, $path); - } - - public function addAsset($type, $path): self - { - $path = preg_replace('/\.(css|js)$/', '', $path); - - if ('css' === $type) { - OC_Util::$styles[] = $path; - } elseif ('js' === $type) { - OC_Util::$scripts[] = $path; - } - - return $this; - } - - public function generatePath($directory, $file): string - { - return implode('/', [ - Application::APP_ID, - $directory, - $file, - ]); - } -} diff --git a/package.json b/package.json index a284b60..503c7d1 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "license": "agpl", "private": true, "scripts": { - "build": "./node_modules/.bin/encore prod", - "dev": "./node_modules/.bin/encore dev", - "watch": "./node_modules/.bin/encore dev --watch", + "build": "NODE_ENV=production ./node_modules/.bin/webpack-cli --progress --config webpack.js", + "dev": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --config webpack.js", + "watch": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --watch --config webpack.js", "lint": "./node_modules/.bin/eslint --ext .js,.vue src", "lint:fix": "./node_modules/.bin/eslint --ext .js,.vue src --fix", "stylelint": "./node_modules/.bin/stylelint src", @@ -12,6 +12,7 @@ }, "dependencies": { "axios": "^0.24.0", + "trim": "^1.0.1", "vue": "^2.6.11" }, "browserslist": [ @@ -23,16 +24,13 @@ "devDependencies": { "@babel/core": "^7.9.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.19.6", "@babel/preset-env": "^7.9.0", - "@babel/runtime": "^7.20.0", "@nextcloud/axios": "^1.8.0", "@nextcloud/browserslist-config": "^1.0.0", "@nextcloud/eslint-config": "^8.1.2", "@nextcloud/initial-state": "^2.0.0", "@nextcloud/l10n": "^1.6.0", "@nextcloud/vue": "^7.0.0", - "@symfony/webpack-encore": "^4.1.1", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "css-loader": "^3.4.2", @@ -47,8 +45,8 @@ "eslint-plugin-vue": "^9.0.0", "eslint-webpack-plugin": "^3.0.0", "file-loader": "^6.0.0", - "sass": "^1.55.0", - "sass-loader": "^13.1.0", + "sass": "^1.49.9", + "sass-loader": "^13.0.2", "stylelint": "^14.0.0", "stylelint-config-recommended-scss": "^7.0.0", "stylelint-scss": "^4.0.0", @@ -57,7 +55,7 @@ "vue-loader": "^15", "vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.7.13", - "webpack": "^5.74.0", + "webpack": "^5.0.0", "webpack-cli": "^4.0.0", "webpack-merge": "^4.2.2", "webpack-node-externals": "^1.7.2" diff --git a/screenshots/admin_settings.png b/screenshots/admin_settings.png index ba7a744..4d7a0e0 100644 Binary files a/screenshots/admin_settings.png and b/screenshots/admin_settings.png differ diff --git a/screenshots/personal_settings.png b/screenshots/personal_settings.png index ba62403..05a73ee 100644 Binary files a/screenshots/personal_settings.png and b/screenshots/personal_settings.png differ diff --git a/src/AdminCategoriesCustom.vue b/src/AdminCategoriesCustom.vue index c210fc3..2a84749 100644 --- a/src/AdminCategoriesCustom.vue +++ b/src/AdminCategoriesCustom.vue @@ -16,7 +16,7 @@ along with this program. If not, see . -->