Files
MokoSuiteClient/source/packages/com_mokosuite/site/src/Controller/DisplayController.php
T
Jonathan Miller 00d44256b4 refactor: rename MokoWaaS to MokoSuite across entire codebase
Rebrand all 17 sub-extensions from mokowaas to mokosuite naming,
including component, plugins, modules, task plugins, and webservices.
Updates package manifest, workflows, docs, wiki, and issue templates.
Adds new plg_system_mokosuite_license extension.
2026-06-07 09:25:45 -05:00

268 lines
6.6 KiB
PHP

<?php
/**
* @package MokoSuite
* @subpackage com_mokosuite.site
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
namespace Moko\Component\MokoSuite\Site\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
class DisplayController extends BaseController
{
protected $default_view = 'tickets';
public function display($cachable = false, $urlparams = [])
{
$user = Factory::getApplication()->getIdentity();
if ($user->guest)
{
Factory::getApplication()->enqueueMessage('Please log in to access the support portal.', 'warning');
Factory::getApplication()->redirect(Route::_(
'index.php?option=com_users&view=login&return=' . base64_encode('index.php?option=com_mokosuite&view=tickets'),
false
));
return;
}
return parent::display($cachable, $urlparams);
}
/**
* Submit a new ticket.
*/
public function submitTicket()
{
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
$user = Factory::getApplication()->getIdentity();
if ($user->guest)
{
$this->jsonResponse(['success' => false, 'message' => 'Please log in.']);
return;
}
$input = Factory::getApplication()->getInput();
// Use admin TicketsModel
$model = $this->getModel('Tickets', 'Administrator');
$this->jsonResponse($model->createTicket([
'subject' => $input->getString('subject', ''),
'body' => $input->getRaw('body', ''),
'priority' => $input->getString('priority', 'normal'),
'category_id' => $input->getInt('category_id', 0),
]));
}
/**
* Submit a reply.
*/
public function submitReply()
{
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
$user = Factory::getApplication()->getIdentity();
$input = Factory::getApplication()->getInput();
if ($user->guest)
{
$this->jsonResponse(['success' => false, 'message' => 'Please log in.']);
return;
}
$ticketId = $input->getInt('ticket_id', 0);
$model = $this->getModel('Tickets', 'Administrator');
$ticket = $model->getTicket($ticketId);
if (!$ticket)
{
$this->jsonResponse(['success' => false, 'message' => 'Ticket not found.']);
return;
}
// Customers can only reply to their own tickets; staff can reply to any
if ((int) $ticket->created_by !== $user->id && !$this->isStaff($user))
{
$this->jsonResponse(['success' => false, 'message' => 'Access denied.']);
return;
}
// Staff replies from frontend are not internal notes
$this->jsonResponse($model->addReply(
$ticketId,
$input->getRaw('body', ''),
false
));
}
/**
* Update ticket status (staff/manager only from frontend).
*/
public function updateStatus()
{
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
$user = Factory::getApplication()->getIdentity();
if (!$this->isStaff($user))
{
$this->jsonResponse(['success' => false, 'message' => 'Access denied.']);
return;
}
$input = Factory::getApplication()->getInput();
$model = $this->getModel('Tickets', 'Administrator');
$this->jsonResponse($model->updateStatus(
$input->getInt('ticket_id', 0),
$input->getString('status', '')
));
}
/**
* Assign a ticket (manager only from frontend).
*/
public function assignTicket()
{
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
$user = Factory::getApplication()->getIdentity();
if (!$user->authorise('mokosuite.tickets.assign', 'com_mokosuite'))
{
$this->jsonResponse(['success' => false, 'message' => 'Access denied.']);
return;
}
$input = Factory::getApplication()->getInput();
$ticketId = $input->getInt('ticket_id', 0);
$assignTo = $input->getInt('assigned_to', 0);
try
{
$db = Factory::getDbo();
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__mokosuite_tickets'))
->set($db->quoteName('assigned_to') . ' = ' . ($assignTo ?: 'NULL'))
->set($db->quoteName('modified') . ' = ' . $db->quote(Factory::getDate()->toSql()))
->where($db->quoteName('id') . ' = ' . $ticketId)
)->execute();
$this->jsonResponse(['success' => true, 'message' => 'Ticket assigned.']);
}
catch (\Throwable $e)
{
$this->jsonResponse(['success' => false, 'message' => $e->getMessage()]);
return;
}
}
/**
* Submit a data privacy request from frontend.
*/
public function submitDataRequest()
{
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
$user = Factory::getApplication()->getIdentity();
if ($user->guest)
{
$this->jsonResponse(['success' => false, 'message' => 'Please log in.']);
return;
}
$type = Factory::getApplication()->getInput()->getString('type', '');
$model = new \Moko\Component\MokoSuite\Administrator\Model\PrivacyModel();
$this->jsonResponse($model->createRequest($user->id, $type, 'Submitted via self-service portal'));
}
/**
* Check if user is support staff (can manage tickets beyond their own).
*/
private function isStaff($user): bool
{
if ($user->guest)
{
return false;
}
// Super admins always staff
if ($user->authorise('core.admin'))
{
return true;
}
// Anyone with mokosuite.tickets ACL on the component is staff
return $user->authorise('mokosuite.tickets', 'com_mokosuite');
}
/**
* Search KB articles via Smart Search (com_finder).
*/
public function searchKb()
{
$query = Factory::getApplication()->getInput()->getString('q', '');
if (strlen($query) < 3)
{
$this->jsonResponse(['results' => []]);
}
try
{
$db = Factory::getDbo();
$escaped = $db->quote('%' . $db->escape($query, true) . '%');
$results = $db->setQuery(
$db->getQuery(true)
->select([
$db->quoteName('l.link_id'),
$db->quoteName('l.title'),
$db->quoteName('l.url'),
$db->quoteName('l.description'),
])
->from($db->quoteName('#__finder_links', 'l'))
->where($db->quoteName('l.published') . ' = 1')
->where('(' . $db->quoteName('l.title') . ' LIKE ' . $escaped
. ' OR ' . $db->quoteName('l.description') . ' LIKE ' . $escaped . ')')
->order($db->quoteName('l.title') . ' ASC')
->setLimit(8)
)->loadObjectList() ?: [];
foreach ($results as $r)
{
$r->description = mb_substr(strip_tags($r->description ?? ''), 0, 150);
}
$this->jsonResponse(['results' => $results]);
}
catch (\Throwable $e)
{
$this->jsonResponse(['results' => []]);
}
}
private function jsonResponse(array $data): void
{
$app = Factory::getApplication();
$app->setHeader('Content-Type', 'application/json');
echo json_encode($data);
$app->close();
}
}