diff --git a/administrator/config.xml b/administrator/config.xml index bd6bd10..4c0f6ff 100644 --- a/administrator/config.xml +++ b/administrator/config.xml @@ -1,6 +1,14 @@
+ JTRASHED - - -
+ +
+ + + + + + + + + + + + +
+ \ No newline at end of file diff --git a/administrator/sql/updates/1.1.9.sql b/administrator/sql/updates/1.1.9.sql new file mode 100644 index 0000000..2e74afa --- /dev/null +++ b/administrator/sql/updates/1.1.9.sql @@ -0,0 +1,5 @@ +ALTER TABLE `#__circolari` + ADD COLUMN `created` DATETIME NULL DEFAULT NULL AFTER `checked_out_time`, + ADD COLUMN `modified` DATETIME NULL DEFAULT NULL AFTER `created_by`, + ADD COLUMN `publish_up` DATETIME NULL DEFAULT NULL AFTER `modified_by`, + ADD COLUMN `publish_down` DATETIME NULL DEFAULT NULL AFTER `publish_up`; diff --git a/administrator/src/Controller/CircolareController.php b/administrator/src/Controller/CircolareController.php index 467e3a9..38c6b70 100644 --- a/administrator/src/Controller/CircolareController.php +++ b/administrator/src/Controller/CircolareController.php @@ -1,4 +1,5 @@ registerTask('apply', 'save'); $this->registerTask('save2new', 'save'); } + + + +public function save($key = null, $urlVar = null) +{ + $app = \Joomla\CMS\Factory::getApplication(); + $input = $app->input; + $post = $input->get('jform', [], 'array'); + $notify= (int)($post['notify'] ?? 0); + + $result = parent::save($key, $urlVar); + + if ($result && $notify) { + $model = $this->getModel('Circolare'); + $id = (int) $model->getState($model->getName().'.id') ?: (int)($post['id'] ?? 0); + if ($id <= 0) { + $db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver'); + $id = (int) $db->insertid(); + } + if ($id > 0) { + (new Notifier())->sendForCircolare($id, ['onlyIfFirmaObbligatoria' => true]); + } + } + return $result; +} } diff --git a/administrator/src/Service/Notifier.php b/administrator/src/Service/Notifier.php new file mode 100644 index 0000000..38c6b70 --- /dev/null +++ b/administrator/src/Service/Notifier.php @@ -0,0 +1,61 @@ + + * @copyright 2025 Tommaso Cippitelli + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Pcrt\Component\Circolari\Administrator\Controller; + +\defined('_JEXEC') or die; + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\CMS\MVC\Factory\MVCFactoryInterface; +use Pcrt\Component\Circolari\Administrator\Service\Notifier; + +/** + * Circolare controller class. + * + * @since 1.0.0 + */ +class CircolareController extends FormController +{ + protected $view_list = 'circolares'; + protected $view_item = 'circolare'; + + + public function __construct($config = array(), MVCFactoryInterface $factory = null, $app = null, $input = null) + { + parent::__construct($config, $factory, $app, $input); + $this->registerTask('apply', 'save'); + $this->registerTask('save2new', 'save'); + } + + + +public function save($key = null, $urlVar = null) +{ + $app = \Joomla\CMS\Factory::getApplication(); + $input = $app->input; + $post = $input->get('jform', [], 'array'); + $notify= (int)($post['notify'] ?? 0); + + $result = parent::save($key, $urlVar); + + if ($result && $notify) { + $model = $this->getModel('Circolare'); + $id = (int) $model->getState($model->getName().'.id') ?: (int)($post['id'] ?? 0); + if ($id <= 0) { + $db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver'); + $id = (int) $db->insertid(); + } + if ($id > 0) { + (new Notifier())->sendForCircolare($id, ['onlyIfFirmaObbligatoria' => true]); + } + } + return $result; +} +} diff --git a/administrator/src/Table/CircolareTable.php b/administrator/src/Table/CircolareTable.php index 1675201..9232d63 100644 --- a/administrator/src/Table/CircolareTable.php +++ b/administrator/src/Table/CircolareTable.php @@ -153,12 +153,64 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl * * @since 1.0.0 */ - public function store($updateNulls = true) - { +public function store($updateNulls = true) + { + $now = Factory::getDate()->toSql(); + $user = Factory::getUser(); + $isNew = empty($this->id); - return parent::store($updateNulls); - } + if ($isNew) { + // created/created_by + if (empty($this->created)) { $this->created = $now; } + if (empty($this->created_by)) { $this->created_by = (int) $user->id; } + + // Se nasce già pubblicata, imposta publish_up + if ((int) ($this->state ?? 0) === 1 && empty($this->publish_up)) { + $this->publish_up = $now; + } + } else { + // modified/modified_by + $this->modified = $now; + if (property_exists($this, 'modified_by')) { + $this->modified_by = (int) $user->id; + } + + // Rileva cambio di stato per publish_up / publish_down + $db = $this->getDbo(); + $q = $db->getQuery(true) + ->select($db->quoteName(['state','publish_up'])) + ->from($db->quoteName('#__circolari')) + ->where($db->quoteName('id') . ' = ' . (int) $this->id); + $db->setQuery($q); + $old = (array) $db->loadAssoc(); + + if ($old) { + $oldState = (int) ($old['state'] ?? 0); + $newState = (int) ($this->state ?? 0); + + if ($oldState !== $newState) { + // Passaggio a PUBBLICATA → publish_up (se non già impostata) + if ($newState === 1 && empty($this->publish_up)) { + $this->publish_up = $now; + } + // Passaggio da PUBBLICATA a NON pubblicata → publish_down ora + if ($oldState === 1 && $newState !== 1) { + $this->publish_down = $now; + } + } + } + } + + // Normalizza ancora (nel caso abbiamo appena impostato date) + foreach (['scadenza','created','modified','publish_up','publish_down'] as $f) { + if (isset($this->$f)) { + $this->$f = self::normalizeDate($this->$f); + } + } + + return parent::store($updateNulls); + } /** * This function convert an array of Access objects into an rules array. @@ -281,6 +333,12 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl return false; } + foreach (['scadenza','created','modified','publish_up','publish_down'] as $f) { + if (isset($this->$f)) { + $this->$f = self::normalizeDate($this->$f); + } + } + return parent::check(); } @@ -360,4 +418,29 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl $this->_db->setQuery($q); return ((int) $this->_db->loadResult()) > 0; } + + /** + * Normalizza varie date in formato SQL + */ + protected static function normalizeDate(?string $raw): ?string + { + if ($raw === null) return null; + $raw = trim((string) $raw); + if ($raw === '' || $raw === '0000-00-00 00:00:00') return null; + + $raw = str_replace('T', ' ', substr($raw, 0, 19)); + foreach (['Y-m-d H:i:s','Y-m-d H:i','d-m-Y H:i:s','d-m-Y H:i','Y-m-d'] as $fmt) { + $dt = \DateTime::createFromFormat($fmt, $raw); + if ($dt instanceof \DateTime) { + return $dt->format('Y-m-d H:i:s'); + } + } + try { + $dt = new \DateTime($raw); + return $dt->format('Y-m-d H:i:s'); + } catch (\Throwable $e) { + return null; + } + } + } diff --git a/administrator/tmpl/circolare/edit.php b/administrator/tmpl/circolare/edit.php index 2e3f030..35b6004 100644 --- a/administrator/tmpl/circolare/edit.php +++ b/administrator/tmpl/circolare/edit.php @@ -37,21 +37,38 @@ HTMLHelper::_('bootstrap.tooltip'); form->renderField('title'); ?> form->renderField('alias'); ?> form->renderField('categoria_id'); ?> - form->renderField('hits'); ?> form->renderField('description'); ?> form->renderField('attachment'); ?> form->renderField('image'); ?> form->renderField('firma_obbligatoria'); ?> form->renderField('usergroup_ids'); ?> form->renderField('scadenza'); ?> + form->renderField('notify'); ?> + + +
+
+
+ + form->renderField('hits'); ?> + form->renderField('created'); ?> + form->renderField('created_by'); ?> + form->renderField('modified'); ?> + form->renderField('modified_by'); ?> + form->renderField('publish_up'); ?> + form->renderField('publish_down'); ?> + +
+
+
+ + - form->renderField('created_by'); ?> - form->renderField('modified_by'); ?> diff --git a/circolari.xml b/circolari.xml index f3de7ea..3ad3d6c 100644 --- a/circolari.xml +++ b/circolari.xml @@ -7,7 +7,7 @@ Tommaso Cippitelli tommaso.cippitelli@protocollicreativi.it http:// - 1.1.7 + 1.1.9 Pcrt\Component\Circolari diff --git a/site/src/Controller/FormController.php b/site/src/Controller/FormController.php index 1aec1ce..459e408 100644 --- a/site/src/Controller/FormController.php +++ b/site/src/Controller/FormController.php @@ -57,6 +57,66 @@ class FormController extends BaseController $id = $model->saveData($data, (int) $user->id); + $input = Factory::getApplication()->input; + $db = Factory::getContainer()->get('DatabaseDriver'); + $groupIds = $input->get('usergroup_ids', [], 'array'); + $groupIds = array_values(array_unique(array_map('intval', (array) $groupIds))); + + try { + $db->transactionStart(); + + // pulisci le righe esistenti per la circolare + $db->setQuery( + $db->getQuery(true) + ->delete('#__circolari_usergroups') + ->where('circolare_id = ' . (int) $id) + )->execute(); + + // inserisci i selezionati con can_firmare=1 (required=0 di default) + if ($groupIds) { + $columns = ['circolare_id', 'usergroup_id', 'required', 'can_firmare']; + $q = $db->getQuery(true) + ->insert('#__circolari_usergroups') + ->columns($db->quoteName($columns)); + + foreach ($groupIds as $gid) { + if ($gid > 0) { + $q->values((int) $id . ',' . (int) $gid . ',0,1'); + } + } + $db->setQuery($q)->execute(); + } + + $db->transactionCommit(); + } catch (\Throwable $e) { + $db->transactionRollback(); + // opzionale: log/avviso + // Factory::getApplication()->enqueueMessage('Errore salvataggio gruppi firma', 'warning'); + } + + $notify = $in->getInt('notify'); + + + if ($id > 0 && $notify) { + + // Carica la classe dal backend se non è già caricata + if (!class_exists('\Pcrt\Component\Circolari\Administrator\Service\Notifier')) { + require_once JPATH_ADMINISTRATOR . '/components/com_circolari/src/Service/Notifier.php'; + } + + try { + $notifier = new \Pcrt\Component\Circolari\Administrator\Service\Notifier(); + $sent = $notifier->sendForCircolare((int) $id, ['onlyIfFirmaObbligatoria' => true]); + file_put_contents(JPATH_ROOT . '/administrator/logs/circolari_debug.log', date('c') . "Notify: \n" . print_r($sent, true), FILE_APPEND); + + // opzionale: feedback + \Joomla\CMS\Factory::getApplication()->enqueueMessage('Email inviate: ' . (int) $sent, 'message'); + } catch (\Throwable $e) { + \Joomla\CMS\Factory::getApplication()->enqueueMessage('Invio email non riuscito.', 'warning'); + // opzionale: logga $e->getMessage() + file_put_contents(JPATH_ROOT . '/administrator/logs/circolari_debug.log', date('c') . "Errore: " . $e, FILE_APPEND); + } + } $this->setRedirect( Route::_('index.php?option=com_circolari&view=circolare&id=' . (int) $id, false), Text::_('JLIB_APPLICATION_SAVE_SUCCESS') diff --git a/site/src/Model/CircolariModel.php b/site/src/Model/CircolariModel.php index 9321b0b..6134767 100644 --- a/site/src/Model/CircolariModel.php +++ b/site/src/Model/CircolariModel.php @@ -1,4 +1,5 @@ getUserStateFromRequest($this->context . '.list.limit', 'limit', $app->get('list_limit'), 'uint'); - $this->setState('list.limit', $limit); + // 1) Fai prima il parent (imposta limit/limitstart e params base) + parent::populateState($ordering, $direction); + + // 2) Limite (puoi mantenerlo come preferisci) + $limit = $app->getUserStateFromRequest( + $this->context . '.list.limit', + 'limit', + $app->get('list_limit'), + 'uint' + ); + $limit = $limit > 0 ? (int) $limit : 10; + $this->setState('list.limit', $limit); + + // 3) START: prendi 'start' se presente e SOVRASCRIVI il limitstart del parent + $start = $app->input->getUInt('start', null); + if ($start !== null) { + $start = max(0, (int) $start); + // normalizza su multipli di limit + $page = (int) floor($start / $limit); + $start = $page * $limit; - $start = $app->getUserStateFromRequest($this->context . '.list.start', 'limitstart', 0, 'uint'); $this->setState('list.start', $start); - - // sorting - $orderCol = $app->getUserStateFromRequest($this->context . '.list.ordering', 'filter_order', $ordering, 'cmd'); - if (!\in_array($orderCol, $this->filter_fields, true)) { - $orderCol = $ordering; - } - $this->setState('list.ordering', $orderCol); - - $orderDirn = strtoupper($app->getUserStateFromRequest($this->context . '.list.direction', 'filter_order_Dir', $direction, 'cmd')); - $this->setState('list.direction', $orderDirn === 'ASC' ? 'ASC' : 'DESC'); - - // filtro testuale - $search = $app->getUserStateFromRequest($this->context . '.list.filter', 'filter-search', '', 'string'); - $this->setState('list.filter', $search); - - // categoria (se usi categoria_id) - $this->setState('filter.categoria_id', $app->input->getInt('categoria_id', 0)); - - parent::populateState($ordering, $direction); + $app->setUserState($this->context . '.list.start', $start); } - protected function getListQuery() - { - $db = $this->getDatabase(); - $q = $db->getQuery(true) - ->select('c.*') - ->from($db->quoteName('#__circolari', 'c')); + // 4) SEARCH + $search = $app->getUserStateFromRequest($this->context . '.list.filter', 'filter-search', '', 'string'); + $this->setState('list.filter', $search); - // colonne effettive in tabella - $cols = array_change_key_case($db->getTableColumns($db->replacePrefix('#__circolari'))); + // 5) Categoria (se usi passato via query) + $this->setState('filter.categoria_id', $app->input->getInt('categoria_id', 0)); - // pubblicati - if (isset($cols['state'])) { - $q->where('COALESCE(c.state,1)=1'); - } - - // categoria_id - $categoria_id = (int) $this->getState('filter.categoria_id', 0); - if ($categoria_id > 0 && isset($cols['categoria_id'])) { - $q->where('c.categoria_id=' . $categoria_id); - } - - // search su title - $search = trim((string) $this->getState('list.filter', '')); - if ($search !== '' && isset($cols['title'])) { - $like = $db->quote('%' . $db->escape($search, true) . '%', false); - $q->where('c.title LIKE ' . $like); - } - - // ordinamento - - return $q; + // 6) Ordinamento + $orderCol = $app->getUserStateFromRequest($this->context . '.list.ordering', 'filter_order', $ordering, 'cmd'); + if (!\in_array($orderCol, $this->filter_fields, true)) { + $orderCol = $ordering; } + $orderDir = strtoupper($app->getUserStateFromRequest($this->context . '.list.direction', 'filter_order_Dir', $direction, 'cmd')); + $this->setState('list.ordering', $orderCol); + $this->setState('list.direction', $orderDir === 'ASC' ? 'ASC' : 'DESC'); +} + +protected function getListQuery() +{ + $db = $this->getDatabase(); + $q = $db->getQuery(true) + ->select('c.*') + ->from($db->quoteName('#__circolari', 'c')); + + $cols = array_change_key_case($db->getTableColumns($db->replacePrefix('#__circolari'))); + + if (isset($cols['state'])) { + $q->where('COALESCE(c.state,1)=1'); + } + + $user = Factory::getUser(); + $now = Factory::getDate()->toSql(); + + if (!$user->authorise('core.edit.state', 'com_circolari')) { + if (isset($cols['publish_up'])) { + $q->where('(c.publish_up IS NULL OR c.publish_up <= ' . $db->quote($now) . ')'); + } + if (isset($cols['publish_down'])) { + $q->where('(c.publish_down IS NULL OR c.publish_down >= ' . $db->quote($now) . ')'); + } + } + + $categoria_id = (int) $this->getState('filter.categoria_id', 0); + if ($categoria_id > 0 && isset($cols['categoria_id'])) { + $q->where('c.categoria_id=' . $categoria_id); + } + + $search = trim((string) $this->getState('list.filter', '')); + if ($search !== '' && isset($cols['title'])) { + $like = $db->quote('%' . $db->escape($search, true) . '%', false); + $q->where('c.title LIKE ' . $like); + } + + // ✅ ORDINE (mancava) + $orderCol = $this->state->get('list.ordering', 'c.created'); + if (!\in_array($orderCol, $this->filter_fields, true)) { + $orderCol = 'c.created'; + } + $orderDirn = $this->state->get('list.direction', 'DESC'); + $q->order($db->escape($orderCol . ' ' . $orderDirn)); + + return $q; +} + // conteggio robusto (gestisce DISTINCT/GROUP BY) public function getTotal() @@ -112,4 +148,4 @@ class CircolariModel extends ListModel return \is_object($it) && !empty($it->id); })); } -} \ No newline at end of file +} diff --git a/site/src/Model/FormModel.php b/site/src/Model/FormModel.php index 0d694ab..cf35066 100644 --- a/site/src/Model/FormModel.php +++ b/site/src/Model/FormModel.php @@ -55,100 +55,68 @@ class FormModel extends BaseDatabaseModel } 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 { - $db = Factory::getContainer()->get('DatabaseDriver'); + 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) []; + } - // crea tipologia - $tipo = (object) [ - 'nome' => $nome, - 'descrizione' => '', - 'state' => 1, - ]; - $db->insertObject('#__circolari_firmetipi', $tipo); - $id = (int) $db->insertid(); + public function saveData(array $data, int $userId): int + { + $app = \Joomla\CMS\Factory::getApplication(); - // 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); + // 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()); } - return $id; - } + // 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'); - 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'; + $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; } } diff --git a/site/src/View/Form/HtmlView.php b/site/src/View/Form/HtmlView.php index 8e19571..bfa5d10 100644 --- a/site/src/View/Form/HtmlView.php +++ b/site/src/View/Form/HtmlView.php @@ -1,4 +1,5 @@ categorie = $model->getCategorie(); $this->firmetipi = $model->getFirmetipi(); $this->bottoniMap = $model->getBottoniByFirmatipo(); + + $db = Factory::getContainer()->get('DatabaseDriver'); + // tutti i gruppi (ordinati per albero) + $this->allUserGroups = (array) $db->setQuery( + $db->getQuery(true) + ->select('id, title') + ->from('#__usergroups') + ->order('lft ASC') + )->loadAssocList(); + + // gruppi già abilitati a firmare (se si sta modificando) + $selected = []; + if (!empty($this->item->id)) { + $selected = (array) $db->setQuery( + $db->getQuery(true) + ->select('usergroup_id') + ->from('#__circolari_usergroups') + ->where('circolare_id = ' . (int) $this->item->id) + ->where('can_firmare = 1') + )->loadColumn(); + } + $this->selectedGroupIds = array_map('intval', $selected); parent::display($tpl); } } diff --git a/site/tmpl/circolari/default.php b/site/tmpl/circolari/default.php index 35ca8d2..27a610f 100644 --- a/site/tmpl/circolari/default.php +++ b/site/tmpl/circolari/default.php @@ -11,7 +11,6 @@ $input = $app->getInput(); // Prendo dati dalla view con fallback ai getter $items = $this->items ?? ($this->get('Items') ?? []); $pagination = $this->pagination ?? ($this->get('Pagination') ?? null); - // Normalizzo items if (!is_array($items)) { $items = (array) $items; @@ -19,64 +18,73 @@ if (!is_array($items)) { $items = array_values(array_filter($items, static fn($it) => is_object($it) && !empty($it->id))); $Itemid = (int) $input->getInt('Itemid', 0); +$params = $this->params ?? Factory::getApplication()->getParams(); +$title = (string) $params->get('page_title', ''); +$desc = (string) $params->get('page_description', ''); + ?> -
- - -
- - - + +
\ No newline at end of file