Compare commits

...

44 Commits

Author SHA1 Message Date
Simon Vieille 30ed6c1b54 release v1.14.0 2020-09-22 15:04:05 +02:00
Simon Vieille bd7cefa6b6 release v1.14.0 2020-09-22 14:59:33 +02:00
Simon Vieille 240a6b1aa4 PSR 2 compliance 2020-09-22 14:35:09 +02:00
Simon Vieille 40e883841e add config proxy 2020-09-22 14:24:53 +02:00
Simon Vieille 7b5e92f83e add config proxy 2020-09-22 14:24:48 +02:00
Simon Vieille 9a67c165bd add constante for the app id and the app name 2020-09-21 11:28:55 +02:00
Simon Vieille 78c95c58ff fix regression with the logo and the avatar display 2020-09-21 09:49:09 +02:00
Simon Vieille 98f74bfe33 remove useless attributes 2020-09-21 09:45:51 +02:00
Simon Vieille a9b4283e76 Merge branch 'feature/conf' into develop 2020-09-20 20:57:02 +02:00
Simon Vieille 85c15a1967 add PHP doc 2020-09-20 20:56:59 +02:00
Simon Vieille 45abf1f09d SideMenu and SideMenuBig use the new controller to get configuration 2020-09-20 20:56:36 +02:00
Simon Vieille 5153bee7c6 - add specific methods to get the config
- add a controller to retrieve most of the configuration
2020-09-20 20:53:03 +02:00
Simon Vieille cd294273cb update description 2020-09-19 15:51:56 +02:00
Simon Vieille be17c0e232 update translations 2020-09-18 13:02:23 +02:00
Simon Vieille 41ca035299 release v1.13.0 2020-09-18 11:12:47 +02:00
Simon Vieille 110ac5b130 update screenshots 2020-09-18 11:12:19 +02:00
Simon Vieille 266ba1453e Merge branch 'feature/issue34' into develop 2020-09-18 11:10:22 +02:00
Simon Vieille ba6d6fdd8f PSR 2 compliance 2020-09-18 11:09:14 +02:00
Simon Vieille e5dd893774 add option to show the avatar instead of the logo 2020-09-18 11:09:01 +02:00
Simon Vieille 85c25f0fc7 update description 2020-09-18 09:14:05 +02:00
Simon Vieille ae130388a5 release v1.12.3 2020-09-18 09:05:06 +02:00
Simon Vieille e653214b91 add a delay before moving elements (fix #33) 2020-09-17 15:19:42 +02:00
Simon Vieille 9ca6a3a995 update screenshots 2020-09-14 14:56:09 +02:00
Simon Vieille 7303dbae41 release v1.12.2 2020-09-14 14:51:39 +02:00
Simon Vieille 511f5a0c45 fix issue #31 2020-09-14 14:40:14 +02:00
Simon Vieille 9ea5c1b59b release v1.12.1 2020-09-14 11:31:50 +02:00
Simon Vieille ade56062e3 fix typo 2020-09-14 11:31:03 +02:00
Simon Vieille 481a2d2606 Merge pull request 'src/l10n/fixtures/de.yaml ändern' (#32) from JuxLe/side_menu:juxle-patch-1 into develop
Reviewed-on: https://gitnet.fr/deblan/side_menu/pulls/32
2020-09-14 09:01:09 +02:00
JuxLe 3a809bb92e „src/l10n/fixtures/de.yaml“ ändern
some translation improvements
2020-09-13 03:18:09 +02:00
Simon Vieille cc2ab04035 release v1.12.0 2020-09-12 16:44:05 +02:00
Simon Vieille 17a57f3685 Merge branch 'feature/issue30-tab' into develop 2020-09-12 16:40:43 +02:00
Simon Vieille d41ecbbd9a add translations 2020-09-12 16:35:30 +02:00
Simon Vieille b6c6fc2486 add target _blank 2020-09-12 16:32:10 +02:00
Simon Vieille 918c55da3e add options to select apps that must be opend on new tab (UI) 2020-09-12 16:10:48 +02:00
Simon Vieille 385c27c691 add documentation 2020-09-11 13:46:48 +02:00
Simon Vieille 54d702ba9f add documentation 2020-09-11 13:46:25 +02:00
Simon Vieille c816c40c19 fix typo 2020-09-11 13:39:41 +02:00
Simon Vieille aad27cd8b4 release v1.11.0 2020-09-11 13:26:01 +02:00
Simon Vieille 96fb33b80b Merge branch 'feature/issue29-tab' into develop 2020-09-11 13:25:09 +02:00
Simon Vieille e10a5f1f62 fix issue with side menu header 2020-09-11 13:23:52 +02:00
Simon Vieille f133932884 add target _blank when the option is enabled 2020-09-11 13:23:40 +02:00
Simon Vieille e287e27375 add translations 2020-09-11 13:06:43 +02:00
Simon Vieille 9ae4d7ee48 add new option: open apps in new tab 2020-09-11 13:03:37 +02:00
Simon Vieille 624bb52ec4 release v1.10.0 2020-09-04 18:14:49 +02:00
27 changed files with 476 additions and 156 deletions
+2
View File
@@ -12,6 +12,8 @@ Comptatible with AppOrder.
* [How to contribute?](#how-to-contribute)
* [Screenshots](https://gitnet.fr/deblan/side_menu/src/branch/master/screenshots/)
You like this app and you want to support me? [Buy me a coffee](https://www.buymeacoffee.com/deblan) ☕
Installation and upgrade
------------------------
+6 -2
View File
@@ -2,7 +2,7 @@
<info>
<id>side_menu</id>
<name>Side menu</name>
<summary>Move the top menu to the left side.</summary>
<summary>Modify the display of the menu.</summary>
<description><![CDATA[Side menu allows you to modify the position of the main menu by creating a panel on the left of the interface or with a big menu on the top.
You can also define apps that must be displayed in the top menu. Fully customisable.
@@ -14,10 +14,14 @@ Comptatible with AppOrder.
You can report a bug or request a feature by opening an issue.
You like this app and you want to support me?
* ☕ [Buy me a coffee](https://www.buymeacoffee.com/deblan)
* 😃 [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments)
]]></description>
<licence>agpl</licence>
<author mail="contact@deblan.fr" homepage="https://www.deblan.io/">Simon Vieille</author>
<version>1.9.3</version>
<version>1.14.0</version>
<namespace>SideMenu</namespace>
<category>customization</category>
<website>https://gitnet.fr/deblan/side_menu</website>
+1
View File
@@ -21,6 +21,7 @@ return [
'routes' => [
['name' => 'Css#stylesheet', 'url' => '/css/stylesheet', 'verb' => 'GET'],
['name' => 'Js#script', 'url' => '/js/script', 'verb' => 'GET'],
['name' => 'Js#config', 'url' => '/js/config', 'verb' => 'GET'],
['name' => 'Nav#items', 'url' => '/nav/items', 'verb' => 'GET'],
['name' => 'PersonalSetting#valueSet', 'url' => '/personalSetting/valueSet', 'verb' => 'POST'],
],
+4
View File
@@ -64,3 +64,7 @@
font-size: 11px;
font-weight: normal;
}
.side-menu-toggler {
cursor: pointer;
}
+6 -6
View File
@@ -211,17 +211,17 @@
display: none;
}
.side-menu-always-displayed #side-menu .side-menu-header {
height: 49px;
}
.side-menu-always-displayed #side-menu.open,
.side-menu-always-displayed #side-menu.open .side-menu-apps-list,
.side-menu-always-displayed #side-menu.open .side-menu-header,
.side-menu-always-displayed #side-menu:hover,
.side-menu-always-displayed #side-menu:hover .side-menu-apps-list,
.side-menu-always-displayed #side-menu:hover .side-menu-header {
.side-menu-always-displayed #side-menu.open .side-menu-header {
width: 100%;
}
.side-menu-always-displayed #side-menu.open .side-menu-app-text,
.side-menu-always-displayed #side-menu:hover .side-menu-app-text {
.side-menu-always-displayed #side-menu.open .side-menu-app-text {
display: inline;
}
+14 -6
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\ConfigProxy;
use OCP\AppFramework\App;
use OCP\IUserSession;
use OCP\Util;
@@ -18,6 +19,9 @@ use Psr\Container\ContainerInterface;
*/
class Application extends App
{
public const APP_ID = 'side_menu';
public const APP_NAME = 'Side menu';
/**
* @var OC\AllConfig
*/
@@ -38,7 +42,7 @@ class Application extends App
*/
public function __construct(array $urlParams = [])
{
parent::__construct('side_menu', $urlParams);
parent::__construct(self::APP_ID, $urlParams);
$this->config = OC::$server->getConfig();
$this->cspnm = OC::$server->getContentSecurityPolicyNonceManager();
@@ -53,7 +57,7 @@ class Application extends App
$enabled = true;
if (null !== $this->user) {
$enabled = (bool) $this->config->getUserValue($this->user->getUid(), 'side_menu', 'enabled', '1');
$enabled = (bool) $this->config->getUserValue($this->user->getUid(), self::APP_ID, 'enabled', '1');
}
return $enabled;
@@ -69,6 +73,10 @@ class Application extends App
$container->registerService('AppRepository', function (ContainerInterface $c) {
return new AppRepository(new OC_App());
});
$container->registerService('ConfigProxy', function (ContainerInterface $c) {
return new ConfigProxy($this->config);
});
}
/**
@@ -76,20 +84,20 @@ class Application extends App
*/
public function registerAssets()
{
Util::addScript('side_menu', 'sideMenu');
Util::addStyle('side_menu', 'sideMenu');
Util::addScript(self::APP_ID, 'sideMenu');
Util::addStyle(self::APP_ID, 'sideMenu');
$stylesheet = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Css.stylesheet',
[
'v' => $this->config->getAppValue('side_menu', 'cache', '0'),
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
]
);
$script = OC::$server->getURLGenerator()->linkToRoute(
'side_menu.Js.script',
[
'v' => $this->config->getAppValue('side_menu', 'cache', '0'),
'v' => $this->config->getAppValue(self::APP_ID, 'cache', '0'),
]
);
+36 -29
View File
@@ -19,27 +19,32 @@
namespace OCA\SideMenu\Controller;
use OC;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
class CssController extends Controller
{
/**
* @var \OCP\IConfig
* @var ConfigProxy
*/
protected $config;
/**
* @param string $appName
* @var User
*/
public function __construct($appName, IRequest $request, IConfig $config)
protected $user;
public function __construct(string $appName, IRequest $request, ConfigProxy $config)
{
parent::__construct($appName, $request);
$this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config;
}
@@ -52,44 +57,46 @@ class CssController extends Controller
*/
public function stylesheet()
{
$backgroundColor = $this->config->getAppValue('side_menu', 'background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('side_menu', 'background-color-to', $backgroundColor);
$response = new TemplateResponse(Application::APP_ID, 'css/stylesheet', $this->getConfig(), 'blank');
$response->addHeader('Content-Type', 'text/css');
$user = OC::$server[IUserSession::class]->getUser();
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true);
return $response;
}
if ($user) {
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
protected function getConfig(): array
{
$backgroundColor = $this->config->getAppValue('background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('background-color-to', $backgroundColor);
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
}
$parameters = [
return [
'vars' => [
'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('side_menu', 'loader-color', '#0e75ac'),
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'),
'icon-invert-filter' => abs((int) $this->config->getAppValue('side_menu', 'icon-invert-filter', '0')).'%',
'icon-opacity' => abs((int) $this->config->getAppValue('side_menu', 'icon-opacity', '100') / 100),
'current-app-background-color' => $this->config->getAppValue('current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('loader-color', '#0e75ac'),
'text-color' => $this->config->getAppValue('text-color', '#FFFFFF'),
'opener' => $this->config->getAppValue('opener', 'side-menu-opener'),
'icon-invert-filter' => abs($this->config->getAppValueInt('icon-invert-filter', '0')).'%',
'icon-opacity' => abs($this->config->getAppValueInt('icon-opacity', '100') / 100),
],
'display-logo' => (bool) $this->config->getAppValue('side_menu', 'display-logo', 1),
'opener-only' => (bool) $this->config->getAppValue('side_menu', 'opener-only', 0),
'external-sites-in-top-menu' => (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0),
'size-icon' => $this->config->getAppValue('side_menu', 'size-icon', 'normal'),
'size-text' => $this->config->getAppValue('side_menu', 'size-text', 'normal'),
'always-displayed' => (bool) $this->config->getAppValue('side_menu', 'always-displayed', '0'),
'big-menu' => (bool) $this->config->getAppValue('side_menu', 'big-menu', '0'),
'display-logo' => $this->config->getAppValueBool('display-logo', 1),
'opener-only' => $this->config->getAppValueBool('opener-only', 0),
'external-sites-in-top-menu' => $this->config->getAppValueBool('external-sites-in-top-menu', 0),
'size-icon' => $this->config->getAppValue('size-icon', 'normal'),
'size-text' => $this->config->getAppValue('size-text', 'normal'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'top-menu-apps' => $topMenuApps,
];
$response = new TemplateResponse('side_menu', 'css/stylesheet', $parameters, 'blank');
$response->addHeader('Content-Type', 'text/css');
return $response;
}
}
+80 -32
View File
@@ -19,27 +19,40 @@
namespace OCA\SideMenu\Controller;
use OC;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCA\SideMenu\Service\ConfigProxy;
use OCA\Theming\ThemingDefaults;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
class JsController extends Controller
{
/**
* @var \OCP\IConfig
* @var ConfigProxy
*/
protected $config;
/**
* @param string $appName
* @var User
*/
public function __construct($appName, IRequest $request, IConfig $config)
protected $user;
/**
* @var ThemingDefaults
*/
protected $themingDefaults;
public function __construct(string $appName, IRequest $request, ConfigProxy $config, ThemingDefaults $themingDefaults)
{
parent::__construct($appName, $request);
$this->themingDefaults = $themingDefaults;
$this->user = OC::$server[IUserSession::class]->getUser();
$this->config = $config;
}
@@ -47,37 +60,72 @@ class JsController extends Controller
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*
* @return Response
*/
public function script()
public function script(): TemplateResponse
{
$user = OC::$server[IUserSession::class]->getUser();
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true);
if ($user) {
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
}
$parameters = [
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'),
'opener-hover' => (bool) $this->config->getAppValue('side_menu', 'opener-hover', '0'),
'external-sites-in-top-menu' => (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0),
'force-light-icon' => (bool) $this->config->getAppValue('side_menu', 'force-light-icon', '0'),
'hide-when-no-apps' => (bool) $this->config->getAppValue('side_menu', 'hide-when-no-apps', '0'),
'loader-enabled' => (bool) $this->config->getAppValue('side_menu', 'loader-enabled', '1'),
'always-displayed' => (bool) $this->config->getAppValue('side_menu', 'always-displayed', '0'),
'big-menu' => (bool) $this->config->getAppValue('side_menu', 'big-menu', '0'),
'top-menu-apps' => $topMenuApps,
];
$response = new TemplateResponse('side_menu', 'js/script', $parameters, 'blank');
$response = new TemplateResponse(Application::APP_ID, 'js/script', $this->getConfig(), 'blank');
$response->addHeader('Content-Type', 'text/javascript');
return $response;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*/
public function config(): JSONResponse
{
return new JSONResponse($this->getConfig());
}
protected function getConfig(): array
{
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$targetBlankApps = $this->config->getAppValueArray('target-blank-apps', '[]');
$useAvatar = $this->config->getAppValueBool('use-avatar', '0');
$avatar = null;
if ($this->user) {
$userTopMenuApps = $this->config->getUserValueArray($this->user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
}
$userTargetBlankMode = $this->config->getUserValueInt($this->user, 'target-blank-mode', '1');
$userTargetBlankApps = $this->config->getUserValueArray($this->user, 'target-blank-apps', '[]');
if (2 === $userTargetBlankMode) {
$targetBlankApps = $userTargetBlankApps;
}
$isAvatarSet = OC::$server->getAvatarManager()->getAvatar($this->user->getUid())->exists();
if ($useAvatar && $isAvatarSet) {
$avatar = OC::$server->getURLGenerator()->linkToRoute(
'core.avatar.getAvatar', [
'userId' => $this->user->getUid(),
'size' => 128,
'v' => $this->config->getUserValueInt($this->user, 'avatar', 'version', 0),
]
);
}
}
return [
'opener-position' => $this->config->getAppValue('opener-position', 'before'),
'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'),
'external-sites-in-top-menu' => $this->config->getAppValueBool('external-sites-in-top-menu', 0),
'force-light-icon' => $this->config->getAppValueBool('force-light-icon', '0'),
'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'),
'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'),
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
'big-menu' => $this->config->getAppValueBool('big-menu', '0'),
'avatar' => $avatar,
'top-menu-apps' => $topMenuApps,
'target-blank-apps' => $targetBlankApps,
'logo' => $this->themingDefaults->getLogo(),
];
}
}
+7 -10
View File
@@ -22,9 +22,9 @@ use OC;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\URLGenerator;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\Service\ConfigProxy;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserSession;
@@ -33,7 +33,7 @@ use OCP\L10N\IFactory;
class NavController extends Controller
{
/**
* @var IConfig
* @var ConfigProxy
*/
protected $config;
@@ -57,13 +57,10 @@ class NavController extends Controller
*/
protected $router;
/**
* @param string $appName
*/
public function __construct(
$appName,
string $appName,
IRequest $request,
IConfig $config,
ConfigProxy $config,
AppRepository $appRepository,
CategoryFetcher $categoryFetcher,
URLGenerator $router,
@@ -91,7 +88,7 @@ class NavController extends Controller
$apps = $this->appRepository->getVisibleApps();
$currentLanguage = substr($this->l10nFactory->findLanguage(), 0, 2);
$categoriesLabels = $this->categoryFetcher->get();
$externalSitesInTopMenu = (bool) $this->config->getAppValue('side_menu', 'external-sites-in-top-menu', 0);
$externalSitesInTopMenu = $this->config->getAppValueBool('external-sites-in-top-menu', 0);
$user = OC::$server[IUserSession::class]->getUser();
$appsCategories = [];
$categoriesAppsCount = [];
@@ -103,8 +100,8 @@ class NavController extends Controller
]);
}
$topMenuApps = (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true);
$userTopMenuApps = (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true);
$topMenuApps = $this->config->getAppValueArray('top-menu-apps', '[]');
$userTopMenuApps = $this->config->getUserValueArray($user, 'top-menu-apps', '[]');
if (!empty($userTopMenuApps)) {
$topMenuApps = $userTopMenuApps;
+25 -1
View File
@@ -23,6 +23,7 @@ use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserSession;
use OCA\SideMenu\AppInfo\Application;
class PersonalSettingController extends Controller
{
@@ -65,6 +66,29 @@ class PersonalSettingController extends Controller
}
}
if ('target-blank-mode' === $name) {
$doSave = true;
if (!in_array($value, ['1', '2'])) {
$value = '1';
}
}
if ('target-blank-apps' === $name) {
$doSave = true;
$data = json_decode($value, true);
if (!is_array($data)) {
$doSave = false;
} else {
foreach ($data as $v) {
if (!is_string($v)) {
$doSave = false;
}
}
}
}
if ('top-menu-apps' === $name) {
$doSave = true;
$data = json_decode($value, true);
@@ -81,7 +105,7 @@ class PersonalSettingController extends Controller
}
if ($doSave) {
$this->config->setUserValue($user->getUid(), 'side_menu', $name, $value);
$this->config->setUserValue($user->getUid(), Application::APP_ID, $name, $value);
return [
'name' => $name,
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace OCA\SideMenu\Service;
use OC\User\User;
use OCA\SideMenu\AppInfo\Application;
use OCP\IConfig;
/**
* class Config.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class ConfigProxy
{
/**
* @var IConfig
*/
protected $config;
public function __construct(IConfig $config)
{
$this->config = $config;
}
public function getAppValue(string $name, string $default): string
{
return (string) $this->config->getAppValue(Application::APP_ID, $name, $default);
}
public function getUserValue(User $user, string $name, string $default): string
{
return (string) $this->config->getUserValue($user->getUid(), Application::APP_ID, $name, $default);
}
public function getAppValueBool(string $name, string $default): bool
{
return (bool) $this->getAppValue($name, $default);
}
public function getAppValueArray(string $name, string $default): array
{
return (array) json_decode($this->getAppValue($name, $default), true);
}
public function getAppValueInt(string $name, string $default): int
{
return (int) $this->getAppValue($name, $default);
}
public function getUserValueBool(User $user, string $name, string $default): bool
{
return (bool) $this->getUserValue($user, $name, $default);
}
public function getUserValueArray(User $user, string $name, string $default): array
{
return (array) json_decode($this->getUserValue($user, $name, $default), true);
}
public function getUserValueInt(User $user, string $name, string $default): int
{
return (int) $this->getUserValue($user, $name, $default);
}
}
+25 -22
View File
@@ -24,6 +24,7 @@ use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
use OCP\Settings\ISettings;
use OCA\SideMenu\AppInfo\Application;
class Admin implements ISettings
{
@@ -60,34 +61,36 @@ class Admin implements ISettings
*/
public function getForm()
{
$backgroundColor = $this->config->getAppValue('side_menu', 'background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue('side_menu', 'background-color-to', $backgroundColor);
$backgroundColor = $this->config->getAppValue(Application::APP_ID, 'background-color', '#333333');
$backgroundColorTo = $this->config->getAppValue(Application::APP_ID, 'background-color-to', $backgroundColor);
$parameters = [
'background-color' => $backgroundColor,
'background-color-to' => $backgroundColorTo,
'current-app-background-color' => $this->config->getAppValue('side_menu', 'current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue('side_menu', 'loader-color', '#0e75ac'),
'icon-invert-filter' => (int) $this->config->getAppValue('side_menu', 'icon-invert-filter', '0'),
'icon-opacity' => (int) $this->config->getAppValue('side_menu', 'icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue('side_menu', 'loader-enabled', '1'),
'text-color' => $this->config->getAppValue('side_menu', 'text-color', '#FFFFFF'),
'cache' => $this->config->getAppValue('side_menu', 'cache', '0'),
'opener' => $this->config->getAppValue('side_menu', 'opener', 'side-menu-opener'),
'always-displayed' => $this->config->getAppValue('side_menu', 'always-displayed', '0'),
'big-menu' => $this->config->getAppValue('side_menu', 'big-menu', '0'),
'display-logo' => $this->config->getAppValue('side_menu', 'display-logo', '1'),
'opener-position' => $this->config->getAppValue('side_menu', 'opener-position', 'before'),
'opener-hover' => $this->config->getAppValue('side_menu', 'opener-hover', '0'),
'opener-only' => $this->config->getAppValue('side_menu', 'opener-only', '0'),
'hide-when-no-apps' => $this->config->getAppValue('side_menu', 'hide-when-no-apps', '0'),
'size-icon' => $this->config->getAppValue('side_menu', 'size-icon', 'normal'),
'size-text' => $this->config->getAppValue('side_menu', 'size-text', 'normal'),
'top-menu-apps' => (array) json_decode($this->config->getAppValue('side_menu', 'top-menu-apps', '[]'), true),
'current-app-background-color' => $this->config->getAppValue(Application::APP_ID, 'current-app-background-color', '#444444'),
'loader-color' => $this->config->getAppValue(Application::APP_ID, 'loader-color', '#0e75ac'),
'icon-invert-filter' => (int) $this->config->getAppValue(Application::APP_ID, 'icon-invert-filter', '0'),
'icon-opacity' => (int) $this->config->getAppValue(Application::APP_ID, 'icon-opacity', '100'),
'loader-enabled' => $this->config->getAppValue(Application::APP_ID, 'loader-enabled', '1'),
'text-color' => $this->config->getAppValue(Application::APP_ID, 'text-color', '#FFFFFF'),
'cache' => $this->config->getAppValue(Application::APP_ID, 'cache', '0'),
'opener' => $this->config->getAppValue(Application::APP_ID, 'opener', 'side-menu-opener'),
'always-displayed' => $this->config->getAppValue(Application::APP_ID, 'always-displayed', '0'),
'big-menu' => $this->config->getAppValue(Application::APP_ID, 'big-menu', '0'),
'display-logo' => $this->config->getAppValue(Application::APP_ID, 'display-logo', '1'),
'use-avatar' => $this->config->getAppValue(Application::APP_ID, 'use-avatar', '0'),
'opener-position' => $this->config->getAppValue(Application::APP_ID, 'opener-position', 'before'),
'opener-hover' => $this->config->getAppValue(Application::APP_ID, 'opener-hover', '0'),
'opener-only' => $this->config->getAppValue(Application::APP_ID, 'opener-only', '0'),
'hide-when-no-apps' => $this->config->getAppValue(Application::APP_ID, 'hide-when-no-apps', '0'),
'size-icon' => $this->config->getAppValue(Application::APP_ID, 'size-icon', 'normal'),
'size-text' => $this->config->getAppValue(Application::APP_ID, 'size-text', 'normal'),
'target-blank-apps' => (array) json_decode($this->config->getAppValue(Application::APP_ID, 'target-blank-apps', '[]'), true),
'top-menu-apps' => (array) json_decode($this->config->getAppValue(Application::APP_ID, 'top-menu-apps', '[]'), true),
'apps' => $this->appRepository->getVisibleApps(),
];
return new TemplateResponse('side_menu', 'settings/admin-form', $parameters, '');
return new TemplateResponse(Application::APP_ID, 'settings/admin-form', $parameters, '');
}
/**
@@ -95,7 +98,7 @@ class Admin implements ISettings
*/
public function getSection()
{
return 'side_menu';
return Application::APP_ID;
}
/**
+3 -2
View File
@@ -21,6 +21,7 @@ namespace OCA\SideMenu\Settings;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class AdminSection implements IIconSection
{
@@ -52,7 +53,7 @@ class AdminSection implements IIconSection
*/
public function getID()
{
return 'side_menu';
return Application::APP_ID;
}
/**
@@ -63,7 +64,7 @@ class AdminSection implements IIconSection
*/
public function getName()
{
return $this->l->t('Side menu');
return $this->l->t(Application::APP_NAME);
}
/**
+7 -4
View File
@@ -25,6 +25,7 @@ use OCP\Settings\ISettings;
use OCP\IConfig;
use OCP\IUserSession;
use OCA\SideMenu\Service\AppRepository;
use OCA\SideMenu\AppInfo\Application;
class Personal implements ISettings
{
@@ -70,12 +71,14 @@ class Personal implements ISettings
$user = $this->userSession->getUser();
$parameters = [
'enabled' => $this->config->getUserValue($user->getUid(), 'side_menu', 'enabled', '1'),
'top-menu-apps' => (array) json_decode($this->config->getUserValue($user->getUid(), 'side_menu', 'top-menu-apps', '[]'), true),
'enabled' => $this->config->getUserValue($user->getUid(), Application::APP_ID, 'enabled', '1'),
'top-menu-apps' => (array) json_decode($this->config->getUserValue($user->getUid(), Application::APP_ID, 'top-menu-apps', '[]'), true),
'target-blank-mode' => $this->config->getUserValue($user->getUid(), Application::APP_ID, 'target-blank-mode', '1'),
'target-blank-apps' => (array) json_decode($this->config->getUserValue($user->getUid(), Application::APP_ID, 'target-blank-apps', '[]'), true),
'apps' => $this->appRepository->getVisibleApps(),
];
return new TemplateResponse('side_menu', 'settings/personal-form', $parameters, '');
return new TemplateResponse(Application::APP_ID, 'settings/personal-form', $parameters, '');
}
/**
@@ -83,7 +86,7 @@ class Personal implements ISettings
*/
public function getSection()
{
return 'side_menu';
return Application::APP_ID;
}
/**
+3 -2
View File
@@ -21,6 +21,7 @@ namespace OCA\SideMenu\Settings;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Settings\IIconSection;
use OCA\SideMenu\AppInfo\Application;
class PersonalSection implements IIconSection
{
@@ -52,7 +53,7 @@ class PersonalSection implements IIconSection
*/
public function getID()
{
return 'side_menu';
return Application::APP_ID;
}
/**
@@ -63,7 +64,7 @@ class PersonalSection implements IIconSection
*/
public function getName()
{
return $this->l->t('Side menu');
return $this->l->t(Application::APP_NAME);
}
/**
Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 58 KiB

+22 -15
View File
@@ -19,14 +19,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div class="side-menu-header">
<button class="side-menu-opener"></button>
<div v-if="logo" class="side-menu-logo">
<div v-if="!avatar && logo" v-bind:class="{'side-menu-logo': true, 'avatardiv': false}">
<img v-bind:src="logo">
</div>
<div v-if="avatar" v-bind:class="{'side-menu-logo': true, 'avatardiv': true}">
<img v-bind:src="avatar">
</div>
</div>
<ul class="side-menu-apps-list">
<li v-for="app in apps" v-bind:class="{'side-menu-app': true, 'active': app.active}">
<a v-bind:href="app.href" v-bind:title="app.name">
<a v-bind:href="app.href" :target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined" v-bind:title="app.name">
<span class="side-menu-app-icon" v-html="app.icon"></span>
<span class="side-menu-app-text" v-html="app.name"></span>
</a>
@@ -37,6 +40,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<script>
import trim from 'trim'
import axios from 'axios'
export default {
name: 'SideMenu',
@@ -44,7 +48,9 @@ export default {
return {
apps: [],
logo: null,
avatar: null,
forceLightIcon: false,
targetBlankApps: [],
}
},
methods: {
@@ -86,6 +92,7 @@ export default {
}
this.apps.push({
id: dataId,
href: href,
name: trim(element.querySelector('span').innerHTML),
icon: svg,
@@ -100,25 +107,25 @@ export default {
}, 1000)
})(this.apps)
},
retrieveLogo() {
const ncLogo = document.querySelector('#nextcloud .logo')
if (ncLogo) {
const url = window.getComputedStyle(ncLogo, null)
.getPropertyValue('background-image')
.replace('url("', '')
.replace('")', '')
retrieveConfig() {
let that = this
if (url && url !== 'none') {
this.logo = url
}
}
axios
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then(function(response) {
const config = response.data
that.targetBlankApps = config['target-blank-apps']
that.forceLightIcon = config['force-light-icon']
that.avatar = config['avatar']
that.logo = config['logo']
});
},
},
mounted() {
this.retrieveConfig()
this.retrieveApps()
this.retrieveLogo()
this.forceLightIcon = document.querySelector('#side-menu-container').getAttribute('data-forcelighticon') == 1;
const menu = document.querySelector('#appmenu')
+18 -2
View File
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div id="side-menu" class="side-menu-big">
<div class="side-menu-header">
<button class="side-menu-opener side-menu-closer"></button>
<button class="side-menu-opener"></button>
<button class="side-menu-opener"></button>
</div>
<div class="side-menu-categories-wrapper">
@@ -28,7 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<ul class="side-menu-apps-list">
<li v-bind:class="{'side-menu-app': true, 'active': activeApp === appId}" v-for="(app, appId) in category.apps">
<a v-bind:href="app.href" v-bind:title="app.name">
<a v-bind:href="app.href" :target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined" v-bind:title="app.name">
<img class="side-menu-app-icon" v-bind:src="app.icon"></span>
<span class="side-menu-app-text" v-html="app.name"></span>
</a>
@@ -50,6 +50,8 @@ export default {
return {
items: [],
activeApp: null,
targetBlank: false,
targetBlankApps: [],
}
},
methods: {
@@ -73,12 +75,26 @@ export default {
jQuery('body').trigger('side-menu.apps', [apps])
});
},
retrieveActiveApp() {
let activeAppLink = document.querySelector('#appmenu a.active')
this.activeApp = activeAppLink ? activeAppLink.parentNode.getAttribute('data-id') : null
},
retrieveConfig() {
let that = this
axios
.get(OC.generateUrl('/apps/side_menu/js/config'))
.then(function(response) {
const config = response.data
that.targetBlankApps = config['target-blank-apps']
});
},
},
mounted() {
this.retrieveConfig()
this.retrieveApps()
this.retrieveActiveApp()
}
+11
View File
@@ -81,6 +81,10 @@ const saveSettings = (key) => {
}
}
const elementToggler = (element) => {
jQuery(element).toggle()
}
jQuery(document).ready(() => {
elements = jQuery('.side-menu-setting')
@@ -118,4 +122,11 @@ jQuery(document).ready(() => {
document.documentElement.style.setProperty('--side-menu-' + name, value)
})
jQuery('.side-menu-toggler').on('click', (event) => {
var target = jQuery(event.target)
var element = target.attr('data-target')
elementToggler(element)
})
});
+16 -11
View File
@@ -1,16 +1,16 @@
"Side menu": "Seitenmenü"
"Enable the side menu": "Aktivieren Sie das Seitenmenü"
"Enable the side menu": "Aktiviere das Seitenmenü"
"No": "Nein"
"Yes": "Ja"
"Menu": "Speisekarte"
'Use the shortcut <span class="keyboard-key">Ctrl</span>+<span class="keyboard-key">o</span> to open and to hide the side menu. Use <span class="keyboard-key">tab</span> to navigate.': 'Verwenden Sie die Tastenkombination <span class="keyboard-key">Strg</span>+<span class="keyboard-key">o</span>, um das Seitenmenü zu öffnen und auszublenden. Verwenden Sie zum Navigieren die Registerkarte <span class="keyboard-key">tab</span>.'
"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 im Seitenmenü verschoben werden müssen"
"Apps that not must be moved in the side menu": "Apps, die nicht ins Seitenmenü verschoben werden müssen"
"If there is no selection then the global configuration is applied.": "Wenn keine Auswahl vorhanden ist, wird die globale Konfiguration angewendet."
"Experimental": "Experimental"
"Save": "Sparen"
"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 coffe ☕": "Gib mir einen Kaffee ☕"
"Buy me a coffee ☕": "Gib mir einen Kaffee ☕"
"Hidden": "Versteckt"
"Small": "Klein"
"Normal": "Normal"
@@ -36,13 +36,13 @@
"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).": "Zeigen Sie das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z. B. öffentliche Seiten)."
"Do not display the side menu and the opener if there is no application (eg: public pages).": "Zeige das Seitenmenü und den Öffner nicht an, wenn keine Anwendung vorhanden ist (z.B. bei öffentlichen Seiten)."
"Panel": "Panel"
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffnen Sie das Menü, wenn Sie die Maus über den Öffner bewegen (auf Touchscreens automatisch deaktiviert)."
"Open the menu when the mouse is hover the opener (automatically disabled on touch screens)": "Öffne das Menü, wenn die Maus über den Öffner bewegt wird (auf Touchscreens automatisch deaktiviert)."
"Display the big menu": "Großes Menü anzeigen"
"The big menu is not compatible with AppOrder.": "Das große Menü ist nicht mit AppOrder kompatibel."
"Display the logo": "Logo anzeigen"
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit der großen Menüanzeige kompatibel."
"This feature is not compatible with the <code>big menu</code> display.": "Diese Funktion ist nicht mit <code>großes Menü</code> kompatibel."
"Icons and texts": "Symbole und Texte"
"Loader enabled": "Loader aktiviert"
"Tips": "Tipps"
@@ -50,5 +50,10 @@
"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ße Speisekarte"
"Big menu": "Großes Menü"
"Live preview": "Live Vorschau"
"Open apps in new tab": "Öffne Apps in einem neuen Tab"
"Use the global setting": "Verwende die globale Einstellung"
"Use my selection": "Verwende meine Auswahl"
"Show and hide the list of applications": "Ein- und Ausblenden der Anwendungsliste"
"Use the avatar instead of the logo": "Verwenden Sie den Avatar anstelle des Logos"
+6 -1
View File
@@ -10,7 +10,7 @@
"Experimental": "Expérimental"
"Save": "Sauvegarder"
"You like this app and you want to support me?": "Vous aimer cette application et vous souhaitez m'aider ?"
"Buy me a coffe ☕": "Offrez moi un café ☕"
"Buy me a coffee ☕": "Offrez moi un café ☕"
"Hidden": "Caché"
"Small": "Petit"
"Normal": "Normal"
@@ -52,3 +52,8 @@
"Not compatible with touch screens.": "Incompatible avec les écrans tactiles."
"Big menu": "Menu large"
"Live preview": "Aperçu en direct"
"Open apps in new tab": "Ouvrir les applications dans un nouvel onglet"
"Use the global setting": "Utiliser la configuration globale"
"Use my selection": "Utiliser ma sélection"
"Show and hide the list of applications": "Afficher et masquer la liste des applications"
"Use the avatar instead of the logo": "Utiliser l'avatar à la place du logo"
+1 -1
View File
@@ -41,4 +41,4 @@ var alwaysDisplayed = function() {
}
}
alwaysDisplayed()
window.setTimeout(alwaysDisplayed, 200)
+4
View File
@@ -38,6 +38,10 @@ var updateTopMenu = function() {
appShown.push(app)
navigationApps.append(app.outerHTML)
}
if (targetBlankApps.indexOf(dataId) !== -1) {
jQuery(app).children('a').attr('target', '_blank');
}
})
var rightHeaderWidth = jQuery('.header-right').outerWidth()
+9 -6
View File
@@ -10,9 +10,7 @@
sideMenu.attr('data-bigmenu', '1')
<?php endif; ?>
<?php if ($_['force-light-icon']): ?>
sideMenuContainer.attr('data-forcelighticon', '1')
<?php endif; ?>
var targetBlankApps = <?php echo json_encode($_['target-blank-apps']) ?>;
body.on('side-menu.apps', function(e, apps) {
<?php if ($_['hide-when-no-apps']): ?>
@@ -63,7 +61,7 @@
}
}
<?php if ($_['opener-hover']): ?>
<?php if ($_['opener-hover'] || ($_['always-displayed'] && !$_['big-menu'])): ?>
var sideMenuMouseLeave = function() {
sideMenu
.removeClass('open')
@@ -83,9 +81,14 @@
}
if (!isTouchDevice) {
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter)
<?php if ($_['opener-hover']): ?>
headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter)
sideMenu.addClass('hide-opener')
sideMenu.addClass('hide-opener')
<?php else: ?>
sideMenu.on('mouseleave', sideMenuMouseLeave)
sideMenu.on('mouseenter', sideMenuOpenerMouseEnter)
<?php endif ?>
}
<?php endif; ?>
+52 -2
View File
@@ -342,6 +342,18 @@ $choicesSizes = [
</select>
</div>
<p><em><?php p($l->t('Use the avatar instead of the logo')); ?></em></p>
<div>
<select id="side-menu-use-avatar" name="use-avatar" class="side-menu-setting">
<?php foreach ($choicesYesNo as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['use-avatar']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label>
<?php p($l->t('Icons and texts')); ?>
@@ -366,6 +378,38 @@ $choicesSizes = [
</select>
</div>
<div>
<label for="side-menu-target-blank">
<?php p($l->t('Open apps in new tab')); ?>
</label>
</div>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div>
<?php endforeach; ?>
</div>
<br>
<div>
<label for="side-menu-loader-enabled">
<?php p($l->t('Loader enabled')); ?>
@@ -393,7 +437,13 @@ $choicesSizes = [
</label>
</div>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox>
<p>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" id="top-menu-apps" data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
@@ -437,7 +487,7 @@ $choicesSizes = [
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button>
<?php p($l->t('Buy me a coffe ☕')); ?>
<?php p($l->t('Buy me a coffee ☕')); ?>
</button>
</a>
</span>
+53 -2
View File
@@ -52,6 +52,51 @@ $choicesYesNo = [
<?php endforeach; ?>
</select>
</div>
<div>
<label for="side-menu-target-blank">
<?php p($l->t('Open apps in new tab')); ?>
</label>
</div>
<div>
<?php $choices = [
'Use the global setting' => '1',
'Use my selection' => '2',
]; ?>
<select id="side-menu-loader-enabled" name="target-blank-mode" class="side-menu-setting" data-personal>
<?php foreach ($choices as $label => $value): ?>
<option value="<?php echo $value ?>" <?php if ($value === $_['target-blank-mode']): ?>selected<?php endif; ?>>
<?php echo $l->t($label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<p>
<a class="side-menu-toggler" data-target="#target-blank-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="target-blank-apps" id="target-blank-apps" data-personal data-checkbox style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
type="checkbox"
name="target-blank-apps[]"
value="<?php echo $app['id'] ?>"
id="target-blank-app-<?php echo $app['id'] ?>"
<?php if (in_array($app['id'], $_['target-blank-apps'])): ?>checked<?php endif; ?>
/>
<label for="target-blank-app-<?php echo $app['id'] ?>">
<?php echo p($l->t($app['name'])); ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="section">
@@ -70,7 +115,13 @@ $choicesYesNo = [
</em>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal>
<p>
<a class="side-menu-toggler" data-target="#top-menu-apps" href="#_">
🖱️ <?php p($l->t('Show and hide the list of applications')); ?>
</a>
</p>
<div class="side-menu-setting" data-name="top-menu-apps" data-checkbox data-personal id="top-menu-apps" style="display: none">
<?php foreach ($_['apps'] as $app): ?>
<div>
<input
@@ -102,7 +153,7 @@ $choicesYesNo = [
<a style="margin-left: 10px" target="_blank" href="https://www.buymeacoffee.com/deblan">
<button>
<?php p($l->t('Buy me a coffe ☕')); ?>
<?php p($l->t('Buy me a coffee ☕')); ?>
</button>
</a>
</span>