diff --git a/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php index 3831664e4..a9d36dd67 100644 --- a/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php @@ -82,6 +82,11 @@ Dict::Add('EN US', 'English', 'English', array( 'Brick:Portal:UserProfile:Photo:Title' => 'Photo', )); +// AggregatePageBrick +Dict::Add('FR FR', 'French', 'Français', array( + 'Brick:Portal:AggregatePage:DefaultTitle' => 'Dashboard', +)); + // BrowseBrick brick Dict::Add('EN US', 'English', 'English', array( 'Brick:Portal:Browse:Name' => 'Browse throught items', diff --git a/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php index e84a5c30a..38146ac23 100644 --- a/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php @@ -82,6 +82,11 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Brick:Portal:UserProfile:Photo:Title' => 'Photo', )); +// AggregatePageBrick +Dict::Add('FR FR', 'French', 'Français', array( + 'Brick:Portal:AggregatePage:DefaultTitle' => 'Tableau de bord', +)); + // BrowseBrick brick Dict::Add('FR FR', 'French', 'Français', array( 'Brick:Portal:Browse:Name' => 'Navigation dans les éléments', diff --git a/datamodels/2.x/itop-portal-base/portal/src/controllers/aggregatepagebrickcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/controllers/aggregatepagebrickcontroller.class.inc.php new file mode 100644 index 000000000..128d3d536 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/controllers/aggregatepagebrickcontroller.class.inc.php @@ -0,0 +1,138 @@ + +// + +namespace Combodo\iTop\Portal\Controller; + +use Combodo\iTop\Portal\Helper\ApplicationHelper; +use Silex\Application; +use Symfony\Component\HttpFoundation\Request; + +class AggregatePageBrickController +{ + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param \Silex\Application $oApp + * @param string $sBrickId + * + * @return response + * @throws \Exception + */ + public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId) + { + /** @var \Combodo\iTop\Portal\Brick\AggregatePageBrick $oBrick */ + $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId); + + $aPortalInstanceBricks = $oApp['combodo.portal.instance.conf']['bricks']; + $aAggregatePageBricksConf = $oBrick->GetAggregatePageBricks(); + $aAggregatePageBricks = $this->GetOrderedAggregatePageBricksObjectsById($aPortalInstanceBricks, + $aAggregatePageBricksConf); + + $aTilesRendering = $this->GetBricksTileRendering($oRequest, $oApp, $aAggregatePageBricks); + + $sLayoutTemplate = $oBrick->GetPageTemplatePath(); + $aData = array( + 'oBrick' => $oBrick, + 'aggregatepage_bricks' => $aAggregatePageBricks, + 'aTilesRendering' => $aTilesRendering, + ); + $oResponse = $oApp['twig']->render($sLayoutTemplate, $aData); + + return $oResponse; + } + + /** + * @param \Combodo\iTop\Portal\Brick\PortalBrick[] $aPortalInstanceBricks + * @param array $aAggregatePageBricksConf + * + * @return array + * @throws \Exception + */ + private function GetOrderedAggregatePageBricksObjectsById($aPortalInstanceBricks, $aAggregatePageBricksConf) + { + $aAggregatePageBricks = array(); + foreach ($aAggregatePageBricksConf as $sBrickId => $iBrickRank) + { + $oPortalBrick = $this->GetBrickFromId($aPortalInstanceBricks, $sBrickId); + if (!isset($oPortalBrick)) + { + throw new \Exception("AggregatePageBrick : non existing brick '$sBrickId'"); + } + $aAggregatePageBricks[] = $oPortalBrick; + } + + return $aAggregatePageBricks; + } + + /** + * @param \Combodo\iTop\Portal\Brick\PortalBrick[] $aBrickList + * @param string $sBrickId + * + * @return \Combodo\iTop\Portal\Brick\PortalBrick found brick using the given id, null if not found + */ + private function GetBrickFromId($aBrickList, $sBrickId) + { + $aFilteredBricks = array_filter( + $aBrickList, + function ($oSearchBrick) use ($sBrickId) { + return ($oSearchBrick->GetId() == $sBrickId); + } + ); + $oFoundBrick = null; + if (count($aFilteredBricks) > 0) + { + $oFoundBrick = reset($aFilteredBricks); + } + + return $oFoundBrick; + } + + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param \Silex\Application $oApp + * @param \Combodo\iTop\Portal\Brick\PortalBrick[] $aBricks + * + * @return array rendering for each included tile (key = brick id, value = rendering) + */ + private function GetBricksTileRendering(Request $oRequest, Application $oApp, $aBricks) + { + $aTilesRendering = array(); + foreach ($aBricks as $oBrick) + { + if ($oBrick->GetTileControllerAction() !== null) + { + $aControllerActionParts = explode('::', $oBrick->GetTileControllerAction()); + if (count($aControllerActionParts) !== 2) + { + $oApp->abort(500, + 'Tile controller action must be of form "\Namespace\ControllerClass::FunctionName" for brick "'.$oBrick->GetId().'"'); + } + + $sControllerName = $aControllerActionParts[0]; + $sControllerAction = $aControllerActionParts[1]; + + $oController = new $sControllerName($oRequest, $oApp, $oBrick->GetId()); + $aTilesRendering[$oBrick->GetId()] = $oController->$sControllerAction($oRequest, $oApp, + $oBrick->GetId()); + } + } + + return $aTilesRendering; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/entities/aggregatepagebrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/entities/aggregatepagebrick.class.inc.php new file mode 100644 index 000000000..c3824a529 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/entities/aggregatepagebrick.class.inc.php @@ -0,0 +1,97 @@ + +// + +namespace Combodo\iTop\Portal\Brick; + +use Combodo\iTop\DesignElement; +use Dict; + +class AggregatePageBrick extends PortalBrick +{ + const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-dashboard'; + const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-dashboard fa-2x'; + const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig'; + + static $sRouteName = 'p_aggregatepage_brick'; + + /** + * @var string[] list of bricks to use, ordered by rank (key=id, value=rank) + */ + private $aAggregatePageBricks = array(); + + /** + * AggregatePageBrick constructor. + */ + function __construct() + { + parent::__construct(); + + $this->SetTitle(Dict::S('Brick:Portal:AggregatePage:DefaultTitle')); + } + + /** + * @param \Combodo\iTop\DesignElement $oMDElement + * + * @return \Combodo\iTop\Portal\Brick\PortalBrick|void + * @throws \DOMFormatException + */ + public function LoadFromXml(DesignElement $oMDElement) + { + parent::LoadFromXml($oMDElement); + + foreach ($oMDElement->GetNodes('./*') as $oBrickSubNode) + { + switch ($oBrickSubNode->nodeName) + { + case 'aggregate_page_bricks': + foreach ($oBrickSubNode->GetNodes('./aggregate_page_brick') as $oAggregatePageBrickNode) + { + if (!$oAggregatePageBrickNode->hasAttribute('id')) + { + throw new \DOMFormatException('AggregatePageBrick : must have an id attribute', null, + null, $oAggregatePageBrickNode); + } + $sBrickName = $oAggregatePageBrickNode->getAttribute('id'); + + $iBrickRank = static::DEFAULT_RANK; + $oOptionalNode = $oAggregatePageBrickNode->GetOptionalElement('rank'); + if ($oOptionalNode !== null) + { + $iBrickRank = $oOptionalNode->GetText(); + } + + $this->aAggregatePageBricks[$sBrickName] = $iBrickRank; + } + } + } + + asort($this->aAggregatePageBricks); + } + + /** + * @return string[] + */ + public function GetAggregatePageBricks() + { + return $this->aAggregatePageBricks; + } + + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/routers/aggregatepagebrickrouter.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/routers/aggregatepagebrickrouter.class.inc.php new file mode 100644 index 000000000..aa0355825 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/routers/aggregatepagebrickrouter.class.inc.php @@ -0,0 +1,34 @@ + +// + +namespace Combodo\iTop\Portal\Router; + +class AggregatePageBrickRouter extends AbstractRouter +{ + static $aRoutes = array( + array( + 'pattern' => '/aggregate-page/{sBrickId}', + 'callback' => 'Combodo\\iTop\\Portal\\Controller\\AggregatePageBrickController::DisplayAction', + 'bind' => 'p_aggregatepage_brick', + 'asserts' => array(), + 'values' => array() + ), + ); +} diff --git a/datamodels/2.x/itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig new file mode 100644 index 000000000..a29aca0ad --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig @@ -0,0 +1,18 @@ +{# itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig #} +{% extends 'itop-portal-base/portal/src/views/bricks/layout.html.twig' %} + +{% block pPageBodyClass %}{{ parent() }} page_aggregate_page_brick home{% endblock %} + +{% block pMainHeaderTitle %}{{ oBrick.GetTitle()|dict_s }}{% endblock %} + +{% block pMainContent %} +
+ {% for brick in aggregatepage_bricks %} + {% if aTilesRendering[brick.GetId] is defined %} + {{ aTilesRendering[brick.GetId]|raw }} + {% else %} + {% include '' ~ brick.GetTileTemplatePath with {brick: brick} %} + {% endif %} + {% endfor %} +
+{% endblock %}