Your IP : 216.73.217.142


Current Path : /var/www/consult-e-syn/public_html/components/com_ats/
Upload File :
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];
	}
}