diff --git a/.babelrc b/.babelrc index 0339d5d0ee..4cfddf102a 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,4 @@ { - "presets": ["env"] + "presets": ["env"], + "plugins": ["syntax-dynamic-import"] } diff --git a/.eslintrc.json b/.eslintrc.json index 384c0813a0..e570104acf 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,9 +5,12 @@ "node": true, "es6": true }, + "parser": "babel-eslint", "parserOptions": { "ecmaVersion": 6, "sourceType": "module", + "allowImportExportEverywhere": true, + "codeFrame": false, "ecmaFeatures": { "jsx": false } diff --git a/.gitignore b/.gitignore index 0c05e127de..865ba315de 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ composer.lock /vendor/ # NPM /node_modules/ +yarn.lock diff --git a/js/common_params.php b/js/common_params.php new file mode 100644 index 0000000000..d7a5cf7c39 --- /dev/null +++ b/js/common_params.php @@ -0,0 +1,40 @@ +getJsParams() as $name => $value) { + Sanitize::printJsValue("common_params['" . $name . "']", $value); +} + +?> diff --git a/js/functions.js b/js/functions.js index f3a66b5a13..23003167c0 100644 --- a/js/functions.js +++ b/js/functions.js @@ -5105,3 +5105,63 @@ jQuery.fn.getPostData = function () { } return dataPost; }; + +/** + * @todo REMOVE THESE FUNCTIONS AFTER COMPLETE CODE BEING MODULAR + * + * Copy of functions copied from different files to make them globally + * available so that build does not break during modularisation + */ +/** + * server_privilages.js + */ +/** + * Validates the "add a user" form + * + * @return boolean whether the form is validated or not + */ +function checkAddUser (the_form) { + if (the_form.elements.pred_hostname.value === 'userdefined' && the_form.elements.hostname.value === '') { + alert(PMA_messages.strHostEmpty); + the_form.elements.hostname.focus(); + return false; + } + + if (the_form.elements.pred_username.value === 'userdefined' && the_form.elements.username.value === '') { + alert(PMA_messages.strUserEmpty); + the_form.elements.username.focus(); + return false; + } + + return PMA_checkPassword($(the_form)); +} // end of the 'checkAddUser()' function + +function checkPasswordStrength (value, meter_obj, meter_object_label, username) { + // List of words we don't want to appear in the password + var customDict = [ + 'phpmyadmin', + 'mariadb', + 'mysql', + 'php', + 'my', + 'admin', + ]; + if (username !== null) { + customDict.push(username); + } + var zxcvbn_obj = zxcvbn(value, customDict); + var strength = zxcvbn_obj.score; + strength = parseInt(strength); + meter_obj.val(strength); + switch (strength) { + case 0: meter_object_label.html(PMA_messages.strExtrWeak); + break; + case 1: meter_object_label.html(PMA_messages.strVeryWeak); + break; + case 2: meter_object_label.html(PMA_messages.strWeak); + break; + case 3: meter_object_label.html(PMA_messages.strGood); + break; + case 4: meter_object_label.html(PMA_messages.strStrong); + } +} diff --git a/js/messages.php b/js/messages.php index 2e91f0d1bb..3a82821f93 100644 --- a/js/messages.php +++ b/js/messages.php @@ -755,6 +755,26 @@ foreach ($js_messages as $name => $js_message) { Sanitize::printJsValue("PMA_messages['" . $name . "']", $js_message); } +/* for new js code */ +/* Calendar */ +echo "var globalVars = new Array();\n"; +echo "globalVars['themeCalendarImage'] = '" , $GLOBALS['pmaThemeImage'] + , 'b_calendar.png' , "';\n"; + +/* Image path */ +echo "globalVars['pmaThemeImage'] = '" , $GLOBALS['pmaThemeImage'] , "';\n"; + +echo "globalVars['mysql_doc_template'] = '" , PhpMyAdmin\Util::getMySQLDocuURL('%s') + , "';\n"; + +//Max input vars allowed by PHP. +$maxInputVars = ini_get('max_input_vars'); +echo "globalVars['maxInputVars'] = '" + , (false === $maxInputVars || '' == $maxInputVars ? 'false' : (int)$maxInputVars) + , ';' . "'\n"; +/* for new js code */ + +/* for old js code */ /* Calendar */ echo "var themeCalendarImage = '" , $GLOBALS['pmaThemeImage'] , 'b_calendar.png' , "';\n"; @@ -770,7 +790,144 @@ $maxInputVars = ini_get('max_input_vars'); echo 'var maxInputVars = ' , (false === $maxInputVars || '' == $maxInputVars ? 'false' : (int)$maxInputVars) , ';' . "\n"; +/* for old js code */ +/* for new js code */ +echo "datePicker = new Array();\n"; +echo "timePicker = new Array();\n"; +/* l10n: Display text for calendar close link */ +Sanitize::printJsValue("datePicker['closeText']", __('Done')); +/* l10n: Display text for previous month link in calendar */ +Sanitize::printJsValue( + "datePicker['prevText']", + _pgettext('Previous month', 'Prev') +); +/* l10n: Display text for next month link in calendar */ +Sanitize::printJsValue( + "datePicker['nextText']", + _pgettext('Next month', 'Next') +); +/* l10n: Display text for current month link in calendar */ +Sanitize::printJsValue("datePicker['currentText']", __('Today')); +Sanitize::printJsValue( + "datePicker['monthNames']", + array( + __('January'), + __('February'), + __('March'), + __('April'), + __('May'), + __('June'), + __('July'), + __('August'), + __('September'), + __('October'), + __('November'), + __('December') + ) +); +Sanitize::printJsValue( + "datePicker['monthNamesShort']", + array( + /* l10n: Short month name */ + __('Jan'), + /* l10n: Short month name */ + __('Feb'), + /* l10n: Short month name */ + __('Mar'), + /* l10n: Short month name */ + __('Apr'), + /* l10n: Short month name */ + _pgettext('Short month name', 'May'), + /* l10n: Short month name */ + __('Jun'), + /* l10n: Short month name */ + __('Jul'), + /* l10n: Short month name */ + __('Aug'), + /* l10n: Short month name */ + __('Sep'), + /* l10n: Short month name */ + __('Oct'), + /* l10n: Short month name */ + __('Nov'), + /* l10n: Short month name */ + __('Dec') + ) +); +Sanitize::printJsValue( + "datePicker['dayNames']", + array( + __('Sunday'), + __('Monday'), + __('Tuesday'), + __('Wednesday'), + __('Thursday'), + __('Friday'), + __('Saturday') + ) +); +Sanitize::printJsValue( + "datePicker['dayNamesShort']", + array( + /* l10n: Short week day name */ + __('Sun'), + /* l10n: Short week day name */ + __('Mon'), + /* l10n: Short week day name */ + __('Tue'), + /* l10n: Short week day name */ + __('Wed'), + /* l10n: Short week day name */ + __('Thu'), + /* l10n: Short week day name */ + __('Fri'), + /* l10n: Short week day name */ + __('Sat') + ) +); +Sanitize::printJsValue( + "datePicker['dayNamesMin']", + array( + /* l10n: Minimal week day name */ + __('Su'), + /* l10n: Minimal week day name */ + __('Mo'), + /* l10n: Minimal week day name */ + __('Tu'), + /* l10n: Minimal week day name */ + __('We'), + /* l10n: Minimal week day name */ + __('Th'), + /* l10n: Minimal week day name */ + __('Fr'), + /* l10n: Minimal week day name */ + __('Sa') + ) +); +/* l10n: Column header for week of the year in calendar */ +Sanitize::printJsValue("datePicker['weekHeader']", __('Wk')); + +Sanitize::printJsValue( + "datePicker['showMonthAfterYear']", + /* l10n: Month-year order for calendar, use either "calendar-month-year" + * or "calendar-year-month". + */ + (__('calendar-month-year') == 'calendar-year-month') +); +/* l10n: Year suffix for calendar, "none" is empty. */ +$year_suffix = _pgettext('Year suffix', 'none'); +Sanitize::printJsValue( + "datePicker['yearSuffix']", + ($year_suffix == 'none' ? '' : $year_suffix) +); +Sanitize::printJsValue("timePicker['timeText']", __('Time')); +Sanitize::printJsValue("timePicker['hourText']", __('Hour')); +Sanitize::printJsValue("timePicker['minuteText']", __('Minute')); +Sanitize::printJsValue("timePicker['secondText']", __('Second')); +/* for new js code */ + +/* for old js code */ echo "if ($.datepicker) {\n"; /* l10n: Display text for calendar close link */ Sanitize::printJsValue("$.datepicker.regional['']['closeText']", __('Done')); @@ -908,11 +1065,78 @@ Sanitize::printJsValue("$.timepicker.regional['']['timeText']", __('Time')); Sanitize::printJsValue("$.timepicker.regional['']['hourText']", __('Hour')); Sanitize::printJsValue("$.timepicker.regional['']['minuteText']", __('Minute')); Sanitize::printJsValue("$.timepicker.regional['']['secondText']", __('Second')); +/* for old js code */ ?> $.extend($.timepicker._defaults, $.timepicker.regional['']); } /* if ($.timepicker) */ diff --git a/js/server_privileges.js b/js/server_privileges.js index eef6f26405..d03d68f3df 100644 --- a/js/server_privileges.js +++ b/js/server_privileges.js @@ -9,57 +9,6 @@ * */ -/** - * Validates the "add a user" form - * - * @return boolean whether the form is validated or not - */ -function checkAddUser (the_form) { - if (the_form.elements.pred_hostname.value === 'userdefined' && the_form.elements.hostname.value === '') { - alert(PMA_messages.strHostEmpty); - the_form.elements.hostname.focus(); - return false; - } - - if (the_form.elements.pred_username.value === 'userdefined' && the_form.elements.username.value === '') { - alert(PMA_messages.strUserEmpty); - the_form.elements.username.focus(); - return false; - } - - return PMA_checkPassword($(the_form)); -} // end of the 'checkAddUser()' function - -function checkPasswordStrength (value, meter_obj, meter_object_label, username) { - // List of words we don't want to appear in the password - customDict = [ - 'phpmyadmin', - 'mariadb', - 'mysql', - 'php', - 'my', - 'admin', - ]; - if (username !== null) { - customDict.push(username); - } - var zxcvbn_obj = zxcvbn(value, customDict); - var strength = zxcvbn_obj.score; - strength = parseInt(strength); - meter_obj.val(strength); - switch (strength) { - case 0: meter_obj_label.html(PMA_messages.strExtrWeak); - break; - case 1: meter_obj_label.html(PMA_messages.strVeryWeak); - break; - case 2: meter_obj_label.html(PMA_messages.strWeak); - break; - case 3: meter_obj_label.html(PMA_messages.strGood); - break; - case 4: meter_obj_label.html(PMA_messages.strStrong); - } -} - /** * AJAX scripts for server_privileges page. * @@ -75,7 +24,6 @@ function checkPasswordStrength (value, meter_obj, meter_object_label, username) * @name document.ready */ - /** * Unbind all event handlers before tearing down a page */ diff --git a/js/src/ajax.js b/js/src/ajax.js new file mode 100644 index 0000000000..0897884595 --- /dev/null +++ b/js/src/ajax.js @@ -0,0 +1,757 @@ +import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_commonParams } from './variables/common_params'; +import { jQuery as $ } from './utils/JqueryExtended'; +import { PMA_getImage } from './functions/get_image'; + +/** + * This object handles ajax requests for pages. It also + * handles the reloading of the main menu and scripts. + */ + +/** + * @todo this function is to be removed when complete code is modularised + */ +function checkNewCode (script) { + var check = script.split('.js'); + // console.log(check); + if (check.length === 2) { + return false; + } else { + return true; + } +} + +export let AJAX = { + /** + * @var bool test variable for checking this object + */ + test: false, + /** + * @var bool active Whether we are busy + */ + active: false, + /** + * @var object source The object whose event initialized the request + */ + source: null, + /** + * @var object xhr A reference to the ajax request that is currently running + */ + xhr: null, + /** + * @var object lockedTargets, list of locked targets + */ + lockedTargets: {}, + /** + * @var function Callback to execute after a successful request + * Used by PMA_commonFunctions from common.js + */ + _callback: function () {}, + /** + * @var bool _debug Makes noise in your Firebug console + */ + _debug: false, + /** + * @var object $msgbox A reference to a jQuery object that links to a message + * box that is generated by PMA_ajaxShowMessage() + */ + $msgbox: null, + /** + * Given the filename of a script, returns a hash to be + * used to refer to all the events registered for the file + * + * @param key string key The filename for which to get the event name + * + * @return int + */ + + hash: function (key) { + /* http://burtleburtle.net/bob/hash/doobs.html#one */ + key += ''; + var len = key.length; + var hash = 0; + var i = 0; + for (; i < len; ++i) { + hash += key.charCodeAt(i); + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return Math.abs(hash); + }, + /** + * Registers an onload event for a file + * + * @param file string file The filename for which to register the event + * @param func function func The function to execute when the page is ready + * + * @return self For chaining + */ + registerOnload: function (file, func) { + var eventName = 'onload_' + this.hash(file); + $(document).on(eventName, func); + if (this._debug) { + console.log( + // no need to translate + 'Registered event ' + eventName + ' for file ' + file + ); + } + return this; + }, + /** + * Registers a teardown event for a file. This is useful to execute functions + * that unbind events for page elements that are about to be removed. + * + * @param string file The filename for which to register the event + * @param function func The function to execute when + * the page is about to be torn down + * + * @return self For chaining + */ + registerTeardown: function (file, func) { + var eventName = 'teardown_' + this.hash(file); + $(document).on(eventName, func); + if (this._debug) { + console.log( + // no need to translate + 'Registered event ' + eventName + ' for file ' + file + ); + } + return this; + }, + /** + * Called when a page has finished loading, once for every + * file that registered to the onload event of that file. + * + * @param string file The filename for which to fire the event + * + * @return void + */ + fireOnload: function (file) { + var eventName = 'onload_' + this.hash(file); + $(document).trigger(eventName); + if (this._debug) { + console.log( + // no need to translate + 'Fired event ' + eventName + ' for file ' + file + ); + } + }, + /** + * Called just before a page is torn down, once for every + * file that registered to the teardown event of that file. + * + * @param string file The filename for which to fire the event + * + * @return void + */ + fireTeardown: function (file) { + var eventName = 'teardown_' + this.hash(file); + $(document).triggerHandler(eventName); + if (this._debug) { + console.log( + // no need to translate + 'Fired event ' + eventName + ' for file ' + file + ); + } + }, + /** + * function to handle lock page mechanism + * + * @param event the event object + * + * @return void + */ + lockPageHandler: function (event) { + var newHash = null; + var oldHash = null; + var lockId; + // CodeMirror lock + if (event.data.value === 3) { + newHash = event.data.content; + oldHash = true; + lockId = 'cm'; + } else { + // Don't lock on enter. + if (0 === event.charCode) { + return; + } + + lockId = $(this).data('lock-id'); + if (typeof lockId === 'undefined') { + return; + } + /* + * @todo Fix Code mirror does not give correct full value (query) + * in textarea, it returns only the change in content. + */ + if (event.data.value === 1) { + newHash = AJAX.hash($(this).val()); + } else { + newHash = AJAX.hash($(this).is(':checked')); + } + oldHash = $(this).data('val-hash'); + } + // Set lock if old value !== new value + // otherwise release lock + if (oldHash !== newHash) { + AJAX.lockedTargets[lockId] = true; + } else { + delete AJAX.lockedTargets[lockId]; + } + // Show lock icon if locked targets is not empty. + // otherwise remove lock icon + if (!$.isEmptyObject(AJAX.lockedTargets)) { + $('#lock_page_icon').html(PMA_getImage('s_lock', PMA_messages.strLockToolTip).toString()); + } else { + $('#lock_page_icon').html(''); + } + }, + /** + * resets the lock + * + * @return void + */ + resetLock: function () { + AJAX.lockedTargets = {}; + $('#lock_page_icon').html(''); + }, + handleMenu: { + replace: function (content) { + $('#floating_menubar').html(content) + // Remove duplicate wrapper + // TODO: don't send it in the response + .children().first().remove(); + $('#topmenu').menuResizer(PMA_mainMenuResizerCallback); + } + }, + /** + * Event handler for clicks on links and form submissions + * + * @param object e Event data + * + * @return void + */ + requestHandler: function (event) { + // In some cases we don't want to handle the request here and either + // leave the browser deal with it natively (e.g: file download) + // or leave an existing ajax event handler present elsewhere deal with it + var href = $(this).attr('href'); + if (typeof event !== 'undefined' && (event.shiftKey || event.ctrlKey)) { + return true; + } else if ($(this).attr('target')) { + return true; + } else if ($(this).hasClass('ajax') || $(this).hasClass('disableAjax')) { + // reset the lockedTargets object, as specified AJAX operation has finished + AJAX.resetLock(); + return true; + } else if (href && href.match(/^#/)) { + return true; + } else if (href && href.match(/^mailto/)) { + return true; + } else if ($(this).hasClass('ui-datepicker-next') || + $(this).hasClass('ui-datepicker-prev') + ) { + return true; + } + if (typeof event !== 'undefined') { + event.preventDefault(); + event.stopImmediatePropagation(); + } + + // triggers a confirm dialog if: + // the user has performed some operations on loaded page + // the user clicks on some link, (won't trigger for buttons) + // the click event is not triggered by script + + if (typeof event !== 'undefined' && event.type === 'click' && + event.isTrigger !== true && + !$.isEmptyObject(AJAX.lockedTargets) + ) { + if (confirm(PMA_messages.strConfirmNavigation) === false) { + return false; + } else { + if (isStorageSupported('localStorage')) { + window.localStorage.removeItem('auto_saved_sql'); + } else { + Cookies.set('auto_saved_sql', ''); + } + } + } + AJAX.resetLock(); + var isLink = !! href || false; + var previousLinkAborted = false; + if (AJAX.active === true) { + // Cancel the old request if abortable, when the user requests + // something else. Otherwise silently bail out, as there is already + // a request well in progress. + if (AJAX.xhr) { + // In case of a link request, attempt aborting + AJAX.xhr.abort(); + if (AJAX.xhr.status === 0 && AJAX.xhr.statusText === 'abort') { + // If aborted + AJAX.$msgbox = PMA_ajaxShowMessage(PMA_messages.strAbortedRequest); + AJAX.active = false; + AJAX.xhr = null; + previousLinkAborted = true; + } else { + // If can't abort + return false; + } + } else { + // In case submitting a form, don't attempt aborting + return false; + } + } + AJAX.source = $(this); + + $('html, body').animate({ scrollTop: 0 }, 'fast'); + + var url = isLink ? href : $(this).attr('action'); + var argsep = PMA_commonParams.get('arg_separator'); + var params = 'ajax_request=true' + argsep + 'ajax_page_request=true'; + var dataPost = AJAX.source.getPostData(); + if (! isLink) { + params += argsep + $(this).serialize(); + } else if (dataPost) { + params += argsep + dataPost; + isLink = false; + } + if (! (history && history.pushState)) { + // Add a list of menu hashes that we have in the cache to the request + params += PMA_MicroHistory.menus.getRequestParam(); + } + + if (AJAX._debug) { + console.log('Loading: ' + url); // no need to translate + } + + if (isLink) { + AJAX.active = true; + AJAX.$msgbox = PMA_ajaxShowMessage(); + // Save reference for the new link request + AJAX.xhr = $.get(url, params, AJAX.responseHandler); + if (history && history.pushState) { + var state = { + url : href + }; + if (previousLinkAborted) { + // hack: there is already an aborted entry on stack + // so just modify the aborted one + history.replaceState(state, null, href); + } else { + history.pushState(state, null, href); + } + } + } else { + /** + * Manually fire the onsubmit event for the form, if any. + * The event was saved in the jQuery data object by an onload + * handler defined below. Workaround for bug #3583316 + */ + var onsubmit = $(this).data('onsubmit'); + // Submit the request if there is no onsubmit handler + // or if it returns a value that evaluates to true + if (typeof onsubmit !== 'function' || onsubmit.apply(this, [event])) { + AJAX.active = true; + AJAX.$msgbox = PMA_ajaxShowMessage(); + $.post(url, params, AJAX.responseHandler); + } + } + }, + /** + * Called after the request that was initiated by this.requestHandler() + * has completed successfully or with a caught error. For completely + * failed requests or requests with uncaught errors, see the .ajaxError + * handler at the bottom of this file. + * + * To refer to self use 'AJAX', instead of 'this' as this function + * is called in the jQuery context. + * + * @param object e Event data + * + * @return void + */ + responseHandler: function (data) { + if (typeof data === 'undefined' || data === null) { + return; + } + if (typeof data.success !== 'undefined' && data.success) { + $('html, body').animate({ scrollTop: 0 }, 'fast'); + PMA_ajaxRemoveMessage(AJAX.$msgbox); + + if (data._redirect) { + PMA_ajaxShowMessage(data._redirect, false); + AJAX.active = false; + AJAX.xhr = null; + return; + } + + AJAX.scriptHandler.reset(function () { + if (data._reloadNavigation) { + PMA_reloadNavigation(); + } + + if (data._title) { + $('title').replaceWith(data._title); + } + if (data._menu) { + if (history && history.pushState) { + var state = { + url : data._selflink, + menu : data._menu + }; + history.replaceState(state, null); + AJAX.handleMenu.replace(data._menu); + } else { + PMA_MicroHistory.menus.replace(data._menu); + PMA_MicroHistory.menus.add(data._menuHash, data._menu); + } + } else if (data._menuHash) { + if (! (history && history.pushState)) { + PMA_MicroHistory.menus.replace(PMA_MicroHistory.menus.get(data._menuHash)); + } + } + if (data._disableNaviSettings) { + PMA_disableNaviSettings(); + } else { + PMA_ensureNaviSettings(data._selflink); + } + + // Remove all containers that may have + // been added outside of #page_content + $('body').children() + .not('#pma_navigation') + .not('#floating_menubar') + .not('#page_nav_icons') + .not('#page_content') + .not('#selflink') + .not('#pma_header') + .not('#pma_footer') + .not('#pma_demo') + .not('#pma_console_container') + .not('#prefs_autoload') + .remove(); + // Replace #page_content with new content + if (data.message && data.message.length > 0) { + $('#page_content').replaceWith( + '
' + data.message + '
' + ); + PMA_highlightSQL($('#page_content')); + checkNumberOfFields(); + } + + if (data._selflink) { + var source = data._selflink.split('?')[0]; + // Check for faulty links + var $selflink_replace = { + 'import.php': 'tbl_sql.php', + 'tbl_chart.php': 'sql.php', + 'tbl_gis_visualization.php': 'sql.php' + }; + if ($selflink_replace[source]) { + var replacement = $selflink_replace[source]; + data._selflink = data._selflink.replace(source, replacement); + } + $('#selflink').find('> a').attr('href', data._selflink); + } + if (data._params) { + PMA_commonParams.setAll(data._params); + } + if (data._scripts) { + AJAX.scriptHandler.load(data._scripts); + } + if (data._selflink && data._scripts && data._menuHash && data._params) { + if (! (history && history.pushState)) { + PMA_MicroHistory.add( + data._selflink, + data._scripts, + data._menuHash, + data._params, + AJAX.source.attr('rel') + ); + } + } + if (data._displayMessage) { + $('#page_content').prepend(data._displayMessage); + PMA_highlightSQL($('#page_content')); + } + + $('#pma_errors').remove(); + + var msg = ''; + if (data._errSubmitMsg) { + msg = data._errSubmitMsg; + } + if (data._errors) { + $('
', { id : 'pma_errors', class : 'clearfloat' }) + .insertAfter('#selflink') + .append(data._errors); + // bind for php error reporting forms (bottom) + $('#pma_ignore_errors_bottom').on('click', function (e) { + e.preventDefault(); + PMA_ignorePhpErrors(); + }); + $('#pma_ignore_all_errors_bottom').on('click', function (e) { + e.preventDefault(); + PMA_ignorePhpErrors(false); + }); + // In case of 'sendErrorReport'='always' + // submit the hidden error reporting form. + if (data._sendErrorAlways === '1' && + data._stopErrorReportLoop !== '1' + ) { + $('#pma_report_errors_form').submit(); + PMA_ajaxShowMessage(PMA_messages.phpErrorsBeingSubmitted, false); + $('html, body').animate({ scrollTop:$(document).height() }, 'slow'); + } else if (data._promptPhpErrors) { + // otherwise just prompt user if it is set so. + msg = msg + PMA_messages.phpErrorsFound; + // scroll to bottom where all the errors are displayed. + $('html, body').animate({ scrollTop:$(document).height() }, 'slow'); + } + } + PMA_ajaxShowMessage(msg, false); + // bind for php error reporting forms (popup) + $('#pma_ignore_errors_popup').on('click', function () { + PMA_ignorePhpErrors(); + }); + $('#pma_ignore_all_errors_popup').on('click', function () { + PMA_ignorePhpErrors(false); + }); + + if (typeof AJAX._callback === 'function') { + AJAX._callback.call(); + } + AJAX._callback = function () {}; + }); + } else { + PMA_ajaxShowMessage(data.error, false); + AJAX.active = false; + AJAX.xhr = null; + PMA_handleRedirectAndReload(data); + if (data.fieldWithError) { + $(':input.error').removeClass('error'); + $('#' + data.fieldWithError).addClass('error'); + } + } + }, + /** + * This object is in charge of downloading scripts, + * keeping track of what's downloaded and firing + * the onload event for them when the page is ready. + */ + scriptHandler: { + /** + * @var array _scripts The list of files already downloaded + */ + _scripts: [], + /** + * @var string _scriptsVersion version of phpMyAdmin from which the + * scripts have been loaded + */ + _scriptsVersion: null, + /** + * @var array _scriptsToBeLoaded The list of files that + * need to be downloaded + */ + _scriptsToBeLoaded: [], + /** + * @var array _scriptsToBeFired The list of files for which + * to fire the onload and unload events + */ + _scriptsToBeFired: [], + _scriptsCompleted: false, + /** + * Records that a file has been downloaded + * + * @param string file The filename + * @param string fire Whether this file will be registering + * onload/teardown events + * + * @return self For chaining + */ + add: function (file, fire, callback) { + var self = this; + this._scripts.push(file); + if (fire) { + // Record whether to fire any events for the file + // This is necessary to correctly tear down the initial page + this._scriptsToBeFired.push(file); + } + /** + * @todo This condition is to be removed once all the files are modularised + */ + if (checkNewCode(file)) { + var fileImports = ['server_privileges', 'server_databases']; + if ($.inArray(file, fileImports) !== -1) { + // Dynamic import to load the files dynamically + // This is used for the purpose of code splitting + import(`./${file}`) + .then((module) => { + /** + * setTimeout is used so that scripts run only when content is + * available. + */ + setTimeout(function () { + /** + * @var i The name of the module exported in corresponding file + */ + for (var i in module) { + // If the export has onload in its name, register onload for that export + if (i.indexOf('onload') !== -1) { + AJAX.registerOnload(`${file}`, module[i]); + } else if (i.indexOf('teardown') !== -1) { + // If the export has teardown in its name, register teardown for that export + AJAX.registerTeardown(file, module[i]); + } + } + // Firinf onload for the files being dynamically imported. + AJAX.fireOnload(file); + }, 250); + // AJAX.fireTeardown('server_databases_new.js'); + }) + // Error of Dynamica Imoprts need to be handled with Tracekit gracefully + .catch(e => console.log(e)); + } + } + return this; + }, + /** + * Download a list of js files in one request + * + * @param array files An array of filenames and flags + * + * @return void + */ + load: function (files, callback) { + var self = this; + var i; + // Clear loaded scripts if they are from another version of phpMyAdmin. + // Depends on common params being set before loading scripts in responseHandler + if (self._scriptsVersion === null) { + self._scriptsVersion = PMA_commonParams.get('PMA_VERSION'); + } else if (self._scriptsVersion !== PMA_commonParams.get('PMA_VERSION')) { + self._scripts = []; + self._scriptsVersion = PMA_commonParams.get('PMA_VERSION'); + } + self._scriptsCompleted = false; + self._scriptsToBeFired = []; + // We need to first complete list of files to load + // as next loop will directly fire requests to load them + // and that triggers removal of them from + // self._scriptsToBeLoaded + for (i in files) { + self._scriptsToBeLoaded.push(files[i].name); + if (files[i].fire) { + self._scriptsToBeFired.push(files[i].name); + } + } + for (i in files) { + var script = files[i].name; + // Only for scripts that we don't already have + if ($.inArray(script, self._scripts) === -1) { + this.add(script, 0, callback); + /** + * @todo To be removed once complete code is modularised + * Only for appending js files in the header if not already present + */ + if (!checkNewCode(script)) { + this.appendScript(script, callback); + } + } + self.done(script, callback); + } + // Trigger callback if there is nothing else to load + self.done(null, callback); + }, + /** + * Called whenever all files are loaded + * + * @return void + */ + done: function (script, callback) { + if (typeof ErrorReport !== 'undefined') { + ErrorReport.wrap_global_functions(); + } + if ($.inArray(script, this._scriptsToBeFired)) { + AJAX.fireOnload(script); + } + if ($.inArray(script, this._scriptsToBeLoaded)) { + this._scriptsToBeLoaded.splice($.inArray(script, this._scriptsToBeLoaded), 1); + } + if (script === null) { + this._scriptsCompleted = true; + } + /* We need to wait for last signal (with null) or last script load */ + AJAX.active = (this._scriptsToBeLoaded.length > 0) || ! this._scriptsCompleted; + /* Run callback on last script */ + if (! AJAX.active && $.isFunction(callback)) { + callback(); + } + }, + /** + * Appends a script element to the head to load the scripts + * + * @return void + * this is to be removed in modularised code + * no appending of scripts is needed + * + * @todo This method will be of no use in modular code + */ + appendScript: function (name, callback) { + var head = document.head || document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + var self = this; + + script.type = 'text/javascript'; + script.src = 'js/' + name + '?' + 'v=' + encodeURIComponent(PMA_commonParams.get('PMA_VERSION')); + script.async = false; + + script.onload = function () { + self.done(name, callback); + }; + head.appendChild(script); + }, + /** + * Fires all the teardown event handlers for the current page + * and rebinds all forms and links to the request handler + * + * @param function callback The callback to call after resetting + * + * @return void + */ + reset: function (callback) { + for (var i in this._scriptsToBeFired) { + AJAX.fireTeardown(this._scriptsToBeFired[i]); + } + this._scriptsToBeFired = []; + /** + * Re-attach a generic event handler to clicks + * on pages and submissions of forms + */ + $(document).off('click', 'a').on('click', 'a', AJAX.requestHandler); + $(document).off('submit', 'form').on('submit', 'form', AJAX.requestHandler); + if (! (history && history.pushState)) { + PMA_MicroHistory.update(); + } + callback(); + } + } +}; + +/** + * Attach a generic event handler to clicks + * on pages and submissions of forms + */ +$(document).on('click', 'a', AJAX.requestHandler); +$(document).on('submit', 'form', AJAX.requestHandler); + +/** + * @todo this is to be removed when complete code is modularised + * Exporsing module to window for use with non modular code + */ +window.AJAX = AJAX; diff --git a/js/src/consts/doclinks.js b/js/src/consts/doclinks.js new file mode 100644 index 0000000000..a07acce03a --- /dev/null +++ b/js/src/consts/doclinks.js @@ -0,0 +1,365 @@ +/** + * Definition of links to MySQL documentation. + */ + +export const mysql_doc_keyword = { + /* Multi word */ + 'CHARACTER SET': Array('charset'), + 'SHOW AUTHORS': Array('show-authors'), + 'SHOW BINARY LOGS': Array('show-binary-logs'), + 'SHOW BINLOG EVENTS': Array('show-binlog-events'), + 'SHOW CHARACTER SET': Array('show-character-set'), + 'SHOW COLLATION': Array('show-collation'), + 'SHOW COLUMNS': Array('show-columns'), + 'SHOW CONTRIBUTORS': Array('show-contributors'), + 'SHOW CREATE DATABASE': Array('show-create-database'), + 'SHOW CREATE EVENT': Array('show-create-event'), + 'SHOW CREATE FUNCTION': Array('show-create-function'), + 'SHOW CREATE PROCEDURE': Array('show-create-procedure'), + 'SHOW CREATE TABLE': Array('show-create-table'), + 'SHOW CREATE TRIGGER': Array('show-create-trigger'), + 'SHOW CREATE VIEW': Array('show-create-view'), + 'SHOW DATABASES': Array('show-databases'), + 'SHOW ENGINE': Array('show-engine'), + 'SHOW ENGINES': Array('show-engines'), + 'SHOW ERRORS': Array('show-errors'), + 'SHOW EVENTS': Array('show-events'), + 'SHOW FUNCTION CODE': Array('show-function-code'), + 'SHOW FUNCTION STATUS': Array('show-function-status'), + 'SHOW GRANTS': Array('show-grants'), + 'SHOW INDEX': Array('show-index'), + 'SHOW MASTER STATUS': Array('show-master-status'), + 'SHOW OPEN TABLES': Array('show-open-tables'), + 'SHOW PLUGINS': Array('show-plugins'), + 'SHOW PRIVILEGES': Array('show-privileges'), + 'SHOW PROCEDURE CODE': Array('show-procedure-code'), + 'SHOW PROCEDURE STATUS': Array('show-procedure-status'), + 'SHOW PROCESSLIST': Array('show-processlist'), + 'SHOW PROFILE': Array('show-profile'), + 'SHOW PROFILES': Array('show-profiles'), + 'SHOW RELAYLOG EVENTS': Array('show-relaylog-events'), + 'SHOW SLAVE HOSTS': Array('show-slave-hosts'), + 'SHOW SLAVE STATUS': Array('show-slave-status'), + 'SHOW STATUS': Array('show-status'), + 'SHOW TABLE STATUS': Array('show-table-status'), + 'SHOW TABLES': Array('show-tables'), + 'SHOW TRIGGERS': Array('show-triggers'), + 'SHOW VARIABLES': Array('show-variables'), + 'SHOW WARNINGS': Array('show-warnings'), + 'LOAD DATA INFILE': Array('load-data'), + 'LOAD XML': Array('load-xml'), + 'LOCK TABLES': Array('lock-tables'), + 'UNLOCK TABLES': Array('lock-tables'), + 'ALTER DATABASE': Array('alter-database'), + 'ALTER EVENT': Array('alter-event'), + 'ALTER LOGFILE GROUP': Array('alter-logfile-group'), + 'ALTER FUNCTION': Array('alter-function'), + 'ALTER PROCEDURE': Array('alter-procedure'), + 'ALTER SERVER': Array('alter-server'), + 'ALTER TABLE': Array('alter-table'), + 'ALTER TABLESPACE': Array('alter-tablespace'), + 'ALTER VIEW': Array('alter-view'), + 'CREATE DATABASE': Array('create-database'), + 'CREATE EVENT': Array('create-event'), + 'CREATE FUNCTION': Array('create-function'), + 'CREATE INDEX': Array('create-index'), + 'CREATE LOGFILE GROUP': Array('create-logfile-group'), + 'CREATE PROCEDURE': Array('create-procedure'), + 'CREATE SERVER': Array('create-server'), + 'CREATE TABLE': Array('create-table'), + 'CREATE TABLESPACE': Array('create-tablespace'), + 'CREATE TRIGGER': Array('create-trigger'), + 'CREATE VIEW': Array('create-view'), + 'DROP DATABASE': Array('drop-database'), + 'DROP EVENT': Array('drop-event'), + 'DROP FUNCTION': Array('drop-function'), + 'DROP INDEX': Array('drop-index'), + 'DROP LOGFILE GROUP': Array('drop-logfile-group'), + 'DROP PROCEDURE': Array('drop-procedure'), + 'DROP SERVER': Array('drop-server'), + 'DROP TABLE': Array('drop-table'), + 'DROP TABLESPACE': Array('drop-tablespace'), + 'DROP TRIGGER': Array('drop-trigger'), + 'DROP VIEW': Array('drop-view'), + 'RENAME TABLE': Array('rename-table'), + 'TRUNCATE TABLE': Array('truncate-table'), + + /* Statements */ + 'SELECT': Array('select'), + 'SET': Array('set'), + 'EXPLAIN': Array('explain'), + 'DESCRIBE': Array('describe'), + 'DELETE': Array('delete'), + 'SHOW': Array('show'), + 'UPDATE': Array('update'), + 'INSERT': Array('insert'), + 'REPLACE': Array('replace'), + 'CALL': Array('call'), + 'DO': Array('do'), + 'HANDLER': Array('handler'), + 'COLLATE': Array('charset-collations'), + + /* Functions */ + 'ABS': Array('mathematical-functions', 'function_abs'), + 'ACOS': Array('mathematical-functions', 'function_acos'), + 'ADDDATE': Array('date-and-time-functions', 'function_adddate'), + 'ADDTIME': Array('date-and-time-functions', 'function_addtime'), + 'AES_DECRYPT': Array('encryption-functions', 'function_aes_decrypt'), + 'AES_ENCRYPT': Array('encryption-functions', 'function_aes_encrypt'), + 'AND': Array('logical-operators', 'operator_and'), + 'ASCII': Array('string-functions', 'function_ascii'), + 'ASIN': Array('mathematical-functions', 'function_asin'), + 'ATAN2': Array('mathematical-functions', 'function_atan2'), + 'ATAN': Array('mathematical-functions', 'function_atan'), + 'AVG': Array('group-by-functions', 'function_avg'), + 'BENCHMARK': Array('information-functions', 'function_benchmark'), + 'BIN': Array('string-functions', 'function_bin'), + 'BINARY': Array('cast-functions', 'operator_binary'), + 'BIT_AND': Array('group-by-functions', 'function_bit_and'), + 'BIT_COUNT': Array('bit-functions', 'function_bit_count'), + 'BIT_LENGTH': Array('string-functions', 'function_bit_length'), + 'BIT_OR': Array('group-by-functions', 'function_bit_or'), + 'BIT_XOR': Array('group-by-functions', 'function_bit_xor'), + 'CASE': Array('control-flow-functions', 'operator_case'), + 'CAST': Array('cast-functions', 'function_cast'), + 'CEIL': Array('mathematical-functions', 'function_ceil'), + 'CEILING': Array('mathematical-functions', 'function_ceiling'), + 'CHAR_LENGTH': Array('string-functions', 'function_char_length'), + 'CHAR': Array('string-functions', 'function_char'), + 'CHARACTER_LENGTH': Array('string-functions', 'function_character_length'), + 'CHARSET': Array('information-functions', 'function_charset'), + 'COALESCE': Array('comparison-operators', 'function_coalesce'), + 'COERCIBILITY': Array('information-functions', 'function_coercibility'), + 'COLLATION': Array('information-functions', 'function_collation'), + 'COMPRESS': Array('encryption-functions', 'function_compress'), + 'CONCAT_WS': Array('string-functions', 'function_concat_ws'), + 'CONCAT': Array('string-functions', 'function_concat'), + 'CONNECTION_ID': Array('information-functions', 'function_connection_id'), + 'CONV': Array('mathematical-functions', 'function_conv'), + 'CONVERT_TZ': Array('date-and-time-functions', 'function_convert_tz'), + 'Convert': Array('cast-functions', 'function_convert'), + 'COS': Array('mathematical-functions', 'function_cos'), + 'COT': Array('mathematical-functions', 'function_cot'), + 'COUNT': Array('group-by-functions', 'function_count'), + 'CRC32': Array('mathematical-functions', 'function_crc32'), + 'CURDATE': Array('date-and-time-functions', 'function_curdate'), + 'CURRENT_DATE': Array('date-and-time-functions', 'function_current_date'), + 'CURRENT_TIME': Array('date-and-time-functions', 'function_current_time'), + 'CURRENT_TIMESTAMP': Array('date-and-time-functions', 'function_current_timestamp'), + 'CURRENT_USER': Array('information-functions', 'function_current_user'), + 'CURTIME': Array('date-and-time-functions', 'function_curtime'), + 'DATABASE': Array('information-functions', 'function_database'), + 'DATE_ADD': Array('date-and-time-functions', 'function_date_add'), + 'DATE_FORMAT': Array('date-and-time-functions', 'function_date_format'), + 'DATE_SUB': Array('date-and-time-functions', 'function_date_sub'), + 'DATE': Array('date-and-time-functions', 'function_date'), + 'DATEDIFF': Array('date-and-time-functions', 'function_datediff'), + 'DAY': Array('date-and-time-functions', 'function_day'), + 'DAYNAME': Array('date-and-time-functions', 'function_dayname'), + 'DAYOFMONTH': Array('date-and-time-functions', 'function_dayofmonth'), + 'DAYOFWEEK': Array('date-and-time-functions', 'function_dayofweek'), + 'DAYOFYEAR': Array('date-and-time-functions', 'function_dayofyear'), + 'DECLARE': Array('declare', 'declare'), + 'DECODE': Array('encryption-functions', 'function_decode'), + 'DEFAULT': Array('miscellaneous-functions', 'function_default'), + 'DEGREES': Array('mathematical-functions', 'function_degrees'), + 'DES_DECRYPT': Array('encryption-functions', 'function_des_decrypt'), + 'DES_ENCRYPT': Array('encryption-functions', 'function_des_encrypt'), + 'DIV': Array('arithmetic-functions', 'operator_div'), + 'ELT': Array('string-functions', 'function_elt'), + 'ENCODE': Array('encryption-functions', 'function_encode'), + 'ENCRYPT': Array('encryption-functions', 'function_encrypt'), + 'EXP': Array('mathematical-functions', 'function_exp'), + 'EXPORT_SET': Array('string-functions', 'function_export_set'), + 'EXTRACT': Array('date-and-time-functions', 'function_extract'), + 'ExtractValue': Array('xml-functions', 'function_extractvalue'), + 'FIELD': Array('string-functions', 'function_field'), + 'FIND_IN_SET': Array('string-functions', 'function_find_in_set'), + 'FLOOR': Array('mathematical-functions', 'function_floor'), + 'FORMAT': Array('string-functions', 'function_format'), + 'FOUND_ROWS': Array('information-functions', 'function_found_rows'), + 'FROM_DAYS': Array('date-and-time-functions', 'function_from_days'), + 'FROM_UNIXTIME': Array('date-and-time-functions', 'function_from_unixtime'), + 'GET_FORMAT': Array('date-and-time-functions', 'function_get_format'), + 'GET_LOCK': Array('miscellaneous-functions', 'function_get_lock'), + 'GREATEST': Array('comparison-operators', 'function_greatest'), + 'GROUP_CONCAT': Array('group-by-functions', 'function_group_concat'), + 'HEX': Array('string-functions', 'function_hex'), + 'HOUR': Array('date-and-time-functions', 'function_hour'), + 'IF': Array('control-flow-functions', 'function_if'), + 'IFNULL': Array('control-flow-functions', 'function_ifnull'), + 'IN': Array('comparison-operators', 'function_in'), + 'INET_ATON': Array('miscellaneous-functions', 'function_inet_aton'), + 'INET_NTOA': Array('miscellaneous-functions', 'function_inet_ntoa'), + 'INSTR': Array('string-functions', 'function_instr'), + 'INTERVAL': Array('comparison-operators', 'function_interval'), + 'IS_FREE_LOCK': Array('miscellaneous-functions', 'function_is_free_lock'), + 'IS_USED_LOCK': Array('miscellaneous-functions', 'function_is_used_lock'), + 'IS': Array('comparison-operators', 'operator_is'), + 'ISNULL': Array('comparison-operators', 'function_isnull'), + 'LAST_DAY': Array('date-and-time-functions', 'function_last_day'), + 'LAST_INSERT_ID': Array('information-functions', 'function_last_insert_id'), + 'LCASE': Array('string-functions', 'function_lcase'), + 'LEAST': Array('comparison-operators', 'function_least'), + 'LEFT': Array('string-functions', 'function_left'), + 'LENGTH': Array('string-functions', 'function_length'), + 'LIKE': Array('string-comparison-functions', 'operator_like'), + 'LN': Array('mathematical-functions', 'function_ln'), + 'LOAD_FILE': Array('string-functions', 'function_load_file'), + 'LOCALTIME': Array('date-and-time-functions', 'function_localtime'), + 'LOCALTIMESTAMP': Array('date-and-time-functions', 'function_localtimestamp'), + 'LOCATE': Array('string-functions', 'function_locate'), + 'LOG10': Array('mathematical-functions', 'function_log10'), + 'LOG2': Array('mathematical-functions', 'function_log2'), + 'LOG': Array('mathematical-functions', 'function_log'), + 'LOWER': Array('string-functions', 'function_lower'), + 'LPAD': Array('string-functions', 'function_lpad'), + 'LTRIM': Array('string-functions', 'function_ltrim'), + 'MAKE_SET': Array('string-functions', 'function_make_set'), + 'MAKEDATE': Array('date-and-time-functions', 'function_makedate'), + 'MAKETIME': Array('date-and-time-functions', 'function_maketime'), + 'MASTER_POS_WAIT': Array('miscellaneous-functions', 'function_master_pos_wait'), + 'MATCH': Array('fulltext-search', 'function_match'), + 'MAX': Array('group-by-functions', 'function_max'), + 'MD5': Array('encryption-functions', 'function_md5'), + 'MICROSECOND': Array('date-and-time-functions', 'function_microsecond'), + 'MID': Array('string-functions', 'function_mid'), + 'MIN': Array('group-by-functions', 'function_min'), + 'MINUTE': Array('date-and-time-functions', 'function_minute'), + 'MOD': Array('mathematical-functions', 'function_mod'), + 'MONTH': Array('date-and-time-functions', 'function_month'), + 'MONTHNAME': Array('date-and-time-functions', 'function_monthname'), + 'NAME_CONST': Array('miscellaneous-functions', 'function_name_const'), + 'NOT': Array('logical-operators', 'operator_not'), + 'NOW': Array('date-and-time-functions', 'function_now'), + 'NULLIF': Array('control-flow-functions', 'function_nullif'), + 'OCT': Array('mathematical-functions', 'function_oct'), + 'OCTET_LENGTH': Array('string-functions', 'function_octet_length'), + 'OLD_PASSWORD': Array('encryption-functions', 'function_old_password'), + 'OR': Array('logical-operators', 'operator_or'), + 'ORD': Array('string-functions', 'function_ord'), + 'PASSWORD': Array('encryption-functions', 'function_password'), + 'PERIOD_ADD': Array('date-and-time-functions', 'function_period_add'), + 'PERIOD_DIFF': Array('date-and-time-functions', 'function_period_diff'), + 'PI': Array('mathematical-functions', 'function_pi'), + 'POSITION': Array('string-functions', 'function_position'), + 'POW': Array('mathematical-functions', 'function_pow'), + 'POWER': Array('mathematical-functions', 'function_power'), + 'QUARTER': Array('date-and-time-functions', 'function_quarter'), + 'QUOTE': Array('string-functions', 'function_quote'), + 'RADIANS': Array('mathematical-functions', 'function_radians'), + 'RAND': Array('mathematical-functions', 'function_rand'), + 'REGEXP': Array('regexp', 'operator_regexp'), + 'RELEASE_LOCK': Array('miscellaneous-functions', 'function_release_lock'), + 'REPEAT': Array('string-functions', 'function_repeat'), + 'REVERSE': Array('string-functions', 'function_reverse'), + 'RIGHT': Array('string-functions', 'function_right'), + 'RLIKE': Array('regexp', 'operator_rlike'), + 'ROUND': Array('mathematical-functions', 'function_round'), + 'ROW_COUNT': Array('information-functions', 'function_row_count'), + 'RPAD': Array('string-functions', 'function_rpad'), + 'RTRIM': Array('string-functions', 'function_rtrim'), + 'SCHEMA': Array('information-functions', 'function_schema'), + 'SEC_TO_TIME': Array('date-and-time-functions', 'function_sec_to_time'), + 'SECOND': Array('date-and-time-functions', 'function_second'), + 'SESSION_USER': Array('information-functions', 'function_session_user'), + 'SHA': Array('encryption-functions', 'function_sha1'), + 'SHA1': Array('encryption-functions', 'function_sha1'), + 'SIGN': Array('mathematical-functions', 'function_sign'), + 'SIN': Array('mathematical-functions', 'function_sin'), + 'SLEEP': Array('miscellaneous-functions', 'function_sleep'), + 'SOUNDEX': Array('string-functions', 'function_soundex'), + 'SPACE': Array('string-functions', 'function_space'), + 'SQRT': Array('mathematical-functions', 'function_sqrt'), + 'STD': Array('group-by-functions', 'function_std'), + 'STDDEV_POP': Array('group-by-functions', 'function_stddev_pop'), + 'STDDEV_SAMP': Array('group-by-functions', 'function_stddev_samp'), + 'STDDEV': Array('group-by-functions', 'function_stddev'), + 'STR_TO_DATE': Array('date-and-time-functions', 'function_str_to_date'), + 'STRCMP': Array('string-comparison-functions', 'function_strcmp'), + 'SUBDATE': Array('date-and-time-functions', 'function_subdate'), + 'SUBSTR': Array('string-functions', 'function_substr'), + 'SUBSTRING_INDEX': Array('string-functions', 'function_substring_index'), + 'SUBSTRING': Array('string-functions', 'function_substring'), + 'SUBTIME': Array('date-and-time-functions', 'function_subtime'), + 'SUM': Array('group-by-functions', 'function_sum'), + 'SYSDATE': Array('date-and-time-functions', 'function_sysdate'), + 'SYSTEM_USER': Array('information-functions', 'function_system_user'), + 'TAN': Array('mathematical-functions', 'function_tan'), + 'TIME_FORMAT': Array('date-and-time-functions', 'function_time_format'), + 'TIME_TO_SEC': Array('date-and-time-functions', 'function_time_to_sec'), + 'TIME': Array('date-and-time-functions', 'function_time'), + 'TIMEDIFF': Array('date-and-time-functions', 'function_timediff'), + 'TIMESTAMP': Array('date-and-time-functions', 'function_timestamp'), + 'TIMESTAMPADD': Array('date-and-time-functions', 'function_timestampadd'), + 'TIMESTAMPDIFF': Array('date-and-time-functions', 'function_timestampdiff'), + 'TO_DAYS': Array('date-and-time-functions', 'function_to_days'), + 'TRIM': Array('string-functions', 'function_trim'), + 'TRUNCATE': Array('mathematical-functions', 'function_truncate'), + 'UCASE': Array('string-functions', 'function_ucase'), + 'UNCOMPRESS': Array('encryption-functions', 'function_uncompress'), + 'UNCOMPRESSED_LENGTH': Array('encryption-functions', 'function_uncompressed_length'), + 'UNHEX': Array('string-functions', 'function_unhex'), + 'UNIX_TIMESTAMP': Array('date-and-time-functions', 'function_unix_timestamp'), + 'UpdateXML': Array('xml-functions', 'function_updatexml'), + 'UPPER': Array('string-functions', 'function_upper'), + 'USER': Array('information-functions', 'function_user'), + 'UTC_DATE': Array('date-and-time-functions', 'function_utc_date'), + 'UTC_TIME': Array('date-and-time-functions', 'function_utc_time'), + 'UTC_TIMESTAMP': Array('date-and-time-functions', 'function_utc_timestamp'), + 'UUID_SHORT': Array('miscellaneous-functions', 'function_uuid_short'), + 'UUID': Array('miscellaneous-functions', 'function_uuid'), + 'VALUES': Array('miscellaneous-functions', 'function_values'), + 'VAR_POP': Array('group-by-functions', 'function_var_pop'), + 'VAR_SAMP': Array('group-by-functions', 'function_var_samp'), + 'VARIANCE': Array('group-by-functions', 'function_variance'), + 'VERSION': Array('information-functions', 'function_version'), + 'WEEK': Array('date-and-time-functions', 'function_week'), + 'WEEKDAY': Array('date-and-time-functions', 'function_weekday'), + 'WEEKOFYEAR': Array('date-and-time-functions', 'function_weekofyear'), + 'XOR': Array('logical-operators', 'operator_xor'), + 'YEAR': Array('date-and-time-functions', 'function_year'), + 'YEARWEEK': Array('date-and-time-functions', 'function_yearweek'), + 'SOUNDS_LIKE': Array('string-functions', 'operator_sounds-like'), + 'IS_NOT_NULL': Array('comparison-operators', 'operator_is-not-null'), + 'IS_NOT': Array('comparison-operators', 'operator_is-not'), + 'IS_NULL': Array('comparison-operators', 'operator_is-null'), + 'NOT_LIKE': Array('string-comparison-functions', 'operator_not-like'), + 'NOT_REGEXP': Array('regexp', 'operator_not-regexp'), + 'COUNT_DISTINCT': Array('group-by-functions', 'function_count-distinct'), + 'NOT_IN': Array('comparison-operators', 'function_not-in') +}; + +export const mysql_doc_builtin = { + 'TINYINT': Array('numeric-types'), + 'SMALLINT': Array('numeric-types'), + 'MEDIUMINT': Array('numeric-types'), + 'INT': Array('numeric-types'), + 'BIGINT': Array('numeric-types'), + 'DECIMAL': Array('numeric-types'), + 'FLOAT': Array('numeric-types'), + 'DOUBLE': Array('numeric-types'), + 'REAL': Array('numeric-types'), + 'BIT': Array('numeric-types'), + 'BOOLEAN': Array('numeric-types'), + 'SERIAL': Array('numeric-types'), + 'DATE': Array('date-and-time-types'), + 'DATETIME': Array('date-and-time-types'), + 'TIMESTAMP': Array('date-and-time-types'), + 'TIME': Array('date-and-time-types'), + 'YEAR': Array('date-and-time-types'), + 'CHAR': Array('string-types'), + 'VARCHAR': Array('string-types'), + 'TINYTEXT': Array('string-types'), + 'TEXT': Array('string-types'), + 'MEDIUMTEXT': Array('string-types'), + 'LONGTEXT': Array('string-types'), + 'BINARY': Array('string-types'), + 'VARBINARY': Array('string-types'), + 'TINYBLOB': Array('string-types'), + 'MEDIUMBLOB': Array('string-types'), + 'BLOB': Array('string-types'), + 'LONGBLOB': Array('string-types'), + 'ENUM': Array('string-types'), + 'SET': Array('string-types') +}; diff --git a/js/src/consts/files.js b/js/src/consts/files.js new file mode 100644 index 0000000000..930c3b3735 --- /dev/null +++ b/js/src/consts/files.js @@ -0,0 +1,14 @@ +/** + * List of JS files need to be loaded at the time of initial page load. + * Eg. For server_privileges.php, server_privileges.js is required + */ + +/** + * @type {Object} files + */ +const files = { + server_privileges: ['server_privileges'], + server_databases: ['server_databases'] +}; + +export default files; diff --git a/js/src/functions/get_image.js b/js/src/functions/get_image.js new file mode 100644 index 0000000000..167197e84a --- /dev/null +++ b/js/src/functions/get_image.js @@ -0,0 +1,78 @@ +import { escapeHtml } from '../utils/Sanitise'; +/** + * Returns an HTML IMG tag for a particular image from a theme, + * which may be an actual file or an icon from a sprite + * + * @param string image The name of the file to get + * @param string alternate Used to set 'alt' and 'title' attributes of the image + * @param object attributes An associative array of other attributes + * + * @return Object The requested image, this object has two methods: + * .toString() - Returns the IMG tag for the requested image + * .attr(name) - Returns a particular attribute of the IMG + * tag given it's name + * .attr(name, value) - Sets a particular attribute of the IMG + * tag to the given value + */ +export function PMA_getImage (image, alternate, attributes) { + // custom image object, it will eventually be returned by this functions + var retval = { + data: { + // this is private + alt: '', + title: '', + src: 'themes/dot.gif', + }, + attr: function (name, value) { + if (value === undefined) { + if (this.data[name] === undefined) { + return ''; + } else { + return this.data[name]; + } + } else { + this.data[name] = value; + } + }, + toString: function () { + var retval = '<' + 'img'; + for (var i in this.data) { + retval += ' ' + i + '="' + this.data[i] + '"'; + } + retval += ' /' + '>'; + return retval; + } + }; + // initialise missing parameters + if (attributes === undefined) { + attributes = {}; + } + if (alternate === undefined) { + alternate = ''; + } + // set alt + if (attributes.alt !== undefined) { + retval.attr('alt', escapeHtml(attributes.alt)); + } else { + retval.attr('alt', escapeHtml(alternate)); + } + // set title + if (attributes.title !== undefined) { + retval.attr('title', escapeHtml(attributes.title)); + } else { + retval.attr('title', escapeHtml(alternate)); + } + // set css classes + retval.attr('class', 'icon ic_' + image); + // set all other attrubutes + for (var i in attributes) { + if (i === 'src') { + // do not allow to override the 'src' attribute + continue; + } + + retval.attr(i, attributes[i]); + } + + return retval; +} diff --git a/js/src/functions/navigation.js b/js/src/functions/navigation.js new file mode 100644 index 0000000000..d8e8a4d6b9 --- /dev/null +++ b/js/src/functions/navigation.js @@ -0,0 +1,159 @@ +/** + * Expand the navigation and highlight the current database or table/view + * + * @returns void + */ +import { PMA_commonParams } from '../variables/common_params'; +export function PMA_showCurrentNavigation () { + var db = PMA_commonParams.get('db'); + var table = PMA_commonParams.get('table'); + + $('#pma_navigation_tree').find('li.selected').removeClass('selected'); + if (db) { + var $dbItem = findLoadedItem($('#pma_navigation_tree').find('> div'), db, 'database', !table); + if ($('#navi_db_select').length && $('option:selected', $('#navi_db_select')).length) { + if (!PMA_selectCurrentDb()) { + return; + } + // If loaded database in navigation is not same as current one + if ($('#pma_navigation_tree_content').find('span.loaded_db:first').text() !== $('#navi_db_select').val()) { + loadChildNodes(false, $('option:selected', $('#navi_db_select')), function (data) { + handleTableOrDb(table, $('#pma_navigation_tree_content')); + var $children = $('#pma_navigation_tree_content').children('div.list_container'); + $children.promise().done(navTreeStateUpdate); + }); + } else { + handleTableOrDb(table, $('#pma_navigation_tree_content')); + } + } else if ($dbItem) { + var $expander = $dbItem.children('div:first').children('a.expander'); + // if not loaded or loaded but collapsed + if (!$expander.hasClass('loaded') || $expander.find('img').is('.ic_b_plus')) { + expandTreeNode($expander, function () { + handleTableOrDb(table, $dbItem); + }); + } else { + handleTableOrDb(table, $dbItem); + } + } + } else if ($('#navi_db_select').length && $('#navi_db_select').val()) { + $('#navi_db_select').val('').hide().trigger('change'); + } + PMA_showFullName($('#pma_navigation_tree')); + + function handleTableOrDb (table, $dbItem) { + if (table) { + loadAndHighlightTableOrView($dbItem, table); + } else { + var $container = $dbItem.children('div.list_container'); + var $tableContainer = $container.children('ul').children('li.tableContainer'); + if ($tableContainer.length > 0) { + var $expander = $tableContainer.children('div:first').children('a.expander'); + $tableContainer.addClass('selected'); + expandTreeNode($expander, function () { + scrollToView($dbItem, true); + }); + } else { + scrollToView($dbItem, true); + } + } + } + + function findLoadedItem ($container, name, clazz, doSelect) { + var ret = false; + $container.children('ul').children('li').each(function () { + var $li = $(this); + // this is a navigation group, recurse + if ($li.is('.navGroup')) { + var $container = $li.children('div.list_container'); + var $childRet = findLoadedItem($container, name, clazz, doSelect); + if ($childRet) { + ret = $childRet; + return false; + } + } else { + // this is a real navigation item + // name and class matches + if ((clazz && $li.is('.' + clazz) || !clazz) && $li.children('a').text() === name) { + if (doSelect) { + $li.addClass('selected'); + } + // taverse up and expand and parent navigation groups + $li.parents('.navGroup').each(function () { + var $cont = $(this).children('div.list_container'); + if (!$cont.is(':visible')) { + $(this).children('div:first').children('a.expander').click(); + } + }); + ret = $li; + return false; + } + } + }); + return ret; + } + + function loadAndHighlightTableOrView ($dbItem, itemName) { + var $container = $dbItem.children('div.list_container'); + var $expander; + var $whichItem = isItemInContainer($container, itemName, 'li.table, li.view'); + // If item already there in some container + if ($whichItem) { + // get the relevant container while may also be a subcontainer + var $relatedContainer = $whichItem.closest('li.subContainer').length ? $whichItem.closest('li.subContainer') : $dbItem; + $whichItem = findLoadedItem($relatedContainer.children('div.list_container'), itemName, null, true); + // Show directly + showTableOrView($whichItem, $relatedContainer.children('div:first').children('a.expander')); + // else if item not there, try loading once + } else { + var $sub_containers = $dbItem.find('.subContainer'); + // If there are subContainers i.e. tableContainer or viewContainer + if ($sub_containers.length > 0) { + var $containers = []; + $sub_containers.each(function (index) { + $containers[index] = $(this); + $expander = $containers[index].children('div:first').children('a.expander'); + if (!$expander.hasClass('loaded')) { + loadAndShowTableOrView($expander, $containers[index], itemName); + } + }); + // else if no subContainers + } else { + $expander = $dbItem.children('div:first').children('a.expander'); + if (!$expander.hasClass('loaded')) { + loadAndShowTableOrView($expander, $dbItem, itemName); + } + } + } + } + + function loadAndShowTableOrView ($expander, $relatedContainer, itemName) { + loadChildNodes(true, $expander, function (data) { + var $whichItem = findLoadedItem($relatedContainer.children('div.list_container'), itemName, null, true); + if ($whichItem) { + showTableOrView($whichItem, $expander); + } + }); + } + + function showTableOrView ($whichItem, $expander) { + expandTreeNode($expander, function (data) { + if ($whichItem) { + scrollToView($whichItem, false); + } + }); + } + + function isItemInContainer ($container, name, clazz) { + var $whichItem = null; + $items = $container.find(clazz); + var found = false; + $items.each(function () { + if ($(this).children('a').text() === name) { + $whichItem = $(this); + return false; + } + }); + return $whichItem; + } +} diff --git a/js/src/index.js b/js/src/index.js new file mode 100644 index 0000000000..452a988f19 --- /dev/null +++ b/js/src/index.js @@ -0,0 +1,86 @@ +import { AJAX } from './ajax'; +import './variables/import_variables'; +import { jQuery as $ } from './utils/JqueryExtended'; +import files from './consts/files'; + +/** + * Page load event handler + */ +$(function () { + var menuContent = $('
') + .append($('#serverinfo').clone()) + .append($('#topmenucontainer').clone()) + .html(); + if (history && history.pushState) { + // set initial state reload + var initState = ('state' in window.history && window.history.state !== null); + var initURL = $('#selflink').find('> a').attr('href') || location.href; + var state = { + url : initURL, + menu : menuContent + }; + history.replaceState(state, null); + + $(window).on('popstate', function (event) { + var initPop = (! initState && location.href === initURL); + initState = true; + // check if popstate fired on first page itself + if (initPop) { + return; + } + var state = event.originalEvent.state; + if (state && state.menu) { + AJAX.$msgbox = PMA_ajaxShowMessage(); + var params = 'ajax_request=true' + PMA_commonParams.get('arg_separator') + 'ajax_page_request=true'; + var url = state.url || location.href; + $.get(url, params, AJAX.responseHandler); + // TODO: Check if sometimes menu is not retrieved from server, + // Not sure but it seems menu was missing only for printview which + // been removed lately, so if it's right some dead menu checks/fallbacks + // may need to be removed from this file and Header.php + // AJAX.handleMenu.replace(event.originalEvent.state.menu); + } + }); + } else { + // Fallback to microhistory mechanism + AJAX.scriptHandler + .load([{ 'name' : 'microhistory.js', 'fire' : 1 }], function () { + // The cache primer is set by the footer class + if (PMA_MicroHistory.primer.url) { + PMA_MicroHistory.menus.add( + PMA_MicroHistory.primer.menuHash, + menuContent + ); + } + $(function () { + // Queue up this event twice to make sure that we get a copy + // of the page after all other onload events have been fired + if (PMA_MicroHistory.primer.url) { + PMA_MicroHistory.add( + PMA_MicroHistory.primer.url, + PMA_MicroHistory.primer.scripts, + PMA_MicroHistory.primer.menuHash + ); + } + }); + }); + } +}); + +/** + * This block of code is for importing javascript files needed + * for the first time loading of the page. + */ +let firstPage = window.location.pathname.replace('/', '').replace('.php', ''); +let indexStart = window.location.search.indexOf('target') + 7; +let indexEnd = window.location.search.indexOf('.php'); +let indexPage = window.location.search.slice(indexStart, indexEnd); +if (typeof files[firstPage] !== 'undefined' && firstPage.toLocaleLowerCase() !== 'index') { + for (let i in files[firstPage]) { + AJAX.scriptHandler.add(files[firstPage][i], 1); + } +} else if (typeof files[indexPage] !== 'undefined' && firstPage.toLocaleLowerCase() === 'index') { + for (let i in files[indexPage]) { + AJAX.scriptHandler.add(files[indexPage][i], 1); + } +} diff --git a/js/src/server_databases.js b/js/src/server_databases.js new file mode 100644 index 0000000000..af5c2c350f --- /dev/null +++ b/js/src/server_databases.js @@ -0,0 +1,151 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * @fileoverview functions used on the server databases list page + * @name Server Databases + * + * @requires jQuery + * @requires jQueryUI + * @required js/functions.js + */ +import { PMA_sprintf } from './utils/sprintf'; +import './variables/import_variables'; +import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; +import { escapeHtml } from './utils/Sanitise'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { jQuery as $ } from './utils/JqueryExtended'; +import { AJAX } from './ajax'; +import { PMA_commonParams } from './variables/common_params'; + +/** + * Unbind all event handlers before tearing down a page + */ +export function teardown1 () { + $(document).off('submit', '#dbStatsForm'); + $(document).off('submit', '#create_database_form.ajax'); +} + +export function onload1 () { + /** + * Attach Event Handler for 'Drop Databases' + */ + $(document).on('submit', '#dbStatsForm', function (event) { + // debugger; + event.preventDefault(); + + var $form = $(this); + + /** + * @var selected_dbs Array containing the names of the checked databases + */ + var selected_dbs = []; + // loop over all checked checkboxes, except the .checkall_box checkbox + $form.find('input:checkbox:checked:not(.checkall_box)').each(function () { + $(this).closest('tr').addClass('removeMe'); + selected_dbs[selected_dbs.length] = 'DROP DATABASE `' + escapeHtml($(this).val()) + '`;'; + }); + if (! selected_dbs.length) { + PMA_ajaxShowMessage( + $('
').text( + PMA_messages.strNoDatabasesSelected + ), + 2000 + ); + return; + } + /** + * @var question String containing the question to be asked for confirmation + */ + var question = PMA_messages.strDropDatabaseStrongWarning + ' ' + + PMA_sprintf(PMA_messages.strDoYouReally, selected_dbs.join('
')); + + var argsep = PMA_commonParams.get('arg_separator'); + $(this).PMA_confirm( + question, + $form.prop('action') + '?' + $(this).serialize() + + argsep + 'drop_selected_dbs=1' + argsep + 'is_js_confirmed=1' + argsep + 'ajax_request=true', + function (url) { + PMA_ajaxShowMessage(PMA_messages.strProcessingRequest, false); + + var params = getJSConfirmCommonParam(this); + + $.post(url, params, function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + + var $rowsToRemove = $form.find('tr.removeMe'); + var $databasesCount = $('#filter-rows-count'); + var newCount = parseInt($databasesCount.text(), 10) - $rowsToRemove.length; + $databasesCount.text(newCount); + + $rowsToRemove.remove(); + $form.find('tbody').PMA_sort_table('.name'); + if ($form.find('tbody').find('tr').length === 0) { + // user just dropped the last db on this page + PMA_commonActions.refreshMain(); + } + PMA_reloadNavigation(); + } else { + $form.find('tr.removeMe').removeClass('removeMe'); + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + } + ); // end $.PMA_confirm() + }); // end of Drop Database action + + /** + * Attach Ajax event handlers for 'Create Database'. + */ + $(document).on('submit', '#create_database_form.ajax', function (event) { + event.preventDefault(); + + var $form = $(this); + + // TODO Remove this section when all browsers support HTML5 "required" property + var newDbNameInput = $form.find('input[name=new_db]'); + if (newDbNameInput.val() === '') { + newDbNameInput.focus(); + alert(PMA_messages.strFormEmpty); + return; + } + // end remove + + PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + PMA_prepareForAjaxRequest($form); + + $.post($form.attr('action'), $form.serialize(), function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + + var $databases_count_object = $('#filter-rows-count'); + var databases_count = parseInt($databases_count_object.text(), 10) + 1; + $databases_count_object.text(databases_count); + PMA_reloadNavigation(); + + // make ajax request to load db structure page - taken from ajax.js + var dbStruct_url = data.url_query; + dbStruct_url = dbStruct_url.replace(/amp;/ig, ''); + var params = 'ajax_request=true' + PMA_commonParams.get('arg_separator') + 'ajax_page_request=true'; + if (! (history && history.pushState)) { + params += PMA_MicroHistory.menus.getRequestParam(); + } + $.get(dbStruct_url, params, AJAX.responseHandler); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + }); // end $(document).on() + + /* Don't show filter if number of databases are very few */ + var databasesCount = $('#filter-rows-count').html(); + if (databasesCount <= 10) { + $('#tableFilter').hide(); + } + + var tableRows = $('.server_databases'); + $.each(tableRows, function (index, item) { + $(this).click(function () { + PMA_commonActions.setDb($(this).attr('data')); + }); + }); +} diff --git a/js/src/server_privileges.js b/js/src/server_privileges.js new file mode 100644 index 0000000000..8bbc69c386 --- /dev/null +++ b/js/src/server_privileges.js @@ -0,0 +1,436 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * @fileoverview functions used in server privilege pages + * @name Server Privileges + * + * @requires jQuery + * @requires jQueryUI + * @requires js/functions.js + * + */ +import { PMA_sprintf } from './utils/sprintf'; +import { checkPasswordStrength, displayPasswordGenerateButton } from './utils/password'; +// import { AJAX } from './ajax'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_ajaxShowMessage, PMA_ajaxRemoveMessage } from './utils/show_ajax_messages'; +import { PMA_commonParams } from './variables/common_params'; +import { jQuery as $ } from './utils/JqueryExtended'; + +/** + * AJAX scripts for server_privileges page. + * + * Actions ajaxified here: + * Add user + * Revoke a user + * Edit privileges + * Export privileges + * Paginate table of users + * Flush privileges + * + * @memberOf jQuery + * @name document.ready + */ + +/** + * Unbind all event handlers before tearing down a page + */ +export function teardown1 () { + $('#fieldset_add_user_login').off('change', 'input[name=\'username\']'); + $(document).off('click', '#fieldset_delete_user_footer #buttonGo.ajax'); + $(document).off('click', 'a.edit_user_group_anchor.ajax'); + $(document).off('click', 'button.mult_submit[value=export]'); + $(document).off('click', 'a.export_user_anchor.ajax'); + $(document).off('click', '#initials_table a.ajax'); + $('#checkbox_drop_users_db').off('click'); + $(document).off('click', '.checkall_box'); + $(document).off('change', '#checkbox_SSL_priv'); + $(document).off('change', 'input[name="ssl_type"]'); + $(document).off('change', '#select_authentication_plugin'); +} + +export function onload1 () { + /** + * Display a warning if there is already a user by the name entered as the username. + */ + $('#fieldset_add_user_login').on('change', 'input[name=\'username\']', function () { + var username = $(this).val(); + var $warning = $('#user_exists_warning'); + if ($('#select_pred_username').val() === 'userdefined' && username !== '') { + var href = $('form[name=\'usersForm\']').attr('action'); + var params = { + 'ajax_request' : true, + 'server' : PMA_commonParams.get('server'), + 'validate_username' : true, + 'username' : username + }; + $.get(href, params, function (data) { + if (data.user_exists) { + $warning.show(); + } else { + $warning.hide(); + } + }); + } else { + $warning.hide(); + } + }); + + /** + * Indicating password strength + */ + var meter_obj; + var meter_obj_label; + var username; + $(document).on('keyup', '#text_pma_pw', function () { + meter_obj = $('#password_strength_meter'); + meter_obj_label = $('#password_strength'); + username = $('input[name="username"]'); + username = username.val(); + checkPasswordStrength($(this).val(), meter_obj, meter_obj_label, username); + }); + + $(document).on('keyup', '#text_pma_change_pw', function () { + meter_obj = $('#change_password_strength_meter'); + meter_obj_label = $('#change_password_strength'); + checkPasswordStrength($(this).val(), meter_obj, meter_obj_label, PMA_commonParams.get('user')); + }); + + /** + * Display a notice if sha256_password is selected + */ + $(document).on('change', '#select_authentication_plugin', function () { + var selected_plugin = $(this).val(); + if (selected_plugin === 'sha256_password') { + $('#ssl_reqd_warning').show(); + } else { + $('#ssl_reqd_warning').hide(); + } + }); + + /** + * AJAX handler for 'Revoke User' + * + * @see PMA_ajaxShowMessage() + * @memberOf jQuery + * @name revoke_user_click + */ + $(document).on('click', '#fieldset_delete_user_footer #buttonGo.ajax', function (event) { + event.preventDefault(); + + var $thisButton = $(this); + var $form = $('#usersForm'); + + $thisButton.PMA_confirm(PMA_messages.strDropUserWarning, $form.attr('action'), function (url) { + var $drop_users_db_checkbox = $('#checkbox_drop_users_db'); + if ($drop_users_db_checkbox.is(':checked')) { + var is_confirmed = confirm(PMA_messages.strDropDatabaseStrongWarning + '\n' + PMA_sprintf(PMA_messages.strDoYouReally, 'DROP DATABASE')); + if (! is_confirmed) { + // Uncheck the drop users database checkbox + $drop_users_db_checkbox.prop('checked', false); + } + } + + PMA_ajaxShowMessage(PMA_messages.strRemovingSelectedUsers); + + var argsep = PMA_commonParams.get('arg_separator'); + $.post(url, $form.serialize() + argsep + 'delete=' + $thisButton.val() + argsep + 'ajax_request=true', function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + // Refresh navigation, if we droppped some databases with the name + // that is the same as the username of the deleted user + if ($('#checkbox_drop_users_db:checked').length) { + PMA_reloadNavigation(); + } + // Remove the revoked user from the users list + $form.find('input:checkbox:checked').parents('tr').slideUp('medium', function () { + var this_user_initial = $(this).find('input:checkbox').val().charAt(0).toUpperCase(); + $(this).remove(); + + // If this is the last user with this_user_initial, remove the link from #initials_table + if ($('#tableuserrights').find('input:checkbox[value^="' + this_user_initial + '"], input:checkbox[value^="' + this_user_initial.toLowerCase() + '"]').length === 0) { + $('#initials_table').find('td > a:contains(' + this_user_initial + ')').parent('td').html(this_user_initial); + } + + // Re-check the classes of each row + $form + .find('tbody').find('tr:odd') + .removeClass('even').addClass('odd') + .end() + .find('tr:even') + .removeClass('odd').addClass('even'); + + // update the checkall checkbox + $(checkboxes_sel).trigger('change'); + }); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + }); + }); // end Revoke User + + $(document).on('click', 'a.edit_user_group_anchor.ajax', function (event) { + event.preventDefault(); + $(this).parents('tr').addClass('current_row'); + var $msg = PMA_ajaxShowMessage(); + $.get( + $(this).attr('href'), + { + 'ajax_request': true, + 'edit_user_group_dialog': true + }, + function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxRemoveMessage($msg); + var buttonOptions = {}; + buttonOptions[PMA_messages.strGo] = function () { + var usrGroup = $('#changeUserGroupDialog') + .find('select[name="userGroup"]') + .val(); + var $message = PMA_ajaxShowMessage(); + var argsep = PMA_commonParams.get('arg_separator'); + $.post( + 'server_privileges.php', + $('#changeUserGroupDialog').find('form').serialize() + argsep + 'ajax_request=1', + function (data) { + PMA_ajaxRemoveMessage($message); + if (typeof data !== 'undefined' && data.success === true) { + $('#usersForm') + .find('.current_row') + .removeClass('current_row') + .find('.usrGroup') + .text(usrGroup); + } else { + PMA_ajaxShowMessage(data.error, false); + $('#usersForm') + .find('.current_row') + .removeClass('current_row'); + } + } + ); + $(this).dialog('close'); + }; + buttonOptions[PMA_messages.strClose] = function () { + $(this).dialog('close'); + }; + var $dialog = $('
') + .attr('id', 'changeUserGroupDialog') + .append(data.message) + .dialog({ + width: 500, + minWidth: 300, + modal: true, + buttons: buttonOptions, + title: $('legend', $(data.message)).text(), + close: function () { + $(this).remove(); + } + }); + $dialog.find('legend').remove(); + } else { + PMA_ajaxShowMessage(data.error, false); + $('#usersForm') + .find('.current_row') + .removeClass('current_row'); + } + } + ); + }); + + /** + * AJAX handler for 'Export Privileges' + * + * @see PMA_ajaxShowMessage() + * @memberOf jQuery + * @name export_user_click + */ + $(document).on('click', 'button.mult_submit[value=export]', function (event) { + event.preventDefault(); + // can't export if no users checked + if ($(this.form).find('input:checked').length === 0) { + PMA_ajaxShowMessage(PMA_messages.strNoAccountSelected, 2000, 'success'); + return; + } + var $msgbox = PMA_ajaxShowMessage(); + var button_options = {}; + button_options[PMA_messages.strClose] = function () { + $(this).dialog('close'); + }; + var argsep = PMA_commonParams.get('arg_separator'); + $.post( + $(this.form).prop('action'), + $(this.form).serialize() + argsep + 'submit_mult=export' + argsep + 'ajax_request=true', + function (data) { + if (typeof data !== 'undefined' && data.success === true) { + var $ajaxDialog = $('
') + .append(data.message) + .dialog({ + title: data.title, + width: 500, + buttons: button_options, + close: function () { + $(this).remove(); + } + }); + PMA_ajaxRemoveMessage($msgbox); + // Attach syntax highlighted editor to export dialog + PMA_getSQLEditor($ajaxDialog.find('textarea')); + } else { + PMA_ajaxShowMessage(data.error, false); + } + } + ); // end $.post + }); + // if exporting non-ajax, highlight anyways + PMA_getSQLEditor($('textarea.export')); + + $(document).on('click', 'a.export_user_anchor.ajax', function (event) { + event.preventDefault(); + var $msgbox = PMA_ajaxShowMessage(); + /** + * @var button_options Object containing options for jQueryUI dialog buttons + */ + var button_options = {}; + button_options[PMA_messages.strClose] = function () { + $(this).dialog('close'); + }; + $.get($(this).attr('href'), { 'ajax_request': true }, function (data) { + if (typeof data !== 'undefined' && data.success === true) { + var $ajaxDialog = $('
') + .append(data.message) + .dialog({ + title: data.title, + width: 500, + buttons: button_options, + close: function () { + $(this).remove(); + } + }); + PMA_ajaxRemoveMessage($msgbox); + // Attach syntax highlighted editor to export dialog + PMA_getSQLEditor($ajaxDialog.find('textarea')); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.get + }); // end export privileges + + /** + * AJAX handler to Paginate the Users Table + * + * @see PMA_ajaxShowMessage() + * @name paginate_users_table_click + * @memberOf jQuery + */ + $(document).on('click', '#initials_table a.ajax', function (event) { + event.preventDefault(); + var $msgbox = PMA_ajaxShowMessage(); + $.get($(this).attr('href'), { 'ajax_request' : true }, function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxRemoveMessage($msgbox); + // This form is not on screen when first entering Privileges + // if there are more than 50 users + $('div.notice').remove(); + $('#usersForm').hide('medium').remove(); + $('#fieldset_add_user').hide('medium').remove(); + $('#initials_table') + .prop('id', 'initials_table_old') + .after(data.message).show('medium') + .siblings('h2').not(':first').remove(); + // prevent double initials table + $('#initials_table_old').remove(); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.get + }); // end of the paginate users table + + $(document).on('change', 'input[name="ssl_type"]', function (e) { + var $div = $('#specified_div'); + if ($('#ssl_type_SPECIFIED').is(':checked')) { + $div.find('input').prop('disabled', false); + } else { + $div.find('input').prop('disabled', true); + } + }); + + $(document).on('change', '#checkbox_SSL_priv', function (e) { + var $div = $('#require_ssl_div'); + if ($(this).is(':checked')) { + $div.find('input').prop('disabled', false); + $('#ssl_type_SPECIFIED').trigger('change'); + } else { + $div.find('input').prop('disabled', true); + } + }); + + $('#checkbox_SSL_priv').trigger('change'); + + /* + * Create submenu for simpler interface + */ + var addOrUpdateSubmenu = function () { + var $topmenu2 = $('#topmenu2'); + var $edit_user_dialog = $('#edit_user_dialog'); + var submenu_label; + var submenu_link; + var link_number; + + // if submenu exists yet, remove it first + if ($topmenu2.length > 0) { + $topmenu2.remove(); + } + + // construct a submenu from the existing fieldsets + $topmenu2 = $('