Compare commits

...

9 Commits

Author SHA1 Message Date
Molkobain
94fb23d366 N°6935 - Migrate "object.xxx" operations / routes to Symfony routes 2024-03-01 16:47:37 +01:00
Molkobain
4e4b6c1ec2 N°6935 - Migrate "object.xxx" operations / routes to Symfony routes 2024-03-01 16:47:37 +01:00
Molkobain
451bab6957 N°6935 - Migrate relative images URLs to absolute ones in order to support pages on different depth levels 2024-03-01 16:47:36 +01:00
Molkobain
85003e89eb N°6935 - Migrate usages of WebPage::add_linked_script() to absolute URIs to ensure they are correctly loaded no matter the endpoint location 2024-03-01 16:47:32 +01:00
Molkobain
5fb5f093e7 N°6935 - Add Symfony Response alternative to Webpage::output() 2024-03-01 16:45:39 +01:00
Molkobain
4c48b65e6b N°6935 - Remove danger zone from iTopWebPage as it was only kept to ensure we didn't a miss in 3.0.0 2024-03-01 16:45:39 +01:00
Molkobain
43d3a3bc67 N°6935 - Deprecate own router system in favor of Symfony's 2024-03-01 16:45:39 +01:00
Molkobain
1fee4372eb N°6648 - Use \utils::GetDataPath() instead of hard-coded paths (#555)
* N°6648 - Use \utils::GetDataPath() instead of hard-coded paths

* Update setup/applicationinstaller.class.inc.php

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-03-01 16:45:39 +01:00
Pierre Goiffon
a4434afa0d 📝 Version history: add 2.7.10 and 3.0.4 2024-03-01 16:45:38 +01:00
60 changed files with 718 additions and 406 deletions

View File

@@ -79,6 +79,10 @@ gitGraph
commit id: "2023-08-10" tag: "2.7.9"
checkout support/3.1
commit id: "2023-12-20" tag: "3.1.1"
checkout support/2.7
commit id: "2024-01-17a" tag: "2.7.10"
checkout support/3.0
commit id: "2024-01-17b" tag: "3.0.4"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

View File

@@ -563,8 +563,8 @@ JS
}
if (!$bEditMode) {
$oPage->add_linked_script('../js/dashlet.js');
$oPage->add_linked_script('../js/dashboard.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/dashlet.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/dashboard.js');
}
return $oDashboard;

View File

@@ -326,6 +326,7 @@ class DataTable
$sPagesLinks = implode('', $aPagesToDisplay);
$sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']';
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
$sHtml =
<<<EOF
@@ -333,11 +334,11 @@ class DataTable
<div $sPagerStyle>
<table id="pager{$this->iListId}" class="pager"><tr>
<td>$sPages</td>
<td><img src="../images/first.png" class="first"/></td>
<td><img src="../images/prev.png" class="prev"/></td>
<td><img src="{$sAppRootUrl}images/first.png" class="first"/>AAAA</td>
<td><img src="{$sAppRootUrl}images/prev.png" class="prev"/></td>
<td><span id="index">$sPagesLinks</span></td>
<td><img src="../images/next.png" class="next"/></td>
<td><img src="../images/last.png" class="last"/></td>
<td><img src="{$sAppRootUrl}images/next.png" class="next"/></td>
<td><img src="{$sAppRootUrl}images/last.png" class="last"/></td>
<td>$sPageSizeCombo</td>
<td><span id="loading">&nbsp;</span><input type="hidden" name="selectionMode" value="$sSelectionMode"></input>
</td>

View File

@@ -26,7 +26,6 @@ use Combodo\iTop\Application\UI\DisplayBlock\BlockCsv\BlockCsv;
use Combodo\iTop\Application\UI\DisplayBlock\BlockList\BlockList;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Service\Router\Router;
require_once(APPROOT.'/application/utils.inc.php');
@@ -570,7 +569,7 @@ class DisplayBlock
} else {
// render it as an Ajax (asynchronous) call
$oHtml->AddCSSClass("loading");
$oHtml->AddHtml("<p><img src=\"../images/indicator_arrows.gif\"> ".Dict::S('UI:Loading').'</p>');
$oHtml->AddHtml("<p><img src=\"".utils::GetAbsoluteUrlAppRoot()."images/indicator_arrows.gif\"> ".Dict::S('UI:Loading').'</p>');
$oPage->add_script('
$.post("ajax.render.php?style='.$this->m_sStyle.'",
{ operation: "ajax", filter: "'.$sFilter.'", extra_params: "'.$sExtraParams.'" },
@@ -1848,7 +1847,6 @@ 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
@@ -2068,7 +2066,8 @@ class MenuBlock extends DisplayBlock
if ($bIsModifyAllowed) {
$aRegularActions['UI:Menu:Modify'] = array(
'label' => Dict::S('UI:Menu:Modify'),
'url' => $oRouter->GenerateUrl('object.modify', ['class' => $sClass, 'id' => $id]) . "{$sContext}#",
// Can't use URL Generator (`$this->oUrlGenerator->generate("b_object_summary", [$sObjClass, $sObjKey])`) yet as we have to find how to inject it here
'url' => "{$sRootUrl}app.php/object/modify/{$sClass}/{$id}{$sContext}#",
) + $aActionParams;
}
if ($bIsDeleteAllowed) {

View File

@@ -303,7 +303,7 @@ class ExcelExporter
{
if ($this->sOutputFilePath == null)
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.xlsx';
}
else
{
@@ -313,14 +313,14 @@ class ExcelExporter
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
return @file_get_contents(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.status');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.data');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
@@ -334,7 +334,7 @@ class ExcelExporter
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$aFiles = glob(utils::GetDataPath().'bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
@@ -416,14 +416,14 @@ class ExcelExporter
protected function CheckDataDir()
{
if(!is_dir(APPROOT."data/bulk_export"))
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
}
@@ -433,12 +433,12 @@ class ExcelExporter
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
return utils::GetDataPath()."bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()

View File

@@ -232,12 +232,13 @@ class DisplayTemplate
static public function UnitTest()
{
require_once(APPROOT.'/application/startup.inc.php');
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
$sTemplate = '<div class="page_header">
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
<h1>$class$: <span class="hilite">$name$</span></h1>
</div>
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
<img src="'.$sAppRootUrl.'images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
<itoptabs>
<itoptab name="Interfaces">
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Interface AS i WHERE i.device_id = $id$</itopblock>
@@ -350,7 +351,8 @@ class ObjectDetailsTemplate extends DisplayTemplate
if ($iFlags & OPT_ATT_SLAVE)
{
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = "&nbsp;<img id=\"synchro_$iInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sAppRooturl = utils::GetAbsoluteUrlAppRoot();
$sSynchroIcon = "&nbsp;<img id=\"synchro_$iInputId\" src=\"{$sAppRooturl}images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{

View File

@@ -228,7 +228,7 @@ class privUITransactionFile
*/
public static function GetNewTransactionId()
{
if (!is_dir(APPROOT.'data/transactions'))
if (!is_dir(utils::GetDataPath().'transactions'))
{
if (!is_writable(APPROOT.'data'))
{
@@ -236,22 +236,22 @@ class privUITransactionFile
}
// condition avoids race condition N°2345
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
if (!mkdir($concurrentDirectory = APPROOT.'data/transactions') && !is_dir($concurrentDirectory))
if (!mkdir($concurrentDirectory = utils::GetDataPath().'transactions') && !is_dir($concurrentDirectory))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
throw new Exception('Failed to create the directory "'.utils::GetDataPath().'transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
if (!is_writable(utils::GetDataPath().'transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
throw new Exception('The directory "'.utils::GetDataPath().'transactions" must be writable to the application.');
}
$iCurrentUserId = static::GetCurrentUserId();
self::CleanupOldTransactions();
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
$sTransactionIdFullPath = tempnam(utils::GetDataPath().'transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
@@ -274,8 +274,8 @@ class privUITransactionFile
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
// Constraint the transaction file within APPROOT.'data/transactions'
$sTransactionDir = realpath(APPROOT.'data/transactions');
// Constraint the transaction file within utils::GetDataPath().'transactions'
$sTransactionDir = realpath(utils::GetDataPath().'transactions');
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
{
@@ -348,7 +348,7 @@ class privUITransactionFile
clearstatcache();
$iLimit = time() - 24*3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : APPROOT.'data/transactions/*';
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : utils::GetDataPath().'transactions/*';
$aTransactions = glob($sPattern);
foreach($aTransactions as $sFileName)
{
@@ -368,7 +368,7 @@ class privUITransactionFile
{
clearstatcache();
$aResult = array();
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
$aTransactions = glob(utils::GetDataPath().'transactions/'.self::GetUserPrefix().'*');
foreach($aTransactions as $sFileName)
{
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);

View File

@@ -161,8 +161,8 @@ class UIExtKeyWidget
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), &$sInputType = '')
{
$sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/extkeywidget.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
@@ -368,7 +368,7 @@ JS
*/
public function DisplayRadio(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, DBObjectset $oAllowedValues, $value, $sFieldName, $sDisplayStyle)
{
$oPage->add_linked_script('../js/forms-json-utils.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
@@ -477,8 +477,8 @@ JS
$this->bSearchMode = $bSearchMode;
}
$sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/extkeywidget.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;

View File

@@ -1446,7 +1446,7 @@ EOF
<<<EOF
function OnTruncatedHistoryToggle(bShowAll)
{
$('#csv_history_reload').html('<img src="../images/indicator.gif"/>');
$('#csv_history_reload').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif"/>');
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
{
$('#$sAjaxDivId').html(data);

View File

@@ -474,14 +474,14 @@ abstract class BulkExport
*/
protected function MakeTmpFile($sExtension)
{
if(!is_dir(APPROOT."data/bulk_export"))
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
$iNum = rand();
@@ -489,7 +489,7 @@ abstract class BulkExport
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
$sFileName = utils::GetDataPath()."bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);

View File

@@ -1552,13 +1552,13 @@ abstract class DBSearch
}
$sLogFile = 'queries.latest';
file_put_contents(APPROOT.'data/'.$sLogFile.'.html', $sHtml);
file_put_contents(utils::GetDataPath().$sLogFile.'.html', $sHtml);
$sLog = "<?php\n\$aQueriesLog = ".var_export(self::$m_aQueriesLog, true).";";
file_put_contents(APPROOT.'data/'.$sLogFile.'.log', $sLog);
file_put_contents(utils::GetDataPath().$sLogFile.'.log', $sLog);
// Cumulate the queries
$sAllQueries = APPROOT.'data/queries.log';
$sAllQueries = utils::GetDataPath().'queries.log';
if (file_exists($sAllQueries))
{
// Merge the new queries into the existing log

View File

@@ -217,7 +217,7 @@ class ormStopWatch
}
else
{
$aProperties['Elapsed'] = 'running <img src="../images/indicator.gif">';
$aProperties['Elapsed'] = 'running <img src="' . utils::GetAbsoluteUrlAppRoot() . 'images/indicator.gif">';
}
$aProperties['Started'] = $oAttDef->SecondsToDate($this->iStarted);

View File

@@ -500,17 +500,17 @@ EOF
if (file_exists($sDotExecutable))
{
// create the file with Graphviz
if (!is_dir(APPROOT."data"))
if (!is_dir(utils::GetDataPath()))
{
@mkdir(APPROOT."data");
@mkdir(utils::GetDataPath());
}
if (!is_dir(APPROOT."data/tmp"))
if (!is_dir(utils::GetDataPath()."tmp"))
{
@mkdir(APPROOT."data/tmp");
@mkdir(utils::GetDataPath()."tmp");
}
$sImageFilePath = tempnam(APPROOT."data/tmp", 'png-');
$sImageFilePath = tempnam(utils::GetDataPath()."tmp", 'png-');
$sDotDescription = $this->GetDotDescription();
$sDotFilePath = tempnam(APPROOT."data/tmp", 'dot-');
$sDotFilePath = tempnam(utils::GetDataPath()."tmp", 'dot-');
$rFile = @fopen($sDotFilePath, "w");
@fwrite($rFile, $sDotDescription);
@@ -556,17 +556,17 @@ EOF
if (file_exists($sDotExecutable))
{
// create the file with Graphviz
if (!is_dir(APPROOT."data"))
if (!is_dir(utils::GetDataPath()))
{
@mkdir(APPROOT."data");
@mkdir(utils::GetDataPath());
}
if (!is_dir(APPROOT."data/tmp"))
if (!is_dir(utils::GetDataPath()."tmp"))
{
@mkdir(APPROOT."data/tmp");
@mkdir(utils::GetDataPath()."tmp");
}
$sXdotFilePath = tempnam(APPROOT."data/tmp", 'xdot-');
$sXdotFilePath = tempnam(utils::GetDataPath()."tmp", 'xdot-');
$sDotDescription = $this->GetDotDescription(true); // true => don't put (localized) labels in the file, since it makes it harder to parse
$sDotFilePath = tempnam(APPROOT."data/tmp", 'dot-');
$sDotFilePath = tempnam(utils::GetDataPath()."tmp", 'dot-');
$rFile = @fopen($sDotFilePath, "w");
@fwrite($rFile, $sDotDescription);

View File

@@ -185,6 +185,7 @@ abstract class AbstractAttachmentsRenderer
{
$sClass = $this->sObjClass;
$sId = $this->iObjKey;
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
$iMaxUploadInBytes = AttachmentPlugIn::GetMaxUploadSize();
$sMaxUploadLabel = AttachmentPlugIn::GetMaxUpload();
$sFileTooBigLabel = Dict::Format('Attachments:Error:FileTooLarge', $sMaxUploadLabel);
@@ -195,7 +196,7 @@ abstract class AbstractAttachmentsRenderer
$oAddButton = FileSelectUIBlockFactory::MakeStandard('file', 'file');
$oAddButton->SetShowFilename(false);
$this->oPage->AddUiBlock($oAddButton);
$this->oPage->add('<span style="display:none;" id="attachment_loading"><img src="../images/indicator.gif"></span> '.$sMaxUploadLabel);
$this->oPage->add('<span style="display:none;" id="attachment_loading"><img src="' . $sAppRootUrl . 'images/indicator.gif"></span> ' . $sMaxUploadLabel);
$this->oPage->add('</div>');
$this->oPage->add('<div class="ibo-attachment--upload-file--drop-zone-hint ibo-svg-illustration--container">');
$this->oPage->add(file_get_contents(APPROOT.'images/illustrations/undraw_upload.svg'));
@@ -342,7 +343,7 @@ abstract class AbstractAttachmentsRenderer
$('.attachment a[href="'+sSrc+'"]').parent().addClass('image-in-use').find('img').wrap('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
});
});
$('.image-in-use-wrapper').append('<div style="position:absolute;top:0;left:0;"><img src="../images/transp-lock.png"></div>');
$('.image-in-use-wrapper').append('<div style="position:absolute;top:0;left:0;"><img src="' + GetAbsoluteUrlModulesRoot() + 'images/transp-lock.png"></div>');
}, 200 );
JS
);

View File

@@ -120,7 +120,7 @@ try
try
{
set_time_limit(0);
$oBB = new BackupExec(APPROOT.'data/backups/manual/', 0 /*iRetentionCount*/);
$oBB = new BackupExec(utils::GetDataPath().'backups/manual/', 0 /*iRetentionCount*/);
$sRes = $oBB->Process(time() + 36000); // 10 hours to complete should be sufficient!
}
catch (Exception $e)
@@ -185,7 +185,7 @@ try
$oDBRS = new DBRestore($oItopConfig);
$oDBRS->SetMySQLBinDir($sMySQLBinDir);
$sBackupDir = APPROOT.'data/backups/';
$sBackupDir = utils::GetDataPath().'backups/';
$sBackupFile = $sBackupDir.$sFile;
$sRes = $oDBRS->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
@@ -210,7 +210,7 @@ try
}
$sFile = utils::ReadParam('file', '', false, 'raw_data');
$oBackup = new DBBackupScheduled();
$sBackupDir = APPROOT.'data/backups/';
$sBackupDir = utils::GetDataPath().'backups/';
$sBackupFilePath = utils::RealPath($sBackupDir.$sFile, $sBackupDir);
if ($sBackupFilePath === false)
{

View File

@@ -154,7 +154,7 @@ class DBRestore extends DBBackup
// Load the database
//
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
$sDataDir = utils::GetDataPath().'tmp-backup-'.rand(10000, getrandmax());
SetupUtils::builddir($sDataDir); // Here is the directory
$oArchive->extractTo($sDataDir);
@@ -164,7 +164,7 @@ class DBRestore extends DBBackup
// Update the code
//
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
$sDeltaFile = utils::GetDataPath().$sEnvironment.'.delta.xml';
if (is_file($sDataDir.'/delta.xml')) {
// Extract and rename delta.xml => <env>.delta.xml;
@@ -172,15 +172,15 @@ class DBRestore extends DBBackup
} else {
@unlink($sDeltaFile);
}
if (is_dir(APPROOT.'data/production-modules/')) {
if (is_dir(utils::GetDataPath().'production-modules/')) {
try {
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
SetupUtils::rrmdir(utils::GetDataPath().'production-modules/');
} catch (Exception $e) {
throw new BackupException("Can't remove production-modules dir", 0, $e);
}
}
if (is_dir($sDataDir.'/production-modules')) {
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
rename($sDataDir.'/production-modules', utils::GetDataPath().'production-modules/');
}
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';

View File

@@ -109,7 +109,7 @@ class BackupExec extends AbstractWeeklyScheduledProcess
{
if (is_null($sBackupDir))
{
$this->sBackupDir = APPROOT.'data/backups/auto/';
$this->sBackupDir = utils::GetDataPath().'backups/auto/';
}
else
{

View File

@@ -131,7 +131,7 @@ try {
// Destination directory
//
// Make sure the target directory exists and is writeable
$sBackupDir = realpath(APPROOT.'data/backups/');
$sBackupDir = realpath(utils::GetDataPath().'backups/');
SetupUtils::builddir($sBackupDir);
if (!is_dir($sBackupDir)) {
$oBlockForChecks->AddSubBlock(

View File

@@ -486,7 +486,7 @@ final class CoreUpdater
*/
private static function GetItopArchiveName()
{
$sItopArchiveName = APPROOT.'data/backups/itop';
$sItopArchiveName = utils::GetDataPath().'backups/itop';
return $sItopArchiveName;
}
@@ -504,7 +504,7 @@ final class CoreUpdater
*/
private static function GetBackupName()
{
$sBackupName = APPROOT.'data/backups/manual/backup-core-update';
$sBackupName = utils::GetDataPath().'backups/manual/backup-core-update';
return $sBackupName;
}

View File

@@ -185,7 +185,7 @@ try
require_once (APPROOT.'/application/loginwebpage.class.inc.php');
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin)
$sDBBackupPath = APPROOT.'data/backups/manual';
$sDBBackupPath = utils::GetDataPath().'backups/manual';
$aChecks = SetupUtils::CheckBackupPrerequisites($sDBBackupPath);
$bFailed = false;
foreach ($aChecks as $oCheckResult)
@@ -258,7 +258,7 @@ try
case 'compile':
SetupLog::Info('Deployment starts...');
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
if (!file_exists(APPROOT.'data/hub/compile_authent') || $sAuthent !== file_get_contents(APPROOT.'data/hub/compile_authent'))
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent'))
{
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
@@ -301,11 +301,11 @@ try
{
SetupLog::Info('Move to production starts...');
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
if (!file_exists(APPROOT.'data/hub/compile_authent') || $sAuthent !== file_get_contents(APPROOT.'data/hub/compile_authent'))
if (!file_exists(utils::GetDataPath().'hub/compile_authent') || $sAuthent !== file_get_contents(utils::GetDataPath().'hub/compile_authent'))
{
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
unlink(APPROOT.'data/hub/compile_authent');
unlink(utils::GetDataPath().'hub/compile_authent');
// Load the "production" config file to clone & update it
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);
SetupUtils::EnterReadOnlyMode($oConfig);
@@ -370,9 +370,9 @@ try
}
catch (Exception $e)
{
if(file_exists(APPROOT.'data/hub/compile_authent'))
if(file_exists(utils::GetDataPath().'hub/compile_authent'))
{
unlink(APPROOT.'data/hub/compile_authent');
unlink(utils::GetDataPath().'hub/compile_authent');
}
// Note: at this point, the dictionnary is not necessarily loaded
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());

View File

@@ -78,7 +78,7 @@ class HubNewsroomProvider extends NewsroomProviderBase
$sBaseUrl = $this->oConfig->GetModuleSetting('itop-hub-connector', 'url').MetaModel::GetModuleSetting('itop-hub-connector', $sRouteCode);
$sParameters = 'uuid[bdd]='.urlencode((string) trim(DBProperty::GetProperty('database_uuid', ''), '{}'));
$sParameters .= '&uuid[file]='.urlencode((string) trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n"));
$sParameters .= '&uuid[file]='.urlencode((string) trim(@file_get_contents(utils::GetDataPath()."instance.txt"), "{} \n"));
$sParameters .= '&uuid[user]='.urlencode(UserRights::GetUserId());
return $sBaseUrl.'?'.$sParameters;

View File

@@ -15,7 +15,7 @@ function DisplayStatus(WebPage $oPage)
$oPage->add('<div class="module-selection-body">');
// Now scan the extensions and display a report of the extensions brought by the hub
$sPath = APPROOT.'data/downloaded-extensions/';
$sPath = utils::GetDataPath().'downloaded-extensions/';
$aExtraDirs = array();
if (is_dir($sPath)) {
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
@@ -84,7 +84,7 @@ function DoLanding(WebPage $oPage)
throw new Exception("Inconsistent version '$sVersion', expecting ".ITOP_VERSION."'");
}
$sFileUUID = (string)trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n");
$sFileUUID = (string)trim(@file_get_contents(utils::GetDataPath()."instance.txt"), "{} \n");
if ($sInstanceUUID != $sFileUUID) {
throw new Exception("Inconsistent file UUID '$sInstanceUUID', expecting ".$sFileUUID."'");
}
@@ -97,7 +97,7 @@ function DoLanding(WebPage $oPage)
// Uncompression of extensions in data/downloaded-extensions
// only newly downloaded extensions reside in this folder
$i = 0;
$sPath = APPROOT.'data/downloaded-extensions/';
$sPath = utils::GetDataPath().'downloaded-extensions/';
if (!is_dir($sPath)) {
if (!mkdir($sPath)) {
throw new Exception("ERROR: Unable to create the directory '$sPath'. Cannot download any extension. Check the access rights on '".dirname('data/downloaded-extensions/')."'");
@@ -112,7 +112,7 @@ function DoLanding(WebPage $oPage)
$sZipArchiveFile = $sPath."/extension-{$i}.zip";
file_put_contents($sZipArchiveFile, $sArchive);
// Expand the content of extension-x.zip into APPROOT.'data/downloaded-extensions/'
// Expand the content of extension-x.zip into utils::GetDataPath().'downloaded-extensions/'
// where the installation will load the extension automatically
$oZip = new ZipArchive();
if (!$oZip->open($sZipArchiveFile)) {
@@ -136,7 +136,7 @@ function DoLanding(WebPage $oPage)
function DoInstall(WebPage $oPage)
{
$sUID = hash('sha256', rand());
file_put_contents(APPROOT.'data/hub/compile_authent', $sUID);
file_put_contents(utils::GetDataPath().'hub/compile_authent', $sUID);
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/css/hub.css');
$oPage->add('<table class="module-selection-banner"><tr>');
@@ -151,7 +151,7 @@ function DoInstall(WebPage $oPage)
// Now scan the extensions and display a report of the extensions brought by the hub
// Now scan the extensions and display a report of the extensions brought by the hub
$sPath = APPROOT.'data/downloaded-extensions/';
$sPath = utils::GetDataPath().'downloaded-extensions/';
$aExtraDirs = array();
if (is_dir($sPath)) {
$aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory
@@ -283,8 +283,8 @@ CSS
break;
case 'install':
if (!file_exists(APPROOT.'data/hub')) {
mkdir(APPROOT.'data/hub');
if (!file_exists(utils::GetDataPath().'hub')) {
mkdir(utils::GetDataPath().'hub');
}
DoInstall($oPage);
break;

View File

@@ -231,7 +231,7 @@ function MakeDataToPost($sTargetRoute)
'itop_hub_target_route' => $sTargetRoute,
'itop_stack' => array(
"uuidBdd" => (string)trim(DBProperty::GetProperty('database_uuid', ''), '{}'), // TODO check if empty and... regenerate a new UUID ??
"uuidFile" => (string)trim(@file_get_contents(APPROOT."data/instance.txt"), "{} \n"), // TODO check if empty and... regenerate a new UUID ??
"uuidFile" => (string)trim(@file_get_contents(utils::GetDataPath()."instance.txt"), "{} \n"), // TODO check if empty and... regenerate a new UUID ??
'instance_friendly_name' => (string)$_SERVER['SERVER_NAME'],
'instance_host' => (string)utils::GetAbsoluteUrlAppRoot(),
'application_name' => (string)ITOP_APPLICATION,

View File

@@ -389,7 +389,6 @@ return array(
'Combodo\\iTop\\Application\\WebPage\\iTopWebPage' => $baseDir . '/sources/Application/WebPage/iTopWebPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWizardWebPage' => $baseDir . '/sources/Application/WebPage/iTopWizardWebPage.php',
'Combodo\\iTop\\Composer\\iTopComposer' => $baseDir . '/sources/Composer/iTopComposer.php',
'Combodo\\iTop\\Controller\\AbstractAppController' => $baseDir . '/sources/Controller/AbstractAppController.php',
'Combodo\\iTop\\Controller\\AbstractController' => $baseDir . '/sources/Controller/AbstractController.php',
'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php',

View File

@@ -764,7 +764,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\WebPage\\iTopWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWebPage.php',
'Combodo\\iTop\\Application\\WebPage\\iTopWizardWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWizardWebPage.php',
'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php',
'Combodo\\iTop\\Controller\\AbstractAppController' => __DIR__ . '/../..' . '/sources/Controller/AbstractAppController.php',
'Combodo\\iTop\\Controller\\AbstractController' => __DIR__ . '/../..' . '/sources/Controller/AbstractController.php',
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',

View File

@@ -6,14 +6,12 @@
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchHelper;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
@@ -23,7 +21,9 @@ use Combodo\iTop\Application\WebPage\ErrorPage;
use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Controller\Base\Layout\ObjectController;
use Combodo\iTop\Kernel;
use Combodo\iTop\Service\Router\Router;
use Symfony\Component\HttpFoundation\Request;
/**
* Displays a popup welcome message, once per session at maximum
@@ -319,6 +319,7 @@ try
$oKPI->ComputeAndReport('User login');
// First check if we can redirect the route to a dedicated controller
// IMPORTANT: Even though iTop router is deprecated, we must ensure old URLs keep working (for a while) as they can be used in received notifications, bookmarks, extensions, ...
$sRoute = utils::ReadParam('route', '', false, utils::ENUM_SANITIZATION_FILTER_ROUTE);
$oRouter = Router::GetInstance();
if ($oRouter->CanDispatchRoute($sRoute)) {
@@ -341,7 +342,131 @@ try
$oP = new iTopWebPage(Dict::S('UI:WelcomeToITop'), $bPrintable);
$oP->SetMessage($sLoginMessage);
// All the following actions use advanced forms that require more javascript to be loaded
//-----------------------------------------------------------------------------------------------------------------
//
// Mind that the following backward compatibility pattern is also present in the `pages/ajax.render.php` endpoint
//
//-----------------------------------------------------------------------------------------------------------------
//
// Kept for backward compatibility with iTop 3.1.x notification URLs, don't remove
// Non-dispatchable "routes" (iTop 3.1) are mapped to their old corresponding "operation" (iTop 3.0 and older), in order to be later redirected to the proper new URLs (iTop 3.2 and newer)
switch ($sRoute) {
case 'object.new':
case 'object.apply_new':
case 'object.modify':
case 'object.apply_modify':
$operation = str_ireplace("object.", "", $sRoute);
break;
}
if (utils::IsNotNullOrEmptyString($sRoute)) {
DeprecatedCallsLog::Debug("Calling UI.php with a deprecated \"route\" parameter that was migrated to Symfony router, redirecting to fallback \"operation\" parameter. If URI comes from a custom code, migrate it. If URI comes from an old notification, mind that it may not work in future versions.", LogChannels::ROUTER, [
"request_uri" => $_SERVER["REQUEST_URI"],
"route" => $sRoute,
"fallback_operation" => $operation,
]);
}
// Kept for backward compatibility with iTop 3.0 and older notification URLs, don't remove
// These operations have been migrated to Symfony router, we need to redirect to the proper new URLs
switch ($operation) {
/** @deprecated 3.1.0 Use the "b_object_new" Symfony route instead */
case 'new':
/** @deprecated 3.1.0 Use the "b_object_apply_new" Symfony route instead */
case 'apply_new':
/** @deprecated 3.1.0 Use the "b_object_modify" Symfony route instead */
case 'modify':
/** @deprecated 3.1.0 Use the "b_object_apply_new" Symfony route instead */
case 'apply_modify':
// Start a Symfony kernel and redirect to the corresponding route / controller
$oKernel = new Kernel("prod", false);
$oRequest = Request::createFromGlobals();
/** @var string $sRouteName Route name to redirect to */
$sRouteName = '';
/** @var string[] $aPath Parts of the Symfony route path (eg. for "/object/{sCLass}/{sId}", ["sClass" => "UserRequest", "sId" => "123"]) */
$aPath = [];
/** @var string[] $aQuery Query parameters of the request -parameters after the "?" of the URI- (eg. for "/object/UserRequest/123?a=1&b=2", ["a" => 1, "b" => 2]) */
$aQuery = $oRequest->query->all();
// Find controller corresponding to the old "operation"
switch ($operation) {
case 'new':
$sRouteName = 'b_object_new';
$aPath['_controller'] = ObjectController::class . "::OperationNew";
$aPath['sClass'] = utils::ReadParam('class', null, false, utils::ENUM_SANITIZATION_FILTER_CLASS);
break;
case 'apply_new':
$sRouteName = 'b_object_apply_new';
$aPath['_controller'] = ObjectController::class . "::OperationApplyNew";
break;
case 'modify':
$sRouteName = 'b_object_modify';
$aPath['_controller'] = ObjectController::class . "::OperationModify";
$aPath['sClass'] = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
$aPath['sId'] = utils::ReadParam('id', '');
break;
case 'apply_modify':
$sRouteName = 'b_object_apply_modify';
$aPath['_controller'] = ObjectController::class . "::OperationApplyModify";
break;
}
// Redirect
///** @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface $oUrlGenerator */
//$oResponse = $oKernel->handle($oRequest);
//$oUrlGenerator = $oKernel->getContainer()->get('router');
// - Solution 1: Simplement redirection
// Pros:
// - Redirects to the new URL
// - Keeps query parameters
// - Uses the real Symfony kernel as it will be a complete new process
// Cons:
// - Loose posted parameters if any (but could be ok as we essentially do this for notifications / bookmarks); extensions will be migrated
// - Makes an HTTP redirection, could be an issue in some infras? I don't think so, we already do it in several places
/*$sRedirectUrl = $oUrlGenerator->generate($sRouteName, $aPath + $aQuery);
// Generated URL is based on the script the Kernel is instantiated in, so we have to correct it to point to the new unique endpoint
$sRedirectUrl = str_ireplace("pages/UI.php", "app.php", $sRedirectUrl);
header("location: $sRedirectUrl");
die();*/
// - Solution 2: Symfony redirection
// Pros:
// - Redirects to the new URL
// - Keeps query parameters
// - Uses the real Symfony kernel as it will be a complete new process
// - Keeps redirection working even though there are deprecated messages printed before headers are send (unlike with solution 1)
// Cons:
// - Loose posted parameters if any (but could be ok as we essentially do this for notifications / bookmarks); extensions will be migrated
// - Makes an HTTP redirection, could be an issue in some infras? I don't think so, we already do it in several places
/*$sRedirectUrl = $oUrlGenerator->generate($sRouteName, $aPath + $aQuery);
// Generated URL is based on the script the Kernel is instantiated in, so we have to correct it to point to the new unique endpoint
$sRedirectUrl = str_ireplace("pages/UI.php", "app.php", $sRedirectUrl);
$oResponse = new RedirectResponse($sRedirectUrl);
$oResponse->send();
$oKernel->terminate($oRequest, $oResponse);
die();*/
// - Solution 3: Internal redirection
// Pros:
// - Keeps query parameters
// - Keeps posted parameters (essential for submission URLs)
// Cons:
// - Still displays the old URL
// - Uses a not so well-formed Symfony kernel?
$oSubRequest = $oRequest->duplicate($aQuery, null, $aPath);
$oResponse = $oKernel->handle($oSubRequest, \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST);
$oResponse->send();
$oKernel->terminate($oSubRequest, $oResponse);
die();
}
// All the following operations use advanced forms that require more javascript to be loaded
switch($operation)
{
case 'new': // Form to create a new object
@@ -350,7 +475,7 @@ try
case 'stimulus': // Form displayed when applying a stimulus (state change)
case 'apply_stimulus': // Form displayed when applying a stimulus (state change)
foreach (ObjectController::EnumRequiredForModificationJsFilesRelPaths() as $sJsFileRelPath) {
$oP->add_linked_script("../$sJsFileRelPath");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "$sJsFileRelPath");
}
break;
}
@@ -651,7 +776,7 @@ try
$oP->SetBreadCrumbEntry($sPageId, $sLabel, $sDescription, '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
$oP->add("<div style=\"padding: 10px;\">\n");
$oP->add("<div class=\"header_message\" id=\"full_text_progress\" style=\"position: fixed; background-color: #cccccc; opacity: 0.7; padding: 1.5em;\">\n");
$oP->add('<img id="full_text_indicator" src="../images/indicator.gif">&nbsp;<span style="padding: 1.5em;">'.Dict::Format('UI:Search:Ongoing', utils::EscapeHtml($sFullText)).'</span>');
$oP->add('<img id="full_text_indicator" src="' . utils::GetAbsoluteUrlAppRoot() . 'images/indicator.gif">&nbsp;<span style="padding: 1.5em;">'.Dict::Format('UI:Search:Ongoing', utils::EscapeHtml($sFullText)).'</span>');
$oP->add("</div>\n");
$oP->add("<div id=\"full_text_results\">\n");
$oP->add("<div id=\"full_text_progress_placeholder\" style=\"padding: 1.5em;\">&nbsp;</div>\n");
@@ -678,15 +803,6 @@ try
///////////////////////////////////////////////////////////////////////////////////////////
/** @deprecated 3.1.0 Use the "object.modify" route instead */
// Kept for backward compatibility with notification URLs, don't remove
case 'modify': // Legacy operation
$oController = new ObjectController();
$oP = $oController->OperationModify();
break;
///////////////////////////////////////////////////////////////////////////////////////////
case 'select_for_modify_all': // Select the list of objects to be modified (bulk modify)
UI::OperationSelectForModifyAll($oP);
break;
@@ -703,22 +819,6 @@ try
break;
///////////////////////////////////////////////////////////////////////////////////////////
/** @deprecated 3.1.0 Use the "object.new" route instead */
// Kept for backward compatibility
case 'new': // Form to create a new object
$oController = new ObjectController();
$oP = $oController->OperationNew();
break;
///////////////////////////////////////////////////////////////////////////////////////////
case 'apply_modify': // Applying the modifications to an existing object
$oController = new ObjectController();
$oP = $oController->OperationApplyModify();
break;
///////////////////////////////////////////////////////////////////////////////////////////
case 'select_for_deletion': // Select multiple objects for deletion
$oP->DisableBreadCrumb();
@@ -813,14 +913,6 @@ try
///////////////////////////////////////////////////////////////////////////////////////////
case 'apply_new': // Creation of a new object
$oController = new ObjectController();
$oP = $oController->OperationApplyNew();
break;
///////////////////////////////////////////////////////////////////////////////////////////
case 'select_bulk_stimulus': // Form displayed when applying a stimulus to many objects
$oP->DisableBreadCrumb();
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');

View File

@@ -33,11 +33,11 @@ ApplicationMenu::CheckMenuIdEnabled('UniversalSearchMenu');
$oAppContext = new ApplicationContext();
$oP = new iTopWebPage(Dict::S('UI:UniversalSearchTitle'));
$oP->add_linked_script("../js/forms-json-utils.js");
$oP->add_linked_script("../js/wizardhelper.js");
$oP->add_linked_script("../js/wizard.utils.js");
$oP->add_linked_script("../js/extkeywidget.js");
$oP->add_linked_script("../js/jquery.blockUI.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/forms-json-utils.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/wizardhelper.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/wizard.utils.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/extkeywidget.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/jquery.blockUI.js");
// From now on the context is limited to the the selected organization ??

View File

@@ -21,8 +21,10 @@ use Combodo\iTop\Controller\Base\Layout\ObjectController;
use Combodo\iTop\Controller\PreferencesController;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
use Combodo\iTop\Kernel;
use Combodo\iTop\Service\Router\Router;
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectManager;
use Symfony\Component\HttpFoundation\Request;
require_once('../approot.inc.php');
@@ -44,7 +46,6 @@ try
$oKPI = new ExecutionKPI();
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
$sRoute = utils::ReadParam('route', '', false, utils::ENUM_SANITIZATION_FILTER_ROUTE);
$operation = utils::ReadParam('operation', '', false, utils::ENUM_SANITIZATION_FILTER_OPERATION);
// Only allow export functions to portal users
@@ -78,6 +79,7 @@ try
}
// First check if we can redirect the route to a dedicated controller
// IMPORTANT: Even though iTop router is deprecated, we must ensure old URLs keep working (for a while) as they can be used in received notifications, bookmarks, extensions, ...
$sRoute = utils::ReadParam('route', '', false, utils::ENUM_SANITIZATION_FILTER_ROUTE);
$oRouter = Router::GetInstance();
if ($oRouter->CanDispatchRoute($sRoute)) {
@@ -97,12 +99,82 @@ try
// Default for most operations, but can be switch to a JsonPage, DownloadPage or else if the operation requires it
$oPage = new AjaxPage("");
//---------------------------------------------------------------------------------------------------------
//
// Mind that the following backward compatibility pattern is also present in the `pages/UI.php` endpoint
//
//---------------------------------------------------------------------------------------------------------
//
// Kept for backward compatibility with iTop 3.1.x notification URLs, don't remove
// Non-dispatchable "routes" (iTop 3.1) are mapped to their old corresponding "operation" (iTop 3.0 and older), in order to be later redirected to the proper new URLs (iTop 3.2 and newer)
switch ($sRoute) {
case 'object.new':
case 'object.modify':
$operation = str_ireplace("object.", "", $sRoute);
break;
}
if (utils::IsNotNullOrEmptyString($sRoute)) {
DeprecatedCallsLog::Debug("Calling UI.php with a deprecated \"route\" parameter that was migrated to Symfony router, redirecting to fallback \"operation\" parameter. If URI comes from a custom code, migrate it. If URI comes from an old notification, mind that it may not work in future versions.", LogChannels::ROUTER, [
"request_uri" => $_SERVER["REQUEST_URI"],
"route" => $sRoute,
"fallback_operation" => $operation,
]);
}
// Kept for backward compatibility with iTop 3.0 and older notification URLs, don't remove
// These operations have been migrated to Symfony router, we need to redirect to the proper new URLs
switch ($operation) {
/** @deprecated 3.1.0 Use the "b_object_new" Symfony route instead */
case 'new':
/** @deprecated 3.1.0 Use the "b_object_modify" Symfony route instead */
case 'modify':
// Start a Symfony kernel and redirect to the corresponding route / controller
$oKernel = new Kernel("prod", false);
$oRequest = Request::createFromGlobals();
/** @var string $sRouteName Route name to redirect to */
$sRouteName = '';
/** @var string[] $aPath Parts of the Symfony route path (eg. for "/object/{sCLass}/{sId}", ["sClass" => "UserRequest", "sId" => "123"]) */
$aPath = [];
/** @var string[] $aQuery Query parameters of the request -parameters after the "?" of the URI- (eg. for "/object/UserRequest/123?a=1&b=2", ["a" => 1, "b" => 2]) */
$aQuery = $oRequest->query->all();
// Find controller corresponding to the old "operation"
switch ($operation) {
case 'new':
$sRouteName = 'b_object_new';
$aPath['_controller'] = ObjectController::class . "::OperationNew";
$aPath['sClass'] = utils::ReadParam('class', null, false, utils::ENUM_SANITIZATION_FILTER_CLASS);
break;
case 'modify':
$sRouteName = 'b_object_modify';
$aPath['_controller'] = ObjectController::class . "::OperationModify";
$aPath['sClass'] = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
$aPath['sId'] = utils::ReadParam('id', '');
break;
}
// Internal Symfony redirection
// Pros:
// - Keeps query parameters
// - Keeps posted parameters (essential for submission URLs)
// Cons:
// - Still displays the old URL
// - Uses a not so well-formed Symfony kernel?
$oSubRequest = $oRequest->duplicate($aQuery, null, $aPath);
$oResponse = $oKernel->handle($oSubRequest, \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST);
$oResponse->send();
$oKernel->terminate($oSubRequest, $oResponse);
die();
}
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
$sEncoding = utils::ReadParam('encoding', 'serialize');
$sClass = utils::ReadParam('class', 'MissingAjaxParam', false, 'class');
$sStyle = utils::ReadParam('style', 'list');
$oAjaxRenderController = new AjaxRenderController();
switch ($operation) {
@@ -1375,7 +1447,7 @@ JS
<<<EOF
$('.search-class-$sClassName button').prop('disabled', true);
$('.search-class-$sClassName h2').append('&nbsp;<img id="indicator" src="../images/indicator.gif">');
$('.search-class-$sClassName h2').append('&nbsp;<img id="indicator" src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif">');
var oParams = {operation: 'full_text_search_enlarge', class: '$sClassName', text: '$sFullTextJS'};
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
$('.search-class-$sClassName').html(data);
@@ -1532,6 +1604,7 @@ EOF
case 'xlsx_export_dialog':
DeprecatedCallsLog::NotifyDeprecatedPhpEndpoint('Use "export_*" operations instead of "'.$operation.'"');
$sFilter = utils::ReadParam('filter', '', false, 'raw_data');
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->SetContentType('text/html');
$oPage->add(
<<<EOF
@@ -1561,11 +1634,11 @@ EOF
padding-left: 16px;
cursor: pointer;
font-size: 10pt;
background: url(../images/minus.gif) 0 2px no-repeat;
background: url({$sAppRootUrl}images/minus.gif) 0 2px no-repeat;
}
.statistics > div.closed {
padding-left: 16px;
background: url(../images/plus.gif) 0 2px no-repeat;
background: url({$sAppRootUrl}images/plus.gif) 0 2px no-repeat;
}
.statistics .closed .stats-data {
@@ -1594,7 +1667,7 @@ EOF
);
$sJSLabels = json_encode($aLabels);
$sFilter = addslashes($sFilter);
$sJSPageUrl = addslashes(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php');
$sJSPageUrl = addslashes($sAppRootUrl.'pages/ajax.render.php');
$oPage->add_ready_script("$('#XlsxExportDlg').xlsxexporter({filter: '$sFilter', labels: $sJSLabels, ajax_page_url: '$sJSPageUrl'});");
break;
@@ -2208,7 +2281,8 @@ EOF
$oPage = new NiceWebPage(Dict::S('UI:BrowseInlineImages'));
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/magnific-popup.css');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.magnific-popup.min.js');
$sImgUrl = utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL;
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
$sImgUrl = $sAppRootUrl.INLINEIMAGE_DOWNLOAD_URL;
/** @noinspection SuspiciousAssignmentsInspection cke_upload_and_browse and cke_browse are chained */
$sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id');
@@ -2236,7 +2310,7 @@ EOF
}
}
$sPostUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?CKEditorFuncNum='.$sCKEditorFuncNum;
$sPostUrl = $sAppRootUrl.'pages/ajax.render.php?CKEditorFuncNum='.$sCKEditorFuncNum;
$oPage->add_style(
<<<EOF
@@ -2304,7 +2378,7 @@ EOF
$oPage->add_ready_script(
<<<EOF
$('#upload_button').on('change', function() {
$('#upload_status').html('<img src="../images/indicator.gif">');
$('#upload_status').html('<img src="{$sAppRootUrl}images/indicator.gif">');
$('#upload_form').submit();
$(this).prop('disabled', true);
});

View File

@@ -281,7 +281,7 @@ try
$oAllCategoriesDashlet
->AddCSSClasses(['ibo-dashlet--is-inline', 'ibo-dashlet-badge'])
->AddSubBlock(DashletFactory::MakeForDashletBadge(
'../images/icons/icons8-audit.svg',
utils::GetAbsoluteUrlAppRoot().'images/icons/icons8-audit.svg',
utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=audit",
$iCategoryCount,
Dict::S('UI:Audit:Interactive:Selection:BadgeAll')
@@ -304,7 +304,7 @@ try
/** @var AuditDomain $oAuditDomain */
while($oAuditDomain = $oDomainSet->Fetch()) {
$sDomainUrl = utils::GetAbsoluteUrlAppRoot()."pages/audit.php?operation=audit&domain=".$oAuditDomain->GetKey();
$sIconUrl = '../images/icons/icons8-puzzle.svg';
$sIconUrl = utils::GetAbsoluteUrlAppRoot().'images/icons/icons8-puzzle.svg';
/** @var \ormDocument $oImage */
$oImage = $oAuditDomain->Get('icon');
if (!$oImage->IsEmpty()) {
@@ -357,9 +357,9 @@ try
$oP->AddUiBlock(TitleUIBlockFactory::MakeForPage($sTitle));
$oP->AddUiBlock(new Text($sSubTitle));
$oTotalBlock = DashletFactory::MakeForDashletBadge('../images/icons/icons8-audit.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsAudited'));
$oErrorBlock = DashletFactory::MakeForDashletBadge('../images/icons/icons8-delete.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsInError'));
$oWorkingBlock = DashletFactory::MakeForDashletBadge('../images/icons/icons8-checkmark.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsValidated'));
$oTotalBlock = DashletFactory::MakeForDashletBadge(utils::GetAbsoluteUrlAppRoot().'images/icons/icons8-audit.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsAudited'));
$oErrorBlock = DashletFactory::MakeForDashletBadge(utils::GetAbsoluteUrlAppRoot().'images/icons/icons8-delete.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsInError'));
$oWorkingBlock = DashletFactory::MakeForDashletBadge(utils::GetAbsoluteUrlAppRoot().'images/icons/icons8-checkmark.svg', '#', 0, Dict::S('UI:Audit:Dashboard:ObjectsValidated'));
$aCSSClasses = ['ibo-dashlet--is-inline', 'ibo-dashlet-badge'];
@@ -436,7 +436,7 @@ try
foreach ($aErrors as $aErrorRow) {
$aObjectsWithErrors[$aErrorRow['id']] = true;
}
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\"><img src=\"../images/icons/icons8-export-csv.svg\" class=\"ibo-audit--audit-line--csv-download\"></a>";
$aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "<a href=\"?operation=errors&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount</a> <a href=\"?operation=csv&category=".$oAuditCategory->GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\"><img src=\"" . utils::GetAbsoluteUrlAppRoot() . "images/icons/icons8-export-csv.svg\" class=\"ibo-audit--audit-line--csv-download\"></a>";
$aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount));
$aRow['class'] = $oAuditCategory->GetReportColor($iCount, $iErrorsCount);
}

View File

@@ -400,6 +400,7 @@ try {
$iUnchanged = 0;
$aTableData = [];
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
foreach ($aRes as $iLine => $aResRow) {
$aTableRow = [];
@@ -414,7 +415,7 @@ try {
$sFinalClass = $aResRow['finalclass'];
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
$sUrl = $oObj->GetHyperlink();
$sStatus = '<img src="../images/unchanged.png" title="'.Dict::S('UI:CSVReport-Icon-Unchanged').'">';
$sStatus = '<img src="' . $sAppRootUrl . 'images/unchanged.png" title="'.Dict::S('UI:CSVReport-Icon-Unchanged').'">';
$sCSSRowClass = 'ibo-csv-import--row-unchanged';
break;
@@ -423,7 +424,7 @@ try {
$sFinalClass = $aResRow['finalclass'];
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
$sUrl = $oObj->GetHyperlink();
$sStatus = '<img src="../images/modified.png" title="'.Dict::S('UI:CSVReport-Icon-Modified').'">';
$sStatus = '<img src="' . $sAppRootUrl . 'images/modified.png" title="'.Dict::S('UI:CSVReport-Icon-Modified').'">';
$sCSSRowClass = 'ibo-csv-import--row-modified';
break;
@@ -432,7 +433,7 @@ try {
$sFinalClass = $aResRow['finalclass'];
$oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue());
$sUrl = $oObj->GetHyperlink();
$sStatus = '<img src="../images/delete.png" title="'.Dict::S('UI:CSVReport-Icon-Missing').'">';
$sStatus = '<img src="' . $sAppRootUrl . 'images/delete.png" title="'.Dict::S('UI:CSVReport-Icon-Missing').'">';
$sCSSRowClass = 'ibo-csv-import--row-modified';
if ($bSimulate) {
$sMessage = Dict::S('UI:CSVReport-Object-MissingToUpdate');
@@ -443,7 +444,7 @@ try {
case 'RowStatus_NewObj':
$iCreated++;
$sStatus = '<img src="../images/added.png" title="'.Dict::S('UI:CSVReport-Icon-Created').'">';
$sStatus = '<img src="' . $sAppRootUrl . 'images/added.png" title="'.Dict::S('UI:CSVReport-Icon-Created').'">';
$sCSSRowClass = 'ibo-csv-import--row-added';
if ($bSimulate) {
$sMessage = Dict::S('UI:CSVReport-Object-ToCreate');
@@ -585,21 +586,21 @@ HTML;
$oMulticolumn->AddCSSClass('ml-1');
$oForm->AddSubBlock($oMulticolumn);
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="../images/unchanged.png">&nbsp;'.sprintf($aDisplayFilters['unchanged'], $iUnchanged), '', "1", "show_unchanged", "checkbox");
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="' . $sAppRootUrl . 'images/unchanged.png">&nbsp;'.sprintf($aDisplayFilters['unchanged'], $iUnchanged), '', "1", "show_unchanged", "checkbox");
$oCheckBoxUnchanged->GetInput()->SetIsChecked(true);
$oCheckBoxUnchanged->SetBeforeInput(false);
$oCheckBoxUnchanged->GetInput()->AddCSSClass('ibo-input-checkbox');
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oCheckBoxUnchanged));
$oPage->add_ready_script("$('#show_unchanged').on('click', function(){ToggleRows('ibo-csv-import--row-unchanged')})");
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="../images/modified.png">&nbsp;'.sprintf($aDisplayFilters['modified'], $iModified), '', "1", "show_modified", "checkbox");
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="' . $sAppRootUrl . 'images/modified.png">&nbsp;'.sprintf($aDisplayFilters['modified'], $iModified), '', "1", "show_modified", "checkbox");
$oCheckBoxUnchanged->GetInput()->SetIsChecked(true);
$oCheckBoxUnchanged->SetBeforeInput(false);
$oCheckBoxUnchanged->GetInput()->AddCSSClass('ibo-input-checkbox');
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oCheckBoxUnchanged));
$oPage->add_ready_script("$('#show_modified').on('click', function(){ToggleRows('ibo-csv-import--row-modified')})");
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="../images/added.png">&nbsp;'.sprintf($aDisplayFilters['added'], $iCreated), '', "1", "show_created", "checkbox");
$oCheckBoxUnchanged = InputUIBlockFactory::MakeForInputWithLabel('<img src="' . $sAppRootUrl . 'images/added.png">&nbsp;'.sprintf($aDisplayFilters['added'], $iCreated), '', "1", "show_created", "checkbox");
$oCheckBoxUnchanged->GetInput()->SetIsChecked(true);
$oCheckBoxUnchanged->SetBeforeInput(false);
$oCheckBoxUnchanged->GetInput()->AddCSSClass('ibo-input-checkbox');

View File

@@ -81,8 +81,8 @@ if (is_link($sPageEnvFullPath)) {
$aPossibleBasePaths = [
APPROOT.$sSourceDir,
APPROOT.'extensions',
APPROOT.'data/'.$sEnvironment.'-modules',
APPROOT.'data/downloaded-extensions', // Hub connector
utils::GetDataPath().$sEnvironment.'-modules',
utils::GetDataPath().'downloaded-extensions', // Hub connector
];
} else {
$aPossibleBasePaths = [$sEnvFullPath];

View File

@@ -111,17 +111,17 @@ $sDotExecutable = MetaModel::GetConfig()->Get('graphviz_path');
if (file_exists($sDotExecutable))
{
// create the file with Graphviz
$sImageFilePath = APPROOT."data/lifecycle/".$sClass.".svg";
if (!is_dir(APPROOT."data"))
$sImageFilePath = utils::GetDataPath()."lifecycle/".$sClass.".svg";
if (!is_dir(utils::GetDataPath()))
{
@mkdir(APPROOT."data");
@mkdir(utils::GetDataPath());
}
if (!is_dir(APPROOT."data/lifecycle"))
if (!is_dir(utils::GetDataPath()."lifecycle"))
{
@mkdir(APPROOT."data/lifecycle");
@mkdir(utils::GetDataPath()."lifecycle");
}
$sDotDescription = GraphvizLifecycle($sClass);
$sDotFilePath = APPROOT."data/lifecycle/{$sClass}.dot";
$sDotFilePath = utils::GetDataPath()."lifecycle/{$sClass}.dot";
$rFile = @fopen($sDotFilePath, "w");
@fwrite($rFile, $sDotDescription);

View File

@@ -133,7 +133,7 @@ function ValidateOtherSettings()
}
else
{
$('#v_default_page_size').html('<img src="../images/validation_error.png"/>');
$('#v_default_page_size').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/validation_error.png"/>');
$('#ibo-misc-settings-submit').prop('disabled', true);
return false;
}
@@ -417,7 +417,7 @@ JS
$oUserPicturePlaceHolderBlock = new Panel(Dict::S('UI:Preferences:ChooseAPlaceholder'), array(), 'grey', 'ibo-user-picture-placeholder');
$sUserPicturesFolder = '../images/user-pictures/';
$sUserPicturesFolder = utils::GetAbsoluteUrlAppRoot() . 'images/user-pictures/';
$sUserDefaultPicture = appUserPreferences::GetPref('user_picture_placeholder', 'default-placeholder.png');
$sUserPicturePlaceHolderHtml = '';
$sUserPicturePlaceHolderHtml .= '<p>'.Dict::S('UI:Preferences:ChooseAPlaceholder+').'</p> <div class="ibo-preferences--user-preferences--picture-placeholder">';

View File

@@ -37,11 +37,11 @@ try
// Main program
//
$oP = new iTopWebPage(Dict::S('Menu:TagAdminMenu+'));
$oP->add_linked_script("../js/forms-json-utils.js");
$oP->add_linked_script("../js/wizardhelper.js");
$oP->add_linked_script("../js/wizard.utils.js");
$oP->add_linked_script("../js/extkeywidget.js");
$oP->add_linked_script("../js/jquery.blockUI.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/forms-json-utils.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/wizardhelper.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/wizard.utils.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/extkeywidget.js");
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/jquery.blockUI.js");
$sBaseClass = 'TagSetFieldData';
$sClass = utils::ReadParam('class', '', false, 'class');

View File

@@ -626,14 +626,14 @@ class ApplicationInstaller
}
}
// Dump the "reference" model, just before loading any actual delta
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$sEnvironment.'.xml');
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'.xml');
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
$sDeltaFile = utils::GetDataPath().$sEnvironment.'.delta.xml';
if (file_exists($sDeltaFile))
{
$oDelta = new MFDeltaModule($sDeltaFile);
$oFactory->LoadModule($oDelta);
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$sEnvironment.'-with-delta.xml');
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$sEnvironment.'-with-delta.xml');
}
$oMFCompiler = new MFCompiler($oFactory, $sEnvironment);
@@ -658,8 +658,8 @@ class ApplicationInstaller
}
// Set an "Instance UUID" identifying this machine based on a file located in the data directory
$sInstanceUUIDFile = APPROOT.'data/instance.txt';
SetupUtils::builddir(APPROOT.'data');
$sInstanceUUIDFile = utils::GetDataPath().'instance.txt';
SetupUtils::builddir(utils::GetDataPath());
if (!file_exists($sInstanceUUIDFile))
{
$sIntanceUUID = utils::CreateUUID('filesystem');

View File

@@ -201,7 +201,7 @@ class DBBackup
$oArchive = new ITopArchiveTar($sTargetFile.'.tar.gz');
$sTmpFolder = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
$sTmpFolder = utils::GetDataPath().'tmp-backup-'.rand(10000, getrandmax());
$aFiles = $this->PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder);
$sFilesList = var_export($aFiles, true);
@@ -248,7 +248,7 @@ class DBBackup
$aRet[] = $sFile;
}
$sDeltaFile = APPROOT.'data/'.utils::GetCurrentEnvironment().'.delta.xml';
$sDeltaFile = utils::GetDataPath().utils::GetCurrentEnvironment().'.delta.xml';
if (file_exists($sDeltaFile))
{
$sFile = $sTmpFolder.'/delta.xml';
@@ -256,7 +256,7 @@ class DBBackup
copy($sDeltaFile, $sFile);
$aRet[] = $sFile;
}
$sExtraDir = APPROOT.'data/'.utils::GetCurrentEnvironment().'-modules/';
$sExtraDir = utils::GetDataPath().utils::GetCurrentEnvironment().'-modules/';
if (is_dir($sExtraDir))
{
$sModules = utils::GetCurrentEnvironment().'-modules';

View File

@@ -380,7 +380,7 @@ class RunTimeEnvironment
{
$aDirsToCompile[] = APPROOT.'extensions';
}
$sExtraDir = APPROOT.'data/'.$this->sTargetEnv.'-modules/';
$sExtraDir = utils::GetDataPath().$this->sTargetEnv.'-modules/';
if (is_dir($sExtraDir))
{
$aDirsToCompile[] = $sExtraDir;
@@ -475,7 +475,7 @@ class RunTimeEnvironment
}
while($bModuleAdded);
$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
$sDeltaFile = utils::GetDataPath().$this->sTargetEnv.'.delta.xml';
if (file_exists($sDeltaFile))
{
$oDelta = new MFDeltaModule($sDeltaFile);
@@ -512,7 +512,7 @@ class RunTimeEnvironment
{
// Just before loading the delta, let's save an image of the datamodel
// in case there is no delta the operation will be done after the end of the loop
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sTargetEnv.'.xml');
}
$oFactory->LoadModule($oModule);
}
@@ -520,10 +520,10 @@ class RunTimeEnvironment
if ($oModule instanceof MFDeltaModule) {
// A delta was loaded, let's save a second copy of the datamodel
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'-with-delta.xml');
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sTargetEnv.'-with-delta.xml');
} else {
// No delta was loaded, let's save the datamodel now
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
$oFactory->SaveToFile(utils::GetDataPath().'datamodel-'.$this->sTargetEnv.'.xml');
}
$sTargetDir = APPROOT.'env-'.$this->sTargetEnv;
@@ -532,7 +532,7 @@ class RunTimeEnvironment
$oMFCompiler = new MFCompiler($oFactory, $this->sFinalEnv);
$oMFCompiler->Compile($sTargetDir, null, $bUseSymLinks, $bSkipTempDir);
$sCacheDir = APPROOT.'data/cache-'.$this->sTargetEnv;
$sCacheDir = utils::GetDataPath().'cache-'.$this->sTargetEnv;
SetupUtils::builddir($sCacheDir);
SetupUtils::tidydir($sCacheDir);
@@ -942,38 +942,38 @@ class RunTimeEnvironment
{
if ($this->sFinalEnv != $this->sTargetEnv)
{
if (file_exists(APPROOT.'data/'.$this->sTargetEnv.'.delta.xml'))
if (file_exists(utils::GetDataPath().$this->sTargetEnv.'.delta.xml'))
{
if (file_exists(APPROOT.'data/'.$this->sFinalEnv.'.delta.xml'))
if (file_exists(utils::GetDataPath().$this->sFinalEnv.'.delta.xml'))
{
// Make a "previous" file
copy(
APPROOT.'data/'.$this->sFinalEnv.'.delta.xml',
APPROOT.'data/'.$this->sFinalEnv.'.delta.prev.xml'
utils::GetDataPath().$this->sFinalEnv.'.delta.xml',
utils::GetDataPath().$this->sFinalEnv.'.delta.prev.xml'
);
}
$this->CommitFile(
APPROOT.'data/'.$this->sTargetEnv.'.delta.xml',
APPROOT.'data/'.$this->sFinalEnv.'.delta.xml'
utils::GetDataPath().$this->sTargetEnv.'.delta.xml',
utils::GetDataPath().$this->sFinalEnv.'.delta.xml'
);
}
$this->CommitFile(
APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml',
APPROOT.'data/datamodel-'.$this->sFinalEnv.'.xml'
utils::GetDataPath().'datamodel-'.$this->sTargetEnv.'.xml',
utils::GetDataPath().'datamodel-'.$this->sFinalEnv.'.xml'
);
$this->CommitFile(
APPROOT.'data/datamodel-'.$this->sTargetEnv.'-with-delta.xml',
APPROOT.'data/datamodel-'.$this->sFinalEnv.'-with-delta.xml',
utils::GetDataPath().'datamodel-'.$this->sTargetEnv.'-with-delta.xml',
utils::GetDataPath().'datamodel-'.$this->sFinalEnv.'-with-delta.xml',
false
);
$this->CommitDir(
APPROOT.'data/'.$this->sTargetEnv.'-modules/',
APPROOT.'data/'.$this->sFinalEnv.'-modules/',
utils::GetDataPath().$this->sTargetEnv.'-modules/',
utils::GetDataPath().$this->sFinalEnv.'-modules/',
false
);
$this->CommitDir(
APPROOT.'data/cache-'.$this->sTargetEnv,
APPROOT.'data/cache-'.$this->sFinalEnv,
utils::GetDataPath().'cache-'.$this->sTargetEnv,
utils::GetDataPath().'cache-'.$this->sFinalEnv,
false
);
$this->CommitDir(

View File

@@ -39,10 +39,10 @@ class SetupPage extends NiceWebPage
public function __construct($sTitle)
{
parent::__construct($sTitle);
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/jquery.blockUI.js");
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'node_modules/@popperjs/core/dist/umd/popper.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'node_modules/tippy.js/dist/tippy-bundle.umd.js');
$this->add_linked_script("../setup/setup.js");
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "setup/setup.js");
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-combodo/font-combodo.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'node_modules/tippy.js/dist/tippy.css');

View File

@@ -1581,7 +1581,7 @@ JS
if (is_dir($oWizard->GetParameter('copy_extensions_from'))) {
$aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from');
}
$sExtraDir = APPROOT.'data/production-modules/';
$sExtraDir = utils::GetDataPath().'production-modules/';
if (is_dir($sExtraDir))
{
$aDirsToScan[] = $sExtraDir;
@@ -1935,7 +1935,7 @@ JS
if (empty($sEnv)) {
$aLicenceFiles = array_merge($aLicenceFiles, glob(APPROOT.'datamodels/*/*/license.*.xml'));
$aLicenceFiles = array_merge($aLicenceFiles, glob(APPROOT.'extensions/{*,*/*}/license.*.xml', GLOB_BRACE));
$aLicenceFiles = array_merge($aLicenceFiles, glob(APPROOT.'data/*-modules/{*,*/*}/license.*.xml', GLOB_BRACE));
$aLicenceFiles = array_merge($aLicenceFiles, glob(utils::GetDataPath().'*-modules/{*,*/*}/license.*.xml', GLOB_BRACE));
}
else
{
@@ -2067,11 +2067,11 @@ JS
if (!is_dir(APPROOT.'data')) {
mkdir(APPROOT.'data');
}
if (!is_dir(APPROOT.'data/setup')) {
mkdir(APPROOT.'data/setup');
if (!is_dir(utils::GetDataPath().'setup')) {
mkdir(utils::GetDataPath().'setup');
}
$sUID = hash('sha256', rand());
file_put_contents(APPROOT.'data/setup/authent', $sUID);
file_put_contents(utils::GetDataPath().'setup/authent', $sUID);
Session::Set('setup_token', $sUID);
return $sUID;
}
@@ -2087,7 +2087,7 @@ JS
final public static function CheckSetupToken($bRemoveToken = false)
{
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
$sTokenFile = APPROOT.'data/setup/authent';
$sTokenFile = utils::GetDataPath().'setup/authent';
if (!file_exists($sTokenFile) || $sAuthent !== file_get_contents($sTokenFile)) {
throw new SecurityException('Setup operations are not allowed outside of the setup');
}
@@ -2106,7 +2106,7 @@ JS
{
if (Session::IsSet('setup_token')) {
$sAuth = Session::Get('setup_token');
$sTokenFile = APPROOT.'data/setup/authent';
$sTokenFile = utils::GetDataPath().'setup/authent';
if (file_exists($sTokenFile) && $sAuth === file_get_contents($sTokenFile)) {
return true;
}
@@ -2120,7 +2120,7 @@ JS
*/
public static function EraseSetupToken()
{
$sTokenFile = APPROOT.'data/setup/authent';
$sTokenFile = utils::GetDataPath().'setup/authent';
if (is_file($sTokenFile)) {
unlink($sTokenFile);
}

View File

@@ -217,7 +217,7 @@ HTML;
}
}
}
$oPage->add_linked_script('../setup/setup.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'setup/setup.js');
$oPage->add_script("function CanMoveForward()\n{\n".$oStep->JSCanMoveForward()."\n}\n");
$oPage->add_script("function CanMoveBackward()\n{\n".$oStep->JSCanMoveBackward()."\n}\n");
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');

View File

@@ -240,7 +240,7 @@ class WizStepInstallOrUpgrade extends WizardStep
$sPreviousVersionDir = '';
if ($sInstallMode == '')
{
$sDBBackupPath = APPROOT.'data/backups/manual/setup-'.date('Y-m-d_H_i');
$sDBBackupPath = utils::GetDataPath().'backups/manual/setup-'.date('Y-m-d_H_i');
$bDBBackup = true;
$aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT);
if ($aPreviousInstance['found']) {
@@ -2307,7 +2307,7 @@ class WizStepSummary extends WizardStep
$oPage->add('<fieldset id="installation_progress"><legend>Progress of the installation</legend>');
$oPage->add('<div id="progress_content">');
$oPage->add_linked_script('../setup/jquery.progression.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . 'setup/jquery.progression.js');
$oPage->add('<p class="center"><span id="setup_msg">Ready to start...</span></p><div style="display:block;margin-left: auto; margin-right:auto;" id="progress">0%</div>');
$oPage->add('</div>'); // progress_content
$oPage->add('</fieldset>');

View File

@@ -24,7 +24,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
define ('KEYS_CACHE_FILE', APPROOT.'data/keyscache.tmp');
define ('KEYS_CACHE_FILE', utils::GetDataPath().'keyscache.tmp');
/**
* Class to load sets of objects from XML files into the database
* XML files can be produced by the 'export' web service or by any other means

View File

@@ -55,7 +55,6 @@ class FormHelper
*/
public static function DisableAttributeBlobInputs(string $sClassName, array &$aExtraParams): void
{
// Initialize extra params array
if (!array_key_exists('fieldsFlags', $aExtraParams)) {
$aExtraParams['fieldsFlags'] = [];
@@ -64,13 +63,13 @@ class FormHelper
$aExtraParams['fieldsComments'] = [];
}
// Iterate throw class attributes...
// Iterate through class attributes...
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
foreach (MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef) {
// Set attribute blobs in read only
if ($oAttDef instanceof AttributeBlob) {
$aExtraParams['fieldsFlags'][$sAttCode] = OPT_ATT_READONLY;
$aExtraParams['fieldsComments'][$sAttCode] = '&nbsp;<img src="../images/transp-lock.png" style="vertical-align:middle" title="'.utils::EscapeHtml(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
$aExtraParams['fieldsComments'][$sAttCode] = '&nbsp;<img src="' . $sAppRootUrl . 'images/transp-lock.png" style="vertical-align:middle" title="'.utils::EscapeHtml(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
}
}
}

View File

@@ -11,7 +11,6 @@ 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\Service\Router\Router;
use DBObject;
use Dict;
use MetaModel;
@@ -100,12 +99,11 @@ class ObjectSummary extends ObjectDetails
*/
private function ComputeActions()
{
$oRouter = Router::GetInstance();
$oDetailsButton = null;
// We can pass a DBObject to the UIBlock, so we check for the DisplayModifyForm method
if(method_exists($this->oObject, 'DisplayModifyForm') && UserRights::IsActionAllowed($this->sClassName, UR_ACTION_MODIFY)) {
$oPopoverMenu = new PopoverMenu();
$oDetailsAction = new URLPopupMenuItem(
'UI:Menu:View',
Dict::S('UI:Menu:View'),
@@ -113,12 +111,13 @@ class ObjectSummary extends ObjectDetails
'_blank'
);
$oModifyButton = ButtonUIBlockFactory::MakeLinkNeutral(
$oRouter->GenerateUrl('object.modify', ['class' => $this->sClassName, 'id' => $this->sObjectId]),
// Can't use URL Generator (`$this->oUrlGenerator->generate("b_object_summary", [$sObjClass, $sObjKey])`) yet as we have to find how to inject it here
"{$sRootUrl}app.php/object/modify/{$this->sClassName}/{$this->sObjectId}",
Dict::S('UI:Menu:Modify'),
'fas fa-external-link-alt',
'_blank',
);
$oPopoverMenu->AddItem('more-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItem($oDetailsAction))->SetContainer(PopoverMenu::ENUM_CONTAINER_PARENT);
$oDetailsButton = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oModifyButton, $oPopoverMenu);

View File

@@ -26,8 +26,8 @@ class ErrorPage extends NiceWebPage
{
$oKpi = new ExecutionKPI();
parent::__construct($sTitle);
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script("../setup/setup.js");
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "js/jquery.blockUI.js");
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "setup/setup.js");
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-combodo/font-combodo.css');
$this->add_saas("css/setup.scss");
@@ -58,7 +58,7 @@ class ErrorPage extends NiceWebPage
if(utils::IsEasterEggAllowed())
{
$this->add('<div class="message message-valid">'.Dict::S('UI:ErrorPage:UnstableVersion').'</div>');
$this->add('<img src="../images/alpha-fatal-error.gif">');
$this->add('<img src="' . utils::GetAbsoluteUrlAppRoot() . 'images/alpha-fatal-error.gif">');
$this->add('<div class="message message-valid">'.nl2br(Dict::S('UI:ErrorPage:KittyDisclaimer')).'</div>');
}
$this->log_error($sText);

View File

@@ -26,6 +26,7 @@ use Dict;
use ExecutionKPI;
use IssueLog;
use MetaModel;
use Symfony\Component\HttpFoundation\Response;
use UserRights;
use utils;
@@ -734,15 +735,28 @@ class WebPage implements Page
* Add a script (as an include, i.e. link) to the header of the page.<br>
* Handles duplicates : calling twice with the same script will add the script only once
*
* @uses WebPage::$a_linked_scripts
* @param string $s_linked_script
* @param string $sLinkedScriptAbsUrl
*
* @return void
* @uses WebPage::$a_linked_scripts
* @since 3.2.0 N°6935 $sLinkedScriptAbsUrl MUST be an absolute URL
*/
public function add_linked_script($s_linked_script)
public function add_linked_script($sLinkedScriptAbsUrl)
{
if (!empty(trim($s_linked_script))) {
$this->a_linked_scripts[$s_linked_script] = $s_linked_script;
// Ensure there is actually an URI
if (utils::IsNullOrEmptyString(trim($sLinkedScriptAbsUrl))) {
return;
}
// Check if URI is absolute ("://" do allow any protocol), otherwise warn that it's a deprecated behavior
if (false === stripos($sLinkedScriptAbsUrl, "://")) {
IssueLog::Warning("Linked script added to page with a non absolute URL, which may lead to it not being loaded and causing javascript errors.", null, [
"linked_script_url" => $sLinkedScriptAbsUrl,
"request_uri" => $_SERVER['REQUEST_URI'],
]);
}
$this->a_linked_scripts[$sLinkedScriptAbsUrl] = $sLinkedScriptAbsUrl;
}
/**
@@ -1331,12 +1345,65 @@ JS;
*/
public function output()
{
$oKpi = new ExecutionKPI();
// Send headers
foreach ($this->a_headers as $sHeader) {
header($sHeader);
}
// Render HTML content
$sHtml = $this->RenderContent();
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' KB)');
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
ExecutionKPI::ReportStats();
}
/**
* @return \Symfony\Component\HttpFoundation\Response Generate the Symfony Response object (content + code + headers) for the current page, this is equivalent (and new way) of \WebPage::output() for Symfony controllers
* @since 3.2.0 N°6935
*/
public function GenerateResponse(): Response
{
// Render HTML content
$sHtml = $this->RenderContent();
// Prepare Symfony Response
$oKpi = new ExecutionKPI();
// Format headers as a key => value array to match Symfony Response format
$aHeadersAsAssocArray = [];
foreach ($this->a_headers as $sHeader) {
// Limit explode to only split at the first occurrence
list($sHeaderName, $sHeaderValue) = explode(': ', $sHeader, 2);
$aHeadersAsAssocArray[$sHeaderName] = $sHeaderValue;
}
$oResponse = new Response($sHtml, Response::HTTP_OK, $aHeadersAsAssocArray);
$oKpi->ComputeAndReport('Preparing response ('.round(strlen($sHtml) / 1024).' KB)');
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
ExecutionKPI::ReportStats();
return $oResponse;
}
/**
* @return string Complete HTML content of the \WebPage
* @throws \CoreTemplateException
* @throws \Twig\Error\LoaderError
* @since 3.2.0 N°6935
*/
protected function RenderContent(): string
{
$oKpi = new ExecutionKPI();
$s_captured_output = $this->ob_get_clean_safe();
$aData = [];
@@ -1390,16 +1457,10 @@ JS;
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport(get_class($this).'output');
// Echo global HTML
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
$oKpi->ComputeAndReport("Rendering content (".static::class.")");
if (class_exists('DBSearch')) {
DBSearch::RecordQueryTrace();
}
ExecutionKPI::ReportStats();
return $sHtml;
}
/**

View File

@@ -829,6 +829,31 @@ HTML;
* @throws \Exception
*/
public function output()
{
// Send headers
if ($this->GetOutputFormat() === 'html') {
foreach ($this->a_headers as $sHeader) {
header($sHeader);
}
}
// Render HTKL content
$sHtml = $this->RenderContent();
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
DBSearch::RecordQueryTrace();
ExecutionKPI::ReportStats();
}
/**
* @inheritDoc
* @since 3.2.0 N°6935
*/
protected function RenderContent(): string
{
$oKpi = new ExecutionKPI();
@@ -933,16 +958,16 @@ HTML;
$aData['aDeferredBlocks']['oPageContent'] = $this->GetDeferredBlocks($this->GetContentLayout());
// - Prepare generic templates
$aData['aTemplates'] = array();
// TODO 3.1 Replace hardcoded 'Please wait' with dict entries
// - Modal template with loader
$oModalTemplateContentBlock = new UIContentBlock();
$oModalTemplateContentBlock->AddCSSClass('ibo-modal')
->AddDataAttribute('role', 'ibo-modal')
->AddSubBlock(SpinnerUIBlockFactory::MakeMedium(null, 'Please wait'));
$aData['aTemplates'][] = TemplateUIBlockFactory::MakeForBlock('ibo-modal-template', $oModalTemplateContentBlock);
// - Small loader template
$oSmallLoaderTemplateContentBlock = new UIContentBlock();
$oSmallLoaderTemplateContentBlock->AddSubBlock(SpinnerUIBlockFactory::MakeSmall(null , 'Please wait'));
@@ -997,66 +1022,14 @@ HTML;
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Send headers
if ($this->GetOutputFormat() === 'html') {
foreach ($this->a_headers as $sHeader) {
header($sHeader);
}
}
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport(get_class($this).' output');
// Echo global HTML
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
DBSearch::RecordQueryTrace();
ExecutionKPI::ReportStats();
return;
/////////////////////////////////////////////////////////
////////////////// ☢ DANGER ZONE ☢ /////////////////////
/////////////////////////////////////////////////////////
// Render the tabs in the page (if any)
// $this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
// Put here the 'ready scripts' that must be executed after all others
$aMultiselectOptions = array(
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
$sJSMultiselectOptions = json_encode($aMultiselectOptions);
$this->add_ready_script(
<<<EOF
// Since the event is only triggered when the hash changes, we need to trigger
// the event now, to handle the hash the page may have loaded with.
$(window).trigger( 'hashchange' );
// Some table are sort-able, some are not, let's fix this
$('table.listResults').each( function() { FixTableSorter($(this)); } );
$('.multiselect').multiselect($sJSMultiselectOptions);
EOF
);
$this->outputCollapsibleSectionInit();
// TODO 3.0.0: Is this for the "Debug" popup? We should do a helper to display a popup in various cases (welcome message for example)
$s_captured_output = $this->ob_get_clean_safe();
$oKpi->ComputeAndReport("Rendering content (".static::class.")");
return $sHtml;
}
/**
* @inheritDoc
* @throws \Exception

View File

@@ -54,7 +54,7 @@ class iTopWizardWebPage extends iTopWebPage
$sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep';
$aSteps[] = "<div class=\"$sStyle\"><span>$sStepTitle</span></div>";
}
$sWizardHeader = "<div class=\"wizHeader\"><h1>".utils::EscapeHtml($this->s_title)."</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
$sWizardHeader = "<div class=\"wizHeader\"><h1>".utils::EscapeHtml($this->s_title)."</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"" . utils::GetAbsoluteUrlAppRoot() . "images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
$this->s_content = "$sWizardHeader<div class=\"wizContainer\">".$this->s_content."</div>";
parent::output();
}

View File

@@ -1,32 +0,0 @@
<?php
/**
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Controller;
/**
* Class AbstractAppController
*
* @package Combodo\iTop\Controller
* @since 3.2.0
*/
class AbstractAppController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
}

View File

@@ -6,6 +6,9 @@
namespace Combodo\iTop\Controller;
use DeprecatedCallsLog;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as BaseAbstractControllerAlias;
/**
* Class AbstractController
*
@@ -15,14 +18,17 @@ namespace Combodo\iTop\Controller;
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Controller
* @since 3.1.0
* @since 3.2.0 N°6935 Controller is now based on Symfony controller
*/
abstract class AbstractController implements iController
abstract class AbstractController extends BaseAbstractControllerAlias implements iController
{
/**
* @inheritDoc
* @deprecated 3.2.0 N°6935 Use \Symfony\Component\HttpFoundation\Request::isXmlHttpRequest() instead
*/
public function IsHandlingXmlHttpRequest(): bool
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod("Use \Symfony\Component\HttpFoundation\Request::isXmlHttpRequest() instead");
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
}
}

View File

@@ -845,7 +845,7 @@ EOF
);
$sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$oPage->add('<div id="about_box"><div class="ibo-about-box--top-part">');
$oPage->add('<div><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logos/logo-combodo-dark.svg?t='.utils::GetCacheBusterTimestamp().'"/></a></div>');
$oPage->add('<div><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="' . utils::GetAbsoluteUrlAppRoot() . 'images/logos/logo-combodo-dark.svg?t='.utils::GetCacheBusterTimestamp().'"/></a></div>');
$oPage->add('<div>'.$sVersionString.'</div>');
$oPage->add("</div>");
self::DisplayAboutLicenses($oPage);
@@ -898,7 +898,7 @@ EOF
if (file_exists(APPROOT.'extensions')) {
$aSearchDirs[] = APPROOT.'extensions';
}
$sExtraDir = APPROOT.'data/'.$sCurrEnv.'-modules/';
$sExtraDir = utils::GetDataPath().$sCurrEnv.'-modules/';
if (file_exists($sExtraDir)) {
$aSearchDirs[] = $sExtraDir;
}
@@ -927,7 +927,7 @@ EOF
// Display
//
$oPage->add('<div id="about_box"><div class="ibo-about-box--top-part">');
$oPage->add('<div><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logos/logo-combodo-dark.svg?t='.utils::GetCacheBusterTimestamp().'"/></a></div>');
$oPage->add('<div><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="' . utils::GetAbsoluteUrlAppRoot() . 'images/logos/logo-combodo-dark.svg?t='.utils::GetCacheBusterTimestamp().'"/></a></div>');
$oPage->add('<div>'.$sVersionString.'<br/>'.'MySQL: '.$sMySQLVersion.'<br/>'.'PHP: '.$sPHPVersion.'<br/></div>');
$oPage->add("</div>");

View File

@@ -30,7 +30,12 @@ use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\JsonPage;
use MetaModel;
use SecurityException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\Annotation\Route;
use Combodo\iTop\Service\SummaryCard\SummaryCardService;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use UserRights;
use utils;
@@ -42,30 +47,47 @@ use utils;
* @since 3.1.0
* @package Combodo\iTop\Controller\Base\Layout
*/
#[Route("/object", name: "b_object_")]
class ObjectController extends AbstractController
{
public const ROUTE_NAMESPACE = 'object';
/** @deprecated 3.2.0 N°6935 Replaced by Symfony route name prefix on the controller */
//public const ROUTE_NAMESPACE = 'object';
/**
* @throws \CoreException
* @throws \MySQLHasGoneAwayException
* @throws \MySQLException
* @throws \DictExceptionMissingString
* @throws \CoreUnexpectedValue
* @throws \ConfigException
* @throws \ApplicationException
* @throws \MissingQueryArgument
* @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $oUrlGenerator
* @since 3.2.0 N°6935
*/
public function OperationNew()
public function __construct(
protected UrlGeneratorInterface $oUrlGenerator
)
{
}
/**
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param string $sClass Class of the datamodel object to create
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
* @throws ApplicationException
*/
#[Route('/new/{sClass}', name: 'new')]
public function OperationNew(Request $oRequest, string $sClass): Response
{
// Retrieve parameters
$bPrintable = utils::ReadParam('printable', '0') === '1';
$sClass = utils::ReadParam('class', '', false, 'class');
$sStateCode = utils::ReadParam('state', '');
$bCheckSubClass = utils::ReadParam('checkSubclass', true);
// Check parameters
if (false === MetaModel::IsValidClass($sClass)) {
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
$oAppContext = new ApplicationContext();
$oRouter = Router::GetInstance();
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage = new AjaxPage('');
} else {
$oPage = new iTopWebPage('', $bPrintable);
@@ -73,7 +95,6 @@ class ObjectController extends AbstractController
$this->AddRequiredForModificationJsFilesToPage($oPage);
}
if (empty($sClass))
{
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'class'));
@@ -133,7 +154,7 @@ class ObjectController extends AbstractController
// - Update flags with parameters set in URL
FormHelper::UpdateFlagsFromContext($oObjToClone, $aFormExtraParams);
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aFormExtraParams['js_handlers'] = [];
$aFormExtraParams['noRelations'] = true;
$aFormExtraParams['hide_transitions'] = true;
@@ -187,9 +208,9 @@ JS;
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObjToClone, array(), $aFormExtraParams);
} else {
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oClassForm = cmdbAbstractObject::DisplayFormBlockSelectClassToCreate($sClass, MetaModel::GetName($sClass), $oAppContext, $aPossibleClasses, ['state' => $sStateCode]);
$sCurrentUrl = $oRouter->GenerateUrl('object.new');
$sCurrentUrl = $this->oUrlGenerator->generate('b_object_new');
$oClassForm->SetOnSubmitJsCode(
<<<JS
let me = this;
@@ -208,25 +229,35 @@ JS
cmdbAbstractObject::DisplaySelectClassToCreate($sClass, $oPage, $oAppContext, $aPossibleClasses,['state' => $sStateCode]);
}
}
return $oPage;
return $oPage->GenerateResponse();
}
/**
* @return iTopWebPage|AjaxPage Object edit form in its webpage
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param string $sClass Class of the datamodel object to modify
* @param string $sId ID of the object to modify
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \SecurityException
* @throws \Exception
*/
public function OperationModify()
#[Route('/modify/{sClass}/{sId}', name: 'modify')]
public function OperationModify(Request $oRequest, string $sClass, string $sId): Response
{
// Retrieve and check parameters
$bPrintable = utils::ReadParam('printable', '0') === '1';
$sClass = utils::ReadParam('class', '', false, 'class');
$sId = utils::ReadParam('id', '');
$sFormTitle = utils::ReadPostedParam('form_title', null, utils::ENUM_SANITIZATION_FILTER_STRING);
// Check parameters
if (false === MetaModel::IsValidClass($sClass)) {
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
if (utils::IsNullOrEmptyString($sClass) || utils::IsNullOrEmptyString($sId))
{
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id'));
@@ -254,7 +285,7 @@ JS
$aFormExtraParams['form_title'] = $sFormTitle;
}
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage = new AjaxPage('');
$aFormExtraParams['js_handlers'] = [];
$aFormExtraParams['noRelations'] = true;
@@ -321,21 +352,25 @@ JS;
// Note: Code duplicated to the case 'apply_modify' in UI.php when a data integrity issue has been found
$oObj->DisplayModifyForm($oPage, $aFormExtraParams); // wizard_container: Display the title above the form
return $oPage;
return $oPage->GenerateResponse();
}
/**
* @return iTopWebPage|JsonPage Object edit form in its webpage
* @param \Symfony\Component\HttpFoundation\Request $oRequest
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \SecurityException
*/
public function OperationApplyNew()
#[Route('/apply_new', name: 'apply_new')]
public function OperationApplyNew(Request $oRequest): Response
{
$bPrintable = utils::ReadParam('printable', '0') === '1';
$aResult = [];
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage = new JsonPage();
$oPage->SetOutputDataOnly(true);
$aResult['success'] = false;
@@ -367,7 +402,7 @@ JS;
$sUser = UserRights::GetUser();
IssueLog::Error(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyCreated')];
} else {
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:ObjectAlreadyCreated'));
@@ -464,7 +499,7 @@ JS;
}
} else {
// Nothing more to do
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['success'] = true;
$aResult['data'] = ['object' => ObjectRepository::ConvertObjectToArray($oObj, $sClass)];
} else {
@@ -476,7 +511,7 @@ JS;
// Found issues, explain and give the user a second chance
//
$aIssues = $e->getIssues();
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => $e->getHtmlMessage()];
} else {
$sClassLabel = MetaModel::GetName($sClass);
@@ -494,15 +529,18 @@ JS;
}
}
}
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage->SetData($aResult);
}
return $oPage;
return $oPage->GenerateResponse();
}
/**
* @return iTopWebPage|JsonPage
* @param \Symfony\Component\HttpFoundation\Request $oRequest
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \ConfigException
@@ -511,10 +549,12 @@ JS;
* @throws \DictExceptionMissingString
* @throws \MySQLException
*/
public function OperationApplyModify(){
public function OperationApplyModify(Request $oRequest): Response
{
$bPrintable = utils::ReadParam('printable', '0') === '1';
$aResult = [];
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage = new JsonPage();
$oPage->SetOutputDataOnly(true);
$aResult['success'] = false;
@@ -546,7 +586,7 @@ JS;
{
$bDisplayDetails = false;
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::S('UI:ObjectDoesNotExist')];
} else {
$oPage->set_title(Dict::S('UI:ErrorPageTitle'));
@@ -572,7 +612,7 @@ JS;
$sUser = UserRights::GetUser();
IssueLog::Error(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyUpdated')];
} else {
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
@@ -598,7 +638,7 @@ JS;
if (!$oObj->IsModified() && empty($aErrors))
{
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())];
} else {
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
@@ -646,7 +686,7 @@ JS;
$sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
$sSeverity = 'ok';
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['success'] = true;
}
}
@@ -656,7 +696,7 @@ JS;
//
$bDisplayDetails = false;
$aIssues = $e->getIssues();
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => $e->getHtmlMessage()];
} else {
$oPage->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
@@ -667,7 +707,7 @@ JS;
}
catch (DeleteException $e)
{
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$aResult['data'] = ['error_message' => Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())];
} else {
// Say two things:
@@ -706,7 +746,7 @@ JS;
// Nothing more to do
$sMessage = isset($sMessage) ? $sMessage : '';
$sSeverity = isset($sSeverity) ? $sSeverity : null;
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
;
} else {
ReloadAndDisplay($oPage, $oObj, 'update', $sMessage, $sSeverity);
@@ -724,27 +764,38 @@ JS;
}
}
}
if ($this->IsHandlingXmlHttpRequest()) {
if ($oRequest->isXmlHttpRequest()) {
$oPage->SetData($aResult);
}
return $oPage;
return $oPage->GenerateResponse();
}
public function OperationSummary() {
/**
* @param \Symfony\Component\HttpFoundation\Request $oRequest
* @param string $sClass Class of the datamodel object to view
* @param string $sId ID of the datamodel object to view (note that friendlyname can also be used but is less efficient)
*
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route('/summary/{sClass}/{sId}', name: 'summary')]
public function OperationSummary(Request $oRequest, string $sClass, string $sId): Response
{
$oPage = new AjaxPage('');
$sClass = utils::ReadParam('obj_class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
$sObjectKey = utils::ReadParam('obj_key', 0, false);
// Check parameters
if (false === MetaModel::IsValidClass($sClass)) {
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
// - Check if we are allowed to see/make summary for this class
if(SummaryCardService::IsAllowedForClass($sClass)){
if (is_numeric($sObjectKey))
if (is_numeric($sId))
{
$oObj = MetaModel::GetObject($sClass, $sObjectKey, false /* MustBeFound */);
$oObj = MetaModel::GetObject($sClass, $sId, false /* MustBeFound */);
}
else
{
$oObj = MetaModel::GetObjectByName($sClass, $sObjectKey, false /* MustBeFound */);
$oObj = MetaModel::GetObjectByName($sClass, $sId, false /* MustBeFound */);
}
if($oObj !== null) {
@@ -758,7 +809,7 @@ JS;
->SetIsClosable(false)
);
}
return $oPage;
return $oPage->GenerateResponse();
}
/**
@@ -771,7 +822,7 @@ JS;
protected function AddRequiredForModificationJsFilesToPage(iTopWebPage &$oPage): void
{
foreach (static::EnumRequiredForModificationJsFilesRelPaths() as $sJsFileRelPath) {
$oPage->add_linked_script("../$sJsFileRelPath");
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot() . "$sJsFileRelPath");
}
}

View File

@@ -6,10 +6,13 @@
namespace Combodo\iTop\Controller;
/**
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 3.1.0
* @package Combodo\iTop\Controller
*
* @deprecated 3.2.0 N°6935 As we now use Symfony controller and routing components, extend \Combodo\iTop\Controller\AbstractController instead
*/
interface iController
{

View File

@@ -6,17 +6,42 @@
namespace Combodo\iTop\Service\Router\Exception;
use DeprecatedCallsLog;
/**
* Class RouteNotFoundException
*
* Means that a said route (eg. "object.modify") could not be found
*
* @internal
*
* @deprecated 3.2.0 N°6935 As we now use Symfony routing component, use the corresponding exceptions instead
*
* Note that we can't call \DeprecatedCallsLog::NotifyDeprecatedFile() at the beginning at the file instead.
* Because
* - As the class is part of the autoloader it will be read when something calls \utils::GetClassesForInterface() which will pop the deprecation message (and break redirection)
* - Not all controllers using Combodo\iTop\Service\Router\Router service can be migrated yet for backward compatibility with extensions reasons
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Service\Router\Exception
* @since 3.1.0
* @internal
*/
class RouteNotFoundException extends RouterException
{
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
/**
* @deprecated 3.2.0 N°6935
*
* Note that we can't call \DeprecatedCallsLog::NotifyDeprecatedFile() at the beginning at the file instead.
* Because
* - As the class is part of the autoloader it will be read when something calls \utils::GetClassesForInterface() which will pop the deprecation message (and break redirection)
* - Not all controllers using Combodo\iTop\Service\Router\Router service can be migrated yet for backward compatibility with extensions reasons
*/
DeprecatedCallsLog::NotifyDeprecatedFile("As we now use Symfony routing component, use the corresponding exceptions instead");
parent::__construct($message, $code, $previous);
}
}

View File

@@ -6,19 +6,42 @@
namespace Combodo\iTop\Service\Router\Exception;
use DeprecatedCallsLog;
use Exception;
/**
* Class RouterException
*
* Base router exception class in case we need to catch all kind of router exceptions (see derived exceptions)
*
* @internal
*
* @deprecated 3.2.0 N°6935 As we now use Symfony routing component, use the corresponding exceptions instead
*
* Note that we can't call \DeprecatedCallsLog::NotifyDeprecatedFile() at the beginning at the file instead.
* Because
* - As the class is part of the autoloader it will be read when something calls \utils::GetClassesForInterface() which will pop the deprecation message (and break redirection)
* - Not all controllers using Combodo\iTop\Service\Router\Router service can be migrated yet for backward compatibility with extensions reasons
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Service\Router\Exception
* @since 3.1.0
* @internal
*/
class RouterException extends Exception
{
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
/**
* @deprecated 3.2.0 N°6935
*
* Note that we can't call \DeprecatedCallsLog::NotifyDeprecatedFile() at the beginning at the file instead.
* Because
* - As the class is part of the autoloader it will be read when something calls \utils::GetClassesForInterface() which will pop the deprecation message (and break redirection)
* - Not all controllers using Combodo\iTop\Service\Router\Router service can be migrated yet for backward compatibility with extensions reasons
*/
DeprecatedCallsLog::NotifyDeprecatedFile("As we now use Symfony routing component, use the corresponding exceptions instead");
parent::__construct($message, $code, $previous);
}
}

View File

@@ -7,20 +7,29 @@
namespace Combodo\iTop\Service\Router;
use Combodo\iTop\Service\Router\Exception\RouteNotFoundException;
use DeprecatedCallsLog;
use ReflectionClass;
use ReflectionMethod;
use utils;
use SetupUtils;
/**
* Class Router
*
* Service to find the corresponding controller / method for a given "route" parameter.
*
* @deprecated 3.2.0 N°6935 As we now use Symfony routing component, use the corresponding service instead
*
* Note that we can't call \DeprecatedCallsLog::NotifyDeprecatedFile() at the beginning at the file instead.
* Because
* - As the class is part of the autoloader it will be read when something calls \utils::GetClassesForInterface() which will pop the deprecation message (and break redirection)
* - Not all controllers using Combodo\iTop\Service\Router\Router service can be migrated yet for backward compatibility with extensions reasons
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop\Service\Router
* @since 3.1.0
* @api
*
* @package Combodo\iTop\Service\Router
*/
class Router
{
@@ -123,6 +132,9 @@ class Router
*/
public function DispatchRoute(string $sRoute)
{
/** @deprecated 3.2.0 N°6935 */
DeprecatedCallsLog::NotifyDeprecatedFile("As we now use Symfony routing component, use the corresponding service instead");
$aMethodSpecs = $this->GetDispatchSpecsForRoute($sRoute);
$mResponse = call_user_func_array([new $aMethodSpecs[0](), $aMethodSpecs[1]], []);
@@ -175,8 +187,15 @@ class Router
// If no cache, force to re-scan for routes
if (count($aRoutes) === 0) {
foreach (utils::GetClassesForInterface('Combodo\iTop\Controller\iController', '', ['[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]']) as $sControllerFQCN) {
$sRouteNamespace = $sControllerFQCN::ROUTE_NAMESPACE;
// Ignore controller with no namespace
// - No ROUTE_NAMESPACE constant
// This case could be for controller extending Combodo\iTop\Controller\AbstractController which is still implementing Combodo\iTop\Controller\iController for compatibility reasons
// but have already been migrated to Symfony router (when migrated, constant must be removed from the controller so it doesn't use the compatibility router)
if (false === defined("$sControllerFQCN::ROUTE_NAMESPACE")) {
continue;
}
$sRouteNamespace = $sControllerFQCN::ROUTE_NAMESPACE;
// - Namespace is empty
if (is_null($sRouteNamespace)) {
continue;
}

View File

@@ -8,9 +8,10 @@ namespace Combodo\iTop\Service\SummaryCard;
use appUserPreferences;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
use Combodo\iTop\Service\Router\Router;
use MetaModel;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use UserRights;
use utils;
/**
* Class SummaryCardService
@@ -19,10 +20,22 @@ use UserRights;
*
* @since 3.1.0
*/
class SummaryCardService {
class SummaryCardService
{
/**
* @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $oUrlGenerator
*
* @since 3.2.0 Add constructor and dependency injection of $oUrlGenerator
*/
public function __construct(
protected UrlGeneratorInterface $oUrlGenerator
)
{
}
/**
* @param $sObjClass
* @param string $sObjClass
* @param $sObjKey
*
* @return string
@@ -30,9 +43,9 @@ class SummaryCardService {
*/
public static function GetHyperlinkMarkup(string $sObjClass, $sObjKey): string
{
$oRouter = Router::GetInstance();
$sRoute = $oRouter->GenerateUrl("object.summary", ["obj_class" => $sObjClass, "obj_key" => $sObjKey]);
return
// Can't use URL Generator (`$this->oUrlGenerator->generate("b_object_summary", [$sObjClass, $sObjKey])`) yet as we have to find how to inject it here
$sRoute = utils::GetAbsoluteUrlAppRoot() . "app.php/object/summary/$sObjClass/$sObjKey";
return
<<<HTML
data-tooltip-content="$sRoute"
data-tooltip-interaction-enabled="true"

View File

@@ -3,7 +3,7 @@
{% apply spaceless %}
<div style="height:250px;width:100%" class="dashboard_chart" id="my_chart_{{ oUIBlock.sChartId }}{{ oUIBlock.iChartCounter }}">
<div style="height:200px;line-height:200px;vertical-align:center;text-align:center;width:100%">
<img src="../images/indicator.gif">
<img src="{{ get_absolute_url_app_root() }}images/indicator.gif">
</div>
</div>
{% endapply %}

View File

@@ -22,7 +22,7 @@ use Combodo\iTop\Application\WebPage\WebPage;
function LogResult($sString)
{
file_put_contents(APPROOT.'data/queries.results.log', "\n".$sString, FILE_APPEND);
file_put_contents(utils::GetDataPath().'queries.results.log', "\n".$sString, FILE_APPEND);
}
function LogBenchmarkCSV()
@@ -40,7 +40,7 @@ function LogBenchmarkCSV()
}
}
$sLine = implode(';', $aValues); // the preferred for MS Excel
file_put_contents(APPROOT.'data/queries.benchmark.csv', "\n".$sLine, FILE_APPEND);
file_put_contents(utils::GetDataPath().'queries.benchmark.csv', "\n".$sLine, FILE_APPEND);
}
class QueryLogEntry
@@ -311,8 +311,8 @@ case 'check':
case 'benchmark':
$oP->add("<h2>Create data/queries.xxx reports</h2>\n");
// Reset the log contents
file_put_contents(APPROOT.'data/queries.results.log', date('Y-m-d H:i:s')."\n");
file_put_contents(APPROOT.'data/queries.benchmark.csv', '');
file_put_contents(utils::GetDataPath().'queries.results.log', date('Y-m-d H:i:s')."\n");
file_put_contents(utils::GetDataPath().'queries.benchmark.csv', '');
LogBenchmarkCSV('type', 'properties', 'make duration', 'class', 'tables', 'query length', 'exec duration', 'rows', 'oql');
$iErrors = 0;

View File

@@ -36,7 +36,7 @@ class TestsRunStartHook implements BeforeFirstTestHook, AfterLastTestHook
{
// Note: This can't be put in the cache-<ENV> folder as we have multiple <ENV> running across the test cases
// We also don't want to put it in the unit tests folder as it is not supposed to be writable
return APPROOT.'data/.php-unit-tests-run-started';
return utils::GetDataPath().'.php-unit-tests-run-started';
}
/**