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("'); $iCellIdx++; } - $oPage->add(''); } + if ($bEditMode) // Add one row for extensibility { - $oPage->add(""); - for($iCols = 0; $iCols < $this->iNbCols; $iCols++) - { - $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('
"); - 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('
"); - $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->p(Dict::S('UI:NoObjectToDisplay')); - if ($bShowMenu) - { + if ($bShowMenu) { $oPage->p(''.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).''); } $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("
$HTMLsTitle
"); + $oDashletContainer->AddHtml("
$HTMLsTitle
"); $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("
$HTMLsTitle
"); + $oDashletContainer->AddHtml("
$HTMLsTitle
"); $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(''); - $oPage->add(''); - $oPage->add(''); + $sHtml = ''; + $sHtml .= '
'; + $sHtml .= '
'; + $sHtml .= '

'.Dict::Format('UI:Pagination:HeaderNoSelection', $iTotal).'

'; + $sHtml .= '
'.$this->sGroupByLabel.''.Dict::S('UI:GroupBy:Count').'
'.$aDisplayData['label'].''.$aDisplayData['value'].'
'; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + foreach ($aDisplayValues as $aDisplayData) { + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; } - $oPage->add(''); - $oPage->add('
'.$this->sGroupByLabel.''.Dict::S('UI:GroupBy:Count').'
'.$aDisplayData['label'].''.$aDisplayData['value'].'
'); - $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(' '); + $sHtml .= ' '; } - $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(' '); + $sHtml .= ' '; } - $oPage->add(''); - $oPage->add('
'.$sValueLabel.''.$sValueLabel.'
'.$iCount.''.$iCount.'
'); - $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 .= '
'; + $sHtml .= '

'; + $sHtml .= ' '.$sClassLabel.': 947'; + $sHtml .= '

'; + $sHtml .= '

'; + $sHtml .= ' '.Dict::Format('UI:ClickToCreateNew', $sClassLabel).''; + $sHtml .= '

'; + $sHtml .= '
'; - $oPage->add('
'); - $oPage->add('

'); - $oPage->add(' '.$sClassLabel.': 947'); - $oPage->add('

'); - $oPage->add('

'); - $oPage->add(' '.Dict::Format('UI:ClickToCreateNew', $sClassLabel).''); - $oPage->add('

'); - $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