diff --git a/ChangeLog b/ChangeLog index 3cd0d8f326..ca7feb5a17 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,7 @@ phpMyAdmin - ChangeLog 3.5.0.0 (not yet released) + rfe #2021981 [interface] Add support for mass prefix change. + "up to date" message on main page when current version is up to date -+ Update to jQuery 1.6.1 ++ Update to jQuery 1.6.2 + Patch #3256122 [search] Show/hide db search results + Patch #3302354 Add gettext wrappers around a message + Remove deprecated function PMA_DBI_get_fields @@ -46,6 +46,8 @@ phpMyAdmin - ChangeLog - bug #3375325 [interface] Page list in navigation frame looks odd - bug #3313235 [interface] Error div misplaced - bug #3374802 [interface] Comment on a column breaks inline editing +- bug #3383711 [display] Order by a column in a view doesn't work in some cases +- bug #3386434 [interface] Add missing space to server status 3.4.4.0 (not yet released) - bug #3323060 [parser] SQL parser breaks AJAX requests if query has unclosed quotes diff --git a/bs_disp_as_mime_type.php b/bs_disp_as_mime_type.php index 9ec0ec5b29..5284e553fa 100644 --- a/bs_disp_as_mime_type.php +++ b/bs_disp_as_mime_type.php @@ -43,14 +43,7 @@ if ($fHnd === false) { $f_size = $hdrs['Content-Length']; -header("Expires: 0"); -header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); -header("Cache-Control: no-store, no-cache, must-revalidate"); -header("Cache-Control: post-check=0, pre-check=0", false); -header("Pragma: no-cache"); -header("Content-type: $c_type"); -header('Content-length: ' . $f_size); -header("Content-disposition: attachment; filename=" . basename($filename)); +PMA_download_header(basename($filename), $c_type, $f_size); $pos = 0; $content = ""; diff --git a/chart_export.php b/chart_export.php deleted file mode 100644 index c05332a031..0000000000 --- a/chart_export.php +++ /dev/null @@ -1,34 +0,0 @@ -'png', 'image/svg+xml'=>'svg'); - - if (! isset($allowed[$_REQUEST['type']])) exit('Invalid export type'); - - if (! preg_match("/(".implode("|",$allowed).")$/i", $_REQUEST['filename'])) - $_REQUEST['filename'] .= '.' . $allowed[$_REQUEST['type']]; - - header("Cache-Control: public"); - header("Content-Description: File Transfer"); - header("Content-Disposition: attachment; filename=".$_REQUEST['filename']); - header("Content-Type: ".$_REQUEST['type']); - header("Content-Transfer-Encoding: binary"); - - if ($allowed[$_REQUEST['type']] != 'svg') - echo base64_decode(substr($_REQUEST['image'], strpos($_REQUEST['image'],',') + 1)); - else - echo $_REQUEST['image']; - -} else exit('Invalid request'); -?> \ No newline at end of file diff --git a/db_datadict.php b/db_datadict.php index 531fbe06fc..acfb531414 100644 --- a/db_datadict.php +++ b/db_datadict.php @@ -119,8 +119,8 @@ while ($row = PMA_DBI_fetch_row($rowset)) { /** * Gets columns properties */ - $result = PMA_DBI_query('SHOW COLUMNS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); + $columns = PMA_DBI_get_columns($db, $table); + $fields_cnt = count($columns); if (PMA_MYSQL_INT_VERSION < 50025) { // We need this to correctly learn if a TIMESTAMP is NOT NULL, since @@ -181,44 +181,22 @@ while ($row = PMA_DBI_fetch_row($rowset)) { NULL'; @@ -284,8 +262,7 @@ while ($row = PMA_DBI_fetch_row($rowset)) { ?> diff --git a/db_operations.php b/db_operations.php index 2382389cf8..6b40947ae5 100644 --- a/db_operations.php +++ b/db_operations.php @@ -359,7 +359,7 @@ if (!$is_information_schema) {
- + -' . __('visual builder') . ''; ?> +', ''); ?> '; } } else { diff --git a/export.php b/export.php index d7c2f87e97..3678924783 100644 --- a/export.php +++ b/export.php @@ -347,25 +347,7 @@ if (!$save_on_server) { // this was reported to happen under Plesk) @ini_set('url_rewriter.tags',''); - header('Content-Type: ' . $mime_type); - header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - // Tested behavior of - // IE 5.50.4807.2300 - // IE 6.0.2800.1106 (small glitch, asks twice when I click Open) - // IE 6.0.2900.2180 - // Firefox 1.0.6 - // in http and https - header('Content-Disposition: attachment; filename="' . $filename . '"'); - if (PMA_USR_BROWSER_AGENT == 'IE') { - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Pragma: public'); - } else { - header('Pragma: no-cache'); - // test case: exporting a database into a .gz file with Safari - // would produce files not having the current time - // (added this header for Safari but should not harm other browsers) - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - } + PMA_download_header($filename, $mime_type); } else { // HTML if ($export_type == 'database') { diff --git a/file_echo.php b/file_echo.php new file mode 100644 index 0000000000..11a4487968 --- /dev/null +++ b/file_echo.php @@ -0,0 +1,68 @@ + 'png', + 'image/svg+xml' => 'svg', + ); + + /* Check whether MIME type is allowed */ + if (! isset($allowed[$_REQUEST['type']])) { + die('Invalid export type'); + } + + /* + * Check file name to match mime type and not contain new lines + * to prevent response splitting. + */ + $extension = $allowed[$_REQUEST['type']]; + $valid_match = '/^[^\n\r]*\.' . $extension . '$/'; + if (! preg_match($valid_match, $_REQUEST['filename'])) { + if (! preg_match('/^[^\n\r]*$/', $_REQUEST['filename'])) { + /* Filename is unsafe, discard it */ + $filename = 'download.' . $extension; + } else { + /* Add extension */ + $filename = $_REQUEST['filename'] . '.' . $extension; + } + } else { + /* Filename from request should be safe here */ + $filename = $_REQUEST['filename']; + } + + /* Decode data */ + if ($extension != 'svg') { + $data = substr($_REQUEST['image'], strpos($_REQUEST['image'], ',') + 1); + $data = base64_decode($data); + } else { + $data = $_REQUEST['image']; + } + + /* Send download header */ + PMA_download_header($filename, $_REQUEST['type'], strlen($data)); + + /* Send data */ + echo $data; + +/* For monitor chart config export */ +} else if (isset($_REQUEST['monitorconfig'])) { + PMA_download_header('monitor.cfg', 'application/force-download'); + echo urldecode($_REQUEST['monitorconfig']); + +/* For monitor chart config import */ +} else if (isset($_REQUEST['import'])) { + header('Content-type: text/plain'); + if(!file_exists($_FILES['file']['tmp_name'])) exit(); + echo file_get_contents($_FILES['file']['tmp_name']); +} +?> diff --git a/import_status.php b/import_status.php index 7c77e31f86..75a336104f 100644 --- a/import_status.php +++ b/import_status.php @@ -9,8 +9,7 @@ require_once './libraries/common.inc.php'; require_once './libraries/display_import_ajax.lib.php'; // AJAX requests can't be cached! -header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 -header("Expires: Sat, 11 Jan 1991 06:30:00 GMT"); // Date in the past +PMA_no_cache_header(); // $GLOBALS["message"] is used for asking for an import message if (isset($GLOBALS["message"]) && $GLOBALS["message"]) { diff --git a/index.php b/index.php index cb5eb63775..5e0fa3619a 100644 --- a/index.php +++ b/index.php @@ -132,7 +132,7 @@ include ('./libraries/header_http.inc.php'); // ]]> diff --git a/js/common.js b/js/common.js index b595108f83..9b5dcf6d31 100644 --- a/js/common.js +++ b/js/common.js @@ -19,7 +19,8 @@ var query_to_load = ''; * * @param string db name */ -function setDb(new_db) { +function setDb(new_db) +{ //alert('setDb(' + new_db + ')'); if (new_db != db) { // db has changed @@ -51,7 +52,8 @@ function setDb(new_db) { * * @param string table name */ -function setTable(new_table) { +function setTable(new_table) +{ //alert('setTable(' + new_table + ')'); if (new_table != table) { // table has changed @@ -86,7 +88,8 @@ function setTable(new_table) { * @uses encodeURIComponent() * @param string url name of page to be loaded */ -function refreshMain(url) { +function refreshMain(url) +{ if (! url) { if (db) { url = opendb_url; @@ -115,12 +118,13 @@ function refreshMain(url) { * @uses lang * @uses collation_connection * @uses encodeURIComponent() - * @param boolean force force reloading + * @param boolean force force reloading */ -function refreshNavigation(force) { +function refreshNavigation(force) +{ // The goTo() function won't refresh in case the target // url is the same as the url given as parameter, but sometimes - // we want to refresh anyway. + // we want to refresh anyway. if (typeof force != undefined && force && window.parent && window.parent.frame_navigation) { window.parent.frame_navigation.location.reload(); } else { @@ -174,7 +178,8 @@ function markDbTable(db, table) /** * sets current selected server, table and db (called from libraries/footer.inc.php) */ -function setAll( new_lang, new_collation_connection, new_server, new_db, new_table, new_token ) { +function setAll( new_lang, new_collation_connection, new_server, new_db, new_table, new_token ) +{ //alert('setAll( ' + new_lang + ', ' + new_collation_connection + ', ' + new_server + ', ' + new_db + ', ' + new_table + ', ' + new_token + ' )'); if (new_server != server || new_lang != lang || new_collation_connection != collation_connection) { @@ -257,7 +262,8 @@ function focus_querywindow(sql_query) * inserts query string into query window textarea * called from script tag in querywindow */ -function insertQuery() { +function insertQuery() +{ if (query_to_load != '' && querywindow.document && querywindow.document.getElementById && querywindow.document.getElementById('sqlquery')) { querywindow.document.getElementById('sqlquery').value = query_to_load; query_to_load = ''; @@ -266,7 +272,8 @@ function insertQuery() { return false; } -function open_querywindow( url ) { +function open_querywindow( url ) +{ if ( ! url ) { url = 'querywindow.php?' + common_query + '&db=' + encodeURIComponent(db) + '&table=' + encodeURIComponent(table); } @@ -293,7 +300,8 @@ function open_querywindow( url ) { return true; } -function refreshQuerywindow( url ) { +function refreshQuerywindow( url ) +{ if ( ! querywindow.closed && querywindow.location ) { if ( ! querywindow.document.sqlform.LockFromUpdate @@ -310,7 +318,8 @@ function refreshQuerywindow( url ) { * @param string targeturl new url to load * @param string target frame where to load the new url */ -function goTo(targeturl, target) { +function goTo(targeturl, target) +{ //alert(targeturl); if ( target == 'main' ) { target = window.frame_content; @@ -339,7 +348,8 @@ function goTo(targeturl, target) { } // opens selected db in main frame -function openDb(new_db) { +function openDb(new_db) +{ //alert('opendb(' + new_db + ')'); setDb(new_db); setTable(''); @@ -347,7 +357,8 @@ function openDb(new_db) { return true; } -function updateTableTitle( table_link_id, new_title ) { +function updateTableTitle( table_link_id, new_title ) +{ //alert('updateTableTitle'); if ( window.parent.frame_navigation.document && window.parent.frame_navigation.document.getElementById(table_link_id) ) { var left = window.parent.frame_navigation.document; diff --git a/js/config.js b/js/config.js index 94b2650616..53702e3938 100644 --- a/js/config.js +++ b/js/config.js @@ -14,7 +14,8 @@ var PMA_messages = {}; * * @param {Element} field */ -function getFieldType(field) { +function getFieldType(field) +{ field = $(field); var tagName = field.prop('tagName'); if (tagName == 'INPUT') { @@ -40,7 +41,8 @@ function getFieldType(field) { * @param {String} field_type see {@link #getFieldType} * @param {String|Boolean} [value] */ -function setFieldValue(field, field_type, value) { +function setFieldValue(field, field_type, value) +{ field = $(field); switch (field_type) { case 'text': @@ -78,7 +80,8 @@ function setFieldValue(field, field_type, value) { * @param {String} field_type returned by {@link #getFieldType} * @type Boolean|String|String[] */ -function getFieldValue(field, field_type) { +function getFieldValue(field, field_type) +{ field = $(field); switch (field_type) { case 'text': @@ -101,7 +104,8 @@ function getFieldValue(field, field_type) { /** * Returns values for all fields in fieldsets */ -function getAllValues() { +function getAllValues() +{ var elements = $('fieldset input, fieldset select, fieldset textarea'); var values = {}; var type, value; @@ -126,7 +130,8 @@ function getAllValues() { * @param {String} type * @return boolean */ -function checkFieldDefault(field, type) { +function checkFieldDefault(field, type) +{ field = $(field); var field_id = field.attr('id'); if (typeof defaultValues[field_id] == 'undefined') { @@ -157,7 +162,8 @@ function checkFieldDefault(field, type) { * Returns element's id prefix * @param {Element} element */ -function getIdPrefix(element) { +function getIdPrefix(element) +{ return $(element).attr('id').replace(/[^-]+$/, ''); } @@ -254,7 +260,8 @@ var validators = { * @param {boolean} onKeyUp whether fire on key up * @param {Array} params validation function parameters */ -function validateField(id, type, onKeyUp, params) { +function validateField(id, type, onKeyUp, params) +{ if (typeof validators[type] == 'undefined') { return; } @@ -272,7 +279,8 @@ function validateField(id, type, onKeyUp, params) { * @type Array * @return array of [function, paramseters to be passed to function] */ -function getFieldValidators(field_id, onKeyUpOnly) { +function getFieldValidators(field_id, onKeyUpOnly) +{ // look for field bound validator var name = field_id.match(/[^-]+$/)[0]; if (typeof validators._field[name] != 'undefined') { @@ -302,7 +310,8 @@ function getFieldValidators(field_id, onKeyUpOnly) { * * @param {Object} error_list list of errors in the form {field id: error array} */ -function displayErrors(error_list) { +function displayErrors(error_list) +{ for (var field_id in error_list) { var errors = error_list[field_id]; var field = $('#'+field_id); @@ -354,7 +363,8 @@ function displayErrors(error_list) { * @param {boolean} isKeyUp * @param {Object} errors */ -function validate_fieldset(fieldset, isKeyUp, errors) { +function validate_fieldset(fieldset, isKeyUp, errors) +{ fieldset = $(fieldset); if (fieldset.length && typeof validators._fieldset[fieldset.attr('id')] != 'undefined') { var fieldset_errors = validators._fieldset[fieldset.attr('id')].apply(fieldset[0], [isKeyUp]); @@ -377,7 +387,8 @@ function validate_fieldset(fieldset, isKeyUp, errors) { * @param {boolean} isKeyUp * @param {Object} errors */ -function validate_field(field, isKeyUp, errors) { +function validate_field(field, isKeyUp, errors) +{ field = $(field); var field_id = field.attr('id'); errors[field_id] = []; @@ -403,7 +414,8 @@ function validate_field(field, isKeyUp, errors) { * @param {Element} field * @param {boolean} isKeyUp */ -function validate_field_and_fieldset(field, isKeyUp) { +function validate_field_and_fieldset(field, isKeyUp) +{ field = $(field); var errors = {}; validate_field(field, isKeyUp, errors); @@ -416,7 +428,8 @@ function validate_field_and_fieldset(field, isKeyUp) { * * @param {Element} field */ -function markField(field) { +function markField(field) +{ field = $(field); var type = getFieldType(field); var isDefault = checkFieldDefault(field, type); @@ -433,7 +446,8 @@ function markField(field) { * @param {Element} field * @param {boolean} display */ -function setRestoreDefaultBtn(field, display) { +function setRestoreDefaultBtn(field, display) +{ var el = $(field).closest('td').find('.restore-default img'); el[display ? 'show' : 'hide'](); } @@ -495,7 +509,8 @@ $(function() { * * @param {String} tab_id */ -function setTab(tab_id) { +function setTab(tab_id) +{ $('.tabs a').removeClass('active').filter('[href=' + tab_id + ']').addClass('active'); $('.tabs_contents fieldset').hide().filter(tab_id).show(); location.hash = 'tab_' + tab_id.substr(1); @@ -562,7 +577,8 @@ $(function() { * * @param {String} field_id */ -function restoreField(field_id) { +function restoreField(field_id) +{ var field = $('#'+field_id); if (field.length == 0 || defaultValues[field_id] == undefined) { return; diff --git a/js/db_search.js b/js/db_search.js index 72b30463d4..3e2eeb40a7 100644 --- a/js/db_search.js +++ b/js/db_search.js @@ -15,7 +15,8 @@ */ /** Loads the database search results */ -function loadResult(result_path , table_name , link , ajaxEnable){ +function loadResult(result_path , table_name , link , ajaxEnable) +{ $(document).ready(function() { if(ajaxEnable) { @@ -43,7 +44,8 @@ function loadResult(result_path , table_name , link , ajaxEnable){ } /** Delete the selected search results */ -function deleteResult(result_path , msg , ajaxEnable){ +function deleteResult(result_path , msg , ajaxEnable) +{ $(document).ready(function() { /** Hides the results shown by the browse criteria */ $("#table-info").hide(); diff --git a/js/db_structure.js b/js/db_structure.js index 559819e11c..c3b280a651 100644 --- a/js/db_structure.js +++ b/js/db_structure.js @@ -24,7 +24,8 @@ * * @param jQuery object $this_anchor */ -function PMA_adjustTotals($this_anchor) { +function PMA_adjustTotals($this_anchor) +{ var $parent_tr = $this_anchor.closest('tr'); var $rows_td = $parent_tr.find('.tbl_rows'); var $size_td = $parent_tr.find('.tbl_size'); diff --git a/js/export.js b/js/export.js index 3fd3c00e9d..a356d4526c 100644 --- a/js/export.js +++ b/js/export.js @@ -3,7 +3,7 @@ * Functions used in the export tab * */ - + /** * Toggles the hiding and showing of each plugin's options * according to the currently selected plugin from the dropdown list @@ -19,7 +19,7 @@ }); /** - * Toggles the enabling and disabling of the SQL plugin's comment options that apply only when exporting structure + * Toggles the enabling and disabling of the SQL plugin's comment options that apply only when exporting structure */ $(document).ready(function() { $("input[type='radio'][name$='sql_structure_or_data']").change(function() { @@ -50,7 +50,8 @@ $(document).ready(function() { * options */ -function toggle_structure_data_opts(pluginName) { +function toggle_structure_data_opts(pluginName) +{ var radioFormName = pluginName + "_structure_or_data"; var dataDiv = "#" + pluginName + "_data"; var structureDiv = "#" + pluginName + "_structure"; @@ -89,7 +90,8 @@ $(document).ready(function() { /** * Toggles the disabling of the "save to file" options */ -function toggle_save_to_file() { +function toggle_save_to_file() +{ if($("#radio_dump_asfile:checked").length == 0) { $("#ul_save_asfile > li").fadeTo('fast', 0.4); $("#ul_save_asfile > li > input").attr('disabled', 'disabled'); @@ -111,7 +113,8 @@ $(document).ready(function() { /** * For SQL plugin, toggles the disabling of the "display comments" options */ -function toggle_sql_include_comments() { +function toggle_sql_include_comments() +{ $("#checkbox_sql_include_comments").change(function() { if($("#checkbox_sql_include_comments:checked").length == 0) { $("#ul_include_comments > li").fadeTo('fast', 0.4); @@ -130,8 +133,8 @@ function toggle_sql_include_comments() { } /** - * For SQL plugin, if "CREATE TABLE options" is checked/unchecked, check/uncheck each of its sub-options - */ + * For SQL plugin, if "CREATE TABLE options" is checked/unchecked, check/uncheck each of its sub-options + */ $(document).ready(function() { $("#checkbox_sql_create_table_statements").change(function() { if($("#checkbox_sql_create_table_statements:checked").length == 0) { @@ -144,7 +147,7 @@ $(document).ready(function() { }); }); -/** +/** * Disables the view output as text option if the output must be saved as a file */ $(document).ready(function() { @@ -164,7 +167,8 @@ $(document).ready(function() { /** * Toggles display of options when quick and custom export are selected */ -function toggle_quick_or_custom() { +function toggle_quick_or_custom() +{ if($("$(this):checked").attr("value") == "custom") { $("#databases_and_tables").show(); $("#rows").show(); @@ -222,4 +226,4 @@ $(document).ready(function() { $("input[type='text'][name='limit_from']").removeAttr('disabled'); } }); -}); \ No newline at end of file +}); diff --git a/js/functions.js b/js/functions.js index 287195dcce..bb976ce91f 100644 --- a/js/functions.js +++ b/js/functions.js @@ -29,14 +29,14 @@ var codemirror_editor = false; */ var chart_activeTimeouts = new Object(); - /** * Add a hidden field to the form to indicate that this will be an * Ajax request (only if this hidden field does not exist) * * @param object the form */ -function PMA_prepareForAjaxRequest($form) { +function PMA_prepareForAjaxRequest($form) +{ if (! $form.find('input:hidden').is('#ajax_request_hidden')) { $form.append(''); } @@ -49,7 +49,8 @@ function PMA_prepareForAjaxRequest($form) { * * @return boolean always true */ -function suggestPassword(passwd_form) { +function suggestPassword(passwd_form) +{ // restrict the password to just letters and numbers to avoid problems: // "editors and viewers regard the password as multiple words and // things like double click no longer work" @@ -69,7 +70,8 @@ function suggestPassword(passwd_form) { /** * Version string to integer conversion. */ -function parseVersionString (str) { +function parseVersionString (str) +{ if (typeof(str) != 'string') { return false; } var add = 0; // Parse possible alpha/beta/rc/ @@ -99,7 +101,8 @@ function parseVersionString (str) { /** * Indicates current available version on main page. */ -function PMA_current_version() { +function PMA_current_version() +{ var current = parseVersionString(pmaversion); var latest = parseVersionString(PMA_latest_version); var version_information_message = PMA_messages['strLatestAvailable'] + ' ' + PMA_latest_version; @@ -125,7 +128,8 @@ function PMA_current_version() { * */ -function displayPasswordGenerateButton() { +function displayPasswordGenerateButton() +{ $('#tr_element_before_generate_password').parent().append('' + PMA_messages['strGeneratePassword'] + ''); $('#div_element_before_generate_password').parent().append('
'); } @@ -135,7 +139,8 @@ function displayPasswordGenerateButton() { * * @param object $this_element a jQuery object pointing to the element */ -function PMA_addDatepicker($this_element, options) { +function PMA_addDatepicker($this_element, options) +{ var showTimeOption = false; if ($this_element.is('.datetimefield')) { showTimeOption = true; @@ -177,7 +182,8 @@ function PMA_addDatepicker($this_element, options) { * @param boolean only_once if true this is only done once * f.e. only on first focus */ -function selectContent( element, lock, only_once ) { +function selectContent( element, lock, only_once ) +{ if ( only_once && only_once_elements[element.name] ) { return; } @@ -721,7 +727,8 @@ var marked_row = new Array; * * @param container DOM element */ -function markAllRows( container_id ) { +function markAllRows( container_id ) +{ $("#"+container_id).find("input:checkbox:enabled").attr('checked', 'checked') .parents("tr").addClass("marked"); @@ -734,7 +741,8 @@ function markAllRows( container_id ) { * * @param container DOM element */ -function unMarkAllRows( container_id ) { +function unMarkAllRows( container_id ) +{ $("#"+container_id).find("input:checkbox:enabled").removeAttr('checked') .parents("tr").removeClass("marked"); @@ -748,7 +756,8 @@ function unMarkAllRows( container_id ) { * @param boolean state new value for checkbox (true or false) * @return boolean always true */ -function setCheckboxes( container_id, state ) { +function setCheckboxes( container_id, state ) +{ if(state) { $("#"+container_id).find("input:checkbox").attr('checked', 'checked'); @@ -778,7 +787,8 @@ function setSelectOptions(the_form, the_select, do_check) /** * Sets current value for query box. */ -function setQuery(query) { +function setQuery(query) +{ if (codemirror_editor) { codemirror_editor.setValue(query); } else { @@ -791,7 +801,8 @@ function setQuery(query) { * Create quick sql statements. * */ -function insertQuery(queryType) { +function insertQuery(queryType) +{ if (queryType == "clear") { setQuery(''); return; @@ -840,7 +851,8 @@ function insertQuery(queryType) { * Inserts multiple fields. * */ -function insertValueQuery() { +function insertValueQuery() +{ var myQuery = document.sqlform.sql_query; var myListBox = document.sqlform.dummy; @@ -885,14 +897,16 @@ function insertValueQuery() { /** * listbox redirection */ -function goToUrl(selObj, goToLocation) { +function goToUrl(selObj, goToLocation) +{ eval("document.location.href = '" + goToLocation + "pos=" + selObj.options[selObj.selectedIndex].value + "'"); } /** * getElement */ -function getElement(e,f){ +function getElement(e,f) +{ if(document.layers){ f=(f)?f:self; if(f.document.layers[e]) { @@ -911,7 +925,8 @@ function getElement(e,f){ /** * Refresh the WYSIWYG scratchboard after changes have been made */ -function refreshDragOption(e) { +function refreshDragOption(e) +{ var elm = $('#' + e); if (elm.css('visibility') == 'visible') { refreshLayout(); @@ -922,7 +937,8 @@ function refreshDragOption(e) { /** * Refresh/resize the WYSIWYG scratchboard */ -function refreshLayout() { +function refreshLayout() +{ var elm = $('#pdflayout') var orientation = $('#orientation_opt').val(); if($('#paper_opt').length==1){ @@ -944,7 +960,8 @@ function refreshLayout() { /** * Show/hide the WYSIWYG scratchboard */ -function ToggleDragDrop(e) { +function ToggleDragDrop(e) +{ var elm = $('#' + e); if (elm.css('visibility') == 'hidden') { PDFinit(); /* Defined in pdf_pages.php */ @@ -962,7 +979,8 @@ function ToggleDragDrop(e) { * PDF scratchboard: When a position is entered manually, update * the fields inside the scratchboard. */ -function dragPlace(no, axis, value) { +function dragPlace(no, axis, value) +{ var elm = $('#table_' + no); if (axis == 'x') { elm.css('left', value + 'px'); @@ -974,7 +992,8 @@ function dragPlace(no, axis, value) { /** * Returns paper sizes for a given format */ -function pdfPaperSize(format, axis) { +function pdfPaperSize(format, axis) +{ switch (format.toUpperCase()) { case '4A0': if (axis == 'x') return 4767.87; else return 6740.79; @@ -1302,7 +1321,8 @@ $(document).ready(function(){ * optional, defaults to 5000 * @return jQuery object jQuery Element that holds the message div */ -function PMA_ajaxShowMessage(message, timeout) { +function PMA_ajaxShowMessage(message, timeout) +{ //Handle the case when a empty data.message is passed. We don't want the empty message if (message == '') { @@ -1352,7 +1372,8 @@ function PMA_ajaxShowMessage(message, timeout) { /** * Removes the message shown for an Ajax operation when it's completed */ -function PMA_ajaxRemoveMessage($this_msgbox) { +function PMA_ajaxRemoveMessage($this_msgbox) +{ if ($this_msgbox != undefined && $this_msgbox instanceof jQuery) { $this_msgbox .stop(true, true) @@ -1363,7 +1384,8 @@ function PMA_ajaxRemoveMessage($this_msgbox) { /** * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected */ -function PMA_showNoticeForEnum(selectElement) { +function PMA_showNoticeForEnum(selectElement) +{ var enum_notice_id = selectElement.attr("id").split("_")[1]; enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2]) + 1); var selectedType = selectElement.attr("value"); @@ -1377,7 +1399,8 @@ function PMA_showNoticeForEnum(selectElement) { /** * Generates a dialog box to pop up the create_table form */ -function PMA_createTableDialog( div, url , target) { +function PMA_createTableDialog( div, url , target) +{ /** * @var button_options Object that stores the options passed to jQueryUI * dialog @@ -1440,7 +1463,8 @@ function PMA_createTableDialog( div, url , target) { * * @return object The created highcharts instance */ -function PMA_createChart(passedSettings) { +function PMA_createChart(passedSettings) +{ var container = passedSettings.chart.renderTo; var settings = { @@ -1575,7 +1599,8 @@ function PMA_createChart(passedSettings) { /* * Creates a Profiling Chart. Used in sql.php and server_status.js */ -function PMA_createProfilingChart(data, options) { +function PMA_createProfilingChart(data, options) +{ return PMA_createChart($.extend(true, { chart: { renderTo: 'profilingchart', @@ -1609,15 +1634,18 @@ function PMA_createProfilingChart(data, options) { } // Formats a profiling duration nicely. Used in PMA_createProfilingChart() and server_status.js -function PMA_prettyProfilingNum(num, acc) { +function PMA_prettyProfilingNum(num, acc) +{ if (!acc) { - acc = 1; + acc = 2; } acc = Math.pow(10,acc); - if (num*1000 < 0.1) { - num = Math.round(acc*(num*1000*1000))/acc + 'µ' + if (num * 1000 < 0.1) { + num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ'; } else if (num < 0.1) { - num = Math.round(acc*(num*1000))/acc + 'm' + num = Math.round(acc * (num * 1000)) / acc + 'm'; + } else { + num = Math.round(acc * num) / acc; } return num + 's'; @@ -2018,7 +2046,7 @@ $(document).ready(function() { $("#result_query .notice").remove(); $("#result_query").prepend((data.message)); $("#copyTable").find("select[name='target_db'] option[value="+data.db+"]").attr('selected', 'selected'); - + //Refresh navigation frame when the table is coppied if (window.parent && window.parent.frame_navigation) { window.parent.frame_navigation.location.reload(); @@ -2202,7 +2230,8 @@ $(document).ready(function() { }); }); -function PMA_verifyTypeOfAllColumns() { +function PMA_verifyTypeOfAllColumns() +{ $("select[class='column_type']").each(function() { PMA_showNoticeForEnum($(this)); }); @@ -2211,7 +2240,8 @@ function PMA_verifyTypeOfAllColumns() { /** * Closes the ENUM/SET editor and removes the data in it */ -function disable_popup() { +function disable_popup() +{ $("#popup_background").fadeOut("fast"); $("#enum_editor").fadeOut("fast"); // clear the data from the text boxes @@ -2307,7 +2337,8 @@ $(document).ready(function() { displayMoreTableOpts(); }); -function displayMoreTableOpts() { +function displayMoreTableOpts() +{ // Remove the actions from the table cells (they are available by default for JavaScript-disabled browsers) // if the table is not a view or information_schema (otherwise there is only one action to hide and there's no point) if($("input[type='hidden'][name='table_type']").val() == "table") { @@ -2424,7 +2455,8 @@ function checkIndexName(form_id) * ommit this parameter the function searches * the footnotes in the whole body **/ -function PMA_convertFootnotesToTooltips($div) { +function PMA_convertFootnotesToTooltips($div) +{ // Hide the footnotes from the footer (which are displayed for // JavaScript-disabled browsers) since the tooltip is sufficient @@ -2570,14 +2602,16 @@ $(function() { /** * Get the row number from the classlist (for example, row_1) */ -function PMA_getRowNumber(classlist) { +function PMA_getRowNumber(classlist) +{ return parseInt(classlist.split(/\s+row_/)[1]); } /** * Changes status of slider */ -function PMA_set_status_label(id) { +function PMA_set_status_label(id) +{ if ($('#' + id).css('display') == 'none') { $('#anchor_status_' + id).text('+ '); } else { @@ -2588,7 +2622,8 @@ function PMA_set_status_label(id) { /** * Initializes slider effect. */ -function PMA_init_slider() { +function PMA_init_slider() +{ $('.pma_auto_slider').each(function(idx, e) { if ($(e).hasClass('slider_init_done')) return; $(e).addClass('slider_init_done'); @@ -2869,7 +2904,8 @@ $(document).ready(function() { * * @return bool True on success, false on failure */ -function PMA_slidingMessage(msg, $obj) { +function PMA_slidingMessage(msg, $obj) +{ if (msg == undefined || msg.length == 0) { // Don't show an empty message return false; @@ -3040,7 +3076,8 @@ $(document).ready(function() { * Create default PMA tooltip for the element specified. The default appearance * can be overriden by specifying optional "options" parameter (see qTip options). */ -function PMA_createqTip($elements, content, options) { +function PMA_createqTip($elements, content, options) +{ var o = { content: content, style: { diff --git a/js/highcharts/exporting.js b/js/highcharts/exporting.js index cb04357b32..6ac3a87ddc 100644 --- a/js/highcharts/exporting.js +++ b/js/highcharts/exporting.js @@ -5,6 +5,9 @@ * (c) 2010 Torstein Hønsi * * License: www.highcharts.com/license + * + * Please Note: This file has been adjusted for use in phpMyAdmin, + * to allow chart exporting without the batik library */ // JSLint options: @@ -104,7 +107,7 @@ defaultOptions.exporting = { //enabled: true, //filename: 'chart', type: 'image/png', - url: 'chart_export.php', + url: 'file_echo.php', width: 800, buttons: { exportButton: { diff --git a/js/import.js b/js/import.js index 84457163a3..68a3793a97 100644 --- a/js/import.js +++ b/js/import.js @@ -9,10 +9,11 @@ * Toggles the hiding and showing of each plugin's options * according to the currently selected plugin from the dropdown list */ -function changePluginOpts() { - $(".format_specific_options").each(function() { +function changePluginOpts() +{ + $(".format_specific_options").each(function() { $(this).hide(); - }); + }); var selected_plugin_name = $("#plugins option:selected").attr("value"); $("#" + selected_plugin_name + "_options").fadeIn('slow'); if(selected_plugin_name == "csv") { @@ -26,7 +27,8 @@ function changePluginOpts() { * Toggles the hiding and showing of each plugin's options and sets the selected value * in the plugin dropdown list according to the format of the selected file */ -function matchFile(fname) { +function matchFile(fname) +{ var fname_array = fname.toLowerCase().split("."); var len = fname_array.length; if(len != 0) { @@ -43,7 +45,7 @@ function matchFile(fname) { } } $(document).ready(function() { - // Initially display the options for the selected plugin + // Initially display the options for the selected plugin changePluginOpts(); // Whenever the selected plugin changes, change the options displayed @@ -79,4 +81,4 @@ $(document).ready(function() { $("#scroll_to_options_msg").hide(); $(".format_specific_options").css({ "border": 0, "margin": 0, "padding": 0 }); $(".format_specific_options h3").remove(); -}); \ No newline at end of file +}); diff --git a/js/jquery/jquery-1.6.1.js b/js/jquery/jquery-1.6.2.js similarity index 96% rename from js/jquery/jquery-1.6.1.js rename to js/jquery/jquery-1.6.2.js index 5d5a1d58ee..f3201aacb6 100644 --- a/js/jquery/jquery-1.6.1.js +++ b/js/jquery/jquery-1.6.2.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v1.6.1 + * jQuery JavaScript Library v1.6.2 * http://jquery.com/ * * Copyright 2011, John Resig @@ -11,7 +11,7 @@ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: Thu May 12 15:04:36 2011 -0400 + * Date: Thu Jun 30 14:16:56 2011 -0400 */ (function( window, undefined ) { @@ -65,6 +65,14 @@ var jQuery = function( selector, context ) { rmsie = /(msie) ([\w.]+)/, rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + // Matches dashed string for camelizing + rdashAlpha = /-([a-z])/ig, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + // Keep a UserAgent string for use with jQuery.browser userAgent = navigator.userAgent, @@ -204,7 +212,7 @@ jQuery.fn = jQuery.prototype = { selector: "", // The current version of jQuery being used - jquery: "1.6.1", + jquery: "1.6.2", // The default length of a jQuery object is 0 length: 0, @@ -603,6 +611,12 @@ jQuery.extend({ } }, + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.replace( rdashAlpha, fcamelCase ); + }, + nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }, @@ -799,7 +813,7 @@ jQuery.extend({ }, // Mutifunctional method to get and set values to a collection - // The value/s can be optionally by executed if its a function + // The value/s can optionally be executed if it's a function access: function( elems, key, value, exec, fn, pass ) { var length = elems.length; @@ -930,7 +944,6 @@ function doScrollCheck() { jQuery.ready(); } -// Expose jQuery to the global object return jQuery; })(); @@ -1147,7 +1160,9 @@ jQuery.support = (function() { support, fragment, body, - bodyStyle, + testElementParent, + testElement, + testElementStyle, tds, events, eventName, @@ -1241,11 +1256,10 @@ jQuery.support = (function() { } if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", function click() { + div.attachEvent( "onclick", function() { // Cloning a node shouldn't copy over any // bound event handlers (IE does this) support.noCloneEvent = false; - div.detachEvent( "onclick", click ); }); div.cloneNode( true ).fireEvent( "onclick" ); } @@ -1270,22 +1284,30 @@ jQuery.support = (function() { // Figure out if the W3C box model works as expected div.style.width = div.style.paddingLeft = "1px"; - // We use our own, invisible, body - body = document.createElement( "body" ); - bodyStyle = { + body = document.getElementsByTagName( "body" )[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { visibility: "hidden", width: 0, height: 0, border: 0, - margin: 0, - // Set background to avoid IE crashes when removing (#9028) - background: "none" + margin: 0 }; - for ( i in bodyStyle ) { - body.style[ i ] = bodyStyle[ i ]; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); } - body.appendChild( div ); - documentElement.insertBefore( body, documentElement.firstChild ); + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) @@ -1344,8 +1366,8 @@ jQuery.support = (function() { } // Remove the body element we added - body.innerHTML = ""; - documentElement.removeChild( body ); + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); // Technique from Juriy Zaytsev // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ @@ -1369,6 +1391,9 @@ jQuery.support = (function() { } } + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + return support; })(); @@ -1486,7 +1511,10 @@ jQuery.extend({ return thisCache[ internalKey ] && thisCache[ internalKey ].events; } - return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache; + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; }, removeData: function( elem, name, pvt /* Internal Use Only */ ) { @@ -1882,7 +1910,7 @@ var rclass = /[\n\t\r]/g, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - rinvalidChar = /\:/, + rinvalidChar = /\:|^on/, formHook, boolHook; jQuery.fn.extend({ @@ -1912,30 +1940,31 @@ jQuery.fn.extend({ }, addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this); - self.addClass( value.call(this, i, self.attr("class") || "") ); + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); }); } if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspace ); + classNames = value.split( rspace ); - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; if ( elem.nodeType === 1 ) { - if ( !elem.className ) { + if ( !elem.className && classNames.length === 1 ) { elem.className = value; } else { - var className = " " + elem.className + " ", - setClass = elem.className; + setClass = " " + elem.className + " "; - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { - setClass += " " + classNames[c]; + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; } } elem.className = jQuery.trim( setClass ); @@ -1948,24 +1977,25 @@ jQuery.fn.extend({ }, removeClass: function( value ) { - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - self.removeClass( value.call(this, i, self.attr("class")) ); + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); }); } if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split( rspace ); + classNames = (value || "").split( rspace ); - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; if ( elem.nodeType === 1 && elem.className ) { if ( value ) { - var className = (" " + elem.className + " ").replace(rclass, " "); - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[c] + " ", " "); + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); } elem.className = jQuery.trim( className ); @@ -1984,9 +2014,8 @@ jQuery.fn.extend({ isBool = typeof stateVal === "boolean"; if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this); - self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } @@ -2040,7 +2069,13 @@ jQuery.fn.extend({ return ret; } - return (elem.value || "").replace(rreturn, ""); + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; } return undefined; @@ -2186,20 +2221,23 @@ jQuery.extend({ notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); // Normalize the name if needed - name = notxml && jQuery.attrFix[ name ] || name; + if ( notxml ) { + name = jQuery.attrFix[ name ] || name; - hooks = jQuery.attrHooks[ name ]; + hooks = jQuery.attrHooks[ name ]; - if ( !hooks ) { - // Use boolHook for boolean attributes - if ( rboolean.test( name ) && - (typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase()) ) { + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) ) { - hooks = boolHook; + hooks = boolHook; - // Use formHook for forms and if the name contains certain characters - } else if ( formHook && (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { - hooks = formHook; + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } } } @@ -2217,8 +2255,8 @@ jQuery.extend({ return value; } - } else if ( hooks && "get" in hooks && notxml ) { - return hooks.get( elem, name ); + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; } else { @@ -2282,6 +2320,25 @@ jQuery.extend({ 0 : undefined; } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } } }, @@ -2311,10 +2368,11 @@ jQuery.extend({ var ret, hooks, notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - // Try to normalize/fix the name - name = notxml && jQuery.propFix[ name ] || name; - - hooks = jQuery.propHooks[ name ]; + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } if ( value !== undefined ) { if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { @@ -2341,7 +2399,7 @@ jQuery.extend({ boolHook = { get: function( elem, name ) { // Align boolean attributes with corresponding properties - return elem[ jQuery.propFix[ name ] || name ] ? + return jQuery.prop( elem, name ) ? name.toLowerCase() : undefined; }, @@ -2356,7 +2414,7 @@ boolHook = { propName = jQuery.propFix[ name ] || name; if ( propName in elem ) { // Only set the IDL specifically if it already exists on the element - elem[ propName ] = value; + elem[ propName ] = true; } elem.setAttribute( name, name.toLowerCase() ); @@ -2365,24 +2423,6 @@ boolHook = { } }; -// Use the value property for back compat -// Use the formHook for button elements in IE6/7 (#1954) -jQuery.attrHooks.value = { - get: function( elem, name ) { - if ( formHook && jQuery.nodeName( elem, "button" ) ) { - return formHook.get( elem, name ); - } - return elem.value; - }, - set: function( elem, value, name ) { - if ( formHook && jQuery.nodeName( elem, "button" ) ) { - return formHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } -}; - // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !jQuery.support.getSetAttribute ) { @@ -2390,7 +2430,7 @@ if ( !jQuery.support.getSetAttribute ) { jQuery.attrFix = jQuery.propFix; // Use this for any attribute on a form in IE6/7 - formHook = jQuery.attrHooks.name = jQuery.valHooks.button = { + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = { get: function( elem, name ) { var ret; ret = elem.getAttributeNode( name ); @@ -2493,8 +2533,7 @@ jQuery.each([ "radio", "checkbox" ], function() { -var hasOwn = Object.prototype.hasOwnProperty, - rnamespaces = /\.(.*)$/, +var rnamespaces = /\.(.*)$/, rformElems = /^(?:textarea|input|select)$/i, rperiod = /\./g, rspaces = / /g, @@ -2838,7 +2877,7 @@ jQuery.event = { event.target = elem; // Clone any incoming data and prepend the event, creating the handler arg list - data = data ? jQuery.makeArray( data ) : []; + data = data != null ? jQuery.makeArray( data ) : []; data.unshift( event ); var cur = elem, @@ -3144,34 +3183,27 @@ jQuery.Event.prototype = { // Checks if an event happened on an element within another element // Used in jQuery.event.special.mouseenter and mouseleave handlers var withinElement = function( event ) { - // Check if mouse(over|out) are still within the same parent element - var parent = event.relatedTarget; - // set the correct event type + // Check if mouse(over|out) are still within the same parent element + var related = event.relatedTarget, + inside = false, + eventType = event.type; + event.type = event.data; - // Firefox sometimes assigns relatedTarget a XUL element - // which we cannot access the parentNode property of - try { + if ( related !== this ) { - // Chrome does something similar, the parentNode property - // can be accessed but is null. - if ( parent && parent !== document && !parent.parentNode ) { - return; + if ( related ) { + inside = jQuery.contains( this, related ); } - // Traverse up the tree - while ( parent && parent !== this ) { - parent = parent.parentNode; - } + if ( !inside ) { - if ( parent !== this ) { - // handle event if we actually just moused on to a non sub-element jQuery.event.handle.apply( this, arguments ); - } - // assuming we've left the element since we most likely mousedover a xul element - } catch(e) { } + event.type = eventType; + } + } }, // In case of event delegation, we only need to rename the event.type, @@ -5890,8 +5922,21 @@ function cloneFixAttributes( src, dest ) { } jQuery.buildFragment = function( args, nodes, scripts ) { - var fragment, cacheable, cacheresults, - doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document); + var fragment, cacheable, cacheresults, doc; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } + + // Ensure that an attr object doesn't incorrectly stand in as a document object + // Chrome and Firefox seem to allow this to occur and will throw exception + // Fixes #8950 + if ( !doc.createDocumentFragment ) { + doc = document; + } // Only cache "small" (1/2 KB) HTML strings that are associated with the main document // Cloning options loses the selected state, so don't cache them @@ -5972,7 +6017,7 @@ function fixDefaultChecked( elem ) { function findInputs( elem ) { if ( jQuery.nodeName( elem, "input" ) ) { fixDefaultChecked( elem ); - } else if ( elem.getElementsByTagName ) { + } else if ( "getElementsByTagName" in elem ) { jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); } } @@ -6021,6 +6066,8 @@ jQuery.extend({ } } + srcElements = destElements = null; + // Return the cloned set return clone; }, @@ -6201,10 +6248,8 @@ function evalScript( i, elem ) { - var ralpha = /alpha\([^)]*\)/i, ropacity = /opacity=([^)]*)/, - rdashAlpha = /-([a-z])/ig, // fixed for IE9, see #8346 rupper = /([A-Z]|^ms)/g, rnumpx = /^-?\d+(?:px)?$/i, @@ -6218,11 +6263,7 @@ var ralpha = /alpha\([^)]*\)/i, curCSS, getComputedStyle, - currentStyle, - - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; + currentStyle; jQuery.fn.css = function( name, value ) { // Setting 'undefined' is a no-op @@ -6257,13 +6298,14 @@ jQuery.extend({ // Exclude the following css properties to add px cssNumber: { - "zIndex": true, + "fillOpacity": true, "fontWeight": true, - "opacity": true, - "zoom": true, "lineHeight": true, + "opacity": true, + "orphans": true, "widows": true, - "orphans": true + "zIndex": true, + "zoom": true }, // Add in properties whose names you wish to fix before @@ -6298,6 +6340,8 @@ jQuery.extend({ // convert relative number strings (+= or -=) to relative numbers. #7345 if ( type === "string" && rrelNum.test( value ) ) { value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; } // If a number was passed in, add 'px' to the (except for certain CSS properties) @@ -6364,10 +6408,6 @@ jQuery.extend({ for ( name in options ) { elem.style[ name ] = old[ name ]; } - }, - - camelCase: function( string ) { - return string.replace( rdashAlpha, fcamelCase ); } }); @@ -6381,44 +6421,21 @@ jQuery.each(["height", "width"], function( i, name ) { if ( computed ) { if ( elem.offsetWidth !== 0 ) { - val = getWH( elem, name, extra ); - + return getWH( elem, name, extra ); } else { jQuery.swap( elem, cssShow, function() { val = getWH( elem, name, extra ); }); } - if ( val <= 0 ) { - val = curCSS( elem, name, name ); - - if ( val === "0px" && currentStyle ) { - val = currentStyle( elem, name, name ); - } - - if ( val != null ) { - // Should return "auto" instead of 0, use 0 for - // temporary backwards-compat - return val === "" || val === "auto" ? "0px" : val; - } - } - - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - - // Should return "auto" instead of 0, use 0 for - // temporary backwards-compat - return val === "" || val === "auto" ? "0px" : val; - } - - return typeof val === "string" ? val : val + "px"; + return val; } }, set: function( elem, value ) { if ( rnumpx.test( value ) ) { // ignore negative width and height values #1599 - value = parseFloat(value); + value = parseFloat( value ); if ( value >= 0 ) { return value + "px"; @@ -6541,27 +6558,50 @@ if ( document.documentElement.currentStyle ) { curCSS = getComputedStyle || currentStyle; function getWH( elem, name, extra ) { - var which = name === "width" ? cssWidth : cssHeight, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight; - if ( extra === "border" ) { - return val; + // Start with offset property + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + which = name === "width" ? cssWidth : cssHeight; + + if ( val > 0 ) { + if ( extra !== "border" ) { + jQuery.each( which, function() { + if ( !extra ) { + val -= parseFloat( jQuery.css( elem, "padding" + this ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + this ) ) || 0; + } else { + val -= parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0; + } + }); + } + + return val + "px"; } - jQuery.each( which, function() { - if ( !extra ) { - val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0; - } + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ] || 0; + } + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; - if ( extra === "margin" ) { - val += parseFloat(jQuery.css( elem, "margin" + this )) || 0; + // Add padding, border, margin + if ( extra ) { + jQuery.each( which, function() { + val += parseFloat( jQuery.css( elem, "padding" + this ) ) || 0; + if ( extra !== "padding" ) { + val += parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + this ) ) || 0; + } + }); + } - } else { - val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0; - } - }); - - return val; + return val + "px"; } if ( jQuery.expr && jQuery.expr.filters ) { @@ -7957,8 +7997,8 @@ var elemdisplay = {}, ], fxNow, requestAnimationFrame = window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame; + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame; jQuery.fn.extend({ show: function( speed, easing, callback ) { @@ -8272,15 +8312,15 @@ jQuery.extend({ // Queueing opt.old = opt.complete; opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + if ( opt.queue !== false ) { jQuery.dequeue( this ); } else if ( noUnmark !== false ) { jQuery._unmark( this ); } - - if ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } }; return opt; @@ -8353,7 +8393,7 @@ jQuery.fx.prototype = { if ( t() && jQuery.timers.push(t) && !timerId ) { // Use requestAnimationFrame instead of setInterval if available if ( requestAnimationFrame ) { - timerId = 1; + timerId = true; raf = function() { // When timerId gets set to null at any point, this stops if ( timerId ) { @@ -8516,7 +8556,8 @@ function defaultDisplay( nodeName ) { if ( !elemdisplay[ nodeName ] ) { - var elem = jQuery( "<" + nodeName + ">" ).appendTo( "body" ), + var body = document.body, + elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), display = elem.css( "display" ); elem.remove(); @@ -8530,14 +8571,15 @@ function defaultDisplay( nodeName ) { iframe.frameBorder = iframe.width = iframe.height = 0; } - document.body.appendChild( iframe ); + body.appendChild( iframe ); // Create a cacheable copy of the iframe document on first call. - // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake html - // document to it, Webkit & Firefox won't allow reusing the iframe document + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. if ( !iframeDoc || !iframe.createElement ) { iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - iframeDoc.write( "" ); + iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "" : "" ) + "" ); + iframeDoc.close(); } elem = iframeDoc.createElement( nodeName ); @@ -8546,7 +8588,7 @@ function defaultDisplay( nodeName ) { display = jQuery.css( elem, "display" ); - document.body.removeChild( iframe ); + body.removeChild( iframe ); } // Store the correct default display @@ -8867,22 +8909,24 @@ function getWindow( elem ) { -// Create innerHeight, innerWidth, outerHeight and outerWidth methods +// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods jQuery.each([ "Height", "Width" ], function( i, name ) { var type = name.toLowerCase(); // innerHeight and innerWidth - jQuery.fn["inner" + name] = function() { - return this[0] ? - parseFloat( jQuery.css( this[0], type, "padding" ) ) : + jQuery.fn[ "inner" + name ] = function() { + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : null; }; // outerHeight and outerWidth - jQuery.fn["outer" + name] = function( margin ) { - return this[0] ? - parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) : + jQuery.fn[ "outer" + name ] = function( margin ) { + var elem = this[0]; + return elem && elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : null; }; @@ -8932,5 +8976,6 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { }); +// Expose jQuery to the global object window.jQuery = window.$ = jQuery; })(window); diff --git a/js/jquery/jquery.sortableTable.js b/js/jquery/jquery.sortableTable.js index 2dd1ede1e2..1f4fc91db8 100644 --- a/js/jquery/jquery.sortableTable.js +++ b/js/jquery/jquery.sortableTable.js @@ -49,7 +49,7 @@ $('table').sortableTable('destroy') - removes all events from the table }, destroy : function( ) { $(this).data('sortableTable').destroy(); - }, + } }; if ( methods[method] ) { diff --git a/js/keyhandler.js b/js/keyhandler.js index f92c7fb340..63a13facfd 100644 --- a/js/keyhandler.js +++ b/js/keyhandler.js @@ -3,7 +3,8 @@ * * @param object event data */ -function onKeyDownArrowsHandler(e) { +function onKeyDownArrowsHandler(e) +{ e = e||window.event; var o = (e.srcElement||e.target); if (!o) return; diff --git a/js/messages.php b/js/messages.php index 4c10d2fee8..82940173b0 100644 --- a/js/messages.php +++ b/js/messages.php @@ -98,12 +98,28 @@ $js_messages['strMiB'] = __('MiB'); $js_messages['strKiB'] = __('KiB'); $js_messages['strAverageLoad'] = __('Average load'); +$js_messages['strTotalMemory'] = __('Total memory'); +$js_messages['strCachedMemory'] = __('Cached memory'); +$js_messages['strBufferedMemory'] = __('Buffered memory'); +$js_messages['strFreeMemory'] = __('Free memory'); +$js_messages['strUsedMemory'] = __('Used memory'); + +$js_messages['strTotalSwap'] = __('Total Swap'); +$js_messages['strCachedSwap'] = __('Cached Swap'); +$js_messages['strUsedSwap'] = __('Used Swap'); +$js_messages['strFreeSwap'] = __('Free Swap'); + +$js_messages['strBytesSent'] = __('Bytes sent'); +$js_messages['strBytesReceived'] = __('Bytes received'); +$js_messages['strConnections'] = __('Connections'); +$js_messages['strProcesses'] = __('Processes'); + /* l10n: Questions is the name of a MySQL Status variable */ $js_messages['strQuestions'] = __('Questions'); $js_messages['strTraffic'] = __('Traffic'); $js_messages['strSettings'] = __('Settings'); $js_messages['strRemoveChart'] = __('Remove chart'); -$js_messages['strEditChart'] = __('Edit labels and series'); +$js_messages['strEditChart'] = __('Edit title and labels'); $js_messages['strAddChart'] = __('Add chart to grid'); $js_messages['strClose'] = __('Close'); $js_messages['strAddOneSeriesWarning'] = __('Please add at least one variable to the series'); @@ -156,6 +172,17 @@ $js_messages['strIgnoreWhereAndGroup'] = __('Group queries, ignoring variable da $js_messages['strSumRows'] = __('Sum of grouped rows:'); $js_messages['strTotal'] = __('Total:'); +$js_messages['strLoadingLogs'] = __('Loading logs'); +$js_messages['strRefreshFailed'] = __('Monitor refresh failed'); +$js_messages['strInvalidResponseExplanation'] = __('While requesting new chart data the server returned an invalid response. This is most likely because your session expired. Reloading the page and reentering your credentials should help.'); +$js_messages['strReloadPage'] = __('Reload page'); + +$js_messages['strAffectedRows'] = __('Affected rows: '); + +$js_messages['strFailedParsingConfig'] = __('Failed parsing config file. It doesn\'t seem to be valid JSON code'); +$js_messages['strFailedBuildingGrid'] = __('Failed building chart grid with imported config. Resetting to default config...'); +$js_messages['strImport'] = __('Import'); + /* For inline query editing */ $js_messages['strGo'] = __('Go'); $js_messages['strCancel'] = __('Cancel'); @@ -223,14 +250,14 @@ $js_messages['strIgnore'] = __('Ignore'); /* For tbl_structure.js */ $js_messages['strAddColumns'] = __('Add columns'); -/* Designer (pmd/scripts/move.js) */ +/* Designer (js/pmd/move.js) */ $js_messages['strSelectReferencedKey'] = __('Select referenced key'); $js_messages['strSelectForeignKey'] = __('Select Foreign Key'); $js_messages['strPleaseSelectPrimaryOrUniqueKey'] = __('Please select the primary key or a unique key'); $js_messages['strChangeDisplay'] = __('Choose column to display'); $js_messages['strLeavingDesigner'] = __('You haven\'t saved the changes in the layout. They will be lost if you don\'t save them.Do you want to continue?'); -/* Visual query builder (pmd/scripts/move.js) */ +/* Visual query builder (js/pmd/move.js) */ $js_messages['strAddOption'] = __('Add an option for column '); /* password generation */ diff --git a/js/navigation.js b/js/navigation.js index 5ce805c278..c70d428c18 100644 --- a/js/navigation.js +++ b/js/navigation.js @@ -17,7 +17,8 @@ var pma_saveframesize_timeout = null; * @param string id id of the element in the DOM * @param boolean only_open do not close/hide element */ -function toggle(id, only_open) { +function toggle(id, only_open) +{ var el = document.getElementById('subel' + id); if (! el) { return false; @@ -104,7 +105,8 @@ function PMA_setFrameSize() * @param string name name of the value to retrieve * @return string value value for the given name from cookie */ -function PMA_getCookie(name) { +function PMA_getCookie(name) +{ var start = document.cookie.indexOf(name + "="); var len = start + name.length + 1; if ((!start) && (name != document.cookie.substring(0, name.length))) { @@ -130,7 +132,8 @@ function PMA_getCookie(name) { * @param string domain * @param boolean secure */ -function PMA_setCookie(name, value, expires, path, domain, secure) { +function PMA_setCookie(name, value, expires, path, domain, secure) +{ document.cookie = name + "=" + escape(value) + ( (expires) ? ";expires=" + expires.toGMTString() : "") + ( (path) ? ";path=" + path : "") + @@ -144,7 +147,8 @@ function PMA_setCookie(name, value, expires, path, domain, secure) { * @param string value requested value * */ -function fast_filter(value){ +function fast_filter(value) +{ lowercase_value = value.toLowerCase(); $("#subel0 a[class!='tableicon']").each(function(idx,elem){ $elem = $(elem); @@ -160,7 +164,8 @@ function fast_filter(value){ /** * Clears fast filter. */ -function clear_fast_filter() { +function clear_fast_filter() +{ var elm = $('#NavFilter input'); elm.val(''); fast_filter(''); @@ -170,7 +175,8 @@ function clear_fast_filter() { /** * Reloads the recent tables list. */ -function PMA_reloadRecentTable() { +function PMA_reloadRecentTable() +{ $.get('navigation.php', { 'token': window.parent.token, 'server': window.parent.server, diff --git a/pmd/scripts/ajax.js b/js/pmd/ajax.js similarity index 100% rename from pmd/scripts/ajax.js rename to js/pmd/ajax.js diff --git a/pmd/scripts/history.js b/js/pmd/history.js similarity index 95% rename from pmd/scripts/history.js rename to js/pmd/history.js index 7caeea93ab..3e3a195a40 100644 --- a/pmd/scripts/history.js +++ b/js/pmd/history.js @@ -17,7 +17,8 @@ var g_index; * @param index has value 1 or 0,decides wheter to hide toggle_container on load. **/ -function panel(index) { +function panel(index) +{ if (!index) { $(".toggle_container").hide(); } @@ -36,14 +37,15 @@ function panel(index) { * @uses history_delete() * * @param {int} init starting index of unsorted array - * @param {int} final last index of unsorted array + * @param {int} finit last index of unsorted array * **/ -function display(init,final) { +function display(init,finit) +{ var str,i,j,k,sto; // this part sorts the history array based on table name,this is needed for clubbing all object of same name together. - for (i = init;i < final;i++) { + for (i = init;i < finit;i++) { sto = history_array[i]; var temp = history_array[i].get_tab() ;//+ '.' + history_array[i].get_obj_no(); for Self JOINS for(j = 0;j < i;j++){ @@ -99,7 +101,8 @@ function display(init,final) { * **/ -function and_or(index) { +function and_or(index) +{ if (history_array[index].get_and_or()) { history_array[index].set_and_or(0); } @@ -118,7 +121,8 @@ function and_or(index) { * **/ -function detail (index) { +function detail (index) +{ var type = history_array[index].get_type(); var str; if (type == "Where") { @@ -158,7 +162,8 @@ function detail (index) { * **/ -function history_delete(index) { +function history_delete(index) +{ for(var k =0 ;k < from_array.length;k++){ if(from_array[k] == history_array[index].get_tab()){ from_array.splice(k,1); @@ -178,7 +183,8 @@ function history_delete(index) { * **/ -function history_edit(index) { +function history_edit(index) +{ g_index = index; var type = history_array[index].get_type(); if (type == "Where") { @@ -225,7 +231,8 @@ function history_edit(index) { * @param index index of history_array where change is to be made **/ -function edit(type) { +function edit(type) +{ if (type == "Rename") { if (document.getElementById('e_rename').value != "") { history_array[g_index].get_obj().setrename_to(document.getElementById('e_rename').value); @@ -271,7 +278,8 @@ function edit(type) { * **/ -function history(ncolumn_name,nobj,ntab,nobj_no,ntype) { +function history(ncolumn_name,nobj,ntab,nobj_no,ntype) +{ var and_or; var obj; var tab; @@ -432,7 +440,8 @@ var aggregate = function(noperator) { * @return unique array */ -function unique(arrayName) { +function unique(arrayName) +{ var newArray=new Array(); label:for(var i=0; ilog-bin=mysql-bin
log-error=mysql-bin.err
"; -function update_config() { +function update_config() +{ var conf_ignore = "binlog_ignore_db="; var conf_do = "binlog_do_db="; var database_list = $('#db_select option:selected:first').val(); @@ -30,24 +31,24 @@ $(document).ready(function() { $('#db_select').change(update_config); $('#master_status_href').click(function() { - $('#replication_master_section').toggle(); + $('#replication_master_section').toggle(); }); $('#master_slaves_href').click(function() { - $('#replication_slaves_section').toggle(); + $('#replication_slaves_section').toggle(); }); $('#slave_status_href').click(function() { - $('#replication_slave_section').toggle(); + $('#replication_slave_section').toggle(); }); $('#slave_control_href').click(function() { - $('#slave_control_gui').toggle(); + $('#slave_control_gui').toggle(); }); $('#slave_errormanagement_href').click(function() { - $('#slave_errormanagement_gui').toggle(); + $('#slave_errormanagement_gui').toggle(); }); $('#slave_synchronization_href').click(function() { - $('#slave_synchronization_gui').toggle(); + $('#slave_synchronization_gui').toggle(); }); $('#db_reset_href').click(function() { - $('#db_select option:selected').attr('selected', false); + $('#db_select option:selected').attr('selected', false); }); }); diff --git a/js/server_privileges.js b/js/server_privileges.js index 35c34aa34e..c695dab478 100644 --- a/js/server_privileges.js +++ b/js/server_privileges.js @@ -81,7 +81,8 @@ function checkAddUser(the_form) * @param new_user_initial the first alphabet of the user's name * @param new_user_initial_string html to replace the initial for pagination */ -function appendNewUser(new_user_string, new_user_initial, new_user_initial_string) { +function appendNewUser(new_user_string, new_user_initial, new_user_initial_string) +{ //Append the newly retrived user to the table now //Calculate the index for the new row diff --git a/js/server_status.js b/js/server_status.js index d8f3788576..5abb5df447 100644 --- a/js/server_status.js +++ b/js/server_status.js @@ -218,7 +218,8 @@ $(function() { true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); - } + }, + error: function() { serverResponseError(); } } } @@ -260,7 +261,8 @@ $(function() { true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); - } + }, + error: function() { serverResponseError(); } } }; @@ -295,7 +297,8 @@ $(function() { true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); - } + }, + error: function() { serverResponseError(); } } }; } else { @@ -348,7 +351,7 @@ $(function() { }); $('#filterText').keyup(function(e) { - word = $(this).val().replace('_',' '); + word = $(this).val().replace(/_/g,' '); if(word.length == 0) textFilter = null; else textFilter = new RegExp("(^|_)" + word,'i'); @@ -363,6 +366,11 @@ $(function() { filterVariables(); }); + $('input#dontFormat').change(function() { + $('#serverstatusvariables td.value span.original').toggle(this.checked); + $('#serverstatusvariables td.value span.formatted').toggle(! this.checked); + }); + /* Adjust DOM / Add handlers to the tabs */ function initTab(tab,data) { switch(tab.attr('id')) { @@ -545,7 +553,55 @@ $(function() { return pointInfo; } + /**** Server config advisor ****/ + $('a[href="#openAdvisorInstructions"]').click(function() { + $('#advisorInstructionsDialog').dialog(); + }); + + $('a[href="#startAnalyzer"]').click(function() { + var $cnt = $('#statustabs_advisor .tabInnerContent'); + $cnt.html(''); + + $.get('server_status.php?'+url_query, { ajax_request: true, advisor: true },function(data) { + var $tbody, $tr, str, even = true; + + data = $.parseJSON(data); + $cnt.html('

Possible performance issues

'); + if(data.fired.length > 0) { + $cnt.append('
IssueRecommendation
'); + $tbody = $cnt.find('table#rulesFired'); + $.each(data.fired, function(key,value) { + $tbody.append($tr = $('' + value.issue + '' + + '' + value.recommendation + ' ')); + even = !even; + + $tr.data('rule',value); + $tr.click(function() { + var rule = $(this).data('rule'); + $('div#emptyDialog').attr('title','Rule details'); + $('div#emptyDialog').html( + '

Issue:
' + rule.issue + '

' + + '

Recommendation:
' + rule.recommendation + '

' + + '

Justification:
' + rule.justification + '

' + + '

Used variable / formula:
' + rule.formula + '

' + + '

Test:
' + rule.test + '

' + ); + $('div#emptyDialog').dialog({ + width: 600, + buttons: { + 'Close' : function() { + $(this).dialog('close'); + } + } + }); + }); + }); + } + }); + + return false; + }); /**** Monitor charting implementation ****/ @@ -555,15 +611,10 @@ $(function() { var newChart = null; var chartSpacing; - // Runtime parameter of the monitor + // Runtime parameter of the monitor, is being fully set in initGrid() var runtime = { // Holds all visible charts in the grid charts: null, - // Current max points per chart (needed for auto calculation) - gridMaxPoints: 20, - // displayed time frame - xmin: -1, - xmax: -1, // Stores the timeout handler so it can be cleared refreshTimeout: null, // Stores the GET request to refresh the charts @@ -573,13 +624,18 @@ $(function() { // To play/pause the monitor redrawCharts: false, // Object that contains a list of nodes that need to be retrieved from the server for chart updates - dataList: [] + dataList: [], + // Current max points per chart (needed for auto calculation) + gridMaxPoints: 20, + // displayed time frame + xmin: -1, + xmax: -1 }; var monitorSettings = null; var defaultMonitorSettings = { - columns: 4, + columns: 3, chartSize: { width: 295, height: 250 }, // Max points in each chart. Settings it to 'auto' sets gridMaxPoints to (chartwidth - 40) / 12 gridMaxPoints: 'auto', @@ -593,20 +649,20 @@ $(function() { var presetCharts = { 'cpu-WINNT': { title: PMA_messages['strSystemCPUUsage'], - nodes: [{ dataType: 'cpu', name: 'loadavg', unit: '%'}] + nodes: [{ dataType: 'cpu', name: PMA_messages['strAverageLoad'], dataPoint: 'loadavg', unit: '%'}] }, 'memory-WINNT': { title: PMA_messages['strSystemMemory'], nodes: [ - { dataType: 'memory', name: 'MemTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalMemory'], dataPoint: 'MemTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedMemory'], dataPoint: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] } ] }, 'swap-WINNT': { title: PMA_messages['strSystemSwap'], nodes: [ - { dataType: 'memory', name: 'SwapTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalSwap'], dataPoint: 'SwapTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedSwap'], dataPoint: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] } ] }, 'cpu-Linux': { @@ -615,25 +671,17 @@ $(function() { { dataType: 'cpu', name: PMA_messages['strAverageLoad'], unit: '%', - transformFn: function(cur, prev) { - console.log('cpu-linux chart, transformFn()'); - console.log(cur); - console.log(prev); - if(prev == null) return undefined; - var diff_total = cur.busy + cur.idle - (prev.busy + prev.idle); - var diff_idle = cur.idle - prev.idle; - return 100*(diff_total - diff_idle) / diff_total; - } + transformFn: 'cpu-linux' } ] }, 'memory-Linux': { title: PMA_messages['strSystemMemory'], nodes: [ - { dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'Cached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'Buffers', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'MemFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedMemory'], dataPoint: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strCachedMemory'], dataPoint: 'Cached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strBufferedMemory'], dataPoint: 'Buffers', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strFreeMemory'], dataPoint:'MemFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] } ], settings: { chart: { @@ -650,9 +698,9 @@ $(function() { 'swap-Linux': { title: PMA_messages['strSystemSwap'], nodes: [ - { dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapCached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalSwap'], dataPoint: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strCachedSwap'], dataPoint: 'SwapCached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strFreeSwap'], dataPoint: 'SwapFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] } ], settings: { chart: { @@ -671,18 +719,18 @@ $(function() { // Default setting defaultChartGrid = { 'c0': { title: PMA_messages['strQuestions'], - nodes: [{ dataType: 'statusvar', name: 'Questions', display: 'differential' }] - }, - 'c1': { + nodes: [{ dataType: 'statusvar', name: PMA_messages['strQuestions'], dataPoint: 'Questions', display: 'differential' }] + }, + 'c1': { title: PMA_messages['strChartConnectionsTitle'], - nodes: [ { dataType: 'statusvar', name: 'Connections', display: 'differential' }, - { dataType: 'proc', name: 'Processes'} ] - }, - 'c2': { + nodes: [ { dataType: 'statusvar', name: PMA_messages['strConnections'], dataPoint: 'Connections', display: 'differential' }, + { dataType: 'proc', name: PMA_messages['strProcesses'], dataPoint: 'processes'} ] + }, + 'c2': { title: PMA_messages['strTraffic'], nodes: [ - { dataType: 'statusvar', name: 'Bytes_sent', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] }, - { dataType: 'statusvar', name: 'Bytes_received', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] } + { dataType: 'statusvar', name: PMA_messages['strBytesSent'], dataPoint: 'Bytes_sent', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] }, + { dataType: 'statusvar', name: PMA_messages['strBytesReceived'], dataPoint: 'Bytes_received', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] } ] } }; @@ -706,7 +754,7 @@ $(function() { menuItems: [{ textKey: 'editChart', onclick: function() { - alert('tbi'); + editChart(this); } }, { textKey: 'removeChart', @@ -746,9 +794,6 @@ $(function() { height: 24 }, events: { - start: function() { - // console.log('start.'); - }, // Drop event. The drag child element is moved into the drop element // and vice versa. So the parameters are switched. drop: function(drag, drop, pos) { @@ -922,14 +967,14 @@ $(function() { saveMonitor(); // Save settings $(this).dialog("close"); - } + }; dlgButtons[PMA_messages['strClose']] = function() { newChart = null; $('span#clearSeriesLink').hide(); $('#seriesPreview').html(''); $(this).dialog("close"); - } + }; $('div#addChartDialog').dialog({ width:'auto', @@ -942,13 +987,111 @@ $(function() { return false; }); + $('a[href="#exportMonitorConfig"]').click(function() { + var gridCopy = {}; + + $.each(runtime.charts, function(key, elem) { + gridCopy[key] = {}; + gridCopy[key].nodes = elem.nodes; + gridCopy[key].settings = elem.settings; + gridCopy[key].title = elem.title; + }); + + var exportData = { + monitorCharts: gridCopy, + monitorSettings: monitorSettings + }; + var $form; + + $('body').append($form = $('
')); + + $form.append(''); + $form.submit(); + $form.remove(); + }); + + $('a[href="#importMonitorConfig"]').click(function() { + $('div#emptyDialog').attr('title','Import monitor configuration'); + $('div#emptyDialog').html('Please select the file you want to import:
'+ + '
'); + + var dlgBtns = {}; + + dlgBtns[PMA_messages['strImport']] = function() { + var $iframe, $form; + $('body').append($iframe = $('')); + var d = $iframe[0].contentWindow.document; + d.open(); d.close(); + mew = d; + + $iframe.load(function() { + var json; + + // Try loading config + try { + var data = $('body',$('iframe#monitorConfigUpload')[0].contentWindow.document).html(); + // Chrome wraps around '
' to any text content -.-
+                    json = $.secureEvalJSON(data.substring(data.indexOf("{"), data.lastIndexOf("}") + 1));
+                } catch (err) {
+                    alert(PMA_messages['strFailedParsingConfig']);
+                    $('div#emptyDialog').dialog('close');
+                    return;
+                }
+
+                // Basic check, is this a monitor config json?
+                if(!json || ! json.monitorCharts || ! json.monitorCharts) {
+                    alert(PMA_messages['strFailedParsingConfig']);
+                    $('div#emptyDialog').dialog('close');
+                    return;
+                }
+
+                // If json ok, try applying config
+                try {
+                    window.localStorage['monitorCharts'] = $.toJSON(json.monitorCharts);
+                    window.localStorage['monitorSettings'] = $.toJSON(json.monitorSettings);
+                    rebuildGrid();
+                } catch(err) {
+                    alert(PMA_messages['strFailedBuildingGrid']);
+                    // If an exception is thrown, load default again
+                    window.localStorage.removeItem('monitorCharts');
+                    window.localStorage.removeItem('monitorSettings');
+                    rebuildGrid();
+                }
+
+                $('div#emptyDialog').dialog('close');
+            });
+
+            $("body", d).append($form=$('div#emptyDialog').find('form'));
+            $form.submit();
+            $('div#emptyDialog').append('');
+        };
+
+        dlgBtns[PMA_messages['strCancel']] = function() {
+            $(this).dialog('close');
+        }
+
+
+        $('div#emptyDialog').dialog({
+            width: 'auto',
+            height: 'auto',
+            buttons: dlgBtns
+        });
+    });
+
+    $('a[href="#clearMonitorConfig"]').click(function() {
+        window.localStorage.removeItem('monitorCharts');
+        window.localStorage.removeItem('monitorSettings');
+        $(this).hide();
+        rebuildGrid();
+    });
+
     $('a[href="#pauseCharts"]').click(function() {
         runtime.redrawCharts = ! runtime.redrawCharts;
         if(! runtime.redrawCharts)
             $(this).html(' ' + PMA_messages['strResumeMonitor']);
         else {
             $(this).html(' ' + PMA_messages['strPauseMonitor']);
-            if(runtime.charts == null) {
+            if(! runtime.charts) {
                 initGrid();
                 $('a[href="#settingsPopup"]').show();
             }
@@ -1067,7 +1210,7 @@ $(function() {
                     });
                 }
             );
-        }
+        };
 
 
         loadLogVars();
@@ -1133,11 +1276,12 @@ $(function() {
 
         var serie = {
             dataType:'statusvar',
+            dataPoint: $('input#variableInput').attr('value'),
             name: $('input#variableInput').attr('value'),
             display: $('input[name="differentialValue"]').attr('checked') ? 'differential' : ''
         };
 
-        if(serie.name == 'Processes') serie.dataType='proc';
+        if(serie.dataPoint == 'Processes') serie.dataType='proc';
 
         if($('input[name="useDivisor"]').attr('checked'))
             serie.valueDivisor = parseInt($('input[name="valueDivisor"]').attr('value'));
@@ -1150,7 +1294,7 @@ $(function() {
         var str = serie.display == 'differential' ? ', ' + PMA_messages['strDifferential'] : '';
         str += serie.valueDivisor ? (', ' + $.sprintf(PMA_messages['strDividedBy'], serie.valueDivisor)) : '';
 
-        $('#seriesPreview').append('- ' + serie.name + str + '
'); + $('#seriesPreview').append('- ' + serie.dataPoint + str + '
'); newChart.nodes.push(serie); @@ -1229,11 +1373,57 @@ $(function() { // Empty cells should keep their size so you can drop onto them $('table#chartGrid tr td').css('width',chartSize().width + 'px'); - buildRequiredDataList(); refreshChartGrid(); } + function destroyGrid() { + if(runtime.charts) + $.each(runtime.charts, function(key, value) { + try { + value.chart.destroy(); + } catch(err) {} + }); + try { + runtime.refreshRequest.abort(); + } catch(err) {} + try { + clearTimeout(runtime.refreshTimeout); + } catch(err) {} + + $('table#chartGrid').html(''); + + runtime.charts = null; + runtime.chartAI = 0; + monitorSettings = null; + } + + function rebuildGrid() { + var oldData = null; + if(runtime.charts) { + oldData = {}; + $.each(runtime.charts, function(key, chartObj) { + for(var i=0; i < chartObj.nodes.length; i++) { + oldData[chartObj.nodes[i].dataPoint] = []; + for(var j=0; j < chartObj.chart.series[i].data.length; j++) + oldData[chartObj.nodes[i].dataPoint].push([chartObj.chart.series[i].data[j].x, chartObj.chart.series[i].data[j].y]); + } + }); + } + + destroyGrid(); + initGrid(); + + if(oldData) { + $.each(runtime.charts, function(key, chartObj) { + for(var j=0; j < chartObj.nodes.length; j++) { + if(oldData[chartObj.nodes[j].dataPoint]) + chartObj.chart.series[j].setData(oldData[chartObj.nodes[j].dataPoint]); + } + }); + } + } + function chartSize() { var wdt = $('div#logTable').innerWidth() / monitorSettings.columns - (monitorSettings.columns - 1) * chartSpacing.width; return { @@ -1284,7 +1474,7 @@ $(function() { $('#logAnalyseDialog').find('dateStart,dateEnd').datepicker('destroy'); $(this).dialog("close"); - } + }; dlgBtns[PMA_messages['strFromGeneralLog']] = function() { var dateStart = Date.parse($('#logAnalyseDialog input[name="dateStart"]').attr('value')) || min; @@ -1301,7 +1491,7 @@ $(function() { $('#logAnalyseDialog').find('dateStart,dateEnd').datepicker('destroy'); $(this).dialog("close"); - } + }; $('#logAnalyseDialog').dialog({ width: 'auto', @@ -1370,10 +1560,58 @@ $(function() { runtime.chartAI++; } - function removeChart(chartObj) { + function editChart(chartObj) { var htmlnode = chartObj.options.chart.renderTo; if(! htmlnode ) return; + var chart=null; + var chartKey=null; + $.each(runtime.charts, function(key, value) { + if(value.chart.options.chart.renderTo == htmlnode) { + chart = value; + chartKey = key; + return false; + } + }); + + if(chart == null) return; + + var htmlStr = '

Chart title:
'; + htmlStr += '

Series:

    '; + for(var i=0; i
    '; + } + + dlgBtns = {}; + dlgBtns['Save'] = function() { + runtime.charts[chartKey].title = $('div#emptyDialog input[name="chartTitle"]').attr('value'); + runtime.charts[chartKey].chart.setTitle({ text: runtime.charts[chartKey].title }); + + $('div#emptyDialog input[name*="chartSerie"]').each(function() { + var idx = $(this).attr('name').split('-')[1]; + runtime.charts[chartKey].nodes[idx].name = $(this).attr('value'); + runtime.charts[chartKey].chart.series[idx].name = $(this).attr('value'); + }); + + $(this).dialog('close'); + saveMonitor(); + }; + dlgBtns['Cancel'] = function() { + $(this).dialog('close'); + }; + + $('div#emptyDialog').attr('title','Edit chart'); + $('div#emptyDialog').html(htmlStr+'
'); + $('div#emptyDialog').dialog({ + width: 'auto', + height: 'auto', + buttons: dlgBtns + }); + } + + function removeChart(chartObj) { + var htmlnode = chartObj.options.chart.renderTo; + if(! htmlnode ) return; $.each(runtime.charts, function(key, value) { if(value.chart.options.chart.renderTo == htmlnode) { @@ -1388,7 +1626,7 @@ $(function() { // which throws an error when the chart is destroyed setTimeout(function() { chartObj.destroy(); - $('li#' + htmlnode).remove(); + $('div#' + htmlnode).remove(); },10); saveMonitor(); // Save settings @@ -1397,7 +1635,12 @@ $(function() { function refreshChartGrid() { /* Send to server */ runtime.refreshRequest = $.post('server_status.php?'+url_query, { ajax_request: true, chart_data: 1, type: 'chartgrid', requiredData: $.toJSON(runtime.dataList) },function(data) { - var chartData = $.parseJSON(data); + var chartData; + try { + chartData = $.parseJSON(data); + } catch(err) { + return serverResponseError(); + } var value, i=0; var diff; @@ -1429,9 +1672,10 @@ $(function() { value = value / elem.nodes[j].valueDivisor; if(elem.nodes[j].transformFn) { - value = elem.nodes[j].transformFn( + value = chartValueTransform( + elem.nodes[j].transformFn, chartData[key][j], - (oldChartData == null) ? null : oldChartData[key][j] + (oldChartData == null ? null : oldChartData[key][j]) ); } @@ -1456,6 +1700,17 @@ $(function() { }); } + function chartValueTransform(name,cur,prev) { + switch(name) { + case 'cpu-linux': + if(prev == null) return undefined; + var diff_total = cur.busy + cur.idle - (prev.busy + prev.idle); + var diff_idle = cur.idle - prev.idle; + return 100*(diff_total - diff_idle) / diff_total; + } + return undefined; + } + /* Build list of nodes that need to be retrieved */ function buildRequiredDataList() { runtime.dataList = {}; @@ -1477,9 +1732,9 @@ $(function() { if(! opts.limitTypes) opts.limitTypes = false; - $('#loadingLogsDialog').html(PMA_messages['strAnalysingLogs'] + ' '); + $('#emptyDialog').html(PMA_messages['strAnalysingLogs'] + ' '); - $('#loadingLogsDialog').dialog({ + $('#emptyDialog').dialog({ width: 'auto', height: 'auto', buttons: { @@ -1503,17 +1758,23 @@ $(function() { limitTypes: opts.limitTypes }, function(data) { - var logData = $.parseJSON(data); + var logData; + try { + logData = $.parseJSON(data); + } catch(err) { + return serverResponseError(); + } if(logData.rows.length != 0) { runtime.logDataCols = buildLogTable(logData); /* Show some stats in the dialog */ - $('#loadingLogsDialog').html('

' + PMA_messages['strLogDataLoaded'] + '

'); + $('#emptyDialog').attr('title', PMA_messages['strLoadingLogs']); + $('#emptyDialog').html('

' + PMA_messages['strLogDataLoaded'] + '

'); $.each(logData.sum, function(key, value) { key = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(); if(key == 'Total') key = '' + key + ''; - $('#loadingLogsDialog').append(key + ': ' + value + '
'); + $('#emptyDialog').append(key + ': ' + value + '
'); }); /* Add filter options if more than a bunch of rows there to filter */ @@ -1551,19 +1812,19 @@ $(function() { dlgBtns[PMA_messages['strJumpToTable']] = function() { $(this).dialog("close"); $(document).scrollTop($('div#logTable').offset().top); - } + }; - $('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns); + $('#emptyDialog').dialog( "option", "buttons", dlgBtns); } else { - $('#loadingLogsDialog').html('

' + PMA_messages['strNoDataFound'] + '

'); + $('#emptyDialog').html('

' + PMA_messages['strNoDataFound'] + '

'); var dlgBtns = {}; dlgBtns[PMA_messages['strClose']] = function() { $(this).dialog("close"); - } + }; - $('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns ); + $('#emptyDialog').dialog( "option", "buttons", dlgBtns ); } } ); @@ -1575,18 +1836,29 @@ $(function() { if(val.length == 0) textFilter = null; else textFilter = new RegExp(val, 'i'); - var rowSum = 0, totalSum = 0; - - var i=0, q; + var rowSum = 0, totalSum = 0, i=0, q; var noVars = $('div#logTable input#noWHEREData').attr('checked'); var equalsFilter = /([^=]+)=(\d+|((\'|"|).*?[^\\])\4((\s+)|$))/gi; var functionFilter = /([a-z0-9_]+)\(.+?\)/gi; var filteredQueries = {}; var filteredQueriesLines = {}; - var hide = false; + var hide = false, rowData; var queryColumnName = runtime.logDataCols[runtime.logDataCols.length - 2]; var sumColumnName = runtime.logDataCols[runtime.logDataCols.length - 1]; + var isSlowLog = opts.src == 'slow'; + var columnSums = {}; + + var countRow = function(query, row) { + var cells = row.match(/(.*?)<\/td>/gi); + if(!columnSums[query]) columnSums[query] = [0,0,0,0]; + + columnSums[query][0] += timeToSec(cells[2].replace(/(|<\/td>)/gi,'')); + columnSums[query][1] += timeToSec(cells[3].replace(/(|<\/td>)/gi,'')); + columnSums[query][2] += parseInt(cells[4].replace(/(|<\/td>)/gi,'')); + columnSums[query][3] += parseInt(cells[5].replace(/(|<\/td>)/gi,'')); + }; + // We just assume the sql text is always in the second last column, and that the total count is right of it $('div#logTable table tbody tr td:nth-child(' + (runtime.logDataCols.length - 1) + ')').each(function() { if(varFilterChange && $(this).html().match(/^SELECT/i)) { @@ -1604,10 +1876,23 @@ $(function() { filteredQueriesLines[q] = i; $(this).text(q); } + if(isSlowLog) countRow(q, $(this).parent().html()); + // Restore original columns } else { - $(this).text($(this).parent().data('query')[queryColumnName]); - $(this).next().text($(this).parent().data('query')[sumColumnName]); + rowData = $(this).parent().data('query'); + + // SQL Text + $(this).text(rowData[queryColumnName]); + // # + $(this).next().text(rowData[sumColumnName]); + // Slow log columns + if(isSlowLog) { + $(this).parent().children('td:nth-child(3)').text(rowData['query_time']); + $(this).parent().children('td:nth-child(4)').text(rowData['lock_time']); + $(this).parent().children('td:nth-child(5)').text(rowData['rows_sent']); + $(this).parent().children('td:nth-child(6)').text(rowData['rows_examined']); + } } } @@ -1634,21 +1919,28 @@ $(function() { i++; }); + // Update count values of grouped entries if(varFilterChange) { if(noVars) { + var numCol, row, $table = $('div#logTable table tbody'); $.each(filteredQueriesLines, function(key,value) { - if(filteredQueries[value] <= 1) return; - - var numCol = $('div#logTable table tbody tr:nth-child(' + (value+1) + ')') - .children(':nth-child(' + (runtime.logDataCols.length) + ')'); + if(filteredQueries[key] <= 1) return; + row = $table.children('tr:nth-child(' + (value+1) + ')'); + numCol = row.children(':nth-child(' + (runtime.logDataCols.length) + ')'); numCol.text(filteredQueries[key]); + + if(isSlowLog) { + row.children('td:nth-child(3)').text(secToTime(columnSums[key][0])); + row.children('td:nth-child(4)').text(secToTime(columnSums[key][1])); + row.children('td:nth-child(5)').text(columnSums[key][2]); + row.children('td:nth-child(6)').text(columnSums[key][3]); + } }); } $('div#logTable table').trigger("update"); setTimeout(function() { - $('div#logTable table').trigger('sorton',[[[runtime.logDataCols.length - 1,1]]]); }, 0); } @@ -1668,6 +1960,24 @@ $(function() { limitTypes: true });*/ + function timeToSec(timeStr) { + var time = timeStr.split(':'); + return parseInt(time[0]*3600) + parseInt(time[1]*60) + parseInt(time[2]); + } + + function secToTime(timeInt) { + hours = Math.floor(timeInt / 3600); + timeInt -= hours*3600; + minutes = Math.floor(timeInt / 60); + timeInt -= minutes*60; + + if(hours < 10) hours = '0' + hours; + if(minutes < 10) minutes = '0' + minutes; + if(timeInt < 10) timeInt = '0' + timeInt; + + return hours + ':' + minutes + ':' + timeInt; + } + function buildLogTable(data) { var rows = data.rows; var cols = new Array(); @@ -1682,7 +1992,7 @@ $(function() { return value.replace(/(\[.*?\])+/g,''); } return value; - } + }; for(var i=0; i < rows.length; i++) { if(i == 0) { @@ -1701,7 +2011,7 @@ $(function() { for(var j=0; j < cols.length; j++) { // Assuming the query column is the second last if(j == cols.length - 2 && rows[i][cols[j]].match(/^SELECT/i)) { - $tRow.append($tCell=$('' + formatValue(cols[j], rows[i][cols[j]]) + '')); + $tRow.append($tCell=$('' + formatValue(cols[j], rows[i][cols[j]]) + '')); $tCell.click(queryAnalyzer); } else $tRow.append('' + formatValue(cols[j], rows[i][cols[j]]) + ''); @@ -1718,13 +2028,17 @@ $(function() { function queryAnalyzer() { - var query = $(this).parent().data('query')[cols[cols.length-2]]; + var query = $(this).parent().data('query').argument || $(this).parent().data('query').sql_text; + var db = $(this).parent().data('query').db || ''; /* A very basic SQL Formatter. Totally fails in the cases of - - Any string appearance containing a MySQL Keyword, surrounded by whitespaces + - Any string appearance containing a MySQL Keyword, surrounded by whitespaces, e.g. WHERE bar = "This where the formatter fails" - Subqueries too probably */ - // .* selector doesn't includde whitespace, [^] doesn't work in IE8, thus we use [^\0] since the zero-byte char (hopefully) doesn't appear in table names ;) + + // Matches the columns to be selected + // .* selector doesn't include whitespace and we have no PCRE_DOTALL modifier, (.|\s)+ crashes Chrome (reported and confirmed), + // [^]+ results in JS error in IE8, thus we use [^\0]+ for matching each column since the zero-byte char (hopefully) doesn't appear in column names ;) var sLists = query.match(/SELECT\s+[^\0]+\s+FROM\s+/gi); if(sLists) { for(var i=0; i < sLists.length; i++) { @@ -1752,7 +2066,8 @@ $(function() { $.post('server_status.php?'+url_query, { ajax_request: true, query_analyzer: true, - query: codemirror_editor.getValue() + query: codemirror_editor.getValue(), + database: db }, function(data) { data = $.parseJSON(data); var totalTime = 0; @@ -1766,14 +2081,39 @@ $(function() { $('div#queryAnalyzerDialog div.placeHolder') .html('
'); - var explain = 'Explain output

'; - $.each(data.explain, function(key,value) { - value = (value==null)?'null':value; + var explain = 'Explain output '+explain_docu; + if(data.explain.length > 1) { + explain += ' ('; + for(var i=0; i < data.explain.length; i++) { + if(i > 0) explain += ', '; + explain += '' + i + ''; + } + explain += ')'; + } + explain +='

'; + for(var i=0; i < data.explain.length; i++) { + explain += '
0? 'style="display:none;"' : '' ) + '>'; + $.each(data.explain[i], function(key,value) { + value = (value==null)?'null':value; + + if(key == 'type' && value.toLowerCase() == 'all') value = '' + value +''; + if(key == 'Extra') value = value.replace(/(using (temporary|filesort))/gi,'$1'); + explain += key+': ' + value + '
'; + }); + explain += '
'; + } + + // Since there is such a nice free space below the explain, lets put it here for now + explain += '

' + PMA_messages['strAffectedRows'] + ' ' + data.affectedRows; - explain += key+': ' + value + '
'; - }); $('div#queryAnalyzerDialog div.placeHolder td.explain').append(explain); + $('div#queryAnalyzerDialog div.placeHolder a[href*="#showExplain"]').click(function() { + var id = $(this).attr('href').split('-')[1]; + $(this).parent().find('div[class*="explain"]').hide(); + $(this).parent().find('div[class*="explain-' + id + '"]').show(); + }); + if(data.profiling) { var chartData = []; var numberTable = ''; @@ -1790,7 +2130,7 @@ $(function() { numberTable += ''; numberTable += '
StatusTime
Total time:' + PMA_prettyProfilingNum(totalTime,2) + '
'; - $('div#queryAnalyzerDialog div.placeHolder td.chart').append('Profiling results (Table | Chart)
' + numberTable + '

'); + $('div#queryAnalyzerDialog div.placeHolder td.chart').append('Profiling results ' + profiling_docu + ' (Table, Chart)
' + numberTable + '
'); $('div#queryAnalyzerDialog div.placeHolder a[href="#showNums"]').click(function() { $('div#queryAnalyzerDialog div#queryProfiling').hide(); @@ -1883,10 +2223,14 @@ $(function() { $('a[href="#clearMonitorConfig"]').show(); } - $('a[href="#clearMonitorConfig"]').click(function() { - window.localStorage.removeItem('monitorCharts'); - window.localStorage.removeItem('monitorSettings'); - $(this).hide(); - }); + function serverResponseError() { + var btns = {}; + btns[PMA_messages['strReloadPage']] = function() { + window.location.reload(); + }; + $('#emptyDialog').attr('title',PMA_messages['strRefreshFailed']); + $('#emptyDialog').html(' ' + PMA_messages['strInvalidResponseExplanation']) + $('#emptyDialog').dialog({ buttons: btns }); + } }); diff --git a/js/server_synchronize.js b/js/server_synchronize.js index 2cc03652fe..8425423c2b 100644 --- a/js/server_synchronize.js +++ b/js/server_synchronize.js @@ -226,33 +226,23 @@ function showDetails(i, update_size, insert_size, remove_size, insert_index, rem */ function ApplySelectedChanges(token) { - var div = document.getElementById("list"); - var table = div.getElementsByTagName('table')[0]; - var table_body = table.getElementsByTagName('tbody')[0]; - // Get all the rows from the details table - var table_rows = table_body.getElementsByTagName('tr'); - var x = table_rows.length; - var i; /** Append the token at the beginning of the query string followed by Table_ids that shows that "Apply Selected Changes" button is pressed */ - var append_string = "?token="+token+"&Table_ids="+1; - for(i=0; i'; saveLink = ' '+PMA_messages['strSave']+' '; @@ -62,7 +63,7 @@ $(function() { $.ajaxSetup({ cache:false }); - + /* Variable editing */ if(isSuperuser) { $('table.data tbody tr td:nth-child(2)').hover( @@ -76,36 +77,36 @@ $(function() { } ); } - + /*** This code snippet takes care that the table stays readable. It cuts off long strings the table overlaps the window size ***/ $('table.data').after($tmpDiv=$(''+testString+'')); charWidth = $tmpDiv.width() / testString.length; $tmpDiv.remove(); - + $(window).resize(limitTableWidth); limitTableWidth(); - + function limitTableWidth() { var fulltext; var charDiff; var maxTableWidth; var $tmpTable; - + $('table.data').after($tmpTable=$('
'+testString+'
')); - maxTableWidth = $('#testTable').width(); + maxTableWidth = $('#testTable').width(); $tmpTable.remove(); charDiff = ($('table.data').width()-maxTableWidth) / charWidth; - + if($('body').innerWidth() < $('table.data').width()+10 || $('body').innerWidth() > $('table.data').width()+20) { var maxChars=0; - + $('table.data tbody tr td:nth-child(2)').each(function() { maxChars=Math.max($(this).text().length,maxChars); }); - + // Do not resize smaller if there's only 50 chars displayed already if(charDiff > 0 && maxChars < 50) return; - + $('table.data tbody tr td:nth-child(2)').each(function() { if((charDiff>0 && $(this).text().length > maxChars-charDiff) || (charDiff<0 && $(this).find('abbr.cutoff').length>0)) { if($(this).find('abbr.cutoff').length > 0) @@ -115,7 +116,7 @@ $(function() { // Do not cut off elements with html in it and hope they are not too long if(fulltext.length != $(this).html().length) return 0; } - + if(fulltext.length < maxChars-charDiff) $(this).html(fulltext); else $(this).html(''+fulltext.substr(0,maxChars-charDiff-3)+'...'); @@ -123,31 +124,31 @@ $(function() { }); } } - + // Filter options are invisible for disabled js users $('fieldset#tableFilter').css('display',''); - + $('#filterText').keyup(function(e) { if($(this).val().length==0) textFilter=null; - else textFilter = new RegExp("(^| )"+$(this).val().replace('_',' '),'i'); + else textFilter = new RegExp("(^| )"+$(this).val().replace(/_/g,' '),'i'); filterVariables(); }); - + function filterVariables() { odd_row=false; var mark_next=false; var firstCell; - + $('table.filteredData tbody tr').each(function() { firstCell = $(this).children(':first'); - + if(mark_next || textFilter==null || textFilter.exec(firstCell.text())) { // If current row is 'marked', also display next row if($(this).hasClass('marked') && !mark_next) mark_next=true; else mark_next=false; - odd_row = !odd_row; + odd_row = !odd_row; $(this).css('display',''); if(odd_row) { $(this).addClass('odd'); @@ -161,4 +162,4 @@ $(function() { } }); } -}); \ No newline at end of file +}); diff --git a/js/sql.js b/js/sql.js index e762bb53c0..2b77af12f0 100644 --- a/js/sql.js +++ b/js/sql.js @@ -15,11 +15,13 @@ var $data_a; * @param string str * @return string the URL-decoded string */ -function PMA_urldecode(str) { +function PMA_urldecode(str) +{ return decodeURIComponent(str.replace(/\+/g, '%20')); } -function PMA_urlencode(str) { +function PMA_urlencode(str) +{ return encodeURIComponent(str.replace(/\%20/g, '+')); } @@ -29,7 +31,8 @@ function PMA_urlencode(str) { * * @param $this_field jQuery object that points to the current field's tr */ -function getFieldName($this_field) { +function getFieldName($this_field) +{ var this_field_index = $this_field.index(); // ltr or rtl direction does not impact how the DOM was generated @@ -52,7 +55,8 @@ function getFieldName($this_field) { * new inline edit anchor to each table row. * */ -function appendInlineAnchor() { +function appendInlineAnchor() +{ // TODO: remove two lines below if vertical display mode has been completely removed var disp_mode = $("#top_direction_dropdown").val(); @@ -1090,7 +1094,8 @@ $(document).ready(function() { * (when called in the situation where no posting was done, the data * parameter is empty) */ -function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data) { +function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data) +{ // deleting the hide button. remove

tags $del_hide.find('a, br').remove(); @@ -1183,7 +1188,8 @@ function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, * Starting from some th, change the class of all td under it. * If isAddClass is specified, it will be used to determine whether to add or remove the class. */ -function PMA_changeClassForColumn($this_th, newclass, isAddClass) { +function PMA_changeClassForColumn($this_th, newclass, isAddClass) +{ // index 0 is the th containing the big T var th_index = $this_th.index(); var has_big_t = !$this_th.closest('tr').children(':first').hasClass('column_heading'); @@ -1232,7 +1238,8 @@ $(document).ready(function() { /* * Profiling Chart */ -function makeProfilingChart() { +function makeProfilingChart() +{ if ($('#profilingchart').length == 0) { return; } diff --git a/js/tbl_change.js b/js/tbl_change.js index 5e6ce842e0..17677b97ee 100644 --- a/js/tbl_change.js +++ b/js/tbl_change.js @@ -64,7 +64,8 @@ function nullify(theType, urlField, md5Field, multi_edit) * Start of validation part */ //function checks the number of days in febuary -function daysInFebruary (year){ +function daysInFebruary (year) +{ return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 ); } //function to convert single digit to double digit @@ -143,7 +144,8 @@ function isTime(val) return true; } -function verificationsAfterFieldChange(urlField, multi_edit, theType){ +function verificationsAfterFieldChange(urlField, multi_edit, theType) +{ var evt = window.event || arguments.callee.caller.arguments[0]; var target = evt.target || evt.srcElement; @@ -226,23 +228,23 @@ $(document).ready(function() { /** * Handles all current checkboxes for Null; this only takes care of the - * checkboxes on currently displayed rows as the rows generated by - * "Continue insertion" are handled in the "Continue insertion" code - * + * checkboxes on currently displayed rows as the rows generated by + * "Continue insertion" are handled in the "Continue insertion" code + * */ $('.checkbox_null').bind('click', function(e) { nullify( // use hidden fields populated by tbl_change.php $(this).siblings('.nullify_code').val(), - $(this).closest('tr').find('input:hidden').first().val(), + $(this).closest('tr').find('input:hidden').first().val(), $(this).siblings('.hashed_field').val(), $(this).siblings('.multi_edit').val() ); }); /** - * Submission of data to be inserted or updated - * + * Submission of data to be inserted or updated + * * @uses PMA_ajaxShowMessage() * * This section has been deactivated. Here are the problems that I've @@ -257,9 +259,9 @@ $(document).ready(function() { * 2. This code can be called if we are editing or inserting. If editing, * the "and then" action can be "go back to this page" or "edit next * row", in which cases it makes sense to use AJAX. But the "go back - * to previous page" and "insert another new row" actions, using AJAX + * to previous page" and "insert another new row" actions, using AJAX * has no obvious advantage. If inserting, the "go back to previous" - * action needs a page refresh anyway. + * action needs a page refresh anyway. */ $("#insertFormDEACTIVATED").live('submit', function(event) { @@ -388,8 +390,8 @@ $(document).ready(function() { .bind('change', function(e) { var $changed_element = $(this); verificationsAfterFieldChange( - $changed_element.data('hashed_field'), - $changed_element.data('new_row_index'), + $changed_element.data('hashed_field'), + $changed_element.data('new_row_index'), $changed_element.closest('tr').find('span.column_type').html() ); }); @@ -401,15 +403,15 @@ $(document).ready(function() { // to the original row, not the cloned one, so unbind() .unbind('click') // Keep these values to be used when the element - // will be clicked + // will be clicked .data('hashed_field', hashed_field) .data('new_row_index', new_row_index) .bind('click', function(e) { var $changed_element = $(this); nullify( $changed_element.siblings('.nullify_code').val(), - $this_element.closest('tr').find('input:hidden').first().val(), - $changed_element.data('hashed_field'), + $this_element.closest('tr').find('input:hidden').first().val(), + $changed_element.data('hashed_field'), '[multi_edit][' + $changed_element.data('new_row_index') + ']' ); }); @@ -461,7 +463,7 @@ $(document).ready(function() { // IMO it's not really important to handle the tabindex for // function and Null var tabindex = 0; - $('.textfield') + $('.textfield') .each(function() { tabindex++; $(this).attr('tabindex', tabindex); diff --git a/js/tbl_chart.js b/js/tbl_chart.js index 16a83b0f81..bec9a5cc59 100644 --- a/js/tbl_chart.js +++ b/js/tbl_chart.js @@ -7,25 +7,25 @@ $(document).ready(function() { var chart_data = jQuery.parseJSON($('#querychart').html()); chart_series = 'columns'; chart_xaxis_idx = $('select[name="chartXAxis"]').attr('value'); - + $('#resizer').resizable({ minHeight:240, minWidth:300, - // On resize, set the chart size to that of the + // On resize, set the chart size to that of the // resizer minus padding. If your chart has a lot of data or other - // content, the redrawing might be slow. In that case, we recommend + // content, the redrawing might be slow. In that case, we recommend // that you use the 'stop' event instead of 'resize'. resize: function() { currentChart.setSize( - this.offsetWidth - 20, + this.offsetWidth - 20, this.offsetHeight - 20, false ); } - }); - + }); + var currentSettings = { - chart: { + chart: { type: 'line', width: $('#resizer').width() - 20, height: $('#resizer').height() - 20 @@ -36,28 +36,28 @@ $(document).ready(function() { yAxis: { title: { text: $('input[name="yaxis_label"]').attr('value') } }, - title: { - text: $('input[name="chartTitle"]').attr('value'), - margin:20 + title: { + text: $('input[name="chartTitle"]').attr('value'), + margin:20 }, plotOptions: { series: {} } } - + $('#querychart').html(''); - + $('input[name="chartType"]').click(function() { currentSettings.chart.type = $(this).attr('value'); - + drawChart(); - + if($(this).attr('value') == 'bar' || $(this).attr('value') == 'column') $('span.barStacked').show(); else $('span.barStacked').hide(); }); - + $('input[name="barStacked"]').click(function() { if(this.checked) $.extend(true,currentSettings,{ plotOptions: { series: { stacking:'normal' } } }); @@ -65,13 +65,13 @@ $(document).ready(function() { $.extend(true,currentSettings,{ plotOptions: { series: { stacking:null } } }); drawChart(); }); - + $('input[name="chartTitle"]').keyup(function() { var title = $(this).attr('value'); if(title.length == 0) title = ' '; currentChart.setTitle({ text: title }); }); - + $('select[name="chartXAxis"]').change(function() { chart_xaxis_idx = this.value; drawChart(); @@ -81,7 +81,7 @@ $(document).ready(function() { chart_series_index = this.selectedIndex; drawChart(); }); - + /* Sucks, we cannot just set axis labels, we have to redraw the chart completely */ $('input[name="xaxis_label"]').keyup(function() { currentSettings.xAxis.title.text = $(this).attr('value'); @@ -91,56 +91,58 @@ $(document).ready(function() { currentSettings.yAxis.title.text = $(this).attr('value'); drawChart(true); }); - + function drawChart(noAnimation) { currentSettings.chart.width = $('#resizer').width() - 20; currentSettings.chart.height = $('#resizer').height() - 20; - + if(currentChart != null) currentChart.destroy(); - + if(noAnimation) currentSettings.plotOptions.series.animation = false; currentChart = PMA_queryChart(chart_data,currentSettings); if(noAnimation) currentSettings.plotOptions.series.animation = true; } - + drawChart(); $('#querychart').show(); }); -function in_array(element,array) { +function in_array(element,array) +{ for(var i=0; i < array.length; i++) if(array[i] == element) return true; return false; } -function PMA_queryChart(data,passedSettings) { +function PMA_queryChart(data,passedSettings) +{ if($('#querychart').length == 0) return; - + var columnNames = Array(); - + var series = new Array(); var xaxis = { type: 'linear' }; var yaxis = new Object(); - + $.each(data[0],function(index,element) { columnNames.push(index); - }); - + }); + switch(passedSettings.chart.type) { case 'column': case 'spline': case 'line': case 'bar': xaxis.categories = new Array(); - + if(chart_series == 'columns') { var j = 0; - for(var i=0; i'+this.series.name+'
'+this.point.name+'
'+this.y; - return ''+this.series.name+'
'+this.y; + if(this.point.name) return ''+this.series.name+'
'+this.point.name+'
'+this.y; + return ''+this.series.name+'
'+this.y; } } }; if(passedSettings.chart.type == 'pie') settings.tooltip.formatter = function() { return ''+columnNames[0]+'
'+this.y; } - + // Overwrite/Merge default settings with passedsettings $.extend(true,settings,passedSettings); - + return PMA_createChart(settings); } diff --git a/js/tbl_gis_visualization.js b/js/tbl_gis_visualization.js index 81a86c480a..978cd69308 100644 --- a/js/tbl_gis_visualization.js +++ b/js/tbl_gis_visualization.js @@ -15,7 +15,8 @@ var svg; /** * Zooms and pans the visualization. */ -function zoomAndPan() { +function zoomAndPan() +{ var g = svg.getElementById('groupPanel'); g.setAttribute('transform', 'translate(' + x + ', ' + y + ') scale(' + scale + ')'); @@ -173,7 +174,7 @@ $(document).ready(function() { y = height / 2 - (height / 2 - y) * 1.5; zoomAndPan(); }); - + $('#zoom_world').live('click', function(e) { e.preventDefault(); scale = 1; @@ -181,7 +182,7 @@ $(document).ready(function() { y = 0; zoomAndPan(); }); - + $('#zoom_out').live('click', function(e) { e.preventDefault(); //zoom out @@ -239,7 +240,7 @@ $(document).ready(function() { }).appendTo("body").fadeIn(200); } }); - + /** * Detect the mouseout event and hide tooltips. */ diff --git a/js/tbl_relation.js b/js/tbl_relation.js index 5cdb0d7d9f..74cb4092ef 100644 --- a/js/tbl_relation.js +++ b/js/tbl_relation.js @@ -1,9 +1,10 @@ /* vim: set expandtab sw=4 ts=4 sts=4: */ /** - * for tbl_relation.php + * for tbl_relation.php * */ -function show_hide_clauses(thisDropdown) { +function show_hide_clauses(thisDropdown) +{ // here, one span contains the label and the clause dropdown // and we have one span for ON DELETE and one for ON UPDATE // diff --git a/js/tbl_structure.js b/js/tbl_structure.js index 897cad8ade..63fa60f90f 100644 --- a/js/tbl_structure.js +++ b/js/tbl_structure.js @@ -341,7 +341,7 @@ $(document).ready(function() { **/ $("#addColumns.ajax input[value=Go]").live('click', function(event){ event.preventDefault(); - + /*Remove the hidden dialogs if there are*/ if ($('#add_columns').length != 0) { $('#add_columns').remove(); @@ -349,7 +349,7 @@ $(document).ready(function() { var $div = $('
'); var $form = $("#addColumns"); - + /** * @var button_options Object that stores the options passed to jQueryUI * dialog @@ -389,7 +389,7 @@ $(document).ready(function() { //Remove the top menu container from the dialog .find("#topmenucontainer").hide() ; // end dialog options - + $div = $("#add_columns"); /*changed the z-index of the enum editor to allow the edit*/ $("#enum_editor").css("z-index", "1100"); @@ -399,7 +399,7 @@ $(document).ready(function() { }) // end $.get() }); - + }) // end $(document).ready() @@ -411,7 +411,8 @@ $(document).ready(function() { * @param string $url Variable which parses the data for the * post action */ -function changeColumns(action,url) { +function changeColumns(action,url) +{ /*Remove the hidden dialogs if there are*/ if ($('#change_column_dialog').length != 0) { $('#change_column_dialog').remove(); diff --git a/js/update-location.js b/js/update-location.js index d271cc82ea..3b9c51d86d 100644 --- a/js/update-location.js +++ b/js/update-location.js @@ -10,13 +10,14 @@ var hash_init_done = 0; /** * Sets hash part in URL, either calls itself in parent frame or does the - * work itself. The hash is not set directly if we did not yet process old + * work itself. The hash is not set directly if we did not yet process old * one. */ -function setURLHash(hash) { +function setURLHash(hash) +{ if (jQuery.browser.webkit) { - /* - * Setting hash leads to reload in webkit: + /* + * Setting hash leads to reload in webkit: * http://www.quirksmode.org/bugreports/archives/2005/05/Safari_13_visual_anomaly_with_windowlocationhref.html */ return; diff --git a/libraries/Theme.class.php b/libraries/Theme.class.php index 7b01fa8a12..0347ac8bc9 100644 --- a/libraries/Theme.class.php +++ b/libraries/Theme.class.php @@ -16,7 +16,8 @@ * * @package phpMyAdmin */ -class PMA_Theme { +class PMA_Theme +{ /** * @var string theme version * @access protected diff --git a/libraries/advisor.lib.php b/libraries/advisor.lib.php new file mode 100644 index 0000000000..f4530974ff --- /dev/null +++ b/libraries/advisor.lib.php @@ -0,0 +1,199 @@ +variables = array_merge(PMA_DBI_fetch_result('SHOW GLOBAL STATUS', 0, 1), PMA_DBI_fetch_result('SHOW GLOBAL VARIABLES', 0, 1)); + // Step 2: Read and parse the list of rules + $this->parseResult = $this->parseRulesFile(); + // Step 3: Feed the variables to the rules and let them fire. Sets $runResult + $this->runRules(); + + /* echo '

'; + echo 'Total rules: '.count($this->parseResult['rules']).'

'; + echo 'Possible performance issues
'; + foreach($this->runResult['fired'] as $rule) { + echo $rule['issue'].'
'; + } + echo '
Rules not checked due to unmet preconditions
'; + foreach($this->runResult['unchecked'] as $rule) { + echo $rule['name'].'
'; + } + echo '
Rules that didn\'t fire
'; + foreach($this->runResult['notfired'] as $rule) { + echo $rule['name'].'
'; + } + + if($this->runResult['errors']) + echo 'There were errors while testing the rules.'; + */ + return $this->runResult; + } + + function runRules() { + $this->runResult = array( 'fired' => array(), 'notfired' => array(), 'unchecked'=> array(), 'errors' => array() ); + + foreach($this->parseResult['rules'] as $rule) { + $this->variables['value'] = 0; + $precond = true; + + if(isset($rule['precondition'])) { + try { + $precond = $this->ruleExprEvaluate($rule['precondition']); + } catch (Exception $e) { + $this->runResult['errors'][] = 'Failed evaluating precondition for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + continue; + } + } + + if(! $precond) + $this->addRule('unchecked', $rule); + else { + try { + $value = $this->ruleExprEvaluate($rule['formula']); + } catch(Exception $e) { + $this->runResult['errors'][] = 'Failed calculating value for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + continue; + } + + $this->variables['value'] = $value; + + try { + if($this->ruleExprEvaluate($rule['test'])) + $this->addRule('fired', $rule); + else $this->addRule('notfired', $rule); + } catch(Exception $e) { + $this->runResult['errors'][] = 'Failed running test for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + } + } + } + + return true; + } + + function addRule($type, $rule) { + switch($type) { + case 'notfired': + case 'fired': + $jst = preg_split('/\s*\|\s*/',$rule['justification'],2); + if(count($jst) > 1) { + $jst[0] = preg_replace('/%( |,|\.|$)/','%%\1',$jst[0]); + try { + $str = $this->ruleExprEvaluate('sprintf("'.$jst[0].'",'.$jst[1].')',strlen('sprintf("'.$jst[0].'"')); + } catch (Exception $e) { + $this->runResult['errors'][] = 'Failed formattingstring for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + return; + } + + $rule['justification'] = $str; + } + break; + } + + $this->runResult[$type][] = $rule; + } + + // Runs a code expression, replacing variable names with their respective values + // ignoreUntil: if > 0, it doesn't replace any variables until that string position, but still evaluates the whole expr + function ruleExprEvaluate($expr, $ignoreUntil) { + if($ignoreUntil > 0) { + $exprIgnore = substr($expr,0,$ignoreUntil); + $expr = substr($expr,$ignoreUntil); + } + $expr = preg_replace('/fired\s*\(\s*(\'|")(.*)\1\s*\)/Uie','1',$expr); //isset($this->runResult[\'fired\'] + $expr = preg_replace('/\b(\w+)\b/e','isset($this->variables[\'\1\']) ? (!is_numeric($this->variables[\'\1\']) ? \'"\'.$this->variables[\'\1\'].\'"\' : $this->variables[\'\1\']) : \'\1\'', $expr); + if($ignoreUntil > 0){ + $expr = $exprIgnore . $expr; + } + $value = 0; + $err = 0; + ob_start(); + eval('$value = '.$expr.';'); + $err = ob_get_contents(); + ob_end_clean(); + if($err) throw new Exception(strip_tags($err) . '
Executed code: $value = '.$expr.';'); + return $value; + } + + function parseRulesFile() { + $file = file('libraries/advisory_rules.txt'); + $errors = array(); + $rules = array(); + $ruleSyntax = array('name','formula','test','issue','recommendation','justification'); + $numRules = count($ruleSyntax); + $numLines = count($file); + $j = -1; + $ruleLine = -1; + + for ($i = 0; $i<$numLines; $i++) { + $line = $file[$i]; + if($line[0] == '#' || $line[0] == "\n") continue; + + // Reading new rule + if(substr($line, 0, 4) == 'rule') { + if($ruleLine > 0) { $errors[] = 'Invalid rule declaration on line '.($i+1). ', expected line '.$ruleSyntax[$ruleLine++].' of previous rule' ; continue; } + $ruleLine = 1; + if(preg_match("/rule\s'(.*)'( \[(.*)\])?$/",$line,$match)) { + $j++; + $rules[$j] = array( 'name' => $match[1]); + if(isset($match[3])) $rules[$j]['precondition'] = $match[3]; + } else { + $errors[] = 'Invalid rule declaration on line '.($i+1); + } + continue; + } else { + if($ruleLine == -1) $errors[] = 'Unexpected characters on line '.($i+1); + } + + // Reading rule lines + if($ruleLine > 0) { + if(!isset($line[0])) continue; // Empty lines are ok + // Non tabbed lines are not + if($line[0] != "\t") { $errors[] = 'Unexpected character on line '.($i+1).'. Expected tab, but found \''.$line[0].'\''; continue; } + $rules[$j][$ruleSyntax[$ruleLine++]] = chop(substr($line,1)); + } + + // Rule complete + if($ruleLine == $numRules) { + $ruleLine = -1; + } + } + + return array('rules' => $rules, 'errors' => $errors); + } +} + +function PMA_bytime($num, $precision) +{ + $per = ''; + if ($num >= 1) { # per second + $per = "per second"; + } + elseif ($num*60 >= 1) { # per minute + $num = $num*60; + $per = "per minute"; + } + elseif ($num*60*60 >=1 ) { # per hour + $num = $num*60*60; + $per = "per hour"; + } + else { + $num = $num*60*60*24; + $per = "per day"; + } + + $num = round($num, $precision); + + if($num == 0) $num = '<'.pow(10,-$precision); + + return "$num $per"; +} + +?> diff --git a/libraries/advisory_rules.txt b/libraries/advisory_rules.txt new file mode 100644 index 0000000000..c193f852d3 --- /dev/null +++ b/libraries/advisory_rules.txt @@ -0,0 +1,427 @@ +# phpMyAdmin Advisory rules file +# Use only UNIX style newlines +# This file is being parsed by advisor.lib.php, which should handle syntax errors correctly. +# However, PHP Warnings and the like are being consumed by the phpMyAdmin error handler, so those won't show up +# E.g.: Justification line is empty because you used an unescape percent sign, sprintf() returns an empty string and no warning/error is shown +# +# Rule Syntax: +# 'rule' identifier[the name of the rule] eexpr [an optional precondition] +# expr [variable or value calculation used for the test] +# expr [test, if evaluted to 'true' it fires the rule. Use 'value' to insert the calculated value (without quotes)] +# string [the issue (what is the problem?)] +# string [the recommendation (how do i fix it?)] +# formatted-string '|' comma-seperated-expr [the justification (result of the calculated value / why did this rule fire?)] + +# comma-seperated-expr: expr(,expr)* +# eexpr: [expr] - expr enclosed in [] +# expr: a php code literal with extras: +# - variable names are replaced with their respective values +# - fired('name of rule') is replaced with true/false when given rule has been fired. Note however that this is a very simple rules engine. Rules are only checked in sequential order as they are written down here. If given rule has not been checked yet, fired() will always evaluate to false +# - 'value' is replaced with the calculated value. If it is a string, it will be put within single quotes +# - other than that you may use any php function, initialized variable or constant +# +# identifier: A string enclosed in single quotes +# string: A quoteless string, may contain HTML. Variable names enclosed in curly braces are replaced with links to directly edit this variable. e.g. {tmp_table_size} +# formatted-string: You may use classic php sprintf() string formatting here, the arguments must be appended after a trailing pipe (|) as mentioned in above syntax +# percent signs (%) are automatically escaped (%%) in the following cases: When followed by a space, dot or comma and at the end of the line) +# +# Comments start with # +# + + +# Queries + +rule 'Uptime below one day' + Uptime + value < 86400 + Uptime is less than 1 day, performance tuning may not be accurate. + To have more accurate averages it is recommended to let the server run for longer than a day before running this analyzer + The uptime is only %s | PMA_timespanFormat(Uptime) + +rule 'Questions below 1,000' + Questions + value < 1000 + Fewer than 1,000 questions have been run against this server. The recommendations may not be accurate. + Let the server run for a longer time until it has executed a greater amount of queries. + Current amount of Questions: %s | Questions + +rule '% slow queries' [Questions > 0] + Slow_queries / Questions * 100 + value >= 5 + There is a lot of slow queries compared to the overall amount of Queries. + You might want to increase {long_query_time} or optimize the queries listed in the slow query log + The slow query rate should be below 5%, your value is %s%. | round(value,2) + +rule 'slow query rate' [Questions > 0] + (Slow_queries / Questions * 100) / Uptime + value * 60 * 60 > 1 + There is a high percentage of slow queries compared to the server uptime. + You might want to increase {long_query_time} or optimize the queries listed in the slow query log + You have a slow query rate of %s per hour, you should have less than 1% per hour. | PMA_bytime(value,2) + +rule 'Long query time' + long_query_time + value >= 10 + long_query_time is set to 10 seconds or more, thus only slow queries that take above 10 seconds are logged. + It is suggested to set {long_query_time} to a lower value, depending on your enviroment. Usually a value of 1-5 seconds is suggested. + long_query_time is currently set to %ss. | value + +rule 'Slow query logging' + log_slow_queries + value == 'OFF' + The slow query log is disabled. + Enable slow query logging by setting {log_slow_queries} to 'ON'. This will help troubleshooting badly performing queries. + log_slow_queries is set to 'OFF' + +# +# versions +rule 'Release Series' + version + !PMA_DRIZZLE && substr(value,0,3) != "5.1" + The MySQL server version is less than 5.1. + You should upgrade, as MySQL 5.1 has improved performance, and MySQL 5.5 even more so. + Current version: %s | value + +rule 'Minor Version' + version + !PMA_DRIZZLE && substr(value,4,2) < 30 + Version less than 5.1.30 (the first GA release of 5.1). + You should upgrade, as recent versions of MySQL 5.1 have improved performance and MySQL 5.5 even more so. + Current version: %s | value + +rule 'Distribution' + version_comment + preg_match('/source/i',value) + Version is compiled from source, not a MySQL official binary. If you did not compile from source, you may be using a package modified by a distribution. + The MySQL manual only is accurate for official MySQL binaries, not any package distributions (such as RedHat, Debian/Ubuntu etc). + 'source' found in version_comment + +rule 'Distribution' + version_comment + preg_match('/percona/i',value) + The MySQL manual only is accurate for official MySQL binaries. + Percona documentation is at http://www.percona.com/docs/wiki/ + 'percona' found in version_comment + +rule 'MySQL Architecture' + system_memory + value > 3072 && !preg_match('/64/',version_compile_machine) + MySQL is not compiled as a 64-bit package, though your memory capacity is above 3 GiB. + MySQL might not be able to access all of your memory. You might want to consider installing the 64-bit version of MySQL. + Available memory on this host: %s | implode(' ',PMA_formatByteDown(value*1024*1024, 2, 2)) + +# +# Query cache + +# Lame: 'ON' == 0 is true, so you need to compare 'ON' == '0' +rule 'Query cache disabled' + query_cache_size + value == 0 || query_cache_type == 'OFF' || query_cache_type == '0' + The query cache is not enabled. + The query cache is known to greatly improve performance if configured correctly. Enable it by setting {query_cache_size} to a 2 digit MiB value and setting {query_cache_type} to 'ON' + query_cache_size is set to 0 or query_cache_type is set to 'OFF' + +rule 'Query cache efficiency (%)' [Com_select + Qcache_hits > 0 && !fired('Query cache disabled')] + Qcache_hits / (Com_select + Qcache_hits) * 100 + value < 20 + Query cache not running efficiently, it has a low hit rate. + Consider increasing {query_cache_limit}. + The current query cache hit rate of %s% is below 20% | round(value,1) + +rule 'Query Cache usage' [!fired('Query cache disabled')] + 100 - Qcache_free_memory / query_cache_size * 100 + value < 80 + Less than 80% of the query cache is being utilized. + This might be caused by {query_cache_limit} being too low. Flushing the query cache might help as well. + The current ratio of free query cache memory to total query cache size is %s%. It should be above 80% | round(value,1) + +rule 'Query cache fragmentation' [!fired('Query cache disabled')] + Qcache_free_blocks / (Qcache_total_blocks / 2) * 100 + value > 20 + The query cache is considerably fragmented. + Severe fragmentation is likely to (further) increase Qcache_lowmem_prunes. This might be caused by many Query cache low memory prunes due to {query_cache_size} being too small. For a immediate but short lived fix you can flush the query cache (might lock the query cache for a long time). Carefully adjusting {query_cache_min_res_unit} to a lower value might help too, e.g. you can set it to the average size of your queries in the cache using this formula: (query_cache_size - qcache_free_memory) / qcache_queries_in_cache + The cache is currently fragmented by %s% , with 100% fragmentation meaning that the query cache is an alternating pattern of free and used blocks. This value should be below 20%. | round(value,1) + +rule 'Query cache low memory prunes' [Qcache_inserts > 0 && !fired('Query cache disabled')] + Qcache_lowmem_prunes / Qcache_inserts * 100 + value > 0.1 + Cached queries are removed due to low query cache memory from the query cache. + You might want to increase {query_cache_size}, however keep in mind that the overhead of maintaining the cache is likely to increase with its size, so do this in small increments and monitor the results. + The ratio of removed queries to inserted queries is %s%. The lower this value is, the better (This rules firing limit: 0.1%) | round(value,1) + +rule 'Query cache max size' [!fired('Query cache disabled')] + query_cache_size + value > 1024 * 128 + The query cache size is above 128 MiB. Big query caches may cause significant overhead that is required to maintain the cache. + Depending on your enviroment, it might be performance increasing to reduce this value. + Current query cache size: %s | implode(' ',PMA_formatByteDown(value, 2, 2)) + +rule 'Query cache min result size' [!fired('Query cache disabled')] + value == 1024*1024 + query_cache_limit + The max size of the result set in the query cache is the default of 1 MiB. + Changing {query_cache_limit} (usually by increasing) may increase efficiency. This variable determines the maximum size a query result may have to be inserted into the query cache. If there are many query results above 1 MiB that are well cacheable (many reads, little writes) then increasing {query_cache_limit} will increase efficiency. Whereas in the case of many query results being above 1 MiB that are not very well cacheable (often invalidated due to table updates) increasing {query_cache_limit} might reduce efficiency. + query_cache_limit is set to 1 MiB + +# +# Sorts +rule '% sorts that cause temporary tales' [Sort_scan + Sort_range > 0] + Sort_merge_passes / (Sort_scan + Sort_range) * 100 + value > 10 + Too many sorts are causing temporary tables. + Consider increasing sort_buffer_size and/or read_rnd_buffer_size, depending on your system memory limits + %s% of all sorts cause temporary tables, this value should be lower than 10%. | round(value,1) + +rule 'rate of sorts that cause temporary tables' + Sort_merge_passes / Uptime + value * 60 * 60 > 1 + Too many sorts are causing temporary tables. + Consider increasing sort_buffer_size and/or read_rnd_buffer_size, depending on your system memory limits + Temporary tables average: %s, this value should be less than 1 per hour. | PMA_bytime(value,2) + +rule 'Sort rows' + Sort_rows / Uptime + value * 60 >= 1 + There are lots of rows being sorted. + While there is nothing wrong with a high amount of row sorting, you might want to make sure that the queries which require a lot of sorting use indexed fields in the ORDER BY clause, as this will result in much faster sorting + Sorted rows average: %s | PMA_bytime(value,2) + +# Joins, scans +rule 'rate of joins without indexes' + (Select_range_check + Select_scan + Select_full_join) / Uptime + value * 60 * 60 > 1 + There are too many joins without indexes. + This means that joins are doing full table scans. Adding indexes for the fields being used in the join conditions will greatly speed up table joins + Table joins average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading first index entry' + Handler_read_first / Uptime + value * 60 * 60 > 1 + The rate of reading the first index entry is high. + This usually indicates frequent full index scans. Full index scans are faster than table scans but require lots of cpu cycles in big tables, if those tables that have or had high volumes of UPDATEs and DELETEs, running 'OPTIMIZE TABLE' might reduce the amount of and/or speed up full index scans. Other than that full index scans can only be reduced by rewriting queries. + Index scans average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading fixed position' + Handler_read_rnd / Uptime + value * 60 * 60 > 1 + The rate of reading data from a fixed position is high. + This indicates many queries need to sort results and/or do a full table scan, including join queries that do not use indexes. Add indexes where applicable. + Rate of reading fixed position average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading next table row' + Handler_read_rnd_next / Uptime + value * 60 * 60 > 1 + The rate of reading the next table row is high. + This indicates many queries are doing full table scans. Add indexes where applicable. + Rate of reading next table row: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# temp tables +rule 'tmp_table_size vs. max_heap_table_size' + tmp_table_size - max_heap_table_size + value !=0 + tmp_table_size and max_heap_table_size are not the same. + If you have deliberatly changed one of either: The server uses the lower value of either to determine the maximum size of in-memory tables. So if you wish to increse the in-memory table limit you will have to increase the other value as well. + Current values are tmp_table_size: %s, max_heap_table_size: %s | implode(' ',PMA_formatByteDown(tmp_table_size, 2, 2)), implode(' ',PMA_formatByteDown(max_heap_table_size, 2, 2)) + +rule '% temp disk tables' [Created_tmp_tables + Created_tmp_disk_tables > 0] + Created_tmp_disk_tables / (Created_tmp_tables + Created_tmp_disk_tables) * 100 + value > 25 + Many temporary tables are being written to disk instead of being kept in memory. + Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To elminiate these you will have to rewrite your queries to avoid those conditions (Within a temprorary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the beginning of an
Article by the Pythian Group + %s% of all temporary tables are being written to disk, this value should be below 25% | round(value,1) + +rule 'temp disk rate' + Created_tmp_disk_tables / Uptime + value * 60 * 60 > 1 + Many temporary tables are being written to disk instead of being kept in memory. + Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To elminiate these you will have to rewrite your queries to avoid those conditions (Within a temprorary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in in the MySQL Documentation + Rate of temporay tables being written to disk: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# I couldn't find any source on the internet that suggests a direct relation between high counts of temporary tables and any of these variables. +# Several independent Blog entries suggest (http://ronaldbradford.com/blog/more-on-understanding-sort_buffer_size-2010-05-10/ and http://www.xaprb.com/blog/2010/05/09/how-to-tune-mysqls-sort_buffer_size/) +# that sort_buffer_size should be left as it is. And increasing read_buffer_size is only suggested when there are a lot of +# table scans (http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_read_buffer_size and other sources) though +# setting it too high is bad too (http://www.mysqlperformanceblog.com/2007/09/17/mysql-what-read_buffer_size-value-is-optimal/). +#rule 'temp table rate' +# Created_tmp_tables / Uptime +# value * 60 * 60 > 1 +# Many intermediate temporary tables are being created. +# This may be caused by queries under certain conditions as mentioned in the MySQL Documentation. Consider increasing {sort_buffer_size} (sorting), {read_rnd_buffer_size} (random read buffer, ie, post-sort), {read_buffer_size} (sequential scan). + +# +# MyISAM index cache +rule 'MyISAM key buffer size' + key_buffer_size + value == 0 + Key buffer is not initialized. No MyISAM indexes will be cached. + Set {key_buffer_size} depending on the size of your MyISAM indexes. 64M is a good start. + key_buffer_size is 0 + +rule 'max % MyISAM key buffer ever used' [key_buffer_size > 0] + Key_blocks_used * key_cache_block_size / key_buffer_size * 100 + value < 95 + MyISAM key buffer (index cache) % used is low. + You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used. + max % MyISAM key buffer ever used: %s, this value should be above 95% | round(value,1) + +# Don't fire if above rule fired - we don't need the same advice twice +rule '% MyISAM key buffer used' [key_buffer_size > 0 && !fired('max % MyISAM key buffer ever used')] + ( 1 - Key_blocks_unused * key_cache_block_size / key_buffer_size) * 100 + value < 95 + MyISAM key buffer (index cache) % used is low. + You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used. + % MyISAM key buffer used: %s, this value should be above 95% | round(value,1) + +rule '% index reads from memory' [Key_read_requests > 0] + 100 - (Key_reads / Key_read_requests * 100) + value < 95 + The % of indexes that use the MyISAM key buffer is low. + You may need to increase {key_buffer_size}. + Index reads from memory: %s%, this value should be above 95% | round(value,1) + +# +# other caches +rule 'rate of table open' + Opened_tables / Uptime + value*60*60 > 10 + The rate of opening tables is high. + Opening tables requires disk I/O which is costly. Increasing {table_open_cache} might avoid this. + Opened table rate: %s, this value should be less than 10 per hour | PMA_bytime(value,2) + +rule '% open files' + Open_files / open_files_limit * 100 + value > 85 + The number of open files is approaching the max number of open files. You may get a "Too many open files" error. + Consider increasing {open_files_limit}, and check the error log when restarting after changing open_files_limit. + The number of opened files is at %s% of the limit. It should be below 85% | round(value,1) + +rule 'rate of open files' + Open_files / Uptime + value * 60 * 60 > 5 + The rate of opening files is high. + Consider increasing {open_files_limit}, and check the error log when restarting after changing open_files_limit. + Opened files rate: %s, this value should be less than 5 per hour | PMA_bytime(value,2) + +rule 'Immediate table locks %' [Table_locks_waited + Table_locks_immediate > 0] + Table_locks_immediate / (Table_locks_waited + Table_locks_immediate) * 100 + value < 95 + Too many table locks were not granted immediately. + Optimize queries and/or use InnoDB to reduce lock wait. + Immediate table locks: %s%, this value should be above 95% | round(value,1) + +rule 'Table lock wait rate' + Table_locks_waited / Uptime + value * 60 * 60 > 1 + Too many table locks were not granted immediately. + Optimize queries and/or use InnoDB to reduce lock wait. + Table lock wait rate: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'thread cache' + thread_cache_size + value < 1 + Thread cache is disabled, resulting in more overhead from new connections to MySQL. + Enable the thread cache by setting {thread_cache_size} > 0. + The thread cache is set to 0 + +rule 'thread cache hit rate %' [thread_cache_size > 0] + 100 - Threads_created / Connections + value < 80 + Thread cache is not efficient. + Increase {thread_cache_size}. + Thread cache hitrate: %s%, this value should be above 80% | round(value,1) + +rule 'Threads that are slow to launch' [slow_launch_time > 0] + Slow_launch_threads + value > 0 + There are too many threads that are slow to launch. + This generally happens in case of general system overload as it is pretty simple operations. You might want to monitor your system load carefully. + %s thread(s) took longer than %s seconds to start, it should be 0 | value, slow_launch_time + +rule 'Slow launch time' + slow_launch_time + value > 2 + Slow_launch_threads is above 2s + Set slow_launch_time to 1s or 2s to correctly count threads that are slow to launch + slow_launch_time is set to %s | value + +# +#Connections +rule '% connections used' + Max_used_connections / max_connections * 100 + value > 80 + The maximum amount of used connnections is getting close to the value of max_connections. + Increase max_connections, or decrease wait_timeout so that connections that do not close database handlers properly get killed sooner. Make sure the code closes database handlers properly. + Max_used_connections is at %s% of max_connections, it should be below 80% | round(value,1) + +rule '% aborted connections' + Aborted_connects / Connections * 100 + value > 1 + Too many connections are aborted. + Connections are usually aborted when they cannot be authorized. This article might help you track down the source. + %s% of all connections are aborted. This value should be below 1% | round(value,1) + +rule 'rate of aborted connections' + Aborted_connects / Uptime + value * 60 * 60 > 1 + Too many connections are aborted + Connections are usually aborted when they cannot be authorized. This article might help you track down the source. + Aborted connections rate is at %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule '% aborted clients' + Aborted_clients / Connections * 100 + value > 2 + Too many clients are aborted. + Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code. + %s% of all clients are aborted. This value should be below 2% | round(value,1) + +rule 'rate of aborted clients' + Aborted_clients / Uptime + value * 60 * 60 > 1 + Too many clients are aborted. + Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code. + Aborted client rate is at %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# +# InnoDB +rule 'Is InnoDB disabled?' + have_innodb + value != "YES" + You do not have InnoDB enabled. + InnoDB is usually the better choice for table engines. + have_innodb is set to 'value' + +rule '% InnoDB log size' [innodb_buffer_pool_size > 0] + innodb_log_file_size / innodb_buffer_pool_size * 100 + value < 20 + The InnoDB log file size is not an appropriate size, in relation to the InnoDB buffer pool. + Especiallay one a system with a lot of writes to InnoDB tables you shoud set innodb_log_file_size to 25% of {innodb_buffer_pool_size}. However the bigger this value, the longer the recovery time will be when database crashes, so this value should not be set much higher than 256 MiB. Please note however that you cannot simply change the value of this variable. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also this blog entry + Your InnoDB log size is at %s% in relation to the InnoDB buffer pool size, it should not be below 20% | round(value,1) + +rule 'Max InnoDB log size' [innodb_buffer_pool_size > 0 && innodb_log_file_size / innodb_buffer_pool_size * 100 < 30] + innodb_log_file_size / (1024 * 1024) + value >= 128 + The InnoDB log file size is inadequately large. + It is usually sufficient to set innodb_log_file_size to 25% of the size of {innodb_buffer_pool_size}. A very innodb_log_file_size slows down the recovery time after a database crash considerably. See also this Article. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also this blog entry + Your absolute InnoD log size is %s MiB | round(value,1) + +rule 'InnoDB buffer pool size' [system_memory > 0] + innodb_buffer_pool_size / system_memory * 100 + value < 60 + Your InnoDB buffer pool is fairly small. + The InnoDB buffer pool has a profound impact on perfomance for InnoDB tables. Assign all your remaining memory to this buffer. For database servers that use solely InnoDB as storage engine and have no other services (e.g. a web server) running, you may set this as high as 80% of your available memory. If that is not the case, you need to carefully assess the memory consumption of your other services and non-InnoDB-Tables and set this variable accordingly. If it is set too high, your system will start swapping, which decreases performance significantly. See also this article + You are currently using %s% of your memory for the InnoDB buffer pool. This rule fires if you are assigning less than 60%, however this might be perfectly adequate for your system if you don't have much InnoDB tables or other services running on the same machine. + +# +# other +rule 'MyISAM concurrent inserts' + concurrent_insert + value == 0 + Enable concurrent_insert by setting it to 1 + Setting {concurrent_insert} to 1 reduces contention between readers and writers for a given table. See also MySQL Documentation + concurrent_insert is set to 0 + +# INSERT DELAYED USAGE +#Delayed_errors 0 +#Delayed_insert_threads 0 +#Delayed_writes 0 +#Not_flushed_delayed_rows diff --git a/libraries/auth/cookie.auth.lib.php b/libraries/auth/cookie.auth.lib.php index 9360b9aae0..4615c55ff9 100644 --- a/libraries/auth/cookie.auth.lib.php +++ b/libraries/auth/cookie.auth.lib.php @@ -81,7 +81,8 @@ if (function_exists('mcrypt_encrypt')) { * * @access public */ -function PMA_get_blowfish_secret() { +function PMA_get_blowfish_secret() +{ if (empty($GLOBALS['cfg']['blowfish_secret'])) { if (empty($_SESSION['auto_blowfish_secret'])) { // this returns 23 characters diff --git a/libraries/auth/signon.auth.lib.php b/libraries/auth/signon.auth.lib.php index 0a079b26a1..976845ccfc 100644 --- a/libraries/auth/signon.auth.lib.php +++ b/libraries/auth/signon.auth.lib.php @@ -18,7 +18,8 @@ * * @access public */ -function PMA_auth() { +function PMA_auth() +{ unset($_SESSION['LAST_SIGNON_URL']); if (empty($GLOBALS['cfg']['Server']['SignonURL'])) { PMA_fatalError('You must set SignonURL!'); diff --git a/libraries/blobstreaming.lib.php b/libraries/blobstreaming.lib.php index 301403c16e..5cfae23d42 100644 --- a/libraries/blobstreaming.lib.php +++ b/libraries/blobstreaming.lib.php @@ -228,15 +228,25 @@ function PMA_BS_GetVariables() return $BS_Variables; } -//======================== -//======================== +/** + * Retrieves and shows PBMS error. + * + * @return nothing + */ function PMA_BS_ReportPBMSError($msg) { $tmp_err = pbms_error(); PMA_showMessage(__('PBMS error') . " $msg $tmp_err"); } -//------------ +/** + * Tries to connect to PBMS server. + * + * @param string $db_name Database name + * @param bool $quiet Whether to report errors + * + * @return bool Connection status. + */ function PMA_do_connect($db_name, $quiet) { $PMA_Config = $GLOBALS['PMA_Config']; @@ -266,17 +276,24 @@ function PMA_do_connect($db_name, $quiet) return true; } -//------------ +/** + * Disconnects from PBMS server. + * + * @return nothing + */ function PMA_do_disconnect() { pbms_close(); } -//------------ /** - * checks whether the BLOB reference looks valid + * Checks whether the BLOB reference looks valid * -*/ + * @param string $bs_reference BLOB reference + * @param string $db_name Database name + * + * @return bool True on success. + */ function PMA_BS_IsPBMSReference($bs_reference, $db_name) { if (PMA_cacheGet('skip_blobstreaming', true)) { @@ -312,7 +329,7 @@ function PMA_BS_CreateReferenceLink($bs_reference, $db_name) $content_type = pbms_get_metadata_value("Content-Type"); if ($content_type == false) { $br = trim($bs_reference); - PMA_BS_ReportPBMSError("PMA_BS_CreateReferenceLink('$br', '$db_name'): " . __('get BLOB Content-Type failed')); + PMA_BS_ReportPBMSError("PMA_BS_CreateReferenceLink('$br', '$db_name'): " . __('PBMS get BLOB Content-Type failed')); } PMA_do_disconnect(); @@ -357,11 +374,12 @@ function PMA_BS_CreateReferenceLink($bs_reference, $db_name) return $output; } -//------------ -// In the future there may be server variables to turn on/off PBMS -// BLOB streaming on a per table or database basis. So in anticipation of this -// PMA_BS_IsTablePBMSEnabled() passes in the table and database name even though -// they are not currently needed. +/** + * In the future there may be server variables to turn on/off PBMS + * BLOB streaming on a per table or database basis. So in anticipation of this + * PMA_BS_IsTablePBMSEnabled() passes in the table and database name even though + * they are not currently needed. + */ function PMA_BS_IsTablePBMSEnabled($db_name, $tbl_name, $tbl_type) { if (PMA_cacheGet('skip_blobstreaming', true)) { diff --git a/libraries/build_html_for_db.lib.php b/libraries/build_html_for_db.lib.php index 6f4357c4cb..8d64549c31 100644 --- a/libraries/build_html_for_db.lib.php +++ b/libraries/build_html_for_db.lib.php @@ -13,7 +13,8 @@ if (! defined('PHPMYADMIN')) { * * @return array */ -function PMA_getColumnOrder() { +function PMA_getColumnOrder() +{ $column_order['DEFAULT_COLLATION_NAME'] = array( 'disp_name' => __('Collation'), @@ -70,7 +71,8 @@ function PMA_getColumnOrder() { * * @return array $column_order, $out */ -function PMA_buildHtmlForDb($current, $is_superuser, $checkall, $url_query, $column_order, $replication_types, $replication_info) { +function PMA_buildHtmlForDb($current, $is_superuser, $checkall, $url_query, $column_order, $replication_types, $replication_info) +{ $out = ''; if ($is_superuser || $GLOBALS['cfg']['AllowUserDropDatabase']) { diff --git a/libraries/charset_conversion.lib.php b/libraries/charset_conversion.lib.php index b3ae693030..8d19ad436f 100644 --- a/libraries/charset_conversion.lib.php +++ b/libraries/charset_conversion.lib.php @@ -67,7 +67,8 @@ if ($PMA_recoding_engine == PMA_CHARSET_ICONV_AIX) { * @access public * */ -function PMA_convert_string($src_charset, $dest_charset, $what) { +function PMA_convert_string($src_charset, $dest_charset, $what) +{ if ($src_charset == $dest_charset) { return $what; } diff --git a/libraries/common.inc.php b/libraries/common.inc.php index 160fcfd6ec..5eb3cb68a7 100644 --- a/libraries/common.inc.php +++ b/libraries/common.inc.php @@ -553,7 +553,7 @@ $_REQUEST['js_frame'] = PMA_ifSetOr($_REQUEST['js_frame'], ''); * @global array $js_include */ $GLOBALS['js_include'] = array(); -$GLOBALS['js_include'][] = 'jquery/jquery-1.6.1.js'; +$GLOBALS['js_include'][] = 'jquery/jquery-1.6.2.js'; $GLOBALS['js_include'][] = 'jquery/jquery-ui-1.8.custom.js'; $GLOBALS['js_include'][] = 'update-location.js'; diff --git a/libraries/common.lib.php b/libraries/common.lib.php index d812262c0b..e491adb13f 100644 --- a/libraries/common.lib.php +++ b/libraries/common.lib.php @@ -6,12 +6,32 @@ * @package phpMyAdmin */ +/** + * Detects which function to use for PMA_pow. + * + * @return string Function name. + */ +function PMA_detect_pow() +{ + if (function_exists('bcpow')) { + // BCMath Arbitrary Precision Mathematics Function + return 'bcpow'; + } elseif (function_exists('gmp_pow')) { + // GMP Function + return 'gmp_pow'; + } else { + // PHP function + return 'pow'; + } +} + /** * Exponential expression / raise number into power * * @param string $base base to raise * @param string $exp exponent to use * @param mixed $use_function pow function to use, or false for auto-detect + * * @return mixed string or float */ function PMA_pow($base, $exp, $use_function = false) @@ -19,16 +39,7 @@ function PMA_pow($base, $exp, $use_function = false) static $pow_function = null; if (null == $pow_function) { - if (function_exists('bcpow')) { - // BCMath Arbitrary Precision Mathematics Function - $pow_function = 'bcpow'; - } elseif (function_exists('gmp_pow')) { - // GMP Function - $pow_function = 'gmp_pow'; - } else { - // PHP function - $pow_function = 'pow'; - } + $pow_function = PMA_detect_pow(); } if (! $use_function) { @@ -64,34 +75,21 @@ function PMA_pow($base, $exp, $use_function = false) * * @param string $icon name of icon file * @param string $alternate alternate text - * @param boolean $container include in container * @param boolean $force_text whether to force alternate text to be displayed * @param boolean $noSprite If true, the image source will be not replaced with a CSS Sprite + * * @return html img tag */ -function PMA_getIcon($icon, $alternate = '', $container = false, $force_text = false, $noSprite = false) +function PMA_getIcon($icon, $alternate = '', $force_text = false, $noSprite = false) { - $include_icon = false; - $include_text = false; - $include_box = false; + // $cfg['PropertiesIconic'] is true or both + $include_icon = ($GLOBALS['cfg']['PropertiesIconic'] !== false); + // $cfg['PropertiesIconic'] is false or both + // OR we have no $include_icon + $include_text = ($force_text || true !== $GLOBALS['cfg']['PropertiesIconic']); $alternate = htmlspecialchars($alternate); $button = ''; - if ($GLOBALS['cfg']['PropertiesIconic']) { - $include_icon = true; - } - - if ($force_text || true !== $GLOBALS['cfg']['PropertiesIconic']) { - // $cfg['PropertiesIconic'] is false or both - // OR we have no $include_icon - $include_text = true; - } - - if ($include_text && $include_icon && $container) { - // we have icon, text and request for container - $include_box = true; - } - // Always use a span (we rely on this in js/sql.js) $button .= ''; @@ -123,6 +121,7 @@ function PMA_getIcon($icon, $alternate = '', $container = false, $force_text = f * Displays the maximum size for an upload * * @param integer $max_upload_size the size + * * @return string the message * * @access public @@ -140,6 +139,7 @@ function PMA_displayMaximumUploadSize($max_upload_size) * the maximum size for upload * * @param integer $max_size the size + * * @return string the INPUT field * * @access public @@ -195,6 +195,7 @@ function PMA_sqlAddSlashes($a_string = '', $is_like = false, $crlf = false, $php * Note: This function does not escape backslashes! * * @param string $name the string to escape + * * @return string the escaped string * * @access public @@ -212,7 +213,9 @@ function PMA_escape_mysql_wildcards($name) * Note: This function does not unescape backslashes! * * @param string $name the string to escape + * * @return string the escaped string + * * @access public */ function PMA_unescape_mysql_wildcards($name) @@ -230,6 +233,7 @@ function PMA_unescape_mysql_wildcards($name) * * @param string $quoted_string string to remove quotes from * @param string $quote type of quote to remove + * * @return string unqoted string */ function PMA_unQuote($quoted_string, $quote = null) @@ -263,6 +267,7 @@ function PMA_unQuote($quoted_string, $quote = null) * @todo move into PMA_Sql * @param mixed $parsed_sql pre-parsed SQL structure * @param string $unparsed_sql raw SQL string + * * @return string the formatted sql * * @global array the configuration array @@ -410,11 +415,13 @@ function PMA_showMySQLDocu($chapter, $link, $big_icon = false, $anchor = '', $ju * Displays a link to the phpMyAdmin documentation * * @param string $anchor anchor in documentation + * * @return string the html link * * @access public */ -function PMA_showDocu($anchor) { +function PMA_showDocu($anchor) +{ if ($GLOBALS['cfg']['ReplaceHelpImg']) { return '' . __('Documentation') . ''; } else { @@ -426,11 +433,13 @@ function PMA_showDocu($anchor) { * Displays a link to the PHP documentation * * @param string $target anchor in documentation + * * @return string the html link * * @access public */ -function PMA_showPHPDocu($target) { +function PMA_showPHPDocu($target) +{ $url = PMA_getPHPDocLink($target); if ($GLOBALS['cfg']['ReplaceHelpImg']) { @@ -446,7 +455,9 @@ function PMA_showPHPDocu($target) { * @param string $message the error message * @param bool $bbcode * @param string $type + * * @return string html code for a footnote marker + * * @access public */ function PMA_showHint($message, $bbcode = false, $type = 'notice') @@ -638,6 +649,7 @@ function PMA_mysqlDie($error_message = '', $the_query = '', * @param string $tables name of tables * @param integer $limit_offset list offset * @param int|bool $limit_count max tables to return + * * @return array (recursive) grouped table list */ function PMA_getTableList($db, $tables = null, $limit_offset = 0, $limit_count = false) @@ -768,7 +780,9 @@ function PMA_getTableList($db, $tables = null, $limit_offset = 0, $limit_count = * or array of it * @param boolean $do_it a flag to bypass this function (used by dump * functions) + * * @return mixed the "backquoted" database, table or field name + * * @access public */ function PMA_backquote($a_name, $do_it = true) @@ -862,7 +876,9 @@ if (!$jsonly) * @param string $sql_query the query to display * @param string $type the type (level) of the message * @param boolean $is_view is this a message after a VIEW operation? + * * @return string + * * @access public */ function PMA_showMessage($message, $sql_query = null, $type = 'notice', $is_view = false) @@ -1218,6 +1234,7 @@ function PMA_showMessage($message, $sql_query = null, $type = 'notice', $is_view * Verifies if current MySQL server supports profiling * * @access public + * * @return boolean whether profiling is supported */ function PMA_profilingSupported() @@ -1303,6 +1320,7 @@ function PMA_formatByteDown($value, $limes = 6, $comma = 0) * Changes thousands and decimal separators to locale specific values. * * @param $value + * * @return string */ function PMA_localizeNumber($value) @@ -1416,6 +1434,7 @@ function PMA_formatNumber($value, $digits_left = 3, $digits_right = 0, $only_dow * Returns the number of bytes when a formatted size is given * * @param string $formatted_size the size expression (for example 8MB) + * * @return integer The numerical part of the expression (for example 8) */ function PMA_extractValueFromFormattedSize($formatted_size) @@ -1437,6 +1456,7 @@ function PMA_extractValueFromFormattedSize($formatted_size) * * @param string $timestamp the current timestamp * @param string $format format + * * @return string the formatted date * * @access public @@ -1506,7 +1526,9 @@ function PMA_localisedDate($timestamp = -1, $format = '') * * @param array $tab array with all options * @param array $url_params + * * @return string html code for one tab, a link if valid otherwise a span + * * @access public */ function PMA_generate_html_tab($tab, $url_params = array(), $base_dir='') @@ -1605,6 +1627,7 @@ function PMA_generate_html_tab($tab, $url_params = array(), $base_dir='') * * @param array $tabs one element per tab * @param string $url_params + * * @return string html-code for tab-navigation */ function PMA_generate_html_tabs($tabs, $url_params, $base_dir='') @@ -1781,6 +1804,7 @@ function PMA_timespanFormat($seconds) * @param string $Separator The Separator (defaults to "
\n") * * @access public + * * @return string The flipped string */ function PMA_flipstring($string, $Separator = "
\n") @@ -1882,6 +1906,7 @@ function PMA_checkParameters($params, $die = true, $request = true) * @param boolean $force_unique generate condition only on pk or unique * * @access public + * * @return array the calculated condition and whether condition is unique */ function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, $force_unique=false) @@ -2051,6 +2076,7 @@ function PMA_buttonOrImage($button_name, $button_class, $image_name, $text, * @param string $prompt The prompt to display (sometimes empty) * * @return string + * * @access public */ function PMA_pageselector($rows, $pageNow = 1, $nbTotalPage = 1, @@ -2146,7 +2172,8 @@ function PMA_pageselector($rows, $pageNow = 1, $nbTotalPage = 1, * * @access public */ -function PMA_listNavigator($count, $pos, $_url_params, $script, $frame, $max_count) { +function PMA_listNavigator($count, $pos, $_url_params, $script, $frame, $max_count) +{ if ($max_count < $count) { echo 'frame_navigation' == $frame ? '