diff --git a/application/dashboard.class.inc.php b/application/dashboard.class.inc.php index 15dafe4bf..72bc4e052 100644 --- a/application/dashboard.class.inc.php +++ b/application/dashboard.class.inc.php @@ -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('
'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'
'); - $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 : + * -|Dashlet + * Since 2.7.0 it is the following, with a "CUSTOM_" prefix if necessary : + * * dashboard menu : _IDrow-col- + * * dashboard attribute : ___IDrow-col- + * + * @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); + } } diff --git a/application/dashboardlayout.class.inc.php b/application/dashboardlayout.class.inc.php index d5ca0de3a..02bae7eef 100644 --- a/application/dashboardlayout.class.inc.php +++ b/application/dashboardlayout.class.inc.php @@ -27,6 +27,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() { @@ -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(""); + $oPage->add(""); 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(""); for($iCols = 0; $iCols < $this->iNbCols; $iCols++) { - $oPage->add(""); + $oPage->add(""); $oPage->add(' '); $oPage->add(''); } @@ -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 : - * -|Dashlet - * Since 2.7.0 it is the following, with a "CUSTOM_" prefix if necessary : - * * dashboard menu : _IDrow-col- - * * dashboard attribute : ___IDrow-col- - * - * @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); } } diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php index a54c8beb8..fd272446e 100644 --- a/application/dashlet.class.inc.php +++ b/application/dashlet.class.inc.php @@ -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)); diff --git a/js/dashboard.js b/js/dashboard.js index 63b88d0e0..7750ae98d 100644 --- a/js/dashboard.js +++ b/js/dashboard.js @@ -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) { diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 403d9fb74..2b96e82cb 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -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; diff --git a/test/application/DashboardLayoutTest.php b/test/application/DashboardLayoutTest.php new file mode 100644 index 000000000..0d6eb2397 --- /dev/null +++ b/test/application/DashboardLayoutTest.php @@ -0,0 +1,72 @@ + 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); + } +}