diff --git a/ChangeLog b/ChangeLog index c6f00a8ab7..721b7d7998 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,7 @@ phpMyAdmin - ChangeLog 4.2.6.0 (not yet released) - bug #4471 Undefined index warning with referenced column. - bug #4027 $cfg['MaxExactCount'] is ignored when BROWSING is back +- bug #4482 Multi Column sorting (improved user experience) 4.2.5.0 (2014-06-26) - bug #4467 shell_exec() has been disabled for security reasons diff --git a/js/makegrid.js b/js/makegrid.js index aedaae3131..8d00f8b17d 100644 --- a/js/makegrid.js +++ b/js/makegrid.js @@ -430,25 +430,24 @@ function PMA_makegrid(t, enableResize, enableReorder, enableVisib, enableGridEdi if ($(t).find('th.marker').length > 0) { g.showMarkHint = true; } - - if (g.showReorderHint && g.reorderHint) { - text += g.reorderHint; - } if (g.showSortHint && g.sortHint) { text += text.length > 0 ? '
' : ''; - text += g.sortHint; + text += '- ' + g.sortHint; } - if (g.showRemColHint && g.strRemColHint) { + if (g.showMultiSortHint && g.strMultiSortHint) { text += text.length > 0 ? '
' : ''; - text += g.strRemColHint; + text += '- ' + g.strMultiSortHint; } - if (g.showMarkHint && g.markHint && - !g.showSortHint // we do not show mark hint, when sort hint is shown + if (g.showMarkHint && g.markHint + && !g.showSortHint // we do not show mark hint, when sort hint is shown + && g.showReorderHint && g.reorderHint ) { text += text.length > 0 ? '
' : ''; - text += g.markHint; + text += '- ' + g.reorderHint; text += text.length > 0 ? '
' : ''; - text += g.copyHint; + text += '- ' + g.markHint; + text += text.length > 0 ? '
' : ''; + text += '- ' + g.copyHint; } } return text; @@ -1852,7 +1851,7 @@ function PMA_makegrid(t, enableResize, enableReorder, enableVisib, enableGridEdi // assign the hints g.sortHint = PMA_messages.strSortHint; - g.strRemColHint = PMA_messages.strRemColHint; + g.strMultiSortHint = PMA_messages.strMultiSortHint; g.markHint = PMA_messages.strColMarkHint; g.copyHint = PMA_messages.strColNameCopyHint; @@ -1906,14 +1905,14 @@ function PMA_makegrid(t, enableResize, enableReorder, enableVisib, enableGridEdi $(t).find('th.draggable a') .mouseenter(function (e) { g.showSortHint = true; - g.showRemColHint = true; + g.showMultiSortHint = true; $(t).find("th.draggable").tooltip("option", { content: g.updateHint() }); }) .mouseleave(function (e) { g.showSortHint = false; - g.showRemColHint = false; + g.showMultiSortHint = false; $(t).find("th.draggable").tooltip("option", { content: g.updateHint() }); diff --git a/js/messages.php b/js/messages.php index a4d09e3112..3e933545e0 100644 --- a/js/messages.php +++ b/js/messages.php @@ -389,8 +389,8 @@ $js_messages['strObjectsCreated'] = __('%d object(s) created.'); $js_messages['strCellEditHint'] = __('Press escape to cancel editing.'); $js_messages['strSaveCellWarning'] = __('You have edited some data and they have not been saved. Are you sure you want to leave this page before saving the data?'); $js_messages['strColOrderHint'] = __('Drag to reorder.'); -$js_messages['strSortHint'] = __('Click to sort.'); -$js_messages['strRemColHint'] = __('Hold shift and click to remove column from ORDER BY clause.'); +$js_messages['strSortHint'] = __('Click to sort results by this column.'); +$js_messages['strMultiSortHint'] = __('Shift+Click to add this column to ORDER BY clause or to toggle ASC/DESC.
- Control+CLick to remove column from ORDER BY clause'); $js_messages['strColMarkHint'] = __('Click to mark/unmark.'); $js_messages['strColNameCopyHint'] = __('Double-click to copy column name.'); $js_messages['strColVisibHint'] = __( diff --git a/js/columndelete.js b/js/multi_column_sort.js similarity index 53% rename from js/columndelete.js rename to js/multi_column_sort.js index c2571661b5..51ea76b34e 100644 --- a/js/columndelete.js +++ b/js/multi_column_sort.js @@ -1,12 +1,12 @@ /* vim: set expandtab sw=4 ts=4 sts=4: */ /** - * @fileoverview Implements the shiftkey + click remove column + * @fileoverview Implements the shiftkey + click remove column * from order by clause funcationality * @name columndelete * * @requires jQuery */ - + function captureURL(url) { var URL = {}; @@ -14,56 +14,62 @@ function captureURL(url) // Exclude the url part till HTTP url = url.substr(url.search("sql.php"), url.length); // The url part between ORDER BY and &session_max_rows needs to be replaced. - URL.head = url.substr(0, url.indexOf('ORDER+BY') + 9); - URL.tail = url.substr(url.indexOf("&session_max_rows"), url.length); - return URL; + URL['head'] = url.substr(0, url.indexOf('ORDER+BY') + 9); + URL['tail'] = url.substr(url.indexOf("&session_max_rows"), url.length); + return URL; } /** * This function is for navigating to the generated URL * - * @param object target HTMLAnchor element + * @param object target HTMLAnchor element * @param object parent HTMLDom Object */ -function redirect(target, parent) +function removeColumnFromMultiSort(target, parent) { var URL = captureURL(target); var begin = target.indexOf('ORDER+BY') + 8; var end = target.indexOf('&session_max_rows'); // get the names of the columns involved var between_part = target.substr(begin, end-begin); - var columns = between_part.split('%2C+'); + var columns = between_part.split('%2C+'); // If the given column is not part of the order clause exit from this function - var index = parent.find('small').length ? parent.find('small').text() : ''; - if (index === "") { + var index = parent.find('small').length ? parent.find('small').text() : ''; + if (index == ''){ return; } // Remove the current clicked column - columns.splice(index-1, 1); + columns.splice(index-1, 1); // If all the columns have been removed dont submit a query with nothing // After order by clause. - if (columns.length === 0) { - var head = URL.head; - head = head.slice(0,head.indexOf('ORDER+BY')); - URL.head = head; - // removing the last sort order should have priority over what + if (columns.length == 0){ + var head = URL['head']; + head = head.slice(0,head.indexOf('ORDER+BY')); + URL['head'] = head; + // removing the last sort order should have priority over what // is remembered via the RememberSorting directive - URL.tail += '&discard_remembered_sort=1'; + URL['tail'] += '&discard_remembered_sort=1'; } - var middle_part = columns.join('%2C+'); - url = URL.head + middle_part + URL.tail; - window.location.replace(url); + var middle_part = columns.join('%2C+'); + url = URL['head'] + middle_part + URL['tail']; + return url; } - AJAX.registerOnload('keyhandler.js', function () { - $("th.draggable.column_heading.pointer.marker a").live('click', function (event) { - if (event.shiftKey) { + $("th.draggable.column_heading.pointer.marker a").on('click', function (event) { + var url = $(this).parent().find('input').val(); + if (event.ctrlKey) { event.preventDefault(); - redirect($(this).attr("href"), $(this).parent()); + url = removeColumnFromMultiSort(url, $(this).parent()); + AJAX.source = $(this); + $.get(url, {'ajax_request' : true, 'ajax_page_request' : true}, AJAX.responseHandler); + } else if (event.shiftKey) { + event.preventDefault(); + AJAX.source = $(this); + $.get(url, {'ajax_request' : true, 'ajax_page_request' : true}, AJAX.responseHandler); } - }); + }); }); AJAX.registerTeardown('keyhandler.js', function () { diff --git a/libraries/DisplayResults.class.php b/libraries/DisplayResults.class.php index a39c7e3cbd..caa6c4534c 100644 --- a/libraries/DisplayResults.class.php +++ b/libraries/DisplayResults.class.php @@ -1852,7 +1852,7 @@ class PMA_DisplayResults // Generates the orderby clause part of the query which is part // of URL - list($sort_order, $order_img) = $this->_makeUrl( + list($single_sort_order, $multi_sort_order, $order_img) = $this->_getSingleAndMultiSortUrls( $sort_expression, $sort_expression_nodirection, $sort_tbl, $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index ); @@ -1862,24 +1862,34 @@ class PMA_DisplayResults . 'LOCK IN SHARE MODE))@is', $unsorted_sql_query, $regs3 )) { - $sorted_sql_query = $regs3[1] . $sort_order . $regs3[2]; + $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2]; + $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2]; } else { - $sorted_sql_query = $unsorted_sql_query . $sort_order; + $single_sorted_sql_query = $unsorted_sql_query . $single_sort_order; + $multi_sorted_sql_query = $unsorted_sql_query . $multi_sort_order; } - $_url_params = array( + $_single_url_params = array( 'db' => $this->__get('db'), 'table' => $this->__get('table'), - 'sql_query' => $sorted_sql_query, + 'sql_query' => $single_sorted_sql_query, 'session_max_rows' => $session_max_rows ); - $order_url = 'sql.php' . PMA_URL_getCommon($_url_params); + + $_multi_url_params = array( + 'db' => $this->__get('db'), + 'table' => $this->__get('table'), + 'sql_query' => $multi_sorted_sql_query, + 'session_max_rows' => $session_max_rows + ); + $single_order_url = 'sql.php' . PMA_URL_getCommon($_single_url_params); + $multi_order_url = 'sql.php' . PMA_URL_getCommon($_multi_url_params); // Displays the sorting URL // enable sort order swapping for image $order_link = $this->_getSortOrderLink( $order_img, $column_index, $direction, - $fields_meta, $order_url + $fields_meta, $single_order_url, $multi_order_url ); $sorted_header_html .= $this->_getDraggableClassForSortableColumns( @@ -1904,13 +1914,13 @@ class PMA_DisplayResults * @param array $fields_meta set of field properties * @param integer $column_index The index number to current column * - * @return array 2 element array - $order_link, $sorted_header_html + * @return array 3 element array - $single_sort_order, $sort_order, $order_img * * @access private * * @see _getOrderLinkAndSortedHeaderHtml() */ - private function _makeUrl( + private function _getSingleAndMultiSortUrls( $sort_expression, $sort_expression_nodirection, $sort_tbl, $name_to_use_in_sort, $sort_direction, $fields_meta, $column_index ) { @@ -1974,17 +1984,36 @@ class PMA_DisplayResults // For a special case where the code generates two dots between // column name and table name. $sort_order = preg_replace("/\.\./", ".", $sort_order); - // Incase the current column name is in the order by clause - // We need to generate the arrow button and related html + // Incase this is the current column save $single_sort_order + if ($current_name == $name_to_use_in_sort) { + if (strpos($current_name, '(') !== false) { + $single_sort_order = "\n" . 'ORDER BY ' . $current_name . ' '; + } else { + $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl + . PMA_Util::backquote( + $current_name + ) . ' '; + } + if ($is_in_sort) { + list($single_sort_order, $order_img) = $this->_getSortingUrlParams( + $sort_direction, $single_sort_order, $column_index, $index + ); + } else { + $single_sort_order .= strtoupper($sort_direction[$index]); + } + } if ($current_name == $name_to_use_in_sort && $is_in_sort) { + // We need to generate the arrow button and related html list($sort_order, $order_img) = $this->_getSortingUrlParams( $sort_direction, $sort_order, $column_index, $index ); + $order_img .= " " . ($index + 1) . ""; } else { $sort_order .= strtoupper($sort_direction[$index]); } // Separate columns by a comma $sort_order .= ", "; + unset($name_to_use_in_sort); } // remove the comma from the last column name in the newly @@ -1993,7 +2022,7 @@ class PMA_DisplayResults if (empty($order_img)) { $order_img = ''; } - return array($sort_order, $order_img); + return array($single_sort_order, $sort_order, $order_img); } /** @@ -2079,49 +2108,37 @@ class PMA_DisplayResults * @param integer $column_index the index of the column * @param integer $index the index of sort direction array. * - * @return array 2 element array - $sort_order, $order_img + * @return array 2 element array - $sort_order, $order_img * * @access private * - * @see _makeUrl() + * @see _getSingleAndMultiSortUrls() */ private function _getSortingUrlParams( $sort_direction, $sort_order, $column_index, $index ) { - - $index2 = $index + 1; if (strtoupper(trim($sort_direction[$index])) == self::DESCENDING_SORT_DIR) { - - $sort_number = "" . $index2 . ""; $sort_order .= ' ASC'; $order_img = ' ' . PMA_Util::getImage( 's_desc.png', __('Descending'), array('class' => "soimg$column_index", 'title' => '') ); - $order_img .= ' ' . PMA_Util::getImage( 's_asc.png', __('Ascending'), array('class' => "soimg$column_index hide", 'title' => '') - ) . $sort_number; - + ); } else { - - $sort_number = "" . $index2 . ""; $sort_order .= ' DESC'; $order_img = ' ' . PMA_Util::getImage( 's_asc.png', __('Ascending'), array('class' => "soimg$column_index", 'title' => '') ); - $order_img .= ' ' . PMA_Util::getImage( 's_desc.png', __('Descending'), array('class' => "soimg$column_index hide", 'title' => '') - ) . $sort_number; - + ); } - return array($sort_order, $order_img); - } // end of the '_getSortingUrlParams()' function @@ -2133,6 +2150,7 @@ class PMA_DisplayResults * @param string $direction the display direction * @param array $fields_meta set of field properties * @param string $order_url the url for sort + * @param string $multi_order_url the url for sort * * @return string the sort order link * @@ -2141,7 +2159,7 @@ class PMA_DisplayResults * @see _getTableHeaders() */ private function _getSortOrderLink( - $order_img, $col_index, $direction, $fields_meta, $order_url + $order_img, $col_index, $direction, $fields_meta, $order_url, $multi_order_url ) { $order_link_params = array(); @@ -2176,9 +2194,11 @@ class PMA_DisplayResults "
\n" ) : htmlspecialchars($fields_meta->name); + $inner_link_content = $order_link_content . $order_img + . ''; return PMA_Util::linkOrButton( - $order_url, $order_link_content . $order_img, + $order_url, $inner_link_content, $order_link_params, false, true ); diff --git a/sql.php b/sql.php index 7c67629d25..eebe9552a6 100644 --- a/sql.php +++ b/sql.php @@ -27,7 +27,7 @@ $scripts->addFile('jquery/jquery.uitablefilter.js'); $scripts->addFile('tbl_change.js'); $scripts->addFile('indexes.js'); $scripts->addFile('gis_data_editor.js'); -$scripts->addFile('columndelete.js'); +$scripts->addFile('multi_column_sort.js'); /** * Set ajax_reload in the response if it was already set