N°2313 - Markup extensibility: Add markup hooks on BrowseBrick and ManageBrick tables

This commit is contained in:
Molkobain
2020-01-08 19:32:38 +01:00
parent f235e0cd66
commit 149dff4b4d
5 changed files with 139 additions and 26 deletions

View File

@@ -30,6 +30,7 @@ use BinaryExpression;
use CMDBSource;
use Combodo\iTop\Portal\Brick\AbstractBrick;
use Combodo\iTop\Portal\Brick\ManageBrick;
use Combodo\iTop\Portal\Helper\ApplicationHelper;
use DBObject;
use DBObjectSet;
use DBSearch;
@@ -592,6 +593,9 @@ class ManageBrickController extends BrickController
/** @var DBObject $oCurrentRow */
while ($oCurrentRow = $oSet->Fetch())
{
$sCurrentObjectClass = get_class($oCurrentRow);
$sCurrentObjectId = $oCurrentRow->GetKey();
// ... Retrieving item's attributes values
$aItemAttrs = array();
foreach ($aColumnsAttrs as $sItemAttr)
@@ -631,6 +635,7 @@ class ManageBrickController extends BrickController
/** @var \AttributeDefinition $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sCurrentClass, $sItemAttr);
$sAttDefClass = get_class($oAttDef);
if ($oAttDef->IsExternalKey())
{
/** @var \AttributeExternalKey $oAttDef */
@@ -688,9 +693,25 @@ class ManageBrickController extends BrickController
}
unset($oAttDef);
// For simple fields, we get the raw (stored) value as well
$bExcludeRawValue = false;
foreach (ApplicationHelper::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($sAttDefClass, $sAttDefClassToExclude, true))
{
$bExcludeRawValue = true;
break;
}
}
$attValueRaw = ($bExcludeRawValue === false) ? $oCurrentRow->Get($sItemAttr) : null;
$aItemAttrs[$sItemAttr] = array(
'att_code' => $sItemAttr,
'value' => $sValue,
'object_class' => $sCurrentObjectClass,
'object_id' => $sCurrentObjectId,
'attribute_code' => $sItemAttr,
'attribute_type' => $sAttDefClass,
'value_raw' => $attValueRaw,
'value_html' => $sValue,
'sort_value' => $sSortValue,
'actions' => $aActions,
);

View File

@@ -469,4 +469,23 @@ class ApplicationHelper
return $aForm;
}
/**
* Return an array of AttributeDefinition classes for which the raw value should be excluded from the markup meta (because its too big / complex)
* IMPORTANT: This needs to be refactored when the portal will handle attributes display in a better way. Also this is a duplicate from cmdbAbstractObject::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as for the moment we have no clean place to put this.
*
* @return array
* @since 2.7.0
*/
public static function GetAttDefClassesToExcludeFromMarkupMetadataRawValue(){
return array(
'AttributeBlob',
'AttributeCustomFields',
'AttributeDashboard',
'AttributeLinkedSet',
'AttributeStopWatch',
'AttributeSubItem',
'AttributeTable',
'AttributeText'
);
}
}

View File

@@ -322,12 +322,26 @@ class BrowseBrickHelper
// Retrieving objects from all levels
$aItems = array_values($aCurrentRow);
$sCurrentObjectClass = get_class($value);
$sCurrentObjectId = $value->GetKey();
$sNameAttCode = $aLevelsProperties[$key]['name_att'];
$sNameAttDef = MetaModel::GetAttributeDef($sCurrentObjectClass, $sNameAttCode);
$sNameAttDefClass = get_class($sNameAttDef);
$aRow[$key] = array(
'level_alias' => $key,
'id' => $value->GetKey(),
'name' => $value->Get($aLevelsProperties[$key]['name_att']),
'class' => get_class($value),
'id' => $sCurrentObjectId,
'name' => $value->Get($sNameAttCode),
'class' => $sCurrentObjectClass,
'action_rules_token' => $this->PrepareActionRulesForItems($aItems, $key, $aLevelsProperties),
'metadata' => array(
'object_class' => $sCurrentObjectClass,
'object_id' => $sCurrentObjectId,
'attribute_code' => $sNameAttCode,
'attribute_type' => $sNameAttDefClass,
'value_raw' => $value->Get($sNameAttCode),
),
);
// Adding optional attributes if necessary
@@ -336,7 +350,7 @@ class BrowseBrickHelper
if ($aLevelsProperties[$key][$sOptionalAttribute] !== null)
{
$sPropertyName = substr($sOptionalAttribute, 0, -4);
$oAttDef = MetaModel::GetAttributeDef(get_class($value), $aLevelsProperties[$key][$sOptionalAttribute]);
$oAttDef = MetaModel::GetAttributeDef($sCurrentObjectClass, $aLevelsProperties[$key][$sOptionalAttribute]);
if ($oAttDef instanceof AttributeImage)
{
@@ -346,8 +360,8 @@ class BrowseBrickHelper
if (is_object($tmpAttValue) && !$tmpAttValue->IsEmpty())
{
$tmpAttValue = $this->oUrlGenerator->generate('p_object_document_display', array(
'sObjectClass' => get_class($value),
'sObjectId' => $value->GetKey(),
'sObjectClass' => $sCurrentObjectClass,
'sObjectId' => $sCurrentObjectId,
'sObjectField' => $aLevelsProperties[$key][$sOptionalAttribute],
'cache' => 86400,
));
@@ -372,7 +386,8 @@ class BrowseBrickHelper
$aRow[$key]['fields'] = array();
foreach ($aLevelsProperties[$key]['fields'] as $aField)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($value), $aField['code']);
$oAttDef = MetaModel::GetAttributeDef($sCurrentObjectClass, $aField['code']);
$sAttDefClass = get_class($oAttDef);
switch (true)
{
@@ -390,8 +405,8 @@ class BrowseBrickHelper
if (is_object($oOrmDoc) && !$oOrmDoc->IsEmpty())
{
$sUrl = $this->oUrlGenerator->generate('p_object_document_display', array(
'sObjectClass' => get_class($value),
'sObjectId' => $value->GetKey(),
'sObjectClass' => $sCurrentObjectClass,
'sObjectId' => $sCurrentObjectId,
'sObjectField' => $aField['code'],
'cache' => 86400,
));
@@ -408,7 +423,26 @@ class BrowseBrickHelper
break;
}
$aRow[$key]['fields'][$aField['code']] = $sHtmlForFieldValue;
// For simple fields, we get the raw (stored) value as well
$bExcludeRawValue = false;
foreach (ApplicationHelper::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($sAttDefClass, $sAttDefClassToExclude, true))
{
$bExcludeRawValue = true;
break;
}
}
$attValueRaw = ($bExcludeRawValue === false) ? $value->Get($aField['code']) : null;
$aRow[$key]['fields'][$aField['code']] = array(
'object_class' => $sCurrentObjectClass,
'object_id' => $sCurrentObjectId,
'attribute_code' => $aField['code'],
'attribute_type' => $sAttDefClass,
'value_raw' => $attValueRaw,
'value_html' => $sHtmlForFieldValue,
);
}
}
}

View File

@@ -67,7 +67,14 @@
// Preparing the cell data
cellElem = (levelActionsKeys.length > 0) ? $('<a></a>') : $('<span></span>');
cellElem.attr('data-item-id', data.id).attr('data-level-alias', data.level_alias);
// Building metadata
for(var sPropName in data.metadata)
{
var propValue = data.metadata[sPropName];
cellElem.attr('data-'+sPropName.replace('_', '-'), propValue);
}
// Building tooltip for the node
// We have to concatenate the HTML as we return the raw HTML of the cell. If we did a jQuery.insertAfter, the tooltip would not be returned.
// For the same reason, tooltip widget is created in "drawCallback" instead of here.
@@ -187,7 +194,26 @@
"title": oLevelsProperties[sKey].fields[i].label,
"defaultContent": "",
"type": "html",
"data": oLevelsProperties[sKey].alias+".fields."+oLevelsProperties[sKey].fields[i].code
"data": oLevelsProperties[sKey].alias+".fields."+oLevelsProperties[sKey].fields[i].code,
"render": function(data, type, row){
var cellElem = $('<span></span>');
// Building value and metadata
for(var sPropName in data)
{
var sPropValue = data[sPropName];
if(sPropName === 'value_html')
{
cellElem.html(sPropValue);
}
else
{
cellElem.attr('data-'+sPropName.replace('_', '-'), sPropValue);
}
}
return cellElem.prop('outerHTML');
},
});
}
}

View File

@@ -8,7 +8,7 @@
{% if aGroupingTabsValues|length > 1 %}
<ul class="nav nav-pills grouping_tabs">
{% for aGroupingTab in aGroupingTabsValues %}
<li{% if sGroupingTab is defined and sGroupingTab == aGroupingTab.value %} class="active"{% endif %}>
<li{% if sGroupingTab is defined and sGroupingTab == aGroupingTab.value %} class="active"{% endif %} data-id="{{ aGroupingTab.value }}" data-label="{{ aGroupingTab.label }}" data-item-count="{{ aGroupingTab.count }}">
<a href="{{ app.url_generator.generate('p_manage_brick_display_as', {'sBrickId': sBrickId, 'sDisplayMode': sDisplayMode, 'sGroupingTab': aGroupingTab.value}) }}"
id="btn_tab_for_{{ aGroupingTab.value }}">
{{ aGroupingTab.label|raw }}
@@ -105,20 +105,33 @@
"title": tableProperties[key].title,
"defaultContent": "",
"type": "html",
"data": "attributes." + key + ".att_code",
"data": "attributes." + key + ".attribute_code",
"render": {
_: function (att_code, type, row) {
_: function (attribute_code, type, row) {
var cellElem;
var itemActions;
var itemPrimarayAction;
var itemPrimaryAction;
var metadataNames = ['object_class', 'object_id', 'attribute_code', 'attribute_type', 'value_raw'];
// Preparing action on the cell
// Note : For now we will use only one action, the secondary actions are therefore not implemented. Only the data structure is done.
itemActions = row.attributes[att_code].actions;
itemActions = row.attributes[attribute_code].actions;
// Preparing the cell data
cellElem = (itemActions.length > 0) ? $('<a></a>') : $('<span></span>');
cellElem.html(row.attributes[att_code].value);
for(var sPropName in row.attributes[attribute_code])
{
var propValue = row.attributes[attribute_code][sPropName];
if(sPropName === 'value_html')
{
cellElem.html(propValue);
}
else if(metadataNames.indexOf(sPropName) > -1)
{
cellElem.attr('data-'+sPropName.replace('_', '-'), propValue)
}
}
// Building actions
if (itemActions.length > 0) {
// - Primary action
@@ -144,8 +157,8 @@
return cellElem.prop('outerHTML');
},
sort: function (att_code, type, row) {
return row.attributes[att_code].sort_value;
sort: function (attribute_code, type, row) {
return row.attributes[attribute_code].sort_value;
},
},
});
@@ -159,8 +172,8 @@
"title": tableProperties[key].title,
"defaultContent": "",
"type": "html",
"data": "attributes." + key + ".att_code",
"render": function (att_code, type, row) {
"data": "attributes." + key + ".attribute_code",
"render": function (attribute_code, type, row) {
var cellElem = $('<div class="group-actions-wrapper"></div>');
var actionsCount = row.actions.length;
@@ -300,11 +313,11 @@
for (var iSortIdx in d.order) {
/* oSortedColumnData {column: index, dir: 'asc'|'desc'} */
var oSortedColumnData = d.order[iSortIdx];
/* sSortedColumnId (eg. 'attributes.ATT_CODE.att_code') */
/* sSortedColumnId (eg. 'attributes.ATT_CODE.attribute_code') */
var sSortedColumnId = d.columns[oSortedColumnData.column].data;
// Make proper attribute alias for OQL from column ID
sSortedColumnId = sSortedColumnId.replace(/^attributes\./, '').replace(/\.att_code$/, '');
sSortedColumnId = sSortedColumnId.replace(/^attributes\./, '').replace(/\.attribute_code$/, '');
d.aSortParams[sSortedColumnId] = (oSortedColumnData.dir === 'asc');
}