diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 66f1e85c2..81fe02719 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -39,6 +39,7 @@ require_once(APPROOT.'/application/ui.linkswidget.class.inc.php'); require_once(APPROOT.'/application/ui.passwordwidget.class.inc.php'); require_once(APPROOT.'/application/ui.extkeywidget.class.inc.php'); require_once(APPROOT.'/application/ui.htmleditorwidget.class.inc.php'); +require_once(APPROOT.'/application/datatable.class.inc.php'); /** * All objects to be displayed in the application (either as a list or as details) @@ -244,7 +245,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay // Display mode if (!$oAttDef->IsLinkset()) continue; // Process only linkset attributes... - $oSet = new DBObjectSet($this->Get($sAttCode)->GetFilter()); + // $oSet = new DBObjectSet($this->Get($sAttCode)->GetFilter()); // Why do something so useless ? + $oSet = $this->Get($sAttCode); $iCount = $oSet->Count(); $sCount = ''; if ($iCount != 0) @@ -679,7 +681,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay $sHtml = ''; $oAppContext = new ApplicationContext(); $sClassName = $oSet->GetFilter()->GetClass(); - $aAttribs = array(); $sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list'; if ($sZListName !== false) { @@ -702,9 +703,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay } } - // Load only the requested columns - $sClassAlias = $oSet->GetFilter()->GetClassAlias(); - $oSet->OptimizeColumnLoad(array($sClassAlias => $aList)); if (!empty($sLinkageAttribute)) { @@ -740,270 +738,45 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay // Then display all the attributes linked to the other end of the relationship $aList = $aDisplayList; } - if ($bSelectMode) - { - if (!$bSingleSelectMode) - { - $aAttribs['form::select'] = array('label' => "", 'description' => Dict::S('UI:SelectAllToggle+')); - } - else - { - $aAttribs['form::select'] = array('label' => "", 'description' => ''); - } - } - if ($bViewLink) - { - $aAttribs['key'] = array('label' => MetaModel::GetName($sClassName), 'description' => ''); - } - foreach($aList as $sAttCode) - { - $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); - $aAttribs[$sAttCode] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint()); - } - $aValues = array(); - $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; - $iMaxObjects = -1; - if ($bDisplayLimit && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())) - { - $iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit(); - $oSet->SetLimit($iMaxObjects); - } - $oSet->Seek(0); - while (($oObj = $oSet->Fetch()) && ($iMaxObjects != 0)) - { - $aRow = array(); - $sHilightClass = $oObj->GetHilightClass(); - if ($sHilightClass != '') - { - $aRow['@class'] = $sHilightClass; - } - if ($bViewLink) - { - $aRow['key'] = $oObj->GetHyperLink(); - } - if ($bSelectMode) - { - if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$oObj->GetKey()])) - { - $sDisabled = ($aExtraParams['selection_enabled'][$oObj->GetKey()]) ? '' : ' disabled="disabled"'; - } - else - { - $sDisabled = ''; - } - if ($bSingleSelectMode) - { - $aRow['form::select'] = "GetKey()."\">"; - } - else - { - $aRow['form::select'] = "GetKey()."\">"; - } - } - foreach($aList as $sAttCode) - { - $aRow[$sAttCode] = $oObj->GetAsHTML($sAttCode); - } - $aValues[] = $aRow; - $iMaxObjects--; - } - $sHtml .= ''; - $sColspan = ''; - - $sFilter = $oSet->GetFilter()->serialize(); - $iMinDisplayLimit = MetaModel::GetConfig()->GetMinDisplayLimit(); - $sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oSet->Count()); - $sLinkLabel = Dict::S('UI:DisplayAll'); - foreach($oSet->GetFilter()->GetInternalParams() as $sName => $sValue) - { - $aExtraParams['query_params'][$sName] = $sValue; - } - - if ($bDisplayMenu) - { - $oMenuBlock = new MenuBlock($oSet->GetFilter()); - $sColspan = 'colspan="2"'; - $aMenuExtraParams = $aExtraParams; - if (!empty($sLinkageAttribute)) - { - //$aMenuExtraParams['linkage'] = $sLinkageAttribute; - $aMenuExtraParams = $aExtraParams; - } - $sHtml .= $oMenuBlock->GetRenderContent($oPage, $aMenuExtraParams, $iListId); - $sHtml .= ''; - } - $sHtml .= "'; - $sHtml .= '
"; - $sHtml .= $oPage->GetTable($aAttribs, $aValues); - $sHtml .= '
'; - $iCount = $oSet->Count(); - if ($bSelectMode) - { - $sHeader = Dict::Format('UI:Pagination:HeaderSelection', ''.$iCount.'', '0'); - } - else - { - $sHeader = Dict::Format('UI:Pagination:HeaderNoSelection', ''.$iCount.''); - } - - // All lists are now paginated in order to benefit from the SQL sort order - - if (!$bDisplayLimit) - { - $sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI - $iPageSize = -1; // Display all - } - else - { - $sPagerStyle = ''; - $iPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); - } - $iDefaultPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); - $sCombo = ''; - $sPages = Dict::S('UI:Pagination:PagesLabel'); - $sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo); - $iNbPages = ($iPageSize == -1) ? 1 : ceil($iCount / $iPageSize); - $aPagesToDisplay = array(); - for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++) - { - if ($idx == 0) - { - $aPagesToDisplay[$idx] = '1'; - } - else - { - $aPagesToDisplay[$idx] = "".(1+$idx).""; - } - } - $iLastPageIdx = $iNbPages - 1; - if (!isset($aPagesToDisplay[$iLastPageIdx])) - { - unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page - $aPagesToDisplay[$iLastPageIdx] = "... $iNbPages"; - } - $sPagesLinks = implode('', $aPagesToDisplay); - $sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']'; - - $sSelectionMode = ($iNbPages == 1) ? '' : 'positive'; - $sHtml = -<< -

$sHeader

-

- - - - - - - -
$sPages$sPagesLinks$sPageSizeCombo 
- - - -EOF -.$sHtml; - $aArgs = $oSet->GetArgs(); - $sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them - $sSelectMode = ''; - $sHeaders = ''; + $sSelectMode = 'none'; if ($bSelectMode) { $sSelectMode = $bSingleSelectMode ? 'single' : 'multiple'; - $sHeaders = 'headers: { 0: {sorter: false}},'; } - $sDisplayKey = ($bViewLink) ? 'true' : 'false'; - // Protect against duplicate elements in the Zlist - $aUniqueOrderedList = array(); - foreach($aList as $sAttCode) - { - $aUniqueOrderedList[$sAttCode] = true; - } - $aUniqueOrderedList = array_keys($aUniqueOrderedList); - $sDisplayList = json_encode($aUniqueOrderedList); - $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : ''; - $oSet->ApplyParameters(); - // Display the actual sort order of the table - $aRealSortOrder = $oSet->GetRealSortOrder(); - $aDefaultSort = array(); - $iColOffset = 0; - if ($bSelectMode) - { - $iColOffset += 1; - } - if ($bViewLink) - { - $iColOffset += 1; - } - foreach($aRealSortOrder as $sColCode => $bAscending) - { - $iPos = array_search($sColCode, $aUniqueOrderedList); - if ($iPos !== false) - { - $aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]"; - } - else if($sColCode == 'friendlyname' && $bViewLink) - { - $aDefaultSort[] = "[".($iColOffset-1).",".($bAscending ? '0' : '1')."]"; - } - } - $sSortList = ''; - if (count($aDefaultSort) > 0) - { - $sSortList = ', sortList: ['.implode(',', $aDefaultSort).']'; - } - $sOQL = addslashes($oSet->GetFilter()->serialize()); - $oPage->add_ready_script( -<<GetClassAlias(); + $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; + + // Load only the requested columns + $oSet->OptimizeColumnLoad(array($sClassAlias => $aList)); + + $sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null; + $aClassAliases = array( $sClassAlias => $sClassName); + $oDataTable = new DataTable($iListId, $oSet, $aClassAliases, $sTableId); + $oSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList)); + + if ($bDisplayLimit) { - if (isset($aExtraParams['cssCount'])) - { - $sCssCount = $aExtraParams['cssCount']; - if ($bSingleSelectMode) - { - $sSelectSelector = ":radio[name^=selectObj]"; - } - else - { - $sSelectSelector = ":checkbox[name^=selectObj]"; - } - $oPage->add_ready_script( -<<GetMinDisplayLimit(); //TODO use user's prefs instead if any + $oSettings->iDefaultPageSize = $iDefaultPageSize; } - - return $sHtml; + + $oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName); + + return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams); } public static function GetDisplayExtendedSet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { - static $iListId = 0; - $iListId++; + if (empty($aExtraParams['currentId'])) + { + $iListId = $oPage->GetUniqueId(); // Works only if not in an Ajax page !! + } + else + { + $iListId = $aExtraParams['currentId']; + } $aList = array(); // Initialize and check the parameters @@ -1046,7 +819,7 @@ EOF } } $aAttribs = array(); - foreach($aAuthorizedClasses as $sAlias => $sClassName) // TO DO: check if the user has enough rights to view the classes of the list... + foreach($aAuthorizedClasses as $sAlias => $sClassName) { if (array_key_exists($sAlias, $aExtraFields)) { @@ -1073,14 +846,8 @@ EOF unset($aList[$sAlias][$index]); } } - if ($bViewLink) - { - $aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => ''); - } - foreach($aList[$sAlias] as $sAttCode) - { - $aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode)); - } + $iDefaultPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); + } // Load only the requested columns $aAttToLoad = array(); // attributes to load @@ -1093,83 +860,25 @@ EOF } $oSet->OptimizeColumnLoad($aAttToLoad); - $aValues = array(); - $oSet->Seek(0); + $iDefaultPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); + $iPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); + $sSelectMode = 'none'; + + $sClassAlias = $oSet->GetClassAlias(); + $oDataTable = new DataTable($iListId, $oSet, $aAuthorizedClasses); + + $oSettings = DataTableSettings::GetDataModelSettings($aAuthorizedClasses, $bViewLink, $aList); + $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; - $iMaxObjects = -1; if ($bDisplayLimit) { - if ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit()) - { - $iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit(); - } + $iDefaultPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); //TODO use user's prefs instead if any + $oSettings->iDefaultPageSize = $iDefaultPageSize; } - while (($aObjects = $oSet->FetchAssoc()) && ($iMaxObjects != 0)) - { - $aRow = array(); - foreach($aAuthorizedClasses as $sAlias => $sClassName) // TO DO: check if the user has enough rights to view the classes of the list... - { - if ($bViewLink) - { - if (is_null($aObjects[$sAlias])) - { - $aRow['key_'.$sAlias] = ''; - } - else - { - $aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink(); - } - } - foreach($aList[$sAlias] as $sAttCode) - { - if (is_null($aObjects[$sAlias])) - { - $aRow[$sAttCode.'_'.$sAlias] = ''; - } - else - { - $aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode); - } - } - } - $aValues[] = $aRow; - $iMaxObjects--; - } - $sHtml .= ''; - $sColspan = ''; - if ($bDisplayMenu) - { - $oMenuBlock = new MenuBlock($oSet->GetFilter()); - $sColspan = 'colspan="2"'; - $aMenuExtraParams = $aExtraParams; - if (!empty($sLinkageAttribute)) - { - $aMenuExtraParams = $aExtraParams; - } - if ($bDisplayLimit && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())) - { - // list truncated - $divId = $aExtraParams['block_id']; - $sFilter = $oSet->GetFilter()->serialize(); - $aExtraParams['display_limit'] = false; // To expand the full list - $sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them - $sHtml .= ''; - } - $sHtml .= "'; - $sHtml .= '
'.Dict::Format('UI:TruncatedResults', MetaModel::GetConfig()->GetMinDisplayLimit(), $oSet->Count()).'  '.Dict::S('UI:DisplayAll').''; - $oPage->add_ready_script("$('#{$divId} table.listResults').addClass('truncated');"); - $oPage->add_ready_script("$('#{$divId} table.listResults tr:last td').addClass('truncated');"); - } - else - { - // Full list - $sHtml .= '
 '.Dict::Format('UI:CountOfResults', $oSet->Count()).''; - } - $sHtml .= $oMenuBlock->GetRenderContent($oPage, $aMenuExtraParams, $aMenuExtraParams['currentId']); - $sHtml .= '
"; - $sHtml .= $oPage->GetTable($aAttribs, $aValues); - $sHtml .= '
'; - return $sHtml; + + $oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName); + + return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams); } static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array()) diff --git a/application/datatable.class.inc.php b/application/datatable.class.inc.php new file mode 100644 index 000000000..2ecd0bfa2 --- /dev/null +++ b/application/datatable.class.inc.php @@ -0,0 +1,769 @@ + + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/gpl-3.0.html GPL + */ +class DataTable +{ + protected $iListId; // Unique ID inside the web page + protected $sTableId; // identifier for sqve the settings (combined with the class aliases) + protected $oSet; // The set of objects to display + protected $aClassAliases; // The aliases (alias => class) inside the set + protected $iNbObjects; // Total number of objects inthe set + protected $bUseCustomSettings; // Whether or not the current display uses custom settings + protected $oDefaultSettings; // the default settings for displaying such a list + + /** + * @param $iListId mixed Unique ID for this div/table in the page + * @param $oSet DBObjectSet The set of data to display + * @param $aClassAliases Hash The list of classes/aliases to be displayed in this set $sAlias => $sClassName + * @param $sTableId mixed A string (or null) identifying this table in order to persist its settings + */ + public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null) + { + $this->iListId = $iListId; + $this->oSet = $oSet; + $this->aClassAliases = $aClassAliases; + $this->sTableId = $sTableId; + $this->iNbObjects = $oSet->Count(); + $this->bUseCustomSettings = false; + $this->oDefaultSettings = null; + } + + public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams) + { + $this->oDefaultSettings = $oSettings; + if ($this->sTableId != null) + { + // Identified tables can have their own specific settings + $oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId); + } + else + { + $oCustomSettings = null; + } + + if ($oCustomSettings != null) + { + // Custom settings overload the default ones + $this->bUseCustomSettings = true; + } + else + { + $oCustomSettings = $oSettings; + } + + if ($oCustomSettings->iDefaultPageSize != -1) + { + $this->oSet->SetLimit($oCustomSettings->iDefaultPageSize); + } + + + return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, true, $sSelectMode, $bViewLink, $aExtraParams); + } + + public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams) + { + $sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode); + $sPager = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex); + $sActionsMenu = ''; + $sToolkitMenu = ''; + if ($bActionsMenu) + { + $sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams); + } + if ($bToolkitMenu) + { + $sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams); + } + $sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams); + $sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink); + + $sHtml = "iListId}\" class=\"datatable\">\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "
$sObjectsCount$sActionsMenu
$sPager$sToolkitMenu
$sDataTable
\n"; + $sHtml .= $sConfigDlg; + + $aOptions = array( + 'sPersistentId' => '', + 'sFilter' => $this->oSet->GetFilter()->serialize(), + 'oColumns' => $aColumns, + 'sSelectMode' => $sSelectMode, + 'sViewLink' => ($bViewLink ? 'true' : 'false'), + 'iNbObjects' => $this->iNbObjects, + 'iDefaultPageSize' => $iDefaultPageSize, + 'iPageSize' => $iPageSize, + 'iPageIndex' => $iPageIndex, + 'oClassAliases' => $this->aClassAliases, + 'sTableId' => $this->sTableId, + 'oExtraParams' => $aExtraParams, + 'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', + 'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object... + 'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object... + 'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')), + ); + if($this->oDefaultSettings != null) + { + $aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings); + } + $sJSOptions = json_encode($aOptions); + $oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);"); + return $sHtml; + } + + /** + * When refreshing the body of a paginated table, get the rows of the table (inside the TBODY) + * return string The HTML rows to insert inside the node + */ + public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams) + { + $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink); + $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams); + + $sHtml = ''; + foreach($aValues as $aRow) + { + $sHtml .= $oPage->GetTableRow($aRow, $aAttribs); + } + return $sHtml; + } + + protected function GetObjectCount(WebPage $oPage, $sSelectMode) + { + if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) + { + $sHtml = Dict::Format('UI:Pagination:HeaderSelection', ''.$this->iNbObjects.'', '0'); + } + else + { + $sHtml = Dict::Format('UI:Pagination:HeaderNoSelection', ''.$this->iNbObjects.''); + } + return $sHtml; + } + protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex) + { + $sHtml = ''; + if ($iPageSize == -1) // Display all + { + $sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI + } + else + { + $sPagerStyle = ''; + } + + $sCombo = ''; + + $sPages = Dict::S('UI:Pagination:PagesLabel'); + $sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo); + + $iNbPages = ($iPageSize == -1) ? 1 : ceil($this->iNbObjects / $iPageSize); + $aPagesToDisplay = array(); + for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++) + { + if ($idx == 0) + { + $aPagesToDisplay[$idx] = '1'; + } + else + { + $aPagesToDisplay[$idx] = "".(1+$idx).""; + } + } + $iLastPageIdx = $iNbPages - 1; + if (!isset($aPagesToDisplay[$iLastPageIdx])) + { + unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page + $aPagesToDisplay[$iLastPageIdx] = "... $iNbPages"; + } + $sPagesLinks = implode('', $aPagesToDisplay); + $sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']'; + + $sSelectionMode = ($iNbPages == 1) ? '' : 'positive'; + $sHtml = +<< +

+ + + + + + + +
$sPages$sPagesLinks$sPageSizeCombo 
+ + + +EOF; + return $sHtml; + } + + protected function GetActionsMenu(WebPage $oPage, $aExtraParams) + { + $oMenuBlock = new MenuBlock($this->oSet->GetFilter()); + + $sHtml = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId); + return $sHtml; + } + + protected function GetToolkitMenu(WebPage $oPage, $aExtraParams) + { + $sMenuTitle = Dict::S('UI:ConfigureThisList'); + $sHtml = '

'; + //$oPage->add_ready_script("$('#tk_{$this->iListId} > ul').popupmenu();"); + return $sHtml; + } + + protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink) + { + $sHtml = "
iListId}\" style=\"display: none;\">"; + $sHtml .= "
"; + $sChecked = ($this->bUseCustomSettings) ? '' : 'checked'; + $sHtml .= "

 ".Dict::S('UI:UseDefaultSettings').'

'; + $sHtml .= "
"; + $sChecked = ($this->bUseCustomSettings) ? 'checked': ''; + $sHtml .= " ".Dict::S('UI:UseSpecificSettings').""; + $sHtml .= Dict::S('UI:ColumnsAndSortOrder').'
    '; + + $sHtml .= '

    '.Dict::Format('UI:Display_X_ItemsPerPage', '').'

    '; + $sHtml .= "
    "; + $sHtml .= "
    "; + $sSaveChecked = ($this->sTableId != null) ? 'checked' : ''; + $sCustomChecked = ($this->sTableId != null) ? 'checked' : ''; + $sGenericChecked = ($this->sTableId == null) ? 'checked' : ''; + $sHtml .= " ".Dict::S('UI:UseSavetheSettings').""; + $sHtml .= '

     '.Dict::S('UI:OnlyForThisList').'    '; + $sHtml .= ' '.Dict::S('UI:ForAllLists').'

    '; + $sHtml .= "
    "; + $sHtml .= '
    '; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= ''; + $sHtml .= '
    '; + $sHtml .= "
    "; + $sHtml .= "
    "; + + $sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle')); + $oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#datatable_{$this->iListId}').datatable('onDlgCancel'); } });"); + + return $sHtml; + } + + public function GetAsHash($oSetting) + { + $aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns); + return $aSettings; + } + + protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink) + { + $aAttribs = array(); + if ($sSelectMode == 'multiple') + { + $aAttribs['form::select'] = array('label' => "iListId}:not(:disabled)', this.checked);\" class=\"checkAll\">", 'description' => Dict::S('UI:SelectAllToggle+')); + } + else if ($sSelectMode == 'single') + { + $aAttribs['form::select'] = array('label' => "", 'description' => ''); + } + + foreach($this->aClassAliases as $sAlias => $sClassName) + { + foreach($aColumns[$sAlias] as $sAttCode => $aData) + { + if ($aData['checked']) + { + if ($sAttCode == '_key_') + { + $aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => ''); + } + else + { + $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); + $aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint()); + } + } + } + } + return $aAttribs; + } + + protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams) + { + $aValues = array(); + $this->oSet->Seek(0); + $iMaxObjects = $iPageSize; + while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0)) + { + $bFirstObject = true; + $aRow = array(); + foreach($this->aClassAliases as $sAlias => $sClassName) + { + $sHilightClass = $aObjects[$sAlias]->GetHilightClass(); + if ($sHilightClass != '') + { + $aRow['@class'] = $sHilightClass; + } + if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject) + { + if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()])) + { + $sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"'; + } + else + { + $sDisabled = ''; + } + if ($sSelectMode == 'single') + { + $aRow['form::select'] = "iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\">"; + } + else + { + $aRow['form::select'] = "iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\">"; + } + } + foreach($aColumns[$sAlias] as $sAttCode => $aData) + { + if ($aData['checked']) + { + if ($sAttCode == '_key_') + { + $aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink(); + } + else + { + $aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode); + } + } + } + $bFirstObject = false; + } + $aValues[] = $aRow; + $iMaxObjects--; + } + return $aValues; + } + + public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams) + { + $iNbPages = ($iPageSize == -1) ? 1 : ceil($this->iNbObjects / $iPageSize); + $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink); + + $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams); + + $sHtml = ''; + + foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue) + { + $aExtraParams['query_params'][$sName] = $sValue; + } + + $sHtml .= "'; + $sHtml .= '
    "; + $sHtml .= $oPage->GetTable($aAttribs, $aValues); + $sHtml .= '
    '; + $iCount = $this->iNbObjects; + + $aArgs = $this->oSet->GetArgs(); + $sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them + $sSelectModeJS = ''; + $sHeaders = ''; + if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) + { + $sSelectModeJS = $sSelectMode; + $sHeaders = 'headers: { 0: {sorter: false}},'; + } + $sDisplayKey = ($bViewLink) ? 'true' : 'false'; + // Protect against duplicate elements in the Zlist + $aUniqueOrderedList = array(); + foreach($this->aClassAliases as $sAlias => $sClassName) + { + foreach($aColumns[$sAlias] as $sAttCode => $aData) + { + if ($aData['checked']) + { + $aUniqueOrderedList[$sAttCode] = true; + } + } + } + $aUniqueOrderedList = array_keys($aUniqueOrderedList); + $sJSColumns = json_encode($aColumns); + $sJSClassAliases = json_encode($this->aClassAliases); + $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : ''; + $this->oSet->ApplyParameters(); + // Display the actual sort order of the table + $aRealSortOrder = $this->oSet->GetRealSortOrder(); + $aDefaultSort = array(); + $iColOffset = 0; + if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) + { + $iColOffset += 1; + } + if ($bViewLink) + { + $iColOffset += 1; + } + foreach($aRealSortOrder as $sColCode => $bAscending) + { + $iPos = array_search($sColCode, $aUniqueOrderedList); + if ($iPos !== false) + { + $aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]"; + } + else if($sColCode == 'friendlyname' && $bViewLink) + { + $aDefaultSort[] = "[".($iColOffset-1).",".($bAscending ? '0' : '1')."]"; + } + } + $sSortList = ''; + if (count($aDefaultSort) > 0) + { + $sSortList = ', sortList: ['.implode(',', $aDefaultSort).']'; + } + $sOQL = addslashes($this->oSet->GetFilter()->serialize()); + $oPage->add_ready_script( +<<iListId} table.listResults'); +oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList'] $sSortList} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount}); +EOF + ); + + //if ($iNbPages == 1) + if (false) + { + if (isset($aExtraParams['cssCount'])) + { + $sCssCount = $aExtraParams['cssCount']; + if ($sSelectMode == 'single') + { + $sSelectSelector = ":radio[name^=selectObj]"; + } + else if ($sSelectMode == 'multiple') + { + $sSelectSelector = ":checkbox[name^=selectObj]"; + } + $oPage->add_ready_script( +<<iListId} table.listResults $sSelectSelector').change(function() { + var c = $('{$sCssCount}'); + var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length; + c.val(v); + $('#{$this->iListId} .selectedCount').text(v); + c.trigger('change'); + }); +EOF + ); + } + } + return $sHtml; + } + + public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart) + { + $iPageSize = $iDefaultPageSize; + $iPageIndex = 1 + floor($iStart / $iPageSize); + $sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex); + $oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');"); + } +} + +class DataTableSettings implements Serializable +{ + public $aClassAliases; + public $sTableId; + public $iDefaultPageSize; + public $aColumns; + + public function __construct($aClassAliases, $sTableId = null) + { + $this->aClassAliases = $aClassAliases; + $this->sTableId = $sTableId; + $this->iDefaultPageSize = 10; + $this->aColumns = array(); + } + + protected function Init($iDefaultPageSize, $aSortOrder, $aColumns) + { + $this->iDefaultPageSize = $iDefaultPageSize; + $this->aColumns = $aColumns; + $this->FixVisibleColumns(); + } + + public function serialize() + { + // Save only the 'visible' columns + $aColumns = array(); + foreach($this->aClassAliases as $sAlias => $sClass) + { + $aColumns[$sAlias] = array(); + foreach($this->aColumns[$sAlias] as $sAttCode => $aData) + { + unset($aData['label']); // Don't save the display name + unset($aData['alias']); // Don't save the alias (redundant) + unset($aData['code']); // Don't save the code (redundant) + if ($aData['checked']) + { + $aColumns[$sAlias][$sAttCode] = $aData; + } + } + } + return serialize( + array( + 'iDefaultPageSize' => $this->iDefaultPageSize, + 'aColumns' => $aColumns, + ) + ); + } + + public function unserialize($sData) + { + $aData = unserialize($sData); + $this->iDefaultPageSize = $aData['iDefaultPageSize']; + $this->aColumns = $aData['aColumns']; + foreach($this->aClassAliases as $sAlias => $sClass) + { + foreach($this->aColumns[$sAlias] as $sAttCode => $aData) + { + $aFieldData = false; + if ($sAttCode == '_key_') + { + $aFieldData = $this->GetFieldData($sAlias, $sAttCode, null, true /* bChecked */, $aData['sort']); + } + else if (MetaModel::isValidAttCode($sClass, $sAttCode)) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $aData['sort']); + } + + if ($aFieldData) + { + $this->aColumns[$sAlias][$sAttCode] = $aFieldData; + } + else + { + unset($this->aColumns[$sAlias][$sAttCode]); + } + } + } + $this->FixVisibleColumns(); + } + + static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists) + { + $oSettings = new DataTableSettings($aClassAliases); + // Retrieve the class specific settings for each class/alias based on the 'list' ZList + //TODO let the caller pass some other default settings (another Zlist, extre fields...) + $aColumns = array(); + foreach($aClassAliases as $sAlias => $sClass) + { + if ($aDefaultLists == null) + { + $aList = cmdbAbstract::FlattenZList(MetaModel::GetZListItems($sClass, 'list')); + } + else + { + $aList = $aDefaultLists[$sAlias]; + } + + $aSortOrder = MetaModel::GetOrderByDefault($sClass); + if ($bViewLink) + { + $sSort = 'none'; + if(array_key_exists('friendlyname', $aSortOrder)) + { + $sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc'; + } + $aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort); + } + foreach($aList as $sAttCode) + { + $sSort = 'none'; + if(array_key_exists($sAttCode, $aSortOrder)) + { + $sSort = $aSortOrder[$sAttCode] ? 'asc' : 'desc'; + } + $oAttDef = Metamodel::GetAttributeDef($sClass, $sAttCode); + $aFieldData = $oSettings->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $sSort); + if ($aFieldData) $aColumns[$sAlias][$sAttCode] = $aFieldData; + } + } + // TODO retrieve the user default page size or the system wide setting + $iDefaultPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); + $oSettings->Init($iDefaultPageSize, $aSortOrder, $aColumns); + return $oSettings; + } + + protected function FixVisibleColumns() + { + foreach($this->aClassAliases as $sAlias => $sClass) + { + foreach($this->aColumns[$sAlias] as $sAttCode => $aData) + { + // Remove non-existent columns + // TODO: check if the existing ones are still valid (in case their type changed) + if (($sAttCode != '_key_') && (!MetaModel::IsValidAttCode($sClass, $sAttCode))) + { + unset($this->aColumns[$sAlias][$sAttCode]); + } + } + $aList = MetaModel::ListAttributeDefs($sClass); + + // Add the other (non visible ones) + foreach($aList as $sAttCode => $oAttDef) + { + if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet)) + { + $aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none'); + if ($aFieldData) $this->aColumns[$sAlias][$sAttCode] = $aFieldData; + } + } + } + } + + static public function GetTableSettings($aClassAliases, $sTableId = null) + { + $pref = null; + $oSettings = new DataTableSettings($aClassAliases, $sTableId); + if ($sTableId != null) + { + // An identified table, let's fetch its own settings (if any) + $pref = appUserPreferences::GetPref($oSettings->GetPrefsKey($sTableId), null); + } + + if ($pref == null) + { + // Try the global preferred values for this class / set of classes + $pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null); + if ($pref == null) + { + // no such settings, use the default values provided by the data model + return null; + } + } + $oSettings->unserialize($pref); + return $oSettings; + } + + public function Save() + { + if ($this->sTableId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead + + $sSettings = $this->serialize(); + appUserPreferences::SetPref($this->GetPrefsKey($this->sTableId), $sSettings); + return true; + } + + public function SaveAsDefault() + { + $sSettings = $this->serialize(); + appUserPreferences::SetPref($this->GetPrefsKey(null), $sSettings); + return true; + } + + + /** + * Clear the preferences for this particular table + * @param $bResetAll boolean If true,the settings for all tables of the same class(es)/alias(es) are reset + */ + public function ResetToDefault($bResetAll) + { + if (($this->sTableId == null) && (!$bResetAll)) return false; // Cannot reset, the table is not identified, use force $bResetAll instead + if ($bResetAll) + { + // Turn the key into a suitable PCRE pattern + $sKey = $this->GetPrefsKey(null); + $sPattern = '!^'.str_replace(array('*'), array('.*'), $sKey).'$!'; + appUserPreferences::UnsetPref($sPattern, true); + } + else + { + appUserPreferences::UnsetPref($this->GetPrefsKey($this->sTableId), false); + } + return true; + } + + protected function GetPrefsKey($sTableId = null) + { + if ($sTableId == null) $sTableId = '*'; + $aKeys = array(); + foreach($this->aClassAliases as $sAlias => $sClass) + { + $aKeys[] = $sAlias.'-'.$sClass; + } + return implode('/', $aKeys).'|'.$sTableId; + } + + protected function GetFieldData($sAlias, $sAttCode, $oAttDef, $bChecked, $sSort) + { + $ret = false; + if ($sAttCode == '_key_') + { + $sLabel = Dict::Format('UI:ExtKey_AsLink', MetaModel::GetName($this->aClassAliases[$sAlias])); + $ret = array( + 'label' => $sLabel, + 'checked' => true, + 'disabled' => true, + 'alias' => $sAlias, + 'code' => $sAttCode, + 'sort' => $sSort, + ); + } + else if (!$oAttDef->IsLinkSet()) + { + $sLabel = $oAttDef->GetLabel(); + if ($oAttDef->IsExternalKey()) + { + $sLabel = Dict::Format('UI:ExtKey_AsLink', $oAttDef->GetLabel()); + } + else if ($oAttDef->IsExternalField()) + { + $oExtAttDef = $oAttDef->GetExtAttDef(); + $sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel()); + } + elseif ($oAttDef instanceof AttributeFriendlyName) + { + $sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel()); + } + $ret = array( + 'label' => $sLabel, + 'checked' => $bChecked, + 'disabled' => false, + 'alias' => $sAlias, + 'code' => $sAttCode, + 'sort' => $sSort, + ); + } + return $ret; + } +} \ No newline at end of file diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 1194f9ef5..fb880dfdf 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1332,11 +1332,11 @@ class MenuBlock extends DisplayBlock if (count($aFavoriteActions) > 0) { - $sHtml .= "
      \n
    • ".Dict::S('UI:Menu:OtherActions')."\n
        \n"; + $sHtml .= "
          \n
        • ".Dict::S('UI:Menu:OtherActions')."\n
            \n"; } else { - $sHtml .= "
              \n
            • ".Dict::S('UI:Menu:Actions')."\n
                \n"; + $sHtml .= "
                  \n
                • ".Dict::S('UI:Menu:Actions')."\n
                    \n"; } $sPrevUrl = ''; foreach ($aActions as $key => $aAction) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 31c9f08ef..1ba34c979 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -77,6 +77,8 @@ class iTopWebPage extends NiceWebPage $this->add_linked_script('../js/g.pie.js'); $this->add_linked_script('../js/g.dot.js'); $this->add_linked_script('../js/charts.js'); + $this->add_linked_script('../js/field_sorter.js'); + $this->add_linked_script('../js/datatable.js'); $this->m_sInitScript = <<< EOF diff --git a/application/user.preferences.class.inc.php b/application/user.preferences.class.inc.php index 3d40db10a..f42d55800 100644 --- a/application/user.preferences.class.inc.php +++ b/application/user.preferences.class.inc.php @@ -77,6 +77,42 @@ class appUserPreferences extends DBObject self::Save(); } + /** + * Clears the value for a given preference (or list of preferences that matches a pattern), and updates the database + * @param string $sPattern Code/Pattern of the properties/preferences to reset + * @param boolean $bPattern Whether or not the supplied code is a PCRE pattern + */ + static function UnsetPref($sCodeOrPattern, $bPattern = false) + { + if (self::$oUserPrefs == null) + { + self::Load(); + } + $aPrefs = self::$oUserPrefs->Get('preferences'); + if ($bPattern) + { + // the supplied code is a pattern, clear all preferences that match + foreach($aPrefs as $sKey => $void) + { + if (preg_match($sCodeOrPattern, $sKey)) + { + unset($aPrefs[$sKey]); + } + } + self::$oUserPrefs->Set('preferences', $aPrefs); + } + else + { + unset($aPrefs[$sCode]); + self::$oUserPrefs->Set('preferences', $aPrefs); + } + // Save only if needed + if (self::$oUserPrefs->IsModified()) + { + self::Save(); + } + } + /** * Call this function to get all the preferences for the user, packed as a JSON object * @return string JSON representation of the preferences