Invio Email

This commit is contained in:
2025-09-10 16:06:20 +02:00
parent f8988556db
commit f1f7872edd
14 changed files with 701 additions and 312 deletions

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset label="COM_CIRCOLARI" name="circolari">
<field
name="email_oggetto"
type="text"
label="Oggetto email"
description="Testo email diretta ai docenti per la firma"
rows="8"
cols="60"
filter="raw" />
<field
name="email_testo"
type="textarea"

View File

@ -9,10 +9,7 @@
<option value="-2">JTRASHED</option>
</field>
<field name="ordering" type="number" default="0" />
<field name="checked_out" type="hidden" filter="unset" />
<field name="checked_out_time" type="hidden" filter="unset" />
<field name="created_by" type="createdby" hidden="true" />
<field name="modified_by" type="modifiedby" hidden="true" />
<field name="title" type="text" label="JGLOBAL_TITLE" required="true" filter="safehtml" />
<field name="alias" type="text" label="JFIELD_ALIAS_LABEL" description="JFIELD_ALIAS_DESC" />
@ -51,7 +48,57 @@
showon="firma_obbligatoria:1" />
<field name="scadenza" type="CalSafe" label="Data Scadenza Firma"
showon="firma_obbligatoria:1" />
<field name="hits" type="number" readonly="true" label="JGLOBAL_HITS" default="0" />
<field name="notify" type="radio"
label="Invia email"
description="Se Sì, invia subito una email ai gruppi selezionati"
default="0"
filter="unset"
showon="firma_obbligatoria:1"
layout="joomla.form.field.radio.switcher">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
<fieldset name="publishing" label="PUBBLICAZIONE">
<field name="hits" type="number" readonly="true" label="JGLOBAL_HITS" default="0" />
<field name="created"
type="CalSafe"
label="JGLOBAL_FIELD_CREATED_LABEL"
readonly="true" />
<field name="created_by"
type="createdby"
label="JGLOBAL_FIELD_CREATED_BY_LABEL"
readonly="true" />
<field name="modified"
type="CalSafe"
label="JGLOBAL_FIELD_MODIFIED_LABEL"
readonly="true" />
<field name="modified_by"
type="modifiedby"
label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
readonly="true" />
<field name="publish_up"
type="CalSafe"
label="JGLOBAL_FIELD_PUBLISH_UP_LABEL"
description="Data/ora di pubblicazione"
showtime="true"
format="Y-m-d H:i:s"
translateformat="0"
filter="raw" />
<field name="publish_down"
type="CalSafe"
label="JGLOBAL_FIELD_PUBLISH_DOWN_LABEL"
description="Data/ora di dispubblicazione"
showtime="true"
format="Y-m-d H:i:s"
translateformat="0"
filter="raw" />
</fieldset>
</form>

View File

@ -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`;

View File

@ -1,4 +1,5 @@
<?php
/**
* @version CVS: 1.0.0
* @package Com_Circolari
@ -13,6 +14,7 @@ namespace Pcrt\Component\Circolari\Administrator\Controller;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Pcrt\Component\Circolari\Administrator\Service\Notifier;
/**
* Circolare controller class.
@ -31,4 +33,29 @@ class CircolareController extends FormController
$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;
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* @version CVS: 1.0.0
* @package Com_Circolari
* @author Tommaso Cippitelli <tommaso.cippitelli@protocollicreativi.it>
* @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;
}
}

View File

@ -155,7 +155,59 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
*/
public function store($updateNulls = true)
{
$now = Factory::getDate()->toSql();
$user = Factory::getUser();
$isNew = empty($this->id);
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);
}
@ -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;
}
}
}

View File

@ -37,21 +37,38 @@ HTMLHelper::_('bootstrap.tooltip');
<?php echo $this->form->renderField('title'); ?>
<?php echo $this->form->renderField('alias'); ?>
<?php echo $this->form->renderField('categoria_id'); ?>
<?php echo $this->form->renderField('hits'); ?>
<?php echo $this->form->renderField('description'); ?>
<?php echo $this->form->renderField('attachment'); ?>
<?php echo $this->form->renderField('image'); ?>
<?php echo $this->form->renderField('firma_obbligatoria'); ?>
<?php echo $this->form->renderField('usergroup_ids'); ?>
<?php echo $this->form->renderField('scadenza'); ?>
<?php echo $this->form->renderField('notify'); ?>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo \Joomla\CMS\HTML\HTMLHelper::_('uitab.addTab', 'myTab', 'publishing', Text::_('Pubblicazione', true)); ?>
<div class="row-fluid">
<div class="col-md-12 form-horizontal">
<fieldset class="adminform">
<legend><?php echo Text::_('Pubblicazione'); ?></legend>
<?php echo $this->form->renderField('hits'); ?>
<?php echo $this->form->renderField('created'); ?>
<?php echo $this->form->renderField('created_by'); ?>
<?php echo $this->form->renderField('modified'); ?>
<?php echo $this->form->renderField('modified_by'); ?>
<?php echo $this->form->renderField('publish_up'); ?>
<?php echo $this->form->renderField('publish_down'); ?>
</fieldset>
</div>
</div>
<?php echo \Joomla\CMS\HTML\HTMLHelper::_('uitab.endTab'); ?>
<input type="hidden" name="jform[id]" value="<?php echo isset($this->item->id) ? $this->item->id : ''; ?>" />
<input type="hidden" name="jform[state]" value="<?php echo isset($this->item->state) ? $this->item->state : ''; ?>" />
<?php echo $this->form->renderField('created_by'); ?>
<?php echo $this->form->renderField('modified_by'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<input type="hidden" name="task" value="" />
<?php echo HTMLHelper::_('form.token'); ?>

View File

@ -7,7 +7,7 @@
<author>Tommaso Cippitelli</author>
<authorEmail>tommaso.cippitelli@protocollicreativi.it</authorEmail>
<authorUrl>http://</authorUrl>
<version>1.1.7</version>
<version>1.1.9</version>
<description></description>
<namespace path="src">Pcrt\Component\Circolari</namespace>

View File

@ -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')

View File

@ -1,4 +1,5 @@
<?php
namespace Pcrt\Component\Circolari\Site\Model;
\defined('_JEXEC') or die;
@ -12,42 +13,62 @@ class CircolariModel extends ListModel
// campi ammessi per sort
protected $filter_fields = [
'id', 'c.id',
'title', 'c.title',
'hits', 'c.hits',
'categoria_id', 'c.categoria_id',
'state', 'c.state',
'id',
'c.id',
'title',
'c.title',
'hits',
'c.hits',
'categoria_id',
'c.categoria_id',
'state',
'c.state',
];
protected function populateState($ordering = 'c.created', $direction = 'DESC')
{
$app = Factory::getApplication();
// paging
$limit = $app->getUserStateFromRequest($this->context . '.list.limit', 'limit', $app->get('list_limit'), 'uint');
// 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);
$start = $app->getUserStateFromRequest($this->context . '.list.start', 'limitstart', 0, 'uint');
$this->setState('list.start', $start);
// 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;
// sorting
$this->setState('list.start', $start);
$app->setUserState($this->context . '.list.start', $start);
}
// 4) SEARCH
$search = $app->getUserStateFromRequest($this->context . '.list.filter', 'filter-search', '', 'string');
$this->setState('list.filter', $search);
// 5) Categoria (se usi passato via query)
$this->setState('filter.categoria_id', $app->input->getInt('categoria_id', 0));
// 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);
$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);
$this->setState('list.direction', $orderDir === 'ASC' ? 'ASC' : 'DESC');
}
protected function getListQuery()
@ -57,32 +78,47 @@ class CircolariModel extends ListModel
->select('c.*')
->from($db->quoteName('#__circolari', 'c'));
// colonne effettive in tabella
$cols = array_change_key_case($db->getTableColumns($db->replacePrefix('#__circolari')));
// pubblicati
if (isset($cols['state'])) {
$q->where('COALESCE(c.state,1)=1');
}
// categoria_id
$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 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
// ✅ 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()
{

View File

@ -95,8 +95,12 @@ public function saveData(array $data, int $userId): int
$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'); }
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) {
@ -109,46 +113,10 @@ public function saveData(array $data, int $userId): int
$table->ordering = $max + 1;
}
if (!$table->store()) { throw new \RuntimeException($table->getError() ?: 'Store failed'); }
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');
// 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\View\Form;
\defined('_JEXEC') or die;
@ -24,6 +25,28 @@ class HtmlView extends BaseHtmlView
$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);
}
}

View File

@ -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,9 +18,18 @@ 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);
?>
<div class="circolari-list">
$params = $this->params ?? Factory::getApplication()->getParams();
$title = (string) $params->get('page_title', '');
$desc = (string) $params->get('page_description', '');
?>
<div class="com-content-category category-list">
<div class="content-category">
<h1><?php echo $title ? $title : ""; ?> </h1>
<div class="category-desc">
<p><span style="color: #333333; font-family: Tahoma, Helvetica, Arial, sans-serif; font-size: 12.16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">
<?php echo $desc ? $desc : ""; ?> </span></p>
</div>
<?php if (!count($items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span>
@ -30,7 +38,7 @@ $Itemid = (int) $input->getInt('Itemid', 0);
</div>
<?php else : ?>
<table class="table table-striped table-bordered table-hover">
<table class="com-content-category__table category table table-striped table-bordered table-hover">
<caption class="visually-hidden"><?php echo Text::_('COM_CIRCOLARI_ELENCO') ?: 'Elenco circolari'; ?></caption>
<thead class="visually-hidden">
<tr>
@ -46,14 +54,14 @@ $Itemid = (int) $input->getInt('Itemid', 0);
$hits = isset($item->hits) ? (int) $item->hits : null;
?>
<tr class="cat-list-row<?php echo $i % 2; ?>">
<th class="list-title" scope="row">
<th class="list-title text-start" scope="row">
<a href="<?php echo $url; ?>">
<?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?>
</a>
</th>
<td class="text-end">
<td class="list-hits">
<?php if ($hits !== null) : ?>
<span class="btn btn-secondary btn-sm disabled" aria-disabled="true">
<span class="badge bg-info" aria-disabled="true">
<?php echo Text::_('JGLOBAL_HITS') ?: 'Visite'; ?>: <?php echo $hits; ?>
</span>
<?php else : ?>
@ -78,5 +86,5 @@ $Itemid = (int) $input->getInt('Itemid', 0);
</div>
</div>
<?php endif; ?>
</div>
</div>

View File

@ -114,6 +114,31 @@ $cancelUrl = $cancelItemId
</div>
<div class="row g-3 mt-2 firma-block" style="display:none;">
<div class="col-md-6 firma-block" style="display:none;">
<label class="form-label" for="jform_usergroup_ids">
<?php echo Text::_('Gruppi che possono firmare'); ?>
</label>
<select id="jform_usergroup_ids"
name="usergroup_ids[]"
class="form-select"
multiple
size="8">
<?php foreach ((array) ($this->allUserGroups ?? []) as $g):
$gid = (int) ($g['id'] ?? 0);
$gtitle = (string) ($g['title'] ?? '');
$sel = in_array($gid, (array) ($this->selectedGroupIds ?? []), true) ? 'selected' : '';
?>
<option value="<?php echo $gid; ?>" <?php echo $sel; ?>>
<?php echo htmlspecialchars($gtitle, ENT_QUOTES, 'UTF-8'); ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-text pc-muted">
<?php echo Text::_('Seleziona i gruppi utenti abilitati alla firma di questa circolare.'); ?>
</div>
</div>
<div class="col-md-6">
<label class="form-label required" id="jform_scadenza-lbl" for="jform_scadenza">
<?php echo Text::_('Scadenza firma'); ?><span class="star" aria-hidden="true">&nbsp;*</span>
@ -121,9 +146,23 @@ $cancelUrl = $cancelItemId
<input type="datetime-local" name="scadenza" id="jform_scadenza" class="form-control"
value="<?php
$sc = (string) ($item->scadenza ?? '');
if ($sc !== '') { echo htmlspecialchars(str_replace(' ', 'T', substr($sc, 0, 16)), ENT_QUOTES, 'UTF-8'); }
if ($sc !== '') {
echo htmlspecialchars(str_replace(' ', 'T', substr($sc, 0, 16)), ENT_QUOTES, 'UTF-8');
}
?>">
</div>
</div>
<div class="col-md-4 mt-4 firma-block" style="display:none;">
<label class="form-label" for="jform_notify">Invia email agli aventi diritto</label>
<input type="hidden" name="notify" value="0">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="jform_notify" name="notify" value="1">
<label class="form-check-label" for="jform_notify">Sì, invia subito</label>
</div>
<div class="form-text">Invia una notifica agli utenti dei gruppi abilitati alla firma.</div>
</div>
<div class="mt-4 d-flex gap-2">
@ -134,13 +173,17 @@ $cancelUrl = $cancelItemId
<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
<script type="application/json" id="bottoni-map">
<?php
echo json_encode($this->bottoniMap ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?></script>
?>
</script>
<script>
(function() {
function byId(id){ return document.getElementById(id); }
function byId(id) {
return document.getElementById(id);
}
const obb = byId('jform_firma_obbligatoria');
const tipo = byId('jform_tipologia_firma_id');
const scad = byId('jform_scadenza');
@ -164,7 +207,9 @@ $cancelUrl = $cancelItemId
function toggleFirma() {
const need = obb && obb.value === '1';
blocks.forEach(function(el){ el.style.display = need ? '' : 'none'; });
blocks.forEach(function(el) {
el.style.display = need ? '' : 'none';
});
if (scad) {
if (need) {
scad.setAttribute('required', 'required');
@ -187,7 +232,8 @@ $cancelUrl = $cancelItemId
var lbl = document.getElementById('jform_scadenza-lbl');
if (lbl) {
lbl.classList.remove('required');
var st = lbl.querySelector('.star'); if (st) st.remove();
var st = lbl.querySelector('.star');
if (st) st.remove();
}
}
}