N°2847 - Datatables for static data

This commit is contained in:
Eric
2020-11-10 17:30:53 +01:00
parent b44e6a4a53
commit 23452804aa
10 changed files with 282 additions and 73 deletions

View File

@@ -415,75 +415,8 @@ class DisplayBlock
switch($this->m_sStyle)
{
case 'count':
if (isset($aExtraParams['group_by']))
{
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
$aLabels = array();
$aValues = array();
$iTotalCount = 0;
foreach ($aRes as $iRow => $aRow)
{
$sValue = $aRow['grouped_by_1'];
$aValues[$iRow] = $sValue;
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = $sHtmlValue;
$aGroupBy[$iRow] = (int) $aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
foreach($aGroupBy as $iRow => $iCount)
{
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
$oSubsetSearch->AddConditionExpression($oCondition);
if (isset($aExtraParams['query_params']))
{
$aQueryParams = $aExtraParams['query_params'];
}
else
{
$aQueryParams = array();
}
$sFilter = rawurlencode($oSubsetSearch->serialize(false, $aQueryParams));
$aData[] = array ('group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"); // TO DO: add the context information
}
$aAttribs =array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array(
'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction),
'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr),
),
);
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount));
$sHtml .= $oPage->GetTable($aAttribs, $aData);
$oPage->add_ready_script("LoadGroupBySortOrder('$sId');\n$('#{$sId} table.listResults').unbind('sortEnd.group_by').bind('sortEnd.group_by', function() { SaveGroupBySortOrder('$sId', $(this)[0].config.sortList); })");
}
else
{
// Simply count the number of elements in the set
$iCount = $this->m_oSet->Count();
$sFormat = 'UI:CountOfObjects';
if (isset($aExtraParams['format']))
{
$sFormat = $aExtraParams['format'];
}
$sHtml .= $oPage->GetP(Dict::Format($sFormat, $iCount));
}
break;
$oBlock = $this->RenderCount($aExtraParams, $oPage, $sId);
break;
case 'join':
$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']): array();
@@ -1382,6 +1315,83 @@ JS
return $oBlock;
}
/**
* @param array $aExtraParams
* @param \WebPage $oPage
* @param string|null $sId
*
* @return \Combodo\iTop\Application\UI\iUIBlock
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \Exception
*/
protected function RenderCount(array $aExtraParams, WebPage $oPage, ?string $sId): iUIBlock
{
if (isset($aExtraParams['group_by'])) {
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
$aLabels = array();
$aValues = array();
$iTotalCount = 0;
foreach ($aRes as $iRow => $aRow) {
$sValue = $aRow['grouped_by_1'];
$aValues[$iRow] = $sValue;
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$aLabels[$iRow] = $sHtmlValue;
$aGroupBy[$iRow] = (int)$aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
foreach ($aGroupBy as $iRow => $iCount) {
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($aValues[$iRow]));
$oSubsetSearch->AddConditionExpression($oCondition);
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
} else {
$aQueryParams = array();
}
$sFilter = rawurlencode($oSubsetSearch->serialize(false, $aQueryParams));
$aData[] = array(
'group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
); // TO DO: add the context information
}
$aAttribs = array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array(
'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction),
'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr),
),
);
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$sTitle = Dict::Format($sFormat, $iTotalCount);
$oBlock = DataTableFactory::MakeForStaticData($sTitle, $aAttribs, $aData);
// $oPage->add_ready_script("LoadGroupBySortOrder('$sId');\n$('#{$sId} table.listResults').unbind('sortEnd.group_by').bind('sortEnd.group_by', function() { SaveGroupBySortOrder('$sId', $(this)[0].config.sortList); })");
} else {
// Simply count the number of elements in the set
$iCount = $this->m_oSet->Count();
$sFormat = 'UI:CountOfObjects';
if (isset($aExtraParams['format'])) {
$sFormat = $aExtraParams['format'];
}
$oBlock = new Html($oPage->GetP(Dict::Format($sFormat, $iCount)));
}
return $oBlock;
}
}
/**

View File

@@ -72,7 +72,7 @@
}
.ibo-datatable-toolbar {
/*position: relative;*/
height: 30px;
//height: 30px;
}
.ibo-criterion-area{
font-size: $ibo-font-size-50 ;

View File

@@ -21,9 +21,9 @@
Dict::Add('EN US', 'English', 'English', array(
'UI:Datatables:Language:Processing' => 'Please wait...',
'UI:Datatables:Language:Search' => 'Filter:',
'UI:Datatables:Language:LengthMenu' => 'Showing _MENU_ out',
'UI:Datatables:Language:LengthMenu' => 'Showing _MENU_ out of',
'UI:Datatables:Language:ZeroRecords' => 'No result',
'UI:Datatables:Language:Info' => 'of _TOTAL_ items',
'UI:Datatables:Language:Info' => '_TOTAL_ items',
'UI:Datatables:Language:InfoEmpty' => 'No information',
'UI:Datatables:Language:InfoFiltered' => 'filtered out of _MAX_ items',
'UI:Datatables:Language:EmptyTable' => 'No data available in this table',

View File

@@ -164,6 +164,7 @@ return array(
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableBlock' => $baseDir . '/sources/application/UI/Component/DataTable/DataTable.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableFactory' => $baseDir . '/sources/application/UI/Component/DataTable/DataTableFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableSettings' => $baseDir . '/sources/application/UI/Component/DataTable/DataTableSettings.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\StaticTable\\StaticTable' => $baseDir . '/sources/application/UI/Component/DataTable/StaticTable/StaticTable.php',
'Combodo\\iTop\\Application\\UI\\Component\\FieldSet\\FieldSet' => $baseDir . '/sources/application/UI/Component/FieldSet/FieldSet.php',
'Combodo\\iTop\\Application\\UI\\Component\\Field\\Field' => $baseDir . '/sources/application/UI/Component/Field/Field.php',
'Combodo\\iTop\\Application\\UI\\Component\\Form\\Form' => $baseDir . '/sources/application/UI/Component/Form/Form.php',

View File

@@ -394,6 +394,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableBlock' => __DIR__ . '/../..' . '/sources/application/UI/Component/DataTable/DataTable.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableFactory' => __DIR__ . '/../..' . '/sources/application/UI/Component/DataTable/DataTableFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\DataTableSettings' => __DIR__ . '/../..' . '/sources/application/UI/Component/DataTable/DataTableSettings.php',
'Combodo\\iTop\\Application\\UI\\Component\\DataTable\\StaticTable\\StaticTable' => __DIR__ . '/../..' . '/sources/application/UI/Component/DataTable/StaticTable/StaticTable.php',
'Combodo\\iTop\\Application\\UI\\Component\\FieldSet\\FieldSet' => __DIR__ . '/../..' . '/sources/application/UI/Component/FieldSet/FieldSet.php',
'Combodo\\iTop\\Application\\UI\\Component\\Field\\Field' => __DIR__ . '/../..' . '/sources/application/UI/Component/Field/Field.php',
'Combodo\\iTop\\Application\\UI\\Component\\Form\\Form' => __DIR__ . '/../..' . '/sources/application/UI/Component/Form/Form.php',

View File

@@ -10,7 +10,9 @@ use ApplicationException;
use AttributeLinkedSet;
use CMDBObjectSet;
use cmdbAbstractObject;
use Combodo\iTop\Application\UI\Component\DataTable\StaticTable\StaticTable;
use Combodo\iTop\Application\UI\Component\Panel\PanelFactory;
use Combodo\iTop\Application\UI\Component\Title\TitleFactory;
use MetaModel;
use appUserPreferences;
use UserRights;
@@ -639,7 +641,19 @@ class DataTableFactory
} )'
];
return $aOptions;
}
public static function MakeForStaticData(string $sTitle, array $aColumns, array $aData, ?string $sId = null)
{
$oBlock = new UIContentBlock();
$oTitle = TitleFactory::MakeNeutral($sTitle, 3);
$oBlock->AddSubBlock($oTitle);
$oTable = new StaticTable($sId);
$oTable->SetColumns($aColumns);
$oTable->SetData($aData);
$oBlock->AddSubBlock($oTable);
return $oBlock;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Combodo\iTop\Application\UI\Component\DataTable\StaticTable;
use Combodo\iTop\Application\UI\Layout\UIContentBlock;
/**
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Tables with static data
* Class StaticTable
*/
class StaticTable extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-datatable';
public const HTML_TEMPLATE_REL_PATH = 'components/datatable/static/layout';
public const JS_TEMPLATE_REL_PATH = 'components/datatable/static/layout';
/**
* @var array of 'entry name' => [
* 'description' => tooltip,
* 'label' => label to display,
* 'class' => cell CSS class,
* 'metadata' => [key => value] transformed into data-key="value"
* ]
*/
private $aColumns;
/**
* @var array of [
* '@class' => css class of the row,
* 'entry name' => [
* 'value_html' => value to display in the cell,
* 'value_raw' => real value put into data-value-raw
* ], ...
* ]
*/
private $aData;
public function __construct(string $sId = null, string $sContainerCSSClass = '')
{
parent::__construct($sId, $sContainerCSSClass);
$this->aColumns = [];
$this->aData = [];
}
/**
* @return array
*/
public function GetColumns(): array
{
return $this->aColumns;
}
/**
* @param array $aColumns
*/
public function SetColumns(array $aColumns): void
{
$this->aColumns = $aColumns;
}
/**
* @return array
*/
public function GetData(): array
{
return $this->aData;
}
/**
* @param array $aData
*/
public function SetData(array $aData): void
{
$this->aData = $aData;
}
}

View File

@@ -62,4 +62,9 @@ class TitleFactory
return $oTitle;
}
public static function MakeNeutral(string $sTitle, int $iLevel = 1, ?string $sId = null)
{
return new Title($sTitle, $iLevel, $sId);
}
}

View File

@@ -0,0 +1,48 @@
{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% set columns = oUIBlock.GetColumns() %}
<table id="{{ oUIBlock.GetId() }}" class="ibo-datatable listResults" style="width:100%;">
<thead>
<tr>
{% for column in columns %}
<th class="ibo-datatable-header" title="{{ column.description }}">{{ column.label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for data in oUIBlock.GetData() %}
{% if data['@class'] is not empty %}
<tr role="row" class="{{ data['@class'] }}">
{% else %}
<tr>
{% endif %}
{% for name,column in columns %}
<td {% if column.class is not empty %}class="{{ column.class }}" {% endif %}
{% if column.metadata is not empty %}
{% for prop,value in column.metadata %}
data-{{ prop|replace({'_': '-'}) }}="{{ value }}"
{% endfor %}
{% endif %}
{% set cellValueHtml = '' %}
{% for cellName,cellValue in data %}
{% if cellName == name %}
{% if cellValue.value_raw is empty %}
{% set cellValueHtml = cellValue %}
{% else %}
data-value-raw="{{ cellValue.value_raw }}"
{% if cellValue.value_html is not empty %}
{% set cellValueHtml = cellValue.value_html %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% if cellValueHtml is empty %}
{% set cellValueHtml = '&nbsp;' %}
{% endif %}
>{{ cellValueHtml|raw }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>

View File

@@ -0,0 +1,47 @@
{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% if oUIBlock.GetOptions() is not empty %}
{% set iPageSize = oUIBlock.GetOptions()["iPageSize"] %}
{% else %}
{% set iPageSize = 10 %}
{% endif %}
$('#{{ oUIBlock.GetId() }}').DataTable({
language: {
processing: "{{ 'UI:Datatables:Language:Processing'|dict_s }}",
search: "{{ 'UI:Datatables:Language:Search'|dict_s }}",
lengthMenu: "{{ 'UI:Datatables:Language:LengthMenu'|dict_s }}",
zeroRecords: "{{ 'UI:Datatables:Language:ZeroRecords'|dict_s }}",
info: "{{ 'UI:Datatables:Language:Info'|dict_s }}",
infoEmpty: "",
infoFiltered: "({{ 'UI:Datatables:Language:InfoFiltered'|dict_s }})",
emptyTable: "{{ 'UI:Datatables:Language:EmptyTable'|dict_s }}",
paginate: {
first: "<<",
previous: "<",
next: ">",
last: ">>"
},
aria: {
sortAscending: ": {{ 'UI:Datatables:Language:Sort:Ascending'|dict_s }}",
sortDescending: ": {{ 'UI:Datatables:Language:Sort:Descending'|dict_s }}"
}
},
order: [],
rowId: "id",
filter: false,
{% if oUIBlock.GetData()|length <= iPageSize %}
paging: false,
{% endif %}
dom: "<'ibo-datatable-toolbar'pil>t<'ibo-datatable-toolbar'pil>",
lengthMenu: [[ {{ iPageSize }}, {{ iPageSize*2 }}, {{ iPageSize*3 }}, {{ iPageSize*4 }}, -1], [ {{ iPageSize }}, {{ iPageSize*2 }}, {{ iPageSize*3 }}, {{ iPageSize*4 }}, "{{ 'Portal:Datatables:Language:DisplayLength:All'|dict_s }}"]],
columns: [
{% for column in oUIBlock.GetColumns() %}
{
width: "auto",
sortable: true
},
{% endfor %}
]
});