| Current Path : /var/www/consult-e-syn/public_html/components/com_ats/ |
| Current File : /var/www/consult-e-syn/public_html/components/com_ats/router.php |
<?php
/**
* @package ats
* @copyright Copyright (c)2011-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
defined('_JEXEC') or die;
use Akeeba\TicketSystem\Admin\Helper\Debug;
use Akeeba\TicketSystem\Site\Helper\Router;
use Akeeba\TicketSystem\Site\Model\Tickets;
use FOF40\Container\Container;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Component\Router\RouterBase;
use Joomla\CMS\Factory;
use Joomla\CMS\Log\Log;
if (!defined('FOF40_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof40/include.php'))
{
return;
}
// Let's get the container so our autoloader gets registered
Container::getInstance('com_ats');
/**
* Class AtsRouter
*
* @since 3.0.4
*/
class AtsRouter extends RouterBase
{
public static $ticketsCache = [];
private static $atsHandleViews = [
'Categories', 'Latest', 'My', 'Mies', 'NewTicket', 'Ticket', 'Tickets',
];
/**
* Loads a minimal snapshot of a ticket.
*
* This method uses a cache to prevent loading the same ticket multiple times on each page, reducing the number of
* queries and the page load time.
*
* @param int $id The ticket ID to load
*
* @return object
*
* @since 4.0.0
*/
public static function loadTicket($id)
{
if (isset(self::$ticketsCache[$id]))
{
return self::$ticketsCache[$id];
}
$container = FOF40\Container\Container::getInstance('com_ats');
/** @var Tickets $ticket */
$ticket = $container->factory->model('Tickets')->tmpInstance();
$ticket->load($id);
self::$ticketsCache[$id] = (object) [
'catid' => $ticket->catid,
'alias' => $ticket->alias,
'ats_ticket_id' => $ticket->ats_ticket_id,
'title' => $ticket->title,
];
return self::$ticketsCache[$id];
}
/**
* Precondition the query.
*
* This adds an Itemid if one is not already specified.
*
* @param array $query
*
* @return array
*
* @throws Exception
* @since 3.3.1
*/
public function preprocess($query)
{
// First, try to find a suitable Itemid using our legacy route building method
$temp = array_merge($query);
$this->build($temp);
$itemid = isset($temp['Itemid']) ? $temp['Itemid'] : null;
// If no Itemid was found fall back to the root node (ATS categories list)
if (empty($itemid))
{
$menuItem = Router::findMenu([
'view' => 'Categories',
]);
$itemid = empty($menuItem) ? null : $menuItem->id;
}
// If I've found an Itemid adjust the query
if ($itemid)
{
$query['Itemid'] = $itemid;
}
return $query;
}
/**
* Build method for URLs
* This method is meant to transform the query parameters into a more human
* readable form. It is only executed when SEF mode is switched on.
*
* @param array &$query An array of URL arguments
*
* @return array The URL arguments to use to assemble the subsequent URL.
*
* @throws Exception
* @since 3.0.4
*/
public function build(&$query)
{
$container = FOF40\Container\Container::getInstance('com_ats');
$segments = [];
// We need to find out if the menu item link has a view param
$menuQuery = [];
$menuView = 'Categories';
$Itemid = Router::getAndPop($query, 'Itemid', 0);
// Get the menu view, if an Item ID exists
if ($Itemid)
{
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
if (is_object($menu))
{
parse_str(str_replace('index.php?', '', $menu->link), $menuQuery); // remove "index.php?" and parse
if (array_key_exists('view', $menuQuery))
{
$menuView = $menuQuery['view'];
}
}
$query['Itemid'] = $Itemid;
}
// Add the view
$newView = array_key_exists('view', $query) ? $query['view'] : $menuView;
// We can only handle specific views. Is it one of them?
if (!in_array($newView, self::$atsHandleViews))
{
if ($Itemid)
{
$query['Itemid'] = $Itemid;
}
return [];
}
// Remove the option and view from the query
Router::getAndPop($query, 'view');
$qoptions = [];
if (isset($query['lang']))
{
$qoptions = ['lang' => $query['lang']];
}
// Fetch the category ID
$catID = Router::getAndPop($query, 'category', 0);
// If there's a NewTicket view without a category ID I have to fake the view so I can handle it differently.
if (($newView == 'NewTicket') && empty($catID))
{
$newView = 'NewTicketLanding';
}
// Build the URL
switch ($newView)
{
case 'Tickets':
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => 'Tickets',
]);
$params = [
'category' => $catID,
];
// First of all let's search for a menu entry that has the Category ID has param
$found = Router::checkMenu($menu, $qoptions, $params);
// No luck? Let's try to search for a menu entry with the Category ID as _query_ param
if (!$found)
{
$qoptions2 = array_merge($qoptions, ['category' => $catID]);
$found = Router::checkMenu($menu, $qoptions2);
}
if (!$found)
{
// Try to find a menu item ID directly for this category
$item = Router::findMenu($qoptions, $params);
// Or, try to find a manual link
if (is_null($item))
{
$item = Router::findMenu($qoptions2);
}
if (!is_null($item))
{
$Itemid = $item->id;
$found = true;
}
}
// Still nothing? Let's fallback to the Categories view (that should be always set)
// PLEASE NOTE: $found MUST BE LEFT TO FALSE! In this way we will take the category slug for the URL
if (!$found)
{
$qoptions = array_merge($qoptions, ['view' => 'Categories']);
$item = Router::findMenu($qoptions);
if (!is_null($item))
{
$Itemid = $item->id;
$query['Itemid'] = $Itemid;
}
}
// Get and append the category alias path, if the category wasn't found
if (!$found)
{
$segments = array_merge($segments, $this->getPathToCategory($catID));
}
break;
case 'Ticket':
$ticketID = Router::getAndPop($query, 'id', 0);
$ticket = self::loadTicket($ticketID);
$catID = $ticket->catid;
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => 'Tickets',
]);
$params = [
'category' => $catID,
];
// Check the current Itemid for fitness
$found = Router::checkMenu($menu, $qoptions, $params);
// Try to validate the menu item with a category ID
if (!$found)
{
$qoptions2 = array_merge($qoptions, ['category' => $catID]);
$found = Router::checkMenu($menu, $qoptions2);
}
if (!$found)
{
// Try to find a menu item ID directly for this category
$item = Router::findMenu($qoptions, $params);
// Or, try to find a manual link
if (is_null($item))
{
$item = Router::findMenu($qoptions2);
}
}
// If the category wasn't found get the ticket system root and append the category alias
if (!$found)
{
// Try to find the ticket system root with an explicit view name
if (is_null($item))
{
unset($qoptions2['category']);
$qoptions2['view'] = 'Categories';
$item = Router::findMenu($qoptions2);
}
// Try to find the ticket system root without any view name
if (is_null($item))
{
$qoptions2['view'] = null;
$item = Router::findMenu($qoptions2);
}
if (is_null($item))
{
static $hasShownMessage = false;
if (!$hasShownMessage)
{
$hasShownMessage = true;
// The user has screwed up their site configuration. We have to log a message.
/** @var SiteApplication $app */
$app = Factory::getApplication();
$lg = $app->getLanguage()->getName();
$errorMessage =
"You do not have a menu item pointing to the list all Akeeba Ticket System categories for the $lg language. Ticket links may be broken until you add such a menu item.";
Log::add($errorMessage, Log::CRITICAL, 'com_ats');
if (Debug::getJoomlaDebug())
{
Factory::getApplication()->enqueueMessage($errorMessage, 'warning');
}
}
}
if (!is_null($item))
{
$Itemid = $item->id;
$segments = array_merge($segments, $this->getPathToCategory($catID));
}
}
// Append the ticket ID and alias
$segments[] = $ticketID . ':' . $ticket->alias;
break;
case 'NewTicket':
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => 'Tickets',
]);
$params = [
'category' => $catID,
];
$found = Router::checkMenu($menu, $qoptions, $params);
if (!$found)
{
$qoptions2 = array_merge($qoptions, ['category' => $catID]);
$found = Router::checkMenu($menu, $qoptions2);
}
if (!$found)
{
// Try to find a menu item ID directly for this category
$item = Router::findMenu($qoptions, $params);
// Or, try to find a manual link
if (is_null($item))
{
$item = Router::findMenu($qoptions2);
}
if (!is_null($item))
{
$Itemid = $item->id;
$found = true;
}
}
// Get and append the category alias path, if the category wasn't found
if (!$found)
{
$segments = array_merge($segments, $this->getPathToCategory($catID));
}
// Append "new"
$segments[] = 'new';
// Make sure the view name is not added
$newView = '';
break;
case 'NewTicketLanding':
$newView = 'NewTicket';
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => 'NewTicket',
]);
$found = Router::checkMenu($menu, $qoptions);
if (!$found)
{
// Try to find a menu item ID directly for this view
$item = Router::findMenu($qoptions);
if (!is_null($item))
{
$Itemid = $item->id;
$found = true;
}
}
if (!$found)
{
// Try to find a menu item ID for the ticket system root
$qoptions2 = [
'option' => 'com_ats',
'view' => 'Categories',
];
$item = Router::findMenu($qoptions2);
if (!is_null($item))
{
$Itemid = $item->id;
}
}
// Append "new"
$segments[] = 'new';
// Make sure the view name is not added
$newView = '';
break;
case 'My':
case 'Mies':
// My and Mies views need to be mapped to 'My'.
$newView = 'My';
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => 'My',
]);
$found = Router::checkMenu($menu, $qoptions);
if (!$found)
{
// Try to find a menu item ID directly for this view
$item = Router::findMenu($qoptions);
$Itemid = null;
if (!is_null($item))
{
$Itemid = $item->id;
$found = true;
// Do not append the view name in this case
$newView = '';
}
}
if (!$found)
{
// Try to find a menu item ID using the plural form
$qoptions['view'] = 'Mies';
// If I have a specific query language, let's try to find out a menu mapped as "all languages"
if (isset($qoptions['lang']) && $qoptions['lang'] != '*')
{
$qoptions['lang'] = '*';
}
$item = Router::findMenu($qoptions);
$Itemid = null;
if (!is_null($item))
{
$Itemid = $item->id;
$found = true;
// Do not append the view name in this case
$newView = '';
}
}
// Still not found? We have to add a query to the main page of the ticket system.
if (!$found)
{
// Do not append the view name in this case
$newView = '';
// Add the view query
$query['view'] = 'Mies';
// Try to find a menu item ID directly for this view
$qoptions['view'] = 'Categories';
$item = Router::findMenu($qoptions);
$Itemid = null;
if (!is_null($item))
{
$Itemid = $item->id;
}
}
break;
case 'Categories':
case 'Latest':
// Do I have to look for a new Item ID?
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
$qoptions = array_merge($qoptions, [
'option' => 'com_ats',
'view' => $newView,
]);
$found = Router::checkMenu($menu, $qoptions);
if (!$found)
{
// Try to find a menu item ID directly for this category
$item = Router::findMenu($qoptions);
if (!is_null($item))
{
$Itemid = $item->id;
}
}
// No other parameters are expected
break;
}
// Process the Itemid
$menuView = null;
if ($Itemid)
{
$menu = Factory::getApplication()->getMenu()->getItem($Itemid);
if (is_object($menu))
{
parse_str(str_replace('index.php?', '', $menu->link), $menuQuery); // remove "index.php?" and parse
if (array_key_exists('view', $menuQuery))
{
$menuView = $menuQuery['view'];
}
}
$query['Itemid'] = $Itemid;
}
// If the menu's view is different to the new view, add the view name to the URL
if (!empty($newView) && ($newView != $menuView))
{
// Only append the view name if the $menuView is not categories, OR if the
// $menuView is categories and the $newView IS NOT 'ticket' or 'tickets'
if ((strtolower($menuView) != strtolower('Categories')) || empty($menuView))
{
array_unshift($segments, $newView);
}
elseif (!in_array(strtolower($newView), ['ticket', 'tickets']))
{
array_unshift($segments, $newView);
}
}
return $segments;
}
/**
* Parse method for URLs
* This method is meant to transform the human readable URL back into
* query parameters. It is only executed when SEF mode is switched on.
*
* @param array &$segments The segments of the URL to parse.
*
* @return array The URL attributes to be used by the application.
*
* @throws Exception
* @since 3.0.4
*/
public function parse(&$segments)
{
// Fetch the default query from the active menu item
$mObject = Factory::getApplication()->getMenu()->getActive();
$query = is_object($mObject) ? $mObject->query : [];
if (!array_key_exists('option', $query))
{
$query['option'] = 'com_ats';
}
if (!array_key_exists('view', $query))
{
$query['view'] = 'Categories';
}
$view = $query['view'];
// Replace : with - in segments
$segments = Router::preconditionSegments($segments);
// Do not process an empty segment list (just in case...)
if (empty($segments))
{
return $query;
}
$view = str_replace([
'categories', 'newticket', 'ticket',
], [
'Categories', 'NewTicket', 'Ticket',
], $view);
// Do not process a view I know nothing at all about
if (!in_array($view, self::$atsHandleViews))
{
return $query;
}
// Initialise
$ticketID = null;
$catID = null;
// Do I have a new ticket view?
$lastSegment = array_pop($segments);
if ($lastSegment == 'new')
{
$view = 'NewTicket';
}
else
{
$segments[] = $lastSegment;
}
// If we have segments and we're in a no-parameters view, we have to deal
// with a different view than the one listed in the menu.
if (in_array($view, ['My', 'Latest']))
{
$view = array_shift($segments);
}
// If this is a categories or tickets view, we have to check the last
// segment to figure out if it's really a "ticket" view.
if (in_array($view, ['Categories', 'Tickets', 'Ticket', 'NewTicket']))
{
// Is it a category path?
$path = implode('/', $segments);
$db = Factory::getDbo();
$q = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__categories'))
->where($db->qn('path') . ' = ' . $db->q($path))
->where($db->qn('extension') . ' = ' . $db->q('com_ats'));
$db->setQuery($q);
$catID = $db->loadResult();
if ($catID)
{
// The category exists, therefore it's a "tickets" view, unless it's
// a 'NewTicket' view
if ($view != 'NewTicket')
{
$view = 'Tickets';
}
}
elseif (!empty($segments))
{
// Do we have a valid-looking ticket ID?
$lastSegment = array_pop($segments);
$segments[] = $lastSegment;
$sParts = explode('-', $lastSegment);
$ticketID = (int) $sParts[0];
if ($ticketID > 0)
{
// We have a valid-looking ticket ID. I think it's a "ticket" view.
$view = 'Ticket';
// We don't care about the path. The ticket ID is enough, he he!
$segments = [];
}
elseif ($view == 'NewTicket')
{
$ticketID = 0;
$catID = $mObject->query['category'];
if (isset($query['id']))
{
unset($query['id']);
}
}
else
{
// This will result in a 404. Can't have it all, dude.
$catID = 0;
$view = 'Tickets';
}
}
$query['view'] = $view;
if ($ticketID)
{
$query['id'] = $ticketID;
}
elseif ($catID)
{
$query['category'] = $catID;
}
}
$segments = [];
return $query;
}
/**
* Returns the path (segment parts) pointing to an ATS category e.g. foo/bar/baz/bat
*
* @param int $catID
*
* @return array
*/
private function getPathToCategory($catID)
{
static $cache = [];
if (isset($cache[$catID]))
{
return $cache[$catID];
}
$db = Factory::getDbo();
$q = $db->getQuery(true)
->select($db->qn('path'))
->from($db->qn('#__categories'))
->where($db->qn('id') . ' = ' . $db->q($catID))
->where($db->qn('extension') . ' = ' . $db->q('com_ats'));
$db->setQuery($q);
$path = $db->loadResult();
$pathParts = explode('/', $path);
$cache[$catID] = [];
foreach ($pathParts as $p)
{
$cache[$catID][] = $p;
}
return $cache[$catID];
}
}