User editable dashboards... implementation in progress

SVN:trunk[2030]
This commit is contained in:
Denis Flaven
2012-05-25 09:20:38 +00:00
parent d71a663208
commit d681e58671
5 changed files with 258 additions and 60 deletions

View File

@@ -28,11 +28,12 @@ abstract class Dashboard
protected $aWidgetsData;
protected $oDOMNode;
protected $sId;
protected $aCells;
public function __construct($sId)
{
$this->sLayoutClass = null;
$this->aDashlets = array();
$this->aCells = array();
$this->oDOMNode = null;
$this->sId = $sId;
}
@@ -49,15 +50,21 @@ abstract class Dashboard
$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)
$oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0);
$oCellsList = $oCellsNode->getElementsByTagName('cell');
foreach($oCellsList as $oCellNode)
{
$sDashletClass = $oDomNode->getAttribute('xsi:type');
$sId = $oDomNode->getAttribute('id');
$oNewDashlet = new $sDashletClass($sId);
$oNewDashlet->FromDOMNode($oDomNode);
$this->aDashlets[] = $oNewDashlet;
$aDashletList = array();
$oDashletList = $oCellNode->getElementsByTagName('dashlet');
foreach($oDashletList as $oDomNode)
{
$sDashletClass = $oDomNode->getAttribute('xsi:type');
$sId = $oDomNode->getAttribute('id');
$oNewDashlet = new $sDashletClass($sId);
$oNewDashlet->FromDOMNode($oDomNode);
$aDashletList[] = $oNewDashlet;
}
$this->aCells[] = $aDashletList;
}
}
@@ -77,16 +84,21 @@ abstract class Dashboard
$oNode = $oDoc->createElement('title', $this->sTitle);
$oMainNode->appendChild($oNode);
$oDashletsNode = $oDoc->createElement('dashlets');
$oMainNode->appendChild($oDashletsNode);
$oCellsNode = $oDoc->createElement('cells');
$oMainNode->appendChild($oCellsNode);
foreach ($this->aDashlets as $oDashlet)
foreach ($this->aCells as $aCell)
{
$oNode = $oDoc->createElement('dashlet');
$oDashletsNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
$oNode->setAttribute('xsi:type', get_class($oDashlet));
$oDashlet->ToDOMNode($oNode);
$oCellNode = $oDoc->createElement('cell');
$oCellsNode->appendChild($oCellNode);
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
$oCellNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
$oNode->setAttribute('xsi:type', get_class($oDashlet));
$oDashlet->ToDOMNode($oNode);
}
}
$sXml = $oDoc->saveXML();
@@ -98,18 +110,23 @@ abstract class Dashboard
$this->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title'];
foreach($aParams['dashlets'] as $aDashletParams)
foreach($aParams['cells'] as $aCell)
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
$oNewDashlet = new $sDashletClass($sId);
$oForm = $oNewDashlet->GetForm();
$oForm->SetParamsContainer($sId);
$oForm->SetPrefix('');
$aValues = $oForm->ReadParams();
$oNewDashlet->FromParams($aValues);
$this->aDashlets[] = $oNewDashlet;
$aCellDashlets = array();
foreach($aCell as $aDashletParams)
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
$oNewDashlet = new $sDashletClass($sId);
$oForm = $oNewDashlet->GetForm();
$oForm->SetParamsContainer($sId);
$oForm->SetPrefix('');
$aValues = $oForm->ReadParams();
$oNewDashlet->FromParams($aValues);
$aCellDashlets[] = $oNewDashlet;
}
$this->aCells[] = $aCellDashlets;
}
}
@@ -138,7 +155,7 @@ abstract class Dashboard
{
$this->sTitle = $sTitle;
}
public function AddDashlet()
{
}
@@ -147,7 +164,7 @@ abstract class Dashboard
{
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aDashlets, $bEditMode, $aExtraParams);
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
$oPage->add_linked_script('../js/dashlet.js');
@@ -173,7 +190,8 @@ abstract class Dashboard
{
$aCallSpec = array($sLayoutClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$oPage->add('<input type="radio" name="layout_class" value="'.$sLayoutClass.'" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" /></label>'); // title="" on either the img or the label does nothing !
$sChecked = ($this->sLayoutClass == $sLayoutClass) ? 'checked' : '';
$oPage->add('<input type="radio" name="layout_class" '.$sChecked.' value="'.$sLayoutClass.'" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" /></label>'); // title="" on either the img or the label does nothing !
}
}
}
@@ -229,17 +247,20 @@ EOF
$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">');
foreach($this->aDashlets as $oDashlet)
foreach($this->aCells as $aCell)
{
$sId = $oDashlet->GetID();
$sClass = get_class($oDashlet);
if ($oDashlet->IsVisible())
foreach($aCell as $oDashlet)
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage);
$oPage->add('</div>');
$sId = $oDashlet->GetID();
$sClass = get_class($oDashlet);
if ($oDashlet->IsVisible())
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage);
$oPage->add('</div>');
}
}
}
$oPage->add('</div>');
@@ -252,6 +273,19 @@ EOF
class RuntimeDashboard extends Dashboard
{
protected $bCustomized;
public function __construct($sId)
{
parent::__construct($sId);
$this->bCustomized = false;
}
public function SetCustomFlag($bCustomized)
{
$this->bCustomized = $bCustomized;
}
protected function SetFormParams($oForm)
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property'));
@@ -284,13 +318,41 @@ class RuntimeDashboard extends Dashboard
}
}
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();
$oUserDashboard->DBDelete();
}
}
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');");
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
$sEditMenu .= "<li><a href=\"#\" onclick=\"return EditDashboard('{$this->sId}')\">Edit This Page</a></li>";
if ($this->bCustomized)
{
$sEditMenu .= "<li><a href=\"#\" onclick=\"return RevertDashboard('{$this->sId}')\">Revert To Original Version</a></li>";
}
$sEditMenu .= "</ul></li></ul></span></td>";
$sEditMenu = addslashes($sEditMenu);
//$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(
<<<EOF
$('#logOffBtn').parent().before('$sEditMenu');
$('#DashboardMenu>ul').popupmenu();
EOF
);
$oPage->add_script(
<<<EOF
function EditDashboard(sId)
@@ -303,6 +365,16 @@ function EditDashboard(sId)
);
return false;
}
function RevertDashboard(sId)
{
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {operation: 'revert_dashboard', dashboard_id: sId},
function(data)
{
$('body').append(data);
}
);
return false;
}
EOF
);
}
@@ -380,8 +452,24 @@ $('#event_bus').bind('dashlet-selected', function(event, data){
});
});
dashboard_prop_size = GetUserPreference('dashboard_prop_size', 300);
$('#dashboard_editor').layout({
east: {
minSize: 150,
size: dashboard_prop_size,
onresize_end: function(name, elt, state, options, layout)
{
if (state.isSliding == false)
{
SetUserPreference('dashboard_prop_size', state.size, true);
}
},
}
});
EOF
);
$oPage->add_ready_script("$('#dashboard_editor').layout({ east__size: 300 });");
$oPage->add_ready_script("");
}
}

View File

@@ -27,9 +27,8 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$this->iNbCols = 1;
}
public function Render($oPage, $aDashlets, $bEditMode = false, $aExtraParams = array())
protected function TrimCell($aDashlets)
{
// Trim the list of dashlets to remove the invisible ones at the end of the array
$aKeys = array_reverse(array_keys($aDashlets));
$idx = 0;
$bNoVisibleFound = true;
@@ -46,24 +45,63 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
}
$idx++;
}
return $aDashlets;
}
protected function TrimCellsArray($aCells)
{
foreach($aCells as $key => $aDashlets)
{
$aCells[$key] = $this->TrimCell($aDashlets);
}
$aKeys = array_reverse(array_keys($aCells));
$idx = 0;
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
$aDashlets = $aCells[$aKeys[$idx]];
if (count($aDashlets) > 0)
{
$bNoVisibleFound = false;
}
else
{
unset($aCells[$aKeys[$idx]]);
}
$idx++;
}
return $aCells;
}
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
$oPage->add('<table style="width:100%"><tbody>');
$iDashletIdx = 0;
$iCellIdx = 0;
$fColSize = 100 / $this->iNbCols;
$sStyle = $bEditMode ? 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode"' : 'style="width: '.$fColSize.'%; "';
$iNbRows = ceil(count($aDashlets) / $this->iNbCols);
$sStyle = $bEditMode ? 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode"' : 'style="width: '.$fColSize.'%;" class="dashboard"';
$iNbRows = ceil(count($aCells) / $this->iNbCols);
for($iRows = 0; $iRows < $iNbRows; $iRows++)
{
$oPage->add('<tr>');
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$oPage->add("<td $sStyle>");
if (array_key_exists($iDashletIdx, $aDashlets))
if (array_key_exists($iCellIdx, $aCells))
{
$oDashlet = $aDashlets[$iDashletIdx];
if ($oDashlet->IsVisible())
$aDashlets = $aCells[$iCellIdx];
if (count($aDashlets) > 0)
{
$oDashlet->DoRender($oPage, $bEditMode, $aExtraParams);
foreach($aDashlets as $oDashlet)
{
if ($oDashlet->IsVisible())
{
$oDashlet->DoRender($oPage, $bEditMode, $aExtraParams);
}
}
}
else
{
@@ -75,7 +113,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$oPage->add('&nbsp;');
}
$oPage->add('</td>');
$iDashletIdx++;
$iCellIdx++;
}
$oPage->add('</tr>');
}

View File

@@ -26,6 +26,7 @@ abstract class Dashlet
protected $bRedrawNeeded;
protected $bFormRedrawNeeded;
protected $aProperties; // array of {property => value}
protected $aCSSClasses;
public function __construct($sId)
{
@@ -33,6 +34,7 @@ abstract class Dashlet
$this->bRedrawNeeded = true; // By default: redraw each time a property changes
$this->bFormRedrawNeeded = false; // By default: no need to redraw the form (independent fields)
$this->aProperties = array(); // By default: there is no property
$this->aCSSClasses = array('dashlet');
}
// Assuming that a property has the type of its default value, set in the constructor
@@ -109,14 +111,15 @@ abstract class Dashlet
public function DoRender($oPage, $bEditMode = false, $aExtraParams = array())
{
$sCSSClasses = implode(' ', $this->aCSSClasses);
if ($bEditMode)
{
$sId = $this->GetID();
$oPage->add('<div class="dashlet" id="dashlet_'.$sId.'">');
$oPage->add('<div class="'.$sCSSClasses.'" id="dashlet_'.$sId.'">');
}
else
{
$oPage->add('<div class="dashlet">');
$oPage->add('<div class="'.$sCSSClasses.'">');
}
$this->Render($oPage, $bEditMode, $aExtraParams);
$oPage->add('</div>');
@@ -293,7 +296,7 @@ class DashletObjectList extends Dashlet
$oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
$oForm->AddField($oField);
$oField = new DesignerTextField('query', 'Query', $this->aProperties['query']);
$oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
$oForm->AddField($oField);
$oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
@@ -379,7 +382,7 @@ abstract class DashletGroupBy extends Dashlet
$oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
$oForm->AddField($oField);
$oField = new DesignerTextField('query', 'Query', $this->aProperties['query']);
$oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
$oForm->AddField($oField);
// Group by field: build the list of possible values (attribute codes + ...)
@@ -575,11 +578,13 @@ class DashletHeader extends Dashlet
}
$oPage->add('<div style="text-align:center" class="dashlet-content">');
$oPage->add('<div class="main_header">');
$aParams = array();
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$oBlock = DisplayBlock::FromTemplate($sXML);
$oBlock->Display($oPage, $sBlockId, $aParams);
$oPage->add('</div>');
$oPage->add('</div>');
}
public function GetPropertiesFields(DesignerForm $oForm)
@@ -603,3 +608,43 @@ class DashletHeader extends Dashlet
);
}
}
class DashletBadge extends Dashlet
{
public function __construct($sId)
{
parent::__construct($sId);
$this->aProperties['class'] = 'Contact';
$this->aCSSClasses[] = 'dashlet-inline';
$this->aCSSClasses[] = 'dashlet-badge';
}
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sClass = $this->aProperties['class'];
$oPage->add('<div style="text-align:center" class="dashlet-content">');
$sXml = "<itopblock BlockClass=\"DisplayBlock\" type=\"actions\" asynchronous=\"false\" encoding=\"text/oql\" parameters=\"context_filter:1\">SELECT $sClass</itopblock>";
$oBlock = DisplayBlock::FromTemplate($sXml);
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
$oPage->add('</div>');
}
public function GetPropertiesFields(DesignerForm $oForm)
{
$oField = new DesignerTextField('class', 'Class', $this->aProperties['class']);
$oForm->AddField($oField);
}
static public function GetInfo()
{
return array(
'label' => 'Badge',
'icon' => 'images/dashlet-badge.png',
'description' => 'Object Icon with new/search',
);
}
}

View File

@@ -150,7 +150,7 @@ class DesignerForm
if ($this->oParentForm == null)
{
$sFormId = $this->sFormId;
$sReturn = '<form id="'.$sFormId.'">';
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
$sReturn .= '<table class="prop_table">';
$sReturn .= '<thead><tr><th class="prop_header">Property</th><th class="prop_header">Value</th><th colspan="2" class="prop_header">&nbsp;</th></tr></thead><tbody>';
}
@@ -627,6 +627,29 @@ EOF
}
}
class DesignerLongTextField extends DesignerTextField
{
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sValidation = $this->oForm->GetValidationArea($this->sCode);
$sPattern = addslashes($this->sValidationPattern);
$sMandatory = $this->bMandatory ? 'true' : 'false';
$sReadOnly = $this->IsReadOnly() ? 'readonly' : '';
$oP->add_ready_script(
<<<EOF
$('#$sId').bind('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', '$sFormId'); } );
{
var myTimer = null;
$('#$sId').bind('keyup', function() { clearTimeout(myTimer); myTimer = setTimeout(function() { $('#$sId').trigger('change', {} ); }, 100); });
}
EOF
);
return array('label' => $this->sLabel, 'value' => "<textarea id=\"$sId\" $sReadOnly name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>".$sValidation);
}
}
class DesignerComboField extends DesignerFormField
{
protected $aAllowedValues;

View File

@@ -807,9 +807,11 @@ class DashboardMenuNode extends MenuNode
protected function GetDashboard()
{
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
if ($sDashboardDefinition !== false)
{
$bCustomized = false;
// Search for an eventual user defined dashboard, overloading the existing one
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -820,10 +822,12 @@ class DashboardMenuNode extends MenuNode
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
$oDashboard = new RuntimeDashboard($this->sMenuId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
}
else
{