Explorar o código

User editable dashboards... implementation in progress

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1992 a333f486-631f-4898-b8df-5754b55c2be0
dflaven %!s(int64=13) %!d(string=hai) anos
pai
achega
3b1ba773df

+ 216 - 0
application/dashboard.class.inc.php

@@ -0,0 +1,216 @@
+<?php
+require_once(APPROOT.'application/dashboardlayout.class.inc.php');
+require_once(APPROOT.'application/dashlet.class.inc.php');
+
+abstract class Dashboard
+{
+	protected $sTitle;
+	protected $sLayoutClass;
+	protected $aWidgetsData;
+	protected $oDOMNode;
+	protected $sId;
+	
+	public function __construct($sId)
+	{
+		$this->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('<h1>'.$this->sTitle.'</h1>');
+		$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('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">Dashboard Properties</div>');
+		$sUrl = utils::GetAbsoluteUrlAppRoot();
+		
+		$oPage->add('<div style="text-align:center">Layout:</div>');
+		$oPage->add('<div id="select_layout" style="text-align:center">');
+		foreach( get_declared_classes() as $sLayoutClass)
+		{
+			if (is_subclass_of($sLayoutClass, 'DashboardLayout'))
+			{
+				$oReflection = new ReflectionClass($sLayoutClass);
+				if (!$oReflection->isAbstract())
+				{
+					$aInfo = $sLayoutClass::GetInfo();
+					$oPage->add('<input type="radio" name="layout_class" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" /></label>'); // title="" on either the img or the label does nothing !
+				}
+			}
+		}
+		$oPage->add('</div>');
+		
+		$oPage->add('</div>');
+		$oPage->add_ready_script("$('#select_layout').buttonset();");
+	}
+	
+	public function RenderDashletsSelection($oPage)
+	{
+		// Toolbox/palette to drag and drop dashlets
+		$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">Available Dashlets</div>');
+		$sUrl = utils::GetAbsoluteUrlAppRoot();
+
+		$oPage->add('<div id="select_dashlet" style="text-align:center">');
+		foreach( get_declared_classes() as $sDashletClass)
+		{
+			if (is_subclass_of($sDashletClass, 'Dashlet'))
+			{
+				$oReflection = new ReflectionClass($sDashletClass);
+				if (!$oReflection->isAbstract())
+				{
+					$aInfo = $sDashletClass::GetInfo();
+					$oPage->add('<span class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
+				}
+			}
+		}
+		$oPage->add('</div>');
+
+		$oPage->add('</div>');
+		$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('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">Dashlet Properties</div>');
+
+		$oPage->add('<div id="dashlet_properties" style="text-align:center">');
+		$oPage->p('Not yet implemented');
+		$oPage->add('</div>');
+
+		$oPage->add('</div>');
+	}
+}
+
+class RuntimeDashboard extends Dashboard
+{
+	public function Save()
+	{
+		
+	}
+	
+	public function Render($oPage, $bEditMode = false, $aExtraParams = array())
+	{
+		parent::Render($oPage, $bEditMode, $aExtraParams);
+		if (!$bEditMode)
+		{
+			$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
+			$oPage->add_ready_script("$('#top-bar').prepend('$sEditBtn');");
+			$oPage->add_script(
+<<<EOF
+function EditDashboard(sId)
+{
+	console.log('Ici');
+	$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'dashboard_editor', id: sId},
+		function(data)
+		{
+			$('body').append(data);
+		}
+	);
+	return false;
+}
+EOF
+			);
+		}
+	}
+	
+	public function RenderEditor($oPage)
+	{
+		$oPage->add('<div id="dashboard_editor">');
+		$oPage->add('<div class="ui-layout-center">');
+		$this->Render($oPage, true);
+		$oPage->add('</div>');
+		$oPage->add('<div class="ui-layout-east">');
+		$this->RenderProperties($oPage);
+		$this->RenderDashletsSelection($oPage);
+		$this->RenderDashletsProperties($oPage);
+		$oPage->add('</div>');
+		$oPage->add('</div>');
+		$sDialogTitle = 'Dashboard Editor';
+		$sOkButtonLabel = Dict::S('UI:Button:Ok');
+		$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
+		$oPage->add_ready_script(
+<<<EOF
+$('#dashboard_editor').dialog({
+	height: $('body').height() - 50,
+	width: $('body').width() - 50,
+	modal: true,
+	title: '$sDialogTitle',
+	buttons: [
+	{ text: "$sOkButtonLabel", click: function() {
+		$(this).dialog( "close" ); $(this).remove();
+	} },
+	{ text: "$sCancelButtonLabel", click: function() { $(this).dialog( "close" ); $(this).remove(); } },
+	],
+	close: function() { $(this).remove(); }
+});
+EOF
+		);
+		$oPage->add_ready_script("$('#dashboard_editor').layout();");
+	}
+}

+ 120 - 0
application/dashboardlayout.class.inc.php

@@ -0,0 +1,120 @@
+<?php
+abstract class DashboardLayout
+{
+	public function __construct()
+	{
+		
+	}
+	
+	abstract public function Render($oPage, $aDashlets, $bEditMode = false);
+	
+	static public function GetInfo()
+	{
+		return array(
+			'label' => '',
+			'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('<table style="width:100%"><tbody>');
+		$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('<tr>');
+			for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
+			{
+				$oPage->add("<td $sStyle>");
+				if ($iDashletIdx <= count($aDashlets))
+				{
+					$oDashlet = $aDashlets[$iDashletIdx];
+					$oDashlet->Render($oPage, $bEditMode, $aExtraParams);
+				}
+				else
+				{
+					$oPage->add('&nbsp;');
+				}
+				$oPage->add('</td>');
+				$iDashletIdx++;
+			}
+			$oPage->add('</tr>');
+		}
+		if ($bEditMode) // Add one row for extensibility
+		{
+			$oPage->add('<tr>');
+			for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
+			{
+				$oPage->add("<td $sStyle>");
+				$oPage->add('&nbsp;');
+				$oPage->add('</td>');
+			}
+			$oPage->add('</tr>');
+		}
+		$oPage->add('</tbody></table>');
+	}
+}
+
+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' => '',
+		);
+	}
+}

+ 228 - 0
application/dashlet.class.inc.php

@@ -0,0 +1,228 @@
+<?php
+abstract class Dashlet
+{
+	public function __construct()
+	{
+		
+	}
+	
+	public function FromDOMNode($oDOMNode)
+	{
+		
+	}
+	
+	public function FromXml($sXml)
+	{
+		
+	}
+	
+	public function FromParams($aParams)
+	{
+		
+	}
+	
+	abstract public function Render($oPage, $bEditMode = false, $aExtraParams = array());
+	
+	public function ToXml(DOMNode $oContainerNode)
+	{
+		
+	}
+	
+	public function GetForm()
+	{
+		
+	}
+	
+	public function OnFieldUpdate($aParams, $sUpdatedFieldCode)
+	{
+		
+	}
+	
+	static public function GetInfo()
+	{
+		return array(
+			'label' => '',
+			'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('<div class="dashlet">');
+		$oPage->add('<div style="text-align:center; line-height:5em" class="dashlet-content"><span>Hello World!</span></div>');
+		$oPage->add('</div>');
+	}
+	
+	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('<div class="dashlet">');
+		$oPage->add('<div style="text-align:center" class="dashlet-content"><div>Fake Bar Chart</div><divp><img src="../images/fake-bar-chart.png"/></div></div>');
+		$oPage->add('</div>');
+	}
+	
+	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('<div class="dashlet">');
+		$oPage->add('<div style="text-align:center" class="dashlet-content"><div>Fake Pie Chart</div><div><img src="../images/fake-pie-chart.png"/></div></div>');
+		$oPage->add('</div>');
+	}
+	
+	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)',
+		);
+	}
+}

+ 64 - 1
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 ?
+		}
+	}
+}
+