require_once(APPROOT.'application/dashboardlayout.class.inc.php'); require_once(APPROOT.'application/dashlet.class.inc.php'); require_once(APPROOT.'core/modelreflection.class.inc.php'); /** * A user editable dashboard page * * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ abstract class Dashboard { protected $sTitle; protected $bAutoReload; protected $iAutoReloadSec; protected $sLayoutClass; protected $aWidgetsData; protected $oDOMNode; protected $sId; protected $aCells; protected $oMetaModel; public function __construct($sId) { $this->sTitle = ''; $this->sLayoutClass = 'DashboardLayoutOneCol'; $this->bAutoReload = false; $this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval(); $this->aCells = array(); $this->oDOMNode = null; $this->sId = $sId; } public function FromXml($sXml) { $this->aCells = array(); // reset the content of the dashboard set_error_handler(array('Dashboard', 'ErrorHandler')); $oDoc = new DOMDocument(); $oDoc->loadXML($sXml); restore_error_handler(); $this->FromDOMDocument($oDoc); } public function FromDOMDocument(DOMDocument $oDoc) { $this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0); if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0)) { $this->sLayoutClass = $oLayoutNode->textContent; } else { $this->sLayoutClass = 'DashboardLayoutOneCol'; } if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0)) { $this->sTitle = $oTitleNode->textContent; } else { $this->sTitle = ''; } $this->bAutoReload = false; $this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval(); if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0)) { if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0)) { $this->bAutoReload = ($oAutoReloadEnabled->textContent == 'true'); } if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0)) { $this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent); } } if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0)) { $oCellsList = $oCellsNode->getElementsByTagName('cell'); $aCellOrder = array(); $iCellRank = 0; foreach($oCellsList as $oCellNode) { $aDashletList = array(); $oCellRank = $oCellNode->getElementsByTagName('rank')->item(0); if ($oCellRank) { $iCellRank = (float)$oCellRank->textContent; } $oDashletsNode = $oCellNode->getElementsByTagName('dashlets')->item(0); { $oDashletList = $oDashletsNode->getElementsByTagName('dashlet'); $iRank = 0; $aDashletOrder = array(); foreach($oDashletList as $oDomNode) { $sDashletClass = $oDomNode->getAttribute('xsi:type'); $oRank = $oDomNode->getElementsByTagName('rank')->item(0); if ($oRank) { $iRank = (float)$oRank->textContent; } $sId = $oDomNode->getAttribute('id'); $oNewDashlet = new $sDashletClass($this->oMetaModel, $sId); $oNewDashlet->FromDOMNode($oDomNode); $aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet); } usort($aDashletOrder, array(get_class($this), 'SortOnRank')); $aDashletList = array(); foreach($aDashletOrder as $aItem) { $aDashletList[] = $aItem['dashlet']; } $aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList); } } usort($aCellOrder, array(get_class($this), 'SortOnRank')); foreach($aCellOrder as $aItem) { $this->aCells[] = $aItem['dashlets']; } } else { $this->aCells = array(); } } static function SortOnRank($aItem1, $aItem2) { return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1; } /** * Error handler to turn XML loading warnings into exceptions */ public static function ErrorHandler($errno, $errstr, $errfile, $errline) { if ($errno == E_WARNING && (substr_count($errstr,"DOMDocument::loadXML()")>0)) { throw new DOMException($errstr); } else { return false; } } public function ToXml() { $oDoc = new DOMDocument(); $oDoc->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS) $oDoc->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect $oMainNode = $oDoc->createElement('dashboard'); $oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance"); $oDoc->appendChild($oMainNode); $this->ToDOMNode($oMainNode); $sXml = $oDoc->saveXML(); return $sXml; } public function ToDOMNode($oDefinition) { $oDoc = $oDefinition->ownerDocument; $oNode = $oDoc->createElement('layout', $this->sLayoutClass); $oDefinition->appendChild($oNode); $oNode = $oDoc->createElement('title', $this->sTitle); $oDefinition->appendChild($oNode); $oAutoReloadNode = $oDoc->createElement('auto_reload'); $oDefinition->appendChild($oAutoReloadNode); $oNode = $oDoc->createElement('enabled', $this->bAutoReload ? 'true' : 'false'); $oAutoReloadNode->appendChild($oNode); $oNode = $oDoc->createElement('interval', $this->iAutoReloadSec); $oAutoReloadNode->appendChild($oNode); $oCellsNode = $oDoc->createElement('cells'); $oDefinition->appendChild($oCellsNode); $iCellRank = 0; foreach ($this->aCells as $aCell) { $oCellNode = $oDoc->createElement('cell'); $oCellNode->setAttribute('id', $iCellRank); $oCellsNode->appendChild($oCellNode); $oCellRank = $oDoc->createElement('rank', $iCellRank); $oCellNode->appendChild($oCellRank); $iCellRank++; $iDashletRank = 0; $oDashletsNode = $oDoc->createElement('dashlets'); $oCellNode->appendChild($oDashletsNode); foreach ($aCell as $oDashlet) { $oNode = $oDoc->createElement('dashlet'); $oDashletsNode->appendChild($oNode); $oNode->setAttribute('id', $oDashlet->GetID()); $oNode->setAttribute('xsi:type', get_class($oDashlet)); $oDashletRank = $oDoc->createElement('rank', $iDashletRank); $oNode->appendChild($oDashletRank); $iDashletRank++; $oDashlet->ToDOMNode($oNode); } } } public function FromParams($aParams) { $this->sLayoutClass = $aParams['layout_class']; $this->sTitle = $aParams['title']; $this->bAutoReload = $aParams['auto_reload'] == 'true'; $this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']); foreach($aParams['cells'] as $aCell) { $aCellDashlets = array(); foreach($aCell as $aDashletParams) { $sDashletClass = $aDashletParams['dashlet_class']; $sId = $aDashletParams['dashlet_id']; $oNewDashlet = new $sDashletClass($this->oMetaModel, $sId); $oForm = $oNewDashlet->GetForm(); $oForm->SetParamsContainer($sId); $oForm->SetPrefix(''); $aValues = $oForm->ReadParams(); $oNewDashlet->FromParams($aValues); $aCellDashlets[] = $oNewDashlet; } $this->aCells[] = $aCellDashlets; } } public function Save() { } public function GetLayout() { return $this->sLayoutClass; } public function SetLayout($sLayoutClass) { $this->sLayoutClass = $sLayoutClass; } public function GetTitle() { return $this->sTitle; } public function SetTitle($sTitle) { $this->sTitle = $sTitle; } public function GetAutoReload() { return $this->bAutoReload; } public function SetAutoReload($bAutoReload) { $this->bAutoReload = $bAutoReload; } public function GetAutoReloadInterval() { return $this->iAutoReloadSec; } public function SetAutoReloadInterval($iAutoReloadSec) { $this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec); } public function AddDashlet($oDashlet) { $sId = $this->GetNewDashletId(); $oDashlet->SetId($sId); $this->aCells[] = array($oDashlet); } public function Render($oPage, $bEditMode = false, $aExtraParams = array()) { $oPage->add('

'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'

'); $oLayout = new $this->sLayoutClass; $oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams); if (!$bEditMode) { $oPage->add_linked_script('../js/dashlet.js'); $oPage->add_linked_script('../js/dashboard.js'); } } public function RenderProperties($oPage) { // menu to pick a layout and edit other properties of the dashboard $oPage->add('
'.Dict::S('UI:DashboardEdit:Properties').'
'); $sUrl = utils::GetAbsoluteUrlAppRoot(); $oPage->add('
'.Dict::S('UI:DashboardEdit:Layout').'
'); $oPage->add('
'); foreach( get_declared_classes() as $sLayoutClass) { if (is_subclass_of($sLayoutClass, 'DashboardLayout')) { $oReflection = new ReflectionClass($sLayoutClass); if (!$oReflection->isAbstract()) { $aCallSpec = array($sLayoutClass, 'GetInfo'); $aInfo = call_user_func($aCallSpec); $sChecked = ($this->sLayoutClass == $sLayoutClass) ? 'checked' : ''; $oPage->add(''); // title="" on either the img or the label does nothing ! } } } $oPage->add('
'); $oForm = new DesignerForm(); $oField = new DesignerHiddenField('dashboard_id', '', $this->sId); $oForm->AddField($oField); $oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle); $oForm->AddField($oField); $oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload); $oForm->AddField($oField); $oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec); $oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit $oForm->AddField($oField); $this->SetFormParams($oForm); $oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard'); $oPage->add('
'); $sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval'))); $oPage->add_ready_script( <<add('
'.Dict::S('UI:DashboardEdit:Dashlets').'
'); $sUrl = utils::GetAbsoluteUrlAppRoot(); $oPage->add('
'); foreach( get_declared_classes() as $sDashletClass) { if (is_subclass_of($sDashletClass, 'Dashlet')) { $oReflection = new ReflectionClass($sDashletClass); if (!$oReflection->isAbstract()) { $aCallSpec = array($sDashletClass, 'IsVisible'); $bVisible = call_user_func($aCallSpec); if ($bVisible) { $aCallSpec = array($sDashletClass, 'GetInfo'); $aInfo = call_user_func($aCallSpec); $oPage->add(''); } } } } $oPage->add('
'); $oPage->add('
'); $oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});"); } public function RenderDashletsProperties($oPage) { // Toolbox/palette to edit the properties of each dashlet $oPage->add('
'.Dict::S('UI:DashboardEdit:DashletProperties').'
'); $oPage->add('
'); foreach($this->aCells as $aCell) { foreach($aCell as $oDashlet) { $sId = $oDashlet->GetID(); $sClass = get_class($oDashlet); if ($oDashlet->IsVisible()) { $oPage->add(''); } } } $oPage->add('
'); $oPage->add('
'); } protected function GetNewDashletId() { $iNewId = 0; foreach($this->aCells as $aDashlets) { foreach($aDashlets as $oDashlet) { $iNewId = max($iNewId, (int)$oDashlet->GetID()); } } return $iNewId + 1; } abstract protected function SetFormParams($oForm); } class RuntimeDashboard extends Dashboard { protected $bCustomized; public function __construct($sId) { parent::__construct($sId); $this->bCustomized = false; $this->oMetaModel = new ModelReflectionRuntime(); } public function SetCustomFlag($bCustomized) { $this->bCustomized = $bCustomized; } protected function SetFormParams($oForm) { $oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property')); } public function Save() { $sXml = $this->ToXml(); $oUDSearch = new DBObjectSearch('UserDashboard'); $oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '='); $oUDSearch->AddCondition('menu_code', $this->sId, '='); $oUDSet = new DBObjectSet($oUDSearch); if ($oUDSet->Count() > 0) { // Assuming there is at most one couple {user, menu}! $oUserDashboard = $oUDSet->Fetch(); $oUserDashboard->Set('contents', $sXml); } else { // No such customized dasboard for the current user, let's create a new record $oUserDashboard = new UserDashboard(); $oUserDashboard->Set('user_id', UserRights::GetUserId()); $oUserDashboard->Set('menu_code', $this->sId); $oUserDashboard->Set('contents', $sXml); } utils::PushArchiveMode(false); $oUserDashboard->DBWrite(); utils::PopArchiveMode(); } public function Revert() { $oUDSearch = new DBObjectSearch('UserDashboard'); $oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '='); $oUDSearch->AddCondition('menu_code', $this->sId, '='); $oUDSet = new DBObjectSet($oUDSearch); if ($oUDSet->Count() > 0) { // Assuming there is at most one couple {user, menu}! $oUserDashboard = $oUDSet->Fetch(); utils::PushArchiveMode(false); $oUserDashboard->DBDelete(); utils::PopArchiveMode(); } } public function RenderEditionTools($oPage) { $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js'); $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js'); $sEditMenu = "