diff --git a/application/dashboardlayout.class.inc.php b/application/dashboardlayout.class.inc.php
index 46da64dfe..6084d3293 100644
--- a/application/dashboardlayout.class.inc.php
+++ b/application/dashboardlayout.class.inc.php
@@ -15,15 +15,17 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see
+use Combodo\iTop\Application\UI\Component\Html\Html;
+use Combodo\iTop\Application\UI\Layout\Dashboard\DashboardColumn;
+use Combodo\iTop\Application\UI\Layout\Dashboard\DashboardLayout as UIDashboardLayout;
+use Combodo\iTop\Application\UI\Layout\Dashboard\DashboardRow;
/**
* Dashboard presentation
- *
+ *
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
- */
-
-
+ */
abstract class DashboardLayout
{
abstract public function Render($oPage, $aDashlets, $bEditMode = false);
@@ -114,72 +116,52 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
- $oPage->add('
');
+ $oDashboardLayout = new UIDashboardLayout();
+ $oPage->AddUiBlock($oDashboardLayout);
+
$iCellIdx = 0;
- $fColSize = ceil(100 / $this->iNbCols); // Note: ceil() is necessary otherwise the table will be too short since the new flex layout (N°2847)
$iNbRows = ceil(count($aCells) / $this->iNbCols);
- $aStyleProperties = [];
- // - Explicit full width when single column
- if($this->iNbCols > 1)
- {
- $aStyleProperties[] = 'width: '.$fColSize.'%;';
- }
- // - Visible borders in editor
- if($bEditMode)
- {
- $aStyleProperties[] = 'border: 1px #ccc dashed;';
+ for ($iRows = 0; $iRows < $iNbRows; $iRows++) {
+ $oDashboardRow = new DashboardRow();
+ $oDashboardLayout->AddDashboardRow($oDashboardRow);
- }
- $sClass = $bEditMode ? 'layout_cell edit_mode' : 'dashboard';
+ for ($iCols = 0; $iCols < $this->iNbCols; $iCols++) {
+ $oDashboardColumn = new DashboardColumn($bEditMode);
+ $oDashboardColumn->SetCellIndex($iCellIdx);
+ $oDashboardRow->AddDashboardColumn($oDashboardColumn);
- for($iRows = 0; $iRows < $iNbRows; $iRows++)
- {
- $oPage->add("");
- for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
- {
- $sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
- $oPage->add("| ");
- if (array_key_exists($iCellIdx, $aCells))
- {
+ if (array_key_exists($iCellIdx, $aCells)) {
$aDashlets = $aCells[$iCellIdx];
- if (count($aDashlets) > 0)
- {
+ if (count($aDashlets) > 0) {
/** @var \Dashlet $oDashlet */
- foreach($aDashlets as $oDashlet)
- {
- if ($oDashlet::IsVisible())
- {
- $oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams);
+ foreach ($aDashlets as $oDashlet) {
+ if ($oDashlet::IsVisible()) {
+ $oDashboardColumn->AddUIBlock($oDashlet->DoRender($oPage, $bEditMode, true /* bEnclosingDiv */, $aExtraParams));
}
}
+ } else {
+ $oDashboardColumn->AddUIBlock(new Html(' '));
}
- else
- {
- $oPage->add(' ');
- }
+ } else {
+ $oDashboardColumn->AddUIBlock(new Html(' '));
}
- else
- {
- $oPage->add(' ');
- }
- $oPage->add(' | ');
$iCellIdx++;
}
- $oPage->add('
');
}
+
if ($bEditMode) // Add one row for extensibility
{
- $oPage->add("");
- for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
- {
- $oPage->add("| ");
- $oPage->add(' ');
- $oPage->add(' | ');
+ $oDashboardRow = new DashboardRow();
+ $oDashboardLayout->AddDashboardRow($oDashboardRow);
+
+ for ($iCols = 0; $iCols < $this->iNbCols; $iCols++) {
+ $oDashboardColumn = new DashboardColumn($bEditMode, true);
+ $oDashboardRow->AddDashboardColumn($oDashboardColumn);
+ $oDashboardColumn->AddUIBlock(new Html(' '));
}
- $oPage->add('
');
}
- $oPage->add('
');
+ return;
}
/**
diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php
index 57e9a5689..e9964b35e 100644
--- a/application/dashlet.class.inc.php
+++ b/application/dashlet.class.inc.php
@@ -16,8 +16,11 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see
+use Combodo\iTop\Application\UI\Component\Dashlet\DashletContainer;
use Combodo\iTop\Application\UI\Component\Dashlet\DashletFactory;
+use Combodo\iTop\Application\UI\Component\Html\Html;
use Combodo\iTop\Application\UI\Component\Panel\PanelFactory;
+use Combodo\iTop\Application\UI\iUIBlock;
require_once(APPROOT.'application/forms.class.inc.php');
@@ -69,21 +72,14 @@ abstract class Dashlet
{
$refValue = $this->aProperties[$sProperty];
$sRefType = gettype($refValue);
- if (gettype($sValue) == $sRefType)
- {
+ if (gettype($sValue) == $sRefType) {
// Do not change anything in that case!
$ret = $sValue;
- }
- elseif ($sRefType == 'boolean')
- {
+ } elseif ($sRefType == 'boolean') {
$ret = ($sValue == 'true');
- }
- elseif ($sRefType == 'array')
- {
+ } elseif (($sRefType == 'array') || (is_array($sValue))) {
$ret = explode(',', $sValue);
- }
- else
- {
+ } else {
$ret = $sValue;
settype($ret, $sRefType);
}
@@ -212,77 +208,57 @@ abstract class Dashlet
*/
public function DoRender($oPage, $bEditMode = false, $bEnclosingDiv = true, $aExtraParams = array())
{
- $sCSSClasses = implode(' ', $this->aCSSClasses);
$sId = $this->GetID();
- if ($bEnclosingDiv)
- {
- if ($bEditMode)
- {
- $oPage->add('');
+
+ $sCSSClasses = implode(' ', $this->aCSSClasses);
+ if ($bEnclosingDiv) {
+ if ($bEditMode) {
+ $oDashletContainer = new DashletContainer("dashlet_{$sId}");
+ } else {
+ $oDashletContainer = new DashletContainer();
}
- else
- {
- $oPage->add('
');
- }
- }
- else
- {
- foreach ($this->aCSSClasses as $sCSSClass)
- {
+ $oDashletContainer->AddCSSClasses($sCSSClasses);
+ } else {
+ $oDashletContainer = new DashletContainer();
+
+ foreach ($this->aCSSClasses as $sCSSClass) {
$oPage->add_ready_script("$('#dashlet_".$sId."').addClass('$sCSSClass');");
}
}
- try
- {
- if (get_class($this->oModelReflection) == 'ModelReflectionRuntime')
- {
- $this->Render($oPage, $bEditMode, $aExtraParams);
+ try {
+ if (get_class($this->oModelReflection) == 'ModelReflectionRuntime') {
+ $oBlock = $this->Render($oPage, $bEditMode, $aExtraParams);
+ } else {
+ $oBlock = $this->RenderNoData($oPage, $bEditMode, $aExtraParams);
}
- else
- {
- $this->RenderNoData($oPage, $bEditMode, $aExtraParams);
- }
- }
- catch(UnknownClassOqlException $e)
- {
+ $oDashletContainer->AddSubBlock($oBlock);
+ } catch (UnknownClassOqlException $e) {
// Maybe the class is part of a non-installed module, fail silently
// Except in Edit mode
- if ($bEditMode)
- {
- $oPage->add('
');
- $oPage->add('
'.$e->GetUserFriendlyDescription().'
');
- $oPage->add('');
+ if ($bEditMode) {
+ $oDashletContainer->AddCSSClasses("dashlet-content");
+ $oDashletContainer->AddHtml('
'.$e->GetUserFriendlyDescription().'
');
}
- }
- catch(OqlException $e)
- {
- $oPage->add('
');
- $oPage->p($e->GetUserFriendlyDescription());
- $oPage->add('
');
- }
- catch(Exception $e)
- {
- $oPage->add('
');
- $oPage->p($e->getMessage());
- $oPage->add('
');
+ } catch (OqlException $e) {
+ $oDashletContainer->AddCSSClasses("dashlet-content");
+ $oDashletContainer->AddHtml('
'.$e->GetUserFriendlyDescription().'
');
+ } catch (Exception $e) {
+ $oDashletContainer->AddCSSClasses("dashlet-content");
+ $oDashletContainer->AddHtml('
'.$e->getMessage().'
');
}
- if ($bEnclosingDiv)
- {
- $oPage->add('
');
- }
-
- if ($bEditMode)
- {
+ if ($bEditMode) {
$sClass = get_class($this);
$sType = $this->sDashletType;
$oPage->add_ready_script(
-<<
Render($oPage, $bEditMode, $aExtraParams);
+ return $this->Render($oPage, $bEditMode, $aExtraParams);
}
/**
@@ -622,12 +600,12 @@ class DashletUnknown extends Dashlet
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
- $oPage->add('');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
- $oPage->add('
');
- $oPage->add('
'.$sExplainText.'
');
+ $oDashletContainer->AddHtml('
');
+ $oDashletContainer->AddHtml('
'.$sExplainText.'
');
- $oPage->add('
');
+ return $oDashletContainer;
}
/**
@@ -642,12 +620,12 @@ class DashletUnknown extends Dashlet
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
- $oPage->add('');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
- $oPage->add('
');
- $oPage->add('
'.$sExplainText.'
');
+ $oDashletContainer->AddHtml('
');
+ $oDashletContainer->AddHtml('
'.$sExplainText.'
');
- $oPage->add('
');
+ return $oDashletContainer;
}
/**
@@ -766,9 +744,9 @@ class DashletProxy extends DashletUnknown
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
// This should never be called.
- $oPage->add('');
- $oPage->add('
This dashlet is not supposed to be rendered as it is just a proxy for third-party widgets.
');
- $oPage->add('
');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
+ $oDashletContainer->AddHtml('This dashlet is not supposed to be rendered as it is just a proxy for third-party widgets.
');
+ return $oDashletContainer;
}
/**
@@ -783,12 +761,14 @@ class DashletProxy extends DashletUnknown
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
- $oPage->add('');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
- $oPage->add('
');
- $oPage->add('
'.$sExplainText.'
');
+ $sHtml = '';
+ $sHtml .= '
';
+ $sHtml .= '
'.$sExplainText.'
';
- $oPage->add('
');
+ $oDashletContainer->AddHtml($sHtml);
+ return $oDashletContainer;
}
/**
@@ -819,7 +799,7 @@ class DashletEmptyCell extends Dashlet
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
- $oPage->add(' ');
+ return new Html(' ');
}
/**
@@ -871,8 +851,7 @@ class DashletPlainText extends Dashlet
$sId = 'plaintext_'.($bEditMode ? 'edit_' : '').$this->sId;
- $oBlock = DashletFactory::MakeForDashletText($sId, $sText);
- $oPage->AddUiBlock($oBlock);
+ return DashletFactory::MakeForDashletText($sId, $sText);
}
/**
@@ -924,7 +903,6 @@ class DashletObjectList extends Dashlet
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
$oPanel = PanelFactory::MakeNeutral(Dict::S($sTitle));
- $oPage->AddUiBlock($oPanel);
$oFilter = $this->GetDBSearch($aExtraParams);
$oBlock = new DisplayBlock($oFilter, 'list');
@@ -934,26 +912,8 @@ class DashletObjectList extends Dashlet
);
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
- }
- public function GetDBSearch($aExtraParams = array())
- {
- $sQuery = $this->aProperties['query'];
- if (isset($aExtraParams['query_params']))
- {
- $aQueryParams = $aExtraParams['query_params'];
- }
- elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id']))
- {
- $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
- $aQueryParams = $oObj->ToArgsForQuery();
- }
- else
- {
- $aQueryParams = array();
- }
-
- return DBObjectSearch::FromOQL($sQuery, $aQueryParams);
+ return $oPanel;
}
/**
@@ -967,22 +927,35 @@ class DashletObjectList extends Dashlet
$oPage->add('');
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle)); // done in the itop block
- if ($sHtmlTitle != '')
- {
+ if ($sHtmlTitle != '') {
$oPage->add('
'.$sHtmlTitle.'
');
}
$oQuery = $this->oModelReflection->GetQuery($sQuery);
$sClass = $oQuery->GetClass();
$oPage->add('
');
$oPage->add('
');
}
+ public function GetDBSearch($aExtraParams = array())
+ {
+ $sQuery = $this->aProperties['query'];
+ if (isset($aExtraParams['query_params'])) {
+ $aQueryParams = $aExtraParams['query_params'];
+ } elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) {
+ $oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
+ $aQueryParams = $oObj->ToArgsForQuery();
+ } else {
+ $aQueryParams = array();
+ }
+
+ return DBObjectSearch::FromOQL($sQuery, $aQueryParams);
+ }
+
/**
* @inheritdoc
*/
@@ -1200,96 +1173,81 @@ abstract class DashletGroupBy extends Dashlet
$sStyle = $this->aProperties['style'];
// First perform the query - if the OQL is not ok, it will generate an exception : no need to go further
- if (isset($aExtraParams['query_params']))
- {
+ if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
- }
- elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id']))
- {
+ } elseif (isset($aExtraParams['this->class']) && isset($aExtraParams['this->id'])) {
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aQueryParams = $oObj->ToArgsForQuery();
- }
- else
- {
+ } else {
$aQueryParams = array();
}
$oFilter = DBObjectSearch::FromOQL($sQuery, $aQueryParams);
$oFilter->SetShowObsoleteData(utils::ShowObsoleteData());
$sClass = $oFilter->GetClass();
- if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode))
- {
- $oPage->add(''.Dict::S('UI:DashletGroupBy:MissingGroupBy').'
');
+ if (!$this->oModelReflection->IsValidAttCode($sClass, $this->sGroupByAttCode)) {
+ return new Html(''.Dict::S('UI:DashletGroupBy:MissingGroupBy').'
');
}
- else
- {
- switch($sStyle)
- {
- case 'bars':
- $sType = 'chart';
- $aParams = array(
- 'chart_type' => 'bars',
- 'chart_title' => $sTitle,
- 'group_by' => $this->sGroupByExpr,
- 'group_by_label' => $this->sGroupByLabel,
- 'aggregation_function' => $this->sAggregationFunction,
- 'aggregation_attribute' => $this->sAggregationAttribute,
- 'limit' => $this->sLimit,
- 'order_direction' => $this->sOrderDirection,
- 'order_by' => $this->sOrderBy,
- );
- $sHtmlTitle = ''; // done in the itop block
- break;
- case 'pie':
- $sType = 'chart';
- $aParams = array(
- 'chart_type' => 'pie',
- 'chart_title' => $sTitle,
- 'group_by' => $this->sGroupByExpr,
- 'group_by_label' => $this->sGroupByLabel,
- 'aggregation_function' => $this->sAggregationFunction,
- 'aggregation_attribute' => $this->sAggregationAttribute,
- 'limit' => $this->sLimit,
- 'order_direction' => $this->sOrderDirection,
- 'order_by' => $this->sOrderBy,
- );
- $sHtmlTitle = ''; // done in the itop block
- break;
+ switch ($sStyle) {
+ case 'bars':
+ $sType = 'chart';
+ $aParams = array(
+ 'chart_type' => 'bars',
+ 'chart_title' => $sTitle,
+ 'group_by' => $this->sGroupByExpr,
+ 'group_by_label' => $this->sGroupByLabel,
+ 'aggregation_function' => $this->sAggregationFunction,
+ 'aggregation_attribute' => $this->sAggregationAttribute,
+ 'limit' => $this->sLimit,
+ 'order_direction' => $this->sOrderDirection,
+ 'order_by' => $this->sOrderBy,
+ );
+ $sHtmlTitle = ''; // done in the itop block
+ break;
- case 'table':
- default:
- $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
- $sType = 'count';
- $aParams = array(
- 'group_by' => $this->sGroupByExpr,
- 'group_by_label' => $this->sGroupByLabel,
- 'aggregation_function' => $this->sAggregationFunction,
- 'aggregation_attribute' => $this->sAggregationAttribute,
- 'limit' => $this->sLimit,
- 'order_direction' => $this->sOrderDirection,
- 'order_by' => $this->sOrderBy,
- );
- break;
- }
+ case 'pie':
+ $sType = 'chart';
+ $aParams = array(
+ 'chart_type' => 'pie',
+ 'chart_title' => $sTitle,
+ 'group_by' => $this->sGroupByExpr,
+ 'group_by_label' => $this->sGroupByLabel,
+ 'aggregation_function' => $this->sAggregationFunction,
+ 'aggregation_attribute' => $this->sAggregationAttribute,
+ 'limit' => $this->sLimit,
+ 'order_direction' => $this->sOrderDirection,
+ 'order_by' => $this->sOrderBy,
+ );
+ $sHtmlTitle = ''; // done in the itop block
+ break;
- $oPanel = PanelFactory::MakeNeutral(Dict::S($sTitle));
- $oPage->AddUiBlock($oPanel);
-
-
-// $oPage->add('');
-// if ($sHtmlTitle != '')
-// {
-// $oPage->add('
'.$sHtmlTitle.'
');
-// }
- $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
- $oBlock = new DisplayBlock($oFilter, $sType);
- $oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
- if ($bEditMode) {
- $oPanel->AddHtml('
');
- }
-// $oPage->add('
');
+ case 'table':
+ default:
+ $sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
+ $sType = 'count';
+ $aParams = array(
+ 'group_by' => $this->sGroupByExpr,
+ 'group_by_label' => $this->sGroupByLabel,
+ 'aggregation_function' => $this->sAggregationFunction,
+ 'aggregation_attribute' => $this->sAggregationAttribute,
+ 'limit' => $this->sLimit,
+ 'order_direction' => $this->sOrderDirection,
+ 'order_by' => $this->sOrderBy,
+ );
+ break;
}
+
+ $oPanel = PanelFactory::MakeNeutral(Dict::S($sTitle));
+
+
+ $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
+ $oBlock = new DisplayBlock($oFilter, $sType);
+ $oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
+ if ($bEditMode) {
+ $oPanel->AddHtml('');
+ }
+ return $oPanel;
}
/**
@@ -1372,9 +1330,9 @@ abstract class DashletGroupBy extends Dashlet
*/
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
{
- $oPage->add('');
- $oPage->add('error!');
- $oPage->add('
');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
+ $oDashletContainer->AddHtml('error!');
+ return $oDashletContainer;
}
/**
@@ -1695,26 +1653,27 @@ class DashletGroupByPie extends DashletGroupBy
*/
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
{
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
+
$sTitle = $this->aProperties['title'];
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$HTMLsTitle = ($sTitle != '') ? ''.utils::HtmlEntities($sTitle).'
' : '';
- $oPage->add("");
+ $oDashletContainer->AddHtml("");
$aDisplayValues = $this->MakeSimulatedData();
$aColumns = array();
$aNames = array();
- foreach($aDisplayValues as $idx => $aValue)
- {
+ foreach ($aDisplayValues as $idx => $aValue) {
$aColumns[] = array('series_'.$idx, (int)$aValue['value']);
$aNames['series_'.$idx] = $aValue['label'];
}
$sJSColumns = json_encode($aColumns);
$sJSNames = json_encode($aNames);
$oPage->add_ready_script(
-<<aProperties['title'];
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$HTMLsTitle = ($sTitle != '') ? ''.utils::HtmlEntities($sTitle).'
' : '';
- $oPage->add("");
+ $oDashletContainer->AddHtml("");
$aDisplayValues = $this->MakeSimulatedData();
$aNames = array();
- foreach($aDisplayValues as $idx => $aValue)
- {
+ foreach ($aDisplayValues as $idx => $aValue) {
$aNames[$idx] = $aValue['label'];
}
$sJSNames = json_encode($aNames);
$sJson = json_encode($aDisplayValues);
$oPage->add_ready_script(
-<<MakeSimulatedData();
$iTotal = 0;
- foreach($aDisplayValues as $iRow => $aDisplayData)
- {
+ foreach ($aDisplayValues as $iRow => $aDisplayData) {
$iTotal += $aDisplayData['value'];
}
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
- $oPage->add('');
- $oPage->add('
');
- $oPage->add('
'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'
');
- $oPage->add('
');
- $oPage->add('');
- $oPage->add('');
- $oPage->add('');
- $oPage->add('');
- $oPage->add('
');
- $oPage->add('');
- $oPage->add('');
- foreach($aDisplayValues as $aDisplayData)
- {
- $oPage->add('');
- $oPage->add('| '.$aDisplayData['label'].' | ');
- $oPage->add(''.$aDisplayData['value'].' | ');
- $oPage->add('
');
+ $sHtml = '';
+ $sHtml .= '';
+ $sHtml .= '
';
+ $sHtml .= '
'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'
';
+ $sHtml .= '
';
+ $sHtml .= '';
+ $sHtml .= '';
+ $sHtml .= '';
+ $sHtml .= '';
+ $sHtml .= '
';
+ $sHtml .= '';
+ $sHtml .= '';
+ foreach ($aDisplayValues as $aDisplayData) {
+ $sHtml .= '';
+ $sHtml .= '| '.$aDisplayData['label'].' | ';
+ $sHtml .= ''.$aDisplayData['value'].' | ';
+ $sHtml .= '
';
}
- $oPage->add('');
- $oPage->add('
');
- $oPage->add('
');
+ $sHtml .= '
';
+ $sHtml .= '
';
+ $sHtml .= '
';
- $oPage->add('
');
+ $sHtml .= ' ';
+
+ $oDashletContainer->AddHtml($sHtml);
+
+ return $oDashletContainer;
}
}
@@ -1926,8 +1894,7 @@ class DashletHeaderStatic extends Dashlet
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
- $oBlock = DashletFactory::MakeForDashletHeaderStatic($this->oModelReflection->DictString($sTitle), $sIconPath);
- $oPage->AddUiBlock($oBlock);
+ return DashletFactory::MakeForDashletHeaderStatic($this->oModelReflection->DictString($sTitle), $sIconPath);
}
/**
@@ -2074,7 +2041,6 @@ class DashletHeaderDynamic extends Dashlet
}
$oPanel = PanelFactory::MakeEnhancedNeutral(Dict::S(str_replace('_', ':', $sTitle)), $sIconPath);
- $oPage->AddUiBlock($oPanel);
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
@@ -2096,8 +2062,7 @@ class DashletHeaderDynamic extends Dashlet
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
$oSubTitle->AddHtml(''.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'');
-// $oPage->add('');
-// $oPage->add('');
+ return $oPanel;
}
/**
@@ -2117,46 +2082,48 @@ class DashletHeaderDynamic extends Dashlet
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
- $oPage->add('');
- $oPage->add('
');
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
- $oPage->add('

');
+ $sHtml = '';
+ $sHtml .= '

';
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$iTotal = 0;
$aValues = $this->GetValues();
- $oPage->add('
');
- $oPage->add('
');
- $oPage->add('
');
- $oPage->add('');
- foreach ($aValues as $sValue)
- {
+ $sHtml .= '';
+ $sHtml .= '
';
+ $sHtml .= '
';
+ $sHtml .= '';
+ foreach ($aValues as $sValue) {
$sValueLabel = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
- $oPage->add(' | '.$sValueLabel.' | ');
+ $sHtml .= ' '.$sValueLabel.' | ';
}
- $oPage->add('
');
- $oPage->add('');
- foreach ($aValues as $sValue)
- {
- $iCount = (int) rand(2, 100);
+ $sHtml .= '
';
+ $sHtml .= '';
+ foreach ($aValues as $sValue) {
+ $iCount = (int)rand(2, 100);
$iTotal += $iCount;
- $oPage->add(' | '.$iCount.' | ');
+ $sHtml .= ' '.$iCount.' | ';
}
- $oPage->add('
');
- $oPage->add('
');
- $oPage->add('
');
+ $sHtml .= '
';
+ $sHtml .= '
';
+ $sHtml .= '
';
$sTitle = $this->oModelReflection->DictString($sTitle);
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
- $oPage->add('
'.utils::HtmlEntities($sTitle).'
');
- $oPage->add('
'.utils::HtmlEntities($sSubtitle).'');
- $oPage->add('
');
+ $sHtml .= '
'.utils::HtmlEntities($sTitle).'
';
+ $sHtml .= '
'.utils::HtmlEntities($sSubtitle).'';
+ $sHtml .= '
';
+
+ $sHtml .= '
';
+
+ $oDashletContainer->AddHtml($sHtml);
+
+ return $oDashletContainer;
- $oPage->add('');
- $oPage->add('');
}
/**
@@ -2312,17 +2279,16 @@ class DashletBadge extends Dashlet
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
+
$sClass = $this->aProperties['class'];
-
- $oPage->add('');
-
$oFilter = new DBObjectSearch($sClass);
$oBlock = new DisplayBlock($oFilter, 'actions');
$aExtraParams['context_filter'] = 1;
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
- $oBlock->Display($oPage, $sBlockId, $aExtraParams);
+ $oBlock->DisplayIntoContentBlock($oDashletContainer, $oPage, $sBlockId, $aExtraParams);
- $oPage->add('
');
+ return $oDashletContainer;
}
/**
@@ -2330,23 +2296,25 @@ class DashletBadge extends Dashlet
*/
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
{
- $sClass = $this->aProperties['class'];
+ $oDashletContainer = new DashletContainer(null, 'dashlet-content');
+ $sClass = $this->aProperties['class'];
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
$sClassLabel = $this->oModelReflection->GetName($sClass);
- $oPage->add('');
+ $sHtml = '';
+ $sHtml .= '
';
- $oPage->add('
');
+ $oDashletContainer->AddHtml($sHtml);
- $oPage->add('
');
+ return $oDashletContainer;
}
static protected $aClassList = null;
diff --git a/css/backoffice/components/_badge.scss b/css/backoffice/components/_badge.scss
index 0c1d0bf35..31570fd21 100644
--- a/css/backoffice/components/_badge.scss
+++ b/css/backoffice/components/_badge.scss
@@ -17,7 +17,7 @@
*/
/* SCSS variables */
-$ibo-badge--margin: 0px 8px !default;
+$ibo-badge--margin: 4px 8px !default;
$ibo-badge--padding: 6px 10px !default;
$ibo-badge--border-radius: $ibo-border-radius-300 !default;
diff --git a/css/backoffice/components/dashlet/_dashlet-header-dynamic.scss b/css/backoffice/components/dashlet/_dashlet-header-dynamic.scss
index 6999d0d6b..4a495455a 100644
--- a/css/backoffice/components/dashlet/_dashlet-header-dynamic.scss
+++ b/css/backoffice/components/dashlet/_dashlet-header-dynamic.scss
@@ -9,6 +9,7 @@ $ibo-dashlet-header-dynamic--count--margin-right: 10px !default;
/* Rules */
.ibo-dashlet-header-dynamic--container {
display: flex;
+ flex-wrap: wrap;
padding-top: 12px;
}
diff --git a/css/backoffice/layout/dashboard/_dashboard.scss b/css/backoffice/layout/dashboard/_dashboard.scss
index 4336387e9..c625ba68d 100644
--- a/css/backoffice/layout/dashboard/_dashboard.scss
+++ b/css/backoffice/layout/dashboard/_dashboard.scss
@@ -17,31 +17,39 @@ $ibo-dashboard--grid--elements-spacing-y: $ibo-dashlet--elements-spacing-y !defa
width: $ibo-dashboard--grid--width;
}
.ibo-dashboard--grid-row{
- display: flex;
- flex-direction: row;
- overflow: hidden; /* Because of the column negative margin (which is for the dashlets spacing) */
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ overflow: hidden; /* Because of the column negative margin (which is for the dashlets spacing) */
- /* Compensate negative margin on inner borders to simulate egal dashlets spacing between rows */
- &:not(:last-child){
- padding-bottom: calc(#{$ibo-dashboard--grid--elements-spacing-y} / 2);
- }
- &:not(:first-child){
- padding-top: calc(#{$ibo-dashboard--grid--elements-spacing-y} / 2);
- }
+ /* Compensate negative margin on inner borders to simulate egal dashlets spacing between rows */
+ &:not(:last-child) {
+ padding-bottom: calc(#{$ibo-dashboard--grid--elements-spacing-y} / 2);
+ }
+
+ &:not(:first-child) {
+ padding-top: calc(#{$ibo-dashboard--grid--elements-spacing-y} / 2);
+ }
}
-.ibo-dashboard--grid-column{
- display: flex;
- flex-flow: row wrap;
- justify-content: space-between;
- align-items: flex-start;
- width: calc(100% + (2 * #{$ibo-dashboard--grid--elements-spacing-x}));
- margin: calc(-1 * #{$ibo-dashboard--grid--elements-spacing-y} / 2) calc(-1 * #{$ibo-dashboard--grid--elements-spacing-x} / 2); /* Because of the margin all around the dashlets, we need to compensate it */
- /* Compensate negative margin on inner borders to simulate egal dashlets spacing between columns */
- &:not(:last-child) {
- margin-right: 0;
- }
- &:not(:first-child) {
- margin-left: 0;
- }
+.ibo-dashboard--grid-column {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: flex-start;
+ align-content: flex-start;
+ width: calc(100% + (2 * #{$ibo-dashboard--grid--elements-spacing-x}));
+ margin: calc(-1 * #{$ibo-dashboard--grid--elements-spacing-y} / 2) calc(-1 * #{$ibo-dashboard--grid--elements-spacing-x} / 2); /* Because of the margin all around the dashlets, we need to compensate it */
+
+ /* Compensate negative margin on inner borders to simulate egal dashlets spacing between columns */
+ &:not(:last-child) {
+ margin-right: 0;
+ }
+
+ &:not(:first-child) {
+ margin-left: 0;
+ }
+}
+
+.ibo-dashboard--grid-column.edit-mode {
+ border: 1px #ccc dashed;
}
\ No newline at end of file
diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php
index 51986d481..c1d33ded2 100644
--- a/lib/composer/autoload_classmap.php
+++ b/lib/composer/autoload_classmap.php
@@ -158,7 +158,7 @@ return array(
'Combodo\\iTop\\Application\\UI\\Component\\Button\\Button' => $baseDir . '/sources/application/UI/Component/Button/Button.php',
'Combodo\\iTop\\Application\\UI\\Component\\Button\\ButtonFactory' => $baseDir . '/sources/application/UI/Component/Button/ButtonFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletBadge' => $baseDir . '/sources/application/UI/Component/Dashlet/DashletBadge.php',
- 'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletComponent' => $baseDir . '/sources/application/UI/Component/Dashlet/DashletComponent.php',
+ 'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletContainer' => $baseDir . '/sources/application/UI/Component/Dashlet/DashletContainer.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletFactory' => $baseDir . '/sources/application/UI/Component/Dashlet/DashletFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletHeaderStatic' => $baseDir . '/sources/application/UI/Component/Dashlet/DashletHeaderStatic.php',
'Combodo\\iTop\\Application\\UI\\Component\\FieldSet\\FieldSet' => $baseDir . '/sources/application/UI/Component/FieldSet/FieldSet.php',
@@ -210,6 +210,9 @@ return array(
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityNewEntryForm\\ActivityNewEntryForm' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityNewEntryForm/ActivityNewEntryForm.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanel' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanelFactory' => $baseDir . '/sources/application/UI/Layout/ActivityPanel/ActivityPanelFactory.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardColumn' => $baseDir . '/sources/application/UI/Layout/Dashboard/DashboardColumn.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardLayout' => $baseDir . '/sources/application/UI/Layout/Dashboard/DashboardLayout.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardRow' => $baseDir . '/sources/application/UI/Layout/Dashboard/DashboardRow.php',
'Combodo\\iTop\\Application\\UI\\Layout\\MultiColumn\\Column\\Column' => $baseDir . '/sources/application/UI/Layout/MultiColumn/Column/Column.php',
'Combodo\\iTop\\Application\\UI\\Layout\\MultiColumn\\MultiColumn' => $baseDir . '/sources/application/UI/Layout/MultiColumn/MultiColumn.php',
'Combodo\\iTop\\Application\\UI\\Layout\\NavigationMenu\\NavigationMenu' => $baseDir . '/sources/application/UI/Layout/NavigationMenu/NavigationMenu.php',
diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php
index 04226e86c..c65c884fc 100644
--- a/lib/composer/autoload_static.php
+++ b/lib/composer/autoload_static.php
@@ -388,7 +388,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Application\\UI\\Component\\Button\\Button' => __DIR__ . '/../..' . '/sources/application/UI/Component/Button/Button.php',
'Combodo\\iTop\\Application\\UI\\Component\\Button\\ButtonFactory' => __DIR__ . '/../..' . '/sources/application/UI/Component/Button/ButtonFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletBadge' => __DIR__ . '/../..' . '/sources/application/UI/Component/Dashlet/DashletBadge.php',
- 'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletComponent' => __DIR__ . '/../..' . '/sources/application/UI/Component/Dashlet/DashletComponent.php',
+ 'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletContainer' => __DIR__ . '/../..' . '/sources/application/UI/Component/Dashlet/DashletContainer.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletFactory' => __DIR__ . '/../..' . '/sources/application/UI/Component/Dashlet/DashletFactory.php',
'Combodo\\iTop\\Application\\UI\\Component\\Dashlet\\DashletHeaderStatic' => __DIR__ . '/../..' . '/sources/application/UI/Component/Dashlet/DashletHeaderStatic.php',
'Combodo\\iTop\\Application\\UI\\Component\\FieldSet\\FieldSet' => __DIR__ . '/../..' . '/sources/application/UI/Component/FieldSet/FieldSet.php',
@@ -440,6 +440,9 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityNewEntryForm\\ActivityNewEntryForm' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityNewEntryForm/ActivityNewEntryForm.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanel' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityPanel.php',
'Combodo\\iTop\\Application\\UI\\Layout\\ActivityPanel\\ActivityPanelFactory' => __DIR__ . '/../..' . '/sources/application/UI/Layout/ActivityPanel/ActivityPanelFactory.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardColumn' => __DIR__ . '/../..' . '/sources/application/UI/Layout/Dashboard/DashboardColumn.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardLayout' => __DIR__ . '/../..' . '/sources/application/UI/Layout/Dashboard/DashboardLayout.php',
+ 'Combodo\\iTop\\Application\\UI\\Layout\\Dashboard\\DashboardRow' => __DIR__ . '/../..' . '/sources/application/UI/Layout/Dashboard/DashboardRow.php',
'Combodo\\iTop\\Application\\UI\\Layout\\MultiColumn\\Column\\Column' => __DIR__ . '/../..' . '/sources/application/UI/Layout/MultiColumn/Column/Column.php',
'Combodo\\iTop\\Application\\UI\\Layout\\MultiColumn\\MultiColumn' => __DIR__ . '/../..' . '/sources/application/UI/Layout/MultiColumn/MultiColumn.php',
'Combodo\\iTop\\Application\\UI\\Layout\\NavigationMenu\\NavigationMenu' => __DIR__ . '/../..' . '/sources/application/UI/Layout/NavigationMenu/NavigationMenu.php',
diff --git a/sources/application/UI/Component/Dashlet/DashletBadge.php b/sources/application/UI/Component/Dashlet/DashletBadge.php
index 7646c1c42..092b0a514 100644
--- a/sources/application/UI/Component/Dashlet/DashletBadge.php
+++ b/sources/application/UI/Component/Dashlet/DashletBadge.php
@@ -8,7 +8,7 @@
namespace Combodo\iTop\Application\UI\Component\Dashlet;
-class DashletBadge extends DashletComponent
+class DashletBadge extends DashletContainer
{
public const BLOCK_CODE = 'ibo-dashlet-badge';
public const HTML_TEMPLATE_REL_PATH = 'components/dashlet/dashletbadge';
diff --git a/sources/application/UI/Component/Dashlet/DashletComponent.php b/sources/application/UI/Component/Dashlet/DashletComponent.php
deleted file mode 100644
index e96025ff3..000000000
--- a/sources/application/UI/Component/Dashlet/DashletComponent.php
+++ /dev/null
@@ -1,22 +0,0 @@
-aUIBlocks = [];
+ $this->iColumnIndex = 0;
+ $this->iCellIndex = 0;
+ $this->bEditMode = $bEditMode;
+ $this->bLastRow = $bLastRow;
+ }
+
+ /**
+ *
+ * @param UIBlock $oUIBlock
+ *
+ * @return DashboardColumn
+ */
+ public function AddUIBlock(UIBlock $oUIBlock): DashboardColumn
+ {
+ $this->aUIBlocks[] = $oUIBlock;
+ return $this;
+ }
+
+ public function GetSubBlocks()
+ {
+ return $this->aUIBlocks;
+ }
+
+ /**
+ * @return int
+ */
+ public function GetColumnIndex(): int
+ {
+ return $this->iColumnIndex;
+ }
+
+ /**
+ * @param int $iColumnIndex
+ *
+ * @return DashboardColumn
+ */
+ public function SetColumnIndex(int $iColumnIndex): DashboardColumn
+ {
+ $this->iColumnIndex = $iColumnIndex;
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function IsEditMode(): bool
+ {
+ return $this->bEditMode;
+ }
+
+ /**
+ * @param bool $bEditMode
+ *
+ * @return DashboardColumn
+ */
+ public function SetEditMode(bool $bEditMode): DashboardColumn
+ {
+ $this->bEditMode = $bEditMode;
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function GetCellIndex(): int
+ {
+ return $this->iCellIndex;
+ }
+
+ /**
+ * @param int $iCellIndex
+ *
+ * @return DashboardColumn
+ */
+ public function SetCellIndex(int $iCellIndex): DashboardColumn
+ {
+ $this->iCellIndex = $iCellIndex;
+ return $this;
+ }
+
+
+}
\ No newline at end of file
diff --git a/sources/application/UI/Layout/Dashboard/DashboardLayout.php b/sources/application/UI/Layout/Dashboard/DashboardLayout.php
new file mode 100644
index 000000000..c9ffaccbb
--- /dev/null
+++ b/sources/application/UI/Layout/Dashboard/DashboardLayout.php
@@ -0,0 +1,47 @@
+aDashboardRows = [];
+ $this->iRows = 0;
+ }
+
+ /**
+ *
+ * @param \Combodo\iTop\Application\UI\Layout\Dashboard\DashboardRow $oDashboardRow
+ *
+ * @return DashboardLayout
+ */
+ public function AddDashboardRow(DashboardRow $oDashboardRow): DashboardLayout
+ {
+ $oDashboardRow->SetRowIndex($this->iRows);
+ $this->aDashboardRows[] = $oDashboardRow;
+ $this->iRows++;
+ return $this;
+ }
+
+ public function GetSubBlocks()
+ {
+ return $this->aDashboardRows;
+ }
+}
\ No newline at end of file
diff --git a/sources/application/UI/Layout/Dashboard/DashboardRow.php b/sources/application/UI/Layout/Dashboard/DashboardRow.php
new file mode 100644
index 000000000..2dc86a3be
--- /dev/null
+++ b/sources/application/UI/Layout/Dashboard/DashboardRow.php
@@ -0,0 +1,69 @@
+aDashboardColumns = [];
+ $this->iRowIndex = 0;
+ $this->iCols = 0;
+ }
+
+ /**
+ *
+ * @param \Combodo\iTop\Application\UI\Layout\Dashboard\DashboardColumn $oDashboardColumn
+ *
+ * @return DashboardRow
+ */
+ public function AddDashboardColumn(DashboardColumn $oDashboardColumn): DashboardRow
+ {
+ $oDashboardColumn->SetColumnIndex($this->iCols);
+ $this->aDashboardColumns[] = $oDashboardColumn;
+ $this->iCols++;
+ return $this;
+ }
+
+ public function GetSubBlocks()
+ {
+ return $this->aDashboardColumns;
+ }
+
+ /**
+ * @return int
+ */
+ public function GetRowIndex(): int
+ {
+ return $this->iRowIndex;
+ }
+
+ /**
+ * @param int $iRowIndex
+ *
+ * @return DashboardRow
+ */
+ public function SetRowIndex(int $iRowIndex): DashboardRow
+ {
+ $this->iRowIndex = $iRowIndex;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/templates/layouts/content-block/layout.html.twig b/templates/layouts/content-block/layout.html.twig
index 99224068f..feb302276 100644
--- a/templates/layouts/content-block/layout.html.twig
+++ b/templates/layouts/content-block/layout.html.twig
@@ -1,3 +1,6 @@
+{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{# Content Block #}
{% apply spaceless %}
{% block iboContentBlockContainer %}
diff --git a/templates/layouts/dashboard/column/layout.html.twig b/templates/layouts/dashboard/column/layout.html.twig
new file mode 100644
index 000000000..09b6e28cc
--- /dev/null
+++ b/templates/layouts/dashboard/column/layout.html.twig
@@ -0,0 +1,21 @@
+{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{# Dashboard Column #}
+{% apply spaceless %}
+ {% if oUIBlock.IsEditMode() %}
+ {% if oUIBlock.IsLastRow() %}
+ {% set sCellClass = "layout_cell edit_mode layout_extension" %}
+ {% else %}
+ {% set sCellClass = "layout_cell edit_mode" %}
+ {% endif %}
+ {% else %}
+ {% set sCellClass = "dashboard" %}
+ {% endif %}
+
+ {% for oSubBlock in oUIBlock.GetSubBlocks() %}
+ {{ render_block(oSubBlock, {aPage: aPage}) }}
+ {% endfor %}
+
+{% endapply %}
\ No newline at end of file
diff --git a/templates/layouts/dashboard/layout.html.twig b/templates/layouts/dashboard/layout.html.twig
new file mode 100644
index 000000000..6156c8836
--- /dev/null
+++ b/templates/layouts/dashboard/layout.html.twig
@@ -0,0 +1,10 @@
+{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{# Dashboard Layout #}
+{% apply spaceless %}
+
+ {% for oSubBlock in oUIBlock.GetSubBlocks() %}
+ {{ render_block(oSubBlock, {aPage: aPage}) }}
+ {% endfor %}
+
+{% endapply %}
\ No newline at end of file
diff --git a/templates/layouts/dashboard/row/layout.html.twig b/templates/layouts/dashboard/row/layout.html.twig
new file mode 100644
index 000000000..421a8548c
--- /dev/null
+++ b/templates/layouts/dashboard/row/layout.html.twig
@@ -0,0 +1,10 @@
+{# @copyright Copyright (C) 2010-2020 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{# Dashboard Row #}
+{% apply spaceless %}
+
+ {% for oSubBlock in oUIBlock.GetSubBlocks() %}
+ {{ render_block(oSubBlock, {aPage: aPage}) }}
+ {% endfor %}
+
+{% endapply %}
\ No newline at end of file