mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-27 06:04:12 +01:00
Compare commits
43 Commits
support/3.
...
feature/61
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
433a6f1c3c | ||
|
|
075e2ec94a | ||
|
|
e5c1a01c69 | ||
|
|
692a8b978f | ||
|
|
a0ecd59568 | ||
|
|
eebc61385d | ||
|
|
3c15186685 | ||
|
|
fa038ded3d | ||
|
|
e7ea1b831c | ||
|
|
f15ac75f8f | ||
|
|
e28dbebbd5 | ||
|
|
4aff65f98b | ||
|
|
8aba578cfa | ||
|
|
7e7f8577e8 | ||
|
|
3c94974d9d | ||
|
|
d6e5069dd5 | ||
|
|
f839638e0b | ||
|
|
740ff8c649 | ||
|
|
cfe227e0c7 | ||
|
|
ed79c8f099 | ||
|
|
4560f751d1 | ||
|
|
fbd72b2783 | ||
|
|
778118cfb4 | ||
|
|
096ed9a63a | ||
|
|
06eb79d4f4 | ||
|
|
4e95ca3c7b | ||
|
|
4c626d0782 | ||
|
|
1114ed9562 | ||
|
|
1ddfaf0b61 | ||
|
|
c6fb03547f | ||
|
|
34368fe795 | ||
|
|
db46298cb8 | ||
|
|
424e7b37d7 | ||
|
|
8ffddeff01 | ||
|
|
21d37fb237 | ||
|
|
fca4006811 | ||
|
|
c3b00939dd | ||
|
|
75df33f606 | ||
|
|
78d8829d65 | ||
|
|
307edd3f7a | ||
|
|
6bf906a72f | ||
|
|
0f016d7511 | ||
|
|
d782987f50 |
@@ -37,7 +37,7 @@ iTop also offers mass import tools to help you being even more efficient.
|
||||
- [iTop Forums][1]: community support
|
||||
- [iTop Tickets][2]: for feature requests and bug reports
|
||||
- [Releases download][3]
|
||||
- [Software requirements][4]
|
||||
- [iTop requirements][4]
|
||||
- [Documentation][5] covering both iTop and its official extensions
|
||||
- [iTop Hub][6] : discover and install extensions !
|
||||
|
||||
@@ -45,7 +45,7 @@ iTop also offers mass import tools to help you being even more efficient.
|
||||
[1]: https://sourceforge.net/p/itop/discussion/
|
||||
[2]: https://sourceforge.net/p/itop/tickets/
|
||||
[3]: https://sourceforge.net/projects/itop/files/itop/
|
||||
[4]: https://www.itophub.io/wiki/page?id=latest:install:upgrading_itop
|
||||
[4]: https://www.itophub.io/wiki/page?id=latest:install:requirements
|
||||
[5]: https://www.itophub.io/wiki
|
||||
[6]: https://store.itophub.io/en_US/
|
||||
|
||||
|
||||
@@ -3054,7 +3054,16 @@ EOF
|
||||
|
||||
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').on('click', function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );");
|
||||
|
||||
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
|
||||
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
|
||||
$sCancelButtonOnClickScript .= $aExtraParams['js_handlers']['cancel_button_on_click'];
|
||||
} else {
|
||||
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
|
||||
}
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$oPage->add_ready_script($sCancelButtonOnClickScript);
|
||||
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
|
||||
@@ -386,7 +386,7 @@ EOF;
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||
|
||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||
$aActions = array(
|
||||
|
||||
@@ -304,7 +304,7 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
@@ -1045,7 +1045,7 @@ JS
|
||||
$aCount = $aCounts[$sStateValue];
|
||||
$sHyperlink = $aCount['link'];
|
||||
$sCountLabel = $aCount['label'];
|
||||
|
||||
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue);
|
||||
// N°5849 - Unencode label for ExternalKey attribute because friendlyname is already html encoded thanks to DBObject::GetName() in AttributeExternalKey::GetAllowedValues(). (A fix in this function may have too much impact).
|
||||
if ($oAttDef instanceof AttributeExternalKey) {
|
||||
@@ -1611,6 +1611,7 @@ JS
|
||||
|
||||
$iTotalCount = 0;
|
||||
$aURLs = array();
|
||||
|
||||
foreach ($aRes as $iRow => $aRow) {
|
||||
$sValue = $aRow['grouped_by_1'];
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
@@ -1621,6 +1622,7 @@ JS
|
||||
'value' => (float)$aRow[$sFctVar],
|
||||
);
|
||||
|
||||
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||
@@ -1637,9 +1639,13 @@ JS
|
||||
|
||||
switch ($sChartType) {
|
||||
case 'bars':
|
||||
$aNames = array();
|
||||
$iMaxNbCharsInLabel = 0;
|
||||
$aNames = [];
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aNames[$idx] = $aValue['label'];
|
||||
if ($iMaxNbCharsInLabel < mb_strlen($aValue['label'])) {
|
||||
$iMaxNbCharsInLabel = mb_strlen($aValue['label']);
|
||||
}
|
||||
}
|
||||
$oBlock = new BlockChartAjaxBars();
|
||||
$oBlock->sJSNames = json_encode($aNames);
|
||||
@@ -1647,21 +1653,31 @@ JS
|
||||
$oBlock->sId = $sId;
|
||||
$oBlock->sJSURLs = $sJSURLs;
|
||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||
$oBlock->iMaxNbCharsInLabel = $iMaxNbCharsInLabel;
|
||||
break;
|
||||
|
||||
case 'pie':
|
||||
$aColumns = array();
|
||||
$aNames = array();
|
||||
$aColumns = [];
|
||||
$aNames = [];
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
|
||||
$iNbLinesToAddForName = 0;
|
||||
if (count($aNames) > 50) {
|
||||
// Calculation of the number of legends line add to the height of the graph to have a maximum of 5 legend columns
|
||||
$iNbLinesIncludedInChartHeight = 10;
|
||||
$iNbLinesToAddForName = ceil(count($aNames) / 5) - $iNbLinesIncludedInChartHeight;
|
||||
}
|
||||
|
||||
$oBlock = new BlockChartAjaxPie();
|
||||
$oBlock->sJSColumns = json_encode($aColumns);
|
||||
$oBlock->sJSNames = json_encode($aNames);
|
||||
$oBlock->sId = $sId;
|
||||
$oBlock->sJSURLs = $sJSURLs;
|
||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||
$oBlock->iNbLinesToAddForName = $iNbLinesToAddForName;
|
||||
break;
|
||||
}
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
|
||||
@@ -211,7 +211,23 @@ class UILinksWidgetDirect
|
||||
$oObj = DBObject::MakeDefaultInstance($sRealClass);
|
||||
$aPrefillParam = array('source_obj' => $oSourceObj);
|
||||
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
$aFormExtraParams = array(
|
||||
'formPrefix' => $this->sInputid,
|
||||
'noRelations' => true,
|
||||
'fieldsFlags' => $aFieldFlags,
|
||||
'js_handlers' => [
|
||||
'cancel_button_on_click' =>
|
||||
<<<JS
|
||||
function() {
|
||||
// Do nothing, already handled by linksdirectwidget.js
|
||||
};
|
||||
JS
|
||||
,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -48,10 +48,9 @@ abstract class HTMLSanitizer
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
} else if (!is_subclass_of($sSanitizerClass, 'HTMLSanitizer')) {
|
||||
if ($sConfigKey === 'html_sanitizer') {
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
if ($sConfigKey === 'svg_sanitizer') {
|
||||
} else {
|
||||
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
|
||||
|
||||
return $sHTML;
|
||||
|
||||
@@ -961,7 +961,9 @@ class ToolsLog extends LogAPI
|
||||
|
||||
/**
|
||||
* @see \CMDBSource::LogDeadLock()
|
||||
* @since 2.7.1
|
||||
* @since 2.7.1 PR #139
|
||||
*
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
|
||||
*/
|
||||
class DeadLockLog extends LogAPI
|
||||
{
|
||||
@@ -986,10 +988,10 @@ class DeadLockLog extends LogAPI
|
||||
{
|
||||
switch ($iMysqlErrorNo)
|
||||
{
|
||||
case 1205:
|
||||
case CMDBSource::MYSQL_ERRNO_WAIT_TIMEOUT:
|
||||
return self::CHANNEL_WAIT_TIMEOUT;
|
||||
break;
|
||||
case 1213:
|
||||
case CMDBSource::MYSQL_ERRNO_DEADLOCK:
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
break;
|
||||
default:
|
||||
@@ -1019,7 +1021,14 @@ class DeadLockLog extends LogAPI
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°3731
|
||||
* Starting with the WARNING level we will log in a dedicated file (/log/deprecated-calls.log) :
|
||||
* - iTop deprecated files or code
|
||||
* - protected trigger_error calls with E_DEPRECATED or E_USER_DEPRECATED
|
||||
*
|
||||
* For the last category, if {@see utils::IsDevelopmentEnvironment()} is true we will do a trigger_error()
|
||||
*
|
||||
* @since 3.0.0 N°3731 first implementation
|
||||
* @link https://www.itophub.io/wiki/page?id=latest:admin:log:channels#deprecated_calls channel used
|
||||
*/
|
||||
class DeprecatedCallsLog extends LogAPI
|
||||
{
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
$ibo-attachment--datatable--icon-preview--max-height: 44px !default;
|
||||
$ibo-attachment--datatable--icon-preview--max-width: $ibo-attachment--datatable--icon-preview--max-height !default;
|
||||
|
||||
$ibo-attachment--datatable--line-height: $ibo-attachment--datatable--icon-preview--max-height !default;
|
||||
|
||||
$ibo-attachment--datatable--first-column--line-height: 0px !default;
|
||||
|
||||
$ibo-attachment--drag-in--border: 2px $ibo-color-grey-400 dashed !default;
|
||||
$ibo-attachment--upload-file--drop-zone-hint--max-height: 200px !default;
|
||||
$ibo-attachment--upload-file--drop-zone-hint--margin: 22px $ibo-spacing-0 !default;
|
||||
@@ -33,10 +29,7 @@ $ibo-attachment--tab-header--drop-in--icon--color: $ibo-color-blue-600 !default;
|
||||
max-width: $ibo-attachment--datatable--icon-preview--max-width;
|
||||
}
|
||||
.ibo-attachment--datatable tbody tr td {
|
||||
line-height: $ibo-attachment--datatable--line-height;
|
||||
}
|
||||
.ibo-attachment--datatable tbody tr td:nth-child(1){
|
||||
line-height: $ibo-attachment--datatable--first-column--line-height;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ibo-attachment--upload-file--drop-zone-hint{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -69,7 +69,7 @@ class UserLocal extends UserInternal
|
||||
const EXPIRE_NEVER = 'never_expire';
|
||||
const EXPIRE_FORCE = 'force_expire';
|
||||
const EXPIRE_ONE_TIME_PWD = 'otp_expire';
|
||||
|
||||
|
||||
/** @var UserLocalPasswordValidity|null */
|
||||
protected $m_oPasswordValidity = null;
|
||||
|
||||
@@ -160,7 +160,7 @@ class UserLocal extends UserInternal
|
||||
|
||||
/**
|
||||
* Use with care!
|
||||
*/
|
||||
*/
|
||||
public function SetPassword($sNewPassword)
|
||||
{
|
||||
$this->Set('password', $sNewPassword);
|
||||
@@ -197,19 +197,39 @@ class UserLocal extends UserInternal
|
||||
|
||||
protected function OnWrite()
|
||||
{
|
||||
if (empty($this->m_oPasswordValidity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('password_renewed_date', $this->ListChanges()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->m_oPasswordValidity))
|
||||
{
|
||||
//password unchanged
|
||||
if (is_null($this->Get('password_renewed_date')))
|
||||
{
|
||||
//initialize password_renewed_date with User creation date
|
||||
$sKey = $this->GetKey();
|
||||
$sOql = <<<OQL
|
||||
SELECT CMDBChangeOpCreate AS ccc
|
||||
JOIN CMDBChange AS c ON ccc.change = c.id
|
||||
WHERE ccc.objclass="UserLocal" AND ccc.objkey="$sKey"
|
||||
OQL;
|
||||
$oCmdbChangeOpSearch = \DBObjectSearch::FromOQL($sOql);
|
||||
$oSet = new \DBObjectSet($oCmdbChangeOpSearch);
|
||||
$oCMDBChangeOpCreate = $oSet->Fetch();
|
||||
if (! is_null($oCMDBChangeOpCreate))
|
||||
{
|
||||
$oUserCreationDateTime = \DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oCMDBChangeOpCreate->Get('date'));
|
||||
$sCreationDate = $oUserCreationDateTime->format(\AttributeDate::GetInternalFormat());
|
||||
$this->Set('password_renewed_date', $sCreationDate);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$sNow = date(\AttributeDate::GetInternalFormat());
|
||||
$this->Set('password_renewed_date', $sNow);
|
||||
|
||||
|
||||
// Reset the "force" expiration flag when the user updates her/his own password!
|
||||
if ($this->IsCurrentUser())
|
||||
{
|
||||
@@ -294,7 +314,7 @@ class UserLocal extends UserInternal
|
||||
{
|
||||
$this->m_aCheckIssues[] = $this->m_oPasswordValidity->getPasswordValidityMessage();
|
||||
}
|
||||
|
||||
|
||||
// A User cannot force a one-time password on herself/himself
|
||||
if ($this->IsCurrentUser()) {
|
||||
if (array_key_exists('expiration', $this->ListChanges()) && ($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD)) {
|
||||
|
||||
@@ -137,6 +137,9 @@ try
|
||||
* As a result we're setting a token file to make sure the restore is called by an authenticated user with the correct rights !
|
||||
*/
|
||||
case 'restore_get_token':
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
|
||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||
if ($oRestoreMutex->IsLocked())
|
||||
@@ -149,12 +152,7 @@ try
|
||||
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
|
||||
file_put_contents($sTokenFile, $sFile);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$("#restore_token").val('$sToken');
|
||||
JS
|
||||
);
|
||||
|
||||
$oPage->SetData(['token' => $sToken]);
|
||||
$oPage->output();
|
||||
break;
|
||||
|
||||
|
||||
@@ -471,7 +471,7 @@ function LaunchRestoreNow(sBackupFile, sConfirmationMessage)
|
||||
$.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){
|
||||
|
||||
// Get the value of restore_token
|
||||
$('#backup_errors').append(data);
|
||||
$('#restore_token').val(data.token);
|
||||
|
||||
var oParams = {};
|
||||
oParams.operation = 'restore_exec';
|
||||
|
||||
@@ -40,6 +40,7 @@ use Dict;
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Exception;
|
||||
use ExceptionLog;
|
||||
use InlineImage;
|
||||
use IssueLog;
|
||||
use MetaModel;
|
||||
@@ -1142,6 +1143,7 @@ class ObjectFormManager extends FormManager
|
||||
|
||||
$sObjectClass = get_class($this->oObject);
|
||||
|
||||
$bExceptionLogged = false;
|
||||
try {
|
||||
// modification flags
|
||||
$bIsNew = $this->oObject->IsNew();
|
||||
@@ -1163,6 +1165,14 @@ class ObjectFormManager extends FormManager
|
||||
throw new Exception($e->getHtmlMessage());
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$aContext = [
|
||||
'origin' => __CLASS__.'::'.__METHOD__,
|
||||
'obj_class' => get_class($this->oObject),
|
||||
'obj_key' => $this->oObject->GetKey(),
|
||||
];
|
||||
ExceptionLog::LogException($e, $aContext);
|
||||
$bExceptionLogged = true;
|
||||
|
||||
if ($bIsNew) {
|
||||
throw new Exception(Dict::S('Portal:Error:ObjectCannotBeCreated'));
|
||||
}
|
||||
@@ -1222,11 +1232,12 @@ class ObjectFormManager extends FormManager
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
$aData['valid'] = false;
|
||||
$aData['messages']['error'] += array('_main' => array($e->getMessage()));
|
||||
IssueLog::Error(__METHOD__.' at line '.__LINE__.' : '.$e->getMessage());
|
||||
if (false === $bExceptionLogged) {
|
||||
IssueLog::Error(__METHOD__.' at line '.__LINE__.' : '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $aData;
|
||||
|
||||
@@ -323,7 +323,7 @@ $(function()
|
||||
oParams.dashletid = sTempDashletId;
|
||||
|
||||
$.post(this.options.new_dashletid_endpoint, oParams, function (data) {
|
||||
me.add_dashlet_prepare(options, data);
|
||||
me.add_dashlet_prepare(options, data.trim());
|
||||
});
|
||||
},
|
||||
add_dashlet_ajax: function (options, sDashletId) {
|
||||
|
||||
@@ -794,8 +794,12 @@ const CombodoTooltip = {
|
||||
InitTooltipFromMarkup: function (oElem, bForce = false) {
|
||||
const oOptions = {};
|
||||
|
||||
// First, check if the tooltip isn't already instantiated
|
||||
if ((oElem.attr('data-tooltip-instantiated') === 'true') && (bForce === false)) {
|
||||
// First, check if the jQuery element actually represent DOM elements
|
||||
if (oElem.length === 0) {
|
||||
return false;
|
||||
}
|
||||
// Then, check if the tooltip isn't already instantiated
|
||||
else if ((oElem.attr('data-tooltip-instantiated') === 'true') && (bForce === false)) {
|
||||
return false;
|
||||
}
|
||||
else if((oElem.attr('data-tooltip-instantiated') === 'true') && (bForce === true) && (oElem[0]._tippy !== undefined)){
|
||||
|
||||
@@ -59,8 +59,43 @@ class ConsoleSimpleFieldRenderer extends FieldRenderer
|
||||
else
|
||||
{
|
||||
$oBlock = FieldUIBlockFactory::MakeStandard($this->oField->GetLabel());
|
||||
$oBlock->AddDataAttribute("input-id",$this->oField->GetGlobalId());
|
||||
$oBlock->AddDataAttribute("input-type",$sFieldClass);
|
||||
$oBlock->SetAttLabel($this->oField->GetLabel())
|
||||
->AddDataAttribute("input-id",$this->oField->GetGlobalId())
|
||||
->AddDataAttribute("input-type",$sFieldClass);
|
||||
|
||||
// Propagate data attribute from Field to UIBlock
|
||||
// Note: This might no longer be necessary after the upcoming attributes rework project
|
||||
foreach ($this->oField->GetMetadata() as $sMetadataKey => $sMetadataValue) {
|
||||
switch ($sMetadataKey) {
|
||||
// Important: Only some data attributes can be overloaded, this is done on purpose (eg. "input-type" set previously by an AttributeCustomFields)
|
||||
case 'attribute-code':
|
||||
case 'attribute-type':
|
||||
case 'input-type':
|
||||
if (utils::IsNotNullOrEmptyString($sMetadataValue)) {
|
||||
switch ($sMetadataKey) {
|
||||
case 'attribute-code':
|
||||
$oBlock->SetAttCode($sMetadataValue);
|
||||
break;
|
||||
|
||||
case 'attribute-type':
|
||||
$oBlock->SetAttType($sMetadataValue ?? '');
|
||||
break;
|
||||
|
||||
case 'input-type':
|
||||
$oBlock->AddDataAttribute($sMetadataKey, $sMetadataValue ?? '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (false === $oBlock->HasDataAttribute($sMetadataKey)) {
|
||||
$oBlock->AddDataAttribute($sMetadataKey, $sMetadataValue ?? '');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($sFieldClass)
|
||||
{
|
||||
case 'Combodo\\iTop\\Form\\Field\\DateTimeField':
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
namespace Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\CaseLogEntryForm;
|
||||
|
||||
use AttributeCaseLog;
|
||||
use cmdbAbstractObject;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\RichText\RichText;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
@@ -105,6 +106,15 @@ class CaseLogEntryForm extends UIContentBlock
|
||||
return $this->sAttCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @since 3.0.4 3.1.0 N°6139
|
||||
*/
|
||||
public function GetAttType(): string
|
||||
{
|
||||
return AttributeCaseLog::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sAttCode
|
||||
* @return string
|
||||
|
||||
@@ -599,6 +599,18 @@ abstract class UIBlock implements iUIBlock
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sName Name of the data attribute
|
||||
*
|
||||
* @return bool True if $sName is already defined (even as a null value) in the UIBLock data attributes, false otherwise
|
||||
* @see static::$aDataAttributes
|
||||
* @since 3.0.4 3.1.0 N°6140
|
||||
*/
|
||||
public function HasDataAttribute(string $sName): bool
|
||||
{
|
||||
return array_key_exists($sName, $this->aDataAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @see static::$aDataAttributes
|
||||
|
||||
@@ -28,7 +28,9 @@ class BlockChartAjaxBars extends UIBlock
|
||||
public $sId;
|
||||
/** @var string */
|
||||
public $sJSURLs;
|
||||
|
||||
/** @var string */
|
||||
public $sURLForRefresh;
|
||||
/** @var int */
|
||||
public $iMaxNbCharsInLabel;
|
||||
|
||||
}
|
||||
@@ -28,6 +28,8 @@ class BlockChartAjaxPie extends UIBlock
|
||||
public $sJSURLs;
|
||||
/** @var string */
|
||||
public $sJSNames;
|
||||
|
||||
/** @var string */
|
||||
public $sURLForRefresh;
|
||||
/** @var int */
|
||||
public $iNbLinesToAddForName;
|
||||
}
|
||||
@@ -1566,7 +1566,7 @@ JS;
|
||||
*/
|
||||
protected function output_dict_entries($bReturnOutput = false)
|
||||
{
|
||||
if ($this->sContentType != 'text/plain' && $this->sContentType != 'application/json') {
|
||||
if ($this->sContentType != 'text/plain' && $this->sContentType != 'application/json' && $this->sContentType != 'application/javascript') {
|
||||
/** @var \iBackofficeDictEntriesExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iBackofficeDictEntriesExtension') as $oExtensionInstance) {
|
||||
foreach ($oExtensionInstance->GetDictEntries() as $sDictEntry) {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
var iChartDefaultHeight = 200,
|
||||
iChartLegendHeight = 6 * {{ oUIBlock.iMaxNbCharsInLabel }},
|
||||
iChartTotalHeight = iChartDefaultHeight+iChartLegendHeight;
|
||||
$('#my_chart_{{ oUIBlock.sId }}').height(iChartTotalHeight+'px');
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: d3.select('#my_chart_{{ oUIBlock.sId }}'),
|
||||
data: {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
// Calculate height of graph : 200px (minimum height for the chart) + 20*iNbLinesToAddForName for the legend
|
||||
var iChartDefaultHeight = 200,
|
||||
iChartLegendHeight = 20 * {{ oUIBlock.iNbLinesToAddForName }} ,
|
||||
iChartTotalHeight = (iChartDefaultHeight+iChartLegendHeight);
|
||||
$('#my_chart_{{ oUIBlock.sId }}').height(iChartTotalHeight+'px');
|
||||
|
||||
var chart = c3.generate({
|
||||
bindto: d3.select('#my_chart_{{ oUIBlock.sId }}'),
|
||||
data: {
|
||||
@@ -9,7 +15,7 @@ var chart = c3.generate({
|
||||
names: {{ oUIBlock.sJSNames|raw }},
|
||||
onclick: function (d) {
|
||||
var aURLs = {{ oUIBlock.sJSURLs|raw }};
|
||||
window.location.href= aURLs[d.index];
|
||||
window.location.href = aURLs[d.index];
|
||||
},
|
||||
order: null
|
||||
},
|
||||
@@ -19,17 +25,19 @@ var chart = c3.generate({
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
value: function (value) { return value; }
|
||||
value: function (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof(charts) === "undefined")
|
||||
if (typeof (charts) === "undefined")
|
||||
{
|
||||
charts = [];
|
||||
refreshChart = [];
|
||||
refreshChart = [];
|
||||
}
|
||||
var idxChart=charts.length;
|
||||
var idxChart = charts.length;
|
||||
charts.push(chart);
|
||||
var refreshChart{{ oUIBlock.sId|sanitize(constant('utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME')) }}=' $.post("{{ oUIBlock.sURLForRefresh|raw }}&refresh='+idxChart+'","", function (data) {'+
|
||||
'charts['+idxChart+'].unload();'+
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
data-object-id="{{ oUIBlock.GetObjectId() }}"
|
||||
data-attribute-code="{{ oUIBlock.GetAttCode() }}"
|
||||
data-attribute-label="{{ oUIBlock.GetAttLabel() }}"
|
||||
data-attribute-type="{{ oUIBlock.GetAttType() }}"
|
||||
data-input-type="{{ constant('cmdbAbstractObject::ENUM_INPUT_TYPE_HTML_EDITOR') }}"
|
||||
data-input-id="{{ oUIBlock.GetTextInput().GetId() }}"
|
||||
data-submit-mode="{{ oUIBlock.GetSubmitMode() }}"
|
||||
method="post">
|
||||
<div class="ibo-caselog-entry-form--actions">
|
||||
|
||||
@@ -294,25 +294,31 @@ class ItopDataTestCase extends ItopTestCase
|
||||
* Create a UserRequest in database
|
||||
*
|
||||
* @param int $iNum
|
||||
* @param int $iTimeSpent
|
||||
* @param int $iOrgId
|
||||
* @param int $iCallerId
|
||||
* @param array $aUserRequestCustomParams set fields values for the UserRequest : attcode as key, att value as value.
|
||||
* If the attcode is already present in the default values, custom value will be kept (see array_merge documentation)
|
||||
*
|
||||
* @return \UserRequest
|
||||
* @throws Exception
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.array-merge.php array_merge PHP function documentation
|
||||
*
|
||||
* @uses \array_merge()
|
||||
* @uses createObject
|
||||
*/
|
||||
protected function CreateUserRequest($iNum, $iTimeSpent = 0, $iOrgId = 0, $iCallerId = 0)
|
||||
{
|
||||
/** @var \UserRequest $oTicket */
|
||||
$oTicket = $this->createObject('UserRequest', array(
|
||||
protected function CreateUserRequest($iNum, $aUserRequestCustomParams = []) {
|
||||
$aUserRequestDefaultParams = [
|
||||
'ref' => 'Ticket_'.$iNum,
|
||||
'title' => 'BUG 1161_'.$iNum,
|
||||
//'request_type' => 'incident',
|
||||
'description' => 'Add aggregate functions',
|
||||
'time_spent' => $iTimeSpent,
|
||||
'caller_id' => $iCallerId,
|
||||
'org_id' => ($iOrgId == 0 ? $this->getTestOrgId() : $iOrgId),
|
||||
));
|
||||
'org_id' => $this->getTestOrgId(),
|
||||
];
|
||||
|
||||
$aUserRequestParams = array_merge($aUserRequestDefaultParams, $aUserRequestCustomParams);
|
||||
|
||||
/** @var \UserRequest $oTicket */
|
||||
$oTicket = $this->createObject('UserRequest', $aUserRequestParams);
|
||||
$this->debug("Created {$oTicket->Get('title')} ({$oTicket->Get('ref')})");
|
||||
|
||||
return $oTicket;
|
||||
|
||||
@@ -56,7 +56,7 @@ class DeadLockInjection
|
||||
if ($this->iRequestCount == $this->iFailAt) {
|
||||
echo "Generating a FAKE DEADLOCK\n";
|
||||
IssueLog::Trace("Generating a FAKE DEADLOCK", 'cmdbsource');
|
||||
throw new MySQLException("FAKE DEADLOCK", [], new Exception("FAKE DEADLOCK", 1213));
|
||||
throw new MySQLException("FAKE DEADLOCK", [], new Exception("FAKE DEADLOCK", CMDBSource::MYSQL_ERRNO_DEADLOCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,11 @@ class DBSearchTest extends ItopDataTestCase
|
||||
$i = 0;
|
||||
foreach($aReq as $aParams)
|
||||
{
|
||||
$oObj = $this->CreateUserRequest($i, $aParams[0], $aOrgIds[$aParams[1]], $aPersonIds[$aParams[2]]);
|
||||
$oObj = $this->CreateUserRequest($i, [
|
||||
'time_spent' => $aParams[0],
|
||||
'org_id' => $aOrgIds[$aParams[1]],
|
||||
'caller_id' => $aPersonIds[$aParams[2]],
|
||||
]);
|
||||
self::assertNotNull($oObj);
|
||||
$i++;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
namespace Combodo\iTop\Test\UnitTest\Module\AuthentLocal;
|
||||
|
||||
|
||||
use AttributeDate;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use Config;
|
||||
use Dict;
|
||||
use MetaModel;
|
||||
use ormLinkSet;
|
||||
use URP_UserProfile;
|
||||
use UserLocal;
|
||||
|
||||
/**
|
||||
@@ -34,23 +40,23 @@ class UserLocalTest extends ItopDataTestCase
|
||||
*/
|
||||
public function testValidatePassword($sPassword, $aValidatorNames, $aConfigValueMap, $bExpectedCheckStatus, $expectedCheckIssues = null, $sUserLanguage = null)
|
||||
{
|
||||
$configMock = $this->createMock(\Config::class);
|
||||
$configMock = $this->createMock(Config::class);
|
||||
$configMock
|
||||
->method('GetModuleSetting')
|
||||
->willReturnMap($aConfigValueMap);
|
||||
restore_error_handler();
|
||||
|
||||
if (isset($sUserLanguage)) {
|
||||
\Dict::SetUserLanguage($sUserLanguage);
|
||||
Dict::SetUserLanguage($sUserLanguage);
|
||||
}
|
||||
|
||||
/** @var UserLocal $oUserLocal */
|
||||
$oUserLocal = \MetaModel::NewObject('UserLocal', array('login' => 'john'));
|
||||
/** @var \ormLinkSet $oProfileSet */
|
||||
$oUserLocal = MetaModel::NewObject(UserLocal::class, array('login' => 'john'));
|
||||
/** @var ormLinkSet $oProfileSet */
|
||||
$oProfileSet = $oUserLocal->Get('profile_list');
|
||||
|
||||
$oProfileSet->AddItem(
|
||||
\MetaModel::NewObject('URP_UserProfile', array('profileid' => 1))
|
||||
MetaModel::NewObject(URP_UserProfile::class, array('profileid' => 1))
|
||||
);
|
||||
|
||||
$aValidatorCollection = array();
|
||||
@@ -242,9 +248,10 @@ class UserLocalTest extends ItopDataTestCase
|
||||
*/
|
||||
public function testPasswordRenewal($sBefore, $sExpectedAfter)
|
||||
{
|
||||
$oBefore = is_null($sBefore) ? null : date(\AttributeDate::GetInternalFormat(), strtotime($sBefore));
|
||||
$oNow = date(\AttributeDate::GetInternalFormat());
|
||||
$oExpectedAfter = is_null($sExpectedAfter) ? null : date(\AttributeDate::GetInternalFormat(), strtotime($sExpectedAfter));
|
||||
$sDateFormat = AttributeDate::GetInternalFormat();
|
||||
$oBefore = is_null($sBefore) ? null : date($sDateFormat, strtotime($sBefore));
|
||||
$oNow = date($sDateFormat);
|
||||
$oExpectedAfter = is_null($sExpectedAfter) ? null : date($sDateFormat, strtotime($sExpectedAfter));
|
||||
|
||||
$aUserLocalValues = array('login' => 'john');
|
||||
if (!is_null($oBefore))
|
||||
@@ -253,15 +260,14 @@ class UserLocalTest extends ItopDataTestCase
|
||||
}
|
||||
|
||||
/** @var UserLocal $oUserLocal */
|
||||
$oUserLocal = \MetaModel::NewObject('UserLocal', $aUserLocalValues);
|
||||
/** @var \ormLinkSet $oProfileSet */
|
||||
$oUserLocal = MetaModel::NewObject(UserLocal::class, $aUserLocalValues);
|
||||
/** @var ormLinkSet $oProfileSet */
|
||||
$oProfileSet = $oUserLocal->Get('profile_list');
|
||||
|
||||
$oProfileSet->AddItem(
|
||||
\MetaModel::NewObject('URP_UserProfile', array('profileid' => 1))
|
||||
MetaModel::NewObject(URP_UserProfile::class, array('profileid' => 1))
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals($oBefore, $oUserLocal->Get('password_renewed_date'));
|
||||
|
||||
//INSERT
|
||||
@@ -270,17 +276,19 @@ class UserLocalTest extends ItopDataTestCase
|
||||
$this->assertEquals($oNow, $oUserLocal->Get('password_renewed_date'), 'INSERT sets the "password_renewed_date" to the current date');
|
||||
|
||||
//UPDATE password_renewed_date
|
||||
$oUserLocal = MetaModel::GetObject(UserLocal::class, $oUserLocal->GetKey());
|
||||
$oUserLocal->Set('password_renewed_date', $oBefore);
|
||||
$oUserLocal->DBWrite();
|
||||
$this->assertEquals($oBefore, $oUserLocal->Get('password_renewed_date'), 'UPDATE can target and change the "password_renewed_date"');
|
||||
|
||||
//UPDATE password
|
||||
$oUserLocal = MetaModel::GetObject(UserLocal::class, $oUserLocal->GetKey());
|
||||
$oUserLocal->Set('password', 'fooBar1???1');
|
||||
$oUserLocal->DBWrite();
|
||||
$this->assertEquals($oExpectedAfter, $oUserLocal->Get('password_renewed_date'), 'UPDATE "password" fields trigger automatic change of the "password_renewed_date" field');
|
||||
|
||||
|
||||
//UPDATE both password & password_renewed_date
|
||||
$oUserLocal = MetaModel::GetObject(UserLocal::class, $oUserLocal->GetKey());
|
||||
$oUserLocal->Set('password', 'fooBar1???2');
|
||||
$oUserLocal->Set('password_renewed_date', $oBefore);
|
||||
$oUserLocal->DBWrite();
|
||||
@@ -304,5 +312,85 @@ class UserLocalTest extends ItopDataTestCase
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider CanExpireFixProvider
|
||||
*
|
||||
*/
|
||||
public function testCanExpireFix($sExpirationMode, $sBefore, bool $bRenewedDateTouched)
|
||||
{
|
||||
$oBefore = is_null($sBefore) ? null : date(AttributeDate::GetInternalFormat(), strtotime($sBefore));
|
||||
$oNow = date(AttributeDate::GetInternalFormat());
|
||||
$oExpectedAfter = $bRenewedDateTouched ? $oNow : $oBefore;
|
||||
|
||||
$aUserLocalValues = array('login' => 'john');
|
||||
if (!is_null($oBefore))
|
||||
{
|
||||
$aUserLocalValues['password_renewed_date'] = $oBefore;
|
||||
}
|
||||
|
||||
/** @var UserLocal $oUserLocal */
|
||||
$oUserLocal = MetaModel::NewObject(UserLocal::class, $aUserLocalValues);
|
||||
/** @var ormLinkSet $oProfileSet */
|
||||
$oProfileSet = $oUserLocal->Get('profile_list');
|
||||
|
||||
$oProfileSet->AddItem(
|
||||
MetaModel::NewObject(URP_UserProfile::class, array('profileid' => 1))
|
||||
);
|
||||
|
||||
$this->assertEquals($oBefore, $oUserLocal->Get('password_renewed_date'));
|
||||
|
||||
//INSERT
|
||||
$oUserLocal->Set('password', 'fooBar1???');
|
||||
$oUserLocal->DBWrite();
|
||||
$this->assertEquals($oNow, $oUserLocal->Get('password_renewed_date'), 'INSERT sets the "password_renewed_date" to the current date');
|
||||
|
||||
$oUserLocal = MetaModel::GetObject(UserLocal::class, $oUserLocal->GetKey());
|
||||
$oUserLocal->Set('password_renewed_date', $oBefore);
|
||||
$oUserLocal->DBWrite();
|
||||
$this->assertEquals($oBefore, $oUserLocal->Get('password_renewed_date'), 'UPDATE can target and change the "password_renewed_date"');
|
||||
|
||||
//UPDATE password
|
||||
$oUserLocal = MetaModel::GetObject(UserLocal::class, $oUserLocal->GetKey());
|
||||
$oUserLocal->Set('expiration', $sExpirationMode);
|
||||
$oUserLocal->DBWrite();
|
||||
$this->assertEquals($oExpectedAfter, $oUserLocal->Get('password_renewed_date'), 'UPDATE "password" fields trigger automatic change of the "password_renewed_date" field');
|
||||
}
|
||||
|
||||
public function CanExpireFixProvider()
|
||||
{
|
||||
return array(
|
||||
'EXPIRE_CAN: nominal case' => array(
|
||||
'sExpirationMode' => 'can_expire',
|
||||
'oExpectedBefore' => null,
|
||||
'bRenewedDateTouched' => true,
|
||||
),
|
||||
'EXPIRE_NEVER (default mode): nothing changed on UserLocal' => array(
|
||||
'sExpirationMode' => 'never_expire',
|
||||
'oExpectedBefore' => null,
|
||||
'bRenewedDateTouched' => false,
|
||||
),
|
||||
'EXPIRE_FORCE: nominal case' => array(
|
||||
'sExpirationMode' => 'force_expire',
|
||||
'oExpectedBefore' => null,
|
||||
'bRenewedDateTouched' => true,
|
||||
),
|
||||
'EXPIRE_ONE_TIME_PWD: nominal case' => array(
|
||||
'sExpirationMode' => 'otp_expire',
|
||||
'oExpectedBefore' => null,
|
||||
'bRenewedDateTouched' => true,
|
||||
),
|
||||
'date initiated' => array(
|
||||
'sExpirationMode' => 'can_expire',
|
||||
'oBefore' => '-1 day',
|
||||
'bRenewedDateTouched' => false,
|
||||
),
|
||||
'date initiated in the future' => array(
|
||||
'sExpirationMode' => 'can_expire',
|
||||
'oBefore' => '+1 day',
|
||||
'bRenewedDateTouched' => false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Combodo\iTop\Test\UnitTest\Webservices;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,11 +21,13 @@ class RestTest extends ItopDataTestCase
|
||||
{
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
const MODE = [ 'JSONDATA_AS_STRING' => 0, 'JSONDATA_AS_FILE' => 1 , 'NO_JSONDATA' => 2 ];
|
||||
const ENUM_JSONDATA_AS_STRING = 0;
|
||||
const ENUM_JSONDATA_AS_FILE = 1;
|
||||
const ENUM_JSONDATA_NONE = 2;
|
||||
|
||||
private $sTmpFile = "";
|
||||
/** @var int $iJsonDataMode */
|
||||
private $sJsonDataMode;
|
||||
private $iJsonDataMode = self::ENUM_JSONDATA_AS_STRING;
|
||||
private $sUrl;
|
||||
private $sLogin;
|
||||
private $sPassword = "Iuytrez9876543ç_è-(";
|
||||
@@ -42,10 +46,10 @@ class RestTest extends ItopDataTestCase
|
||||
unlink($this->sTmpFile);
|
||||
}
|
||||
|
||||
$this->sUrl = \MetaModel::GetConfig()->Get('app_root_url');
|
||||
$this->sUrl = MetaModel::GetConfig()->Get('app_root_url');
|
||||
|
||||
$oRestProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'REST Services User'), true);
|
||||
$oAdminProfile = \MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true);
|
||||
$oRestProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'REST Services User'), true);
|
||||
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true);
|
||||
|
||||
if (is_object($oRestProfile) && is_object($oAdminProfile)) {
|
||||
$oUser = $this->CreateUser($this->sLogin, $oRestProfile->GetKey(), $this->sPassword);
|
||||
@@ -53,6 +57,28 @@ class RestTest extends ItopDataTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testJSONPCallback()
|
||||
{
|
||||
$sCallbackName = 'fooCallback';
|
||||
$sJsonData = <<<JSON
|
||||
{
|
||||
"operation": "list_operations"
|
||||
}
|
||||
JSON;
|
||||
|
||||
// Test regular JSON result
|
||||
$sJSONResult = $this->CallRestApi($sJsonData);
|
||||
// - Try to decode JSON to array to check if it is well-formed
|
||||
$aJSONResultAsArray = json_decode($sJSONResult, true);
|
||||
if (false === is_array($aJSONResultAsArray)) {
|
||||
$this->fail('JSON result could not be decoded as array, it might be malformed');
|
||||
}
|
||||
|
||||
// Test JSONP with callback by checking that it is the same as the regular JSON but within the JS callback
|
||||
$sJSONPResult = $this->CallRestApi($sJsonData, $sCallbackName);
|
||||
$this->assertEquals($sCallbackName.'('.$sJSONResult.')', $sJSONPResult, 'JSONP response callback does not match expected result');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider BasicProvider
|
||||
* @param int $iJsonDataMode
|
||||
@@ -67,7 +93,7 @@ class RestTest extends ItopDataTestCase
|
||||
$aJson = json_decode($sOuputJson, true);
|
||||
$this->assertNotNull($aJson, "Cannot decode returned JSON : $sOuputJson");
|
||||
|
||||
if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){
|
||||
if ($this->iJsonDataMode === static::ENUM_JSONDATA_NONE){
|
||||
$this->assertStringContainsString("3", "".$aJson['code'], $sOuputJson);
|
||||
$this->assertStringContainsString("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson);
|
||||
return;
|
||||
@@ -121,7 +147,7 @@ JSON;
|
||||
$sOuputJson = $this->CreateTicketViaApi($description);
|
||||
$aJson = json_decode($sOuputJson, true);
|
||||
|
||||
if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){
|
||||
if ($this->iJsonDataMode === static::ENUM_JSONDATA_NONE){
|
||||
$this->assertStringContainsString("3", "".$aJson['code'], $sOuputJson);
|
||||
$this->assertStringContainsString("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson);
|
||||
return;
|
||||
@@ -160,7 +186,7 @@ JSON;
|
||||
$sOuputJson = $this->CreateTicketViaApi($description);
|
||||
$aJson = json_decode($sOuputJson, true);
|
||||
|
||||
if ($this->iJsonDataMode === self::MODE['NO_JSONDATA']){
|
||||
if ($this->iJsonDataMode === static::ENUM_JSONDATA_NONE){
|
||||
$this->assertStringContainsString("3", "".$aJson['code'], $sOuputJson);
|
||||
$this->assertStringContainsString("Error: Missing parameter 'json_data'", "".$aJson['message'], $sOuputJson);
|
||||
return;
|
||||
@@ -198,9 +224,9 @@ JSON;
|
||||
|
||||
public function BasicProvider(){
|
||||
return [
|
||||
'call rest call' => [ 'sJsonDataMode' => self::MODE['JSONDATA_AS_STRING']],
|
||||
'pass json_data as file' => [ 'sJsonDataMode' => self::MODE['JSONDATA_AS_FILE']],
|
||||
'no json data' => [ 'sJsonDataMode' => self::MODE['NO_JSONDATA']]
|
||||
'call rest call' => [ 'sJsonDataMode' => static::ENUM_JSONDATA_AS_STRING],
|
||||
'pass json_data as file' => [ 'sJsonDataMode' => static::ENUM_JSONDATA_AS_FILE],
|
||||
'no json data' => [ 'sJsonDataMode' => static::ENUM_JSONDATA_NONE]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -246,7 +272,7 @@ JSON;
|
||||
|
||||
}
|
||||
|
||||
private function CallRestApi($sJsonDataContent){
|
||||
private function CallRestApi(string $sJsonDataContent, string $sCallbackName = null){
|
||||
$ch = curl_init();
|
||||
$aPostFields = [
|
||||
'version' => '1.3',
|
||||
@@ -254,16 +280,20 @@ JSON;
|
||||
'auth_pwd' => $this->sPassword,
|
||||
];
|
||||
|
||||
if ($this->iJsonDataMode === self::MODE['JSONDATA_AS_STRING']){
|
||||
if ($this->iJsonDataMode === static::ENUM_JSONDATA_AS_STRING) {
|
||||
$this->sTmpFile = tempnam(sys_get_temp_dir(), 'jsondata_');
|
||||
file_put_contents($this->sTmpFile, $sJsonDataContent);
|
||||
|
||||
$oCurlFile = curl_file_create($this->sTmpFile);
|
||||
$aPostFields['json_data'] = $oCurlFile;
|
||||
}else if ($this->iJsonDataMode === self::MODE['JSONDATA_AS_FILE']){
|
||||
} else if ($this->iJsonDataMode === static::ENUM_JSONDATA_AS_FILE) {
|
||||
$aPostFields['json_data'] = $sJsonDataContent;
|
||||
}
|
||||
|
||||
if (utils::IsNotNullOrEmptyString($sCallbackName)) {
|
||||
$aPostFields['callback'] = $sCallbackName;
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, "$this->sUrl/webservices/rest.php");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
|
||||
Reference in New Issue
Block a user