N°2735 - Continue rework of the dashlet id generation:

- Move generation from DashboardLayout to Dashboard
- Migrate dashlet user preference in RuntimeDashboard only (and not in DesignTimeDashboard)
This commit is contained in:
Molkobain
2020-02-26 16:29:32 +01:00
parent 401f82062a
commit e2a3e0e74f
6 changed files with 250 additions and 110 deletions

View File

@@ -46,6 +46,8 @@ abstract class Dashboard
protected $aCells;
/** @var \ModelReflection $oMetaModel */
protected $oMetaModel;
/** @var bool $bCustomized */
protected $bCustomized;
/**
* Dashboard constructor.
@@ -61,6 +63,7 @@ abstract class Dashboard
$this->aCells = array();
$this->oDOMNode = null;
$this->sId = $sId;
$this->bCustomized = false;
}
/**
@@ -399,6 +402,24 @@ abstract class Dashboard
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
/**
* @return bool
* @since 2.7.0
*/
public function GetCustomFlag()
{
return $this->bCustomized;
}
/**
* @param bool $bCustomized
* @since 2.7.0
*/
public function SetCustomFlag($bCustomized)
{
$this->bCustomized = $bCustomized;
}
/**
* @param \Dashlet $oDashlet
*/
@@ -517,8 +538,18 @@ EOF
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
$oLayout = new $this->sLayoutClass;
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass;
foreach($this->aCells as $iCellIdx => $aDashlets)
{
foreach($aDashlets as $oDashlet)
{
$aDashletCoordinates = $oLayout->GetDashletCoordinates($iCellIdx);
$this->PrepareDashletForRendering($oDashlet, $aDashletCoordinates, $aExtraParams);
}
}
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
@@ -632,6 +663,22 @@ EOF
return $iNewId + 1;
}
/**
* Prepare dashlet for rendering:
* - Fix ID to unique within the dashboard
*
* @param \Dashlet $oDashlet
* @param array $aCoordinates Contains x, y (starting from 0)
* @param array $aExtraParams
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardSanitizedId = utils::Sanitize($this->GetId(), '', 'element_identifier');
$sDashletIdNew = static::GetDashletUniqueId($this->GetCustomFlag(), $sDashboardSanitizedId, $aCoordinates[1], $aCoordinates[0], $sDashletIdOrig);
$oDashlet->SetID($sDashletIdNew);
}
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
@@ -655,6 +702,37 @@ EOF
return 'DashletUnknown';
}
/**
* N°2634: we must have a unique id per dashlet!
* To avoid collision with other dashlets with the same ID we prefix it with row/cell id
* Collisions typically happen with extensions.
*
* @param boolean $bIsCustomized
* @param string $sDashboardDivId
* @param int $iRow
* @param int $iCol
* @param string $sDashletIdOrig
*
* @return string
*
* @since 2.7.0 N°2735
*/
public static function GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRow, $iCol, $sDashletIdOrig)
{
if(strpos($sDashletIdOrig, 'IDrow') !== false)
{
return $sDashletIdOrig;
}
$sDashletId = $sDashboardDivId."_IDrow$iRow-col$iCol-$sDashletIdOrig";
if ($bIsCustomized)
{
$sDashletId = 'CUSTOM_'.$sDashletId;
}
return $sDashletId;
}
/**
* @return mixed
*/
@@ -669,8 +747,6 @@ EOF
*/
class RuntimeDashboard extends Dashboard
{
/** @var bool $bCustomized */
protected $bCustomized;
/** @var string $sDefinitionFile */
private $sDefinitionFile = '';
/** @var null $sReloadURL */
@@ -682,18 +758,9 @@ class RuntimeDashboard extends Dashboard
public function __construct($sId)
{
parent::__construct($sId);
$this->bCustomized = false;
$this->oMetaModel = new ModelReflectionRuntime();
}
/**
* @param bool $bCustomized
*/
public function SetCustomFlag($bCustomized)
{
$this->bCustomized = $bCustomized;
}
/**
* @inheritDoc
* @throws \Exception
@@ -830,8 +897,6 @@ class RuntimeDashboard extends Dashboard
$aRenderParams = $aExtraParams;
}
$aRenderParams['bCustomized'] = $this->bCustomized;
parent::Render($oPage, $bEditMode, $aRenderParams);
if (isset($aExtraParams['query_params']['this->object()']))
@@ -1427,4 +1492,81 @@ JS
{
$this->sReloadURL = $sReloadURL;
}
/**
* @inheritDoc
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
{
$sDashletIdOrig = $oDashlet->GetID();
parent::PrepareDashletForRendering($oDashlet, $aCoordinates);
$this->UpdateDashletUserPrefs($oDashlet, $sDashletIdOrig, $aExtraParams);
}
/**
* Migrate dashlet specific prefs to new format
* Before 2.7.0 we were using the same for dashboard menu or dashboard attributes, standard or custom :
* <alias>-<class>|Dashlet<idx_dashlet>
* Since 2.7.0 it is the following, with a "CUSTOM_" prefix if necessary :
* * dashboard menu : <dashboard_id>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
* * dashboard attribute : <class>__<attcode>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
*
* @param \Dashlet $oDashlet
* @param string $sDashletIdOrig
*
* @param array $aExtraParams
*
* @since 2.7.0 N°2735
*/
private function UpdateDashletUserPrefs(Dashlet $oDashlet, $sDashletIdOrig, array $aExtraParams)
{
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
if (!$bIsDashletWithListPref)
{
return;
}
/** @var \DashletObjectList $oDashlet */
$bDashletIdInNewFormat = ($sDashletIdOrig === $oDashlet->GetID());
if ($bDashletIdInNewFormat)
{
return;
}
$sNewPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $oDashlet->GetID());
$sPrefValueForNewKey = appUserPreferences::GetPref($sNewPrefKey, null);
$bHasPrefInNewFormat = ($sPrefValueForNewKey !== null);
if ($bHasPrefInNewFormat)
{
return;
}
$sOldPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $sDashletIdOrig);
$sPrefValueForOldKey = appUserPreferences::GetPref($sOldPrefKey, null);
$bHasPrefInOldFormat = ($sPrefValueForOldKey !== null);
if (!$bHasPrefInOldFormat)
{
return;
}
appUserPreferences::SetPref($sNewPrefKey, $sPrefValueForOldKey);
appUserPreferences::UnsetPref($sOldPrefKey);
}
/**
* @param \DashletObjectList $oDashlet
* @param array $aExtraParams
* @param string $sDashletId
*
* @return string
* @since 2.7.0
*/
private function GetDashletObjectListAppUserPreferencesPrefix(DashletObjectList $oDashlet, $aExtraParams, $sDashletId)
{
$sDataTableId = Dashlet::APPUSERPREFERENCES_PREFIX.$sDashletId;
$oFilter = $oDashlet->GetDBSearch($aExtraParams);
$aClassAliases = $oFilter->GetSelectedClasses();
return DataTableSettings::GetAppUserPreferenceKey($aClassAliases, $sDataTableId);
}
}

View File

@@ -28,6 +28,14 @@ abstract class DashboardLayout
{
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
/**
* @param int $iCellIdx
*
* @return array Containing 2 scalars: Col number and row number (starting from 0)
* @since 2.7.0
*/
abstract public function GetDashletCoordinates($iCellIdx);
public static function GetInfo()
{
return array(
@@ -47,37 +55,6 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$this->iNbCols = 1;
}
/**
* N°2634 : we must have a unique id per dashlet !
* To avoid collision with other dashlets with the same ID we prefix it with row/cell id
* Collisions typically happen with extensions.
*
* @param boolean $bIsCustomized
* @param string $sDashboardDivId
* @param int $iRow
* @param int $iCell
* @param string $sDashletIdOrig
*
* @return string
*
* @since 2.7.0 N°2735
*/
public static function GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRow, $iCell, $sDashletIdOrig)
{
if(strpos($sDashletIdOrig, 'IDrow') !== false)
{
return $sDashletIdOrig;
}
$sDashletId = $sDashboardDivId."_IDrow$iRow-col$iCell-$sDashletIdOrig";
if ($bIsCustomized)
{
$sDashletId = 'CUSTOM_'.$sDashletId;
}
return $sDashletId;
}
protected function TrimCell($aDashlets)
{
$aKeys = array_reverse(array_keys($aDashlets));
@@ -150,7 +127,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-cell-index=\"$iCellIdx\">");
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-column-index=\"$iCols\" data-dashboard-cell-index=\"$iCellIdx\">");
if (array_key_exists($iCellIdx, $aCells))
{
$aDashlets = $aCells[$iCellIdx];
@@ -161,12 +138,6 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
{
if ($oDashlet::IsVisible())
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardDivId = $aExtraParams['dashboard_div_id'];
$bIsCustomized = (array_key_exists('bCustomized', $aExtraParams) && ((bool)$aExtraParams['bCustomized']) === true);
$sDashletId = self::GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRows, $iCols, $sDashletIdOrig);
$oDashlet->SetID($sDashletId);
$this->UpdateDashletsUserPrefs($oDashlet, $sDashletIdOrig, $aExtraParams);
$oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams);
}
}
@@ -191,7 +162,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$oPage->add("<tr data-dashboard-row-index=\"$iRows\">");
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$oPage->add("<td $sStyle>");
$oPage->add("<td $sStyle data-dashboard-column-index=\"$iCols\">");
$oPage->add('&nbsp;');
$oPage->add('</td>');
}
@@ -201,61 +172,14 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
}
/**
* Migrate dashlet specific prefs to new format
* Before 2.7.0 we were using the same for dashboard menu or dashboard attributes, standard or custom :
* <alias>-<class>|Dashlet<idx_dashlet>
* Since 2.7.0 it is the following, with a "CUSTOM_" prefix if necessary :
* * dashboard menu : <dashboard_id>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
* * dashboard attribute : <class>__<attcode>_IDrow<row_idx>-col<col_idx>-<dashlet_idx>
*
* @param \Dashlet $oDashlet
* @param string $sDashletIdOrig
*
* @param array $aExtraParams
*
* @since 2.7.0 N°2735
* @inheritDoc
*/
private function UpdateDashletsUserPrefs(\Dashlet $oDashlet, $sDashletIdOrig, array $aExtraParams)
public function GetDashletCoordinates($iCellIdx)
{
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
if (!$bIsDashletWithListPref)
{
return;
}
/** @var \DashletObjectList $oDashlet */
$iColNumber = (int) $iCellIdx % $this->iNbCols;
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
$bDashletIdInNewFormat = ($sDashletIdOrig === $oDashlet->GetID());
if ($bDashletIdInNewFormat)
{
return;
}
$sNewPrefKey = $this->GetDashletAppUserPrefPrefix($oDashlet, $aExtraParams, $oDashlet->GetID());
$sPrefValueForNewKey = appUserPreferences::GetPref($sNewPrefKey, null);
$bHasPrefInNewFormat = ($sPrefValueForNewKey !== null);
if ($bHasPrefInNewFormat)
{
return;
}
$sOldPrefKey = $this->GetDashletAppUserPrefPrefix($oDashlet, $aExtraParams, $sDashletIdOrig);
$sPrefValueForOldKey = appUserPreferences::GetPref($sOldPrefKey, null);
$bHasPrefInOldFormat = ($sPrefValueForOldKey !== null);
if (!$bHasPrefInOldFormat)
{
return;
}
appUserPreferences::SetPref($sNewPrefKey, $sPrefValueForOldKey);
appUserPreferences::UnsetPref($sOldPrefKey);
}
private function GetDashletAppUserPrefPrefix(\DashletObjectList $oDashlet, array $aExtraParams, $sDashletId)
{
$sDataTableId = DashletObjectList::APPUSERPREFERENCE_TABLE_PREFIX.$sDashletId;
$oFilter = $oDashlet->GetDBSearch($aExtraParams);
$aClassAliases = $oFilter->GetSelectedClasses();
return DataTableSettings::GetAppUserPreferenceKey($aClassAliases, $sDataTableId);
return array($iColNumber, $iRowNumber);
}
}

View File

@@ -26,6 +26,9 @@ require_once(APPROOT.'application/forms.class.inc.php');
*/
abstract class Dashlet
{
/** @var string */
const APPUSERPREFERENCES_PREFIX = 'Dashlet';
protected $oModelReflection;
protected $sId;
protected $bRedrawNeeded;
@@ -892,7 +895,6 @@ class DashletPlainText extends Dashlet
class DashletObjectList extends Dashlet
{
const APPUSERPREFERENCE_TABLE_PREFIX = 'Dashlet';
/**
* @inheritdoc
*/
@@ -926,7 +928,7 @@ class DashletObjectList extends Dashlet
$oBlock = new DisplayBlock($oFilter, 'list');
$aParams = array(
'menu' => $sShowMenu,
'table_id' => self::APPUSERPREFERENCE_TABLE_PREFIX.$this->sId,
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
);
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
$oBlock->Display($oPage, $sBlockId, array_merge($aExtraParams, $aParams));

View File

@@ -323,7 +323,7 @@ $(function()
var oParams = this.options.new_dashletid_parameters;
oParams.dashboardid = me.options.dashboard_id;
oParams.iRow = $container.closest("tr").data("dashboard-row-index");
oParams.iCell = $container.data("dashboard-cell-index");
oParams.iCol = $container.data("dashboard-column-index");
oParams.dashletid = sTempDashletId;
$.post(this.options.render_to, oParams, function(data) {

View File

@@ -1218,9 +1218,9 @@ EOF
case 'new_dashlet_id':
$sDashboardDivId = utils::ReadParam("dashboardid");
$iRow = utils::ReadParam("iRow");
$iCell = utils::ReadParam("iCell");
$iCol = utils::ReadParam("iCol");
$sDashletIdOrig = utils::ReadParam("dashletid");
$sFinalDashletId = DashboardLayoutMultiCol::GetDashletUniqueId(true, $sDashboardDivId, $iRow, $iCell, $sDashletIdOrig);
$sFinalDashletId = Dashboard::GetDashletUniqueId(true, $sDashboardDivId, $iRow, $iCol, $sDashletIdOrig);
$oPage = new ajax_page('');
$oPage->add($sFinalDashletId);
break;

View File

@@ -0,0 +1,72 @@
<?php
/**
* Copyright (C) 2013-2020 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
*/
use Combodo\iTop\Test\UnitTest\ItopTestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
* @covers utils
*/
class DashboardLayoutTest extends ItopTestCase
{
public function setUp()
{
parent::setUp();
require_once APPROOT.'application/dashboardlayout.class.inc.php';
}
/**
* @return array
*/
public function GetDashletCoordinatesProvider()
{
return array(
'OneColLayout-Cell0' => array('DashboardLayoutOneCol', 0, array(0, 0)),
'OneColLayout-Cell1' => array('DashboardLayoutOneCol', 1, array(0, 1)),
'TwoColsLayout-Cell0' => array('DashboardLayoutTwoCols', 0, array(0, 0)),
'TwoColsLayout-Cell1' => array('DashboardLayoutTwoCols', 1, array(1, 0)),
'TwoColsLayout-Cell2' => array('DashboardLayoutTwoCols', 2, array(0, 1)),
'TwoColsLayout-Cell3' => array('DashboardLayoutTwoCols', 3, array(1, 1)),
'ThreeColsLayout-Cell0' => array('DashboardLayoutThreeCols', 0, array(0, 0)),
'ThreeColsLayout-Cell1' => array('DashboardLayoutThreeCols', 1, array(1, 0)),
'ThreeColsLayout-Cell2' => array('DashboardLayoutThreeCols', 2, array(2, 0)),
'ThreeColsLayout-Cell3' => array('DashboardLayoutThreeCols', 3, array(0, 1)),
'ThreeColsLayout-Cell4' => array('DashboardLayoutThreeCols', 4, array(1, 1)),
'ThreeColsLayout-Cell5' => array('DashboardLayoutThreeCols', 5, array(2, 1)),
);
}
/**
* @param string $sDashboardLayoutClass
* @param int $iCellIdx
* @param array $aExpectedCoordinates
* @dataProvider GetDashletCoordinatesProvider
* @since N°2735
*/
public function testGetDashletCoordinates($sDashboardLayoutClass, $iCellIdx, $aExpectedCoordinates)
{
$oDashboardLayout = new $sDashboardLayoutClass();
$aDashletCoordinates = $oDashboardLayout->GetDashletCoordinates($iCellIdx);
$this->assertEquals($aExpectedCoordinates,$aDashletCoordinates);
}
}