From 5157f511fca0c394fbd295e2f236c52858856e40 Mon Sep 17 00:00:00 2001 From: bdalsass <95754414+bdalsass@users.noreply.github.com> Date: Mon, 26 Sep 2022 08:20:28 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B05073=20-=20Implements=20line=20actions?= =?UTF-8?q?=20in=20a=20datatable=20(#337)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * datatable row actions Below is a sample of extra param to enable feature: $aExtraParams['row_actions'] = [ [ 'tooltip' => 'add an element', 'icon_css_class' => 'fa-plus', 'css_class' => 'ibo-is-success', 'level' => 'secondary', 'on_action_js' => 'console.log(aData);', ], [ 'tooltip' => 'remove an element', 'icon_css_class' => 'fa-minus', 'css_class' => 'ibo-is-danger', 'level' => 'secondary', 'on_action_js' => 'console.log("You clicked the remove button");', ], [ 'tooltip' => 'open in new tab', 'icon_css_class' => 'fa-external-link-square-alt', 'on_action_js' => 'window.open("http://localhost/itop-branchs/dev/pages/UI.php?operation=details&class=UserRequest&id=" + aData.id + "&c[menu]=UserRequest%3AOpenRequests");', ], [ 'tooltip' => 'other actions', 'icon_css_class' => 'fa-ellipsis-v', 'on_action_js' => 'console.log(event);', ], ]; * datatable row actions (update) * datatable row actions (update) * datatable row actions (add template role) * datatable row actions (align actions) * datatable row actions (change template factory make to make standard) * datatable row actions (use trait to handle row actions) * datatable row actions (row actions templates) * datatable row actions (row actions templates) * datatable row actions (row actions templates) * datatable row actions (extends to static and form) * datatable row actions (extends to static and form) * datatable row actions (code review S) * datatable row actions (code review S) * datatable row actions (code review S) * Update js/dataTables.main.js Co-authored-by: Molkobain * Update js/dataTables.main.js Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php Co-authored-by: Molkobain * Update templates/base/components/datatable/row-actions/handler.js.twig Co-authored-by: Molkobain * datatable row actions (code review M) * Update js/dataTables.main.js Co-authored-by: Molkobain * Update js/dataTables.main.js Co-authored-by: Molkobain * Update js/dataTables.main.js Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php Co-authored-by: Molkobain * Update js/dataTables.main.js Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php Co-authored-by: Molkobain * Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php Co-authored-by: Molkobain * Update application/utils.inc.php Co-authored-by: Molkobain * datatable row actions (code review M2) * datatable row actions (code review M3) Co-authored-by: Molkobain --- application/utils.inc.php | 45 +++++- css/backoffice/components/_datatable.scss | 4 + .../en.dictionary.itop.datatable.php | 22 +-- .../fr.dictionary.itop.datatable.php | 22 +-- js/dataTables.main.js | 28 +++- lib/composer/autoload_classmap.php | 3 + lib/composer/autoload_static.php | 3 + lib/composer/installed.php | 4 +- .../UI/Base/Component/DataTable/DataTable.php | 3 + .../DataTable/DataTableUIBlockFactory.php | 135 +++++++++++++++--- .../StaticTable/FormTable/FormTable.php | 9 +- .../DataTable/StaticTable/StaticTable.php | 28 +++- .../Component/DataTable/tTableRowActions.php | 73 ++++++++++ .../UI/Base/Component/Template/Template.php | 37 +++++ .../Template/TemplateUIBlockFactory.php | 50 +++++++ .../components/datatable/layout.html.twig | 9 +- .../components/datatable/layout.ready.js.twig | 7 +- .../datatable/row-actions/handler.js.twig | 15 ++ .../static/formtable/layout.html.twig | 10 +- .../static/formtable/layout.ready.js.twig | 9 +- .../datatable/static/layout.html.twig | 6 +- .../datatable/static/layout.ready.js.twig | 7 +- .../base/components/template/layout.html.twig | 9 ++ 23 files changed, 467 insertions(+), 71 deletions(-) create mode 100644 sources/Application/UI/Base/Component/DataTable/tTableRowActions.php create mode 100644 sources/Application/UI/Base/Component/Template/Template.php create mode 100644 sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php create mode 100644 templates/base/components/datatable/row-actions/handler.js.twig create mode 100644 templates/base/components/template/layout.html.twig diff --git a/application/utils.inc.php b/application/utils.inc.php index eb451d9af..f04f8971b 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -3143,18 +3143,53 @@ HTML; */ public static function AddParameterToUrl(string $sUrl, string $sParamName, string $sParamValue): string { - if (strpos($sUrl, '?') === false) - { + if (strpos($sUrl, '?') === false) { $sUrl = $sUrl.'?'.urlencode($sParamName).'='.urlencode($sParamValue); - } - else - { + } else { $sUrl = $sUrl.'&'.urlencode($sParamName).'='.urlencode($sParamValue); } return $sUrl; } + /** + * Return traits array used by a class and by parent classes hierarchy. + * + * @see https://www.php.net/manual/en/function.class-uses.php#110752 + * + * @param string $sClass Class to scan + * @param bool $bAutoload Autoload flag + * + * @return array traits used + * @since 3.1.0 + */ + public static function TraitsUsedByClass(string $sClass, bool $bAutoload = true): array + { + $aTraits = []; + do { + $aTraits = array_merge(class_uses($sClass, $bAutoload), $aTraits); + } while ($sClass = get_parent_class($sClass)); + foreach ($aTraits as $sTrait => $same) { + $aTraits = array_merge(class_uses($sTrait, $bAutoload), $aTraits); + } + + return array_unique($aTraits); + } + + /** + * Test trait usage by a class or by parent classes hierarchy. + * + * @param string $sTrait Trait to search for + * @param string $sClass Class to check + * + * @return bool + * @since 3.1.0 + */ + public static function IsTraitUsedByClass(string $sTrait, string $sClass): bool + { + return in_array($sTrait, self::TraitsUsedByClass($sClass, true)); + } + public static function GetUniqId() { return hash('sha256', uniqid(sprintf('%x', rand()), true).sprintf('%x', rand())); diff --git a/css/backoffice/components/_datatable.scss b/css/backoffice/components/_datatable.scss index 63d5e3186..4160af243 100644 --- a/css/backoffice/components/_datatable.scss +++ b/css/backoffice/components/_datatable.scss @@ -119,6 +119,10 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default; &.selected { background-color: $ibo-datatable--row--background-color--is-selected; } + + .ibo-datatable--row-actions-toolbar{ + justify-content: end; + } } } diff --git a/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php b/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php index 0b5d98c92..6f584e4d4 100644 --- a/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php +++ b/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php @@ -19,14 +19,16 @@ // Display DataTable Dict::Add('EN US', 'English', 'English', array( - 'UI:Datatables:Language:Processing' => 'Please wait...', - 'UI:Datatables:Language:LengthMenu' => '_MENU_ per page', - 'UI:Datatables:Language:ZeroRecords' => 'No result', - 'UI:Datatables:Language:Info' => '_TOTAL_ item(s)', - 'UI:Datatables:Language:InfoEmpty' => 'No information', - 'UI:Datatables:Language:EmptyTable' => 'No data available in this table', - 'UI:Datatables:Language:Error' => 'An error occured while running the query', - 'UI:Datatables:Language:DisplayLength:All' => 'All', - 'UI:Datatables:Language:Sort:Ascending' => 'enable for an ascending sort', - 'UI:Datatables:Language:Sort:Descending' => 'enable for a descending sort', + 'UI:Datatables:Language:Processing' => 'Please wait...', + 'UI:Datatables:Language:LengthMenu' => '_MENU_ per page', + 'UI:Datatables:Language:ZeroRecords' => 'No result', + 'UI:Datatables:Language:Info' => '_TOTAL_ item(s)', + 'UI:Datatables:Language:InfoEmpty' => 'No information', + 'UI:Datatables:Language:EmptyTable' => 'No data available in this table', + 'UI:Datatables:Language:Error' => 'An error occured while running the query', + 'UI:Datatables:Language:DisplayLength:All' => 'All', + 'UI:Datatables:Language:Sort:Ascending' => 'enable for an ascending sort', + 'UI:Datatables:Language:Sort:Descending' => 'enable for a descending sort', + 'UI:Datatables:Column:RowActions:Label' => '', + 'UI:Datatables:Column:RowActions:Description' => '', )); \ No newline at end of file diff --git a/dictionaries/ui/components/datatable/fr.dictionary.itop.datatable.php b/dictionaries/ui/components/datatable/fr.dictionary.itop.datatable.php index ab8a8f6e0..98799ce23 100644 --- a/dictionaries/ui/components/datatable/fr.dictionary.itop.datatable.php +++ b/dictionaries/ui/components/datatable/fr.dictionary.itop.datatable.php @@ -18,14 +18,16 @@ */ // Display DataTable Dict::Add('FR FR', 'French', 'Français', array( - 'UI:Datatables:Language:Processing' => 'Patientez ...', - 'UI:Datatables:Language:LengthMenu' => '_MENU_ par page', - 'UI:Datatables:Language:ZeroRecords' => 'Pas de résultat', - 'UI:Datatables:Language:Info' => '_TOTAL_ élément(s)', - 'UI:Datatables:Language:InfoEmpty' => 'Pas d\'information', - 'UI:Datatables:Language:EmptyTable' => 'Pas de résultat', - 'UI:Datatables:Language:Error' => 'Erreur lors du chargement des données', - 'UI:Datatables:Language:DisplayLength:All' => 'Tous', - 'UI:Datatables:Language:Sort:Ascending' => 'tri croissant', - 'UI:Datatables:Language:Sort:Descending' => 'tri décroissant', + 'UI:Datatables:Language:Processing' => 'Patientez ...', + 'UI:Datatables:Language:LengthMenu' => '_MENU_ par page', + 'UI:Datatables:Language:ZeroRecords' => 'Pas de résultat', + 'UI:Datatables:Language:Info' => '_TOTAL_ élément(s)', + 'UI:Datatables:Language:InfoEmpty' => 'Pas d\'information', + 'UI:Datatables:Language:EmptyTable' => 'Pas de résultat', + 'UI:Datatables:Language:Error' => 'Erreur lors du chargement des données', + 'UI:Datatables:Language:DisplayLength:All' => 'Tous', + 'UI:Datatables:Language:Sort:Ascending' => 'tri croissant', + 'UI:Datatables:Language:Sort:Descending' => 'tri décroissant', + 'UI:Datatables:Column:RowActions:Label' => '', + 'UI:Datatables:Column:RowActions:Description' => '', )); \ No newline at end of file diff --git a/js/dataTables.main.js b/js/dataTables.main.js index 86740209c..bf7eb0bf4 100644 --- a/js/dataTables.main.js +++ b/js/dataTables.main.js @@ -78,4 +78,30 @@ function getMultipleSelectionParams(listId) }); return oRes; -} \ No newline at end of file +} + +/** + * Return column JSON declaration for row actions. + * Could be part of column or columnDefs declaration of datatable.js. + * + * @param sTableId + * @param iColumnTargetIndex + * @returns {*} + * @since 3.1.0 + */ +function getRowActionsColumnDefinition(sTableId, iColumnTargetIndex = -1) +{ + let aColumn = { + type: "html", + orderable: false, + render: function ( data, type, row, meta ) { + return $(`#${sTableId}_actions_buttons_template`).html(); + } + }; + + if (iColumnTargetIndex !== -1) { + aColumn['targets'] = iColumnTargetIndex; + } + + return aColumn; +} diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 0137d277d..abef8259a 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -227,6 +227,7 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTableRow\\FormTableRow' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTableRow/FormTableRow.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => $baseDir . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php', @@ -272,6 +273,8 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateHelper' => $baseDir . '/sources/Application/UI/Base/Component/QuickCreate/QuickCreateHelper.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Spinner\\Spinner' => $baseDir . '/sources/Application/UI/Base/Component/Spinner/Spinner.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Spinner\\SpinnerUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Spinner/SpinnerUIBlockFactory.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Template\\Template' => $baseDir . '/sources/Application/UI/Base/Component/Template/Template.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Template\\TemplateUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Text\\Text' => $baseDir . '/sources/Application/UI/Base/Component/Text/Text.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\Title' => $baseDir . '/sources/Application/UI/Base/Component/Title/Title.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Title/TitleUIBlockFactory.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 6866fc9e7..5dca15b8f 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -592,6 +592,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTableRow\\FormTableRow' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTableRow/FormTableRow.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php', @@ -637,6 +638,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\UI\\Base\\Component\\QuickCreate\\QuickCreateHelper' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/QuickCreate/QuickCreateHelper.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Spinner\\Spinner' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Spinner/Spinner.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Spinner\\SpinnerUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Spinner/SpinnerUIBlockFactory.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Template\\Template' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Template/Template.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Template\\TemplateUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Text\\Text' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Text/Text.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\Title' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Title/Title.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Title\\TitleUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Title/TitleUIBlockFactory.php', diff --git a/lib/composer/installed.php b/lib/composer/installed.php index dee14e84b..f74f47b1d 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '7b60c9c71af3167cc075063ce67b836b96a9e2f0', + 'reference' => '8a3e07dd80c8316d68ad44a892c51f4ed5de572c', 'name' => 'combodo/itop', 'dev' => true, ), @@ -25,7 +25,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '7b60c9c71af3167cc075063ce67b836b96a9e2f0', + 'reference' => '8a3e07dd80c8316d68ad44a892c51f4ed5de572c', 'dev_requirement' => false, ), 'combodo/tcpdf' => array( diff --git a/sources/Application/UI/Base/Component/DataTable/DataTable.php b/sources/Application/UI/Base/Component/DataTable/DataTable.php index c9d2e24c9..b7acb0eee 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTable.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTable.php @@ -21,6 +21,7 @@ use DataTableConfig; class DataTable extends UIContentBlock { use tJSRefreshCallback; + use tTableRowActions; // Overloaded constants public const BLOCK_CODE = 'ibo-datatable'; @@ -51,6 +52,7 @@ class DataTable extends UIContentBlock */ protected $aInitDisplayData; + /** * Panel constructor. * @@ -250,4 +252,5 @@ class DataTable extends UIContentBlock return []; } + } diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php index 8b728366b..ebabc69f9 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php @@ -12,6 +12,7 @@ use appUserPreferences; use AttributeLinkedSet; use cmdbAbstractObject; use Combodo\iTop\Application\UI\Base\AbstractUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection; use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTable\FormTable; use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTableRow\FormTableRow; @@ -19,8 +20,10 @@ use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\StaticTable use Combodo\iTop\Application\UI\Base\Component\Html\Html; use Combodo\iTop\Application\UI\Base\Component\Html\HtmlFactory; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Template\TemplateUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; +use Combodo\iTop\Application\UI\Base\iUIBlock; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use Combodo\iTop\Controller\AjaxRenderController; use DBObjectSet; @@ -180,6 +183,46 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory return $oContainer; } + /** + * Make a row actions toolbar template. + * + * @param iUIBlock $oTable datatable object that needs to use tTableRowActions trait + * + * @return \Combodo\iTop\Application\UI\Base\Component\Template\Template + * @throws \Exception + * @since 3.1.0 + */ + public static function MakeActionRowToolbarTemplate(iUIBlock $oTable) + { + // test trait + $sTableClass = get_class($oTable); + if (!utils::IsTraitUsedByClass(tTableRowActions::class, $sTableClass)) { + throw new \Exception("DataTableUIBlockFactory::MakeActionRowToolbarTemplate: {$sTableClass} iUIBlock needs tTableRowActions trait"); + } + + // row actions template + $oTemplate = TemplateUIBlockFactory::MakeStandard($oTable->GetId().'_actions_buttons_template'); + + // row actions toolbar container + $oToolbar = ToolbarUIBlockFactory::MakeStandard(); + $oToolbar->AddCSSClass('ibo-datatable--row-actions-toolbar'); + + // for each action...create an icon button + foreach ($oTable->GetRowActions() as $iKey => $aAction) { + $oButton = ButtonUIBlockFactory::MakeIconAction( + array_key_exists('icon_classes', $aAction) ? $aAction['icon_classes'] : 'fas fa-question', + array_key_exists('tooltip', $aAction) ? $aAction['tooltip'] : '', + array_key_exists('name', $aAction) ? $aAction['name'] : 'undefined' + ); + $oButton->SetDataAttributes(['action-id' => $iKey]); + $oToolbar->AddSubBlock($oButton); + } + + $oTemplate->AddSubBlock($oToolbar); + + return $oTemplate; + } + /** * Make a basis Panel component * @@ -457,9 +500,8 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory } else { $aOptions['sSelectedRows'] = '[]'; } - $aExtraParams['table_id']=$sTableId; - $aExtraParams['list_id']=$sListId; - + $aExtraParams['table_id'] = $sTableId; + $aExtraParams['list_id'] = $sListId; $oDataTable->SetOptions($aOptions); $oDataTable->SetAjaxUrl(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php"); @@ -475,6 +517,12 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $oDataTable->SetResultColumns($oCustomSettings->aColumns); $oDataTable->SetInitDisplayData(AjaxRenderController::GetDataForTable($oSet, $aClassAliases, $aColumnsToLoad, $sIdName, $aExtraParams)); + // row actions + if (isset($aExtraParams['row_actions'])) { + $oDataTable->SetRowActions($aExtraParams['row_actions']); + } + + return $oDataTable; } @@ -713,6 +761,11 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $oDataTable->SetResultColumns($oCustomSettings->aColumns); $oDataTable->SetInitDisplayData(AjaxRenderController::GetDataForTable($oSet, $aClassAliases, $aColumnsToLoad, $sIdName, $aExtraParams)); + // row actions + if (isset($aExtraParams['row_actions'])) { + $oDataTable->SetRowActions($aExtraParams['row_actions']); + } + return $oDataTable; } @@ -908,6 +961,7 @@ JS; * @param array $aExtraParams * @param string $sFilter * @param array $aOptions + * @param array $aRowActions @since 3.1.0 * * * $aColumns =[ * 'nameField1' => ['label' => labelFIeld1, 'description' => descriptionField1], @@ -917,7 +971,7 @@ JS; * * @return \Combodo\iTop\Application\UI\Base\Layout\UIContentBlock */ - public static function MakeForStaticData(string $sTitle, array $aColumns, array $aData, ?string $sId = null, array $aExtraParams = [], string $sFilter = "", array $aOptions = []) + public static function MakeForStaticData(string $sTitle, array $aColumns, array $aData, ?string $sId = null, array $aExtraParams = [], string $sFilter = "", array $aOptions = [], array $aRowActions = null) { $oBlock = new UIContentBlock(); if ($sTitle != "") { @@ -925,6 +979,13 @@ JS; $oBlock->AddSubBlock($oTitle); } $oTable = new StaticTable($sId, [], $aExtraParams); + if ($aRowActions != null) { + $oTable->SetRowActions($aRowActions); + $aColumns['actions'] = [ + 'label' => Dict::S('UI:Datatables:Column:RowActions:Label'), + 'description' => Dict::S('UI:Datatables:Column:RowActions:Description'), + ]; + } $oTable->SetColumns($aColumns); $oTable->SetData($aData); $oTable->SetFilter($sFilter); @@ -940,6 +1001,7 @@ JS; * @param array $aColumns * @param array $aData * @param string $sFilter + * @param array $aRowActions @since 3.1.0 * * $aColumns =[ * 'nameField1' => ['label' => labelFIeld1, 'description' => descriptionField1], @@ -949,10 +1011,17 @@ JS; * * @return \Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTable\FormTable */ - public static function MakeForForm(string $sRef, array $aColumns, array $aData = [], string $sFilter = '') + public static function MakeForForm(string $sRef, array $aColumns, array $aData = [], string $sFilter = '', array $aRowActions = null) { $oTable = new FormTable("datatable_".$sRef); $oTable->SetRef($sRef); + if ($aRowActions != null) { + $oTable->SetRowActions($aRowActions); + $aColumns['actions'] = [ + 'label' => Dict::S('UI:Datatables:Column:RowActions:Label'), + 'description' => Dict::S('UI:Datatables:Column:RowActions:Description'), + ]; + } $oTable->SetColumns($aColumns); $oTable->SetFilter($sFilter); @@ -970,24 +1039,44 @@ JS; public static function GetAllowedParams(): array { return [ - 'surround_with_panel', /** bool embed table into a Panel */ - 'menu', /** bool display table menu */ - 'view_link', /** bool display the friendlyname column with links to the objects details */ - 'link_attr', /** string link att code */ - 'object_id', /** int Id of the object linked */ - 'target_attr', /** string target att code of the link */ - 'selection_mode', /** bool activate selection */ - 'selection_type', /** string 'multiple' or 'single' */ - 'extra_fields', /** string comma separated list of link att code to display ('alias.attcode')*/ - 'zlist', /** string name of the zlist to display when 'extra_fields' is not set */ - 'display_limit', /** bool if true pagination is used (default = true) */ - 'table_id', /** string datatable id */ - 'cssCount', /** string external counter (input hidden) js selector */ - 'selected_rows', /** array list of Ids already selected when displaying the datatable */ - 'display_aliases', /** string comma separated list of class aliases to display */ - 'list_id', /** string list outer id */ - 'selection_enabled', /** list of id in witch select is allowed, if not exists all lines are selectable */ - 'id_for_select', /**give definition of id for select checkbox*/ + 'surround_with_panel', + /** bool embed table into a Panel */ + 'menu', + /** bool display table menu */ + 'view_link', + /** bool display the friendlyname column with links to the objects details */ + 'link_attr', + /** string link att code */ + 'object_id', + /** int Id of the object linked */ + 'target_attr', + /** string target att code of the link */ + 'selection_mode', + /** bool activate selection */ + 'selection_type', + /** string 'multiple' or 'single' */ + 'extra_fields', + /** string comma separated list of link att code to display ('alias.attcode')*/ + 'zlist', + /** string name of the zlist to display when 'extra_fields' is not set */ + 'display_limit', + /** bool if true pagination is used (default = true) */ + 'table_id', + /** string datatable id */ + 'cssCount', + /** string external counter (input hidden) js selector */ + 'selected_rows', + /** array list of Ids already selected when displaying the datatable */ + 'display_aliases', + /** string comma separated list of class aliases to display */ + 'list_id', + /** string list outer id */ + 'selection_enabled', + /** list of id in witch select is allowed, if not exists all lines are selectable */ + 'id_for_select', + /**give definition of id for select checkbox*/ + 'row_actions', + /** array of blocks displayed on every row */ ]; } } \ No newline at end of file diff --git a/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php b/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php index 84d6e2552..079c2c352 100644 --- a/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php +++ b/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php @@ -9,6 +9,7 @@ namespace Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormT use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTableRow\FormTableRow; use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\StaticTable; +use Combodo\iTop\Application\UI\Base\Component\DataTable\tTableRowActions; use Combodo\iTop\Application\UI\Base\iUIBlock; /** @@ -19,10 +20,10 @@ use Combodo\iTop\Application\UI\Base\iUIBlock; class FormTable extends StaticTable { // Overloaded constants - public const BLOCK_CODE = 'ibo-formtable'; - public const REQUIRES_ANCESTORS_DEFAULT_JS_FILES = true; - public const REQUIRES_ANCESTORS_DEFAULT_CSS_FILES = true; - public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/datatable/static/formtable/layout'; + public const BLOCK_CODE = 'ibo-formtable'; + public const REQUIRES_ANCESTORS_DEFAULT_JS_FILES = true; + public const REQUIRES_ANCESTORS_DEFAULT_CSS_FILES = true; + public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/datatable/static/formtable/layout'; public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/datatable/static/formtable/layout'; /** @var string */ diff --git a/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php b/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php index 75eeb0907..8ccc845de 100644 --- a/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php +++ b/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php @@ -2,6 +2,7 @@ namespace Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable; +use Combodo\iTop\Application\UI\Base\Component\DataTable\tTableRowActions; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use Combodo\iTop\Application\UI\Base\tJSRefreshCallback; use utils; @@ -18,12 +19,13 @@ use utils; class StaticTable extends UIContentBlock { use tJSRefreshCallback; + use tTableRowActions; // Overloaded constants - public const BLOCK_CODE = 'ibo-datatable'; - public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/datatable/static/layout'; + public const BLOCK_CODE = 'ibo-datatable'; + public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/datatable/static/layout'; public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/datatable/static/layout'; - public const DEFAULT_JS_FILES_REL_PATH = [ + public const DEFAULT_JS_FILES_REL_PATH = [ 'node_modules/datatables.net/js/jquery.dataTables.min.js', 'node_modules/datatables.net-fixedheader/js/dataTables.fixedHeader.min.js', 'node_modules/datatables.net-responsive/js/dataTables.responsive.min.js', @@ -59,7 +61,7 @@ class StaticTable extends UIContentBlock private $aExtraParams; /*@var string $sUrlForRefresh*/ private $sFilter; - /** @var array $aOptions + /** @var array $aOptions * List of specific options for display datatable */ private $aOptions; @@ -81,6 +83,17 @@ class StaticTable extends UIContentBlock return $this->aColumns; } + /** + * Return columns count. + * + * @return int + * @since 3.1.0 + */ + public function GetColumnsCount(): int + { + return count($this->aColumns); + } + /** * @param array $aColumns * @@ -129,8 +142,8 @@ class StaticTable extends UIContentBlock { //$('#".$this->sId."').DataTable().clear().rows.add(data).draw() $aParams = [ - 'style' => 'list', - 'filter' => $this->sFilter, + 'style' => 'list', + 'filter' => $this->sFilter, 'extra_params' => $this->aExtraParams, ]; @@ -140,7 +153,7 @@ class StaticTable extends UIContentBlock $('#".$this->sId."').dataTable().fnAddData(data); });"; } - + /** * @return mixed */ @@ -149,6 +162,7 @@ class StaticTable extends UIContentBlock if (isset($this->aOptions[$sOption])) { return $this->aOptions[$sOption]; } + return null; } diff --git a/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php new file mode 100644 index 000000000..e69fb1a32 --- /dev/null +++ b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php @@ -0,0 +1,73 @@ + { + * tooltip: string, + * icon_classes: string, + * js_row_action: string + * } + */ + protected $aRowActions; + + /** + * Set row actions. + * + * @param array $aRowActions + * + * @return $this + */ + public function SetRowActions(array $aRowActions) + { + $this->aRowActions = $aRowActions; + + return $this; + } + + /** + * Get row actions. + * + * @return array + */ + public function GetRowActions(): array + { + return $this->aRowActions; + } + + /** + * Return true if row actions is set and not empty. + * + * @return bool + */ + public function HasRowActions(): bool + { + return isset($this->aRowActions) && count($this->aRowActions); + } + + /** + * Return row actions template. + * + * @return \Combodo\iTop\Application\UI\Base\Component\Template\Template + */ + public function GetRowActionsTemplate() + { + return DataTableUIBlockFactory::MakeActionRowToolbarTemplate($this); + } +} \ No newline at end of file diff --git a/sources/Application/UI/Base/Component/Template/Template.php b/sources/Application/UI/Base/Component/Template/Template.php new file mode 100644 index 000000000..234c1fa76 --- /dev/null +++ b/sources/Application/UI/Base/Component/Template/Template.php @@ -0,0 +1,37 @@ + + * @package Combodo\iTop\Application\UI\Base\Component\Template + * @since 3.1.0 + */ +class Template extends UIContentBlock +{ + // Overloaded constants + public const BLOCK_CODE = 'ibo-template'; + public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/template/layout'; + +} diff --git a/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php b/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php new file mode 100644 index 000000000..b62220552 --- /dev/null +++ b/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php @@ -0,0 +1,50 @@ + + * @package Combodo\iTop\Application\UI\Base\Component\Template + * @since 3.1.0 + * @link + */ +class TemplateUIBlockFactory extends AbstractUIBlockFactory +{ + /** @inheritDoc */ + public const TWIG_TAG_NAME = 'UITemplate'; + /** @inheritDoc */ + public const UI_BLOCK_CLASS_NAME = Template::class; + + /** + * Make a Template component + * + * @return \Combodo\iTop\Application\UI\Base\Component\Template\Template + */ + public static function MakeStandard(string $sId) + { + return new Template($sId); + } +} \ No newline at end of file diff --git a/templates/base/components/datatable/layout.html.twig b/templates/base/components/datatable/layout.html.twig index 9b4550fda..9ec38c93a 100644 --- a/templates/base/components/datatable/layout.html.twig +++ b/templates/base/components/datatable/layout.html.twig @@ -20,5 +20,12 @@ {% for aColumn in oUIBlock.GetDisplayColumns() %} {{ aColumn["attribute_label"] }} {% endfor %} + {% if oUIBlock.HasRowActions() %} + {{ 'UI:Datatables:Column:RowActions:Label'|dict_s }} + {% endif %} - \ No newline at end of file + + +{% if oUIBlock.HasRowActions() %} + {{ render_block(oUIBlock.GetRowActionsTemplate()) }} +{% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/layout.ready.js.twig b/templates/base/components/datatable/layout.ready.js.twig index eb4d421f6..24aedf67c 100644 --- a/templates/base/components/datatable/layout.ready.js.twig +++ b/templates/base/components/datatable/layout.ready.js.twig @@ -208,6 +208,9 @@ var oTable{{ sListIDForVarSuffix }} = $('#{{ oUIBlock.GetId() }}').DataTable({ } }, {% endfor %} + {% if oUIBlock.HasRowActions() %} + getRowActionsColumnDefinition('{{ oUIBlock.GetId() }}'), + {% endif %} ], ajax: $.fn.dataTable.pipeline({ url: "{{ oUIBlock.GetAjaxUrl() }}", @@ -415,4 +418,6 @@ if(window.ResizeObserver){ }, 120); }); oTable{{ sListIDForVarSuffix }}Resize.observe($('#{{ oUIBlock.GetId() }}')[0]); -} \ No newline at end of file +} + +{% include 'base/components/datatable/row-actions/handler.js.twig' %} \ No newline at end of file diff --git a/templates/base/components/datatable/row-actions/handler.js.twig b/templates/base/components/datatable/row-actions/handler.js.twig new file mode 100644 index 000000000..1bf8a0453 --- /dev/null +++ b/templates/base/components/datatable/row-actions/handler.js.twig @@ -0,0 +1,15 @@ +{# @copyright Copyright (C) 2010-2022 Combodo SARL #} +{# @license http://opensource.org/licenses/AGPL-3.0 #} + +// for each row action +{% if oUIBlock.HasRowActions() %} + {% for aAction in oUIBlock.GetRowActions() %} + $('#{{ oUIBlock.GetId() }} tbody').on('click', 'button[data-action-id="{{ loop.index0 }}"]', function() { + let iActionId = $(this).data('action-id'); + let oDatatable = $('#{{ oUIBlock.GetId() }}').DataTable(); + let oTrElement = $(this).closest('tr'); + let aData = oDatatable.row(oTrElement).data(); + {{ aAction.js_row_action|raw }}; + }); + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/static/formtable/layout.html.twig b/templates/base/components/datatable/static/formtable/layout.html.twig index 8b8eb3327..3da718895 100644 --- a/templates/base/components/datatable/static/formtable/layout.html.twig +++ b/templates/base/components/datatable/static/formtable/layout.html.twig @@ -13,8 +13,12 @@ - {% for oSubBlock in oUIBlock.GetRows() %} - {{ render_block(oSubBlock, {aPage: aPage}) }} + {% for oRowBlock in oUIBlock.GetRows() %} + {{ render_block(oRowBlock, {aPage: aPage}) }} {% endfor %} - \ No newline at end of file + + +{% if oUIBlock.HasRowActions() %} + {{ render_block(oUIBlock.GetRowActionsTemplate()) }} +{% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/static/formtable/layout.ready.js.twig b/templates/base/components/datatable/static/formtable/layout.ready.js.twig index 99d5c15bf..388f6c385 100644 --- a/templates/base/components/datatable/static/formtable/layout.ready.js.twig +++ b/templates/base/components/datatable/static/formtable/layout.ready.js.twig @@ -20,7 +20,10 @@ var oTable{{ sListIDForVarSuffix }} = $('#{{ oUIBlock.GetId() }}').DataTable({ }, {% endif %} columnDefs: [ - {orderable: false, targets: 0} + {orderable: false, targets: 0}, + {% if oUIBlock.HasRowActions() %} + getRowActionsColumnDefinition('{{ oUIBlock.GetId() }}', {{ oUIBlock.GetColumnsCount() - 1 }}), + {% endif %} ], {% endif %} drawCallback: function (settings) { @@ -123,4 +126,6 @@ if (window.ResizeObserver) {% endif %} -} \ No newline at end of file +} + +{% include 'base/components/datatable/row-actions/handler.js.twig' %} \ No newline at end of file diff --git a/templates/base/components/datatable/static/layout.html.twig b/templates/base/components/datatable/static/layout.html.twig index c06a7b6b5..a7b9748b8 100644 --- a/templates/base/components/datatable/static/layout.html.twig +++ b/templates/base/components/datatable/static/layout.html.twig @@ -44,4 +44,8 @@ {% endfor %} - \ No newline at end of file + + +{% if oUIBlock.HasRowActions() %} + {{ render_block(oUIBlock.GetRowActionsTemplate()) }} +{% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/static/layout.ready.js.twig b/templates/base/components/datatable/static/layout.ready.js.twig index dbe75ed5e..d3fa65444 100644 --- a/templates/base/components/datatable/static/layout.ready.js.twig +++ b/templates/base/components/datatable/static/layout.ready.js.twig @@ -54,6 +54,9 @@ var oTable{{ sListIDForVarSuffix }} = $('#{{ oUIBlock.GetId() }}').DataTable({ sortable: true }, {% endfor %} + {% if oUIBlock.HasRowActions() %} + getRowActionsColumnDefinition('{{ oUIBlock.GetId() }}'), + {% endif %} ], drawCallback: function (settings) { if(settings.json) @@ -103,4 +106,6 @@ if (window.ResizeObserver) }, 120); }); oStaticTable{{ sListIDForVarSuffix }}Resize.observe($('#{{ oUIBlock.GetId() }}')[0]); -} \ No newline at end of file +} + +{% include 'base/components/datatable/row-actions/handler.js.twig' %} \ No newline at end of file diff --git a/templates/base/components/template/layout.html.twig b/templates/base/components/template/layout.html.twig new file mode 100644 index 000000000..8671ff5a3 --- /dev/null +++ b/templates/base/components/template/layout.html.twig @@ -0,0 +1,9 @@ +{# @copyright Copyright (C) 2010-2022 Combodo SARL #} +{# @license http://opensource.org/licenses/AGPL-3.0 #} +{% apply spaceless %} + +{% endapply %} \ No newline at end of file