소스 검색

User editable dashboards... implementation in progress

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2040 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 13 년 전
부모
커밋
5c0317c777

+ 117 - 2
application/dashboard.class.inc.php

@@ -156,8 +156,11 @@ abstract class Dashboard
 		$this->sTitle = $sTitle;
 	}
 		
-	public function AddDashlet()
+	public function AddDashlet($oDashlet)
 	{
+		$sId = $this->GetNewDashletId();
+		$oDashlet->SetId($sId);
+		$this->aCells[] = array($oDashlet);
 	}
 	
 	public function Render($oPage, $bEditMode = false, $aExtraParams = array())
@@ -169,7 +172,6 @@ abstract class Dashboard
 		{
 			$oPage->add_linked_script('../js/dashlet.js');
 			$oPage->add_linked_script('../js/dashboard.js');
-			$oPage->add_linked_script('../js/property_field.js');
 		}
 	}
 	
@@ -279,6 +281,11 @@ EOF
 		$oPage->add('</div>');
 	}
 	
+	protected function GetNewDashletId()
+	{
+		return 999;
+	}
+	
 	abstract protected function SetFormParams($oForm);
 }
 
@@ -483,4 +490,112 @@ EOF
 		);
 		$oPage->add_ready_script("");
 	}
+	
+	public static function GetDashletCreationForm($sOQL)
+	{
+		$oForm = new DesignerForm();
+	
+		// Get the list of all 'dashboard' menus in which we can insert a dashlet
+		$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
+		$aAllowedDashboards = array();
+		foreach($aAllMenus as $idx => $aMenu)
+		{
+			$oMenu = $aMenu['node'];
+			if ($oMenu instanceof DashboardMenuNode)
+			{
+				$aAllowedDashboards[$oMenu->GetMenuId()] = Dict::S($oMenu->GetMenuId());
+			}
+		}
+		
+		$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
+		$sDefaultDashboard = $aKeys[0];
+		$oField = new DesignerComboField('menu_id', 'Dashboard', $sDefaultDashboard);
+		$oField->SetAllowedValues($aAllowedDashboards);
+		$oField->SetMandatory(true);
+		$oForm->AddField($oField);
+				
+		// Get the list of possible dashlets that support a creation from
+		// an OQL
+		$aDashlets = array();
+		foreach( get_declared_classes() as $sDashletClass)
+		{
+			if (is_subclass_of($sDashletClass, 'Dashlet'))
+			{
+				$oReflection = new ReflectionClass($sDashletClass);
+				if (!$oReflection->isAbstract())
+				{
+					$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
+					$bShorcutMode = call_user_func($aCallSpec);
+					if ($bShorcutMode)
+					{
+						$aCallSpec = array($sDashletClass, 'GetInfo');
+						$aInfo = call_user_func($aCallSpec);
+						$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
+					}
+				}
+			}
+		}
+		
+		$oSelectorField = new DesignerFormSelectorField('dashlet_class', 'Dashlet Type', '');
+		$oForm->AddField($oSelectorField);
+		foreach($aDashlets as $sDashletClass => $aDashletInfo)
+		{
+			$oSubForm = new DesignerForm();
+			$oDashlet = new $sDashletClass(0);
+			$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
+			
+			$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
+		}
+		$oField = new DesignerBooleanField('open_editor', 'Edit the Dashboard', true);
+		$oForm->AddField($oField);
+		
+		return $oForm;
+	}
+	
+	public static function GetDashletCreationDlgFromOQL($oPage, $sOQL)
+	{
+		$oPage->add('<div id="dashlet_creation_dlg">');
+
+		$oForm = self::GetDashletCreationForm($sOQL);
+		
+		$oForm->Render($oPage);
+		$oPage->add('</div>');
+		
+		$sDialogTitle = 'Create a new Dashlet';
+		$sOkButtonLabel = Dict::S('UI:Button:Ok');
+		$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
+		
+		$oPage->add_ready_script(
+<<<EOF
+$('#dashlet_creation_dlg').dialog({
+	width: 400,
+	modal: true,
+	title: '$sDialogTitle',
+	buttons: [
+	{ text: "$sOkButtonLabel", click: function() {
+		var oForm = $(this).find('form');
+		var sFormId = oForm.attr('id');
+		var oParams = null;
+		var aErrors = ValidateForm(sFormId, false);
+		if (aErrors.length == 0)
+		{
+			oParams = ReadFormParams(sFormId);
+		}
+		oParams.operation = 'add_dashlet';
+		var me = $(this);
+		$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
+			me.dialog( "close" );
+			me.remove();
+			$('body').append(data);
+		});
+	} },
+	{ text: "$sCancelButtonLabel", click: function() {
+		$(this).dialog( "close" ); $(this).remove();
+	} },
+	],
+	close: function() { $(this).remove(); }
+});
+EOF
+		);
+	}
 }

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

@@ -134,6 +134,11 @@ EOF
 		}
 	}
 	
+	public function SetID($sId)
+	{
+		$this->sId = $sId;
+	}
+	
 	public function GetID()
 	{
 		return $this->sId;
@@ -201,6 +206,16 @@ EOF
 	{
 		return true;
 	}
+	
+	static public function CanCreateFromOQL()
+	{
+		return false;
+	}
+	
+	public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
+	{
+		// Default: do nothing since it's not supported
+	}
 }
 
 class DashletEmptyCell extends Dashlet
@@ -311,6 +326,23 @@ class DashletObjectList extends Dashlet
 			'description' => 'Object list dashlet',
 		);
 	}
+	
+	static public function CanCreateFromOQL()
+	{
+		return true;
+	}
+	
+	public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
+	{
+		$oField = new DesignerTextField('title', 'Title', '');
+		$oForm->AddField($oField);
+
+		$oField = new DesignerHiddenField('query', 'Query', $sOQL);
+		$oForm->AddField($oField);
+
+		$oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
+		$oForm->AddField($oField);
+	}
 }
 
 abstract class DashletGroupBy extends Dashlet
@@ -474,6 +506,45 @@ abstract class DashletGroupBy extends Dashlet
 			'description' => 'Grouped objects dashlet',
 		);
 	}
+	
+	static public function CanCreateFromOQL()
+	{
+		return true;
+	}
+	
+	public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
+	{
+		$oField = new DesignerTextField('title', 'Title', '');
+		$oForm->AddField($oField);
+
+		$oField = new DesignerHiddenField('query', 'Query', $sOQL);
+		$oForm->AddField($oField);
+
+		// Group by field: build the list of possible values (attribute codes + ...)
+		$oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
+		$sClass = $oSearch->GetClass();
+		$aGroupBy = array();
+		foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
+		{
+			if (!$oAttDef->IsScalar()) continue; // skip link sets
+			if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) continue; // skip external keys
+			$aGroupBy[$sAttCode] = $oAttDef->GetLabel();
+
+			if ($oAttDef instanceof AttributeDateTime)
+			{
+				//date_format(start_date, '%d')
+				$aGroupBy['date_of_'.$sAttCode] = 'Day of '.$oAttDef->GetLabel();
+			}
+
+		}
+
+		$oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
+		$oField->SetAllowedValues($aGroupBy);
+		$oForm->AddField($oField);
+
+		$oField = new DesignerHiddenField('style', '', $this->aProperties['style']);
+		$oForm->AddField($oField);
+	}
 }
 
 class DashletGroupByPie extends DashletGroupBy

+ 7 - 2
application/displayblock.class.inc.php

@@ -1067,7 +1067,7 @@ class MenuBlock extends DisplayBlock
 		$sClass = $this->m_oFilter->GetClass();
 		$oSet = new CMDBObjectSet($this->m_oFilter);
 		$sFilter = $this->m_oFilter->serialize();
-		$sFilterDesc = $this->m_oFilter->ToOql();
+		$sFilterDesc = $this->m_oFilter->ToOql(true);
 		$aActions = array();
 		$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
 		$sRootUrl = utils::GetAbsoluteUrlAppRoot();
@@ -1140,6 +1140,8 @@ class MenuBlock extends DisplayBlock
 				$sUrl = ApplicationContext::MakeObjectUrl($sClass, $id);
 				$aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl));
 				$aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=$sFilter&format=csv{$sContext}");
+				$sOQL = addslashes($sFilterDesc);
+				$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
 			}
 			$this->AddMenuSeparator($aActions);
 			foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
@@ -1219,6 +1221,8 @@ class MenuBlock extends DisplayBlock
 				$sUrl = utils::GetAbsoluteUrlAppRoot();
 				$aActions['UI:Menu:EMail'] = array ('label' => Dict::S('UI:Menu:EMail'), 'url' => "mailto:?subject=$sFilterDesc&body=".urlencode("{$sUrl}pages/$sUIPage?operation=search&filter=$sFilter{$sContext}"));
 				$aActions['UI:Menu:CSVExport'] = array ('label' => Dict::S('UI:Menu:CSVExport'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=search&filter=$sFilter&format=csv{$sContext}");
+				$sOQL = addslashes($sFilterDesc);
+				$aActions['UI:Menu:AddToDashboard'] = array ('label' => Dict::S('UI:Menu:AddToDashboard'), 'url' => "#", 'onclick' => "return DashletCreationDlg('$sOQL')");
 			}
 			$this->AddMenuSeparator($aActions);
 			foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance)
@@ -1267,6 +1271,7 @@ class MenuBlock extends DisplayBlock
 			else
 			{
 				$sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : "";
+				$sOnClick = isset($aAction['onclick']) ? " onclick=\"{$aAction['onclick']}\"" : "";
 				if (empty($aAction['url']))
 				{
 					if ($sPrevUrl != '') // Don't output consecutively two separators...
@@ -1277,7 +1282,7 @@ class MenuBlock extends DisplayBlock
 				}
 				else
 				{
-					$sHtml .= "<li><a href=\"{$aAction['url']}\"$sClass>{$aAction['label']}</a></li>\n";
+					$sHtml .= "<li><a href=\"{$aAction['url']}\"$sClass $sOnClick>{$aAction['label']}</a></li>\n";
 					$sPrevUrl = $aAction['url'];
 				}
 			}

+ 2 - 0
application/itopwebpage.class.inc.php

@@ -68,6 +68,8 @@ class iTopWebPage extends NiceWebPage
 		$this->add_linked_script("../js/ckeditor/ckeditor.js");
 		$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
 		$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
+		$this->add_linked_script('../js/property_field.js');
+		
 		$this->m_sInitScript =
 <<< EOF
 	try

+ 22 - 0
application/menunode.class.inc.php

@@ -842,6 +842,13 @@ class DashboardMenuNode extends MenuNode
 		if ($oDashboard != null)
 		{
 			$oDashboard->Render($oPage, false, $aExtraParams);
+			
+			$bEdit = utils::ReadParam('edit', false);
+			if ($bEdit)
+			{
+				$sId = addslashes($this->sMenuId);
+				$oPage->add_ready_script("EditDashboard('$sId');");
+			}
 		}
 		else
 		{
@@ -861,5 +868,20 @@ class DashboardMenuNode extends MenuNode
 			$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
 		}
 	}
+	
+	public function AddDashlet($oDashlet)
+	{
+		$oDashboard = $this->GetDashboard();
+		if ($oDashboard != null)
+		{
+			$oDashboard->AddDashlet($oDashlet);
+			$oDashboard->Save();
+		}
+		else
+		{
+			$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
+		}
+	}
+	
 }