N°3227 For AttributeOQL add a new icon to pick a query from queries phrasebook

Add a new Query.is_template field (default value 'no') to filter queries usable as template.

Also lots of modifications in ExtKeyWidget, both JS and PHP objects :
* pass filter on dialog content generation (was using only targetClass, so search criteria weren't set - but search results were ok as they are made from another ajax query)
* fix loading still displayed after dialog close
* can now get the defined attribute value (was always returning the selected object id)
This commit is contained in:
Pierre Goiffon
2020-11-30 17:05:47 +01:00
parent 77b72a6eb1
commit c117a23e0d
8 changed files with 205 additions and 87 deletions

View File

@@ -1882,24 +1882,68 @@ HTML
$sStyle = 'style="'.implode('; ', $aStyles).'"';
}
if ($oAttDef->GetEditClass() == 'OQLExpression')
{
if ($oAttDef->GetEditClass() == 'OQLExpression') {
// predefined queries N°3227
$sPredefinedBtnId = 'predef_btn_'.$sFieldPrefix.$sAttCode.$sNameSuffix;
$sSearchQuerylbl = Dict::S('UI:Edit:SearchQuery');
$sAdditionalStuff = <<<HTML
<button id="$sPredefinedBtnId" type="button" title="$sSearchQuerylbl"
class="ibo-button ibo-is-alternative ibo-is-neutral ibo-action-button">
<i class="fas fa-search"></i>
</button>
HTML;
$oPage->add_ready_script(<<<JS
// noinspection JSAnnotator
oACWidget_{$iId} = new ExtKeyWidget('$iId', 'QueryOQL', 'SELECT QueryOQL WHERE is_template = \'yes\'', '$sSearchQuerylbl', true, null, null, true, true, 'oql');
// noinspection JSAnnotator
oACWidget_{$iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>Use the search form above to search for objects to be added.</p></div>";
$("#$sPredefinedBtnId").click(function () {
oACWidget_{$iId}.Search();
});
if ($('#ac_dlg_{$iId}').length == 0)
{
$('body').append('<div id="ac_dlg_{$iId}"></div>');
$('#ac_dlg_{$iId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
autoOpen: false,
modal: true,
title: '$sSearchQuerylbl',
resizeStop: oACWidget_{$iId}.UpdateSizes,
close: oACWidget_{$iId}.OnClose
});
}
JS
);
// test query link
$sTestResId = 'query_res_'.$sFieldPrefix.$sAttCode.$sNameSuffix; //$oPage->GetUniqueId();
$sBaseUrl = utils::GetAbsoluteUrlAppRoot().'pages/run_query.php?expression=';
$sInitialUrl = $sBaseUrl.urlencode($sEditValue);
$sAdditionalStuff = "<a id=\"$sTestResId\" target=\"_blank\" href=\"$sInitialUrl\">".Dict::S('UI:Edit:TestQuery')."</a>";
$oPage->add_ready_script("$('#$iId').bind('change keyup', function(evt, sFormId) { $('#$sTestResId').attr('href', '$sBaseUrl'+encodeURIComponent($(this).val())); } );");
$sTestQuerylbl = Dict::S('UI:Edit:TestQuery');
$sAdditionalStuff .= <<<HTML
<button type="button" title="{$sTestQuerylbl}"
class="ibo-button ibo-is-alternative ibo-is-neutral ibo-action-button">
<a id="$sTestResId" target="_blank" href="$sInitialUrl">
<i class="fas fa-play"></i>
</a>
</button>
HTML;
$oPage->add_ready_script(<<<JS
$('#$iId').bind('change keyup', function(evt, sFormId) {
$('#$sTestResId').attr('href', '$sBaseUrl'+encodeURIComponent($(this).val()));
});
JS
);
} else {
$sAdditionalStuff = '';
}
else
{
$sAdditionalStuff = "";
}
// Ok, the text area is drawn here
$sHTMLValue = "<div class=\"field_input_zone field_input_text\"><div class=\"f_i_text_header\"><span class=\"fullscreen_button\" title=\"".Dict::S('UI:ToggleFullScreen')."\"></span></div><textarea class=\"\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\" $sStyle>".htmlentities($sEditValue,
ENT_QUOTES, 'UTF-8')."</textarea>$sAdditionalStuff</div>{$sValidationSpan}{$sReloadSpan}";
// Ok, the text area is drawn here
$sHTMLValue = "$sAdditionalStuff<div class=\"field_input_zone field_input_text\"><div class=\"f_i_text_header\"><span class=\"fullscreen_button\" title=\"".Dict::S('UI:ToggleFullScreen')."\"></span></div><textarea class=\"\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\" $sStyle>".htmlentities($sEditValue,
ENT_QUOTES, 'UTF-8')."</textarea></div>{$sValidationSpan}{$sReloadSpan}";
$oPage->add_ready_script(
<<<EOF
$oPage->add_ready_script(
<<<EOF
$('#$iId').closest('.field_input_text').find('.fullscreen_button').on('click', function(oEvent){
var oOriginField = $('#$iId').closest('.field_input_text');
var oClonedField = oOriginField.clone();

View File

@@ -1,33 +1,28 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
/*
* Copyright (C) 2010-2020 Combodo SARL
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* 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
*/
abstract class Query extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @since 3.0.0 N°3227 add is_template field for predefined queries
*/
public static function Init()
{
$aParams = array
@@ -42,17 +37,38 @@ abstract class Query extends cmdbAbstractObject
"db_finalclass_field" => "realclass",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("name", array(
"allowed_values" => null,
"sql" => "name",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeText("description", array(
"allowed_values" => null,
"sql" => "description",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeEnum("is_template", array(
'allowed_values' => new ValueSetEnum('yes,no'),
'sql' => 'is_template',
'default_value' => 'no',
'is_null_allowed' => false,
'depends_on' => [],
)));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details',
array('name', 'is_template', 'description')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'is_template')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search',
array('name', 'description', 'is_template')); // Criteria of the default search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
@@ -74,16 +90,30 @@ class QueryOQL extends Query
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array(
"allowed_values" => null,
"sql" => "oql",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeText("fields", array(
"allowed_values" => null,
"sql" => "fields",
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
)));
// Rolled back to AttributeText until AttributeQueryAttCodeSet can manage fields order correctly
//MetaModel::Init_AddAttribute(new AttributeQueryAttCodeSet("fields", array("allowed_values"=>null,"max_items" => 1000, "query_field" => "oql", "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array('oql'))));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details',
array('name', 'is_template', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields', 'oql')); // Criteria of the std search form
MetaModel::Init_SetZListItems('standard_search',
array('name', 'description', 'is_template', 'fields', 'oql')); // Criteria of the std search form
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())

View File

@@ -66,12 +66,16 @@ class UIExtKeyWidget
protected $iId;
protected $sTargetClass;
protected $sFilter;
protected $sAttCode;
//@deprecated
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
static public function DisplayFromAttCode(
$oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs,
$bSearchMode = false
)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
@@ -103,17 +107,17 @@ class UIExtKeyWidget
return $oWidget->DisplaySelect($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs);
}
}
else
{
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
} else {
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle);
}
}
public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false)
public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false, $sFilter = null)
{
$this->sTargetClass = $sTargetClass;
$this->sFilter = $sFilter;
$this->iId = $iInputId;
$this->sAttCode = $sAttCode;
$this->bSearchMode = $bSearchMode;
@@ -642,6 +646,9 @@ JS
$aParams = array('query_params' => $aArgs);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$oFilter = $oSet->GetFilter();
} else if (!empty($this->sFilter)) {
$aParams = array();
$oFilter = DBObjectSearch::FromOQL($this->sFilter);
} else {
$aParams = array();
$oFilter = new DBObjectSearch($this->sTargetClass);
@@ -656,7 +663,7 @@ JS
'table_inner_id' => "{$this->iId}_results",
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId
'cssCount' => '#count_'.$this->iId,
)
));
$sCancel = Dict::S('UI:Button:Cancel');
@@ -803,17 +810,27 @@ JS
}
/**
* Get the display name of the selected object, to fill back the autocomplete
* @param int $iObjId object ID
* @param string $sFormAttCode attcode if we need a value that isn't the display name
*
* @return string Either the display name of the selected object or the specified attribute value
*
* @uses \DBObject::GetName() to get display name
*
* @since 3.0.0 N°3227 add the $sAttCode parameter and method PHPDoc
*/
public function GetObjectName($iObjId)
public function GetObjectName($iObjId, $sFormAttCode = null)
{
$aModifierProps = array();
$aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
if ($oObj)
{
return $oObj->GetName();
if ($oObj) {
if (is_null($sFormAttCode)) {
return $oObj->GetName();
} else {
return $oObj->Get($sFormAttCode);
}
}
else
{

View File

@@ -32,7 +32,6 @@
* @copyright Copyright (C) 2016-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ContextTag
{
const TAG_PORTAL = 'GUI:Portal';

View File

@@ -88,6 +88,10 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Query/Attribute:name+' => 'Identifies the query',
'Class:Query/Attribute:description' => 'Description',
'Class:Query/Attribute:description+' => 'Long description for the query (purpose, usage, etc.)',
'Class:Query/Attribute:is_template' => 'Template for OQL fields',
'Class:Query/Attribute:is_template+' => 'Usable as source for recipient OQL in Notifications',
'Class:Query/Attribute:is_template/Value:yes' => 'Yes',
'Class:Query/Attribute:is_template/Value:no' => 'No',
'Class:QueryOQL/Attribute:fields' => 'Fields',
'Class:QueryOQL/Attribute:fields+' => 'Coma separated list of attributes (or alias.attribute) to export',
'Class:QueryOQL' => 'OQL Query',
@@ -776,6 +780,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:Schema:Attribute/Filter' => 'Filter~~',
'UI:Schema:DefaultNullValue' => 'Default null : "%1$s"~~',
'UI:LinksWidget:Autocomplete+' => 'Type the first 3 characters...',
'UI:Edit:SearchQuery' => 'Select a predefined query',
'UI:Edit:TestQuery' => 'Test query',
'UI:Combo:SelectValue' => '--- select a value ---',
'UI:Label:SelectedObjects' => 'Selected objects: ',

View File

@@ -71,6 +71,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:Query/Attribute:name+' => 'Identification de la requête',
'Class:Query/Attribute:description' => 'Description',
'Class:Query/Attribute:description+' => 'Description complète (finalité, utilisations, public)',
'Class:Query/Attribute:is_template' => 'Base pour champ OQL',
'Class:Query/Attribute:is_template+' => 'Utilisable comme base pour les destinataires des Notifications',
'Class:Query/Attribute:is_template/Value:yes' => 'Oui',
'Class:Query/Attribute:is_template/Value:no' => 'Non',
'Class:QueryOQL/Attribute:fields' => 'Champs',
'Class:QueryOQL/Attribute:fields+' => 'Liste CSV des attributs (ou alias.attribut) à exporter',
'Class:QueryOQL' => 'Requête OQL',
@@ -755,6 +759,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'UI:Schema:Attribute/Filter' => 'Filtre',
'UI:Schema:DefaultNullValue' => 'Valeur null par défaut : "%1$s"',
'UI:LinksWidget:Autocomplete+' => 'Tapez les 3 premiers caractères...',
'UI:Edit:SearchQuery' => 'Sélectionner une requête prédéfinie',
'UI:Edit:TestQuery' => 'Tester la requête',
'UI:Combo:SelectValue' => '--- choisissez une valeur ---',
'UI:Label:SelectedObjects' => 'Objets sélectionnés: ',

View File

@@ -16,7 +16,7 @@
* You should have received a copy of the GNU Affero General Public License
*/
function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper, sAttCode, bSearchMode, bDoSearch) {
function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper, sAttCode, bSearchMode, bDoSearch, sFormAttCode) {
this.id = id;
this.sOriginalTargetClass = sTargetClass;
this.sTargetClass = sTargetClass;
@@ -30,6 +30,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
this.bDoSearch = bDoSearch; // false if the search is not launched
this.sFormAttCode = sFormAttCode;
var me = this;
this.Init = function () {
@@ -37,18 +39,14 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
$('#'+this.id+'_btnRemove').prop('disabled', true);
$('#'+this.id+'_linksToRemove').val('');
}
this.AddSelectize = function(options, initValue)
{
this.AddSelectize = function (options, initValue) {
$('#'+me.id).selectize({
render: {
item: function(item) {
if ( item.obsolescence_flag == 1)
{
val = '<span class="object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw"></span>'+item.label;
}
else
{
val = item.label;
render: {
item: function (item) {
if (item.obsolescence_flag == 1) {
val = '<span class="object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw"></span>'+item.label;
} else {
val = item.label;
}
return $("<div>")
.append(val);
@@ -208,6 +206,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
sTitle: me.sTitle,
sAttCode: me.sAttCode,
sTargetClass: me.sTargetClass,
sFilter: me.sFilter,
bSearchMode: me.bSearchMode,
operation: 'objectSearchForm'
};
@@ -358,6 +357,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
iInputId: me.id,
iObjectId: iObjectId,
sAttCode: me.sAttCode,
sFormAttCode: me.sFormAttCode,
bSearchMode: me.bSearchMode,
operation: 'getObjectName'
};
@@ -371,18 +371,32 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
function (data) {
var oTemp = $('<div>'+data.name+'</div>');
var txt = oTemp.text(); // this causes HTML entities to be interpreted
$('#label_'+me.id).val(txt);
$('#label_'+me.id).removeClass('ac_dlg_loading');
var newValue;
if ($('#label_'+me.id).length) {
newValue = iObjectId;
$('#label_'+me.id).val(txt);
$('#label_'+me.id).removeClass('ac_dlg_loading');
} else {
// N°3227 if no label_* field present, we just want to pick the attribute value !
newValue = txt;
}
var prevValue = $('#'+me.id).val();
$('#'+me.id).val(iObjectId);
if (prevValue != iObjectId)
{
$('#'+me.id).val(newValue);
if (prevValue != newValue) {
// dependent fields will be updated using the WizardHelper JS object
$('#'+me.id).trigger('validate');
$('#'+me.id).trigger('extkeychange');
$('#'+me.id).trigger('change');
}
$('#label_'+me.id).focus();
if ($('#label_'+me.id).length) {
$('#label_'+me.id).focus();
} else {
$('#'+me.id).focus();
}
me.ajax_request = null;
},
'json'
@@ -396,9 +410,14 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
// dialog is very slow. So empty it each time.
this.OnClose = function () {
me.StopPendingRequest();
if (me.bSelectMode) {
$('#fstatus_'+me.id).html('');
} else {
$('#label_'+me.id).removeClass('ac_dlg_loading');
}
// called by the dialog, so in the context 'this' points to the jQueryObject
if (me.emptyOnClose)
{
if (me.emptyOnClose) {
$('#dr_'+me.id).html(me.emptyHtml);
}
$('#label_'+me.id).removeClass('ac_dlg_loading');

View File

@@ -753,19 +753,17 @@ try
case 'objectSearchForm':
$oPage->SetContentType('text/html');
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
$sFilter = utils::ReadParam('sFilter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$iInputId = utils::ReadParam('iInputId', '');
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
$sAttCode = utils::ReadParam('sAttCode', '');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode, $sFilter);
$sJson = utils::ReadParam('json', '', false, 'raw_data');
if (!empty($sJson))
{
if (!empty($sJson)) {
$oWizardHelper = WizardHelper::FromJSON($sJson);
$oObj = $oWizardHelper->GetTargetObject();
}
else
{
} else {
// Search form: no current object
$oObj = null;
}
@@ -831,9 +829,10 @@ try
$iInputId = utils::ReadParam('iInputId', '');
$iObjectId = utils::ReadParam('iObjectId', '');
$bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true');
$sFormAttCode = utils::ReadParam('sFormAttCode', null);
$oKPI = new ExecutionKPI();
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode);
$sName = $oWidget->GetObjectName($iObjectId);
$sName = $oWidget->GetObjectName($iObjectId, $sFormAttCode);
echo json_encode(array('name' => $sName));
$oKPI->ComputeAndReport('Data fetch and format');
break;