mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-25 21:34:12 +01:00
Compare commits
28 Commits
feature/87
...
feature/89
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024b842d33 | ||
|
|
0703469465 | ||
|
|
12bf16f790 | ||
|
|
3b62597092 | ||
|
|
c0a2771d4e | ||
|
|
6bd5a7b634 | ||
|
|
82b7ef86c7 | ||
|
|
4f878536a8 | ||
|
|
d937ec0350 | ||
|
|
985db46960 | ||
|
|
01adaadfad | ||
|
|
643752f8e7 | ||
|
|
0e0c09c420 | ||
|
|
f34373be6d | ||
|
|
4dba47798c | ||
|
|
49a7f3118d | ||
|
|
09afcb229c | ||
|
|
3df4ddc696 | ||
|
|
1fe401c102 | ||
|
|
6cb08ba361 | ||
|
|
f9db405343 | ||
|
|
4f1f144c51 | ||
|
|
ef8ade5d20 | ||
|
|
0b242d872a | ||
|
|
d9261b8342 | ||
|
|
4187f552a9 | ||
|
|
7f7ce0837e | ||
|
|
7df09541ac |
@@ -4259,6 +4259,9 @@ class AttributeText extends AttributeString
|
||||
|
||||
public static function RenderWikiHtml($sText, $bWikiOnly = false)
|
||||
{
|
||||
// N°8681 - Ensure to have a string value
|
||||
$sText = $sText ?? '';
|
||||
|
||||
if (!$bWikiOnly) {
|
||||
$sPattern = '/'.str_replace('/', '\/', utils::GetConfig()->Get('url_validation_pattern')).'/i';
|
||||
if (preg_match_all(
|
||||
|
||||
@@ -266,6 +266,9 @@ class InlineImage extends DBObject
|
||||
*/
|
||||
public static function FixUrls($sHtml)
|
||||
{
|
||||
// N°8681 - Ensure to have a string value
|
||||
$sHtml = $sHtml ?? '';
|
||||
|
||||
$aNeedles = [];
|
||||
$aReplacements = [];
|
||||
// Find img tags with an attribute data-img-id
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Core\Kpi\KpiLogData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*/
|
||||
|
||||
class ExecutionKPI
|
||||
class ExecutionKPI implements iEventServiceSetup
|
||||
{
|
||||
protected static $m_bEnabled_Duration = false;
|
||||
protected static $m_bEnabled_Memory = false;
|
||||
@@ -23,15 +25,18 @@ class ExecutionKPI
|
||||
|
||||
protected static $m_aStats = []; // Recurrent operations
|
||||
protected static $m_aExecData = []; // One shot operations
|
||||
/**
|
||||
* @var array[ExecutionKPI]
|
||||
*/
|
||||
protected static $m_aExecutionStack = []; // embedded execution stats
|
||||
/** @var true */
|
||||
private static bool $bMetamodelStarted = false;
|
||||
|
||||
private static ?float $fLastReportTime = null;
|
||||
private static ?float $iLastReportMemory = null;
|
||||
|
||||
// For stats
|
||||
protected $m_fStarted = null;
|
||||
protected $m_fChildrenDuration = 0; // Count embedded
|
||||
protected $m_iInitialMemory = null;
|
||||
|
||||
private static array $aBootstrapOperations = [];
|
||||
|
||||
public static function EnableDuration($iLevel)
|
||||
{
|
||||
if ($iLevel > 0) {
|
||||
@@ -71,6 +76,7 @@ class ExecutionKPI
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,7 +103,7 @@ class ExecutionKPI
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
$sSlowQueries = '';
|
||||
if (self::$m_fSlowQueries > 0) {
|
||||
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
|
||||
$sSlowQueries = '. Slow Queries: '.self::$m_fSlowQueries.'s';
|
||||
}
|
||||
|
||||
$aExtensions = [];
|
||||
@@ -127,7 +133,7 @@ class ExecutionKPI
|
||||
$sRequest .= ' operation: '.$_POST['operation'];
|
||||
}
|
||||
|
||||
$fStop = MyHelpers::getmicrotime();
|
||||
$fStop = microtime(true);
|
||||
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
@@ -151,17 +157,17 @@ class ExecutionKPI
|
||||
|
||||
$sTableStyle = 'background-color: #ccc; margin: 10px;';
|
||||
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml = '<hr/>';
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
|
||||
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
|
||||
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
|
||||
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= '<p>log_kpi_user_id: '.UserRights::GetUserId().'</p>';
|
||||
$sHtml .= '<div>';
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>";
|
||||
$sHtml .= "</thead>";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation</th><th>Begin</th><th>End</th><th>Duration</th><th>Memory start</th><th>Memory end</th><th>Memory peak</th>';
|
||||
$sHtml .= '</thead>';
|
||||
foreach (self::$m_aExecData as $aOpStats) {
|
||||
$sOperation = $aOpStats['op'];
|
||||
$sBegin = round($aOpStats['time_begin'], 3);
|
||||
@@ -180,12 +186,12 @@ class ExecutionKPI
|
||||
}
|
||||
}
|
||||
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= " <td>$sOperation</td><td>$sBegin</td><td>$sEnd</td><td>$sDuration</td><td>$sMemBegin</td><td>$sMemEnd</td><td>$sMemPeak</td>";
|
||||
$sHtml .= "</tr>";
|
||||
$sHtml .= '</tr>';
|
||||
}
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$aConsolidatedStats = [];
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats) {
|
||||
@@ -208,20 +214,20 @@ class ExecutionKPI
|
||||
}
|
||||
}
|
||||
$aConsolidatedStats[$sOperation] = [
|
||||
'count' => $iTotalOp,
|
||||
'count' => $iTotalOp,
|
||||
'duration' => $fTotalOp,
|
||||
'min' => $fMinOp,
|
||||
'max' => $fMaxOp,
|
||||
'avg' => $fTotalOp / $iTotalOp,
|
||||
'min' => $fMinOp,
|
||||
'max' => $fMaxOp,
|
||||
'avg' => $fTotalOp / $iTotalOp,
|
||||
'max_args' => $sMaxOpArguments,
|
||||
];
|
||||
}
|
||||
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= '<div>';
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>";
|
||||
$sHtml .= "</thead>";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th><th>Avg</th>';
|
||||
$sHtml .= '</thead>';
|
||||
foreach ($aConsolidatedStats as $sOperation => $aOpStats) {
|
||||
$sOperation = '<a href="#'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
$sCount = $aOpStats['count'];
|
||||
@@ -230,14 +236,14 @@ class ExecutionKPI
|
||||
$sMax = '<a href="#'.md5($sExecId.$aOpStats['max_args']).'">'.round($aOpStats['max'], 3).'</a>';
|
||||
$sAvg = round($aOpStats['avg'], 3);
|
||||
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= " <td>$sOperation</td><td>$sCount</td><td>$sDuration</td><td>$sMin</td><td>$sMax</td><td>$sAvg</td>";
|
||||
$sHtml .= "</tr>";
|
||||
$sHtml .= '</tr>';
|
||||
}
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</div>";
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= "</div>";
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= "<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>";
|
||||
|
||||
@@ -287,18 +293,18 @@ class ExecutionKPI
|
||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||
$sHtml .= "<h4>$sOperationHtml</h4>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
$sHtml .= "<thead>";
|
||||
$sHtml .= " <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>";
|
||||
$sHtml .= "</thead>";
|
||||
$sHtml .= '<thead>';
|
||||
$sHtml .= ' <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>';
|
||||
$sHtml .= '</thead>';
|
||||
$bDisplayHeader = false;
|
||||
}
|
||||
$sHtml .= "<tr>";
|
||||
$sHtml .= '<tr>';
|
||||
$sHtml .= " <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>";
|
||||
$sHtml .= "</tr>";
|
||||
$sHtml .= '</tr>';
|
||||
}
|
||||
}
|
||||
if (!$bDisplayHeader) {
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= '</table>';
|
||||
$sHtml .= "<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>";
|
||||
}
|
||||
self::Report($sHtml);
|
||||
@@ -333,39 +339,50 @@ class ExecutionKPI
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
$fStarted = $this->m_fStarted;
|
||||
$fStopped = $this->m_fStarted;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = [
|
||||
'op' => $sOperationDesc,
|
||||
'time_begin' => $this->m_fStarted - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
];
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_fStarted = $fStopped;
|
||||
if (is_null(static::$fLastReportTime)) {
|
||||
static::$fLastReportTime = $fItopStarted;
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (is_null(static::$iLastReportMemory)) {
|
||||
global $iItopInitialMemory;
|
||||
static::$iLastReportMemory = $iItopInitialMemory;
|
||||
}
|
||||
|
||||
$fStarted = static::$fLastReportTime;
|
||||
$fStopped = microtime(true);
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$aNewEntry = [
|
||||
'op' => $sOperationDesc,
|
||||
'time_begin' => $fStarted - $fItopStarted,
|
||||
'time_end' => $fStopped - $fItopStarted,
|
||||
];
|
||||
}
|
||||
static::$fLastReportTime = $fStopped;
|
||||
|
||||
$iInitialMemory = static::$iLastReportMemory;
|
||||
$iCurrentMemory = $iInitialMemory;
|
||||
$iPeakMemory = $iInitialMemory;
|
||||
if (self::$m_bEnabled_Memory) {
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
if (is_null($aNewEntry)) {
|
||||
$aNewEntry = ['op' => $sOperationDesc];
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_begin'] = $iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
static::$iLastReportMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$aCallstack = ['callstack' => $this->GetCallStack()];
|
||||
if (static::$bMetamodelStarted) {
|
||||
foreach (static::$aBootstrapOperations as $oLog) {
|
||||
$this->LogOperation($oLog);
|
||||
}
|
||||
static::$aBootstrapOperations = [];
|
||||
// Invoke extensions to log the KPI operation
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
@@ -376,9 +393,24 @@ class ExecutionKPI
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
$this->LogOperation($oKPILogData);
|
||||
} else {
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
'Step',
|
||||
$sOperationDesc,
|
||||
$fStarted,
|
||||
$fStopped,
|
||||
'',
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
static::$aBootstrapOperations[] = $oKPILogData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,13 +420,21 @@ class ExecutionKPI
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
private function LogOperation(KpiLogData $oKPILogData): void
|
||||
{
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute statistics for a call to an extension
|
||||
* Note: not working in dev mode (with links to env-production)
|
||||
*
|
||||
* @param object|string $object object called
|
||||
* @param string $sMethod method called on the object
|
||||
* @param string $sMessage additional message
|
||||
* @param string $sMethod method called on the object
|
||||
* @param string $sMessage additional message
|
||||
*
|
||||
* @return bool true if an extension was found for this object::method
|
||||
* @throws \ReflectionException
|
||||
@@ -423,21 +463,23 @@ class ExecutionKPI
|
||||
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fStopped = microtime(true);
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$aCallstack = [];
|
||||
if (self::$m_bGenerateLegacyReport) {
|
||||
if (self::$m_bBlameCaller) {
|
||||
$aCallstack = MyHelpers::get_callstack(1);
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
];
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'time' => $fDuration,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$aCallstack = ['callstack' => $this->GetCallStack()];
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
@@ -448,33 +490,45 @@ class ExecutionKPI
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
}
|
||||
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$sExtension = '';
|
||||
if (static::$bMetamodelStarted) {
|
||||
foreach (static::$aBootstrapOperations as $oLog) {
|
||||
$this->LogOperation($oLog);
|
||||
}
|
||||
static::$aBootstrapOperations = [];
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
$sExtension,
|
||||
'',
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
$this->LogOperation($oKPILogData);
|
||||
} else {
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
'',
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack
|
||||
);
|
||||
static::$aBootstrapOperations[] = $oKPILogData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
{
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$this->m_fStarted = microtime(true);
|
||||
}
|
||||
$this->m_fStarted = microtime(true);
|
||||
|
||||
if (self::$m_bEnabled_Memory) {
|
||||
$this->m_iInitialMemory = self::memory_get_usage();
|
||||
@@ -503,7 +557,33 @@ class ExecutionKPI
|
||||
if (function_exists('memory_get_peak_usage')) {
|
||||
return memory_get_peak_usage($bRealUsage);
|
||||
}
|
||||
|
||||
// PHP > 5.2.1 - this verb depends on a compilation option
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ModuleHandlerApiInterface methods
|
||||
*/
|
||||
|
||||
public static function OnMetaModelStarted()
|
||||
{
|
||||
static::$bMetamodelStarted = true;
|
||||
}
|
||||
|
||||
public function RegisterEventsAndListeners()
|
||||
{
|
||||
EventService::RegisterListener(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED, [$this, 'OnMetaModelStarted']);
|
||||
}
|
||||
|
||||
private function GetCallStack(): string
|
||||
{
|
||||
$aCallStack = MyHelpers::get_callstack(2);
|
||||
$sCallStack = "Call stack:\n";
|
||||
foreach ($aCallStack as $index => $aLine) {
|
||||
$sCallStack .= "#$index ".$aLine['File'].'('.$aLine['Line'].'): '.$aLine['Function']."\n";
|
||||
}
|
||||
|
||||
return $sCallStack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
$ibo-multi-column--margin-x: -16px !default; /* This is to compensate columns padding and make the whole multicolumn align with the parent borders (cf. Bootstrap rows / cols) */
|
||||
$ibo-multi-column--margin-y: $ibo-spacing-0 !default;
|
||||
$ibo-multi-column--row-gap: $ibo-spacing-800 !default;
|
||||
|
||||
.ibo-multi-column {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: $ibo-multi-column--margin-y $ibo-multi-column--margin-x;
|
||||
row-gap: $ibo-multi-column--row-gap;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<module_parameters>
|
||||
<parameters id="authent-local" _delta="define">
|
||||
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$</password_validation.pattern>
|
||||
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{12,}$</password_validation.pattern>
|
||||
<password_validation.message type="hash"/>
|
||||
</parameters>
|
||||
</module_parameters>
|
||||
|
||||
@@ -29,7 +29,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Heslo nemůže uživatel změnit.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Heslo bylo obnoveno',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Termín, kdy bylo heslo změneno',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 8 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 12 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
|
||||
'UserLocal:password:expiration' => 'Níže uvedená pole vyžadují rozšíření',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Nastavení exspirace "Jednorázového hesla" nelze u vlastního účtu uživatele.',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort entspricht nicht dem in den Konfigurationsregeln hinterlegten RegEx-Ausdruck',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort muss mindestens 12 Zeichen lang sein und Großbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten.',
|
||||
'UserLocal:password:expiration' => 'Die folgenden Felder benötigen eine '.ITOP_APPLICATION_SHORT.' Erweiterung',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Das setzen des Passwortablaufs auf "Einmalpasswort" ist für den eigenen Benutzer nicht erlaubt.',
|
||||
]);
|
||||
|
||||
@@ -55,7 +55,7 @@ Dict::Add('EN US', 'English', 'English', [
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
|
||||
]);
|
||||
|
||||
@@ -55,7 +55,7 @@ Dict::Add('EN GB', 'British English', 'British English', [
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
|
||||
]);
|
||||
|
||||
@@ -25,7 +25,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'El usuario no puede cambiar la contraseña.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 12 caracteres e incluir mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Configurar expiración de contraseña para "ontraseña de un solo uso" no está permitido para su propio Usuario',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 12 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
||||
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'A felhasználó nem változtathat jelszót.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'A jelszó legutóbbi módosításának időpontja',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 8 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 12 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
|
||||
'UserLocal:password:expiration' => 'Az alábbi mezőkhöz egy bővítmény szükséges',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'A jelszó lejárati idejének beállítása "Egyszeri jelszóra" nem engedélyezett a saját Felhasználó számára.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'La password non può essere cambiata dall\'utente.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Rinnovo della password',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Quando è stata cambiata l\'ultima volta la password',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 8 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 12 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
|
||||
'UserLocal:password:expiration' => 'I campi sottostanti richiedono un\'estensione',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impostare la scadenza della password su "Password monouso" non è consentito per il proprio utente',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'De gebruiker kan dit wachtwoord niet veranderen.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Wachtwoord laatst aangepast',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Tijdstip waarop het wachtwoord het laatst aangepast werd.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 8 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 12 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
|
||||
'UserLocal:password:expiration' => 'De velden hieronder vereisen een extensie.',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Je kan geen eenmalig wachtwoord instellen voor je eigen gebruiker.',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Hasło nie może być zmienione przez użytkownika.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Odnowienie hasła',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Kiedy ostatnio zmieniano hasło',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 8 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 12 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
|
||||
'UserLocal:password:expiration' => 'Poniższe pola wymagają rozszerzenia',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Ustawienie wygaśnięcia hasła "Hasło jednorazowe" nie jest dozwolone dla własnego użytkownika',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Senha não pode ser alterada pelo usuário',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Data da última alteração de senha',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Quando a senha foi alterada anteriormente',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A senha deve ter no mínimo 8 caracteres e incluir letras maiúsculas, minúsculas, números e símbolos',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A senha deve ter no mínimo 12 caracteres e incluir letras maiúsculas, minúsculas, números e símbolos',
|
||||
'UserLocal:password:expiration' => 'O campo abaixo requer uma extensão',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Definir a expiração da senha para One-Time Password (OTP) não é permitido para o seu próprio usuário',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Дата изменения пароля',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Когда пароль был изменен в последний раз',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 8 символов и включать прописные, строчные, числовые и специальные символы.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 12 символов и включать прописные, строчные, числовые и специальные символы.',
|
||||
'UserLocal:password:expiration' => 'Поля требуют наличия доп. расширения',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -28,7 +28,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
]);
|
||||
|
||||
@@ -51,7 +51,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '用户不允许修改密码.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => '密码更新',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => '上次修改密码的时间',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少8个字符, 包含大小写, 数字和特殊字符.',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少12个字符, 包含大小写, 数字和特殊字符.',
|
||||
'UserLocal:password:expiration' => '下面的区域需要插件扩展',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => '不允许用户为自己设置 "一次性密码" 的失效期限',
|
||||
]);
|
||||
|
||||
@@ -19,7 +19,7 @@ Dict::Add('FR FR', 'French', 'Français', [
|
||||
'Class:FAQ/Attribute:summary+' => '',
|
||||
'Class:FAQ/Attribute:description' => 'Description',
|
||||
'Class:FAQ/Attribute:description+' => '',
|
||||
'Class:FAQ/Attribute:category_id' => 'Categorie',
|
||||
'Class:FAQ/Attribute:category_id' => 'Catégorie',
|
||||
'Class:FAQ/Attribute:category_id+' => '',
|
||||
'Class:FAQ/Attribute:category_name' => 'Nom catégorie',
|
||||
'Class:FAQ/Attribute:category_name+' => '',
|
||||
|
||||
@@ -238,6 +238,11 @@
|
||||
<action id="action:bulk delete">allow</action>
|
||||
</actions>
|
||||
</group>
|
||||
<group id="Ticketing">
|
||||
<actions>
|
||||
<action id="stimulus:ev_close">allow</action>
|
||||
</actions>
|
||||
</group>
|
||||
<group id="UserRequest">
|
||||
<actions>
|
||||
<action id="stimulus:ev_approve">allow</action>
|
||||
@@ -254,8 +259,10 @@
|
||||
<group id="Incident">
|
||||
<actions>
|
||||
<action id="stimulus:ev_assign">allow</action>
|
||||
<action id="stimulus:ev_dispatch">allow</action>
|
||||
<action id="stimulus:ev_reassign">allow</action>
|
||||
<action id="stimulus:ev_resolve">allow</action>
|
||||
<action id="stimulus:ev_reopen">allow</action>
|
||||
<action id="stimulus:ev_close">allow</action>
|
||||
<action id="stimulus:ev_pending">allow</action>
|
||||
</actions>
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace Combodo\iTop\Core\Kpi;
|
||||
|
||||
class KpiLogData
|
||||
{
|
||||
public const TYPE_REPORT = 'report';
|
||||
public const TYPE_STATS = 'stats';
|
||||
public const TYPE_REPORT = 'report';
|
||||
public const TYPE_STATS = 'stats';
|
||||
public const TYPE_REQUEST = 'request';
|
||||
|
||||
/** @var string */
|
||||
@@ -33,6 +33,8 @@ class KpiLogData
|
||||
public $iPeakMemory;
|
||||
/** @var array */
|
||||
public $aData;
|
||||
// Computed
|
||||
public string $sDuration;
|
||||
|
||||
/**
|
||||
* @param string $sType
|
||||
@@ -43,9 +45,10 @@ class KpiLogData
|
||||
* @param string $sExtension
|
||||
* @param int $iInitialMemory
|
||||
* @param int $iCurrentMemory
|
||||
* @param int $iPeakMemory
|
||||
* @param array $aData
|
||||
*/
|
||||
public function __construct($sType, $sOperation, $sArguments, $fStartTime, $fStopTime, $sExtension, $iInitialMemory = 0, $iCurrentMemory = 0, $iPeakMemory = 0, $aData = [])
|
||||
public function __construct($sType, $sOperation, $sArguments, float $fStartTime, float $fStopTime, $sExtension, $iInitialMemory = 0, $iCurrentMemory = 0, $iPeakMemory = 0, $aData = [])
|
||||
{
|
||||
$this->sType = $sType;
|
||||
$this->sOperation = $sOperation;
|
||||
@@ -57,6 +60,7 @@ class KpiLogData
|
||||
$this->iCurrentMemory = $iCurrentMemory;
|
||||
$this->iPeakMemory = $iPeakMemory;
|
||||
$this->aData = $aData;
|
||||
$this->sDuration = sprintf('%01.3f', $fStopTime - $fStartTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,21 +70,22 @@ class KpiLogData
|
||||
*/
|
||||
public static function GetCSVHeader()
|
||||
{
|
||||
return "Type,Operation,Arguments,StartTime,StopTime,Duration,Extension,InitialMemory,CurrentMemory,PeakMemory";
|
||||
return 'Type,Operation,Arguments,StartTime,StopTime,Duration,Extension,InitialMemory,CurrentMemory,PeakMemory';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CSV line for the values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetCSV()
|
||||
{
|
||||
$fDuration = sprintf('%01.4f', $this->fStopTime - $this->fStartTime);
|
||||
$sType = $this->RemoveQuotes($this->sType);
|
||||
$sOperation = $this->RemoveQuotes($this->sOperation);
|
||||
$sArguments = $this->RemoveQuotes($this->sArguments);
|
||||
$sExtension = $this->RemoveQuotes($this->sExtension);
|
||||
return "\"$sType\",\"$sOperation\",\"$sArguments\",$this->fStartTime,$this->fStopTime,$fDuration,\"$sExtension\",$this->iInitialMemory,$this->iCurrentMemory,$this->iPeakMemory";
|
||||
|
||||
return "\"$sType\",\"$sOperation\",\"$sArguments\",$this->fStartTime,$this->fStopTime,$this->sDuration,\"$sExtension\",$this->iInitialMemory,$this->iCurrentMemory,$this->iPeakMemory";
|
||||
}
|
||||
|
||||
private function RemoveQuotes(string $sEntry): string
|
||||
@@ -98,6 +103,7 @@ class KpiLogData
|
||||
if ($oOther->fStartTime > $this->fStartTime) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[infra]
|
||||
; STS version : testing greatest PHP version possible
|
||||
php_version=8.2-apache
|
||||
php_version=8.3-apache
|
||||
; N°6629 perf bug on some tests on mariadb for now, so specifying MySQL
|
||||
db_version=5.7
|
||||
db_version=latest-mariadb
|
||||
|
||||
[itop]
|
||||
itop_setup=tests/setup_params/default-params.xml
|
||||
|
||||
@@ -13,13 +13,12 @@ $config = new PhpCsFixer\Config();
|
||||
return $config->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'indentation_type' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'concat_space' => true,
|
||||
'trailing_comma_in_multiline' => true,
|
||||
'no_extra_blank_lines' => true, // default value ['tokens' => ['extra']]
|
||||
'array_syntax' => true, // default value ['syntax' => 'short']
|
||||
'concat_space' => true, // default value ['spacing' => 'none']
|
||||
'trailing_comma_in_multiline' => true, // default value ['after_heredoc' => false, 'elements' => ['arrays']]
|
||||
])
|
||||
->setIndent("\t")
|
||||
->setLineEnding("\n")
|
||||
->setFinder($finder)
|
||||
;
|
||||
;
|
||||
|
||||
129
tests/php-static-analysis/README.md
Normal file
129
tests/php-static-analysis/README.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# PHP static analysis
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usages](#usages)
|
||||
- [Analysing a package](#analysing-a-package)
|
||||
- [Analysing a module](#analysing-a-module)
|
||||
- [Configuration](#configuration)
|
||||
- [Adjust local configuration to your needs](#adjust-local-configuration-to-your-needs)
|
||||
- [Adjust configuration for a particular CI repository / job](#adjust-configuration-for-a-particular-ci-repository--job)
|
||||
|
||||
## Installation
|
||||
- Install dependencies by running `composer install` in this folder
|
||||
- You should be all set! 🚀
|
||||
|
||||
## Usages
|
||||
### Analysing a package
|
||||
_Do this if you want to analyse the whole iTop package (iTop core, extensions, third-party libs, ...)_
|
||||
|
||||
- Make sure you ran a setup on your iTop as it will analyse the `env-production` folder
|
||||
- Open a prompt in your iTop folder
|
||||
- Run the following command
|
||||
```bash
|
||||
tests/php-static-analysis/vendor/bin/phpstan analyse \
|
||||
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
|
||||
--error-format raw
|
||||
```
|
||||
|
||||
You will then have an output like this listing all errors:
|
||||
```bash
|
||||
tests/php-static-analysis/vendor/bin/phpstan analyse \
|
||||
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
|
||||
--error-format raw
|
||||
|
||||
1049/1049 [============================] 100%
|
||||
|
||||
<ITOP>\addons\userrights\userrightsprofile.class.inc.php:552:Call to static method InitSharedClassProperties() on an unknown class SharedObject.
|
||||
<ITOP>\addons\userrights\userrightsprofile.db.class.inc.php:927:Call to static method GetSharedClassProperties() on an unknown class SharedObject.
|
||||
<ITOP>\addons\userrights\userrightsprojection.class.inc.php:722:Access to an undefined property UserRightsProjection::$m_aClassProjs.
|
||||
<ITOP>\application\applicationextension.inc.php:295:Method AbstractPreferencesExtension::ApplyPreferences() should return bool but return statement is missing.
|
||||
<ITOP>\application\cmdbabstract.class.inc.php:1010:Class utils referenced with incorrect case: Utils.
|
||||
[...]
|
||||
```
|
||||
|
||||
### Analysing a module
|
||||
_Do this if you only want to analyse one or more modules within this iTop but not the whole package_
|
||||
|
||||
- Make sure you ran a setup on your iTop as it will analyse the `env-production` folder
|
||||
- Open a prompt in your iTop folder
|
||||
- Run the following command
|
||||
```
|
||||
tests/php-static-analysis/vendor/bin/phpstan analyse \
|
||||
--configuration ./tests/php-static-analysis/config/for-module.dist.neon \
|
||||
--error-format raw \
|
||||
env-production/<MODULE_CODE_1> [env-production/<MODULE_CODE_2> ...]
|
||||
```
|
||||
|
||||
You will then have an output like this listing all errors:
|
||||
```
|
||||
tests/php-static-analysis/vendor/bin/phpstan analyse \
|
||||
--configuration ./tests/php-static-analysis/config/for-module.dist.neon \
|
||||
--error-format raw \
|
||||
env-production/authent-ldap env-production/itop-oauth-client
|
||||
|
||||
49/49 [============================] 100%
|
||||
|
||||
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:79:Undefined variable: $hDS
|
||||
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:80:Undefined variable: $name
|
||||
<ITOP>\env-production\authent-ldap\model.authent-ldap.php:80:Undefined variable: $value
|
||||
<ITOP>\env-production\itop-oauth-client\vendor\composer\InstalledVersions.php:105:Parameter $parser of method Composer\InstalledVersions::satisfies() has invalid type Composer\Semver\VersionParser.
|
||||
[...]
|
||||
```
|
||||
|
||||
## Configuration
|
||||
### Adjust local configuration to your needs
|
||||
#### Define which PHP version to run the analysis for
|
||||
The way we configured PHPStan in this project changes how it will find the PHP version to run the analysis for. \
|
||||
By default PHPStan check the information from the composer.json file, but we changed that (via the `config/php-includes/set-php-version-from-process.php` include) so it used the PHP
|
||||
version currently ran by the CLI.
|
||||
|
||||
So all you have to do is either:
|
||||
- Prepend your command line with the path of the executable of the desired PHP version
|
||||
- Change the default PHP interpreter in your IDE settings
|
||||
|
||||
#### Change some parameters for a local run
|
||||
If you want to change some particular settings (eg. the memory limit, the rules level, ...) for a local run of the analysis you have 2 choices.
|
||||
|
||||
##### Method 1: CLI parameter
|
||||
For most parameters there is a good chance you can just add the parameter and its value in your command, which will override the one defined in the configuration file. \
|
||||
Below are some example, but your can find the complete reference [here](https://phpstan.org/user-guide/command-line-usage).
|
||||
|
||||
```bash
|
||||
--memory-limit 1G
|
||||
--level 5
|
||||
--error-format raw
|
||||
[...]
|
||||
```
|
||||
|
||||
**Pros** Quick and easy to try different parameters \
|
||||
**Cons** Parameters aren't saved, so you'll have to remember them and put them again next time
|
||||
|
||||
##### Method 2: Configuration file
|
||||
Crafting your own configuration file gives you the ability to fine tune any parameters, it's way more powerful but can also quickly lead to crashes if you mess with the symbols discovery (classes, ...). \
|
||||
But mostly it can be saved, shared, re-used; which is it's main purpose.
|
||||
|
||||
It is recommended that you create your configuration file from scratch and that you include the `base.dist.neon` so you are bootstrapped for the symbols discovery. Then you can override any parameter. \
|
||||
Check [the documentation](https://phpstan.org/config-reference#multiple-files) for more information.
|
||||
|
||||
```neon
|
||||
includes:
|
||||
- base.dist.neon
|
||||
|
||||
parameters:
|
||||
# Override parameters here
|
||||
```
|
||||
|
||||
#### Analyse only one (or some) folder(s) quicker
|
||||
It's pretty easy and good news you don't need to create a new configuration file or change an existing one. \
|
||||
Just adapt and use command lines from the [usages section](#usages) and add the folders you want to analyse at the end of the command, exactly like when analysing modules.
|
||||
|
||||
For example if you want to analyse just `<ITOP>/setup` and `<ITOP>/sources`, use something like:
|
||||
```
|
||||
tests/php-static-analysis/vendor/bin/phpstan analyse \
|
||||
--configuration ./tests/php-static-analysis/config/for-package.dist.neon \
|
||||
--error-format raw \
|
||||
setup sources
|
||||
```
|
||||
|
||||
### Adjust configuration for a particular CI repository / job
|
||||
TODO
|
||||
5
tests/php-static-analysis/composer.json
Normal file
5
tests/php-static-analysis/composer.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
72
tests/php-static-analysis/composer.lock
generated
Normal file
72
tests/php-static-analysis/composer.lock
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "cc6d7580a5e98236d68d8b91de9ddebb",
|
||||
"packages": [
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.33",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-05T10:24:31+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
29
tests/php-static-analysis/config/README.md
Normal file
29
tests/php-static-analysis/config/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
## Disclaimer
|
||||
DON'T modify the following files without knowledge and discussing with the team:
|
||||
- base.dist.neon
|
||||
- for-package.dist.neon
|
||||
- for-module.dist.neon
|
||||
|
||||
## Purpose of these files
|
||||
### base.dist.neon
|
||||
This configuration file contains the common parameters for all analysis, whereas it is a package, a module or something specific. Among others:
|
||||
- Rules level for analysis
|
||||
- PHP version to compare
|
||||
- Necessary files for autoloaders discovery and such
|
||||
- ...
|
||||
|
||||
This file should not be modified for your specific needs, you should always include it and override the desired parameters. \
|
||||
See how it is done in `for-package.dist.neon` and `for-module.dist.neon` or on the documentation [here](https://phpstan.org/config-reference#multiple-files).
|
||||
|
||||
### for-package.dist.neon
|
||||
This configuration file contains the parameters to analyse a package (iTop core, modules, third-party libs).
|
||||
|
||||
### for-module.dist.neon
|
||||
This configuration file contains the parameters to analyse one or more modules only.
|
||||
|
||||
## How / when can I modify these files?
|
||||
**You CAN'T!** \
|
||||
Well, unless there is a good reason and you talked about it with the team. But you should never modify them for a specific need on your local environment.
|
||||
|
||||
- If you have a particular need for your local environment (eg. increase memory limit, change rules levels, analyse only a specific folder), check the [Configuration section](../#configuration) of the main README.md.
|
||||
- If you feel like there is need for an adjustment in the default configurations, discuss it with th team and make a PR.
|
||||
40
tests/php-static-analysis/config/base.dist.neon
Normal file
40
tests/php-static-analysis/config/base.dist.neon
Normal file
@@ -0,0 +1,40 @@
|
||||
includes:
|
||||
- php-includes/set-php-version-from-process.php # Workaround to set PHP version to the on running the CLI
|
||||
# for an explanation of the baseline concept, see: https://phpstan.org/user-guide/baseline
|
||||
#baseline HERE DO NOT REMOVE FOR CI
|
||||
|
||||
parameters:
|
||||
level: 0
|
||||
#phpVersion: null # Explicitly commented as we rather use the detected version from the above include (`php-includes/target-php-version.php`)
|
||||
editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' # Open in PHPStorm as it's Combodo's default IDE
|
||||
bootstrapFiles:
|
||||
- ../../../approot.inc.php
|
||||
- ../../../bootstrap.inc.php
|
||||
|
||||
scanFiles:
|
||||
# Files necessary as they contain some declarations (constants, classes, functions, ...)
|
||||
- ../../../approot.inc.php
|
||||
- ../../../bootstrap.inc.php
|
||||
excludePaths:
|
||||
analyse:
|
||||
# For third-party libs we should analyse them in a dedicated configuration as we can't improve / clean them which would
|
||||
# prevent us from raising the rules level as we improve / clean our codebase
|
||||
- ../../../lib # Irrelevant as we only want to analyze our codebase
|
||||
- ../../../node_modules # Irrelevant as we only want to analyze our codebase
|
||||
analyseAndScan:
|
||||
# This file generates "unignorable errors" for the baseline due to its format, so we don't have any other choice than to exclude it.
|
||||
# But mind that it will prevent PHPStan from warning us about PHP syntax errors in this file.
|
||||
- ../../../core/oql/build/PHP/Lempar.php
|
||||
|
||||
#- ../../../data # Left and commented on purpose to show that we want to analyse the generated cache files
|
||||
|
||||
# Note 1: We can't analyse these folders as if a PHP file requires another PHP element declared in an XML file, it won't find it. So we rely only on `env-production`
|
||||
# Note 2: Only the options selected during the setup will be analysed correctly in `env-production`. For unselected options, we still want to ignore them during the analysis as they would only give a false sentiment of security as their XML PHP classes / snippets / etc would not be tested.
|
||||
- ../../../data/production-modules (?) # Irrelevent as it will already be in `env-production` (for local run only, not useful in the CI)
|
||||
- ../../../datamodels # Irrelevent as it will already be in `env-production`
|
||||
- ../../../extensions # Irrelevent as it will already be in `env-production` (for local run only, not useful in the CI)
|
||||
- ../../../env-php-unit-tests (?) # Irrelevant as it will either already be in `env-production` or might be desynchronized from `env-production`
|
||||
- ../../../env-toolkit (?) # Irrelevent as it will either already be in `env-production` or might be desynchronized from `env-production` (for local run only, not useful in the CI)
|
||||
|
||||
- ../../../tests (?) # Exclude tests for now
|
||||
- ../../../toolkit (?) # Exlclude toolkit for now
|
||||
15
tests/php-static-analysis/config/for-module.dist.neon
Normal file
15
tests/php-static-analysis/config/for-module.dist.neon
Normal file
@@ -0,0 +1,15 @@
|
||||
includes:
|
||||
- base.dist.neon
|
||||
|
||||
parameters:
|
||||
paths:
|
||||
# We just want to analyse the module folder(s), either:
|
||||
# - Create your own `for-module.neon` file, include this one and override this parameter (see https://phpstan.org/config-reference#multiple-files)
|
||||
# - Pass the module folder(s) in the commande line (see https://phpstan.org/config-reference#analysed-files)
|
||||
scanDirectories:
|
||||
# Unlike for `for-package.dist.neon`, here we need to scan all the folders to discover symbols, but we only want to analyse the module folder.
|
||||
# We initially thought of doing it through the `excludePaths` param. by excluding everything but the module folder, but it doesn't seem to be possible, because it uses the `fnmatch()` function.
|
||||
# As a workaround, we list here all the folders to scan.
|
||||
#
|
||||
# Scan the whole project and rely on the `excludePaths` param. to filter the unnecessary
|
||||
- ../../..
|
||||
7
tests/php-static-analysis/config/for-package.dist.neon
Normal file
7
tests/php-static-analysis/config/for-package.dist.neon
Normal file
@@ -0,0 +1,7 @@
|
||||
includes:
|
||||
- base.dist.neon
|
||||
|
||||
parameters:
|
||||
paths:
|
||||
# We want to analyse almost the whole project, so we do a negative selection between the `paths` and `excludePaths` (see base.dist.neon) parameters
|
||||
- ../../../
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is only here to allow setting a specific PHP version to run the analysis for without
|
||||
* having to explicitly set it in the .neon file. This is the best way we found so far.
|
||||
*
|
||||
* @link https://phpstan.org/config-reference#phpversion
|
||||
*
|
||||
* Usage: Uses the CLI PHP version by default, which would work fine for
|
||||
* - The CI as the docker image has the target PHP version in both CLI and web
|
||||
* - The developer's IDE as PHPStorm also has a default PHP version configured which can be changed on the fly
|
||||
*/
|
||||
|
||||
// Default PHP version to analyse is the one running in CLI
|
||||
$config = [];
|
||||
$config['parameters']['phpVersion'] = PHP_VERSION_ID;
|
||||
|
||||
return $config;
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
Documentation on creating and maintaining tests in iTop.
|
||||
|
||||
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### PHPUnit configuration file
|
||||
@@ -78,7 +75,8 @@ Example :
|
||||
$oTagData->DBDelete();
|
||||
```
|
||||
|
||||
Warning : when the condition is met the test is finished and following code will be ignored !
|
||||
> [!WARNING]
|
||||
> When the condition is met the test is finished and following code will be ignored !
|
||||
|
||||
Another way to do is using try/catch blocks, for example :
|
||||
```php
|
||||
|
||||
@@ -28,14 +28,14 @@ class AjaxPageTest extends ItopDataTestCase
|
||||
$iLastCompilation = filemtime(APPROOT.'env-production');
|
||||
|
||||
// When
|
||||
$sOutput = $this->CallItopUrl(
|
||||
"/pages/exec.php?exec_module=itop-hub-connector&exec_page=ajax.php",
|
||||
$sOutput = $this->CallItopUri(
|
||||
"pages/exec.php?exec_module=itop-hub-connector&exec_page=ajax.php",
|
||||
[
|
||||
'auth_user' => $sLogin,
|
||||
'auth_pwd' => self::AUTHENTICATION_PASSWORD,
|
||||
'operation' => "compile",
|
||||
'authent' => self::AUTHENTICATION_TOKEN,
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
// Then
|
||||
@@ -53,26 +53,4 @@ class AjaxPageTest extends ItopDataTestCase
|
||||
clearstatcache();
|
||||
$this->assertGreaterThan($iLastCompilation, filemtime(APPROOT.'env-production'), 'The env-production directory should have been rebuilt');
|
||||
}
|
||||
|
||||
protected function CallItopUrl($sUri, ?array $aPostFields = null, bool $bXDebugEnabled = false)
|
||||
{
|
||||
$ch = curl_init();
|
||||
if ($bXDebugEnabled) {
|
||||
curl_setopt($ch, CURLOPT_COOKIE, 'XDEBUG_SESSION=phpstorm');
|
||||
}
|
||||
|
||||
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
|
||||
var_dump($sUrl);
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
$sOutput = curl_exec($ch);
|
||||
//echo "$sUrl error code:".curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $sOutput;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2013-2024 Combodo SAS
|
||||
*
|
||||
@@ -24,19 +25,15 @@
|
||||
require_once('../../../approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
|
||||
$sEnvironment = MetaModel::GetEnvironmentId();
|
||||
$aEntries = array();
|
||||
$aEntries = [];
|
||||
$aCacheUserData = apc_cache_info_compat();
|
||||
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
|
||||
{
|
||||
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list'])) {
|
||||
$sPrefix = 'itop-'.$sEnvironment.'-query-cache-';
|
||||
|
||||
foreach($aCacheUserData['cache_list'] as $i => $aEntry)
|
||||
{
|
||||
foreach ($aCacheUserData['cache_list'] as $i => $aEntry) {
|
||||
$sEntryKey = array_key_exists('info', $aEntry) ? $aEntry['info'] : $aEntry['key'];
|
||||
if (strpos($sEntryKey, $sPrefix) === 0)
|
||||
{
|
||||
if (strpos($sEntryKey, $sPrefix) === 0) {
|
||||
$aEntries[] = $sEntryKey;
|
||||
}
|
||||
}
|
||||
@@ -44,52 +41,39 @@ if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
|
||||
|
||||
echo "<pre>";
|
||||
|
||||
if (empty($aEntries))
|
||||
{
|
||||
if (empty($aEntries)) {
|
||||
echo "No Data";
|
||||
return;
|
||||
}
|
||||
|
||||
$sKey = $aEntries[0];
|
||||
$result = apc_fetch($sKey);
|
||||
if (!is_object($result))
|
||||
{
|
||||
if (!is_object($result)) {
|
||||
return;
|
||||
}
|
||||
$oSQLQuery = $result;
|
||||
|
||||
echo "NB Tables before;NB Tables after;";
|
||||
foreach($oSQLQuery->m_aContextData as $sField => $oValue)
|
||||
{
|
||||
foreach ($oSQLQuery->m_aContextData as $sField => $oValue) {
|
||||
echo $sField.';';
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
sort($aEntries);
|
||||
|
||||
foreach($aEntries as $sKey)
|
||||
{
|
||||
foreach ($aEntries as $sKey) {
|
||||
$result = apc_fetch($sKey);
|
||||
if (is_object($result))
|
||||
{
|
||||
if (is_object($result)) {
|
||||
$oSQLQuery = $result;
|
||||
if (isset($oSQLQuery->m_aContextData))
|
||||
{
|
||||
if (isset($oSQLQuery->m_aContextData)) {
|
||||
echo $oSQLQuery->m_iOriginalTableCount.";".$oSQLQuery->CountTables().';';
|
||||
foreach($oSQLQuery->m_aContextData as $oValue)
|
||||
{
|
||||
if (is_array($oValue))
|
||||
{
|
||||
foreach ($oSQLQuery->m_aContextData as $oValue) {
|
||||
if (is_array($oValue)) {
|
||||
$sVal = json_encode($oValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (empty($oValue))
|
||||
{
|
||||
} else {
|
||||
if (empty($oValue)) {
|
||||
$sVal = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sVal = $oValue;
|
||||
}
|
||||
}
|
||||
@@ -101,4 +85,3 @@ foreach($aEntries as $sKey)
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use ArchivedObjectException;
|
||||
use CMDBObject;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
use Config;
|
||||
use Contact;
|
||||
use DBObject;
|
||||
use DBObjectSet;
|
||||
@@ -65,6 +66,9 @@ abstract class ItopDataTestCase extends ItopTestCase
|
||||
private $aCreatedObjects = [];
|
||||
private $aEventListeners = [];
|
||||
|
||||
protected ?string $sConfigTmpBackupFile = null;
|
||||
protected ?Config $oiTopConfig = null;
|
||||
|
||||
/**
|
||||
* @var bool When testing with silo, there are some cache we need to update on tearDown. Doing it all the time will cost too much, so it's opt-in !
|
||||
* @see tearDown
|
||||
@@ -119,6 +123,8 @@ abstract class ItopDataTestCase extends ItopTestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
\IssueLog::Error($this->getName());
|
||||
|
||||
$this->PrepareEnvironment();
|
||||
|
||||
if (static::USE_TRANSACTION) {
|
||||
@@ -185,6 +191,8 @@ abstract class ItopDataTestCase extends ItopTestCase
|
||||
|
||||
CMDBObject::SetCurrentChange(null);
|
||||
|
||||
$this->RestoreConfiguration();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
@@ -1434,4 +1442,35 @@ abstract class ItopDataTestCase extends ItopTestCase
|
||||
$oObject->Set($sStopwatchAttCode, $oStopwatch);
|
||||
}
|
||||
|
||||
protected function BackupConfiguration(): void
|
||||
{
|
||||
$sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
|
||||
clearstatcache();
|
||||
echo sprintf("rights via ls on %s:\n %s \n", $sConfigPath, exec("ls -al $sConfigPath"));
|
||||
$sFilePermOutput = substr(sprintf('%o', fileperms('/etc/passwd')), -4);
|
||||
echo sprintf("rights via fileperms on %s:\n %s \n", $sConfigPath, $sFilePermOutput);
|
||||
|
||||
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
|
||||
MetaModel::GetConfig()->WriteToFile($this->sConfigTmpBackupFile);
|
||||
$this->oiTopConfig = new Config($sConfigPath);
|
||||
}
|
||||
|
||||
protected function RestoreConfiguration(): void
|
||||
{
|
||||
if (is_null($this->sConfigTmpBackupFile) || ! is_file($this->sConfigTmpBackupFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($this->oiTopConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//put config back
|
||||
$sConfigPath = $this->oiTopConfig->GetLoadedFile();
|
||||
@chmod($sConfigPath, 0770);
|
||||
$oConfig = new Config($this->sConfigTmpBackupFile);
|
||||
$oConfig->WriteToFile($sConfigPath);
|
||||
@chmod($sConfigPath, 0440);
|
||||
@unlink($this->sConfigTmpBackupFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Combodo\iTop\Test\UnitTest;
|
||||
use CMDBSource;
|
||||
use DeprecatedCallsLog;
|
||||
use MySQLTransactionNotClosedException;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use ReflectionMethod;
|
||||
use SetupUtils;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
@@ -28,6 +28,7 @@ use const DEBUG_BACKTRACE_IGNORE_ARGS;
|
||||
abstract class ItopTestCase extends KernelTestCase
|
||||
{
|
||||
public const TEST_LOG_DIR = 'test';
|
||||
protected array $aFileToClean = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@@ -36,7 +37,7 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
public const DISABLE_DEPRECATEDCALLSLOG_ERRORHANDLER = true;
|
||||
public static $DEBUG_UNIT_TEST = false;
|
||||
protected static $aBackupStaticProperties = [];
|
||||
|
||||
public ?array $aLastCurlGetInfo = null;
|
||||
/**
|
||||
* @link https://docs.phpunit.de/en/9.6/annotations.html#preserveglobalstate PHPUnit `preserveGlobalState` annotation documentation
|
||||
*
|
||||
@@ -149,6 +150,17 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Check globals
|
||||
global $fItopStarted;
|
||||
if (is_null($fItopStarted)) {
|
||||
$fItopStarted = microtime(true);
|
||||
}
|
||||
|
||||
global $iItopInitialMemory;
|
||||
if (is_null($iItopInitialMemory)) {
|
||||
$iItopInitialMemory = memory_get_usage(true);
|
||||
}
|
||||
|
||||
// Hack - Required the first time the Portal kernel is booted on a newly installed iTop
|
||||
$_ENV['COMBODO_PORTAL_BASE_ABSOLUTE_PATH'] = __DIR__.'/../../../../../env-production/itop-portal-base/portal/public/';
|
||||
|
||||
@@ -174,6 +186,15 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
}
|
||||
throw new MySQLTransactionNotClosedException('Some DB transactions were opened but not closed ! Fix the code by adding ROLLBACK or COMMIT statements !', []);
|
||||
}
|
||||
|
||||
foreach ($this->aFileToClean as $sPath) {
|
||||
if (is_file($sPath)) {
|
||||
@unlink($sPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
SetupUtils::tidydir($sPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,4 +652,62 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
fclose($handle);
|
||||
return array_reverse($aLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrl
|
||||
* @param array|null $aPostFields
|
||||
* @param array|null $aCurlOptions
|
||||
* @param $bXDebugEnabled
|
||||
* @return string
|
||||
*/
|
||||
protected function CallUrl($sUrl, ?array $aPostFields = [], ?array $aCurlOptions = [], $bXDebugEnabled = false): string
|
||||
{
|
||||
$ch = curl_init();
|
||||
if ($bXDebugEnabled) {
|
||||
curl_setopt($ch, CURLOPT_COOKIE, "XDEBUG_SESSION=phpstorm");
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_setopt_array($ch, $aCurlOptions);
|
||||
if ($this->IsArrayOfArray($aPostFields)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($aPostFields));
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
}
|
||||
|
||||
$sOutput = curl_exec($ch);
|
||||
|
||||
$info = curl_getinfo($ch);
|
||||
$this->aLastCurlGetInfo = $info;
|
||||
$sErrorMsg = curl_error($ch);
|
||||
$iErrorCode = curl_errno($ch);
|
||||
curl_close($ch);
|
||||
|
||||
\IssueLog::Info(__METHOD__, null, ['url' => $sUrl, 'error' => $sErrorMsg, 'error_code' => $iErrorCode, 'post_fields' => $aPostFields, 'info' => $info]);
|
||||
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
private function IsArrayOfArray(array $aStruct): bool
|
||||
{
|
||||
foreach ($aStruct as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function CallItopUri(string $sUri, ?array $aPostFields = [], ?array $aCurlOptions = [], $bXDebugEnabled = false): string
|
||||
{
|
||||
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
|
||||
|
||||
return $this->CallUrl($sUrl, $aPostFields, $aCurlOptions, $bXDebugEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
|
||||
class LoginTest extends ItopDataTestCase
|
||||
{
|
||||
protected $sConfigTmpBackupFile;
|
||||
protected $sConfigPath;
|
||||
protected $sLoginMode;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
clearstatcache();
|
||||
|
||||
// The test consists in requesting UI.php from outside iTop with a specific configuration
|
||||
// Hence the configuration file must be tweaked on disk (and restored)
|
||||
$this->sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
|
||||
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
|
||||
file_put_contents($this->sConfigTmpBackupFile, file_get_contents($this->sConfigPath));
|
||||
|
||||
$oConfig = new \Config($this->sConfigPath);
|
||||
$this->sLoginMode = "unimplemented_loginmode";
|
||||
$oConfig->AddAllowedLoginTypes($this->sLoginMode);
|
||||
|
||||
@chmod($this->sConfigPath, 0770);
|
||||
$oConfig->WriteToFile();
|
||||
@chmod($this->sConfigPath, 0444);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (! is_null($this->sConfigTmpBackupFile) && is_file($this->sConfigTmpBackupFile)) {
|
||||
//put config back
|
||||
@chmod($this->sConfigPath, 0770);
|
||||
file_put_contents($this->sConfigPath, file_get_contents($this->sConfigTmpBackupFile));
|
||||
@chmod($this->sConfigPath, 0444);
|
||||
@unlink($this->sConfigTmpBackupFile);
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function CallItopUrlByCurl($sUri, ?array $aPostFields = [])
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
$sUrl = MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
if (0 !== sizeof($aPostFields)) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$sOutput = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return $sOutput;
|
||||
}
|
||||
}
|
||||
@@ -172,34 +172,12 @@ class QueryTest extends ItopDataTestCase
|
||||
{
|
||||
// compute request url
|
||||
$url = $oQuery->GetExportUrl();
|
||||
$aCurlOptions = [
|
||||
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
|
||||
CURLOPT_USERPWD => self::USER.':'.self::PASSWORD,
|
||||
];
|
||||
|
||||
// open curl
|
||||
$curl = curl_init();
|
||||
|
||||
// curl options
|
||||
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, self::USER.':'.self::PASSWORD);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
// execute curl
|
||||
$result = curl_exec($curl);
|
||||
if (curl_errno($curl)) {
|
||||
$info = curl_getinfo($curl);
|
||||
var_export($info);
|
||||
var_dump([
|
||||
'url' => $url,
|
||||
'app_root_url:' => MetaModel::GetConfig()->Get('app_root_url'),
|
||||
'GetAbsoluteUrlAppRoot:' => \utils::GetAbsoluteUrlAppRoot(),
|
||||
]);
|
||||
}
|
||||
// close curl
|
||||
curl_close($curl);
|
||||
|
||||
return $result;
|
||||
return $this->CallUrl($url, [], $aCurlOptions);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
|
||||
@@ -13,13 +13,6 @@ class AttributeDefinitionTest extends ItopDataTestCase
|
||||
{
|
||||
public const CREATE_TEST_ORG = true;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'core/attributedef.class.inc.php');
|
||||
|
||||
}
|
||||
|
||||
public function testGetImportColumns()
|
||||
{
|
||||
$oAttributeDefinition = MetaModel::GetAttributeDef("ApplicationSolution", "status");
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use AttributeText;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
class AttributeTextTest extends ItopDataTestCase
|
||||
{
|
||||
protected \Organization $oTestOrganizationForAttributeText;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->oTestOrganizationForAttributeText = $this->CreateOrganization('Test for AttributeTextTest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers AttributeText::RenderWikiHtml
|
||||
*/
|
||||
public function testRenderWikiHtml_nonWikiUrlVariants()
|
||||
{
|
||||
// String value
|
||||
$sInput = 'This hyperlink https://combodo.com should be in an anchor tag.';
|
||||
$sExpected = 'This hyperlink <a href="https://combodo.com">https://combodo.com</a> should be in an anchor tag.';
|
||||
$this->assertEquals($sExpected, AttributeText::RenderWikiHtml($sInput));
|
||||
|
||||
// Empty string value
|
||||
$this->assertEquals('', AttributeText::RenderWikiHtml(''));
|
||||
|
||||
// Null value
|
||||
$this->assertEquals('', AttributeText::RenderWikiHtml(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers AttributeText::RenderWikiHtml
|
||||
*/
|
||||
public function testRenderWikiHtml_bWikiOnlyAbsentOrFalse_shouldTransformBothRegularAndWikiHyperlinks()
|
||||
{
|
||||
$sInput = 'A regular hyperlink https://combodo.com and a wiki hyperlink to an existing object [[Organization:'.$this->oTestOrganizationForAttributeText->GetKey().']]';
|
||||
|
||||
// bWikiOnly default value
|
||||
$sResult = AttributeText::RenderWikiHtml($sInput);
|
||||
$this->assertStringContainsString('<a href="https://combodo.com">', $sResult);
|
||||
$this->assertStringContainsString('class="object-ref-link"', $sResult);
|
||||
|
||||
// bWikiOnly = false
|
||||
$sResult = AttributeText::RenderWikiHtml($sInput, false);
|
||||
$this->assertStringContainsString('<a href="https://combodo.com">', $sResult);
|
||||
$this->assertStringContainsString('class="object-ref-link"', $sResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers AttributeText::RenderWikiHtml
|
||||
*/
|
||||
public function testRenderWikiHtml_bWikiOnlyToTrue_shouldNotTransformRegularHyperlinkButTransformWikiHyperlink()
|
||||
{
|
||||
$sInput = 'A regular hyperlink https://combodo.com and a wiki hyperlink to an existing object [[Organization:'.$this->oTestOrganizationForAttributeText->GetKey().']]';
|
||||
$sResult = AttributeText::RenderWikiHtml($sInput, true);
|
||||
$this->assertStringNotContainsString('<a href="https://combodo.com">', $sResult);
|
||||
$this->assertStringContainsString('class="object-ref-link"', $sResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers AttributeText::RenderWikiHtml
|
||||
*/
|
||||
public function testRenderWikiHtml_shouldTransformWikiHyperlinkForExistingObjectsOnly()
|
||||
{
|
||||
$sInput = 'A wiki hyperlink to a non existing object [[Organization:123456789]] and a wiki hyperlink to an existing object [[Organization:'.$this->oTestOrganizationForAttributeText->GetKey().']]';
|
||||
$sResult = AttributeText::RenderWikiHtml($sInput);
|
||||
$this->assertStringContainsString('wiki_broken_link', $sResult);
|
||||
$this->assertStringContainsString('class="object-ref-link"', $sResult);
|
||||
}
|
||||
}
|
||||
@@ -59,4 +59,43 @@ class InlineImageTest extends ItopDataTestCase
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers InlineImage::FixUrls
|
||||
*/
|
||||
public function testFixUrls_shouldReturnAnEmptyStringIfNullOrEmptyStringPassed()
|
||||
{
|
||||
$sResult = InlineImage::FixUrls(null);
|
||||
$this->assertEquals('', $sResult);
|
||||
|
||||
$sResult = InlineImage::FixUrls('');
|
||||
$this->assertEquals('', $sResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers InlineImage::FixUrls
|
||||
*/
|
||||
public function testFixUrls_shouldReturnUnchangedValueIfValueContainsNoImage()
|
||||
{
|
||||
$sHtml = '<div><p>Texte sans image</p></div>';
|
||||
$sResult = InlineImage::FixUrls($sHtml);
|
||||
$this->assertEquals($sHtml, $sResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers InlineImage::FixUrls
|
||||
*/
|
||||
public function testFixUrls_shouldReplaceImagesSrcWithCurrentAppRootUrlAndSecret()
|
||||
{
|
||||
$sHtml = <<<HTML
|
||||
<div>
|
||||
<img src="/images/test1.png" data-img-id="123" data-img-secret="abc" />
|
||||
<img src="/images/test2.png" data-img-id="456" data-img-secret="def" />
|
||||
</div>
|
||||
HTML;
|
||||
$sResult = InlineImage::FixUrls($sHtml);
|
||||
$this->assertStringContainsString('<img', $sResult);
|
||||
$this->assertStringContainsString(\utils::EscapeHtml(\utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL.'123&s=abc'), $sResult);
|
||||
$this->assertStringContainsString(\utils::EscapeHtml(\utils::GetAbsoluteUrlAppRoot().INLINEIMAGE_DOWNLOAD_URL.'456&s=def'), $sResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ class UserLocalTest extends ItopDataTestCase
|
||||
|
||||
],
|
||||
'expectedCheckStatus' => false,
|
||||
'expectedCheckIssues' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'expectedCheckIssues' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
|
||||
'userLanguage' => 'EN US',
|
||||
],
|
||||
'notValidPattern custom message string not array' => [
|
||||
|
||||
@@ -12,10 +12,8 @@ class CliResetSessionTest extends ItopDataTestCase
|
||||
public const USE_TRANSACTION = false;
|
||||
|
||||
private $sCookieFile = "";
|
||||
private $sUrl;
|
||||
private $sLogin;
|
||||
private $sPassword = "Iuytrez9876543ç_è-(";
|
||||
protected $sConfigTmpBackupFile;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
@@ -24,16 +22,13 @@ class CliResetSessionTest extends ItopDataTestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->sConfigTmpBackupFile = tempnam(sys_get_temp_dir(), "config_");
|
||||
MetaModel::GetConfig()->WriteToFile($this->sConfigTmpBackupFile);
|
||||
$this->BackupConfiguration();
|
||||
|
||||
$this->sLogin = "rest-user-".date('dmYHis');
|
||||
$this->CreateTestOrganization();
|
||||
|
||||
$this->sCookieFile = tempnam(sys_get_temp_dir(), 'jsondata_');
|
||||
|
||||
$this->sUrl = \MetaModel::GetConfig()->Get('app_root_url');
|
||||
|
||||
$oRestProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => 'REST Services User'], true);
|
||||
$oAdminProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => 'Administrator'], true);
|
||||
|
||||
@@ -47,16 +42,6 @@ class CliResetSessionTest extends ItopDataTestCase
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
if (! is_null($this->sConfigTmpBackupFile) && is_file($this->sConfigTmpBackupFile)) {
|
||||
//put config back
|
||||
$sConfigPath = MetaModel::GetConfig()->GetLoadedFile();
|
||||
@chmod($sConfigPath, 0770);
|
||||
$oConfig = new Config($this->sConfigTmpBackupFile);
|
||||
$oConfig->WriteToFile($sConfigPath);
|
||||
@chmod($sConfigPath, 0444);
|
||||
unlink($this->sConfigTmpBackupFile);
|
||||
}
|
||||
|
||||
if (!empty($this->sCookieFile)) {
|
||||
unlink($this->sCookieFile);
|
||||
}
|
||||
@@ -150,26 +135,18 @@ class CliResetSessionTest extends ItopDataTestCase
|
||||
*/
|
||||
private function SendHTTPRequestWithCookies($sUri, $aPostFields, $sForcedLoginMode = null): string
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->sCookieFile);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->sCookieFile);
|
||||
|
||||
$sUrl = "$this->sUrl/$sUri";
|
||||
if (!is_null($sForcedLoginMode)) {
|
||||
$sUrl .= "?login_mode=$sForcedLoginMode";
|
||||
$sUri .= "?login_mode=$sForcedLoginMode";
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$sResponse = curl_exec($ch);
|
||||
$aCurlOptions = [
|
||||
CURLOPT_COOKIEJAR => $this->sCookieFile,
|
||||
CURLOPT_COOKIEFILE => $this->sCookieFile,
|
||||
CURLOPT_HEADER => 1,
|
||||
];
|
||||
|
||||
$sResponse = $this->CallItopUri($sUri, $aPostFields, $aCurlOptions);
|
||||
var_dump($this->aLastCurlGetInfo);
|
||||
/** $sResponse example
|
||||
* "HTTP/1.1 200 OK
|
||||
Date: Wed, 07 Jun 2023 05:00:40 GMT
|
||||
@@ -177,16 +154,15 @@ class CliResetSessionTest extends ItopDataTestCase
|
||||
Set-Cookie: itop-2e83d2e9b00e354fdc528621cac532ac=q7ldcjq0rvbn33ccr9q8u8e953; path=/
|
||||
*/
|
||||
//var_dump($sResponse);
|
||||
$iHeaderSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$iHeaderSize = $this->aLastCurlGetInfo['header_size'] ?? 0;
|
||||
$sBody = substr($sResponse, $iHeaderSize);
|
||||
|
||||
//$iHttpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
||||
if (preg_match('/HTTP.* (\d*) /', $sResponse, $aMatches)) {
|
||||
$sHttpCode = $aMatches[1];
|
||||
} else {
|
||||
$sHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$sHttpCode = $this->aLastCurlGetInfo['http_code'] ?? -1;
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$this->assertEquals(200, $sHttpCode, "The test logic assumes that the HTTP request is correctly handled");
|
||||
return $sBody;
|
||||
|
||||
@@ -17,7 +17,6 @@ class RestTest extends ItopDataTestCase
|
||||
public const USE_TRANSACTION = false;
|
||||
public const CREATE_TEST_ORG = false;
|
||||
|
||||
private static $sUrl;
|
||||
private static $sLogin;
|
||||
private static $sPassword = "Iuytrez9876543ç_è-(";
|
||||
|
||||
@@ -44,7 +43,6 @@ class RestTest extends ItopDataTestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
static::$sUrl = MetaModel::GetConfig()->Get('app_root_url');
|
||||
static::$sLogin = "rest-user-".date('dmYHis');
|
||||
|
||||
$this->CreateTestOrganization();
|
||||
@@ -96,7 +94,6 @@ class RestTest extends ItopDataTestCase
|
||||
|
||||
public function testPostJSONDataAsCurlFile()
|
||||
{
|
||||
$sCallbackName = 'fooCallback';
|
||||
$sJsonData = '{"operation": "list_operations"}';
|
||||
|
||||
// Test regular JSON result
|
||||
@@ -297,16 +294,7 @@ JSON;
|
||||
$aPostFields['callback'] = $sCallbackName;
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, static::$sUrl."/webservices/rest.php");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$sJson = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$sJson = $this->CallItopUri('webservices/rest.php', $aPostFields);
|
||||
|
||||
if (!is_null($sTmpFile)) {
|
||||
unlink($sTmpFile);
|
||||
|
||||
Reference in New Issue
Block a user