mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
Merge branch 'designer-3.2.1' into support/3.2.1
This commit is contained in:
@@ -199,7 +199,7 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
|
||||
libxml_clear_errors();
|
||||
$oFileXml->formatOutput = true;
|
||||
$oFileXml->preserveWhiteSpace = false;
|
||||
$oFileXml->loadXML($sFileContent);
|
||||
$oFileXml->loadXML($sFileContent, LIBXML_BIGLINES);
|
||||
|
||||
$oFileItopFormat = new iTopDesignFormat($oFileXml);
|
||||
|
||||
|
||||
@@ -347,6 +347,7 @@ EOF
|
||||
{
|
||||
$oPage->add('<div class="ui-dialog-header">'.$sIntroduction.'</div>');
|
||||
}
|
||||
$oPage->add('<div class="designer-dialog-error"></div>');
|
||||
$this->Render($oPage);
|
||||
$oPage->add('</div>');
|
||||
|
||||
|
||||
@@ -611,12 +611,12 @@ class CMDBSource
|
||||
catch (mysqli_sql_exception $e)
|
||||
{
|
||||
self::LogDeadLock($e, true);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
throw new MySQLException('Failed to issue SQL query', ['query' => $sSql, $e, 'stack' => $e->getTraceAsString()]);
|
||||
} finally {
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
}
|
||||
if ($oResult === false) {
|
||||
$aContext = array('query' => $sSql);
|
||||
$aContext = ["\nstack" => (new Exception(''))->getTraceAsString(), "\nquery" => $sSql];
|
||||
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection(true)->errno;
|
||||
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
||||
|
||||
@@ -1233,6 +1233,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.session_handler_extension' => [
|
||||
'type' => 'string',
|
||||
'description' => 'to store more data in itop session files, set your own iSessionHandlerExtension implementation class in this variable',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.gc_threshold' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',
|
||||
|
||||
@@ -71,6 +71,11 @@ class DesignDocument extends DOMDocument
|
||||
$this->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
|
||||
}
|
||||
|
||||
public function loadXML(string $source, int $options = 0)
|
||||
{
|
||||
return parent::loadXML($source, $options | LIBXML_BIGLINES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload of the standard API
|
||||
*
|
||||
|
||||
@@ -6801,12 +6801,14 @@ abstract class MetaModel
|
||||
if ($bMustBeFound && empty($aRow))
|
||||
{
|
||||
$sNotFoundErrorMessage = "No result for the single row query";
|
||||
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
|
||||
$e = new CoreException($sNotFoundErrorMessage);
|
||||
IssueLog::Error($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
|
||||
'class' => $sClass,
|
||||
'key' => $iKey,
|
||||
'sql_query' => $sSQL,
|
||||
'stack' => $e->getTraceAsString(),
|
||||
]);
|
||||
throw new CoreException($sNotFoundErrorMessage);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $aRow;
|
||||
|
||||
@@ -548,6 +548,7 @@ function ValidateWithPattern(sFieldId, bMandatory, sPattern, sFormId, aForbidden
|
||||
if (sMessage)
|
||||
{
|
||||
$('#'+sFieldId).attr('data-tooltip-content', sMessage);
|
||||
$('#'+sFieldId).attr('data-tooltip-theme', 'error');
|
||||
CombodoTooltip.InitTooltipFromMarkup($('#'+sFieldId), true);
|
||||
$('#'+sFieldId)[0]._tippy.show();
|
||||
}
|
||||
|
||||
@@ -525,6 +525,7 @@ return array(
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionGC' => $baseDir . '/sources/SessionTracker/SessionGC.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionHandler' => $baseDir . '/sources/SessionTracker/SessionHandler.php',
|
||||
'Combodo\\iTop\\SessionTracker\\iSessionHandlerExtension' => $baseDir . '/sources/SessionTracker/iSessionHandlerExtension.php',
|
||||
'CompileCSSService' => $baseDir . '/application/compilecssservice.class.inc.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Config' => $baseDir . '/core/config.class.inc.php',
|
||||
@@ -1560,6 +1561,7 @@ return array(
|
||||
'Sabberworm\\CSS\\Value\\URL' => $vendorDir . '/sabberworm/php-css-parser/src/Value/URL.php',
|
||||
'Sabberworm\\CSS\\Value\\Value' => $vendorDir . '/sabberworm/php-css-parser/src/Value/Value.php',
|
||||
'Sabberworm\\CSS\\Value\\ValueList' => $vendorDir . '/sabberworm/php-css-parser/src/Value/ValueList.php',
|
||||
'SanitizeTrait' => $baseDir . '/core/restservices.class.inc.php',
|
||||
'ScalarExpression' => $baseDir . '/core/oql/expression.class.inc.php',
|
||||
'ScalarOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php',
|
||||
'ScssPhp\\ScssPhp\\Base\\Range' => $vendorDir . '/scssphp/scssphp/src/Base/Range.php',
|
||||
@@ -3201,6 +3203,7 @@ return array(
|
||||
'iPreferencesExtension' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iProcess' => $baseDir . '/core/backgroundprocess.inc.php',
|
||||
'iQueryModifier' => $baseDir . '/core/querymodifier.class.inc.php',
|
||||
'iRestInputSanitizer' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iRestServiceProvider' => $baseDir . '/application/applicationextension.inc.php',
|
||||
'iScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php',
|
||||
'iSelfRegister' => $baseDir . '/core/userrights.class.inc.php',
|
||||
|
||||
@@ -915,6 +915,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectsEvents' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectsEvents.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionGC' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionGC.php',
|
||||
'Combodo\\iTop\\SessionTracker\\SessionHandler' => __DIR__ . '/../..' . '/sources/SessionTracker/SessionHandler.php',
|
||||
'Combodo\\iTop\\SessionTracker\\iSessionHandlerExtension' => __DIR__ . '/../..' . '/sources/SessionTracker/iSessionHandlerExtension.php',
|
||||
'CompileCSSService' => __DIR__ . '/../..' . '/application/compilecssservice.class.inc.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
@@ -1950,6 +1951,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Sabberworm\\CSS\\Value\\URL' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Value/URL.php',
|
||||
'Sabberworm\\CSS\\Value\\Value' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Value/Value.php',
|
||||
'Sabberworm\\CSS\\Value\\ValueList' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Value/ValueList.php',
|
||||
'SanitizeTrait' => __DIR__ . '/../..' . '/core/restservices.class.inc.php',
|
||||
'ScalarExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php',
|
||||
'ScalarOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php',
|
||||
'ScssPhp\\ScssPhp\\Base\\Range' => __DIR__ . '/..' . '/scssphp/scssphp/src/Base/Range.php',
|
||||
@@ -3591,6 +3593,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'iPreferencesExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
|
||||
'iQueryModifier' => __DIR__ . '/../..' . '/core/querymodifier.class.inc.php',
|
||||
'iRestInputSanitizer' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iRestServiceProvider' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
|
||||
'iScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php',
|
||||
'iSelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php',
|
||||
|
||||
@@ -69,14 +69,37 @@ class MFException extends Exception
|
||||
* MFException constructor.
|
||||
*
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param $message
|
||||
* @param $code: error code
|
||||
* @param $oNode: dom node
|
||||
* @param $sXPath: XML xpath: if provided used in exception message. otherwise computed via $oNode
|
||||
* @param $sExtraInfo: additional information stored in exception
|
||||
* @param $oParentFallbackNode: fallback dom node (usually parent). in case $oNode XML line is wrong (set to 0), line number computed/displayed in error message comes from $oParentFallbackNode
|
||||
*/
|
||||
public function __construct($message = null, $code = null, $iSourceLineNumber = 0, $sXPath = '', $sExtraInfo = '', $previous = null)
|
||||
public function __construct($message, $code, $oNode, $sXPath = null, $sExtraInfo = '', $oParentFallbackNode=null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$iSourceLineNumber = ModelFactory::GetXMLLineNumber($oNode);
|
||||
if ($iSourceLineNumber==0 && ! is_null($oParentFallbackNode)){
|
||||
$iSourceLineNumber = ModelFactory::GetXMLLineNumber($oParentFallbackNode);
|
||||
}
|
||||
|
||||
if (is_null($sXPath)){
|
||||
$sXPath = DesignDocument::GetItopNodePath($oNode);
|
||||
}
|
||||
|
||||
$this->iSourceLineNumber = $iSourceLineNumber;
|
||||
$this->iSourceLineOffset = 0;
|
||||
$this->sXPath = $sXPath;
|
||||
$this->sExtraInfo = $sExtraInfo;
|
||||
parent::__construct("$sXPath at line $iSourceLineNumber: $message", $code);
|
||||
|
||||
$aContext = [
|
||||
'error' => $code,
|
||||
'stack' => $this->getTraceAsString(),
|
||||
'extra_info' => $sExtraInfo,
|
||||
];
|
||||
\IssueLog::Error($this->getMessage(), null, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,6 +219,10 @@ class MFModule
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir($sRootDir)) {
|
||||
$sRootDir = APPROOT.$sRootDir;
|
||||
}
|
||||
|
||||
// Scan the module's root directory to find the datamodel(*).xml files
|
||||
if ($hDir = opendir($sRootDir))
|
||||
{
|
||||
@@ -749,10 +776,7 @@ class ModelFactory
|
||||
case 'define_if_not_exists':
|
||||
/** @var \MFElement $oParentNode */
|
||||
$oParentNode = $oSubClassNode->parentNode;
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oParentNode);
|
||||
$sItopNodePath = DesignDocument::GetItopNodePath($oParentNode);
|
||||
throw new MFException("$sItopNodePath at line $iLine: _delta=\"$sParentDeltaSpec\" not supported for classes in hierarchy",
|
||||
MFException::NOT_FOUND, $iLine, $sItopNodePath);
|
||||
throw new MFException("_delta=\"$sParentDeltaSpec\" not supported for classes in hierarchy", MFException::NOT_FOUND, $oParentNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,10 +832,7 @@ class ModelFactory
|
||||
// Move class after new parent class (before its next sibling)
|
||||
$oNodeForTargetParent = $oTargetDocument->GetNodes("/itop_design/classes/class[@id=\"$sParentClassName\"]")->item(0);
|
||||
if (is_null($oNodeForTargetParent)) {
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oSourceParentClassNode);
|
||||
$sItopNodePath = DesignDocument::GetItopNodePath($oSourceParentClassNode);
|
||||
throw new MFException($sItopNodePath." at line $iLine: invalid parent class '$sParentClassName'",
|
||||
MFException::NOT_FOUND, $iLine, $sItopNodePath);
|
||||
throw new MFException("invalid parent class '$sParentClassName'", MFException::NOT_FOUND, $oSourceParentClassNode);
|
||||
}
|
||||
$oNextParentSibling = $oNodeForTargetParent->nextSibling;
|
||||
if ($oNextParentSibling) {
|
||||
@@ -839,20 +860,14 @@ class ModelFactory
|
||||
if (!$oTargetNode || $oTargetNode->IsRemoved()) {
|
||||
// The node does not exist or is marked as removed
|
||||
if ($bMustExist) {
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oSourceNode);
|
||||
$sItopNodePath = DesignDocument::GetItopNodePath($oSourceNode);
|
||||
throw new MFException($sItopNodePath.' at line '.$iLine.': could not be found or marked as removed',
|
||||
MFException::NOT_FOUND, $iLine, $sItopNodePath);
|
||||
throw new MFException("could not be found or marked as removed", MFException::NOT_FOUND, $oSourceNode);
|
||||
}
|
||||
if ($bIfExists) {
|
||||
// Do not continue deeper
|
||||
$oTargetNode = null;
|
||||
} else {
|
||||
if (!$bSpecifiedMerge && $sMode === self::LOAD_DELTA_MODE_STRICT && ($sSearchId !== '' || is_null($oSourceNode->GetFirstElementChild()))) {
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oSourceNode);
|
||||
$sItopNodePath = DesignDocument::GetItopNodePath($oSourceNode);
|
||||
throw new MFException($sItopNodePath.' at line '.$iLine.': could not be found or marked as removed (strict mode)',
|
||||
MFException::NOT_FOUND, $iLine, $sItopNodePath, 'strict mode');
|
||||
throw new MFException("could not be found or marked as removed (strict mode)", MFException::NOT_FOUND, $oSourceNode, null, 'strict mode');
|
||||
}
|
||||
|
||||
// Ignore renaming non-existant node
|
||||
@@ -901,10 +916,7 @@ class ModelFactory
|
||||
if (is_null($oSourceNode->GetFirstElementChild()) && $oTargetParentNode instanceof MFElement) {
|
||||
// Leaf node
|
||||
if ($sMode === self::LOAD_DELTA_MODE_STRICT && !$oSourceNode->hasAttribute('_rename_from') && trim($oSourceNode->GetText('')) !== '') {
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oSourceNode);
|
||||
$sItopNodePath = DesignDocument::GetItopNodePath($oSourceNode);
|
||||
throw new MFException($sItopNodePath.' at line '.$iLine.': cannot be modified without _delta flag (strict mode)',
|
||||
MFException::AMBIGUOUS_LEAF, $iLine, $sItopNodePath, 'strict mode');
|
||||
throw new MFException("cannot be modified without _delta flag (strict mode)", MFException::AMBIGUOUS_LEAF, $oSourceNode, null, 'strict mode');
|
||||
} else {
|
||||
// Lax mode: same as redefine
|
||||
// Replace the existing node by the given node - copy child nodes as well
|
||||
@@ -912,7 +924,7 @@ class ModelFactory
|
||||
if (trim($oSourceNode->GetText('')) !== '') {
|
||||
$oTargetNode = $oTargetDocument->importNode($oSourceNode, true);
|
||||
$sSearchId = $oSourceNode->hasAttribute('_rename_from') ? $oSourceNode->getAttribute('_rename_from') : $oSourceNode->getAttribute('id');
|
||||
$oTargetParentNode->RedefineChildNode($oTargetNode, $sSearchId);
|
||||
$oTargetParentNode->RedefineChildNode($oTargetNode, $sSearchId, $oSourceNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -956,7 +968,7 @@ class ModelFactory
|
||||
// Replace the existing node by the given node - copy child nodes as well
|
||||
/** @var \MFElement $oTargetNode */
|
||||
$oTargetNode = $oTargetDocument->importNode($oSourceNode, true);
|
||||
$oTargetParentNode->RedefineChildNode($oTargetNode, $sSearchId);
|
||||
$oTargetParentNode->RedefineChildNode($oTargetNode, $sSearchId, $oSourceNode);
|
||||
break;
|
||||
|
||||
case 'delete_if_exists':
|
||||
@@ -976,25 +988,18 @@ class ModelFactory
|
||||
case 'delete':
|
||||
/** @var \MFElement $oTargetNode */
|
||||
$oTargetNode = $oTargetParentNode->_FindChildNode($oSourceNode, $sSearchId);
|
||||
$sPath = MFDocument::GetItopNodePath($oSourceNode);
|
||||
$iLine = $this->GetXMLLineNumber($oSourceNode);
|
||||
|
||||
if ($oTargetNode == null) {
|
||||
throw new MFException($sPath.' at line '.$iLine.": could not be deleted (not found)", MFException::COULD_NOT_BE_DELETED,
|
||||
$iLine, $sPath);
|
||||
throw new MFException("could not be deleted (not found)", MFException::COULD_NOT_BE_DELETED, $oSourceNode);
|
||||
}
|
||||
if ($oTargetNode->IsRemoved()) {
|
||||
throw new MFException($sPath.' at line '.$iLine.": could not be deleted (already marked as deleted)",
|
||||
MFException::ALREADY_DELETED, $iLine, $sPath);
|
||||
throw new MFException("could not be deleted (already marked as deleted)", MFException::ALREADY_DELETED, $oSourceNode);
|
||||
}
|
||||
$oTargetNode->Delete();
|
||||
break;
|
||||
|
||||
default:
|
||||
$sPath = MFDocument::GetItopNodePath($oSourceNode);
|
||||
$iLine = $this->GetXMLLineNumber($oSourceNode);
|
||||
throw new MFException($sPath.' at line '.$iLine.": unexpected value for attribute _delta: '".$sDeltaSpec."'",
|
||||
MFException::INVALID_DELTA, $iLine, $sPath, $sDeltaSpec);
|
||||
throw new MFException("unexpected value for attribute _delta: '".$sDeltaSpec."'", MFException::INVALID_DELTA, $oSourceNode, null, $sDeltaSpec);
|
||||
}
|
||||
|
||||
if ($oTargetNode && $oTargetNode->parentNode) {
|
||||
@@ -2124,7 +2129,7 @@ class MFElement extends Combodo\iTop\DesignElement
|
||||
*/
|
||||
public function IsInDefinition()
|
||||
{
|
||||
// Iterate through the parents: reset the flag if any of them has a flag set
|
||||
// Iterate through the parents: reset the flag if any of them has a flag set
|
||||
for ($oParent = $this; $oParent instanceof MFElement; $oParent = $oParent->parentNode)
|
||||
{
|
||||
if ($oParent->GetAlteration() != '')
|
||||
@@ -2201,14 +2206,12 @@ class MFElement extends Combodo\iTop\DesignElement
|
||||
if ($oExisting)
|
||||
{
|
||||
if (!$oExisting->IsRemoved()) {
|
||||
$sPath = MFDocument::GetItopNodePath($oNode);
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oNode);
|
||||
$sExistingPath = MFDocument::GetItopNodePath($oExisting).' created_in: ['.$oExisting->getAttribute('_created_in').']';
|
||||
$iExistingLine = ModelFactory::GetXMLLineNumber($oExisting);
|
||||
$sExceptionMessage = <<<EOF
|
||||
`{$sPath}` at line {$iLine} could not be added : already exists in `{$sExistingPath}` at line {$iExistingLine}
|
||||
could not be added : already exists in `{$sExistingPath}` at line {$iExistingLine}
|
||||
EOF;
|
||||
throw new MFException($sExceptionMessage, MFException::COULD_NOT_BE_ADDED, $iLine, $sPath);
|
||||
throw new MFException($sExceptionMessage, MFException::COULD_NOT_BE_ADDED, $oNode);
|
||||
}
|
||||
$oExisting->ReplaceWithSingleNode($oNode);
|
||||
$sFlag = 'replaced';
|
||||
@@ -2229,13 +2232,14 @@ EOF;
|
||||
*
|
||||
* @param MFElement $oNode The node (including all subnodes) to set
|
||||
* @param string|null $sSearchId
|
||||
* @param mixed $oParentFallbackNode: provided to print accurate line number in case $oNode line is 0
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MFException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function RedefineChildNode(MFElement $oNode, $sSearchId = null)
|
||||
public function RedefineChildNode(MFElement $oNode, $sSearchId = null, $oParentFallbackNode=null)
|
||||
{
|
||||
// First: cleanup any flag behind the new node, and eventually add trace data
|
||||
$oNode->ApplyChanges();
|
||||
@@ -2245,17 +2249,13 @@ EOF;
|
||||
if (!$oExisting)
|
||||
{
|
||||
$sPath = MFDocument::GetItopNodePath($this)."/".$oNode->tagName.(empty($sSearchId) ? '' : "[$sSearchId]");
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oNode);
|
||||
throw new MFException($sPath." at line $iLine: could not be modified (not found)", MFException::COULD_NOT_BE_MODIFIED_NOT_FOUND,
|
||||
$sPath, $iLine);
|
||||
throw new MFException('could not be modified (not found)', MFException::COULD_NOT_BE_MODIFIED_NOT_FOUND, $oNode, $sPath, $oParentFallbackNode);
|
||||
}
|
||||
$sPrevFlag = $oExisting->GetAlteration();
|
||||
$sOldId = $oExisting->getAttribute('_old_id');
|
||||
if ($oExisting->IsRemoved()) {
|
||||
$sPath = MFDocument::GetItopNodePath($this)."/".$oNode->tagName.(empty($sSearchId) ? '' : "[$sSearchId]");
|
||||
$iLine = ModelFactory::GetXMLLineNumber($oNode);
|
||||
throw new MFException($sPath." at line $iLine: could not be modified (marked as deleted)",
|
||||
MFException::COULD_NOT_BE_MODIFIED_ALREADY_DELETED, $sPath, $iLine);
|
||||
throw new MFException('could not be modified (marked as deleted)', MFException::COULD_NOT_BE_MODIFIED_ALREADY_DELETED, $oNode, $sPath, $oParentFallbackNode);
|
||||
}
|
||||
$oExisting->ReplaceWithSingleNode($oNode);
|
||||
if (!$this->IsInDefinition()) {
|
||||
|
||||
@@ -120,10 +120,6 @@ class ModuleDiscovery
|
||||
$aArgs['module_file'] = $sFilePath;
|
||||
|
||||
list($sModuleName, $sModuleVersion) = static::GetModuleName($sId);
|
||||
if ($sModuleVersion == '')
|
||||
{
|
||||
$sModuleVersion = '1.0.0';
|
||||
}
|
||||
|
||||
if (array_key_exists($sModuleName, self::$m_aModuleVersionByName))
|
||||
{
|
||||
@@ -233,7 +229,7 @@ class ModuleDiscovery
|
||||
}
|
||||
ksort($aDependencies);
|
||||
$aOrderedModules = [];
|
||||
$iLoopCount = 1;
|
||||
$iLoopCount = 0;
|
||||
while(($iLoopCount < count($aModules)) && (count($aDependencies) > 0) )
|
||||
{
|
||||
foreach($aDependencies as $sId => $aRemainingDeps)
|
||||
@@ -308,16 +304,8 @@ class ModuleDiscovery
|
||||
// Separate the module names from their version for an easier comparison later
|
||||
foreach($aOrderedModules as $sModuleId)
|
||||
{
|
||||
$aMatches = array();
|
||||
if (preg_match('|^([^/]+)/(.*)$|', $sModuleId, $aMatches))
|
||||
{
|
||||
$aModuleVersions[$aMatches[1]] = $aMatches[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
// No version number found, assume 1.0.0
|
||||
$aModuleVersions[$sModuleId] = '1.0.0';
|
||||
}
|
||||
list($sModuleName, $sVersion) = self::GetModuleName($sModuleId);
|
||||
$aModuleVersions[$sModuleName] = $sVersion;
|
||||
}
|
||||
if (preg_match_all('/([^\(\)&| ]+)/', $sDepString, $aMatches))
|
||||
{
|
||||
@@ -456,13 +444,17 @@ class ModuleDiscovery
|
||||
{
|
||||
$sName = $aMatches[1];
|
||||
$sVersion = $aMatches[2];
|
||||
if ($sVersion === ""){
|
||||
$sVersion = "1.0.0";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sName = $sModuleId;
|
||||
$sVersion = "";
|
||||
$sVersion = "1.0.0";
|
||||
}
|
||||
return array($sName, $sVersion);
|
||||
|
||||
return [$sName, $sVersion];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -773,7 +773,7 @@ abstract class Controller extends AbstractController
|
||||
{
|
||||
// iTop 3.1 and older compatibility, if not an URI we don't know if its relative to app root or module root
|
||||
if (strpos($sLinkedScript, "://") === false) {
|
||||
$this->m_oPage->add_linked_script($sLinkedScript);
|
||||
$this->m_oPage->LinkScriptFromAppRoot(utils::LocalPath($sLinkedScript, APPROOT));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1280,18 +1280,19 @@ JS;
|
||||
protected function AddCompatibilityFiles(string $sFileType, string $sMode): void
|
||||
{
|
||||
$sConstantName = 'COMPATIBILITY_'.strtoupper($sMode).'_LINKED_'. ($sFileType === static::ENUM_COMPATIBILITY_FILE_TYPE_CSS ? 'STYLESHEETS' : 'SCRIPTS') .'_REL_PATH';
|
||||
$sMethodName = 'add_linked_'.($sFileType === static::ENUM_COMPATIBILITY_FILE_TYPE_CSS ? 'stylesheet' : 'script');
|
||||
$sMethodName = 'Link'.($sFileType === static::ENUM_COMPATIBILITY_FILE_TYPE_CSS ? 'Resource' : 'Script').'FromAppRoot';
|
||||
|
||||
|
||||
// Add ancestors files
|
||||
foreach (array_reverse(class_parents(static::class)) as $sParentClass) {
|
||||
foreach (constant($sParentClass.'::'.$sConstantName) as $sFile) {
|
||||
$this->$sMethodName(utils::GetAbsoluteUrlAppRoot().$sFile);
|
||||
$this->$sMethodName($sFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Add current class files
|
||||
foreach (constant('static::'.$sConstantName) as $sFile) {
|
||||
$this->$sMethodName(utils::GetAbsoluteUrlAppRoot().$sFile);
|
||||
$this->$sMethodName($sFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -664,6 +664,7 @@ JS;
|
||||
$aResult['data'] = ['error_message' => $e->getHtmlMessage()];
|
||||
} else {
|
||||
$oPage->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
||||
$oObj->Reload();
|
||||
$oObj->DisplayModifyForm($oPage,
|
||||
array('wizard_container' => true)); // wizard_container: display the wizard border and the title
|
||||
}
|
||||
|
||||
@@ -139,23 +139,59 @@ class SessionHandler extends \SessionHandler
|
||||
// - Data corruption (not a json / not an array / no previous creation_time key)
|
||||
$iCreationTime = time();
|
||||
|
||||
$aJson=[];
|
||||
if (! is_null($sPreviousFileVersionContent)) {
|
||||
$aJson = json_decode($sPreviousFileVersionContent, true);
|
||||
if (is_array($aJson) && array_key_exists('creation_time', $aJson)) {
|
||||
$iCreationTime = $aJson['creation_time'];
|
||||
if (is_array($aJson)){
|
||||
if (array_key_exists('creation_time', $aJson)) {
|
||||
$iCreationTime = $aJson['creation_time'];
|
||||
}
|
||||
} else {
|
||||
$aJson=[];
|
||||
}
|
||||
}
|
||||
|
||||
$aData = [
|
||||
'login_mode' => Session::Get('login_mode'),
|
||||
'user_id' => $sUserId,
|
||||
'creation_time' => $iCreationTime,
|
||||
'context' => implode('|', ContextTag::GetStack())
|
||||
];
|
||||
|
||||
$oiSessionHandlerExtension = $this->GetSessionHandlerExtension();
|
||||
if (! is_null($oiSessionHandlerExtension)){
|
||||
$oiSessionHandlerExtension->CompleteSessionData($aJson, $aData);
|
||||
}
|
||||
|
||||
return json_encode (
|
||||
[
|
||||
'login_mode' => Session::Get('login_mode'),
|
||||
'user_id' => $sUserId,
|
||||
'creation_time' => $iCreationTime,
|
||||
'context' => implode('|', ContextTag::GetStack())
|
||||
]
|
||||
$aData
|
||||
);
|
||||
} catch(Exception $e) {
|
||||
IssueLog::Error(__METHOD__, null, [ 'error' => $e->getMessage() ]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function GetSessionHandlerExtension() : ?iSessionHandlerExtension
|
||||
{
|
||||
$sSessionHandlerExtensionClass = utils::GetConfig()->Get('sessions_tracking.session_handler_extension');
|
||||
if (strlen($sSessionHandlerExtensionClass) !=0){
|
||||
try{
|
||||
if (! class_exists($sSessionHandlerExtensionClass)){
|
||||
throw new \Exception("Cannot find class");
|
||||
}
|
||||
|
||||
/** @var iSessionHandlerExtension $oSessionHandlerExtension */
|
||||
$oSessionHandlerExtension = new $sSessionHandlerExtensionClass;
|
||||
if ($oSessionHandlerExtension instanceof iSessionHandlerExtension){
|
||||
return $oSessionHandlerExtension;
|
||||
}
|
||||
|
||||
throw new \Exception("Not an instance of iSessionHandlerExtension");
|
||||
} catch(\Exception $e) {
|
||||
IssueLog::Error(__METHOD__ . ': cannot instanciate iSessionHandlerExtension', null, ['sessions_tracking.session_handler_extension' => $sSessionHandlerExtensionClass]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
16
sources/SessionTracker/iSessionHandlerExtension.php
Normal file
16
sources/SessionTracker/iSessionHandlerExtension.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\SessionTracker;
|
||||
|
||||
interface iSessionHandlerExtension {
|
||||
public function __construct();
|
||||
|
||||
/**
|
||||
* Called by SessionHandler to change data stored in iTop session files
|
||||
* @param array $aJson: previous data stored in session file
|
||||
* @param array $aData: usual session data see @SessionHandler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function CompleteSessionData(array $aJson, array &$aData) : void;
|
||||
}
|
||||
@@ -2440,7 +2440,9 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
// Really modified ?
|
||||
if ($oDestObj->IsModified())
|
||||
{
|
||||
$oDestObj::SetCurrentChange($oChange);
|
||||
if(method_exists(get_class($oDestObj), "SetCurrentChange")){
|
||||
$oDestObj::SetCurrentChange($oChange);
|
||||
}
|
||||
$oDestObj->DBUpdate();
|
||||
$bModified = true;
|
||||
$oStatLog->AddTrace('Updated object - Values: {'.implode(', ', $aValueTrace).'}', $this);
|
||||
@@ -2499,7 +2501,10 @@ class SynchroReplica extends DBObject implements iDisplay
|
||||
$aValueTrace[] = "$sAttCode: $value";
|
||||
}
|
||||
}
|
||||
$oDestObj::SetCurrentChange($oChange);
|
||||
|
||||
if(method_exists(get_class($oDestObj), "SetCurrentChange")){
|
||||
$oDestObj::SetCurrentChange($oChange);
|
||||
}
|
||||
$iNew = $oDestObj->DBInsert();
|
||||
|
||||
$this->Set('dest_id', $oDestObj->GetKey());
|
||||
|
||||
@@ -179,14 +179,14 @@ abstract class TestHandler
|
||||
}
|
||||
catch (CoreException $e)
|
||||
{
|
||||
//$this->ReportError($e->getMessage());
|
||||
//$this->ReportError($e->__tostring());
|
||||
//$this->ReportErrors($e->getMessage());
|
||||
//$this->ReportErrors($e->__tostring());
|
||||
$this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
//$this->ReportError($e->getMessage());
|
||||
//$this->ReportError($e->__tostring());
|
||||
//$this->ReportErrors($e->getMessage());
|
||||
//$this->ReportErrors($e->__tostring());
|
||||
$this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString());
|
||||
}
|
||||
restore_error_handler();
|
||||
|
||||
@@ -397,7 +397,7 @@ class TestMyBizModel extends TestBizModel
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
// $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>");
|
||||
// $this->ReportErrors("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>");
|
||||
// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
|
||||
//$this->test_linksinfo();
|
||||
//$this->test_list_attributes();
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
class ModuleDiscoveryTest extends ItopTestCase
|
||||
{
|
||||
public function GetModuleNameProvider()
|
||||
{
|
||||
return [
|
||||
'nominal' => [
|
||||
'sModuleId' => 'a/1.2.3',
|
||||
'name' => 'a',
|
||||
'version' => '1.2.3',
|
||||
],
|
||||
'develop' => [
|
||||
'sModuleId' => 'a/1.2.3-dev',
|
||||
'name' => 'a',
|
||||
'version' => '1.2.3-dev',
|
||||
],
|
||||
'missing version => 1.0.0' => [
|
||||
'sModuleId' => 'a/',
|
||||
'name' => 'a',
|
||||
'version' => '1.0.0',
|
||||
],
|
||||
'missing everything except name' => [
|
||||
'sModuleId' => 'a',
|
||||
'name' => 'a',
|
||||
'version' => '1.0.0',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->RequireOnceItopFile('setup/modulediscovery.class.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider GetModuleNameProvider
|
||||
*/
|
||||
public function testGetModuleName($sModuleId, $expectedName, $expectedVersion)
|
||||
{
|
||||
$this->assertEquals([$expectedName, $expectedVersion], \ModuleDiscovery::GetModuleName($sModuleId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,19 +3,21 @@
|
||||
namespace Combodo\iTop\Test\UnitTest\SessionTracker;
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\SessionTracker\iSessionHandlerExtension;
|
||||
use Combodo\iTop\SessionTracker\SessionHandler;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ContextTag;
|
||||
|
||||
class SessionHandlerTest extends ItopDataTestCase
|
||||
{
|
||||
private $aFiles ;
|
||||
private $oTag ;
|
||||
private $aFiles;
|
||||
private $oTag;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->aFiles=[];
|
||||
$this->RequireOnceUnitTestFile('./iSessionHandlerExtensionExamples.php');
|
||||
$this->aFiles = [];
|
||||
$this->oTag = new ContextTag(ContextTag::TAG_REST);
|
||||
}
|
||||
|
||||
@@ -24,47 +26,53 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
parent::tearDown();
|
||||
$this->oTag = null;
|
||||
|
||||
foreach ($this->aFiles as $sFile){
|
||||
if (is_file($sFile)){
|
||||
foreach ($this->aFiles as $sFile) {
|
||||
if (is_file($sFile)) {
|
||||
@unlink($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function CreateUserAndLogIn() : ? string {
|
||||
private function CreateUserAndLogIn(): ?string
|
||||
{
|
||||
$_SESSION = [];
|
||||
$oUser = $this->CreateContactlessUser("admin" . uniqid(), 1, "1234@Abcdefg");
|
||||
$oUser = $this->CreateContactlessUser("admin".uniqid(), 1, "1234@Abcdefg");
|
||||
|
||||
\UserRights::Login($oUser->Get('login'));
|
||||
|
||||
return $oUser->GetKey();
|
||||
}
|
||||
|
||||
private function GenerateSessionContent(SessionHandler $oSessionHandler, ?string $sPreviousFileVersionContent) : ?string {
|
||||
private function GenerateSessionContent(SessionHandler $oSessionHandler, ?string $sPreviousFileVersionContent): ?string
|
||||
{
|
||||
return $this->InvokeNonPublicMethod(SessionHandler::class, "generate_session_content", $oSessionHandler, $aArgs = [$sPreviousFileVersionContent]);
|
||||
}
|
||||
|
||||
/*
|
||||
* @covers SessionHandler::generate_session_content
|
||||
*/
|
||||
public function testGenerateSessionContentNoUserLoggedIn(){
|
||||
public function testGenerateSessionContentNoUserLoggedIn()
|
||||
{
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$sContent = $this->GenerateSessionContent($oSessionHandler, null);
|
||||
$this->assertNull($sContent, "Session content should be null when there is no user logged in");
|
||||
}
|
||||
|
||||
public function GenerateSessionContentCorruptedPreviousFileContentProvider() {
|
||||
public function GenerateSessionContentCorruptedPreviousFileContentProvider()
|
||||
{
|
||||
return [
|
||||
'not a json' => [ "not a json" ],
|
||||
'not an array' => [ json_encode("not an array") ],
|
||||
'array without creation_time key' => [ json_encode([]) ],
|
||||
'not a json' => ["not a json"],
|
||||
'not an array' => [json_encode("not an array")],
|
||||
'array without creation_time key' => [json_encode([])],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers SessionHandler::generate_session_content
|
||||
* @covers SessionHandler::generate_session_content
|
||||
* @dataProvider GenerateSessionContentCorruptedPreviousFileContentProvider
|
||||
*/
|
||||
public function testGenerateSessionContent_SessionFileRepairment(?string $sFileContent){
|
||||
public function testGenerateSessionContent_SessionFileRepairment(?string $sFileContent)
|
||||
{
|
||||
$sUserId = $this->CreateUserAndLogIn();
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
@@ -84,7 +92,8 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
/*
|
||||
* @covers SessionHandler::generate_session_content
|
||||
*/
|
||||
public function testGenerateSessionContent(){
|
||||
public function testGenerateSessionContent()
|
||||
{
|
||||
$sUserId = $this->CreateUserAndLogIn();
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
@@ -105,33 +114,36 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
|
||||
// Switch context + change user id via impersonation
|
||||
// check it is still tracked in session files
|
||||
$oOtherUser = $this->CreateContactlessUser("admin" . uniqid(), 1, "1234@Abcdefg");
|
||||
$oOtherUser = $this->CreateContactlessUser("admin".uniqid(), 1, "1234@Abcdefg");
|
||||
$this->assertTrue(\UserRights::Impersonate($oOtherUser->Get('login')), "Failed to execute impersonate on: ".$oOtherUser->Get('login'));
|
||||
$oTag2 = new ContextTag(ContextTag::TAG_SYNCHRO);
|
||||
$sNewContent = $this->GenerateSessionContent($oSessionHandler, $sFirstContent);
|
||||
$this->assertNotNull($sNewContent, 'Should not return null');
|
||||
$aJson = json_decode($sNewContent, true);
|
||||
$this->assertNotEquals(false, $aJson, 'Should return a valid json string, found: '.$sNewContent);
|
||||
$this->assertEquals(ContextTag::TAG_REST . '|' . ContextTag::TAG_SYNCHRO, $aJson['context'] ?? '', "After impersonation, should report the new context tags in [context]: $sNewContent");
|
||||
$this->assertEquals(ContextTag::TAG_REST.'|'.ContextTag::TAG_SYNCHRO, $aJson['context'] ?? '', "After impersonation, should report the new context tags in [context]: $sNewContent");
|
||||
$this->assertEquals($iFirstSessionCreationTime, $aJson['creation_time'] ?? '', "After impersonation, should still report the the session start timestamp in [creation_time]: $sNewContent");
|
||||
$this->assertEquals('foo_login_mode', $aJson['login_mode'] ?? '', "After impersonation, should still report the login mode in [login_mode]: $sNewContent");
|
||||
$this->assertEquals($oOtherUser->GetKey(), $aJson['user_id'] ?? '', "Should report the impersonate user in [user_id]: $sNewContent");
|
||||
}
|
||||
|
||||
private function touchSessionFile(SessionHandler $oSessionHandler, $session_id) : ?string {
|
||||
private function touchSessionFile(SessionHandler $oSessionHandler, $session_id): ?string
|
||||
{
|
||||
$sRes = $this->InvokeNonPublicMethod(SessionHandler::class, "touch_session_file", $oSessionHandler, $aArgs = [$session_id]);
|
||||
if (!is_null($sRes) && is_file($sRes)) {
|
||||
// Record the file for cleanup on tearDown
|
||||
$this->aFiles[] = $sRes;
|
||||
}
|
||||
clearstatcache();
|
||||
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
/*
|
||||
* @covers SessionHandler::touch_session_file
|
||||
*/
|
||||
public function testTouchSessionFile_NoUserLoggedIn(){
|
||||
public function testTouchSessionFile_NoUserLoggedIn()
|
||||
{
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$session_id = uniqid();
|
||||
$sFile = $this->touchSessionFile($oSessionHandler, $session_id);
|
||||
@@ -143,7 +155,8 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
/*
|
||||
* @covers SessionHandler::touch_session_file
|
||||
*/
|
||||
public function testTouchSessionFile_UserLoggedIn(){
|
||||
public function testTouchSessionFile_UserLoggedIn()
|
||||
{
|
||||
$sUserId = $this->CreateUserAndLogIn();
|
||||
Session::Set('login_mode', 'foo_login_mode');
|
||||
|
||||
@@ -174,7 +187,8 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
/**
|
||||
* @covers SessionHandler::touch_session_file
|
||||
*/
|
||||
public function testTouchSessionFileWithEmptySessionId() {
|
||||
public function testTouchSessionFileWithEmptySessionId()
|
||||
{
|
||||
$this->CreateUserAndLogIn();
|
||||
Session::Set('login_mode', 'toto');
|
||||
|
||||
@@ -183,32 +197,36 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
$this->assertNull($this->touchSessionFile($oSessionHandler, false), 'Should return null when session id (boolean) false');
|
||||
}
|
||||
|
||||
private function GetFilePath(SessionHandler $oSessionHandler, $session_id) : string {
|
||||
private function GetFilePath(SessionHandler $oSessionHandler, $session_id): string
|
||||
{
|
||||
$sFile = $this->InvokeNonPublicMethod(SessionHandler::class, "get_file_path", $oSessionHandler, $aArgs = [$session_id]);
|
||||
// Record file for cleanup on tearDown
|
||||
$this->aFiles[] = $sFile;
|
||||
|
||||
return $sFile;
|
||||
}
|
||||
|
||||
public function GgcWithTimeLimitProvider(){
|
||||
public function GgcWithTimeLimitProvider()
|
||||
{
|
||||
return [
|
||||
'no cleanup time limit' => [
|
||||
'iTimeLimit' => -1,
|
||||
'iExpectedProcessed' => 2
|
||||
'no cleanup time limit' => [
|
||||
'iTimeLimit' => -1,
|
||||
'iExpectedProcessed' => 2,
|
||||
],
|
||||
'cleanup time limit in the pass => first file removed only' => [
|
||||
'iTimeLimit' => time() - 1,
|
||||
'iExpectedProcessed' => 1
|
||||
'iTimeLimit' => time() - 1,
|
||||
'iExpectedProcessed' => 1,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers SessionHandler::gc_with_time_limit
|
||||
* @covers SessionHandler::list_session_files
|
||||
* @covers SessionHandler::gc_with_time_limit
|
||||
* @covers SessionHandler::list_session_files
|
||||
* @dataProvider GgcWithTimeLimitProvider
|
||||
*/
|
||||
public function testGgcWithTimeLimit($iTimeLimit, $iExpectedProcessed) {
|
||||
public function testGgcWithTimeLimit($iTimeLimit, $iExpectedProcessed)
|
||||
{
|
||||
$oSessionHandler = new SessionHandler();
|
||||
//remove all first
|
||||
$oSessionHandler->gc_with_time_limit(-1);
|
||||
@@ -218,11 +236,11 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
$iNbExpiredFiles = 2;
|
||||
$iNbFiles = 5;
|
||||
$iExpiredTimeStamp = time() - $max_lifetime - 1;
|
||||
for($i=0; $i<$iNbFiles; $i++) {
|
||||
for ($i = 0; $i < $iNbFiles; $i++) {
|
||||
$sFile = $this->GetFilePath($oSessionHandler, uniqid());
|
||||
file_put_contents($sFile, "fakedata");
|
||||
|
||||
if ($iNbExpiredFiles > 0){
|
||||
if ($iNbExpiredFiles > 0) {
|
||||
$iNbExpiredFiles--;
|
||||
touch($sFile, $iExpiredTimeStamp);
|
||||
}
|
||||
@@ -230,7 +248,7 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
|
||||
$aFoundSessionFiles = $oSessionHandler->list_session_files();
|
||||
$this->assertEquals($iNbFiles, sizeof($aFoundSessionFiles), 'list_session_files should reports all files');
|
||||
foreach ($aFoundSessionFiles as $sFile){
|
||||
foreach ($aFoundSessionFiles as $sFile) {
|
||||
$this->assertTrue(is_file($sFile), 'list_session_files should return a valid file paths, found: '.$sFile);
|
||||
}
|
||||
|
||||
@@ -238,4 +256,73 @@ class SessionHandlerTest extends ItopDataTestCase
|
||||
$this->assertEquals($iExpectedProcessed, $iProcessed, 'gc_with_time_limit should report the count of expired files');
|
||||
$this->assertEquals($iNbFiles - $iExpectedProcessed, sizeof($oSessionHandler->list_session_files()), 'gc_with_time_limit should actually remove all processed files');
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetSessionHandlerExtension_NoExtension()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', '');
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$oSessionHandlerExtension = $this->InvokeNonPublicMethod(SessionHandler::class, "GetSessionHandlerExtension", $oSessionHandler);
|
||||
$this->assertNull($oSessionHandlerExtension, "by default no extension");
|
||||
}
|
||||
|
||||
public function testGetSessionHandlerExtension_InvalidClassExtension()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', 'dddf');
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$oSessionHandlerExtension = $this->InvokeNonPublicMethod(SessionHandler::class, "GetSessionHandlerExtension", $oSessionHandler);
|
||||
$this->assertNull($oSessionHandlerExtension);
|
||||
}
|
||||
|
||||
public function testGetSessionHandlerExtension_InvalidExtension()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', Session::class);
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$oSessionHandlerExtension = $this->InvokeNonPublicMethod(SessionHandler::class, "GetSessionHandlerExtension", $oSessionHandler);
|
||||
$this->assertNull($oSessionHandlerExtension);
|
||||
}
|
||||
|
||||
public function testGetSessionHandlerExtension_OK()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', 'Combodo\iTop\Test\UnitTest\SessionTracker\BasicSessionHandlerExtension');
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$oSessionHandlerExtension = $this->InvokeNonPublicMethod(SessionHandler::class, "GetSessionHandlerExtension", $oSessionHandler);
|
||||
$this->assertNotNull($oSessionHandlerExtension, "by default no extension");
|
||||
$this->assertTrue($oSessionHandlerExtension instanceof iSessionHandlerExtension);
|
||||
$this->assertInstanceOf(BasicSessionHandlerExtension::class, $oSessionHandlerExtension);
|
||||
}
|
||||
|
||||
public function testGenerateSessionContent_WithAdditionalDataProvidedBySessionHandlerExtension()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', 'Combodo\iTop\Test\UnitTest\SessionTracker\BasicSessionHandlerExtension');
|
||||
$sUserId = $this->CreateUserAndLogIn();
|
||||
|
||||
$oSessionHandler = new SessionHandler();
|
||||
Session::Set('login_mode', 'foo_login_mode');
|
||||
|
||||
//first time
|
||||
$sFirstContent = $this->GenerateSessionContent($oSessionHandler, null);
|
||||
$this->assertNotNull($sFirstContent, 'Should not return null');
|
||||
$aJson = json_decode($sFirstContent, true);
|
||||
$this->assertNotEquals(false, $aJson, 'Should return a valid json string, found: '.$sFirstContent);
|
||||
|
||||
$this->assertEquals($sUserId, $aJson['user_id'] ?? '', "Should report the login of the logged in user in [user_id]: $sFirstContent");
|
||||
$this->assertEquals(ContextTag::TAG_REST, $aJson['context'] ?? '', "Should report the context tag(s) in [context]: $sFirstContent");
|
||||
$this->assertIsInt($aJson['creation_time'] ?? '', "Should report the session start timestamp in [creation_time]: $sFirstContent");
|
||||
$this->assertEquals('foo_login_mode', $aJson['login_mode'] ?? '', "Should report the current login mode in [login_mode]: $sFirstContent");
|
||||
|
||||
$this->assertEquals('gabuzomeu', $aJson['shadok'] ?? '', "Should report the current login mode in [shadok]: $sFirstContent");
|
||||
}
|
||||
|
||||
public function testGenerateSessionContentWithPreviousNonArrayContentAndWithAdditionalDataProvidedBySessionHandlerExtension()
|
||||
{
|
||||
\utils::GetConfig()->Set('sessions_tracking.session_handler_extension', 'Combodo\iTop\Test\UnitTest\SessionTracker\BasicSessionHandlerExtension');
|
||||
$this->CreateUserAndLogIn();
|
||||
$oSessionHandler = new SessionHandler();
|
||||
$sContent = $this->GenerateSessionContent($oSessionHandler, json_encode(null));
|
||||
$this->assertNotNull($sContent, "Call to CompleteSessionData should NOT fail due to Argument #1 (\$aJson) must be of type array, null given");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\SessionTracker;
|
||||
|
||||
use Combodo\iTop\SessionTracker\iSessionHandlerExtension;
|
||||
|
||||
class BasicSessionHandlerExtension implements iSessionHandlerExtension {
|
||||
public function __construct(){
|
||||
}
|
||||
|
||||
public function CompleteSessionData(array $aJson, array &$aData): void
|
||||
{
|
||||
$aData['shadok']='gabuzomeu';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user