mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-09 03:38:43 +02:00
Compare commits
48 Commits
odain
...
feature/88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fd23b7042 | ||
|
|
30f80b0297 | ||
|
|
3450aebb2e | ||
|
|
8db1e2b604 | ||
|
|
b86226ef7f | ||
|
|
83ff3b1629 | ||
|
|
b3a640564b | ||
|
|
91e6b157b7 | ||
|
|
ba76877f81 | ||
|
|
c89a75716e | ||
|
|
93b658646b | ||
|
|
3590ddedf0 | ||
|
|
0d0261b0cc | ||
|
|
7dd6903739 | ||
|
|
7f87182b15 | ||
|
|
2c380e8561 | ||
|
|
9cfaf59669 | ||
|
|
4ed3323956 | ||
|
|
271fed210a | ||
|
|
7bec8d9ad0 | ||
|
|
9362a0b0ae | ||
|
|
4f5d6c47fa | ||
|
|
45e0cee1ee | ||
|
|
fb8b4a07b3 | ||
|
|
dec5da0136 | ||
|
|
63532d20a5 | ||
|
|
5ae8edd2c0 | ||
|
|
a13543c0d3 | ||
|
|
8e581c903b | ||
|
|
c9fe2eda36 | ||
|
|
bd01cbb031 | ||
|
|
15eb0f86d7 | ||
|
|
12a06fcfc0 | ||
|
|
4743cdcf0f | ||
|
|
0d1af2964a | ||
|
|
47b93175b3 | ||
|
|
9b2259726b | ||
|
|
90ace885cd | ||
|
|
b667df2d6c | ||
|
|
99cfe95c32 | ||
|
|
10d1f5735e | ||
|
|
c94ba187bb | ||
|
|
9396fe6815 | ||
|
|
70f77aac68 | ||
|
|
e1d0b0d200 | ||
|
|
be29343b49 | ||
|
|
070b96c9b5 | ||
|
|
167cb84c93 |
@@ -75,10 +75,13 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|mixed
|
||||
* @return bool
|
||||
*/
|
||||
private function GetAuthUser()
|
||||
{
|
||||
return MetaModel::GetConfig()->GetExternalAuthenticationVariable();
|
||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
||||
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
|
||||
/** @var string $sAuthUser */
|
||||
return $sAuthUser; // Retrieve the value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ JS
|
||||
<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->sInputid}">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}">
|
||||
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
|
||||
</form>
|
||||
|
||||
@@ -68,7 +68,7 @@ class UISearchFormForeignKeys
|
||||
<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->m_iInputId}">
|
||||
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
|
||||
</form>
|
||||
|
||||
@@ -75,7 +75,6 @@ define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
||||
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
||||
define('DEFAULT_HASH_ALGO', PASSWORD_DEFAULT);
|
||||
|
||||
/**
|
||||
* Config
|
||||
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
|
||||
@@ -869,14 +868,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'ext_auth_variable' => [
|
||||
'type' => 'string',
|
||||
'description' => 'External authentication expression (allowed: $_SERVER[\'key\'], $_COOKIE[\'key\'], $_REQUEST[\'key\'], getallheaders()[\'Header-Name\'])',
|
||||
'default' => '$_SERVER[\'REMOTE_USER\']',
|
||||
'value' => '$_SERVER[\'REMOTE_USER\']',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'login_debug' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Activate the login FSM debug',
|
||||
@@ -1619,7 +1610,7 @@ class Config
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'search_manual_submit' => [
|
||||
'type' => 'bool',
|
||||
'type' => 'array',
|
||||
'description' => 'Force manual submit of search all requests',
|
||||
'default' => false,
|
||||
'value' => true,
|
||||
@@ -1976,6 +1967,11 @@ class Config
|
||||
*/
|
||||
protected $m_sDefaultLanguage;
|
||||
|
||||
/**
|
||||
* @var string Name of the PHP variable in which external authentication information is passed by the web server
|
||||
*/
|
||||
protected $m_sExtAuthVariable;
|
||||
|
||||
/**
|
||||
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
|
||||
* unless you want to import a database from another iTop instance, in which case you must use
|
||||
@@ -2044,6 +2040,7 @@ class Config
|
||||
$this->m_iFastReloadInterval = DEFAULT_FAST_RELOAD_INTERVAL;
|
||||
$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
|
||||
$this->m_sDefaultLanguage = 'EN US';
|
||||
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_aCharsets = [];
|
||||
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
|
||||
$this->m_iPasswordHashAlgo = DEFAULT_HASH_ALGO;
|
||||
@@ -2189,6 +2186,7 @@ class Config
|
||||
$this->m_aModuleSettings = isset($MyModuleSettings) ? $MyModuleSettings : [];
|
||||
|
||||
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
|
||||
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
|
||||
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
|
||||
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
|
||||
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : [];
|
||||
@@ -2350,73 +2348,9 @@ class Config
|
||||
return explode('|', $this->m_aSettings['allowed_login_types']['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|mixed
|
||||
* @since 3.2.3 return the parsed value instead of an unsecured variable name
|
||||
*/
|
||||
public function GetExternalAuthenticationVariable()
|
||||
{
|
||||
$sExpression = $this->Get('ext_auth_variable');
|
||||
$aParsed = $this->ParseExternalAuthVariableExpression($sExpression);
|
||||
if ($aParsed === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sKey = $aParsed['key'];
|
||||
switch ($aParsed['type']) {
|
||||
case 'server':
|
||||
return $_SERVER[$sKey] ?? false;
|
||||
case 'cookie':
|
||||
return $_COOKIE[$sKey] ?? false;
|
||||
case 'request':
|
||||
return $_REQUEST[$sKey] ?? false;
|
||||
case 'header':
|
||||
if (!function_exists('getallheaders')) {
|
||||
return false;
|
||||
}
|
||||
$aHeaders = getallheaders();
|
||||
if (!is_array($aHeaders)) {
|
||||
return false;
|
||||
}
|
||||
return $aHeaders[$sKey] ?? false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sExpression
|
||||
* @return array|null
|
||||
*/
|
||||
private function ParseExternalAuthVariableExpression($sExpression)
|
||||
{
|
||||
// If it's a configuration parameter it's probably already trimmed, but just in case
|
||||
$sExpression = trim((string) $sExpression);
|
||||
if ($sExpression === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Match $_SERVER/$_COOKIE/$_REQUEST['key'] with optional whitespace and single/double quotes.
|
||||
if (preg_match('/^\$_(SERVER|COOKIE|REQUEST)\s*\[\s*(["\'])\s*([^"\']+)\2\s*\]\s*$/', $sExpression, $aMatches) === 1) {
|
||||
$sContext = strtoupper($aMatches[1]);
|
||||
$sKey = $aMatches[3];
|
||||
return [
|
||||
'type' => strtolower($sContext),
|
||||
'key' => $sKey,
|
||||
'normalized' => '$_'.$sContext.'[\''.$sKey.'\']',
|
||||
];
|
||||
}
|
||||
|
||||
// Match getallheaders()['Header-Name'] in a case-insensitive way.
|
||||
if (preg_match('/^getallheaders\(\)\s*\[\s*(["\'])\s*([^"\']+)\1\s*\]\s*$/i', $sExpression, $aMatches) === 1) {
|
||||
$sKey = $aMatches[2];
|
||||
return [
|
||||
'type' => 'header',
|
||||
'key' => $sKey,
|
||||
'normalized' => 'getallheaders()[\''.$sKey.'\']',
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->m_sExtAuthVariable;
|
||||
}
|
||||
|
||||
public function GetCSVImportCharsets()
|
||||
@@ -2512,7 +2446,7 @@ class Config
|
||||
|
||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||
{
|
||||
$this->Set('ext_auth_variable', $sExtAuthVariable);
|
||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||
}
|
||||
|
||||
public function SetEncryptionKey($sKey)
|
||||
@@ -2566,6 +2500,7 @@ class Config
|
||||
$aSettings['fast_reload_interval'] = $this->m_iFastReloadInterval;
|
||||
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
|
||||
$aSettings['default_language'] = $this->m_sDefaultLanguage;
|
||||
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
|
||||
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
|
||||
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
|
||||
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
|
||||
@@ -2668,6 +2603,7 @@ class Config
|
||||
// Old fashioned remaining values
|
||||
$aOtherValues = [
|
||||
'default_language' => $this->m_sDefaultLanguage,
|
||||
'ext_auth_variable' => $this->m_sExtAuthVariable,
|
||||
'encryption_key' => $this->m_sEncryptionKey,
|
||||
'encryption_library' => $this->m_sEncryptionLibrary,
|
||||
'csv_import_charsets' => $this->m_aCharsets,
|
||||
|
||||
@@ -123,6 +123,7 @@ class HubController
|
||||
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
|
||||
}
|
||||
// First step: prepare the datamodel, if it fails, roll-back
|
||||
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
||||
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
|
||||
|
||||
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
|
||||
|
||||
@@ -1983,18 +1983,6 @@
|
||||
<rank>3</rank>
|
||||
<class>LogicalVolume</class>
|
||||
</dashlet>
|
||||
<dashlet id="FiberChannelInterface" xsi:type="DashletBadge" _delta="define">
|
||||
<rank>1.3</rank>
|
||||
<class>FiberChannelInterface</class>
|
||||
</dashlet>
|
||||
<dashlet id="NASFileSystem" xsi:type="DashletBadge" _delta="define">
|
||||
<rank>3.3</rank>
|
||||
<class>NASFileSystem</class>
|
||||
</dashlet>
|
||||
<dashlet id="Tape" xsi:type="DashletBadge" _delta="define">
|
||||
<rank>3.5</rank>
|
||||
<class>Tape</class>
|
||||
</dashlet>
|
||||
</dashlets>
|
||||
</cell>
|
||||
</cells>
|
||||
|
||||
@@ -189,14 +189,11 @@ $(function()
|
||||
this.buildData.script_code = '';
|
||||
this.buildData.style_code = '';
|
||||
|
||||
for (let i in oData.updated_fields)
|
||||
for (var i in oData.updated_fields)
|
||||
{
|
||||
const oUpdatedField = oData.updated_fields[i];
|
||||
const oPreviousField = this.options.fields_list[oUpdatedField.id];
|
||||
if (!oPreviousField || JSON.stringify(oPreviousField) !== JSON.stringify(oUpdatedField)) {
|
||||
this.options.fields_list[oUpdatedField.id] = oUpdatedField;
|
||||
this._prepareField(oUpdatedField.id);
|
||||
}
|
||||
var oUpdatedField = oData.updated_fields[i];
|
||||
this.options.fields_list[oUpdatedField.id] = oUpdatedField;
|
||||
this._prepareField(oUpdatedField.id);
|
||||
}
|
||||
|
||||
// Adding code to the dom
|
||||
|
||||
@@ -301,112 +301,88 @@ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue, origi
|
||||
return true; // Do not stop propagation ??
|
||||
}
|
||||
|
||||
function EvaluateCKEditorValidation(oOptions)
|
||||
function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
|
||||
{
|
||||
let oField = $('#'+oOptions.sFieldId);
|
||||
let oField = $('#'+sFieldId);
|
||||
if (oField.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let oCKEditor = CombodoCKEditorHandler.GetInstanceSynchronous('#'+oOptions.sFieldId);
|
||||
let bValid = true;
|
||||
let sExplain = '';
|
||||
let sTextContent;
|
||||
let sTextOriginalContents;
|
||||
let oCKEditor = CombodoCKEditorHandler.GetInstanceSynchronous('#'+sFieldId);
|
||||
|
||||
var bValid;
|
||||
var sExplain = '';
|
||||
if (oField.prop('disabled')) {
|
||||
bValid = true; // disabled fields are not checked
|
||||
} else {
|
||||
// If the CKEditor is not yet loaded, we need to wait for it to be ready
|
||||
// but as we need this function to be synchronous, we need to call it again when the CKEditor is ready
|
||||
if (oCKEditor === undefined){
|
||||
CombodoCKEditorHandler.GetInstance('#'+oOptions.sFieldId).then((oCKEditor) => {
|
||||
oOptions.onRetry();
|
||||
CombodoCKEditorHandler.GetInstance('#'+sFieldId).then((oCKEditor) => {
|
||||
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
|
||||
});
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
let sTextContent;
|
||||
let sFormattedContent = oCKEditor.getData();
|
||||
|
||||
// Get the contents without the tags
|
||||
// Check if we have a formatted content that is HTML, otherwise we just have plain text, and we can use it directly
|
||||
sTextContent = $(sFormattedContent).length > 0 ? $(sFormattedContent).text() : sFormattedContent;
|
||||
|
||||
if (sTextContent === '')
|
||||
{
|
||||
if (sTextContent === '') {
|
||||
// No plain text, maybe there is just an image
|
||||
let oImg = $(sFormattedContent).find('img');
|
||||
if (oImg.length !== 0)
|
||||
{
|
||||
let oImg = $(sFormattedContent).find("img");
|
||||
if (oImg.length !== 0) {
|
||||
sTextContent = 'image';
|
||||
}
|
||||
}
|
||||
|
||||
let oFormattedOriginalContents = (oOptions.sOriginalValue !== undefined) ? $('<div></div>').html(oOptions.sOriginalValue) : undefined;
|
||||
sTextOriginalContents = (oFormattedOriginalContents !== undefined) ? oFormattedOriginalContents.text() : undefined;
|
||||
// Get the original value without the tags
|
||||
let oFormattedOriginalContents = (originalValue !== undefined) ? $('<div></div>').html(originalValue) : undefined;
|
||||
let sTextOriginalContents = (oFormattedOriginalContents !== undefined) ? oFormattedOriginalContents.text() : undefined;
|
||||
|
||||
if (oOptions.validate !== undefined) {
|
||||
let oValidation = oOptions.validate(sTextContent, sTextOriginalContents);
|
||||
bValid = oValidation.bValid;
|
||||
sExplain = oValidation.sExplain;
|
||||
if (bMandatory && (sTextContent === nullValue)) {
|
||||
bValid = false;
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
} else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents)) {
|
||||
bValid = false;
|
||||
if (sTextOriginalContents === nullValue) {
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
} else {
|
||||
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
|
||||
sExplain = Dict.S('UI:ValueMustBeChanged');
|
||||
}
|
||||
} else {
|
||||
bValid = true;
|
||||
}
|
||||
|
||||
// Put an event to check the field when the content changes, remove the event right after as we'll call this same function again, and we don't want to call the event more than once (especially not ^2 times on each call)
|
||||
|
||||
// Put and event to check the field when the content changes, remove the event right after as we'll call this same function again, and we don't want to call the event more than once (especially not ^2 times on each call)
|
||||
oCKEditor.model.document.once('change:data', (event) => {
|
||||
oOptions.onChange();
|
||||
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
|
||||
});
|
||||
}
|
||||
|
||||
ReportFieldValidationStatus(oOptions.sFieldId, oOptions.sFormId, bValid, sExplain);
|
||||
|
||||
ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain);
|
||||
return bValid;
|
||||
}
|
||||
|
||||
function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue)
|
||||
{
|
||||
return EvaluateCKEditorValidation({
|
||||
sFieldId: sFieldId,
|
||||
sFormId: sFormId,
|
||||
sOriginalValue: originalValue,
|
||||
onRetry: function() {
|
||||
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
|
||||
},
|
||||
onChange: function() {
|
||||
ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue);
|
||||
},
|
||||
validate: function(sTextContent, sTextOriginalContents) {
|
||||
var bValid;
|
||||
var sExplain = '';
|
||||
if (bMandatory && (sTextContent === nullValue)) {
|
||||
bValid = false;
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
} else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents)) {
|
||||
bValid = false;
|
||||
if (sTextOriginalContents === nullValue) {
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
} else {
|
||||
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
|
||||
sExplain = Dict.S('UI:ValueMustBeChanged');
|
||||
}
|
||||
} else {
|
||||
bValid = true;
|
||||
}
|
||||
return {bValid: bValid, sExplain: sExplain};
|
||||
}
|
||||
});
|
||||
}
|
||||
function ResetPwd(id)
|
||||
{
|
||||
// Reset the values of the password fields
|
||||
$('#'+id).val('*****');
|
||||
$('#'+id+'_confirm').val('*****');
|
||||
// And reset the flag, to tell it that the password remains unchanged
|
||||
$('#'+id+'_changed').val(0);
|
||||
// Visual feedback, None when it's Ok
|
||||
$('#v_'+id).html('');
|
||||
// Reset the values of the password fields
|
||||
$('#'+id).val('*****');
|
||||
$('#'+id+'_confirm').val('*****');
|
||||
// And reset the flag, to tell it that the password remains unchanged
|
||||
$('#'+id+'_changed').val(0);
|
||||
// Visual feedback, None when it's Ok
|
||||
$('#v_'+id).html('');
|
||||
}
|
||||
|
||||
// Called whenever the content of a one way encrypted password changes
|
||||
function PasswordFieldChanged(id)
|
||||
{
|
||||
// Set the flag, to tell that the password changed
|
||||
$('#'+id+'_changed').val(1);
|
||||
// Set the flag, to tell that the password changed
|
||||
$('#'+id+'_changed').val(1);
|
||||
}
|
||||
|
||||
// Special validation function for one way encrypted password fields
|
||||
@@ -439,48 +415,37 @@ function ValidatePasswordField(id, sFormId)
|
||||
// to determine if the field is empty or not
|
||||
function ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue)
|
||||
{
|
||||
return EvaluateCKEditorValidation({
|
||||
sFieldId: sFieldId,
|
||||
sFormId: sFormId,
|
||||
sOriginalValue: originalValue,
|
||||
onRetry: function() {
|
||||
ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);
|
||||
},
|
||||
onChange: function() {
|
||||
ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);
|
||||
},
|
||||
validate: function(sTextContent, sTextOriginalContents) {
|
||||
var bValid;
|
||||
var sExplain = '';
|
||||
// CaseLog is special: history count matters when deciding if the field is empty
|
||||
var count = $('#'+sFieldId+'_count').val();
|
||||
var bValid = true;
|
||||
var sExplain = '';
|
||||
var sTextContent;
|
||||
|
||||
if ($('#'+sFieldId).prop('disabled'))
|
||||
{
|
||||
bValid = true; // disabled fields are not checked
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the contents (with tags)
|
||||
// Note: For CaseLog we can't retrieve the formatted contents from CKEditor (unlike in ValidateCKEditorField() method) because of the place holder.
|
||||
sTextContent = $('#' + sFieldId).val();
|
||||
var count = $('#'+sFieldId+'_count').val();
|
||||
|
||||
if (bMandatory && (count == 0) && (sTextContent === nullValue))
|
||||
{
|
||||
// No previous entry and no content typed
|
||||
bValid = false;
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
}
|
||||
else if ((sTextOriginalContents !== undefined) && (sTextContent === sTextOriginalContents))
|
||||
{
|
||||
bValid = false;
|
||||
if (sTextOriginalContents === nullValue)
|
||||
{
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value.
|
||||
sExplain = Dict.S('UI:ValueMustBeChanged');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bValid = true;
|
||||
}
|
||||
return {bValid: bValid, sExplain: sExplain};
|
||||
if (bMandatory && (count == 0) && (sTextContent == nullValue))
|
||||
{
|
||||
// No previous entry and no content typed
|
||||
bValid = false;
|
||||
sExplain = Dict.S('UI:ValueMustBeSet');
|
||||
}
|
||||
});
|
||||
else if ((originalValue != undefined) && (sTextContent == originalValue))
|
||||
{
|
||||
bValid = false;
|
||||
sExplain = Dict.S('UI:ValueMustBeChanged');
|
||||
}
|
||||
}
|
||||
ReportFieldValidationStatus(sFieldId, sFormId, bValid, '' /* sExplain */);
|
||||
|
||||
// We need to check periodically as CKEditor doesn't trigger our events. More details in UIHTMLEditorWidget::Display() @ line 92
|
||||
setTimeout(function(){ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);}, 500);
|
||||
}
|
||||
|
||||
// Validate the inputs depending on the current setting
|
||||
|
||||
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
@@ -134,7 +134,6 @@ return array(
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
|
||||
@@ -62,7 +62,7 @@ return array(
|
||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'Pelago\\Emogrifier\\' => array($vendorDir . '/pelago/emogrifier/src'),
|
||||
'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src', $vendorDir . '/league/oauth2-google/src'),
|
||||
'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-google/src', $vendorDir . '/league/oauth2-client/src'),
|
||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
|
||||
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
|
||||
|
||||
@@ -345,8 +345,8 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
),
|
||||
'League\\OAuth2\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/oauth2-client/src',
|
||||
1 => __DIR__ . '/..' . '/league/oauth2-google/src',
|
||||
0 => __DIR__ . '/..' . '/league/oauth2-google/src',
|
||||
1 => __DIR__ . '/..' . '/league/oauth2-client/src',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
@@ -520,7 +520,6 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685
|
||||
'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php',
|
||||
'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php',
|
||||
'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php',
|
||||
|
||||
@@ -36,8 +36,7 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
);
|
||||
}
|
||||
|
||||
73
pages/UI.php
73
pages/UI.php
@@ -5,7 +5,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\SearchHelper;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
@@ -127,6 +126,72 @@ function SetObjectBreadCrumbEntry(DBObject $oObj, WebPage $oPage)
|
||||
$oPage->SetBreadCrumbEntry("ui-details-$sClass-".$oObj->GetKey(), $oObj->Get('friendlyname'), MetaModel::GetName($sClass).': '.$oObj->Get('friendlyname'), '', $sIcon, $sIconType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the result of a search request
|
||||
* @param $oP WebPage Web page for the output
|
||||
* @param $oFilter DBSearch The search of objects to display
|
||||
* @param $bSearchForm boolean Whether or not to display the search form at the top the page
|
||||
* @param $sBaseClass string The base class for the search (can be different from the actual class of the results)
|
||||
* @param $sFormat string The format to use for the output: csv or html
|
||||
* @param $bDoSearch bool True to display the search results below the search form
|
||||
* @param $bSearchFormOpen bool True to display the search form fully expanded (only if $bSearchForm of course)
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = [])
|
||||
{
|
||||
//search block
|
||||
$oBlockForm = null;
|
||||
if ($bSearchForm) {
|
||||
$aParams['open'] = $bSearchFormOpen;
|
||||
if (false === isset($aParams['table_id'])) {
|
||||
$aParams['table_id'] = 'result_1';
|
||||
}
|
||||
if (!empty($sBaseClass)) {
|
||||
$aParams['baseClass'] = $sBaseClass;
|
||||
}
|
||||
$oBlockForm = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, $aParams);
|
||||
|
||||
if (!$bDoSearch) {
|
||||
$oBlockForm->Display($oP, 0);
|
||||
}
|
||||
}
|
||||
if ($bDoSearch) {
|
||||
if (strtolower($sFormat) == 'csv') {
|
||||
$oBlock = new DisplayBlock($oFilter, 'csv', false);
|
||||
// Adjust the size of the Textarea containing the CSV to fit almost all the remaining space
|
||||
$oP->add_ready_script(" $('#1>textarea').height($('#1').parent().height() - $('#0').outerHeight() - 30).width( $('#1').parent().width() - 20);"); // adjust the size of the block
|
||||
} else {
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
|
||||
// Breadcrumb
|
||||
//$iCount = $oBlock->GetDisplayedCount();
|
||||
$sPageId = "ui-search-".$oFilter->GetClass();
|
||||
$sLabel = MetaModel::GetName($oFilter->GetClass());
|
||||
$oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||
}
|
||||
if ($bSearchForm) {
|
||||
//add search block
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
|
||||
if ($sTableId == '') {
|
||||
$sTableId = 'result_1';
|
||||
}
|
||||
$aExtraParams['table_id'] = $sTableId;
|
||||
$aExtraParams['submit_on_load'] = false;
|
||||
$oUIBlockForm = $oBlockForm->GetDisplay($oP, 'search_1', $aExtraParams);
|
||||
//add result block
|
||||
$oUIBlock = $oBlock->GetDisplay($oP, $sTableId);
|
||||
$oUIBlock->AddCSSClasses(['display_block', 'sf_results_area']);
|
||||
$oUIBlock->AddDataAttribute('target', 'search_results');
|
||||
//$oUIBlockForm->AddSubBlock($oUIBlock);
|
||||
$oP->AddUiBlock($oUIBlockForm);
|
||||
$oUIBlockForm->AddSubBlock($oUIBlock);
|
||||
} else {
|
||||
$oBlock->Display($oP, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a form (checkboxes) to select the objects for which to apply a given action
|
||||
* Only the objects for which the action is valid can be checked. By default all valid objects are checked
|
||||
@@ -395,7 +460,7 @@ try {
|
||||
$sOQL = "SELECT $sOQLClass $sOQLClause";
|
||||
try {
|
||||
$oFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, $sBaseClass, $sFormat);
|
||||
DisplaySearchSet($oP, $oFilter, $bSearchForm, $sBaseClass, $sFormat);
|
||||
} catch (CoreException $e) {
|
||||
$oFilter = new DBObjectSearch($sOQLClass);
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
@@ -422,7 +487,7 @@ try {
|
||||
}
|
||||
$oP->set_title(Dict::S('UI:SearchResultsPageTitle'));
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, $bDoSearch, true /* Search Form Expanded */);
|
||||
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, $bDoSearch, true /* Search Form Expanded */);
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -444,7 +509,7 @@ try {
|
||||
// $sParams = utils::ReadParam('aParams', '{}', false, \utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
// $aParams = json_decode($sParams, true);
|
||||
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat); //, true, true, $aParams
|
||||
DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat); //, true, true, $aParams
|
||||
break;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
|
||||
namespace Combodo\iTop\Setup\FeatureRemoval;
|
||||
|
||||
use Combodo\iTop\Setup\ModuleDependency\Module;
|
||||
use Config;
|
||||
use InstallationChoicesToModuleConverter;
|
||||
use iTopExtensionsMap;
|
||||
use MetaModel;
|
||||
use ModuleDiscovery;
|
||||
use RunTimeEnvironment;
|
||||
use SetupUtils;
|
||||
use utils;
|
||||
|
||||
class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
|
||||
{
|
||||
@@ -61,27 +57,6 @@ class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
|
||||
$oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes);
|
||||
}
|
||||
|
||||
private function GetModulesToLoad(string $sSourceEnv, $aSearchDirs): array
|
||||
{
|
||||
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$aChoices = iTopExtensionsMap::GetChoicesFromDatabase($oSourceConfig);
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
|
||||
$sInstallFilePath = APPROOT.$sSourceDir.'/installation.xml';
|
||||
if (! is_file($sInstallFilePath)) {
|
||||
$sInstallFilePath = null;
|
||||
}
|
||||
|
||||
$aModuleIdsToLoad = InstallationChoicesToModuleConverter::GetInstance()->GetModules($aChoices, $aSearchDirs, $sInstallFilePath);
|
||||
$aModulesToLoad = [];
|
||||
foreach ($aModuleIdsToLoad as $sModuleId) {
|
||||
$oModule = new Module($sModuleId);
|
||||
$sModuleName = $oModule->GetModuleName();
|
||||
$aModulesToLoad[] = $sModuleName;
|
||||
}
|
||||
return $aModulesToLoad;
|
||||
}
|
||||
|
||||
public function Cleanup(): void
|
||||
{
|
||||
$sEnv = $this->sBuildEnv;
|
||||
|
||||
@@ -1170,6 +1170,7 @@ class ModelFactory
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
SetupLog::Exception(__METHOD__, $e);
|
||||
$aLoadedModuleNames = [];
|
||||
foreach (self::$aLoadedModules as $oLoadedModule) {
|
||||
$aLoadedModuleNames[] = $oLoadedModule->GetName().':'.$oLoadedModule->GetVersion();
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||
use Combodo\iTop\Setup\ModuleDependency\Module;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
|
||||
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReaderException;
|
||||
|
||||
@@ -468,7 +468,9 @@ class RunTimeEnvironment
|
||||
// Determine the installed modules and extensions
|
||||
//
|
||||
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
|
||||
$aAvailableModules = $this->AnalyzeInstallation($oSourceConfig, $aDirsToCompile);
|
||||
|
||||
$aModulesToLoad = $this->GetModulesToLoad($this->sFinalEnv, $aDirsToCompile);
|
||||
$aAvailableModules = $this->AnalyzeInstallation($oSourceConfig, $aDirsToCompile, false, $aModulesToLoad);
|
||||
|
||||
// Actually read the modules available for the build environment,
|
||||
// but get the selection from the source environment and finally
|
||||
@@ -1594,4 +1596,28 @@ class RunTimeEnvironment
|
||||
|
||||
return substr_compare($sHaystack, $sNeedle, 0, strlen($sNeedle)) === 0;
|
||||
}
|
||||
|
||||
protected function GetModulesToLoad(string $sSourceEnv, $aSearchDirs): ?array
|
||||
{
|
||||
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
|
||||
$aChoices = iTopExtensionsMap::GetChoicesFromDatabase($oSourceConfig);
|
||||
if (false === $aChoices) {
|
||||
return null;
|
||||
}
|
||||
$sSourceDir = $oSourceConfig->Get('source_dir');
|
||||
|
||||
$sInstallFilePath = APPROOT.$sSourceDir.'/installation.xml';
|
||||
if (! is_file($sInstallFilePath)) {
|
||||
$sInstallFilePath = null;
|
||||
}
|
||||
|
||||
$aModuleIdsToLoad = InstallationChoicesToModuleConverter::GetInstance()->GetModules($aChoices, $aSearchDirs, $sInstallFilePath);
|
||||
$aModulesToLoad = [];
|
||||
foreach ($aModuleIdsToLoad as $sModuleId) {
|
||||
$oModule = new Module($sModuleId);
|
||||
$sModuleName = $oModule->GetModuleName();
|
||||
$aModulesToLoad[] = $sModuleName;
|
||||
}
|
||||
return $aModulesToLoad;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class SetupPage extends NiceWebPage
|
||||
{
|
||||
public const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/setuppage/layout';
|
||||
|
||||
public const BODY_DATA_GUI_TYPE = 'setup';
|
||||
protected const BODY_DATA_GUI_TYPE = 'setup';
|
||||
|
||||
public function __construct($sTitle)
|
||||
{
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Helper;
|
||||
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use DBSearch;
|
||||
use DisplayBlock;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
class SearchHelper
|
||||
{
|
||||
/**
|
||||
* Displays the result of a search request
|
||||
* @param $oP WebPage Web page for the output
|
||||
* @param $oFilter DBSearch The search of objects to display
|
||||
* @param $bSearchForm boolean Whether or not to display the search form at the top the page
|
||||
* @param $sBaseClass string The base class for the search (can be different from the actual class of the results)
|
||||
* @param $sFormat string The format to use for the output: csv or html
|
||||
* @param $bDoSearch bool True to display the search results below the search form
|
||||
* @param $bSearchFormOpen bool True to display the search form fully expanded (only if $bSearchForm of course)
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
public static function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = []): void
|
||||
{
|
||||
//search block
|
||||
$oBlockForm = null;
|
||||
if ($bSearchForm) {
|
||||
$aParams['open'] = $bSearchFormOpen;
|
||||
if (false === isset($aParams['table_id'])) {
|
||||
$aParams['table_id'] = 'result_1';
|
||||
}
|
||||
if (!empty($sBaseClass)) {
|
||||
$aParams['baseClass'] = $sBaseClass;
|
||||
}
|
||||
$oBlockForm = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, $aParams);
|
||||
|
||||
if (!$bDoSearch) {
|
||||
$oBlockForm->Display($oP, 0);
|
||||
}
|
||||
}
|
||||
if ($bDoSearch) {
|
||||
if (strtolower($sFormat) == 'csv') {
|
||||
$oBlock = new DisplayBlock($oFilter, 'csv', false);
|
||||
// Adjust the size of the Textarea containing the CSV to fit almost all the remaining space
|
||||
$oP->add_ready_script(" $('#1>textarea').height($('#1').parent().height() - $('#0').outerHeight() - 30).width( $('#1').parent().width() - 20);"); // adjust the size of the block
|
||||
} else {
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
|
||||
// Breadcrumb
|
||||
//$iCount = $oBlock->GetDisplayedCount();
|
||||
$sPageId = "ui-search-".$oFilter->GetClass();
|
||||
$sLabel = MetaModel::GetName($oFilter->GetClass());
|
||||
$oP->SetBreadCrumbEntry($sPageId, $sLabel, '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||
}
|
||||
if ($bSearchForm) {
|
||||
//add search block
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
|
||||
if ($sTableId == '') {
|
||||
$sTableId = 'result_1';
|
||||
}
|
||||
$aExtraParams['table_id'] = $sTableId;
|
||||
$aExtraParams['submit_on_load'] = false;
|
||||
$oUIBlockForm = $oBlockForm->GetDisplay($oP, 'search_1', $aExtraParams);
|
||||
|
||||
// If the class is not high cardinality, we can display the results directly in the same page
|
||||
if (!utils::IsHighCardinality($oFilter->GetClass())) {
|
||||
//add result block
|
||||
$oUIBlock = $oBlock->GetDisplay($oP, $sTableId);
|
||||
$oUIBlock->AddCSSClasses(['display_block', 'sf_results_area']);
|
||||
$oUIBlock->AddDataAttribute('target', 'search_results');
|
||||
$oUIBlockForm->AddSubBlock($oUIBlock);
|
||||
}
|
||||
|
||||
$oP->AddUiBlock($oUIBlockForm);
|
||||
} else {
|
||||
$oBlock->Display($oP, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ class WebPage implements Page
|
||||
*/
|
||||
public const DEFAULT_PAGE_TEMPLATE_REL_PATH = 'pages/backoffice/webpage/layout';
|
||||
|
||||
public const BODY_DATA_GUI_TYPE = 'backoffice';
|
||||
protected const BODY_DATA_GUI_TYPE = 'backoffice';
|
||||
|
||||
protected $s_title;
|
||||
protected $s_content;
|
||||
|
||||
@@ -7,6 +7,6 @@ oWidget{{ oUIBlock.oUILinksDirectWidget.GetInputId() }} = $('#{{ oUIBlock.oUILin
|
||||
input_name: '{{ oUIBlock.sInputName }}',
|
||||
submit_to: '{{ oUIBlock.sSubmitUrl }}',
|
||||
oWizardHelper: {{ oUIBlock.sWizHelper }},
|
||||
do_search: {{ oUIBlock.sJSDoSearch }}
|
||||
do_search: '{{ oUIBlock.sJSDoSearch }}'
|
||||
});
|
||||
{% endapply %}
|
||||
@@ -49,7 +49,7 @@
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body data-gui-type="{{ aPage.sBodyDataGuiType|default(constant('Combodo\\iTop\\Application\\WebPage\\WebPage::BODY_DATA_GUI_TYPE')) }}">
|
||||
<body data-gui-type="{{ aPage.sBodyDataGuiType|default('backoffice') }}">
|
||||
{% if aPage.isPrintable %}<div class="printable-content" style="width: 27.7cm;">{% endif %}
|
||||
{% block iboPageBodyHtml %}
|
||||
<div id="ibo-page-container">
|
||||
|
||||
@@ -727,16 +727,6 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
{
|
||||
$sUrl = \MetaModel::GetConfig()->Get('app_root_url')."/$sUri";
|
||||
|
||||
// Add PHP version in header to be able to handle Docker dev environments with automatic PHP version detection (instead of hardcoding the PHP version in the app_root_url)
|
||||
$sPhpVersion = PHP_VERSION;
|
||||
$aPhpVersionParts = explode('.', $sPhpVersion);
|
||||
$sPhpVersionHeaderValue = ($aPhpVersionParts[0] ?? '0').($aPhpVersionParts[1] ?? '0');
|
||||
$aCurlOptions = $aCurlOptions ?? [];
|
||||
$aCurlOptions[CURLOPT_HTTPHEADER] = array_merge(
|
||||
$aCurlOptions[CURLOPT_HTTPHEADER] ?? [],
|
||||
['X-PHP-Version: '.$sPhpVersionHeaderValue]
|
||||
);
|
||||
|
||||
return $this->CallUrl($sUrl, $aPostFields, $aCurlOptions, $bXDebugEnabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application\Helper;
|
||||
|
||||
use Combodo\iTop\Application\Helper\SearchHelper;
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use DBSearch;
|
||||
use MetaModel;
|
||||
|
||||
class SearchHelperTest extends ItopDataTestCase
|
||||
{
|
||||
protected static array $aHighCardinalityClasses = [];
|
||||
protected static bool $bSearchManualSubmit = false;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::$aHighCardinalityClasses = MetaModel::GetConfig()->Get('high_cardinality_classes');
|
||||
self::$bSearchManualSubmit = MetaModel::GetConfig()->Get('search_manual_submit');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
MetaModel::GetConfig()->Set('high_cardinality_classes', static::$aHighCardinalityClasses);
|
||||
MetaModel::GetConfig()->Set('search_manual_submit', static::$bSearchManualSubmit);
|
||||
}
|
||||
|
||||
public function testDisplaySearchSetWithNoHighCardinalityClassesAddsResultSubBlock(): void
|
||||
{
|
||||
MetaModel::GetConfig()->Set('high_cardinality_classes', []);
|
||||
MetaModel::GetConfig()->Set('search_manual_submit', false);
|
||||
|
||||
$oP = new iTopWebPage('SearchHelperTest');
|
||||
$oFilter = DBSearch::FromOQL('SELECT UserRequest');
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter);
|
||||
$oContentLayout = $oP->GetContentLayout();
|
||||
$this->assertTrue($oContentLayout->HasSubBlock('search_1'));
|
||||
$oSearchBlock = $oContentLayout->getSubBlock('search_1');
|
||||
$this->assertTrue($oSearchBlock->HasSubBlock('result_1'));
|
||||
|
||||
if (ob_get_level() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
public function testDisplaySearchSetWithHighCardinalityClassesDoesNotAddResultSubBlock(): void
|
||||
{
|
||||
MetaModel::GetConfig()->Set('high_cardinality_classes', ['UserRequest']);
|
||||
MetaModel::GetConfig()->Set('search_manual_submit', false);
|
||||
|
||||
$oP = new iTopWebPage('SearchHelperTest');
|
||||
$oFilter = DBSearch::FromOQL('SELECT UserRequest');
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter);
|
||||
$oContentLayout = $oP->GetContentLayout();
|
||||
$this->assertTrue($oContentLayout->HasSubBlock('search_1'));
|
||||
$oSearchBlock = $oContentLayout->getSubBlock('search_1');
|
||||
$this->assertFalse($oSearchBlock->HasSubBlock('result_1'));
|
||||
|
||||
if (ob_get_level() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
|
||||
public function testDisplaySearchSetWithSearchManualSubmitAndWithoutHighCardinalityClassesDoesNotAddResultSubBlock(): void
|
||||
{
|
||||
MetaModel::GetConfig()->Set('high_cardinality_classes', []);
|
||||
MetaModel::GetConfig()->Set('search_manual_submit', true);
|
||||
|
||||
$oP = new iTopWebPage('SearchHelperTest');
|
||||
$oFilter = DBSearch::FromOQL('SELECT UserRequest');
|
||||
SearchHelper::DisplaySearchSet($oP, $oFilter);
|
||||
$oContentLayout = $oP->GetContentLayout();
|
||||
$this->assertTrue($oContentLayout->HasSubBlock('search_1'));
|
||||
$oSearchBlock = $oContentLayout->getSubBlock('search_1');
|
||||
$this->assertFalse($oSearchBlock->HasSubBlock('result_1'));
|
||||
|
||||
if (ob_get_level() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2010-2024 Combodo SAS
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use utils;
|
||||
|
||||
class LoginExternalTest extends ItopDataTestCase
|
||||
{
|
||||
private $oConfig;
|
||||
private $sOriginalExtAuthVariable;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
require_once APPROOT.'application/loginexternal.class.inc.php';
|
||||
$this->oConfig = utils::GetConfig();
|
||||
$this->sOriginalExtAuthVariable = $this->oConfig->Get('ext_auth_variable');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->oConfig->SetExternalAuthenticationVariable($this->sOriginalExtAuthVariable);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
private function CallGetAuthUser()
|
||||
{
|
||||
$oLoginExternal = new \LoginExternal();
|
||||
$oMethod = new \ReflectionMethod(\LoginExternal::class, 'GetAuthUser');
|
||||
$oMethod->setAccessible(true);
|
||||
return $oMethod->invoke($oLoginExternal);
|
||||
}
|
||||
|
||||
public function testGetAuthUserFromServerVariable()
|
||||
{
|
||||
$_SERVER['REMOTE_USER'] = 'alice';
|
||||
$this->oConfig->SetExternalAuthenticationVariable('$_SERVER[\'REMOTE_USER\']');
|
||||
|
||||
$this->assertSame('alice', $this->CallGetAuthUser());
|
||||
}
|
||||
|
||||
public function testGetAuthUserFromCookie()
|
||||
{
|
||||
$_COOKIE['auth_user'] = 'bob';
|
||||
$this->oConfig->SetExternalAuthenticationVariable('$_COOKIE[\'auth_user\']');
|
||||
|
||||
$this->assertSame('bob', $this->CallGetAuthUser());
|
||||
}
|
||||
|
||||
public function testGetAuthUserFromRequest()
|
||||
{
|
||||
$_REQUEST['auth_user'] = 'carol';
|
||||
$this->oConfig->SetExternalAuthenticationVariable('$_REQUEST[\'auth_user\']');
|
||||
|
||||
$this->assertSame('carol', $this->CallGetAuthUser());
|
||||
}
|
||||
|
||||
public function testInvalidExpressionReturnsFalse()
|
||||
{
|
||||
$this->oConfig->SetExternalAuthenticationVariable('$_SERVER[\'HTTP_X_CMD\']) ? print(\'x\') : false; //');
|
||||
|
||||
$this->assertFalse($this->CallGetAuthUser());
|
||||
}
|
||||
|
||||
public function testGetAuthUserFromHeaderWithoutAllowlist()
|
||||
{
|
||||
if (!function_exists('getallheaders')) {
|
||||
$this->markTestSkipped('getallheaders() not available');
|
||||
}
|
||||
$_SERVER['HTTP_X_REMOTE_USER'] = 'CN=header-test';
|
||||
$this->oConfig->SetExternalAuthenticationVariable('getallheaders()[\'X-Remote-User\']');
|
||||
|
||||
$this->assertSame('CN=header-test', $this->CallGetAuthUser());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user