mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 02:28:44 +02:00
N°6208 - Router: Add mechanism to generate complete route URL from its code
This commit is contained in:
@@ -24,6 +24,7 @@ use Combodo\iTop\Application\UI\DisplayBlock\BlockChartAjaxBars\BlockChartAjaxBa
|
||||
use Combodo\iTop\Application\UI\DisplayBlock\BlockChartAjaxPie\BlockChartAjaxPie;
|
||||
use Combodo\iTop\Application\UI\DisplayBlock\BlockCsv\BlockCsv;
|
||||
use Combodo\iTop\Application\UI\DisplayBlock\BlockList\BlockList;
|
||||
use Combodo\iTop\Router\Router;
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
@@ -1722,6 +1723,7 @@ class MenuBlock extends DisplayBlock
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
|
||||
{
|
||||
$oRouter = Router::GetInstance();
|
||||
$oRenderBlock = new UIContentBlock();
|
||||
|
||||
if ($this->m_sStyle == 'popup') // popup is a synonym of 'list' for backward compatibility
|
||||
@@ -1893,7 +1895,7 @@ class MenuBlock extends DisplayBlock
|
||||
if ($bIsModifyAllowed) {
|
||||
$aRegularActions['UI:Menu:Modify'] = array(
|
||||
'label' => Dict::S('UI:Menu:Modify'),
|
||||
'url' => "{$sRootUrl}pages/$sUIPage?route=object.modify&class=$sClass&id=$id{$sContext}#",
|
||||
'url' => $oRouter->GenerateUrl('object.modify', ['class' => $sClass, 'id' => $id]) . "{$sContext}#",
|
||||
) + $aActionParams;
|
||||
}
|
||||
if ($bIsDeleteAllowed) {
|
||||
|
||||
@@ -605,6 +605,12 @@ class LogChannels
|
||||
public const NOTIFICATIONS = 'notifications';
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const ROUTER = 'router';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -438,6 +438,8 @@ return array(
|
||||
'Combodo\\iTop\\Renderer\\FieldRenderer' => $baseDir . '/sources/Renderer/FieldRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\FormRenderer' => $baseDir . '/sources/Renderer/FormRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\RenderingOutput' => $baseDir . '/sources/Renderer/RenderingOutput.php',
|
||||
'Combodo\\iTop\\Router\\Exception\\RouteNotFoundException' => $baseDir . '/sources/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Router\\Exception\\RouterException' => $baseDir . '/sources/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Router\\Router' => $baseDir . '/sources/Router/Router.php',
|
||||
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => $baseDir . '/sources/Service/Base/ObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => $baseDir . '/sources/Service/Base/iDataPostProcessor.php',
|
||||
|
||||
@@ -803,6 +803,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Renderer\\FieldRenderer' => __DIR__ . '/../..' . '/sources/Renderer/FieldRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\FormRenderer' => __DIR__ . '/../..' . '/sources/Renderer/FormRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\RenderingOutput' => __DIR__ . '/../..' . '/sources/Renderer/RenderingOutput.php',
|
||||
'Combodo\\iTop\\Router\\Exception\\RouteNotFoundException' => __DIR__ . '/../..' . '/sources/Router/Exception/RouteNotFoundException.php',
|
||||
'Combodo\\iTop\\Router\\Exception\\RouterException' => __DIR__ . '/../..' . '/sources/Router/Exception/RouterException.php',
|
||||
'Combodo\\iTop\\Router\\Router' => __DIR__ . '/../..' . '/sources/Router/Router.php',
|
||||
'Combodo\\iTop\\Service\\Base\\ObjectRepository' => __DIR__ . '/../..' . '/sources/Service/Base/ObjectRepository.php',
|
||||
'Combodo\\iTop\\Service\\Base\\iDataPostProcessor' => __DIR__ . '/../..' . '/sources/Service/Base/iDataPostProcessor.php',
|
||||
|
||||
@@ -11,6 +11,7 @@ use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\Popov
|
||||
use Combodo\iTop\Application\UI\Base\tUIContentAreas;
|
||||
use Combodo\iTop\Application\UI\Base\UIBlock;
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
use Combodo\iTop\Router\Router;
|
||||
use DBObject;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
@@ -99,6 +100,7 @@ class ObjectSummary extends ObjectDetails
|
||||
*/
|
||||
private function ComputeActions()
|
||||
{
|
||||
$oRouter = Router::GetInstance();
|
||||
$oDetailsButton = null;
|
||||
if(UserRights::IsActionAllowed($this->sClassName, UR_ACTION_MODIFY)) {
|
||||
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
@@ -111,7 +113,7 @@ class ObjectSummary extends ObjectDetails
|
||||
'_blank'
|
||||
);
|
||||
$oModifyButton = ButtonUIBlockFactory::MakeLinkNeutral(
|
||||
$sRootUrl.'pages/UI.php?route=object.modify&class='.$this->sClassName.'&id='.$this->sObjectId,
|
||||
$oRouter->GenerateUrl('object.modify', ['class' => $this->sClassName, 'id' => $this->sObjectId]),
|
||||
Dict::S('UI:Menu:Modify'),
|
||||
'fas fa-external-link-alt',
|
||||
'_blank',
|
||||
|
||||
@@ -18,6 +18,7 @@ use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreateHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
|
||||
use Combodo\iTop\Controller\AbstractController;
|
||||
use Combodo\iTop\Router\Router;
|
||||
use Combodo\iTop\Service\Base\ObjectRepository;
|
||||
use CoreCannotSaveObjectException;
|
||||
use DeleteException;
|
||||
@@ -62,6 +63,7 @@ class ObjectController extends AbstractController
|
||||
$sStateCode = utils::ReadParam('state', '');
|
||||
$bCheckSubClass = utils::ReadParam('checkSubclass', true);
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oRouter = Router::GetInstance();
|
||||
|
||||
if ($this->IsHandlingXmlHttpRequest()) {
|
||||
$oPage = new AjaxPage('');
|
||||
@@ -181,7 +183,7 @@ JS;
|
||||
} else {
|
||||
if ($this->IsHandlingXmlHttpRequest()) {
|
||||
$oClassForm = cmdbAbstractObject::DisplayFormBlockSelectClassToCreate($sClass, MetaModel::GetName($sClass), $oAppContext, $aPossibleClasses, ['state' => $sStateCode]);
|
||||
$sCurrentUrl = utils::GetAbsoluteUrlAppRoot().'/pages/UI.php?route=object.new';
|
||||
$sCurrentUrl = $oRouter->GenerateUrl('object.new');
|
||||
$oClassForm->SetOnSubmitJsCode(
|
||||
<<<JS
|
||||
let me = this;
|
||||
|
||||
@@ -11,6 +11,7 @@ use cmdbAbstractObject;
|
||||
use Combodo\iTop\Application\Helper\LegacyFormHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
|
||||
use Combodo\iTop\Controller\AbstractController;
|
||||
use Combodo\iTop\Router\Router;
|
||||
use Combodo\iTop\Service\Base\ObjectRepository;
|
||||
use Exception;
|
||||
use JsonPage;
|
||||
@@ -131,7 +132,8 @@ class LinkSetController extends AbstractController
|
||||
if (!$this->IsHandlingXmlHttpRequest()) {
|
||||
throw new CoreException('LinksetController can only be called in ajax.');
|
||||
}
|
||||
|
||||
|
||||
$oRouter = Router::GetInstance();
|
||||
$oPage = new AjaxPage('');
|
||||
|
||||
$sProposedRealClass = utils::ReadParam('class', '', false, 'class');
|
||||
@@ -231,7 +233,7 @@ JS
|
||||
'att_code' => $sAttCode,
|
||||
'host_class' => $sClass,
|
||||
'host_id' => $sId]);
|
||||
$sCurrentUrl = utils::GetAbsoluteUrlAppRoot().'/pages/UI.php?route=linkset.create_linked_object';
|
||||
$sCurrentUrl = $oRouter->GenerateUrl('linkset.create_linked_object');
|
||||
$oClassForm->SetOnSubmitJsCode(
|
||||
<<<JS
|
||||
let me = this;
|
||||
|
||||
22
sources/Router/Exception/RouteNotFoundException.php
Normal file
22
sources/Router/Exception/RouteNotFoundException.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Router\Exception;
|
||||
|
||||
/**
|
||||
* Class RouteNotFoundException
|
||||
*
|
||||
* Means that a said route (eg. "object.modify") could not be found
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @package Combodo\iTop\Router\Exception
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
class RouteNotFoundException extends RouterException
|
||||
{
|
||||
|
||||
}
|
||||
24
sources/Router/Exception/RouterException.php
Normal file
24
sources/Router/Exception/RouterException.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Router\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class RouterException
|
||||
*
|
||||
* Base router exception class in case we need to catch all kind of router exceptions (see derived exceptions)
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @package Combodo\iTop\Router\Exception
|
||||
* @since 3.1.0
|
||||
* @internal
|
||||
*/
|
||||
class RouterException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Combodo\iTop\Router;
|
||||
|
||||
use Combodo\iTop\Router\Exception\RouteNotFoundException;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use utils;
|
||||
@@ -89,6 +90,37 @@ class Router
|
||||
// Don't do anything, we don't want to be initialized
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sRoute Code of the route to generate the URL for (eg. "object.modify" => "https://itop/pages/UI.php?route=object.modify")
|
||||
* @param array $aParams Parameters to add to the URL query string, they will be URL-encoded automatically.
|
||||
* Note that only scalars and arrays are supported.
|
||||
* (eg. ["foo" => "bar", "some_array" => [1, 2, 3]] will be append to the URL as "&foo=bar&some_array[]=1&some_array[]=2&some_array[]=3")
|
||||
* @param bool $bAbsoluteUrl Whether the URL should be absolute (include the app root URL) or not
|
||||
*
|
||||
* @return string Absolute or relative URL to access $sRoute
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GenerateUrl(string $sRoute, array $aParams = [], bool $bAbsoluteUrl = true): string
|
||||
{
|
||||
// Stop if route cannot be found, it will ease DX and troubleshooting
|
||||
if (false === $this->CanDispatchRoute($sRoute)) {
|
||||
throw new RouteNotFoundException('Could not find route "'.$sRoute.'"');
|
||||
}
|
||||
|
||||
// Prepare base URL
|
||||
$sUrl = $bAbsoluteUrl ? utils::GetAbsoluteUrlAppRoot() : '';
|
||||
|
||||
// Add route URL
|
||||
$sUrl .= 'pages/UI.php?route=' . $sRoute;
|
||||
|
||||
// Add parameters and url encode them
|
||||
if (count($aParams) > 0) {
|
||||
$sUrl .= '&' . http_build_query($aParams);
|
||||
}
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sRoute
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use Combodo\iTop\Controller\AbstractController;
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
use Combodo\iTop\Router\Router;
|
||||
|
||||
/**
|
||||
* Class SummaryCardService
|
||||
@@ -25,7 +26,8 @@ class SummaryCardService {
|
||||
*/
|
||||
public static function GetHyperlinkMarkup(string $sObjClass, $sObjKey): string
|
||||
{
|
||||
$sRoute = utils::GetAbsoluteUrlAppRoot()."/pages/ajax.render.php?route=object.summary&obj_key=$sObjKey&obj_class=$sObjClass";
|
||||
$oRouter = Router::GetInstance();
|
||||
$sRoute = $oRouter->GenerateUrl("object.summary", ["obj_class" => $sObjClass, "obj_key" => $sObjKey]);
|
||||
return
|
||||
<<<HTML
|
||||
data-tooltip-content="$sRoute"
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\useCombodo\iTop\Router;
|
||||
|
||||
use Combodo\iTop\Router\Exception\RouteNotFoundException;
|
||||
use Combodo\iTop\Router\Router;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class RouterTest
|
||||
@@ -16,6 +20,101 @@ use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
*/
|
||||
class RouterTest extends ItopTestCase
|
||||
{
|
||||
/**
|
||||
* @covers \Combodo\iTop\Router\Router::GenerateUrl
|
||||
* @dataProvider GenerateUrlProvider
|
||||
*
|
||||
* @param string $sExpectedUrl URL contains a <APP_ROOT_URL> placeholder that will be replaced with the real app root url at run time
|
||||
* @param bool $bValid
|
||||
* @param string $sRoute
|
||||
* @param array $aParams
|
||||
* @param bool $bAbsoluteUrl
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testGenerateUrl(string $sExpectedUrl, bool $bValid, string $sRoute, array $aParams, bool $bAbsoluteUrl = true): void
|
||||
{
|
||||
$oRouter = Router::GetInstance();
|
||||
|
||||
if (false === $bValid) {
|
||||
$this->expectException(RouteNotFoundException::class);
|
||||
}
|
||||
$sTestedUrl = $oRouter->GenerateUrl($sRoute, $aParams, $bAbsoluteUrl);
|
||||
$sExpectedUrl = str_ireplace('<APP_ROOT_URL>', utils::GetAbsoluteUrlAppRoot(), $sExpectedUrl);
|
||||
|
||||
$this->assertEquals($sTestedUrl, $sExpectedUrl, 'Generated URL does not match');
|
||||
}
|
||||
|
||||
public function GenerateUrlProvider(): array
|
||||
{
|
||||
return [
|
||||
'invalid route' => [
|
||||
'',
|
||||
false,
|
||||
'foo.bar',
|
||||
[],
|
||||
true,
|
||||
],
|
||||
'relative route with no params' => [
|
||||
'pages/UI.php?route=object.modify',
|
||||
true,
|
||||
'object.modify',
|
||||
[],
|
||||
false,
|
||||
],
|
||||
'absolute route with no params' => [
|
||||
'<APP_ROOT_URL>pages/UI.php?route=object.modify',
|
||||
true,
|
||||
'object.modify',
|
||||
[],
|
||||
true,
|
||||
],
|
||||
'absolute route with scalar params' => [
|
||||
'<APP_ROOT_URL>pages/UI.php?route=object.modify&class=Person&id=123',
|
||||
true,
|
||||
'object.modify',
|
||||
[
|
||||
'class' => 'Person',
|
||||
'id' => 123
|
||||
],
|
||||
true,
|
||||
],
|
||||
'absolute route with 1 dimension array params' => [
|
||||
'<APP_ROOT_URL>pages/UI.php?route=object.modify&class=Person&id=123&default%5Bname%5D=Castor&default%5Bfirst_name%5D=P%C3%A8re',
|
||||
true,
|
||||
'object.modify',
|
||||
[
|
||||
'class' => 'Person',
|
||||
'id' => 123,
|
||||
'default' => [
|
||||
'name' => 'Castor',
|
||||
'first_name' => 'Père',
|
||||
],
|
||||
],
|
||||
true,
|
||||
],
|
||||
'absolute route with 2 dimensions array params' => [
|
||||
'<APP_ROOT_URL>pages/UI.php?route=object.modify&class=Person&id=123&default%5Bname%5D=Castor&default%5Bfirst_name%5D=P%C3%A8re&foo%5Bfirst%5D%5B0%5D=10&foo%5Bfirst%5D%5B1%5D=20&foo%5Bsecond%5D%5B0%5D=30&foo%5Bsecond%5D%5B1%5D=40',
|
||||
true,
|
||||
'object.modify',
|
||||
[
|
||||
'class' => 'Person',
|
||||
'id' => 123,
|
||||
'default' => [
|
||||
'name' => 'Castor',
|
||||
'first_name' => 'Père',
|
||||
],
|
||||
'foo' => [
|
||||
'first' => ['10', '20'],
|
||||
'second' => ['30', '40'],
|
||||
],
|
||||
],
|
||||
true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider CanDispatchRouteProvider
|
||||
* @covers \Combodo\iTop\Router\Router::CanDispatchRoute
|
||||
|
||||
Reference in New Issue
Block a user