diff --git a/administrator/forms/circolare.xml b/administrator/forms/circolare.xml
index 5ce21f5..13f2d49 100644
--- a/administrator/forms/circolare.xml
+++ b/administrator/forms/circolare.xml
@@ -14,6 +14,10 @@
+
loadForm(
- 'com_circolari.circolare',
- 'circolare',
- array(
- 'control' => 'jform',
- 'load_data' => $loadData
- )
- );
+ /**
+ * 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();
- if (empty($form))
- {
- return false;
- }
+ // Get the form.
+ $form = $this->loadForm(
+ 'com_circolari.circolare',
+ 'circolare',
+ array(
+ 'control' => 'jform',
+ 'load_data' => $loadData
+ )
+ );
- return $form;
- }
-
- /**
- * Method to get the data that should be injected in the form.
- *
- * @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($form)) {
+ return false;
+ }
- if (empty($data))
- {
- if ($this->item === null)
- {
- $this->item = $this->getItem();
- }
+ return $form;
+ }
- $data = $this->item;
-
- }
- return $data;
- }
- /**
- * Method to get a single record.
- *
- * @param integer $pk The id of the primary key.
- *
- * @return mixed Object on success, false on failure.
- *
- * @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
- }
+ /**
+ * Method to get the data that should be injected in the form.
+ *
+ * @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());
- return $item;
-
- }
+ if (empty($data)) {
+ if ($this->item === null) {
+ $this->item = $this->getItem();
+ }
- /**
- * 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();
+ $data = $this->item;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Method to get a single record.
+ *
+ * @param integer $pk The id of the primary key.
+ *
+ * @return mixed Object on success, false on failure.
+ *
+ * @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();
- // Access checks.
- if (!$user->authorise('core.create', 'com_circolari'))
- {
- throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED'));
- }
+ // Access checks.
+ if (!$user->authorise('core.create', 'com_circolari')) {
+ 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.
- PluginHelper::importPlugin($this->events_map['save']);
+ // Include the plugins for the save events.
+ PluginHelper::importPlugin($this->events_map['save']);
- $table = $this->getTable();
+ $table = $this->getTable();
- foreach ($pks as $pk)
- {
-
- if ($table->load($pk, true))
- {
- // Reset the id to create a new record.
- $table->id = 0;
+ foreach ($pks as $pk) {
- if (!$table->check())
- {
- throw new \Exception($table->getError());
- }
-
+ if ($table->load($pk, true)) {
+ // Reset the id to create a new record.
+ $table->id = 0;
- // Create the before save event.
- $beforeSaveEvent = AbstractEvent::create(
- $this->event_before_save,
- [
- 'context' => $context,
- 'subject' => $table,
- 'isNew' => true,
- 'data' => $table,
- ]
- );
+ if (!$table->check()) {
+ throw new \Exception($table->getError());
+ }
- // 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
- $result = isset($dispatchResult['result']) ? $dispatchResult['result'] : [];
+ // Create the before save event.
+ $beforeSaveEvent = AbstractEvent::create(
+ $this->event_before_save,
+ [
+ 'context' => $context,
+ 'subject' => $table,
+ 'isNew' => true,
+ 'data' => $table,
+ ]
+ );
- // Proceed with your logic
- if (in_array(false, $result, true) || !$table->store()) {
- throw new \Exception($table->getError());
- }
+ // Trigger the before save event.
+ $dispatchResult = Factory::getApplication()->getDispatcher()->dispatch($this->event_before_save, $beforeSaveEvent);
- // 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());
- }
-
- }
+ // Check if dispatch result is an array and handle accordingly
+ $result = isset($dispatchResult['result']) ? $dispatchResult['result'] : [];
- // Clean cache
- $this->cleanCache();
+ // Proceed with your logic
+ 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());
+ }
+ }
- /**
- * Prepare and sanitise the table prior to saving.
- *
- * @param Table $table Table Object
- *
- * @return void
- *
- * @since 1.0.0
- */
- protected function prepareTable($table)
- {
- jimport('joomla.filter.output');
+ // Clean cache
+ $this->cleanCache();
- 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;
- }
- }
- }
+ return true;
+ }
+
+ /**
+ * Prepare and sanitise the table prior to saving.
+ *
+ * @param Table $table Table Object
+ *
+ * @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;
+ }
}
diff --git a/administrator/src/Table/CircolareTable.php b/administrator/src/Table/CircolareTable.php
index bb84ed5..2f67584 100644
--- a/administrator/src/Table/CircolareTable.php
+++ b/administrator/src/Table/CircolareTable.php
@@ -1,4 +1,5 @@
typeAlias = 'com_circolari.circolare';
parent::__construct('#__circolari', 'id', $db);
$this->setColumnAlias('published', 'state');
-
}
/**
@@ -87,41 +88,35 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$date = Factory::getDate();
$task = Factory::getApplication()->input->get('task');
$user = Factory::getApplication()->getIdentity();
-
+
$input = Factory::getApplication()->input;
$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;
}
- if ($array['id'] == 0 && empty($array['modified_by']))
- {
+ if ($array['id'] == 0 && empty($array['modified_by'])) {
$array['modified_by'] = Factory::getUser()->id;
}
- if ($task == 'apply' || $task == 'save')
- {
+ if ($task == 'apply' || $task == 'save') {
$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->loadArray($array['params']);
$array['params'] = (string) $registry;
}
- if (isset($array['metadata']) && is_array($array['metadata']))
- {
+ if (isset($array['metadata']) && is_array($array['metadata'])) {
$registry = new Registry;
$registry->loadArray($array['metadata']);
$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(
JPATH_ADMINISTRATOR . '/components/com_circolari/access.xml',
"/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();
$array_jaccess = array();
- foreach ($actions as $action)
- {
- if (key_exists($action->name, $default_actions))
- {
+ foreach ($actions as $action) {
+ if (key_exists($action->name, $default_actions)) {
$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.
- if (isset($array['rules']) && is_array($array['rules']))
- {
+ if (isset($array['rules']) && is_array($array['rules'])) {
$this->setRules($array['rules']);
}
@@ -163,8 +155,8 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
*/
public function store($updateNulls = true)
{
-
-
+
+
return parent::store($updateNulls);
}
@@ -179,14 +171,11 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
{
$rules = array();
- foreach ($jaccessrules as $action => $jaccess)
- {
+ foreach ($jaccessrules as $action => $jaccess) {
$actions = array();
- if ($jaccess)
- {
- foreach ($jaccess->getData() as $group => $allow)
- {
+ if ($jaccess) {
+ foreach ($jaccess->getData() as $group => $allow) {
$actions[$group] = ((bool)$allow);
}
}
@@ -205,12 +194,46 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
public function check()
{
// 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();
}
-
-
+
+// 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();
}
@@ -251,8 +274,7 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
$assetParent->loadByName('com_circolari');
// Return the found asset-parent-id
- if ($assetParent->id)
- {
+ if ($assetParent->id) {
$assetParentId = $assetParent->id;
}
@@ -261,19 +283,35 @@ class CircolareTable extends Table implements VersionableTableInterface, Taggabl
//XXX_CUSTOM_TABLE_FUNCTION
-
- /**
- * Delete a record by id
- *
- * @param mixed $pk Primary key value to delete. Optional
- *
- * @return bool
- */
- public function delete($pk = null)
- {
- $this->load($pk);
- $result = parent::delete($pk);
-
- return $result;
- }
+
+ /**
+ * Delete a record by id
+ *
+ * @param mixed $pk Primary key value to delete. Optional
+ *
+ * @return bool
+ */
+ public function delete($pk = null)
+ {
+ $this->load($pk);
+ $result = parent::delete($pk);
+
+ 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;
+ }
+
}
diff --git a/administrator/tmpl/circolare/edit.php b/administrator/tmpl/circolare/edit.php
index b91ed74..6eba66d 100644
--- a/administrator/tmpl/circolare/edit.php
+++ b/administrator/tmpl/circolare/edit.php
@@ -35,6 +35,7 @@ HTMLHelper::_('bootstrap.tooltip');