Skip to content

Commit 0fa427e

Browse files
committed
feat: Aggiunta possibilità prevedere collegamenti sulla barra di navigazione superiore definendoli da DB
1 parent f0c0ecb commit 0fa427e

4 files changed

Lines changed: 356 additions & 1 deletion

File tree

include/top.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,19 @@
334334
<script type="text/javascript" charset="utf-8" src="'.$js.'"></script>';
335335
}
336336

337+
// Autoload asset da moduli
338+
// Cerca in modules/{directory}/assets/navbar.js
339+
foreach (Module::all(['id', 'directory']) as $mod) {
340+
if (empty($mod->directory)) {
341+
continue;
342+
}
343+
$relative = '/modules/'.$mod->directory.'/assets/navbar.js';
344+
if (file_exists(base_dir().$relative)) {
345+
echo '
346+
<script type="text/javascript" charset="utf-8" src="'.base_path_osm().$relative.'"></script>';
347+
}
348+
}
349+
337350
// Impostazioni di default per gli alert
338351
echo '
339352
<script>
@@ -495,7 +508,7 @@
495508
<i class="fa fa-info nav-icon"></i>
496509
</a>
497510
</li>
498-
511+
'.NavbarLinks::renderRight().'
499512
<li class="nav-item">
500513
<a href="'.base_path_osm().'/index.php?op=logout" onclick="sessionStorage.clear()" class="nav-link bg-danger" title="'.tr('Esci').'">
501514
<i class="fa fa-power-off nav-icon"></i>

src/Models/Link.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
/*
4+
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
5+
* Copyright (C) DevCode s.r.l.
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
namespace Models;
22+
23+
use Common\SimpleModelTrait;
24+
use Illuminate\Database\Eloquent\Model;
25+
use Traits\RecordTrait;
26+
27+
/**
28+
* Voce della Navbar Right/Left (tabella `zz_links`).
29+
*
30+
* Campi traducibili (`label`, `title`) vivono in `zz_links_lang` — vedi RecordTrait.
31+
*/
32+
class Link extends Model
33+
{
34+
use SimpleModelTrait;
35+
use RecordTrait;
36+
37+
protected $table = 'zz_links';
38+
39+
protected static $translated_fields = [
40+
'label',
41+
'title',
42+
];
43+
44+
public function parent()
45+
{
46+
return $this->belongsTo(self::class, 'parent');
47+
}
48+
49+
public function children()
50+
{
51+
return $this->hasMany(self::class, 'parent')->orderBy('order');
52+
}
53+
54+
public function hostModule()
55+
{
56+
return $this->belongsTo(Module::class, 'id_module');
57+
}
58+
59+
public function hasChildren(): bool
60+
{
61+
return $this->children()->exists();
62+
}
63+
64+
public function getLabelAttribute(): string
65+
{
66+
return (string) $this->getTranslation('label');
67+
}
68+
69+
public function getTitleAttribute(): string
70+
{
71+
return (string) $this->getTranslation('title');
72+
}
73+
74+
public static function getTranslatedFields()
75+
{
76+
return self::$translated_fields;
77+
}
78+
79+
public function getModuleAttribute()
80+
{
81+
return $this->hostModule ? $this->hostModule->name : null;
82+
}
83+
}

src/NavbarLinks.php

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
<?php
2+
3+
/*
4+
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
5+
* Copyright (C) DevCode s.r.l.
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
use Illuminate\Database\Eloquent\Collection;
22+
use Models\Link;
23+
use Models\Module;
24+
use Models\Plugin;
25+
26+
/**
27+
* Helper per il rendering delle voci navbar dalla tabella `zz_links`.
28+
*
29+
* `renderRight()` / `renderLeft()` ritornano HTML pronto per essere concatenato
30+
* dentro `include/top.php`. Ogni campo DB è escapato via `e()`.
31+
*/
32+
class NavbarLinks
33+
{
34+
/** Regex di sicurezza per value di type=javascript (nome funzione globale). */
35+
private const JS_VALUE_REGEX = '/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/';
36+
37+
public static function renderRight(): string
38+
{
39+
return self::renderPosition('right');
40+
}
41+
42+
public static function renderLeft(): string
43+
{
44+
return self::renderPosition('left');
45+
}
46+
47+
private static function renderPosition(string $position): string
48+
{
49+
$links = self::getTopLevelLinks($position);
50+
51+
$html = '';
52+
foreach ($links as $link) {
53+
if (!self::visible($link)) {
54+
continue;
55+
}
56+
$html .= self::renderItem($link);
57+
}
58+
59+
return $html;
60+
}
61+
62+
private static function getTopLevelLinks(string $position): Collection
63+
{
64+
return Link::query()
65+
->where('position', $position)
66+
->whereNull('parent')
67+
->where('enabled', 1)
68+
->orderBy('order')
69+
->with(['children' => function ($q) {
70+
$q->where('enabled', 1)->orderBy('order');
71+
}])
72+
->get();
73+
}
74+
75+
private static function renderItem(Link $link): string
76+
{
77+
if ($link->hasChildren()) {
78+
return self::renderDropdown($link);
79+
}
80+
81+
return self::renderSingle($link);
82+
}
83+
84+
private static function renderSingle(Link $link): string
85+
{
86+
$iconClass = trim($link->icon.' nav-icon '.(string) $link->color);
87+
88+
return '<li class="nav-item">'
89+
.'<a href="'.e(self::url($link)).'" class="nav-link" '
90+
.self::onclickAttr($link).' '
91+
.self::targetAttr($link).' '
92+
.'title="'.e($link->title).'">'
93+
.'<i class="'.e($iconClass).'"></i>'
94+
.'</a>'
95+
.'</li>';
96+
}
97+
98+
private static function renderDropdown(Link $link): string
99+
{
100+
$iconClass = trim($link->icon.' nav-icon '.(string) $link->color);
101+
102+
$items = '';
103+
foreach ($link->children as $child) {
104+
if (!self::visible($child)) {
105+
continue;
106+
}
107+
$childIcon = trim($child->icon.' '.(string) $child->color);
108+
$items .= '<a class="dropdown-item" href="'.e(self::url($child)).'" '
109+
.self::onclickAttr($child).' '
110+
.self::targetAttr($child).' '
111+
.'title="'.e($child->title).'">'
112+
.'<i class="'.e($childIcon).'"></i> '
113+
.e($child->label)
114+
.'</a>';
115+
}
116+
117+
return '<li class="nav-item dropdown">'
118+
.'<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" '
119+
.'title="'.e($link->title).'">'
120+
.'<i class="'.e($iconClass).'"></i>'
121+
.'</a>'
122+
.'<div class="dropdown-menu dropdown-menu-right">'
123+
.$items
124+
.'</div>'
125+
.'</li>';
126+
}
127+
128+
public static function url(Link $link): string
129+
{
130+
switch ($link->type) {
131+
case 'link':
132+
return (string) $link->value;
133+
134+
case 'javascript':
135+
return '#';
136+
137+
case 'module':
138+
$mod = Module::where('name', $link->value)->first();
139+
if (!$mod) {
140+
return '#';
141+
}
142+
143+
return base_path_osm().'/controller.php?id_module='.$mod->id;
144+
145+
case 'plugin':
146+
$plg = Plugin::where('name', $link->value)->first();
147+
if (!$plg) {
148+
return '#';
149+
}
150+
151+
return base_path_osm().'/plugin_editor.php?id_plugin='.$plg->id;
152+
}
153+
154+
return '#';
155+
}
156+
157+
public static function onclickAttr(Link $link): string
158+
{
159+
if ($link->type !== 'javascript') {
160+
return '';
161+
}
162+
163+
$fn = (string) $link->value;
164+
if (!preg_match(self::JS_VALUE_REGEX, $fn)) {
165+
return '';
166+
}
167+
168+
return 'onclick="if(typeof window[\''.$fn.'\']===\'function\'){window[\''.$fn.'\']();}return false;"';
169+
}
170+
171+
public static function targetAttr(Link $link): string
172+
{
173+
return $link->type === 'link' ? 'target="_blank"' : '';
174+
}
175+
176+
public static function visible(Link $link): bool
177+
{
178+
if (!$link->enabled) {
179+
return false;
180+
}
181+
182+
if ($link->type === 'module') {
183+
$mod = Module::where('name', $link->value)->first();
184+
if (!$mod || !$mod->enabled) {
185+
return false;
186+
}
187+
188+
return Modules::getPermission($mod->id) !== '-';
189+
}
190+
191+
if ($link->type === 'plugin') {
192+
$plg = Plugin::where('name', $link->value)->first();
193+
if (!$plg || !$plg->enabled) {
194+
return false;
195+
}
196+
197+
return $plg->permission !== '-';
198+
}
199+
200+
if ($link->id_module) {
201+
return Modules::getPermission($link->id_module) !== '-';
202+
}
203+
204+
return true;
205+
}
206+
207+
public static function validateValue(string $type, string $value): bool
208+
{
209+
if ($type === 'javascript') {
210+
return (bool) preg_match(self::JS_VALUE_REGEX, $value);
211+
}
212+
213+
return true;
214+
}
215+
}

update/2_11.sql

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,47 @@ INSERT INTO `zz_views_lang` (`id_lang`, `id_record`, `title`) VALUES
324324

325325
-- Aggiunta del campo serial alla tabella my_componenti per la gestione dei seriali nei componenti degli impianti
326326
ALTER TABLE `my_componenti` ADD `serial` VARCHAR(255) NULL AFTER `id_articolo`;
327+
328+
-- Tabelle per la gestione DB-driven delle voci della Navbar Right Menu.
329+
-- I moduli consumer aggiungono righe qui invece di iniettare <li> via JS DOM-manipulation.
330+
CREATE TABLE IF NOT EXISTS `zz_links` (
331+
`id` INT(11) NOT NULL AUTO_INCREMENT,
332+
`name` VARCHAR(255) NOT NULL,
333+
`icon` VARCHAR(255) NOT NULL DEFAULT '',
334+
`color` VARCHAR(64) NULL DEFAULT NULL,
335+
`position` ENUM('right','left') NOT NULL DEFAULT 'right',
336+
`order` INT(11) NOT NULL DEFAULT 0,
337+
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
338+
`type` ENUM('link','javascript','module','plugin') NOT NULL,
339+
`value` VARCHAR(500) NOT NULL,
340+
`parent` INT(11) NULL DEFAULT NULL,
341+
`id_module` INT(11) NULL DEFAULT NULL,
342+
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
343+
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
344+
PRIMARY KEY (`id`),
345+
UNIQUE KEY `uk_zz_links_name` (`name`),
346+
KEY `idx_zz_links_parent` (`parent`),
347+
KEY `idx_zz_links_enabled_order` (`enabled`, `order`),
348+
KEY `idx_zz_links_id_module` (`id_module`),
349+
CONSTRAINT `fk_zz_links_parent`
350+
FOREIGN KEY (`parent`) REFERENCES `zz_links`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT,
351+
CONSTRAINT `fk_zz_links_module`
352+
FOREIGN KEY (`id_module`) REFERENCES `zz_modules`(`id`) ON DELETE SET NULL ON UPDATE RESTRICT
353+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
354+
355+
CREATE TABLE IF NOT EXISTS `zz_links_lang` (
356+
`id` INT(11) NOT NULL AUTO_INCREMENT,
357+
`id_lang` INT(11) NOT NULL,
358+
`id_record` INT(11) NOT NULL,
359+
`label` VARCHAR(255) NOT NULL DEFAULT '',
360+
`title` VARCHAR(255) NOT NULL DEFAULT '',
361+
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
362+
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
363+
PRIMARY KEY (`id`),
364+
UNIQUE KEY `uk_zz_links_lang_record` (`id_lang`, `id_record`),
365+
KEY `idx_zz_links_lang_record` (`id_record`),
366+
CONSTRAINT `fk_zz_links_lang_record`
367+
FOREIGN KEY (`id_record`) REFERENCES `zz_links`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT,
368+
CONSTRAINT `fk_zz_links_lang_lang`
369+
FOREIGN KEY (`id_lang`) REFERENCES `zz_langs`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT
370+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

0 commit comments

Comments
 (0)