diff --git a/application/dashboard.class.inc.php b/application/dashboard.class.inc.php new file mode 100644 index 000000000..4eb8ac215 --- /dev/null +++ b/application/dashboard.class.inc.php @@ -0,0 +1,216 @@ +sLayoutClass = null; + $this->aDashlets = array(); + $this->oDOMNode = null; + $this->sId = $sId; + } + + public function FromXml($sXml) + { + $oDoc = new DOMDocument(); + $oDoc->loadXML($sXml); + $this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0); + + $oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0); + $this->sLayoutClass = $oLayoutNode->textContent; + + $oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0); + $this->sTitle = $oTitleNode->textContent; + + $oDashletsNode = $this->oDOMNode->getElementsByTagName('dashlets')->item(0); + $oDashletList = $oDashletsNode->getElementsByTagName('dashlet'); + foreach($oDashletList as $oDomNode) + { + $sDashletClass = $oDomNode->getAttribute('xsi:type'); + $oNewDashlet = new $sDashletClass; + $oNewDashlet->FromDOMNode($oDomNode); + $this->aDashlets[] = $oNewDashlet; + } + } + + public function FromParams($aParams) + { + + } + + 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 AddDashlet() + { + } + + public function Render($oPage, $bEditMode = false, $aExtraParams = array()) + { + $oPage->add('

'.$this->sTitle.'

'); + $oLayout = new $this->sLayoutClass; + $oLayout->Render($oPage, $this->aDashlets, $bEditMode, $aExtraParams); + } + + public function RenderProperties($oPage) + { + // menu to pick a layout and edit other properties of the dashboard + $oPage->add('
Dashboard Properties
'); + $sUrl = utils::GetAbsoluteUrlAppRoot(); + + $oPage->add('
Layout:
'); + $oPage->add('
'); + foreach( get_declared_classes() as $sLayoutClass) + { + if (is_subclass_of($sLayoutClass, 'DashboardLayout')) + { + $oReflection = new ReflectionClass($sLayoutClass); + if (!$oReflection->isAbstract()) + { + $aInfo = $sLayoutClass::GetInfo(); + $oPage->add(''); // title="" on either the img or the label does nothing ! + } + } + } + $oPage->add('
'); + + $oPage->add('
'); + $oPage->add_ready_script("$('#select_layout').buttonset();"); + } + + public function RenderDashletsSelection($oPage) + { + // Toolbox/palette to drag and drop dashlets + $oPage->add('
Available 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()) + { + $aInfo = $sDashletClass::GetInfo(); + $oPage->add(''); + } + } + } + $oPage->add('
'); + + $oPage->add('
'); + $oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});"); + $oPage->add_ready_script("$('.layout_cell').droppable({accept:'.dashlet_icon', hoverClass:'dragHover'});"); + } + + public function RenderDashletsProperties($oPage) + { + // Toolbox/palette to edit the properties of each dashlet + $oPage->add('
Dashlet Properties
'); + + $oPage->add('
'); + $oPage->p('Not yet implemented'); + $oPage->add('
'); + + $oPage->add('
'); + } +} + +class RuntimeDashboard extends Dashboard +{ + public function Save() + { + + } + + public function Render($oPage, $bEditMode = false, $aExtraParams = array()) + { + parent::Render($oPage, $bEditMode, $aExtraParams); + if (!$bEditMode) + { + $sEditBtn = addslashes('
'); + $oPage->add_ready_script("$('#top-bar').prepend('$sEditBtn');"); + $oPage->add_script( +<<add('
'); + $oPage->add('
'); + $this->Render($oPage, true); + $oPage->add('
'); + $oPage->add('
'); + $this->RenderProperties($oPage); + $this->RenderDashletsSelection($oPage); + $this->RenderDashletsProperties($oPage); + $oPage->add('
'); + $oPage->add('
'); + $sDialogTitle = 'Dashboard Editor'; + $sOkButtonLabel = Dict::S('UI:Button:Ok'); + $sCancelButtonLabel = Dict::S('UI:Button:Cancel'); + $oPage->add_ready_script( +<<add_ready_script("$('#dashboard_editor').layout();"); + } +} \ No newline at end of file diff --git a/application/dashboardlayout.class.inc.php b/application/dashboardlayout.class.inc.php new file mode 100644 index 000000000..3eb30cf76 --- /dev/null +++ b/application/dashboardlayout.class.inc.php @@ -0,0 +1,120 @@ + '', + 'icon' => '', + 'description' => '', + ); + } +} + +abstract class DashboardLayoutMultiCol extends DashboardLayout +{ + protected $iNbCols; + + public function __construct() + { + $this->iNbCols = 1; + } + + public function Render($oPage, $aDashlets, $bEditMode = false, $aExtraParams = array()) + { + $oPage->add(''); + $iDashletIdx = 0; + $sStyle = $bEditMode ? 'style="border: 1px #ccc dashed;" class="layout_cell edit_mode"' : ''; + $iNbRows = ceil(count($aDashlets) / $this->iNbCols); + for($iRows = 0; $iRows < $iNbRows; $iRows++) + { + $oPage->add(''); + for($iCols = 0; $iCols < $this->iNbCols; $iCols++) + { + $oPage->add("'); + $iDashletIdx++; + } + $oPage->add(''); + } + if ($bEditMode) // Add one row for extensibility + { + $oPage->add(''); + for($iCols = 0; $iCols < $this->iNbCols; $iCols++) + { + $oPage->add("'); + } + $oPage->add(''); + } + $oPage->add('
"); + if ($iDashletIdx <= count($aDashlets)) + { + $oDashlet = $aDashlets[$iDashletIdx]; + $oDashlet->Render($oPage, $bEditMode, $aExtraParams); + } + else + { + $oPage->add(' '); + } + $oPage->add('
"); + $oPage->add(' '); + $oPage->add('
'); + } +} + +class DashboardLayoutOneCol extends DashboardLayoutMultiCol +{ + public function __construct() + { + parent::__construct(); + $this->iNbCols = 1; + } + static public function GetInfo() + { + return array( + 'label' => 'One Column', + 'icon' => 'images/layout_1col.png', + 'description' => '', + ); + } +} + +class DashboardLayoutTwoCols extends DashboardLayoutMultiCol +{ + public function __construct() + { + parent::__construct(); + $this->iNbCols = 2; + } + static public function GetInfo() + { + return array( + 'label' => 'Two Columns', + 'icon' => 'images/layout_2col.png', + 'description' => '', + ); + } +} + +class DashboardLayoutThreeCols extends DashboardLayoutMultiCol +{ + public function __construct() + { + parent::__construct(); + $this->iNbCols = 3; + } + static public function GetInfo() + { + return array( + 'label' => 'Two Columns', + 'icon' => 'images/layout_3col.png', + 'description' => '', + ); + } +} \ No newline at end of file diff --git a/application/dashlet.class.inc.php b/application/dashlet.class.inc.php new file mode 100644 index 000000000..2d4594842 --- /dev/null +++ b/application/dashlet.class.inc.php @@ -0,0 +1,228 @@ + '', + 'icon' => '', + 'description' => '', + ); + } +} + +class DashletHelloWorld extends Dashlet +{ + public function __construct() + { + + } + + public function FromDOMNode($oDOMNode) + { + + } + + public function FromXml($sXml) + { + + } + + public function FromParams($aParams) + { + + } + + public function Render($oPage, $bEditMode = false, $aExtraParams = array()) + { + $oPage->add('
'); + $oPage->add('
Hello World!
'); + $oPage->add('
'); + } + + public function ToXml(DOMNode $oContainerNode) + { + $oNewNodeNode = $oContainerNode->ownerDocument->createElement('hello_world', 'test'); + $oContainerNode->appendChild($oNewNodeNode); + } + + public function GetForm() + { + + } + + public function OnFieldUpdate($aParams, $sUpdatedFieldCode) + { + return array( + 'status_ok' => true, + 'redraw' => false, + 'fields' => array(), + ); + } + + static public function GetInfo() + { + return array( + 'label' => 'Hello World', + 'icon' => 'images/dashlet-text.png', + 'description' => 'Hello World test Dashlet', + ); + } +} + + +class DashletFakeBarChart extends Dashlet +{ + public function __construct() + { + + } + + public function FromDOMNode($oDOMNode) + { + + } + + public function FromXml($sXml) + { + + } + + public function FromParams($aParams) + { + + } + + public function Render($oPage, $bEditMode = false, $aExtraParams = array()) + { + $oPage->add('
'); + $oPage->add('
Fake Bar Chart
'); + $oPage->add(''); + } + + public function ToXml(DOMNode $oContainerNode) + { + $oNewNodeNode = $oContainerNode->ownerDocument->createElement('fake_bar_chart', 'test'); + $oContainerNode->appendChild($oNewNodeNode); + } + + public function GetForm() + { + + } + + public function OnFieldUpdate($aParams, $sUpdatedFieldCode) + { + return array( + 'status_ok' => true, + 'redraw' => false, + 'fields' => array(), + ); + } + + static public function GetInfo() + { + return array( + 'label' => 'Bar Chart', + 'icon' => 'images/dashlet-bar-chart.png', + 'description' => 'Fake Bar Chart (for testing)', + ); + } +} + + +class DashletFakePieChart extends Dashlet +{ + public function __construct() + { + + } + + public function FromDOMNode($oDOMNode) + { + + } + + public function FromXml($sXml) + { + + } + + public function FromParams($aParams) + { + + } + + public function Render($oPage, $bEditMode = false, $aExtraParams = array()) + { + $oPage->add('
'); + $oPage->add('
Fake Pie Chart
'); + $oPage->add('
'); + } + + public function ToXml(DOMNode $oContainerNode) + { + $oNewNodeNode = $oContainerNode->ownerDocument->createElement('fake_pie_chart', 'test'); + $oContainerNode->appendChild($oNewNodeNode); + } + + public function GetForm() + { + + } + + public function OnFieldUpdate($aParams, $sUpdatedFieldCode) + { + return array( + 'status_ok' => true, + 'redraw' => false, + 'fields' => array(), + ); + } + + static public function GetInfo() + { + return array( + 'label' => 'Pie Chart', + 'icon' => 'images/dashlet-pie-chart.png', + 'description' => 'Fake Pie Chart (for testing)', + ); + } +} \ No newline at end of file diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 73ef30a34..ce2368056 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -771,4 +771,67 @@ class NewObjectMenuNode extends MenuNode assert(false); // Shall never be called, the external web page will handle the display by itself } } -?> + +require_once(APPROOT.'application/dashboard.class.inc.php'); +/** + * This class defines a menu item which content is based on XML dashboard. + */ +class DashboardMenuNode extends MenuNode +{ + protected $sDashboardFile; + + /** + * Create a menu item based on a custom template and inserts it into the application's main menu + * @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary) + * @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content + * @param integer $iParentIndex ID of the parent menu + * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value + * @param string $sEnableClass Name of class of object + * @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE + * @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them... + * @return MenuNode + */ + public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null) + { + parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus); + $this->sDashboardFile = $sDashboardFile; + $this->aReflectionProperties['dashboard_file'] = $sDashboardFile; + } + + public function GetHyperlink($aExtraParams) + { + if ($this->sDashboardFile == '') return ''; + return parent::GetHyperlink($aExtraParams); + } + + public function RenderContent(WebPage $oPage, $aExtraParams = array()) + { + $sDashboardDefinition = @file_get_contents($this->sDashboardFile); + if ($sDashboardDefinition !== false) + { + $oDashboard = new RuntimeDashboard($this->sMenuId); + $oDashboard->FromXml($sDashboardDefinition); + $oDashboard->Render($oPage, false, $aExtraParams); + } + else + { + $oPage->p("Error: failed to load template file: '{$this->sDashboardFile}'"); // No need to translate ? + } + } + + public function RenderEditor(WebPage $oPage) + { + $sDashboardDefinition = @file_get_contents($this->sDashboardFile); + if ($sDashboardDefinition !== false) + { + $oDashboard = new RuntimeDashboard($this->sMenuId); + $oDashboard->FromXml($sDashboardDefinition); + $oDashboard->RenderEditor($oPage); + } + else + { + $oPage->p("Error: failed to load template file: '{$this->sDashboardFile}'"); // No need to translate ? + } + } +} +