User editable dashboards... implementation in progress

SVN:trunk[2040]
This commit is contained in:
Denis Flaven
2012-05-25 15:59:24 +00:00
parent a40e902808
commit 2c00c115d6
5 changed files with 219 additions and 4 deletions

View File

@@ -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
);
}
}

View File

@@ -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

View File

@@ -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'];
}
}

View File

@@ -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

View File

@@ -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}'");
}
}
}