diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index c278fa136..2d97d5482 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -757,19 +757,17 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay } foreach($aList as $sAttCode) { - $aAttribs[$sAttCode] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $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 && $bTruncated) - //{ - if ($bDisplayLimit && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())) - { - $iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit(); - $oSet->SetLimit($iMaxObjects); - } - //} + if ($bDisplayLimit && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())) + { + $iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit(); + $oSet->SetLimit($iMaxObjects); + } $oSet->Seek(0); while (($oObj = $oSet->Fetch()) && ($iMaxObjects != 0)) { @@ -811,14 +809,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay } $sHtml .= ''; $sColspan = ''; -// if (isset($aExtraParams['block_id'])) -// { -// $divId = $aExtraParams['block_id']; -// } -// else -// { -// $divId = 'missingblockid'; -// } + $sFilter = $oSet->GetFilter()->serialize(); $iMinDisplayLimit = MetaModel::GetConfig()->GetMinDisplayLimit(); $sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oSet->Count()); @@ -854,30 +845,62 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay { $sHeader = Dict::Format('UI:Pagination:HeaderNoSelection', ''.$iCount.''); } - if ($bDisplayLimit && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())) + + // All lists are now paginated in order to benefit from the SQL sort order + + if (!$bDisplayLimit) { - $sCombo = ''; + for($iPage = 1; $iPage < 5; $iPage++) + { + $sSelected = ($iPage == $iPageSize) ? 'selected="selected"' : ''; + $iNbItems = $iPage * $iDefaultPageSize; + $sCombo .= ""; + } + $sSelected = (-1 == $iPageSize) ? 'selected="selected"' : ''; + $sCombo .= ""; + $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) { - $sSelected = ''; - if ($iPage == 1) - { - $sSelected = 'selected="selected"'; - } - $iNbItems = $iPage * MetaModel::GetConfig()->GetMinDisplayLimit(); - $sCombo .= ""; + $aPagesToDisplay[$idx] = '1'; } - $sCombo .= ""; - $sCombo .= ''; - $sPages = Dict::S('UI:Pagination:PagesLabel'); - $sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo); -$sHtml = + 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)).']'; + + $sHtml = << +

$sHeader

- + @@ -889,63 +912,62 @@ $sHtml = 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 = ''; - if ($bSelectMode) - { - $sSelectMode = $bSingleSelectMode ? 'single' : 'multiple'; - $sHeaders = 'headers: { 0: {sorter: false}},'; - } - $sDisplayKey = ($bViewLink) ? 'true' : 'false'; - $sDisplayList = json_encode($aList); - $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : ''; - $iPageSize = MetaModel::GetConfig()->GetMinDisplayLimit(); - $oSet->ApplyParameters(); - $sOQL = addslashes($oSet->GetFilter()->serialize()); - $oPage->add_ready_script("$('#{$iListId} table.listResults').tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectMode', displayKey: $sDisplayKey, displayList: $sDisplayList $sCssCount});\n"); - } - else + $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 = ''; + if ($bSelectMode) { -$sHtml = -<< -

$sHeader

- -EOF -.$sHtml; - $sHeaders = ''; - 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) { - $sHeaders = 'headers: { 0: {sorter: false}},'; + $aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]"; } - $oPage->add_ready_script("$('#{$iListId} table.listResults').tableHover().tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} );\n"); - // Manage how we update the 'Ok/Add' buttons that depend on the number of selected items - if (isset($aExtraParams['cssCount'])) + else if($sColCode == 'friendlyname' && $bViewLink) { - $sCssCount = $aExtraParams['cssCount']; - if ($bSingleSelectMode) - { - $sSelectSelector = ":radio[name^=selectObj]"; - } - else - { - $sSelectSelector = ":checkbox[name^=selectObj]"; - } - $oPage->add_ready_script( -<< 0) + { + $sSortList = ', sortList: ['.implode(',', $aDefaultSort).']'; + } + $sOQL = addslashes($oSet->GetFilter()->serialize()); + $oPage->add_ready_script( +<<m_sMenu = ""; $this->m_sMessage = ''; $this->SetRootUrl(utils::GetAbsoluteUrlAppRoot()); - $oAppContext = new ApplicationContext(); - $sExtraParams = $oAppContext->GetForLink(); - $sAppContext = addslashes($sExtraParams); $this->add_header("Content-type: text/html; charset=utf-8"); $this->add_header("Cache-control: no-cache"); $this->add_linked_stylesheet("../css/jquery.treeview.css"); $this->add_linked_stylesheet("../css/jquery.autocomplete.css"); $this->add_linked_script('../js/jquery.layout.min.js'); $this->add_linked_script('../js/jquery.ba-bbq.min.js'); - $this->add_linked_script("../js/jquery.tablehover.js"); $this->add_linked_script("../js/jquery.treeview.js"); $this->add_linked_script("../js/jquery.autocomplete.js"); $this->add_linked_script("../js/jquery.positionBy.js"); $this->add_linked_script("../js/jquery.popupmenu.js"); $this->add_linked_script("../js/date.js"); - $this->add_linked_script("../js/jquery.tablesorter.min.js"); $this->add_linked_script("../js/jquery.blockUI.js"); $this->add_linked_script("../js/utils.js"); $this->add_linked_script("../js/swfobject.js"); $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/jquery.tablesorter.pager.js"); $this->m_sInitScript = <<< EOF try @@ -162,46 +156,6 @@ EOF ; $this->add_ready_script( <<< EOF - //add new widget called TruncatedList to properly display truncated lists when they are sorted - $.tablesorter.addWidget({ - // give the widget a id - id: "truncatedList", - // format is called when the on init and when a sorting has finished - format: function(table) - { - // Check if there is a "truncated" line - this.truncatedList = false; - if ($("tr td.truncated",table).length > 0) - { - this.truncatedList = true; - } - if (this.truncatedList) - { - $("tr td",table).removeClass('truncated'); - $("tr:last td",table).addClass('truncated'); - } - } - }); - - - $.tablesorter.addWidget({ - // give the widget a id - id: "myZebra", - // format is called when the on init and when a sorting has finished - format: function(table) - { - // Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc.. - $("tbody tr:even",table).addClass('even'); - $("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even'); - $("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even'); - $("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even'); - // In case we sort again the table, we need to remove the added 'even' classes on odd rows - $("tbody tr:odd",table).removeClass('even'); - $("tbody tr.red_even:odd",table).removeClass('even').removeClass('red_even').addClass('red'); - $("tbody tr.orange_even:odd",table).removeClass('even').removeClass('orange_even').addClass('orange'); - $("tbody tr.green_even:odd",table).removeClass('even').removeClass('green_even').addClass('green'); - } - }); // Adjust initial size $('.v-resizable').each( function() @@ -307,7 +261,7 @@ EOF }); // End of Tabs handling - $("table.listResults").tableHover(); // hover tables + $(".date-pick").datepicker({ showOn: 'button', buttonImage: '../images/calendar.png', @@ -408,21 +362,7 @@ EOF if ($('#rawOutput > div').html() != '') { $('#rawOutput').dialog( {autoOpen: true, modal:false}); - } - } - - function AddAppContext(sURL) - { - var sContext = '$sAppContext'; - if (sContext.length > 0) - { - if (sURL.indexOf('?') == -1) - { - return sURL+'?'+sContext; - } - return sURL+'&'+sContext; } - return sURL; } var oUserPreferences = $sUserPrefs; @@ -535,19 +475,7 @@ EOF public function output() { $sAbsURLAppRoot = addslashes($this->m_sRootUrl); - $sAbsURLModulesRoot = addslashes(utils::GetAbsoluteUrlModulesRoot()); - $this->add_script( -<<set_base($this->m_sRootUrl.'pages/'); $sForm = $this->GetSiloSelectionForm(); $this->DisplayMenu(); // Compute the menu diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index 77659c02f..f99a77e66 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -38,12 +38,92 @@ class NiceWebPage extends WebPage $this->m_aReadyScripts = array(); $this->add_linked_script("../js/jquery-1.4.2.min.js"); $this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.2.custom.css'); + $this->add_style('body { overflow: auto; }'); $this->add_linked_script('../js/jquery-ui-1.8.2.custom.min.js'); $this->add_linked_script("../js/hovertip.js"); + // table sorting + $this->add_linked_script("../js/jquery.tablesorter.min.js"); + $this->add_linked_script("../js/jquery.tablesorter.pager.js"); + $this->add_linked_script("../js/jquery.tablehover.js"); + $this->add_ready_script( +<<< EOF + //add new widget called TruncatedList to properly display truncated lists when they are sorted + $.tablesorter.addWidget({ + // give the widget a id + id: "truncatedList", + // format is called when the on init and when a sorting has finished + format: function(table) + { + // Check if there is a "truncated" line + this.truncatedList = false; + if ($("tr td.truncated",table).length > 0) + { + this.truncatedList = true; + } + if (this.truncatedList) + { + $("tr td",table).removeClass('truncated'); + $("tr:last td",table).addClass('truncated'); + } + } + }); + + $.tablesorter.addWidget({ + // give the widget a id + id: "myZebra", + // format is called when the on init and when a sorting has finished + format: function(table) + { + // Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc.. + $("tbody tr:even",table).addClass('even'); + $("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even'); + $("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even'); + $("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even'); + // In case we sort again the table, we need to remove the added 'even' classes on odd rows + $("tbody tr:odd",table).removeClass('even'); + $("tbody tr.red_even:odd",table).removeClass('even').removeClass('red_even').addClass('red'); + $("tbody tr.orange_even:odd",table).removeClass('even').removeClass('orange_even').addClass('orange'); + $("tbody tr.green_even:odd",table).removeClass('even').removeClass('green_even').addClass('green'); + } + }); + $("table.listResults").tableHover(); // hover tables +EOF + ); $this->add_linked_stylesheet("../css/light-grey.css"); - $this->m_sRootUrl = '../'; - } + $this->m_sRootUrl = utils::GetAbsoluteUrlAppRoot(); + $sAbsURLAppRoot = addslashes($this->m_sRootUrl); + $sAbsURLModulesRoot = addslashes(utils::GetAbsoluteUrlModulesRoot()); + $oAppContext = new ApplicationContext(); + $sExtraParams = $oAppContext->GetForLink(); + $sAppContext = addslashes($sExtraParams); + $this->add_script( +<< 0) + { + if (sURL.indexOf('?') == -1) + { + return sURL+'?'+sContext; + } + return sURL+'&'+sContext; + } + return sURL; +} +EOF + ); + } public function SetRootUrl($sRootUrl) { @@ -100,15 +180,6 @@ class NiceWebPage extends WebPage */ public function output() { - $sAbsURLAppRoot = addslashes($this->m_sRootUrl); - $this->add_script( -<<set_base($this->m_sRootUrl.'pages/'); if (count($this->m_aReadyScripts)>0) { diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 77004f09f..9aad0e917 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -323,6 +323,11 @@ abstract class AttributeDefinition // Note: This is the responsibility of this function to place backticks around column aliases return array('`'.$sClassAlias.$this->GetCode().'`'); } + + public function GetOrderByHint() + { + return ''; + } // Import - differs slightly from SQL input, but identical in most cases // @@ -2120,6 +2125,13 @@ class AttributeEnum extends AttributeString if ($proposedValue == '') return null; return parent::MakeRealValue($proposedValue, $oHostObj); } + + public function GetOrderByHint() + { + $aValues = $this->GetAllowedValues(); + + return Dict::Format('UI:OrderByHint_Values', implode(', ', $aValues)); + } } /** diff --git a/css/light-grey.css b/css/light-grey.css index fc79f910b..f81745959 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -1070,6 +1070,8 @@ table.pagination tr td { } .pager td span { min-width: 20px; + padding-left: 2px; + padding-right: 2px; display:inline-block; text-align: center; cursor: pointer; diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index 4a891a8e1..9b5a4843a 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -956,5 +956,6 @@ When associated with a trigger, each action is given an "order" number, specifyi 'Note that this is not a security setting, objects from any organization are still visible and can be accessed by selecting "All Organizations" in the drop-down list.', 'UI:NavigateAwayConfirmationMessage' => 'Any modification will be discarded.', 'UI:Create_Class_InState' => 'Create the %1$s in state: ', + 'UI:OrderByHint_Values' => 'Sort order: %1$s', )); ?> diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index e085f5d40..bb726663f 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -800,5 +800,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'Ceci n\'est pas un réglage de sécurité. Les objets de toutes les organisations sont toujours visibles en choisissant "Toutes les Organisations" dans le menu.', 'UI:NavigateAwayConfirmationMessage' => 'Toute modification sera perdue.', 'UI:Create_Class_InState' => 'Créer l\'objet %1$s dans l\'état: ', + 'UI:OrderByHint_Values' => 'Ordre de tri: %1$s', )); ?> diff --git a/js/jquery.tablesorter.pager.js b/js/jquery.tablesorter.pager.js index 4f5d0defa..b3c3270b2 100644 --- a/js/jquery.tablesorter.pager.js +++ b/js/jquery.tablesorter.pager.js @@ -288,40 +288,62 @@ function sprintf(format, etc) { function renderPager(table, pager) { var c = table.config; - var s = c.page - 2; + var aPages = [0]; // first page + var s = c.page - 1; var nb = Math.ceil(c.totalRows / c.size); - if (s < 0) + if (s < 1) { - s = 0; + s = 1; } - var e = s +5; - if (e > nb) + var e = s +3; + if (e >= nb) { e = nb; - s = e - 5; - if (s < 0) s = 0; + if ((e - 4) > 1) + { + s = e - 4; + } } - txt = ''; for(var i=s; i 1) && (nb > i)) + { + aPages.push(nb - 1); // very last page + } + + txt = ''; + for(i=0; i 1) && (aPages[i+1] != aPages[i]+1)) { - link = ' '+page+' '; + sDotsAfter = '...'; // Gap between the last 2 page numbers + } + if ((i == aPages.length-1) && (aPages.length > 1) && (aPages[i-1] != aPages[i]-1)) + { + sDotsBefore = '...'; // Gap between the first 2 page numbers + } + if (aPages[i] != c.page) + { + link = ' '+sDotsBefore+page+sDotsAfter+' '; } else { - link = ' '+page+' '; + link = ' '+sDotsBefore+page+sDotsAfter+' '; } txt += link; } txt += ''; $('#total', pager).text(c.totalRows); $('#index', pager).html(txt); - for(var j=s; j
$sPages $sPagesLinks $sPageSizeCombo