Skip to content

Commit e7135e3

Browse files
committed
feat: Aggiunto modulo Link navbar sotto Strumenti per gestire link sulla navbar
1 parent bb3aee5 commit e7135e3

7 files changed

Lines changed: 444 additions & 18 deletions

File tree

include/top.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@
505505
<i class="fa fa-info nav-icon"></i>
506506
</a>
507507
</li>
508-
'.NavbarLinks::renderRight().'
508+
'.NavbarLinks::render().'
509509
<li class="nav-item">
510510
<a href="'.base_path_osm().'/index.php?op=logout" onclick="sessionStorage.clear()" class="nav-link bg-danger" title="'.tr('Esci').'">
511511
<i class="fa fa-power-off nav-icon"></i>

modules/link_navbar/actions.php

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
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+
include_once __DIR__.'/../../core.php';
22+
23+
use Models\Link;
24+
25+
/**
26+
* Normalizza il campo assets dal POST.
27+
* Accetta JSON array o stringa vuota. Restituisce stringa JSON o null.
28+
*/
29+
$normalize_assets = function ($raw) {
30+
$raw = trim((string) $raw);
31+
if ($raw === '') {
32+
return null;
33+
}
34+
$decoded = json_decode($raw, true);
35+
if (!is_array($decoded)) {
36+
return false; // marker errore parsing
37+
}
38+
// Filtra elementi non-string e vuoti
39+
$clean = [];
40+
foreach ($decoded as $entry) {
41+
if (is_string($entry) && trim($entry) !== '') {
42+
$clean[] = trim($entry);
43+
}
44+
}
45+
46+
return empty($clean) ? null : json_encode($clean, JSON_UNESCAPED_SLASHES);
47+
};
48+
49+
/**
50+
* Validazione e normalizzazione type/value.
51+
*/
52+
$validate_value = function ($type, $value) {
53+
if ($type === 'javascript') {
54+
return NavbarLinks::validateValue('javascript', (string) $value);
55+
}
56+
57+
return true;
58+
};
59+
60+
switch (post('op')) {
61+
case 'update':
62+
$name = trim((string) post('name'));
63+
$type = post('type');
64+
$value = trim((string) post('value'));
65+
66+
if (empty($name)) {
67+
flash()->error(tr('Il nome interno è obbligatorio.'));
68+
break;
69+
}
70+
71+
// Unique name check (escludi record corrente)
72+
$exists = Link::where('name', $name)->where('id', '!=', $id_record)->exists();
73+
if ($exists) {
74+
flash()->error(tr('Il nome interno _NAME_ è già in uso.', ['_NAME_' => $name]));
75+
break;
76+
}
77+
78+
if (!$validate_value($type, $value)) {
79+
flash()->error(tr('Per type=javascript il valore deve essere un nome funzione globale valido (regex ^[a-zA-Z_$][a-zA-Z0-9_$.]*$).'));
80+
break;
81+
}
82+
83+
$assets_normalized = $normalize_assets(post('assets'));
84+
if ($assets_normalized === false) {
85+
flash()->error(tr('Il campo Assets contiene JSON non valido.'));
86+
break;
87+
}
88+
89+
$link->name = $name;
90+
$link->icon = (string) post('icon');
91+
$link->color = post('color') ?: null;
92+
$link->order = (int) post('order');
93+
$link->enabled = (int) post('enabled');
94+
$link->type = $type;
95+
$link->value = $value;
96+
$link->parent = post('parent') ?: null;
97+
$link->id_module = post('id_module') ?: null;
98+
$link->assets = $assets_normalized;
99+
$link->save();
100+
101+
$link->setTranslation('label', (string) post('label'));
102+
$link->setTranslation('title', (string) post('title'));
103+
104+
flash()->info(tr('Voce navbar salvata.'));
105+
break;
106+
107+
case 'add':
108+
$name = trim((string) post('name'));
109+
$label = trim((string) post('label'));
110+
$type = post('type');
111+
$value = trim((string) post('value'));
112+
113+
if (empty($name) || empty($label) || empty($type) || empty($value)) {
114+
flash()->error(tr('Compilare tutti i campi obbligatori.'));
115+
break;
116+
}
117+
118+
if (Link::where('name', $name)->exists()) {
119+
flash()->error(tr('Il nome interno _NAME_ è già in uso.', ['_NAME_' => $name]));
120+
break;
121+
}
122+
123+
if (!$validate_value($type, $value)) {
124+
flash()->error(tr('Per type=javascript il valore deve essere un nome funzione globale valido.'));
125+
break;
126+
}
127+
128+
$new = new Link();
129+
$new->name = $name;
130+
$new->icon = (string) post('icon') ?: 'fa fa-link';
131+
$new->color = post('color') ?: null;
132+
$new->order = (int) post('order');
133+
$new->enabled = 1;
134+
$new->type = $type;
135+
$new->value = $value;
136+
$new->save();
137+
138+
$new->setTranslation('label', $label);
139+
$new->setTranslation('title', $label);
140+
141+
$id_record = $new->id;
142+
flash()->info(tr('Voce navbar creata.'));
143+
break;
144+
145+
case 'delete':
146+
if (!empty($link)) {
147+
$link->delete();
148+
flash()->info(tr('Voce navbar eliminata.'));
149+
}
150+
break;
151+
}

modules/link_navbar/add.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
include_once __DIR__.'/../../core.php';
22+
?>
23+
<form action="" method="post" id="add-form">
24+
<input type="hidden" name="op" value="add">
25+
<input type="hidden" name="backto" value="record-edit">
26+
27+
<div class="row">
28+
<div class="col-md-6">
29+
{[ "type": "text", "label": "<?php echo tr('Nome interno (slug univoco)'); ?>", "name": "name", "required": 1, "help": "<?php echo tr('Identificatore macchina, lowercase con trattini'); ?>" ]}
30+
</div>
31+
<div class="col-md-6">
32+
{[ "type": "text", "label": "<?php echo tr('Etichetta visibile'); ?>", "name": "label", "required": 1, "help": "<?php echo tr('Testo visibile nei dropdown'); ?>" ]}
33+
</div>
34+
</div>
35+
36+
<div class="row">
37+
<div class="col-md-4">
38+
{[ "type": "select", "label": "<?php echo tr('Tipo'); ?>", "name": "type", "required": 1, "values": "list=\"link\": \"<?php echo tr('Link URL'); ?>\", \"javascript\": \"<?php echo tr('JavaScript'); ?>\", \"module\": \"<?php echo tr('Modulo'); ?>\", \"plugin\": \"<?php echo tr('Plugin'); ?>\"" ]}
39+
</div>
40+
<div class="col-md-8">
41+
{[ "type": "text", "label": "<?php echo tr('Valore'); ?>", "name": "value", "required": 1, "help": "<?php echo tr('URL per Link, nome funzione globale per JavaScript, nome modulo/plugin'); ?>" ]}
42+
</div>
43+
</div>
44+
45+
<div class="row">
46+
<div class="col-md-4">
47+
{[ "type": "text", "label": "<?php echo tr('Icona'); ?>", "name": "icon", "value": "fa fa-link", "help": "<?php echo tr('Classe FontAwesome'); ?>" ]}
48+
</div>
49+
<div class="col-md-4">
50+
{[ "type": "select", "label": "<?php echo tr('Colore'); ?>", "name": "color", "values": "list=\"\": \"<?php echo tr('Nessuno'); ?>\", \"text-primary\": \"<?php echo tr('Primary'); ?>\", \"text-success\": \"<?php echo tr('Success'); ?>\", \"text-warning\": \"<?php echo tr('Warning'); ?>\", \"text-danger\": \"<?php echo tr('Danger'); ?>\", \"text-info\": \"<?php echo tr('Info'); ?>\", \"text-muted\": \"<?php echo tr('Muted'); ?>\"" ]}
51+
</div>
52+
<div class="col-md-4">
53+
{[ "type": "number", "label": "<?php echo tr('Ordine'); ?>", "name": "order", "value": "0" ]}
54+
</div>
55+
</div>
56+
57+
<div class="modal-footer">
58+
<div class="col-md-12 text-right">
59+
<button type="submit" class="btn btn-primary"><i class="fa fa-plus"></i> <?php echo tr('Aggiungi'); ?></button>
60+
</div>
61+
</div>
62+
</form>

modules/link_navbar/edit.php

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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+
include_once __DIR__.'/../../core.php';
22+
23+
$assets_pretty = '';
24+
if (!empty($record['assets'])) {
25+
$decoded = json_decode($record['assets'], true);
26+
if (is_array($decoded)) {
27+
$assets_pretty = json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
28+
} else {
29+
$assets_pretty = $record['assets'];
30+
}
31+
}
32+
?>
33+
<form action="" method="post" id="edit-form">
34+
<input type="hidden" name="op" value="update">
35+
<input type="hidden" name="backto" value="record-edit">
36+
37+
<div class="row">
38+
<div class="col-md-8">
39+
<div class="card card-primary">
40+
<div class="card-header">
41+
<h3 class="card-title"><?php echo tr('Informazioni base'); ?></h3>
42+
</div>
43+
<div class="card-body">
44+
<div class="row">
45+
<div class="col-md-6">
46+
{[ "type": "text", "label": "<?php echo tr('Nome interno'); ?>", "name": "name", "required": 1, "value": "$name$", "help": "<?php echo tr('Identificatore macchina univoco (slug). Usato come riferimento dai moduli consumer.'); ?>" ]}
47+
</div>
48+
<div class="col-md-6">
49+
{[ "type": "text", "label": "<?php echo tr('Etichetta'); ?>", "name": "label", "required": 1, "value": "$label$" ]}
50+
</div>
51+
</div>
52+
53+
<div class="row">
54+
<div class="col-md-12">
55+
{[ "type": "text", "label": "<?php echo tr('Tooltip'); ?>", "name": "title", "value": "$title$" ]}
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
</div>
61+
62+
<div class="col-md-4">
63+
<div class="card card-primary">
64+
<div class="card-header">
65+
<h3 class="card-title"><?php echo tr('Stato'); ?></h3>
66+
</div>
67+
<div class="card-body">
68+
{[ "type": "checkbox", "label": "<?php echo tr('Abilitato'); ?>", "name": "enabled", "value": "$enabled$" ]}
69+
70+
{[ "type": "number", "label": "<?php echo tr('Ordine'); ?>", "name": "order", "value": "$order$" ]}
71+
</div>
72+
</div>
73+
</div>
74+
</div>
75+
76+
<div class="card card-primary">
77+
<div class="card-header">
78+
<h3 class="card-title"><?php echo tr('Comportamento'); ?></h3>
79+
</div>
80+
<div class="card-body">
81+
<div class="row">
82+
<div class="col-md-4">
83+
{[ "type": "select", "label": "<?php echo tr('Tipo'); ?>", "name": "type", "required": 1, "value": "$type$", "values": "list=\"link\": \"<?php echo tr('Link URL'); ?>\", \"javascript\": \"<?php echo tr('JavaScript'); ?>\", \"module\": \"<?php echo tr('Modulo'); ?>\", \"plugin\": \"<?php echo tr('Plugin'); ?>\"" ]}
84+
</div>
85+
<div class="col-md-8">
86+
{[ "type": "text", "label": "<?php echo tr('Valore'); ?>", "name": "value", "required": 1, "value": "$value$", "help": "<?php echo tr('Link: URL. JavaScript: nome funzione globale (regex ^[a-zA-Z_$][a-zA-Z0-9_$.]*$). Modulo/Plugin: nome registrato in zz_modules / zz_plugins.'); ?>" ]}
87+
</div>
88+
</div>
89+
</div>
90+
</div>
91+
92+
<div class="card card-primary">
93+
<div class="card-header">
94+
<h3 class="card-title"><?php echo tr('Aspetto'); ?></h3>
95+
</div>
96+
<div class="card-body">
97+
<div class="row">
98+
<div class="col-md-6">
99+
{[ "type": "text", "label": "<?php echo tr('Icona'); ?>", "name": "icon", "value": "$icon$", "icon-after": "<i class=\"<?php echo addslashes((string) ($record['icon'] ?? '')); ?>\"></i>" ]}
100+
</div>
101+
<div class="col-md-6">
102+
{[ "type": "select", "label": "<?php echo tr('Colore'); ?>", "name": "color", "value": "$color$", "values": "list=\"\": \"<?php echo tr('Nessuno'); ?>\", \"text-primary\": \"<?php echo tr('Primary'); ?>\", \"text-success\": \"<?php echo tr('Success'); ?>\", \"text-warning\": \"<?php echo tr('Warning'); ?>\", \"text-danger\": \"<?php echo tr('Danger'); ?>\", \"text-info\": \"<?php echo tr('Info'); ?>\", \"text-muted\": \"<?php echo tr('Muted'); ?>\"" ]}
103+
</div>
104+
</div>
105+
</div>
106+
</div>
107+
108+
<div class="card card-primary">
109+
<div class="card-header">
110+
<h3 class="card-title"><?php echo tr('Struttura e permessi'); ?></h3>
111+
</div>
112+
<div class="card-body">
113+
<div class="row">
114+
<div class="col-md-6">
115+
{[ "type": "select", "label": "<?php echo tr('Voce padre'); ?>", "name": "parent", "value": "$parent$", "values": "query=SELECT zz_links.id AS id, CONCAT(zz_links.name, ' (', COALESCE(zz_links_lang.label, ''), ')') AS descrizione FROM zz_links LEFT JOIN zz_links_lang ON (zz_links.id = zz_links_lang.id_record AND zz_links_lang.id_lang = <?php echo prepare(Models\Locale::getDefault()->id); ?>) WHERE zz_links.id != <?php echo (int) $id_record; ?> AND zz_links.parent IS NULL ORDER BY zz_links.name", "placeholder": "<?php echo tr('Top level'); ?>", "help": "<?php echo tr('Se valorizzato, voce appare come elemento di un dropdown.'); ?>" ]}
116+
</div>
117+
<div class="col-md-6">
118+
{[ "type": "select", "label": "<?php echo tr('Modulo associato'); ?>", "name": "id_module", "value": "$id_module$", "values": "query=SELECT zz_modules.id AS id, COALESCE(zz_modules_lang.title, zz_modules.name) AS descrizione FROM zz_modules LEFT JOIN zz_modules_lang ON (zz_modules.id = zz_modules_lang.id_record AND zz_modules_lang.id_lang = <?php echo prepare(Models\Locale::getDefault()->id); ?>) WHERE zz_modules.enabled = 1 ORDER BY descrizione", "placeholder": "<?php echo tr('Nessuno'); ?>", "help": "<?php echo tr('Se valorizzato per type=link/javascript: voce visibile solo se utente ha permessi sul modulo. Determina anche directory per asset shorthand.'); ?>" ]}
119+
</div>
120+
</div>
121+
</div>
122+
</div>
123+
124+
<div class="card card-primary">
125+
<div class="card-header">
126+
<h3 class="card-title"><?php echo tr('Asset JS'); ?></h3>
127+
</div>
128+
<div class="card-body">
129+
<div class="row">
130+
<div class="col-md-12">
131+
{[ "type": "textarea", "label": "<?php echo tr('Lista asset (JSON array)'); ?>", "name": "assets", "value": "<?php echo addslashes($assets_pretty); ?>", "rows": "5", "help": "<?php echo tr('Esempio: [\"file.js\", \"modules/altro/assets/dist/js/x.js\"]. Shorthand (solo filename) richiede modulo associato e cerca in modules/{dir}/assets/dist/js/. Path con / è interpretato da OSM root (cross-module).'); ?>" ]}
132+
</div>
133+
</div>
134+
</div>
135+
</div>
136+
</form>
137+
138+
<a class="btn btn-danger ask" data-backto="record-list">
139+
<i class="fa fa-trash"></i> <?php echo tr('Elimina'); ?>
140+
</a>

0 commit comments

Comments
 (0)