Campi obbligatori & fix

This commit is contained in:
2025-09-08 17:38:47 +02:00
parent cbcbcc6f8e
commit 5dec036e78
13 changed files with 711 additions and 114 deletions

View File

@ -1,4 +1,5 @@
<?php
namespace Pcrt\Component\Circolari\Site\Controller;
\defined('_JEXEC') or die;
@ -26,6 +27,10 @@ class DisplayController extends BaseController
}
}
if (!in_array($view, ['circolari', 'circolare', 'form'])) {
$this->input->set('view', $this->default_view);
}
return parent::display($cachable, $urlparams);
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace Pcrt\Component\Circolari\Site\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
class FormController extends BaseController
{
public function display($cachable = false, $urlparams = [])
{
// Solo utenti autenticati + permesso admin/manage
$app = Factory::getApplication();
$user = $app->getIdentity();
if ($user->guest || (!$user->authorise('core.manage', 'com_circolari') && !$user->authorise('core.admin', 'com_circolari'))) {
throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
$view = $this->input->getCmd('view', 'form');
$layout = $this->input->getCmd('layout', 'edit');
$id = $this->input->getInt('id');
$this->input->set('view', $view);
$this->input->set('layout', $layout);
$this->input->set('id', $id);
return parent::display();
}
public function save()
{
if (!Session::checkToken('post')) {
throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403);
}
$app = Factory::getApplication();
$user = $app->getIdentity();
if ($user->guest || (!$user->authorise('core.manage', 'com_circolari') && !$user->authorise('core.admin', 'com_circolari'))) {
throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
$data = [
'id' => $this->input->getInt('id', 0),
'title' => trim($this->input->getString('title', '')),
'alias' => trim($this->input->getString('alias', '')),
'description' => $this->input->get('description', '', 'raw'),
'categoria_id' => $this->input->getInt('categoria_id', 0),
'tipologia_firma_id' => $this->input->getInt('tipologia_firma_id', 0),
'firma_obbligatoria' => $this->input->getInt('firma_obbligatoria', 0),
'scadenza' => $this->input->getString('scadenza', ''), // 'YYYY-MM-DD HH:MM'
'state' => $this->input->getInt('state', 1),
// nuovi campi per creare tipologia al volo
'nuova_tipologia_nome' => trim($this->input->getString('nuova_tipologia_nome', '')),
'nuovi_bottoni' => trim($this->input->getString('nuovi_bottoni', '')),
];
if ($data['title'] === '') {
$app->enqueueMessage(Text::_('COM_CIRCOLARI_ERR_TITLE_REQUIRED'), 'error');
$this->setRedirect(Route::_('index.php?option=com_circolari&view=form&layout=edit&id=' . (int)$data['id'], false));
return;
}
/** @var \Pcrt\Component\Circolari\Site\Model\FormModel $model */
$model = $this->getModel('Form', 'Site');
try {
$id = $model->saveData($data, $user->id);
$app->enqueueMessage(Text::_('COM_CIRCOLARI_MSG_SAVED_OK'), 'message');
$this->setRedirect(Route::_('index.php?option=com_circolari&view=circolare&id=' . (int)$id, false));
} catch (\Throwable $e) {
$app->enqueueMessage($e->getMessage(), 'error');
$this->setRedirect(Route::_('index.php?option=com_circolari&view=form&layout=edit&id=' . (int)$data['id'], false));
}
}
public function cancel()
{
$this->setRedirect(Route::_('index.php?option=com_circolari&view=circolari', false));
}
}

View File

@ -0,0 +1,148 @@
<?php
namespace Pcrt\Component\Circolari\Site\Model;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
class FormModel extends BaseDatabaseModel
{
public function getItem(int $id = 0): ?\stdClass
{
if ($id <= 0) return null;
$db = Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('*')
->from('#__circolari')
->where('id = ' . (int)$id);
$db->setQuery($q);
return $db->loadObject() ?: null;
}
public function getCategorie(): array
{
$db = Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('id, title, state')
->from('#__circolari_categorie')
->where('state = 1')
->order('title ASC');
$db->setQuery($q);
return $db->loadAssocList() ?: [];
}
public function getFirmetipi(): array
{
$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('id, nome')
->from('#__circolari_firmetipi')
->where('state = 1')
->order('nome ASC');
$db->setQuery($q);
return $db->loadAssocList() ?: [];
}
public function getBottoniByFirmatipo(): array
{
$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('f.id AS firmatipo_id, b.label')
->from('#__circolari_firmetipi AS f')
->leftJoin('#__circolari_firmetipi_bottoni AS b ON b.firmatipo_id = f.id')
->where('f.state = 1')
->order('f.id ASC, b.ordering ASC, b.id ASC');
$db->setQuery($q);
$rows = $db->loadAssocList() ?: [];
$map = [];
foreach ($rows as $r) {
$fid = (int) $r['firmatipo_id'];
if (!isset($map[$fid])) $map[$fid] = [];
if (!empty($r['label'])) $map[$fid][] = $r['label'];
}
return $map;
}
/**
* Salva/aggiorna la circolare; crea anche una nuova tipologia e bottoni se richiesto.
* Ritorna l'ID della circolare salvata.
*/
public function saveData(array $data, int $userId): int
{
$db = Factory::getContainer()->get('DatabaseDriver');
// 2) normalizza alias
if (empty($data['alias'])) {
$data['alias'] = $this->slugify($data['title']);
}
// 3) INSERT o UPDATE su #__circolari
$now = Factory::getDate()->toSql();
$row = (object) [
'id' => (int)($data['id'] ?? 0),
'title' => $data['title'],
'alias' => $data['alias'],
'description' => $data['description'],
'categoria_id' => (int)$data['categoria_id'],
'tipologia_firma_id' => (int)$data['tipologia_firma_id'],
'firma_obbligatoria' => (int)$data['firma_obbligatoria'],
'scadenza' => $data['scadenza'] ?: null,
'state' => (int)$data['state'],
];
if ($row->id > 0) {
$row->modified_by = $userId;
$row->checked_out = 0;
$row->checked_out_time = null;
$db->updateObject('#__circolari', $row, ['id']);
$id = $row->id;
} else {
$row->created_by = $userId;
$db->insertObject('#__circolari', $row);
$id = (int)$db->insertid();
}
return $id;
}
private function createNewFirmatipo(string $nome, string $bottoniRiga): int
{
$db = Factory::getContainer()->get('DatabaseDriver');
// crea tipologia
$tipo = (object) [
'nome' => $nome,
'descrizione' => '',
'state' => 1,
];
$db->insertObject('#__circolari_firmetipi', $tipo);
$id = (int) $db->insertid();
// crea bottoni (uno per riga)
$labels = array_filter(array_map('trim', preg_split('/\R+/', $bottoniRiga ?: '')));
$ordering = 1;
foreach ($labels as $label) {
$btn = (object) [
'firmatipo_id' => $id,
'label' => $label,
'ordering' => $ordering++,
];
$db->insertObject('#__circolari_firmetipi_bottoni', $btn);
}
return $id;
}
private function slugify(string $s): string
{
$t = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
$t = strtolower($t);
$t = preg_replace('~[^a-z0-9]+~', '-', $t);
$t = trim($t, '-');
return $t ?: 'circolare';
}
}

View File

@ -1,4 +1,5 @@
<?php
namespace Pcrt\Component\Circolari\Site\Service;
\defined('_JEXEC') or die;
@ -8,123 +9,101 @@ use Joomla\CMS\Menu\AbstractMenu;
use Joomla\CMS\Factory;
use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
class Router extends RouterView
{
public function __construct(SiteApplication $app, AbstractMenu $menu)
{
// Parent: category (annidata → più segmenti)
// 1) CATEGORIA (se la usi nei percorsi)
$category = new RouterViewConfiguration('category');
$category->setKey('id')->setNestable();
$this->registerView($category);
// Child: circolare (lega la category tramite categoria_id)
$circolare = new RouterViewConfiguration('circolari');
$circolare->setKey('id')->setParent($category, 'categoria_id', 'id');
// 2) LISTA (nessuna key!)
$circolari = new RouterViewConfiguration('circolari');
// Se vuoi, puoi lasciarla senza parent, oppure solo setParent($category) SENZA variabili
// $circolari->setParent($category); // opzionale, ma senza 2° e 3° argomento
$this->registerView($circolari);
// DETTAGLIO (ha la key id)
$circolare = new RouterViewConfiguration('circolare');
$circolare->setKey('id')->setParent($circolari);
$this->registerView($circolare);
// 4) FORM (se presente)
$form = new RouterViewConfiguration('form');
$form->setParent($circolari);
$this->registerView($form);
parent::__construct($app, $menu);
// Regole standard di routing
$this->attachRule(new StandardRules($this));
// Regole: prima Menu, poi Standard, poi NoMenu
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
}
/* ---------------- BUILD ---------------- */
// Segmenti categoria = path completo (parent1/parent2/figlia)
// Segmento categoria = alias (fallback id)
public function getCategorySegment($id, $query)
{
$id = (int) ($id ?: 0);
if ($id <= 0) {
return [];
}
$db = Factory::getContainer()->get('DatabaseDriver');
$path = (string) $db->setQuery(
$db->getQuery(true)
->select('path')
->from('#__circolari_categorie')
->where('id = ' . $id)
->where("extension = 'com_content'")
$db = Factory::getContainer()->get('DatabaseDriver');
$alias = (string) $db->setQuery(
$db->getQuery(true)->select('alias')->from('#__circolari_categorie')->where('id=' . (int)$id)
)->loadResult();
return $path !== '' ? array_filter(explode('/', $path)) : [(string) $id];
return $alias !== '' ? [$alias] : [(string) (int) $id];
}
// Ultimo segmento = SOLO alias della circolare
// Segmento dettaglio = alias (fallback id se vuoto)
public function getCircolareSegment($id, $query)
{
$db = Factory::getContainer()->get('DatabaseDriver');
$alias = (string) $db->setQuery(
$db->getQuery(true)
->select('alias')
->from('#__circolari')
->where('id = ' . (int) $id)
$db->getQuery(true)->select('alias')->from('#__circolari')->where('id=' . (int)$id)
)->loadResult();
// Nessun fallback numerico: imponiamo l'alias
return [$alias];
return [$alias !== '' ? $alias : (string) (int) $id];
}
/* ---------------- PARSE ---------------- */
// Ricava categoria_id accumulando i segmenti e risolvendo per PATH completo
public function getCategoryId($segment, $query)
{
static $segments = [];
$segments[] = $segment;
$path = implode('/', $segments);
$db = Factory::getContainer()->get('DatabaseDriver');
// match per path (univoco in com_content)
$id = (int) $db->setQuery(
$db->getQuery(true)
->select('id')
->from('#__circolari_categorie')
->where('path = ' . $db->quote($path))
->where("extension = 'com_content'")
$db->getQuery(true)->select('id')->from('#__circolari_categorie')->where('alias=' . $db->quote($segment))
)->loadResult();
if ($id > 0) {
return $id;
}
// fallback (singolo alias) se si visita direttamente un livello intermedio
$id = (int) $db->setQuery(
$db->getQuery(true)
->select('id')
->from('#__circolari_categorie')
->where('alias = ' . $db->quote($segment))
->where("extension = 'com_content'")
->order('level DESC')
)->loadResult();
return $id ?: 0;
return $id > 0 ? $id : (ctype_digit((string)$segment) ? (int)$segment : 0);
}
// Alias circolare (+ categoria_id già risolto) → id
public function getCircolareId($segment, $query)
{
$categoria_id = (int) ($query['categoria_id'] ?? 0);
$db = Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('id')
->from('#__circolari')
->where('alias = ' . $db->quote($segment));
->select('id')
->from('#__circolari')
->where('alias = ' . $db->quote($segment));
if ($categoria_id > 0) {
$q->where('categoria_id = ' . $categoria_id);
}
$db->setQuery($q);
$id = (int) $db->loadResult();
return (int) $db->loadResult() ?: 0;
// fallback numerico sullultimo segmento
if ($id === 0 && ctype_digit((string)$segment)) {
$id = (int) $segment;
}
return $id;
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Pcrt\Component\Circolari\Site\View\Form;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Factory;
class HtmlView extends BaseHtmlView
{
public $item;
public $categorie = [];
public $firmetipi = [];
public $bottoniMap = [];
public function display($tpl = null)
{
$model = $this->getModel();
$this->item = $model->getItem((int) \Joomla\CMS\Factory::getApplication()->input->getInt('id', 0));
$this->categorie = $model->getCategorie();
$this->firmetipi = $model->getFirmetipi();
$this->bottoniMap = $model->getBottoniByFirmatipo();
$app = Factory::getApplication();
$user = $app->getIdentity();
if ($user->guest || (!$user->authorise('core.manage', 'com_circolari') && !$user->authorise('core.admin', 'com_circolari'))) {
throw new \RuntimeException(\Joomla\CMS\Language\Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
/** @var \Pcrt\Component\Circolari\Site\Model\FormModel $model */
$model = $this->getModel();
$id = (int) $app->input->getInt('id', 0);
$this->item = $model->getItem($id);
$this->categorie = $model->getCategorie();
$this->firmetipi = $model->getFirmetipi();
parent::display($tpl);
}
}