generazione automatica alias

This commit is contained in:
2025-08-26 11:47:03 +02:00
parent edb8d4e873
commit 2f251bbfb0
7 changed files with 364 additions and 270 deletions

View File

@ -14,6 +14,10 @@
<field name="created_by" type="createdby" default="0" label="JGLOBAL_FIELD_CREATED_BY_LABEL" description="JGLOBAL_FIELD_CREATED_BY_DESC" hidden="true" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_CREATED_BY"/> <field name="created_by" type="createdby" default="0" label="JGLOBAL_FIELD_CREATED_BY_LABEL" description="JGLOBAL_FIELD_CREATED_BY_DESC" hidden="true" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_CREATED_BY"/>
<field name="modified_by" type="modifiedby" default="0" label="JGLOBAL_FIELD_MODIFIED_BY_LABEL" description="JGLOBAL_FIELD_MODIFIED_BY_DESC" hidden="true" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_MODIFIED_BY"/> <field name="modified_by" type="modifiedby" default="0" label="JGLOBAL_FIELD_MODIFIED_BY_LABEL" description="JGLOBAL_FIELD_MODIFIED_BY_DESC" hidden="true" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_MODIFIED_BY"/>
<field name="title" filter="safehtml" type="text" label="JGLOBAL_TITLE" description="JFIELD_TITLE_DESC" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_TITLE"/> <field name="title" filter="safehtml" type="text" label="JGLOBAL_TITLE" description="JFIELD_TITLE_DESC" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_TITLE"/>
<field name="alias"
type="text"
label="JFIELD_ALIAS_LABEL"
description="JFIELD_ALIAS_DESC" />
<field name="description" filter="safehtml" type="textarea" label="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_DESCRIPTION" description="COM_CIRCOLARI_FORM_DESC_CIRCOLARE_DESCRIPTION" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_DESCRIPTION"/> <field name="description" filter="safehtml" type="textarea" label="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_DESCRIPTION" description="COM_CIRCOLARI_FORM_DESC_CIRCOLARE_DESCRIPTION" hint="COM_CIRCOLARI_FORM_LBL_CIRCOLARE_DESCRIPTION"/>
<field name="attachment" type="media" <field name="attachment" type="media"
label="COM_CIRCOLARI_FIELD_ATTACHMENT_LABEL" label="COM_CIRCOLARI_FIELD_ATTACHMENT_LABEL"

View File

@ -1 +1,9 @@
/* Only premium users are allowed to update a component */ /* Only premium users are allowed to update a component */
ALTER TABLE `#__circolari`
ADD COLUMN `alias` VARCHAR(255) NOT NULL DEFAULT '' AFTER `title`,
ADD INDEX `idx_alias` (`alias`);
-- (facoltativo) prima valorizzazione "grezza" per i record esistenti
UPDATE `#__circolari`
SET `alias` = LOWER(REPLACE(TRIM(`title`), ' ', '-'))
WHERE (`alias` = '' OR `alias` IS NULL) AND `title` IS NOT NULL;

View File

@ -0,0 +1 @@
/* Only premium users are allowed to update a component */

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* @version CVS: 1.0.0 * @version CVS: 1.0.0
* @package Com_Circolari * @package Com_Circolari
@ -20,6 +21,7 @@ use \Joomla\CMS\Helper\TagsHelper;
use \Joomla\CMS\Filter\OutputFilter; use \Joomla\CMS\Filter\OutputFilter;
use \Joomla\CMS\Event\Model; use \Joomla\CMS\Event\Model;
use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Event\AbstractEvent;
use Joomla\CMS\Application\ApplicationHelper;
/** /**
@ -29,249 +31,289 @@ use Joomla\CMS\Event\AbstractEvent;
*/ */
class CircolareModel extends AdminModel class CircolareModel extends AdminModel
{ {
/** /**
* @var string The prefix to use with controller messages. * @var string The prefix to use with controller messages.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
protected $text_prefix = 'COM_CIRCOLARI'; protected $text_prefix = 'COM_CIRCOLARI';
/** /**
* @var string Alias to manage history control * @var string Alias to manage history control
* *
* @since 1.0.0 * @since 1.0.0
*/ */
public $typeAlias = 'com_circolari.circolare'; public $typeAlias = 'com_circolari.circolare';
/** /**
* @var null Item data * @var null Item data
* *
* @since 1.0.0 * @since 1.0.0
*/ */
protected $item = null; protected $item = null;
/**
* Returns a reference to the a Table object, always creating it.
*
* @param string $type The table type to instantiate
* @param string $prefix A prefix for the table class name. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return Table A database object
*
* @since 1.0.0
*/
public function getTable($type = 'Circolare', $prefix = 'Administrator', $config = array())
{
return parent::getTable($type, $prefix, $config);
}
/**
* Method to get the record form.
*
* @param array $data An optional array of data for the form to interogate.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \JForm|boolean A \JForm object on success, false on failure
*
* @since 1.0.0
*/
public function getForm($data = array(), $loadData = true)
{
// Initialise variables.
$app = Factory::getApplication();
// Get the form. /**
$form = $this->loadForm( * Returns a reference to the a Table object, always creating it.
'com_circolari.circolare', *
'circolare', * @param string $type The table type to instantiate
array( * @param string $prefix A prefix for the table class name. Optional.
'control' => 'jform', * @param array $config Configuration array for model. Optional.
'load_data' => $loadData *
) * @return Table A database object
); *
* @since 1.0.0
*/
public function getTable($type = 'Circolare', $prefix = 'Administrator', $config = array())
{
return parent::getTable($type, $prefix, $config);
}
/**
* Method to get the record form.
*
* @param array $data An optional array of data for the form to interogate.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \JForm|boolean A \JForm object on success, false on failure
*
* @since 1.0.0
*/
public function getForm($data = array(), $loadData = true)
{
// Initialise variables.
$app = Factory::getApplication();
if (empty($form)) // Get the form.
{ $form = $this->loadForm(
return false; 'com_circolari.circolare',
} 'circolare',
array(
'control' => 'jform',
'load_data' => $loadData
)
);
return $form;
}
/** if (empty($form)) {
* Method to get the data that should be injected in the form. return false;
* }
* @return mixed The data for the form.
*
* @since 1.0.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState('com_circolari.edit.circolare.data', array());
if (empty($data)) return $form;
{ }
if ($this->item === null)
{
$this->item = $this->getItem();
}
$data = $this->item;
}
return $data;
}
/** /**
* Method to get a single record. * Method to get the data that should be injected in the form.
* *
* @param integer $pk The id of the primary key. * @return mixed The data for the form.
* *
* @return mixed Object on success, false on failure. * @since 1.0.0
* */
* @since 1.0.0 protected function loadFormData()
*/ {
public function getItem($pk = null) // Check the session for previously entered form data.
{ $data = Factory::getApplication()->getUserState('com_circolari.edit.circolare.data', array());
if ($item = parent::getItem($pk))
{
if (isset($item->params))
{
$item->params = json_encode($item->params);
}
// Do any procesing on fields here if needed
}
return $item; if (empty($data)) {
if ($this->item === null) {
} $this->item = $this->getItem();
}
/** $data = $this->item;
* Method to duplicate an Circolare }
*
* @param array &$pks An array of primary key IDs. return $data;
* }
* @return boolean True if successful.
* /**
* @throws Exception * Method to get a single record.
*/ *
public function duplicate(&$pks) * @param integer $pk The id of the primary key.
{ *
$app = Factory::getApplication(); * @return mixed Object on success, false on failure.
$user = $app->getIdentity(); *
* @since 1.0.0
*/
public function getItem($pk = null)
{
if ($item = parent::getItem($pk)) {
if (isset($item->params)) {
$item->params = json_encode($item->params);
}
// Do any procesing on fields here if needed
}
return $item;
}
/**
* Method to duplicate an Circolare
*
* @param array &$pks An array of primary key IDs.
*
* @return boolean True if successful.
*
* @throws Exception
*/
public function duplicate(&$pks)
{
$app = Factory::getApplication();
$user = $app->getIdentity();
$dispatcher = $this->getDispatcher(); $dispatcher = $this->getDispatcher();
// Access checks. // Access checks.
if (!$user->authorise('core.create', 'com_circolari')) if (!$user->authorise('core.create', 'com_circolari')) {
{ throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED'));
throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED')); }
}
$context = $this->option . '.' . $this->name; $context = $this->option . '.' . $this->name;
// Include the plugins for the save events. // Include the plugins for the save events.
PluginHelper::importPlugin($this->events_map['save']); PluginHelper::importPlugin($this->events_map['save']);
$table = $this->getTable(); $table = $this->getTable();
foreach ($pks as $pk) foreach ($pks as $pk) {
{
if ($table->load($pk, true))
{
// Reset the id to create a new record.
$table->id = 0;
if (!$table->check()) if ($table->load($pk, true)) {
{ // Reset the id to create a new record.
throw new \Exception($table->getError()); $table->id = 0;
}
// Create the before save event. if (!$table->check()) {
$beforeSaveEvent = AbstractEvent::create( throw new \Exception($table->getError());
$this->event_before_save, }
[
'context' => $context,
'subject' => $table,
'isNew' => true,
'data' => $table,
]
);
// Trigger the before save event.
$dispatchResult = Factory::getApplication()->getDispatcher()->dispatch($this->event_before_save, $beforeSaveEvent);
// Check if dispatch result is an array and handle accordingly // Create the before save event.
$result = isset($dispatchResult['result']) ? $dispatchResult['result'] : []; $beforeSaveEvent = AbstractEvent::create(
$this->event_before_save,
[
'context' => $context,
'subject' => $table,
'isNew' => true,
'data' => $table,
]
);
// Proceed with your logic // Trigger the before save event.
if (in_array(false, $result, true) || !$table->store()) { $dispatchResult = Factory::getApplication()->getDispatcher()->dispatch($this->event_before_save, $beforeSaveEvent);
throw new \Exception($table->getError());
}
// Trigger the after save event. // Check if dispatch result is an array and handle accordingly
Factory::getApplication()->getDispatcher()->dispatch( $result = isset($dispatchResult['result']) ? $dispatchResult['result'] : [];
$this->event_after_save,
AbstractEvent::create(
$this->event_after_save,
[
'context' => $context,
'subject' => $table,
'isNew' => true,
'data' => $table,
]
)
);
}
else
{
throw new \Exception($table->getError());
}
}
// Clean cache // Proceed with your logic
$this->cleanCache(); if (in_array(false, $result, true) || !$table->store()) {
throw new \Exception($table->getError());
}
return true; // Trigger the after save event.
} Factory::getApplication()->getDispatcher()->dispatch(
$this->event_after_save,
AbstractEvent::create(
$this->event_after_save,
[
'context' => $context,
'subject' => $table,
'isNew' => true,
'data' => $table,
]
)
);
} else {
throw new \Exception($table->getError());
}
}
/** // Clean cache
* Prepare and sanitise the table prior to saving. $this->cleanCache();
*
* @param Table $table Table Object
*
* @return void
*
* @since 1.0.0
*/
protected function prepareTable($table)
{
jimport('joomla.filter.output');
if (empty($table->id)) return true;
{ }
// Set ordering to the last item if not set
if (@$table->ordering === '') /**
{ * Prepare and sanitise the table prior to saving.
$db = $this->getDbo(); *
$db->setQuery('SELECT MAX(ordering) FROM #__circolari'); * @param Table $table Table Object
$max = $db->loadResult(); *
$table->ordering = $max + 1; * @return void
} *
} * @since 1.0.0
} */
protected function prepareTable($table)
{
jimport('joomla.filter.output');
if (empty($table->id)) {
// Set ordering to the last item if not set
if (@$table->ordering === '') {
$db = $this->getDbo();
$db->setQuery('SELECT MAX(ordering) FROM #__circolari');
$max = $db->loadResult();
$table->ordering = $max + 1;
}
}
}
public function check()
{
// ordering per nuovi record
if (property_exists($this, 'ordering') && (int) $this->id === 0) {
$this->ordering = self::getNextOrder();
}
// Titolo obbligatorio (se vuoi forzarlo)
$this->title = trim((string) ($this->title ?? ''));
if ($this->title === '') {
$this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE'));
return false;
}
// --- ALIAS: genera se vuoto, ripulisci, evita "solo numeri", garantisci univocità ---
$this->alias = trim((string) ($this->alias ?? ''));
// Se mancante → dal titolo
if ($this->alias === '') {
$this->alias = ApplicationHelper::stringURLSafe($this->title);
} else {
$this->alias = ApplicationHelper::stringURLSafe($this->alias);
}
// Evita alias vuoto o numerico puro
if ($this->alias === '' || ctype_digit($this->alias)) {
$seed = (int) ($this->id ?: time());
$this->alias = ApplicationHelper::stringURLSafe($this->title . '-' . $seed);
}
// Unicità alias nel contesto tabella
$base = $this->alias;
$i = 2;
while ($this->aliasExists($this->alias, (int) $this->id)) {
$this->alias = $base . '-' . $i;
$i++;
}
return parent::check();
}
protected function aliasExists(string $alias, int $excludeId = 0): bool
{
$q = $this->_db->getQuery(true)
->select('COUNT(*)')
->from($this->_db->quoteName('#__circolari'))
->where($this->_db->quoteName('alias') . ' = ' . $this->_db->quote($alias));
if ($excludeId > 0) {
$q->where($this->_db->quoteName('id') . ' != ' . (int) $excludeId);
}
$this->_db->setQuery($q);
return ((int) $this->_db->loadResult()) > 0;
}
} }

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* @version CVS: 1.0.0 * @version CVS: 1.0.0
* @package Com_Circolari * @package Com_Circolari
@ -25,6 +26,7 @@ use \Joomla\CMS\Filesystem\File;
use \Joomla\Registry\Registry; use \Joomla\Registry\Registry;
use \Pcrt\Component\Circolari\Administrator\Helper\CircolariHelper; use \Pcrt\Component\Circolari\Administrator\Helper\CircolariHelper;
use \Joomla\CMS\Helper\ContentHelper; use \Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Application\ApplicationHelper;
/** /**
@ -37,14 +39,14 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
use TaggableTableTrait; use TaggableTableTrait;
/** /**
* Indicates that columns fully support the NULL value in the database * Indicates that columns fully support the NULL value in the database
* *
* @var boolean * @var boolean
* @since 4.0.0 * @since 4.0.0
*/ */
protected $_supportNullValue = true; protected $_supportNullValue = true;
/** /**
* Constructor * Constructor
* *
@ -55,7 +57,6 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$this->typeAlias = 'com_circolari.circolare'; $this->typeAlias = 'com_circolari.circolare';
parent::__construct('#__circolari', 'id', $db); parent::__construct('#__circolari', 'id', $db);
$this->setColumnAlias('published', 'state'); $this->setColumnAlias('published', 'state');
} }
/** /**
@ -87,41 +88,35 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$date = Factory::getDate(); $date = Factory::getDate();
$task = Factory::getApplication()->input->get('task'); $task = Factory::getApplication()->input->get('task');
$user = Factory::getApplication()->getIdentity(); $user = Factory::getApplication()->getIdentity();
$input = Factory::getApplication()->input; $input = Factory::getApplication()->input;
$task = $input->getString('task', ''); $task = $input->getString('task', '');
if ($array['id'] == 0 && empty($array['created_by'])) if ($array['id'] == 0 && empty($array['created_by'])) {
{
$array['created_by'] = Factory::getUser()->id; $array['created_by'] = Factory::getUser()->id;
} }
if ($array['id'] == 0 && empty($array['modified_by'])) if ($array['id'] == 0 && empty($array['modified_by'])) {
{
$array['modified_by'] = Factory::getUser()->id; $array['modified_by'] = Factory::getUser()->id;
} }
if ($task == 'apply' || $task == 'save') if ($task == 'apply' || $task == 'save') {
{
$array['modified_by'] = Factory::getUser()->id; $array['modified_by'] = Factory::getUser()->id;
} }
if (isset($array['params']) && is_array($array['params'])) if (isset($array['params']) && is_array($array['params'])) {
{
$registry = new Registry; $registry = new Registry;
$registry->loadArray($array['params']); $registry->loadArray($array['params']);
$array['params'] = (string) $registry; $array['params'] = (string) $registry;
} }
if (isset($array['metadata']) && is_array($array['metadata'])) if (isset($array['metadata']) && is_array($array['metadata'])) {
{
$registry = new Registry; $registry = new Registry;
$registry->loadArray($array['metadata']); $registry->loadArray($array['metadata']);
$array['metadata'] = (string) $registry; $array['metadata'] = (string) $registry;
} }
if (!$user->authorise('core.admin', 'com_circolari.circolare.' . $array['id'])) if (!$user->authorise('core.admin', 'com_circolari.circolare.' . $array['id'])) {
{
$actions = Access::getActionsFromFile( $actions = Access::getActionsFromFile(
JPATH_ADMINISTRATOR . '/components/com_circolari/access.xml', JPATH_ADMINISTRATOR . '/components/com_circolari/access.xml',
"/access/section[@name='circolare']/" "/access/section[@name='circolare']/"
@ -129,10 +124,8 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$default_actions = Access::getAssetRules('com_circolari.circolare.' . $array['id'])->getData(); $default_actions = Access::getAssetRules('com_circolari.circolare.' . $array['id'])->getData();
$array_jaccess = array(); $array_jaccess = array();
foreach ($actions as $action) foreach ($actions as $action) {
{ if (key_exists($action->name, $default_actions)) {
if (key_exists($action->name, $default_actions))
{
$array_jaccess[$action->name] = $default_actions[$action->name]; $array_jaccess[$action->name] = $default_actions[$action->name];
} }
} }
@ -141,8 +134,7 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
} }
// Bind the rules for ACL where supported. // Bind the rules for ACL where supported.
if (isset($array['rules']) && is_array($array['rules'])) if (isset($array['rules']) && is_array($array['rules'])) {
{
$this->setRules($array['rules']); $this->setRules($array['rules']);
} }
@ -163,8 +155,8 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
*/ */
public function store($updateNulls = true) public function store($updateNulls = true)
{ {
return parent::store($updateNulls); return parent::store($updateNulls);
} }
@ -179,14 +171,11 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
{ {
$rules = array(); $rules = array();
foreach ($jaccessrules as $action => $jaccess) foreach ($jaccessrules as $action => $jaccess) {
{
$actions = array(); $actions = array();
if ($jaccess) if ($jaccess) {
{ foreach ($jaccess->getData() as $group => $allow) {
foreach ($jaccess->getData() as $group => $allow)
{
$actions[$group] = ((bool)$allow); $actions[$group] = ((bool)$allow);
} }
} }
@ -205,12 +194,46 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
public function check() public function check()
{ {
// If there is an ordering column and this is a new row then get the next ordering value // If there is an ordering column and this is a new row then get the next ordering value
if (property_exists($this, 'ordering') && $this->id == 0) if (property_exists($this, 'ordering') && $this->id == 0) {
{
$this->ordering = self::getNextOrder(); $this->ordering = self::getNextOrder();
} }
// Ordering per nuovi record
if (property_exists($this, 'ordering') && (int) $this->id === 0) {
$this->ordering = self::getNextOrder();
}
// Titolo obbligatorio (se preferisci non forzarlo, rimuovi questo blocco)
$this->title = trim((string) ($this->title ?? ''));
if ($this->title === '') {
$this->setError(Text::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE'));
return false;
}
// --- ALIAS: genera se vuoto, pulisci, evita solo-numeri, rendilo unico ---
$this->alias = trim((string) ($this->alias ?? ''));
// Se non presente → dal titolo
if ($this->alias === '') {
$this->alias = ApplicationHelper::stringURLSafe($this->title);
} else {
$this->alias = ApplicationHelper::stringURLSafe($this->alias);
}
// Evita alias vuoto o numerico puro
if ($this->alias === '' || ctype_digit($this->alias)) {
$seed = (int) ($this->id ?: time());
$this->alias = ApplicationHelper::stringURLSafe($this->title . '-' . $seed);
}
// Unicità alias nella tabella
$base = $this->alias;
$i = 2;
while ($this->aliasExists($this->alias, (int) $this->id)) {
$this->alias = $base . '-' . $i;
$i++;
}
return parent::check(); return parent::check();
} }
@ -251,8 +274,7 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$assetParent->loadByName('com_circolari'); $assetParent->loadByName('com_circolari');
// Return the found asset-parent-id // Return the found asset-parent-id
if ($assetParent->id) if ($assetParent->id) {
{
$assetParentId = $assetParent->id; $assetParentId = $assetParent->id;
} }
@ -261,19 +283,35 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
//XXX_CUSTOM_TABLE_FUNCTION //XXX_CUSTOM_TABLE_FUNCTION
/** /**
* Delete a record by id * Delete a record by id
* *
* @param mixed $pk Primary key value to delete. Optional * @param mixed $pk Primary key value to delete. Optional
* *
* @return bool * @return bool
*/ */
public function delete($pk = null) public function delete($pk = null)
{ {
$this->load($pk); $this->load($pk);
$result = parent::delete($pk); $result = parent::delete($pk);
return $result; return $result;
} }
protected function aliasExists(string $alias, int $excludeId = 0): bool
{
$q = $this->_db->getQuery(true)
->select('COUNT(*)')
->from($this->_db->quoteName('#__circolari'))
->where($this->_db->quoteName('alias') . ' = ' . $this->_db->quote($alias));
if ($excludeId > 0) {
$q->where($this->_db->quoteName('id') . ' != ' . (int) $excludeId);
}
$this->_db->setQuery($q);
return ((int) $this->_db->loadResult()) > 0;
}
} }

View File

@ -35,6 +35,7 @@ HTMLHelper::_('bootstrap.tooltip');
<fieldset class="adminform"> <fieldset class="adminform">
<legend><?php echo Text::_('COM_CIRCOLARI_FIELDSET_CIRCOLARI'); ?></legend> <legend><?php echo Text::_('COM_CIRCOLARI_FIELDSET_CIRCOLARI'); ?></legend>
<?php echo $this->form->renderField('title'); ?> <?php echo $this->form->renderField('title'); ?>
<?php echo $this->form->renderField('alias'); ?>
<?php echo $this->form->renderField('description'); ?> <?php echo $this->form->renderField('description'); ?>
<?php echo $this->form->renderField('attachment'); ?> <?php echo $this->form->renderField('attachment'); ?>
<?php echo $this->form->renderField('image'); ?> <?php echo $this->form->renderField('image'); ?>

View File

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