Skip to content

Commit

Permalink
Jroute between sites (#16879)
Browse files Browse the repository at this point in the history
* allow to force site client when we are in admin

* allow from site to admin too

* first try to make language filter router work on admin too when called via JRoute

* revert

* revert 2

* make language filter pluign work with JRoute from admin

* cs

* cs 2

* use app

* solve php notices

* Few tweaks for the PR

* Suggestions from @mbabker, and few other improvements.

* Moved active client detection logic to JRoute::_() and disallow null client value in JRoute::link()

* Expand mocking in JHtmlBehaviorTest

* Mocking in JHtmlIconsTest

* Fix reset of router property

* Mocking in JPaginationTest

* Mocking in JDocumentOpensearchTest

* Mocking in JDocumentRendererHtmlModulesTest

* Mocking in JFormFieldRulesTest

* Test fixes

* Moved SEF mode detection to CMSApplication for it being called from all application instances.

* Remove magic methods to force using of JRoute::link() directly
  • Loading branch information
izharaazmi authored and Michael Babker committed May 12, 2018
1 parent 393c72d commit de9d0ea
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 61 deletions.
2 changes: 2 additions & 0 deletions libraries/src/Application/CMSApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ public static function getRouter($name = null, array $options = array())
$name = $app->getName();
}

$options['mode'] = \JFactory::getConfig()->get('sef');

try
{
$router = \JRouter::getInstance($name, $options);
Expand Down
2 changes: 0 additions & 2 deletions libraries/src/Application/SiteApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,6 @@ public function getPathway($name = 'site', $options = array())
*/
public static function getRouter($name = 'site', array $options = array())
{
$options['mode'] = \JFactory::getConfig()->get('sef');

return parent::getRouter($name, $options);
}

Expand Down
8 changes: 5 additions & 3 deletions libraries/src/Router/AdministratorRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Uri\Uri;

/**
* Class to create and parse routes
*
Expand All @@ -20,7 +22,7 @@ class AdministratorRouter extends Router
/**
* Function to convert a route to an internal URI.
*
* @param \JUri &$uri The uri.
* @param Uri &$uri The uri.
*
* @return array
*
Expand All @@ -36,7 +38,7 @@ public function parse(&$uri)
*
* @param string $url The internal URL
*
* @return string The absolute search engine friendly URL
* @return Uri The absolute search engine friendly URL
*
* @since 1.5
*/
Expand All @@ -49,7 +51,7 @@ public function build($url)
$route = $uri->getPath();

// Add basepath to the uri
$uri->setPath(\JUri::base(true) . '/' . $route);
$uri->setPath(Uri::root(true) . '/' . basename(JPATH_ADMINISTRATOR) . '/' . $route);

return $uri;
}
Expand Down
67 changes: 51 additions & 16 deletions libraries/src/Router/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class Route
/**
* The route object so we don't have to keep fetching it.
*
* @var Router
* @var Router[]
* @since 12.2
*/
private static $_router = null;
private static $_router = array();

/**
* Translates an internal Joomla URL to a humanly readable URL.
* Translates an internal Joomla URL to a humanly readable URL. This method builds links for the current active client.
*
* @param string $url Absolute or Relative URI to Joomla resource.
* @param boolean $xhtml Replace & by & for XML compliance.
Expand All @@ -38,33 +38,68 @@ class Route
* 1: Make URI secure using global secure site URI.
* 2: Make URI unsecure using the global unsecure site URI.
*
* @return string The translated humanly readable URL.
* @return string The translated humanly readable URL.
*
* @since 11.1
*/
public static function _($url, $xhtml = true, $ssl = null)
{
if (!self::$_router)
try
{
// Get the router.
$app = Factory::getApplication();
self::$_router = $app::getRouter();
$app = Factory::getApplication();
$client = $app->getName();

// Make sure that we have our router
if (!self::$_router)
{
return;
}
return static::link($client, $url, $xhtml, $ssl);
}
catch (\RuntimeException $e)
{
// Before __DEPLOY_VERSION__ this method failed silently on router error. This B/C will be removed in Joomla 4.0.
return null;
}
}

/**
* Translates an internal Joomla URL to a humanly readable URL.
* NOTE: To build link for active client instead of a specific client, you can use <var>JRoute::_()</var>
*
* @param string $client The client name for which to build the link.
* @param string $url Absolute or Relative URI to Joomla resource.
* @param boolean $xhtml Replace & by &amp; for XML compliance.
* @param integer $ssl Secure state for the resolved URI.
* 0: (default) No change, use the protocol currently used in the request
* 1: Make URI secure using global secure site URI.
* 2: Make URI unsecure using the global unsecure site URI.
*
* @return string The translated humanly readable URL.
*
* @throws \RuntimeException
*
* @since __DEPLOY_VERSION__
*/
public static function link($client, $url, $xhtml = true, $ssl = null)
{
// If we cannot process this $url exit early.
if (!is_array($url) && (strpos($url, '&') !== 0) && (strpos($url, 'index.php') !== 0))
{
return $url;
}

// Build route.
$uri = self::$_router->build($url);
// Get the router instance, only attempt when a client name is given.
if ($client && !isset(self::$_router[$client]))
{
$app = Factory::getApplication();

self::$_router[$client] = $app->getRouter($client);
}

// Make sure that we have our router
if (!isset(self::$_router[$client]))
{
throw new \RuntimeException(\JText::sprintf('JLIB_APPLICATION_ERROR_ROUTER_LOAD', $client), 500);
}

// Build route.
$uri = self::$_router[$client]->build($url);
$scheme = array('path', 'query', 'fragment');

/*
Expand All @@ -80,7 +115,7 @@ public static function _($url, $xhtml = true, $ssl = null)

if (!is_array($host_port))
{
$uri2 = Uri::getInstance();
$uri2 = Uri::getInstance();
$host_port = array($uri2->getHost(), $uri2->getPort());
}

Expand Down
4 changes: 2 additions & 2 deletions libraries/src/Router/SiteRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ public function build($url)
}
}

// Add basepath to the uri
$uri->setPath(\JUri::base(true) . '/' . $route);
// Add frontend basepath to the uri
$uri->setPath(\JUri::root(true) . '/' . $route);

return $uri;
}
Expand Down
62 changes: 38 additions & 24 deletions plugins/system/languagefilter/languagefilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,15 @@ public function __construct(&$subject, $config)

$this->app = JFactory::getApplication();

// Setup language data.
$this->mode_sef = $this->app->get('sef', 0);
$this->sefs = JLanguageHelper::getLanguages('sef');
$this->lang_codes = JLanguageHelper::getLanguages('lang_code');
$this->default_lang = JComponentHelper::getParams('com_languages')->get('site', 'en-GB');

// If language filter plugin is executed in a site page.
if ($this->app->isClient('site'))
{
// Setup language data.
$this->mode_sef = $this->app->get('sef', 0);
$this->sefs = JLanguageHelper::getLanguages('sef');
$this->lang_codes = JLanguageHelper::getLanguages('lang_code');
$this->default_lang = JComponentHelper::getParams('com_languages')->get('site', 'en-GB');

$levels = JFactory::getUser()->getAuthorisedViewLevels();

foreach ($this->sefs as $sef => $language)
Expand All @@ -112,6 +113,21 @@ public function __construct(&$subject, $config)
}
}
}
// If language filter plugin is executed in a admin page (ex: JRoute site).
else
{
// Set current language to default site language, fallback to en-GB if there is no content language for the default site language.
$this->current_lang = isset($this->lang_codes[$this->default_lang]) ? $this->default_lang : 'en-GB';

foreach ($this->sefs as $sef => $language)
{
if (!array_key_exists($language->lang_code, JLanguageHelper::getInstalledLanguages(0)))
{
unset($this->lang_codes[$language->lang_code]);
unset($this->sefs[$language->sef]);
}
}
}
}

/**
Expand All @@ -125,26 +141,24 @@ public function onAfterInitialise()
{
$this->app->item_associations = $this->params->get('item_associations', 0);

if ($this->app->isClient('site'))
{
$router = $this->app->getRouter();

// Attach build rules for language SEF.
$router->attachBuildRule(array($this, 'preprocessBuildRule'), JRouter::PROCESS_BEFORE);
$router->attachBuildRule(array($this, 'buildRule'), JRouter::PROCESS_DURING);
// We need to make sure we are always using the site router, even if the language plugin is executed in admin app.
$router = JApplicationCms::getInstance('site')->getRouter('site');

if ($this->mode_sef)
{
$router->attachBuildRule(array($this, 'postprocessSEFBuildRule'), JRouter::PROCESS_AFTER);
}
else
{
$router->attachBuildRule(array($this, 'postprocessNonSEFBuildRule'), JRouter::PROCESS_AFTER);
}
// Attach build rules for language SEF.
$router->attachBuildRule(array($this, 'preprocessBuildRule'), JRouter::PROCESS_BEFORE);
$router->attachBuildRule(array($this, 'buildRule'), JRouter::PROCESS_DURING);

// Attach parse rules for language SEF.
$router->attachParseRule(array($this, 'parseRule'), JRouter::PROCESS_DURING);
if ($this->mode_sef)
{
$router->attachBuildRule(array($this, 'postprocessSEFBuildRule'), JRouter::PROCESS_AFTER);
}
else
{
$router->attachBuildRule(array($this, 'postprocessNonSEFBuildRule'), JRouter::PROCESS_AFTER);
}

// Attach parse rules for language SEF.
$router->attachParseRule(array($this, 'parseRule'), JRouter::PROCESS_DURING);
}

/**
Expand All @@ -157,7 +171,7 @@ public function onAfterInitialise()
public function onAfterRoute()
{
// Add custom site name.
if (isset($this->lang_codes[$this->current_lang]) && $this->lang_codes[$this->current_lang]->sitename)
if ($this->app->isClient('site') && isset($this->lang_codes[$this->current_lang]) && $this->lang_codes[$this->current_lang]->sitename)
{
$this->app->set('sitename', $this->lang_codes[$this->current_lang]->sitename);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/core/case/case.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class_exists('JApplication');
* @param array $options A set of options to configure the mock.
* @param array $constructor An array containing constructor arguments to inject into the mock.
*
* @return JApplicationCms
* @return JApplicationCms|PHPUnit_Framework_MockObject_MockObject
*
* @since 3.2
*/
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/core/mock/application/cms.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public static function getMethods()
// Collect all the relevant methods in JApplicationCms (work in progress).
$methods = array(
'getMenu',
'getName',
'getPathway',
'getTemplate',
'getLanguageFilter',
Expand Down Expand Up @@ -75,7 +76,7 @@ public static function addBehaviours($test, $mockObject, $options)
* @param array $options A set of options to configure the mock.
* @param array $constructor An array containing constructor arguments to inject into the mock.
*
* @return PHPUnit_Framework_MockObject_MockObject
* @return JApplicationCms|PHPUnit_Framework_MockObject_MockObject
*
* @since 3.2
*/
Expand Down
21 changes: 20 additions & 1 deletion tests/unit/suites/libraries/cms/html/JHtmlBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ protected function setUp()

$this->saveFactoryState();

JFactory::$application = $this->getMockCmsApp();
$mockApp = $this->getMockCmsApp();
$mockApp->expects($this->any())
->method('getName')
->willReturn('site');

$mockApp->expects($this->any())
->method('isClient')
->with('site')
->willReturn(true);

JFactory::$application = $mockApp;
JFactory::$document = $this->getMockDocument();
JFactory::$session = $this->getMockSession();

Expand All @@ -56,6 +66,13 @@ protected function setUp()

$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['SCRIPT_NAME'] = '';

$mockRouter = $this->getMockBuilder('Joomla\\CMS\\Router\\Router')->getMock();
$mockRouter->expects($this->any())
->method('build')
->willReturn(new \JUri);

TestReflection::setValue('JRoute', '_router', array('site' => $mockRouter));
}

/**
Expand All @@ -68,6 +85,8 @@ protected function setUp()
*/
protected function tearDown()
{
TestReflection::setValue('JRoute', '_router', array());

$_SERVER = $this->backupServer;
unset($this->backupServer);
$this->restoreFactoryState();
Expand Down
21 changes: 20 additions & 1 deletion tests/unit/suites/libraries/cms/html/JHtmlIconsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,24 @@ protected function setUp()
// We need to mock the application
$this->saveFactoryState();

JFactory::$application = $this->getMockCmsApp();
$mockApp = $this->getMockCmsApp();
$mockApp->expects($this->any())
->method('getName')
->willReturn('administrator');

$mockApp->expects($this->any())
->method('isClient')
->with('administrator')
->willReturn(true);

JFactory::$application = $mockApp;

$mockRouter = $this->getMockBuilder('Joomla\\CMS\\Router\\Router')->getMock();
$mockRouter->expects($this->any())
->method('build')
->willReturn(new \JUri);

TestReflection::setValue('JRoute', '_router', array('site' => $mockRouter));
}

/**
Expand All @@ -44,6 +61,8 @@ protected function setUp()
*/
protected function tearDown()
{
TestReflection::setValue('JRoute', '_router', array());

// Restore the factory state
$this->restoreFactoryState();

Expand Down

0 comments on commit de9d0ea

Please sign in to comment.