N°3648 - Feedback alpha 3.0 : finish relation edition

This commit is contained in:
acognet
2021-02-15 16:50:24 +01:00
parent bcdb6bdac8
commit c1c8fad97e
11 changed files with 244 additions and 37 deletions

View File

@@ -154,17 +154,25 @@ $(function()
// - Update URL hash when tab is activated
_onTabActivated: function(oUI)
{
let oState = {};
let oState = {};
// Get the id of this tab widget.
const sId = this.element.attr( 'id' );
// Get the id of this tab widget.
const sId = this.element.attr('id');
// Get the index of this tab.
const iIdx = $(oUI.newTab).prevAll().length;
//Datatable are not displayed correctly when hidden
$(oUI.newPanel).find('.dataTables_scrollBody > .ibo-datatable').each(function () {
if ($('#'+this.id).find('thead').is(':visible')) {
$('#'+this.id).DataTable().columns.adjust().draw();
$('#'+this.id).find('thead').hide();
}
});
// Set the state!
oState[ sId ] = iIdx;
$.bbq.pushState( oState );
// Get the index of this tab.
const iIdx = $(oUI.newTab).prevAll().length;
// Set the state!
oState[sId] = iIdx;
$.bbq.pushState(oState);
},
// - Change current tab as necessary when URL hash changes
_onHashChange: function()

View File

@@ -285,12 +285,13 @@ function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sN
}
$oForm->AddSubBlock($oAppContext->GetForFormBlock());
$oDisplayBlock = new DisplayBlock($oFilter, 'list', false);
//by default all the elements are selected
$aExtraParams['selectionMode'] = 'negative';
$oForm->AddSubBlock($oDisplayBlock->GetDisplay($oP, 1, $aExtraParams));
$oForm->AddSubBlock(ButtonUIBlockFactory::MakeNeutral(Dict::S('UI:Button:Cancel'), 'cancel')->SetOnClickJsCode('window.history.back()'));
$oForm->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), 'next', Dict::S('UI:Button:Next'), true));
$oP->AddUiBlock($oForm);
$oP->add_ready_script("$('#1 table.listResults').trigger('check_all');");
}
function DisplayNavigatorListTab($oP, $aResults, $sRelation, $sDirection, $oObj)

View File

@@ -11,6 +11,8 @@ use ApplicationMenu;
use AttributeLinkedSet;
use BulkExport;
use BulkExportException;
use CMDBObjectSet;
use CMDBSource;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityEntry\ActivityEntryFactory;
@@ -451,4 +453,158 @@ class AjaxRenderController
return $aResults;
}
/**
* @param string $sEncoding
* @param string $sFilter
*
* @return array
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public static function RefreshDashletList(string $sStyle, string $sFilter): array
{
$aExtraParams = utils::ReadParam('extra_params', '', false, 'raw_data');
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (isset($aExtraParams['group_by'])) {
$sAlias = $oFilter->GetClassAlias();
if (isset($aExtraParams['group_by_label'])) {
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
$sGroupByLabel = $aExtraParams['group_by_label'];
} else {
// Backward compatibility: group_by is simply a field id
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($oFilter->GetClass(), $aExtraParams['group_by']);
}
// Security filtering
$aFields = $oGroupByExp->ListRequiredFields();
foreach ($aFields as $sFieldAlias) {
$aMatches = array();
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches)) {
$sFieldClass = $oFilter->GetClassName($aMatches[1]);
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
if ($oAttDef instanceof AttributeOneWayPassword) {
throw new Exception('Grouping on password fields is not supported.');
}
}
}
$aGroupBy = array();
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$aQueryParams = array();
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
$aFunctions = array();
$sAggregationFunction = 'count';
$sFctVar = '_itop_count_';
$sAggregationAttr = '';
if (isset($aExtraParams['aggregation_function']) && !empty($aExtraParams['aggregation_attribute'])) {
$sAggregationFunction = $aExtraParams['aggregation_function'];
$sAggregationAttr = $aExtraParams['aggregation_attribute'];
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAggregationAttr.'`');
$oFctExpr = new FunctionExpression(strtoupper($sAggregationFunction), array($oAttrExpr));
$sFctVar = '_itop_'.$sAggregationFunction.'_';
$aFunctions = array($sFctVar => $oFctExpr);
}
if (!empty($sAggregationAttr)) {
$sClass = $oFilter->GetClass();
$sAggregationAttr = MetaModel::GetLabel($sClass, $sAggregationAttr);
}
$iLimit = 0;
if (isset($aExtraParams['limit'])) {
$iLimit = intval($aExtraParams['limit']);
}
$aOrderBy = array();
if (isset($aExtraParams['order_direction']) && isset($aExtraParams['order_by'])) {
switch ($aExtraParams['order_by']) {
case 'attribute':
$aOrderBy = array('grouped_by_1' => ($aExtraParams['order_direction'] === 'asc'));
break;
case 'function':
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
break;
}
}
$sSql = $oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy, true, $aFunctions, $aOrderBy, $iLimit);
$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($oFilter, $sValue, $sValue);
$aLabels[$iRow] = $sHtmlValue;
$aGroupBy[$iRow] = (int)$aRow[$sFctVar];
$iTotalCount += $aRow['_itop_count_'];
}
$aResult = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
foreach ($aGroupBy as $iRow => $iCount) {
// Build the search for this subset
$oSubsetSearch = $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));
$aResult[] = 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
}
} else {
// Simply count the number of elements in the set
$aOrderBy = [];
if (isset($aExtraParams['order_direction']) && isset($aExtraParams['order_by'])) {
$aOrderBy = ['order_by' => $aExtraParams['order_by'], 'order_direction' => $aExtraParams['order_direction']];
}
$oSet = new CMDBObjectSet($oFilter, $aOrderBy, $aExtraParams);
$iCount = $oSet->Count();
$sFormat = 'UI:CountOfObjects';
if (isset($aExtraParams['format'])) {
$sFormat = $aExtraParams['format'];
}
$aResult = ['result' => Dict::Format($sFormat, $iCount)];
}
return $aResult;
}
public static function RefreshCount(string $sFilter): array
{
$aExtraParams = utils::ReadParam('extra_params', '', false, 'raw_data');
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oSet = new CMDBObjectSet($oFilter, [], $aExtraParams);
$iCount = $oSet->Count();
$aResult = ['count' => $iCount];
return $aResult;
}
}

View File

@@ -214,7 +214,6 @@ class DataTableSettings implements Serializable
return null;
}
}
$oSettings->unserialize($pref);
return $oSettings;
}

View File

@@ -115,7 +115,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
}
if (!isset($aExtraParams['surround_with_panel']) || $aExtraParams['surround_with_panel']) {
$oContainer = PanelUIBlockFactory::MakeForClass($oSet->GetClass(), "Result")->AddCSSClass('ibo-datatable-panel');
$oContainer = PanelUIBlockFactory::MakeForClass($oSet->GetClass(), "")->AddCSSClass('ibo-datatable-panel');
$oContainer->AddToolbarBlock($oBlockMenu);
$oContainer->AddMainBlock($oDataTable);
} else {
@@ -147,13 +147,6 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
public static function MakeForRendering(string $sListId, DBObjectSet $oSet, $aExtraParams = array())
{
$oDataTable = new DataTable('datatable_'.$sListId);
///////////////////////////////////////////////////
/*TODO 3.0.0 PrintableVersion
if ($oPage->IsPrintableVersion() || $oPage->is_pdf())
{
return self::GetDisplaySetForPrinting($oPage, $oSet, $aExtraParams);
}
*/
// Initialize and check the parameters
$bViewLink = isset($aExtraParams['view_link']) ? $aExtraParams['view_link'] : true;
@@ -352,6 +345,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
$aOptions['select_mode'] = "single";
}
}
$aOptions['selectionMode'] = $aExtraParams['selectionMode']?? 'positive';
if (isset($aExtraParams['cssCount'])) {
$aOptions['sCountSelector'] = $aExtraParams['cssCount'];
@@ -577,6 +571,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
$aOptions['select_mode'] = "single";
}
}
$aOptions['selectionMode'] = $aExtraParams['selectionMode']?? 'positive';
$aOptions['sort'] = $aSortDatable;

View File

@@ -1,7 +1,7 @@
{% for oSubBlock in oUIBlock.GetSubBlocks() %}{{ render_block(oSubBlock, {aPage: aPage}) }}{% endfor %}
{% if oUIBlock.GetOptions("select_mode") is not empty %}
<input type="hidden" name="selectionMode" value="positive">
<input type="hidden" name="selectionMode" value="{{ oUIBlock.GetOption("selectionMode") }}">
<input type="hidden" name="selectionCount" value="0">
<div data-target="ibo-datatable--selection"></div>
{% if oUIBlock.GetAjaxData("extra_params") is not empty %}

View File

@@ -4,7 +4,7 @@
{% if oUIBlock.GetOption("iPageSize") is not empty %}
{% set iPageSize = oUIBlock.GetOption("iPageSize") %}
{% else %}
{% set iPageSize = 10 %}
{% set iPageSize = 10 %}
{% endif %}
$('#{{ oUIBlock.GetId() }}').closest("[role=dialog]").on("dialogbeforeclose", function () {
@@ -13,10 +13,36 @@ $('#{{ oUIBlock.GetId() }}').closest("[role=dialog]").on("dialogbeforeclose", fu
$('#{{ sListId }}').data('target', 'ibo-datatables--outer');
if ($.fn.dataTable.isDataTable('#{{ oUIBlock.GetId() }}')) {
if ($('#{{ oUIBlock.GetId() }}') != 'undefined' && $.fn.dataTable.isDataTable('#{{ oUIBlock.GetId() }}'))
{
$('#{{ oUIBlock.GetId() }}').DataTable().destroy(false);
}
//define maxHeight for Datatable
var maxHeight{{ sListId }} = 300;
if ($('#{{ oUIBlock.GetId() }}').closest('.ui-dialog').length > 0)
{
//we are in dialogbox
maxHeight{{ sListId }} = $('#{{ oUIBlock.GetId() }}').closest('.ui-dialog').height();
a
}
else
{
maxHeight{{ sListId }} = $(window).height()-$('#ibo-top-container').outerHeight()+$('#ibo-main-content').height()-$('#ibo-main-content').outerHeight();
}
if ($('#{{ oUIBlock.GetId() }}').closest('[data-target=search_results]').parent().find('.ibo-search-form-panel').length > 0)
{
//we are in dialogbox
maxHeight{{ sListId }} = maxHeight{{ sListId }}-$('#{{ oUIBlock.GetId() }}').closest('[data-target=search_results]').parent().find('.ibo-search-form-panel').height();
}
if (maxHeight{{ sListId }} < 300)
{
maxHeight{{ sListId }} = 250;
}
else
{
maxHeight{{ sListId }} = maxHeight{{ sListId }} -50;
}
console.warn('#{{ oUIBlock.GetId() }}');
var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
language: {
processing: "{{ 'UI:Datatables:Language:Processing'|dict_s }}",
@@ -38,6 +64,9 @@ var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
sortDescending: ": {{ 'UI:Datatables:Language:Sort:Descending'|dict_s }}"
}
},
scrollY: maxHeight{{ sListId }},
scrollX: true,
scrollCollapse: true,
lengthMenu: [[ {{ iPageSize }}, {{ iPageSize*2 }}, {{ iPageSize*3 }}, {{ iPageSize*4 }}, -1], [ {{ iPageSize }}, {{ iPageSize*2 }}, {{ iPageSize*3 }}, {{ iPageSize*4 }}, "{{ 'Portal:Datatables:Language:DisplayLength:All'|dict_s }}"]],
dom: "<'ibo-datatable-toolbar'pil>t<'ibo-datatable-toolbar'pil>",
{% if( oUIBlock.GetOptions("sort")[0] is defined ) %}
@@ -51,23 +80,30 @@ var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
style: "{{ oUIBlock.GetOption("select_mode") }}"
},
rowCallback: function (oRow, oData) {
if ($(this).closest('.ibo-panel--body').find('[name=selectionMode]').val() === "negative") {
if ($(this).closest('.ibo-panel--body').find('[name=selectionMode]').val() === "negative")
{
if (oSelectedItems{{ sListId }}.indexOf(oData.id) === -1) {
$(oRow).select();
$(oRow).find('td:first-child input').prop('checked', true);
}
} else {
if (oSelectedItems{{ sListId }}.indexOf(oData.id) > -1) {
if (oSelectedItems{{ sListId }}.indexOf(oData.id) > -1)
{
$(oRow).select();
$(oRow).find('td:first-child input').prop('checked', true);
}
}
},
drawCallback: function () {
if ($(this).closest('.ibo-panel--body').find('[name=selectionMode]').val() === "negative") {
$(this).find('[name=selectAll]').prop('checked', true);
console.warn('la');
console.warn($(this).closest('.ibo-panel--body').find('[name=selectionMode]').val());
if ($(this).closest('.ibo-panel--body').find('[name=selectionMode]').val() === "negative")
{
$(this).closest('.dataTables_wrapper').find('.checkAll')[0].checked = true;
$(this).DataTable().rows({page: 'current'}).select();
} else {
}
else
{
$(this).closest('.dataTables_wrapper').find('.dataTables_paginate, .dataTables_info').show();
}
},
@@ -84,11 +120,12 @@ var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
width: "20px",
searchable: false,
sortable: false,
orderable: false,
title:
{% if oUIBlock.GetOption("select_mode") != "single" %}
'<span class="row_input"><input type="checkbox" onclick="checkAllDataTable(\'{{ oUIBlock.GetId() }}\',this.checked,\'{{ oUIBlock.GetOption("sListId") }}\');" class="checkAll" id="field_{{ oUIBlock.GetId() }}_check_all" name="field_{{ oUIBlock.GetId() }}_check_all" title="{{ 'UI:SearchValue:CheckAll'|dict_s }} / {{ 'UI:SearchValue:UncheckAll'|dict_s }}"></span>'
{% if oUIBlock.GetOption("select_mode") != "single" %}
'<span class="row_input"><input type="checkbox" onclick="checkAllDataTable(\'{{ oUIBlock.GetId() }}\',this.checked,\'{{ oUIBlock.GetOption("sListId") }}\');" class="checkAll" id="field_{{ oUIBlock.GetId() }}_check_all" name="field_{{ oUIBlock.GetId() }}_check_all" title="{{ 'UI:SearchValue:CheckAll'|dict_s }} / {{ 'UI:SearchValue:UncheckAll'|dict_s }}"/></span>'
{% else %}
'<span class="row_input"><input type="checkbox" style="display: none;" onclick="checkAllDataTable(\'{{ oUIBlock.GetId() }}\',this.checked,\'{{ oUIBlock.GetOption("sListId") }}\');" class="checkAll" id="field_{{ oUIBlock.GetId() }}_check_all" name="field_{{ oUIBlock.GetId() }}_check_all" title="{{ 'UI:SearchValue:CheckAll'|dict_s }} / {{ 'UI:SearchValue:UncheckAll'|dict_s }}"></span>'
'<span class="row_input"><input type="checkbox" style="display: none;" onclick="checkAllDataTable(\'{{ oUIBlock.GetId() }}\',this.checked,\'{{ oUIBlock.GetOption("sListId") }}\');" class="checkAll" id="field_{{ oUIBlock.GetId() }}_check_all" name="field_{{ oUIBlock.GetId() }}_check_all" title="{{ 'UI:SearchValue:CheckAll'|dict_s }} / {{ 'UI:SearchValue:UncheckAll'|dict_s }}"/></span>'
{% endif %},
type: "html",
data: "",
@@ -110,7 +147,8 @@ var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
{% endif %}
{% for aColumn in oUIBlock.GetDisplayColumns() %}
{
width: "auto",
// width: 100,
autoWidth: true,
searchable: false,
sortable: true,
title: "{{ aColumn["attribute_label"] }}",
@@ -137,7 +175,9 @@ var oTable{{ sListId }} = $('#{{ oUIBlock.GetId() }}').DataTable({
pages: 5 // number of pages to cache
}),
initComplete: function () {
if (this.api().page.info().pages < 2) {
this.api().columns.adjust().draw();
if (this.api().page.info().pages < 2)
{
this.parent().find('.dataTables_paginate').hide();
this.parent().find('.dataTables_length').hide();
}

View File

@@ -4,7 +4,7 @@
<input type="hidden" name="attr_{{ oUIBlock.GetRef() }}" value="">
{% set columns = oUIBlock.GetColumns() %}
<table id="{{ oUIBlock.GetId() }}" class="ibo-datatable listResults{% if oUIBlock.IsHidden() %} ibo-is-hidden{% endif %}" style="width:100%;">
<table id="{{ oUIBlock.GetId() }}" class="ibo-datatable listResults{% if oUIBlock.IsHidden() %} ibo-is-hidden{% endif %}" >
<thead>
<tr>
{% for column in columns %}

View File

@@ -1,9 +1,15 @@
$('#{{ oUIBlock.GetId() }}').DataTable({
var table{{ oUIBlock.GetId()|sanitize_identifier }}= $('#{{ oUIBlock.GetId() }}').DataTable({
language: {
emptyTable: "{{ 'UI:Message:EmptyList:UseAdd'|dict_s }}"
},
scrollX: true,
scrollCollapse: true,
paging: false,
filter: false,
search: false,
dom: "t"
});
dom: "t",
});
//table{{ oUIBlock.GetId()|sanitize_identifier }}.columns.adjust().draw();
//$(".dataTables_scrollBody thead").hide();

View File

@@ -2,7 +2,7 @@
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% set columns = oUIBlock.GetColumns() %}
<table id="{{ oUIBlock.GetId() }}" class="ibo-datatable listResults{% if oUIBlock.IsHidden() %} ibo-is-hidden{% endif %}" style="width:100%;">
<table id="{{ oUIBlock.GetId() }}" class="ibo-datatable listResults{% if oUIBlock.IsHidden() %} ibo-is-hidden{% endif %}">
<thead>
<tr>
{% for column in columns %}

View File

@@ -28,6 +28,8 @@ $('#{{ oUIBlock.GetId() }}').DataTable({
sortDescending: ": {{ 'UI:Datatables:Language:Sort:Descending'|dict_s }}"
}
},
scrollX: true,
scrollCollapse: true,
order: [],
rowId: "id",
filter: false,