Modifica Circolare da frontend

This commit is contained in:
2025-09-09 11:47:02 +02:00
parent b10ac62efa
commit f8988556db
6 changed files with 157 additions and 173 deletions

View File

@ -1,4 +1,5 @@
<?php
namespace Pcrt\Component\Circolari\Site\Controller;
\defined('_JEXEC') or die;
@ -13,68 +14,53 @@ 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();
$this->input->set('view', 'form');
return parent::display($cachable, $urlparams);
}
public function save()
{
if (!Session::checkToken('post')) {
throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403);
}
$app = Factory::getApplication();
$user = Factory::getUser();
$app = Factory::getApplication();
$user = $app->getIdentity();
if ($user->guest || (!$user->authorise('core.manage', 'com_circolari') && !$user->authorise('core.admin', 'com_circolari'))) {
// Permessi: admin/gestione o edit
if (!$user->id || (!$user->authorise('core.manage', 'com_circolari') && !$user->authorise('core.edit', '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 (!Session::checkToken()) {
throw new \RuntimeException(Text::_('JINVALID_TOKEN'), 403);
}
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;
$in = $app->input;
$data = $in->get('jform', [], 'array');
// Il tuo form frontend invia campi "piatti"
if (!$data) {
$data = [
'id' => $in->getInt('id', 0),
'title' => $in->getString('title', ''),
'alias' => $in->getString('alias', ''),
'categoria_id' => $in->getInt('categoria_id', 0),
'description' => $in->getRaw('description', ''),
'state' => $in->getInt('state', 1),
'firma_obbligatoria' => $in->getInt('firma_obbligatoria', 0),
'tipologia_firma_id' => $in->getInt('tipologia_firma_id', 0),
'scadenza' => $in->getString('scadenza', ''), // datetime-local
];
}
/** @var \Pcrt\Component\Circolari\Site\Model\FormModel $model */
$model = $this->getModel('Form', 'Site');
$model = $this->getModel('Form');
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));
}
$id = $model->saveData($data, (int) $user->id);
$this->setRedirect(
Route::_('index.php?option=com_circolari&view=circolare&id=' . (int) $id, false),
Text::_('JLIB_APPLICATION_SAVE_SUCCESS')
);
}
public function cancel()

View File

@ -9,18 +9,6 @@ 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
{
@ -66,48 +54,66 @@ class FormModel extends BaseDatabaseModel
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;
public function getItem(int $id = 0): \stdClass
{
if ($id <= 0) {
return (object) [];
}
$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$q = $db->getQuery(true)
->select('*')->from('#__circolari')->where('id=' . (int) $id);
$db->setQuery($q);
return $db->loadObject() ?: (object) [];
}
public function saveData(array $data, int $userId): int
{
$app = \Joomla\CMS\Factory::getApplication();
// Alias di fallback
if (empty($data['alias']) && !empty($data['title'])) {
$alias = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $data['title']);
$alias = preg_replace('~[^a-z0-9]+~i', '-', strtolower($alias));
$alias = trim($alias, '-');
$data['alias'] = $alias ?: ('circolare-' . uniqid());
}
// JTable amministrativa per coerenza di check/store
$factory = $app->bootComponent('com_circolari')->getMVCFactory();
/** @var \Pcrt\Component\Circolari\Administrator\Table\CircolareTable $table */
$table = $factory->createTable('Circolare', 'Administrator');
$id = (int) ($data['id'] ?? 0);
if ($id > 0) {
$table->load($id);
$data['modified_by'] = $userId;
} else {
$data['created_by'] = $userId;
}
// Sblocca/normalizza
$data['checked_out'] = 0;
$data['checked_out_time'] = null;
if (!$table->bind($data)) { throw new \RuntimeException($table->getError() ?: 'Bind failed'); }
if (!$table->check()) { throw new \RuntimeException($table->getError() ?: 'Check failed'); }
// ordering se nuovo
if (empty($table->id) || (int) $table->ordering === 0) {
$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$max = (int) $db->setQuery(
$db->getQuery(true)->select('COALESCE(MAX(ordering),0)')
->from('#__circolari')
->where('categoria_id=' . (int) ($table->categoria_id ?? 0))
)->loadResult();
$table->ordering = $max + 1;
}
if (!$table->store()) { throw new \RuntimeException($table->getError() ?: 'Store failed'); }
return (int) $table->id;
}
private function createNewFirmatipo(string $nome, string $bottoniRiga): int
{

View File

@ -17,102 +17,99 @@ class Router extends RouterView
{
public function __construct(SiteApplication $app, AbstractMenu $menu)
{
// 1) CATEGORIA (se la usi nei percorsi)
// 2) LISTA (nessuna key!)
// 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)
// DETTAGLIO (ha key id)
$circolare = new RouterViewConfiguration('circolare');
$circolare->setKey('id')->setParent($circolari);
$this->registerView($circolare);
// 4) FORM (se presente)
// FORM (nessuna key)
$form = new RouterViewConfiguration('form');
$form->setParent($circolari);
$this->registerView($form);
parent::__construct($app, $menu);
// Regole: prima Menu, poi Standard, poi NoMenu
$this->attachRule(new MenuRules($this));
$this->attachRule(new StandardRules($this));
$this->attachRule(new NomenuRules($this));
}
/* ---------------- BUILD ---------------- */
// Pulisci la query quando costruisci gli URL
public function build(&$query)
{
// Se sto costruendo 'form', non agganciare Itemid di altre view del componente
if (($query['view'] ?? '') === 'form' && isset($query['Itemid'])) {
$item = $this->menu->getItem((int) $query['Itemid']);
if ($item && $item->component === 'com_circolari') {
$v = is_array($item->query ?? null) ? ($item->query['view'] ?? '') : '';
if ($v !== 'form') {
unset($query['Itemid']); // evita override della view
}
}
}
// Lista: non appendere categorie in querystring
if (($query['view'] ?? '') === 'circolari') {
unset($query['categoria_id'], $query['catid'], $query['categories'], $query['categorie'], $query['category_id']);
}
// Segmento categoria = alias (fallback id)
if (($query['view'] ?? '') === 'form' && isset($query['Itemid'])) {
$item = $this->menu->getItem((int) $query['Itemid']);
if ($item && $item->component === 'com_circolari') {
$v = is_array($item->query ?? null) ? ($item->query['view'] ?? '') : '';
if ($v !== 'form') {
unset($query['Itemid']);
}
}
}
return parent::build($query);
}
// Segmento dettaglio = alias (fallback id se vuoto)
// Segmento dettaglio = alias (fallback id)
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();
return [$alias !== '' ? $alias : (string) (int) $id];
}
/* ---------------- PARSE ---------------- */
// Parse del dettaglio: alias -> id (opz. con categoria)
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));
if ($categoria_id > 0) {
$q->where('categoria_id = ' . $categoria_id);
if (!empty($query['categoria_id'])) {
$q->where('categoria_id = ' . (int) $query['categoria_id']);
}
$db->setQuery($q);
$id = (int) $db->loadResult();
// fallback numerico sullultimo segmento
if ($id === 0 && ctype_digit((string)$segment)) {
// fallback numerico
if ($id === 0 && ctype_digit((string) $segment)) {
$id = (int) $segment;
}
return $id;
}
// ⬇️ AGGIUNGI in Pcrt\Component\Circolari\Site\Service\Router
// Rimuove categoria_id (ecc.) dalla LISTA prima che Joomla costruisca lURL.
// Così sparisce anche se il link della voce di menu in DB lo contiene ancora.
public function build(&$query)
{
if (isset($query['view']) && $query['view'] === 'circolari') {
foreach (['categoria_id', 'catid', 'categorie', 'categories', 'category_id'] as $k) {
if (array_key_exists($k, $query)) {
unset($query[$k]);
}
}
}
// Se vuoi essere ultra-clean, rimuovi anche id/Itemid “sporchi” (non necessario di solito)
// if (isset($query['view']) && $query['view'] === 'circolari' && isset($query['id'])) unset($query['id']);
return parent::build($query);
}
// (Opzionale) Non reintrodurre variabili categoria in parse: la lista si filtra da params del menu.
// (Opzionale) Non reintrodurre categorie in parse per la lista
public function parse(&$segments)
{
$vars = parent::parse($segments);
if (isset($vars['view']) && $vars['view'] === 'circolari') {
if (($vars['view'] ?? '') === 'circolari') {
unset($vars['categoria_id'], $vars['catid'], $vars['categorie'], $vars['category_id']);
}

View File

@ -1,11 +1,10 @@
<?php
namespace Pcrt\Component\Circolari\Site\View\Form;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
class HtmlView extends BaseHtmlView
{
@ -16,24 +15,14 @@ class HtmlView extends BaseHtmlView
public function display($tpl = null)
{
$app = Factory::getApplication();
$model = $this->getModel();
$this->item = $model->getItem((int) \Joomla\CMS\Factory::getApplication()->input->getInt('id', 0));
$id = $app->input->getInt('id', 0);
$this->item = $model->getItem($id);
$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);
}

View File

@ -48,6 +48,13 @@ if ($canAdmin) {
<div class="container my-5 mega-container">
<?php if ($canAdmin): ?>
<a class="btn btn-primary"
href="<?php echo \Joomla\CMS\Router\Route::_('index.php?option=com_circolari&task=form.display&id=' . (int)$this->item->id, false); ?>">
Modifica Circolare
</a>
<?php endif; ?>
<div class="row mb-4 align-items-end">
<div class="col-md-9 col-12">
<h1 class="h2 mb-1"><?= $this->escape($item->title); ?></h1>
@ -109,15 +116,14 @@ if ($canAdmin) {
<div class="card mt-4">
<div class="card-header d-flex justify-content-between align-items-center">
<strong>Firme ricevute</strong>
<a class="btn btn-outline-secondary btn-sm"
href="<?=
\Joomla\CMS\Router\Route::_(
'index.php?option=com_circolari&task=circolare.exportFirme&id='.(int)$item->id.'&'.\Joomla\CMS\Session\Session::getFormToken().'=1'
); ?>"
download
>
Scarica Excel
</a>
<a class="btn btn-outline-secondary btn-sm"
href="<?=
\Joomla\CMS\Router\Route::_(
'index.php?option=com_circolari&task=circolare.exportFirme&id=' . (int)$item->id . '&' . \Joomla\CMS\Session\Session::getFormToken() . '=1'
); ?>"
download>
Scarica Excel
</a>
</div>

View File

@ -32,7 +32,7 @@ $cancelUrl = $cancelItemId
<div class="container py-3">
<h2 class="mb-3"><?php echo Text::_('Nuova circolare'); ?></h2>
<form action="<?php echo $action; ?>" method="post" class="form-validate">
<form action="<?php echo Route::_('index.php?option=com_circolari&task=form.save'); ?>" method="post" class="form-validate">
<div class="mb-3">
<label class="form-label" for="title"><?php echo Text::_('Titolo'); ?></label>
@ -131,8 +131,8 @@ $cancelUrl = $cancelItemId
<a href="<?php echo $cancelUrl; ?>" class="btn btn-outline-secondary"><?php echo Text::_('Annulla'); ?></a>
</div>
<input type="hidden" name="id" value="<?php echo (int) ($item->id ?? 0); ?>">
<?php echo HTMLHelper::_('form.token'); ?>
<input type="hidden" name="id" value="<?php echo (int) ($this->item->id ?? 0); ?>">
<?php echo \Joomla\CMS\HTML\HTMLHelper::_('form.token'); ?>
<script type="application/json" id="bottoni-map"><?php
echo json_encode($this->bottoniMap ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);