diff --git a/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php b/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php
index 47186369..883ec836 100644
--- a/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php
+++ b/source/packages/com_mokosuite/admin/src/Controller/DisplayController.php
@@ -33,9 +33,10 @@ class DisplayController extends BaseController
'waflog' => 'core.admin',
'categories' => 'mokosuite.tickets',
'canned' => 'mokosuite.tickets',
- 'automation' => 'core.admin',
- 'database' => 'core.admin',
- 'cleanup' => 'mokosuite.cache',
+ 'automation' => 'core.admin',
+ 'database' => 'core.admin',
+ 'cleanup' => 'mokosuite.cache',
+ 'ticketsettings' => 'core.admin',
];
public function display($cachable = false, $urlparams = [])
@@ -413,6 +414,81 @@ class DisplayController extends BaseController
));
}
+ // ==================================================================
+ // Ticket Settings — Status/Priority CRUD
+ // ==================================================================
+
+ public function saveStatus()
+ {
+ Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
+
+ if (!$this->checkAcl('core.admin'))
+ {
+ $this->jsonForbidden();
+ return;
+ }
+
+ $input = Factory::getApplication()->getInput();
+ $this->jsonResponse($this->getModel('Tickets')->saveStatus([
+ 'id' => $input->getInt('id', 0),
+ 'title' => $input->getString('title', ''),
+ 'alias' => $input->getString('alias', ''),
+ 'color' => $input->getString('color', 'bg-secondary'),
+ 'is_default' => $input->getInt('is_default', 0),
+ 'is_closed' => $input->getInt('is_closed', 0),
+ 'ordering' => $input->getInt('ordering', 0),
+ ]));
+ }
+
+ public function deleteStatus()
+ {
+ Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
+
+ if (!$this->checkAcl('core.admin'))
+ {
+ $this->jsonForbidden();
+ return;
+ }
+
+ $id = Factory::getApplication()->getInput()->getInt('id', 0);
+ $this->jsonResponse($this->getModel('Tickets')->deleteStatus($id));
+ }
+
+ public function savePriority()
+ {
+ Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
+
+ if (!$this->checkAcl('core.admin'))
+ {
+ $this->jsonForbidden();
+ return;
+ }
+
+ $input = Factory::getApplication()->getInput();
+ $this->jsonResponse($this->getModel('Tickets')->savePriority([
+ 'id' => $input->getInt('id', 0),
+ 'title' => $input->getString('title', ''),
+ 'alias' => $input->getString('alias', ''),
+ 'color' => $input->getString('color', 'bg-secondary'),
+ 'is_default' => $input->getInt('is_default', 0),
+ 'ordering' => $input->getInt('ordering', 0),
+ ]));
+ }
+
+ public function deletePriority()
+ {
+ Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
+
+ if (!$this->checkAcl('core.admin'))
+ {
+ $this->jsonForbidden();
+ return;
+ }
+
+ $id = Factory::getApplication()->getInput()->getInt('id', 0);
+ $this->jsonResponse($this->getModel('Tickets')->deletePriority($id));
+ }
+
// ==================================================================
// KB Search
// ==================================================================
diff --git a/source/packages/com_mokosuite/admin/src/Model/TicketsModel.php b/source/packages/com_mokosuite/admin/src/Model/TicketsModel.php
index 2838c41c..df73885e 100644
--- a/source/packages/com_mokosuite/admin/src/Model/TicketsModel.php
+++ b/source/packages/com_mokosuite/admin/src/Model/TicketsModel.php
@@ -1133,6 +1133,117 @@ class TicketsModel extends BaseDatabaseModel
return $db->loadObjectList() ?: [];
}
+ // ==================================================================
+ // Status/Priority CRUD
+ // ==================================================================
+
+ public function saveStatus(array $data): array
+ {
+ $db = $this->getDatabase();
+ $obj = (object) $data;
+
+ if (!empty($obj->title) && empty($obj->alias))
+ {
+ $obj->alias = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $obj->title));
+ }
+
+ if (empty($obj->id))
+ {
+ unset($obj->id);
+ $db->insertObject('#__mokosuite_ticket_statuses', $obj, 'id');
+
+ return ['status' => 'ok', 'id' => (int) $obj->id, 'message' => 'Status created'];
+ }
+
+ $db->updateObject('#__mokosuite_ticket_statuses', $obj, 'id');
+
+ return ['status' => 'ok', 'id' => (int) $obj->id, 'message' => 'Status updated'];
+ }
+
+ public function deleteStatus(int $id): array
+ {
+ if ($id < 1)
+ {
+ return ['status' => 'error', 'message' => 'Invalid ID'];
+ }
+
+ $db = $this->getDatabase();
+
+ // Check no tickets use this status
+ $db->setQuery(
+ $db->getQuery(true)
+ ->select('COUNT(*)')
+ ->from($db->quoteName('#__mokosuite_tickets'))
+ ->where($db->quoteName('status_id') . ' = ' . $id)
+ );
+
+ if ((int) $db->loadResult() > 0)
+ {
+ return ['status' => 'error', 'message' => 'Cannot delete — status is in use by tickets'];
+ }
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete($db->quoteName('#__mokosuite_ticket_statuses'))
+ ->where($db->quoteName('id') . ' = ' . $id)
+ )->execute();
+
+ return ['status' => 'ok', 'message' => 'Status deleted'];
+ }
+
+ public function savePriority(array $data): array
+ {
+ $db = $this->getDatabase();
+ $obj = (object) $data;
+
+ if (!empty($obj->title) && empty($obj->alias))
+ {
+ $obj->alias = strtolower(preg_replace('/[^a-z0-9]+/i', '_', $obj->title));
+ }
+
+ if (empty($obj->id))
+ {
+ unset($obj->id);
+ $db->insertObject('#__mokosuite_ticket_priorities', $obj, 'id');
+
+ return ['status' => 'ok', 'id' => (int) $obj->id, 'message' => 'Priority created'];
+ }
+
+ $db->updateObject('#__mokosuite_ticket_priorities', $obj, 'id');
+
+ return ['status' => 'ok', 'id' => (int) $obj->id, 'message' => 'Priority updated'];
+ }
+
+ public function deletePriority(int $id): array
+ {
+ if ($id < 1)
+ {
+ return ['status' => 'error', 'message' => 'Invalid ID'];
+ }
+
+ $db = $this->getDatabase();
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->select('COUNT(*)')
+ ->from($db->quoteName('#__mokosuite_tickets'))
+ ->where($db->quoteName('priority_id') . ' = ' . $id)
+ );
+
+ if ((int) $db->loadResult() > 0)
+ {
+ return ['status' => 'error', 'message' => 'Cannot delete — priority is in use by tickets'];
+ }
+
+ $db->setQuery(
+ $db->getQuery(true)
+ ->delete($db->quoteName('#__mokosuite_ticket_priorities'))
+ ->where($db->quoteName('id') . ' = ' . $id)
+ )->execute();
+
+ return ['status' => 'ok', 'message' => 'Priority deleted'];
+ }
+
// ==================================================================
// Akeeba Ticket System Importer
// ==================================================================
diff --git a/source/packages/com_mokosuite/admin/src/View/Ticketsettings/HtmlView.php b/source/packages/com_mokosuite/admin/src/View/Ticketsettings/HtmlView.php
new file mode 100644
index 00000000..7cab5fe3
--- /dev/null
+++ b/source/packages/com_mokosuite/admin/src/View/Ticketsettings/HtmlView.php
@@ -0,0 +1,41 @@
+
+ *
+ * SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
+ *
+ * @package MokoSuite
+ * @subpackage Component
+ */
+
+namespace Moko\Component\MokoSuite\Administrator\View\Ticketsettings;
+
+defined('_JEXEC') or die;
+
+use Joomla\CMS\Language\Text;
+use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
+use Joomla\CMS\Toolbar\ToolbarHelper;
+
+class HtmlView extends BaseHtmlView
+{
+ protected $statuses = [];
+ protected $priorities = [];
+
+ public function display($tpl = null)
+ {
+ $model = $this->getModel('Tickets');
+
+ $this->statuses = $model->getStatuses();
+ $this->priorities = $model->getPriorities();
+
+ $this->addToolbar();
+
+ parent::display($tpl);
+ }
+
+ protected function addToolbar(): void
+ {
+ ToolbarHelper::title(Text::_('COM_MOKOSUITE_TICKET_SETTINGS'), 'cog');
+ ToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mokosuite&view=tickets');
+ }
+}
diff --git a/source/packages/com_mokosuite/admin/src/View/Ticketsettings/index.html b/source/packages/com_mokosuite/admin/src/View/Ticketsettings/index.html
new file mode 100644
index 00000000..94906bce
--- /dev/null
+++ b/source/packages/com_mokosuite/admin/src/View/Ticketsettings/index.html
@@ -0,0 +1 @@
+
diff --git a/source/packages/com_mokosuite/admin/tmpl/ticketsettings/default.php b/source/packages/com_mokosuite/admin/tmpl/ticketsettings/default.php
new file mode 100644
index 00000000..6f806192
--- /dev/null
+++ b/source/packages/com_mokosuite/admin/tmpl/ticketsettings/default.php
@@ -0,0 +1,203 @@
+
+ *
+ * SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
+ *
+ * @package MokoSuite
+ * @subpackage Component
+ */
+
+defined('_JEXEC') or die;
+
+use Joomla\CMS\HTML\HTMLHelper;
+use Joomla\CMS\Router\Route;
+use Joomla\CMS\Session\Session;
+
+$token = Session::getFormToken();
+
+$colorOptions = [
+ 'bg-primary', 'bg-secondary', 'bg-success', 'bg-danger',
+ 'bg-warning text-dark', 'bg-info text-dark', 'bg-dark', 'bg-light text-dark',
+];
+?>
+
+
+
+
+
+
+
+
+
+
+ | Title |
+ Color |
+ Default |
+ Closed? |
+ Order |
+ Actions |
+
+
+
+ statuses as $s): ?>
+
+ | escape($s->title); ?> (escape($s->alias); ?>) |
+ |
+ is_default ? 'Yes' : ''; ?> |
+ is_closed ? 'Closed' : ''; ?> |
+ ordering; ?> |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Title |
+ Color |
+ Default |
+ Order |
+ Actions |
+
+
+
+ priorities as $p): ?>
+
+ | escape($p->title); ?> (escape($p->alias); ?>) |
+ |
+ is_default ? 'Yes' : ''; ?> |
+ ordering; ?> |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/packages/com_mokosuite/admin/tmpl/ticketsettings/index.html b/source/packages/com_mokosuite/admin/tmpl/ticketsettings/index.html
new file mode 100644
index 00000000..94906bce
--- /dev/null
+++ b/source/packages/com_mokosuite/admin/tmpl/ticketsettings/index.html
@@ -0,0 +1 @@
+