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);
+ }
+}