Compare commits

...

31 Commits

Author SHA1 Message Date
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
Simon Vieille b208c8cb19 release v1.19.1 2020-10-21 18:06:00 +02:00
Simon Vieille 0e5cd135b4 fix issue #47 2020-10-21 18:05:36 +02:00
28 changed files with 2110 additions and 502 deletions
+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
...
+1027
View File
File diff suppressed because one or more lines are too long
-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 -2
View File
@@ -6,13 +6,19 @@ 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. 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) * [Installation and upgrade](#installation-and-upgrade)
* [How to contribute?](#how-to-contribute) * [How to contribute?](#how-to-contribute)
* [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/) * [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 Installation and upgrade
------------------------ ------------------------
+13 -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. 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. 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) * PHP >= 7.3
* 😃 [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) * 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> ]]></description>
<version>1.19.0</version> <version>1.23.0</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author> <author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author>
<namespace>SideMenu</namespace> <namespace>SideMenu</namespace>
@@ -30,7 +36,7 @@ If you like this application and want to support the development:
</documentation> </documentation>
<category>customization</category> <category>customization</category>
<website>https://gitnet.fr/deblan/side_menu</website> <website>https://gitnet.fr/deblan/side_menu</website>
<discussion>https://matrix.to/#/!GhfQePnOSPYpYEunWM:neutralnetwork.org</discussion> <discussion>https://matrix.to/#/!TFPucDATKODpHNVAtu:neutralnetwork.org?via=neutralnetwork.org</discussion>
<bugs>https://gitnet.fr/deblan/side_menu/issues</bugs> <bugs>https://gitnet.fr/deblan/side_menu/issues</bugs>
<repository type="git">https://gitnet.fr/deblan/side_menu</repository> <repository type="git">https://gitnet.fr/deblan/side_menu</repository>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc19_default_menu.png</screenshot>
@@ -40,7 +46,7 @@ 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/nc20_big_menu_responsive.png</screenshot>
<screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot> <screenshot>https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png</screenshot>
<dependencies> <dependencies>
<nextcloud min-version="18" max-version="20"/> <nextcloud min-version="18" max-version="21"/>
</dependencies> </dependencies>
<settings> <settings>
<admin>OCA\SideMenu\Settings\Admin</admin> <admin>OCA\SideMenu\Settings\Admin</admin>
+6
View File
@@ -1,4 +1,10 @@
<?php <?php
/**
* Generates l10n files using Yaml.
*
* Usage:
* php bin/generate_l10n.php
*/
function generateJsContent($translations) 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,
]);
}
+51
View File
@@ -68,3 +68,54 @@
.side-menu-toggler { .side-menu-toggler {
cursor: pointer; 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.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;
}
+6 -1
View File
@@ -6,6 +6,7 @@ use OC;
use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\User\User; use OC\User\User;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\IUserSession; use OCP\IUserSession;
@@ -84,8 +85,12 @@ class Application extends App
return new AppRepository(); return new AppRepository();
}); });
$container->registerService('CategoryRepository', function (ContainerInterface $c) {
return new CategoryRepository();
});
$container->registerService('ConfigProxy', function (ContainerInterface $c) { $container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config); return new ConfigProxy();
}); });
} }
+40 -8
View File
@@ -65,29 +65,61 @@ class CssController extends Controller
protected function getConfig(): array 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'); $isForced = $this->config->getAppValueBool('force', '0');
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $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) { if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps) && !$isForced) { if (!empty($userTopMenuApps) && !$isForced) {
$topMenuApps = $userTopMenuApps; $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');
} 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');
} }
return [ return [
'vars' => [ 'vars' => [
'background-color' => $backgroundColor, 'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo, 'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'), 'current-app-background-color' => $currentAppBackgroundColor,
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'), 'loader-color' => $loaderColor,
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'), 'text-color' => $textColor,
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'), 'opener' => $opener,
'icon-invert-filter' => abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%', 'icon-invert-filter' => $iconInvertFilter,
'icon-opacity' => abs($this->config->getAppValueInt('icon-opacity', '100') / 100), 'icon-opacity' => $iconOpacity,
], ],
'display-logo' => $this->config->getAppValueBool('display-logo', '1'), 'display-logo' => $this->config->getAppValueBool('display-logo', '1'),
'opener-only' => $this->config->getAppValueBool('opener-only', '0'), 'opener-only' => $this->config->getAppValueBool('opener-only', '0'),
+5
View File
@@ -97,6 +97,7 @@ class JsController extends Controller
$targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]'); $targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]');
$useAvatar = $this->config->getAppValueBool('use-avatar', '0'); $useAvatar = $this->config->getAppValueBool('use-avatar', '0');
$isForced = $this->config->getAppValueBool('force', '0'); $isForced = $this->config->getAppValueBool('force', '0');
$addLogoLink = $this->config->getAppValueBool('add-logo-link', '1');
$avatar = null; $avatar = null;
$settings = null; $settings = null;
@@ -141,6 +142,8 @@ class JsController extends Controller
} }
} }
$indexUrl = OC::$server->getURLGenerator()->linkTo('', 'index.php');
return [ return [
'opener-position' => $this->config->getAppValue('opener-position', 'before'), 'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'), 'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'),
@@ -150,11 +153,13 @@ class JsController extends Controller
'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'), 'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'), 'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'), 'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'avatar' => $avatar, 'avatar' => $avatar,
'top-menu-apps' => $topMenuApps, 'top-menu-apps' => $topMenuApps,
'target-blank-apps' => $targetBlankApps, 'target-blank-apps' => $targetBlankApps,
'settings' => $settings, 'settings' => $settings,
'logo' => $this->themingDefaults->getLogo(), '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;
use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator; use OC\URLGenerator;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCP\L10N\IFactory; use OCP\L10N\IFactory;
@@ -39,11 +37,6 @@ class NavController extends Controller
*/ */
protected $config; protected $config;
/**
* @var IConfig
*/
protected $iConfig;
/** /**
* @var AppRepository * @var AppRepository
*/ */
@@ -67,20 +60,17 @@ class NavController extends Controller
public function __construct( public function __construct(
string $appName, string $appName,
IRequest $request, IRequest $request,
IConfig $iConfig,
ConfigProxy $config, ConfigProxy $config,
AppRepository $appRepository, AppRepository $appRepository,
CategoryFetcher $categoryFetcher, CategoryRepository $categoryRepository,
URLGenerator $router, URLGenerator $router,
IL10N $trans,
IFactory $l10nFactory IFactory $l10nFactory
) { ) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->config = $config; $this->config = $config;
$this->iConfig = $iConfig;
$this->appRepository = $appRepository; $this->appRepository = $appRepository;
$this->categoryFetcher = $categoryFetcher; $this->categoryRepository = $categoryRepository;
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->router = $router; $this->router = $router;
} }
@@ -94,13 +84,7 @@ class NavController extends Controller
*/ */
public function items() 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(); $user = OC::$server[IUserSession::class]->getUser();
$isForced = $this->config->getAppValueBool('force', '0');
$appsCategories = [];
$categoriesAppsCount = [];
$items = []; $items = [];
if (!$user) { if (!$user) {
@@ -109,39 +93,28 @@ class NavController extends Controller
]); ]);
} }
$categoriesLabels = $this->config->getAppValueArray('cache-categories', '[]'); $apps = $this->appRepository->getVisibleApps();
$categoriesLabels = $this->categoryRepository->getOrderedCategories();
if (empty($categoriesLabels)) { $hiddenApps = $this->config->getAppValueArray('big-menu-hidden-apps', '[]');
$categoriesLabels = $this->categoryFetcher->get(); $isForced = $this->config->getAppValueBool('force', '0');
$this->iConfig->setAppValue(Application::APP_ID, 'cache-categories', json_encode($categoriesLabels));
}
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]'); $topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]'); $userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
$appsCategories = [];
$categoriesAppsCount = [];
if (!$isForced && !empty($userTopMenuApps)) { if (!$isForced && !empty($userTopMenuApps)) {
$topMenuApps = $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) { foreach ($apps as $app) {
if (in_array($app['id'], $topMenuApps)) { if (in_array($app['id'], $topMenuApps)) {
continue; continue;
} }
if (in_array($app['id'], $hiddenApps)) {
continue;
}
$categories = (array) $app['category']; $categories = (array) $app['category'];
$appsCategories[$app['id']] = []; $appsCategories[$app['id']] = [];
@@ -153,6 +126,7 @@ class NavController extends Controller
if (!isset($items[$category])) { if (!isset($items[$category])) {
$items[$category] = [ $items[$category] = [
'name' => $categoriesLabels[$category] ?? $category, 'name' => $categoriesLabels[$category] ?? $category,
'categoryId' => $category,
'apps' => [], 'apps' => [],
]; ];
} }
@@ -211,8 +185,17 @@ class NavController extends Controller
} }
} }
usort($items, function ($a, $b) { usort($items, function ($a, $b) use ($categoriesLabels) {
return ($a['name'] < $b['name']) ? -1 : 1; foreach ($categoriesLabels as $key => $value) {
if ($a['categoryId'] === $key) {
return -1;
}
if ($b['categoryId'] === $key) {
return 1;
}
}
return 0;
}); });
return new JSONResponse([ return new JSONResponse([
+2 -2
View File
@@ -18,13 +18,13 @@
namespace OCA\SideMenu\Controller; namespace OCA\SideMenu\Controller;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Response;
use OCP\IConfig; use OCP\IConfig;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession; use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
class PersonalSettingController extends Controller class PersonalSettingController extends Controller
{ {
+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; $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);
} }
} }
+32 -4
View File
@@ -20,6 +20,7 @@ namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application; use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\AppRepository; use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\CategoryRepository;
use OCA\SideMenu\Service\ConfigProxy; use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N; use OCP\IL10N;
@@ -48,12 +49,23 @@ class Admin implements ISettings
*/ */
private $appRepository; 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->l = $l;
$this->logger = $logger; $this->logger = $logger;
$this->config = $config; $this->config = $config;
$this->appRepository = $appRepository; $this->appRepository = $appRepository;
$this->categoryRepository = $categoryRepository;
} }
/** /**
@@ -64,6 +76,9 @@ class Admin implements ISettings
$backgroundColor = $this->config->getAppValue('background-color', '#333333'); $backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor); $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 = [ $parameters = [
'background-color' => $backgroundColor, 'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo, 'background-color-to' => $backgroundColorTo,
@@ -71,13 +86,23 @@ class Admin implements ISettings
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'), 'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'), 'icon-invert-filter' => $this->config->getAppValueInt('icon-invert-filter', '0'),
'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'), 'icon-opacity' => $this->config->getAppValueInt('icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue('loader-enabled', '1'),
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'), '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-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'), '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'), 'always-displayed' => $this->config->getAppValue('always-displayed', '0'),
'big-menu' => $this->config->getAppValue('big-menu', '0'), 'big-menu' => $this->config->getAppValue('big-menu', '0'),
'big-menu-hidden-apps' => $this->config->getAppValueArray('big-menu-hidden-apps', '[]'),
'display-logo' => $this->config->getAppValue('display-logo', '1'), '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'), 'use-avatar' => $this->config->getAppValue('use-avatar', '0'),
'opener-position' => $this->config->getAppValue('opener-position', 'before'), 'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValue('opener-hover', '0'), 'opener-hover' => $this->config->getAppValue('opener-hover', '0'),
@@ -90,7 +115,10 @@ class Admin implements ISettings
'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'), 'target-blank-apps' => $this->config->getAppValueArray('target-blank-apps', '[]'),
'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'), 'top-menu-apps' => $this->config->getAppValueArray('top-menu-apps', '[]'),
'default-enabled' => $this->config->getAppValue('default-enabled', '1'), '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(), 'apps' => $this->appRepository->getVisibleApps(),
'categories' => $this->categoryRepository->getOrderedCategories(),
]; ];
return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, ''); return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
+4 -8
View File
@@ -18,10 +18,10 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Settings\IIconSection; use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class AdminSection implements IIconSection class AdminSection implements IIconSection
{ {
@@ -35,10 +35,6 @@ class AdminSection implements IIconSection
*/ */
private $url; private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
public function __construct(IURLGenerator $url, IL10N $l) public function __construct(IURLGenerator $url, IL10N $l)
{ {
$this->url = $url; $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, * returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap' * e.g. 'ldap'.
* *
* @returns string * @returns string
*/ */
@@ -69,8 +65,8 @@ class AdminSection implements IIconSection
/** /**
* @return int whether the form should be rather on the top or bottom of * @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order 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 priority values. It is required to return a value between 0 and 99.
* *
* E.g.: 70 * E.g.: 70
*/ */
+4 -8
View File
@@ -18,10 +18,10 @@
namespace OCA\SideMenu\Settings; namespace OCA\SideMenu\Settings;
use OCA\SideMenu\AppInfo\Application;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Settings\IIconSection; use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class PersonalSection implements IIconSection class PersonalSection implements IIconSection
{ {
@@ -35,10 +35,6 @@ class PersonalSection implements IIconSection
*/ */
private $url; private $url;
/**
* @param IURLGenerator $url
* @param IL10N $l
*/
public function __construct(IURLGenerator $url, IL10N $l) public function __construct(IURLGenerator $url, IL10N $l)
{ {
$this->url = $url; $this->url = $url;
@@ -47,7 +43,7 @@ class PersonalSection implements IIconSection
/** /**
* returns the ID of the section. It is supposed to be a lower case string, * returns the ID of the section. It is supposed to be a lower case string,
* e.g. 'ldap' * e.g. 'ldap'.
* *
* @returns string * @returns string
*/ */
@@ -69,8 +65,8 @@ class PersonalSection implements IIconSection
/** /**
* @return int whether the form should be rather on the top or bottom of * @return int whether the form should be rather on the top or bottom of
* the settings navigation. The sections are arranged in ascending order 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 priority values. It is required to return a value between 0 and 99.
* *
* E.g.: 70 * E.g.: 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> <template>
<div v-bind:class="classes"> <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> </div>
</template> </template>
@@ -28,6 +31,10 @@ export default {
type: String, type: String,
required: true required: true
}, },
link: {
type: String,
required: false
},
classes: { classes: {
type: Object, type: Object,
required: true required: true
+13 -2
View File
@@ -25,8 +25,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<OpenerButton /> <OpenerButton />
<Logo v-if="!avatar && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}" v-bind:image="logo" /> <Logo
<Logo v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}" v-bind:image="avatar" /> 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> </div>
<ul class="side-menu-apps-list"> <ul class="side-menu-apps-list">
@@ -62,6 +71,7 @@ export default {
return { return {
apps: [], apps: [],
logo: null, logo: null,
logoLink: null,
avatar: null, avatar: null,
forceLightIcon: false, forceLightIcon: false,
targetBlankApps: [], targetBlankApps: [],
@@ -135,6 +145,7 @@ export default {
that.forceLightIcon = config['force-light-icon'] that.forceLightIcon = config['force-light-icon']
that.avatar = config['avatar'] that.avatar = config['avatar']
that.logo = config['logo'] that.logo = config['logo']
that.logoLink = config['logo-link']
that.settings = config['settings'] that.settings = config['settings']
}); });
}, },
+1 -1
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:icon="app.icon"
v-bind:label="app.name" v-bind:label="app.name"
v-bind:href="app.href" 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> </ul>
</div> </div>
+16
View File
@@ -129,4 +129,20 @@ jQuery(document).ready(() => {
elementToggler(element) elementToggler(element)
}) })
jQuery("#categories-list .side-menu-setting-list").sortable({
forcePlaceholderSize: true,
placeholder: 'placeholder',
stop: function (event, ui) {
let value = []
jQuery('#categories-list .side-menu-setting-list-item').each(function() {
value.push(jQuery(this).attr('data-id'))
});
value = JSON.stringify(value)
jQuery('input[name="categories-order"]').val(value)
}
});
}); });
+11
View File
@@ -64,3 +64,14 @@
"Show the link to settings": "Zeigen Sie den Link zu den Einstellungen an" "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" "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." "Except when the configuration is forced.": "Außer wenn die Konfiguration erzwungen wird."
"Apps that should not be displayed in the menu": "Apps, die nicht im Menü angezeigt werden sollen"
"This feature is only compatible with the <code>big menu</code> display.": "Kompatibel mit der Anzeige <code> Großes 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": "Kundenspezifisch"
"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 Parameter werden verwendet, wenn Dark Theme oder Breeze Dark Theme aktiviert sind."
+11
View File
@@ -64,3 +64,14 @@
"Show the link to settings": "Afficher le lien vers les paramètres" "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" "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." "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."
+13 -7
View File
@@ -34,10 +34,14 @@ var alwaysDisplayed = function() {
for (var i in fixedElements) { for (var i in fixedElements) {
var element = fixedElements[i] var element = fixedElements[i]
var value = window.getComputedStyle(element, null).getPropertyValue('left') var computedStyle = window.getComputedStyle(element, null)
var intValue = parseInt(value.replace('px', '')) var left = computedStyle.getPropertyValue('left')
var right = computedStyle.getPropertyValue('right')
element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)') if (right !== '0px') {
var intValue = parseInt(left.replace('px', ''))
element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)')
}
} }
} }
@@ -45,14 +49,17 @@ let content = document.getElementById('content')
if (content && content.classList.contains('app-settings')) { if (content && content.classList.contains('app-settings')) {
let loaded = false let loaded = false
const config = {attributes: false, childList: true, subtree: true} const config = {
attributes: false,
childList: true,
subtree: true
}
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
if (loaded) { if (loaded) {
return; return;
} }
const element = content.querySelector('#app-category-your-apps') const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul')
|| content.querySelector('#app-navigation ul')
if (element) { if (element) {
loaded = true loaded = true
@@ -65,4 +72,3 @@ if (content && content.classList.contains('app-settings')) {
} else { } else {
window.setTimeout(alwaysDisplayed, 200) window.setTimeout(alwaysDisplayed, 200)
} }
File diff suppressed because it is too large Load Diff