diff --git a/db_central_columns.php b/db_central_columns.php index 84c71e3d54..8a0845ac6d 100644 --- a/db_central_columns.php +++ b/db_central_columns.php @@ -93,9 +93,7 @@ if (isset($_POST['add_column'])) { $response = Response::getInstance(); $header = $response->getHeader(); $scripts = $header->getScripts(); -$scripts->addFile('vendor/jquery/jquery.uitablefilter.js'); -$scripts->addFile('vendor/jquery/jquery.tablesorter.js'); -$scripts->addFile('db_central_columns.js'); +$scripts->addFile('db_central_columns'); $cfgCentralColumns = $centralColumns->getParams(); $pmadb = $cfgCentralColumns['db']; $pmatable = $cfgCentralColumns['table']; diff --git a/db_export.php b/db_export.php index ffba3fb7a7..45a3ddcad2 100644 --- a/db_export.php +++ b/db_export.php @@ -24,7 +24,7 @@ PageSettings::showGroup('Export'); $response = Response::getInstance(); $header = $response->getHeader(); $scripts = $header->getScripts(); -$scripts->addFile('export.js'); +$scripts->addFile('export'); $export = new Export(); diff --git a/db_import.php b/db_import.php index 567034076a..70a9312107 100644 --- a/db_import.php +++ b/db_import.php @@ -18,7 +18,7 @@ PageSettings::showGroup('Import'); $response = Response::getInstance(); $header = $response->getHeader(); $scripts = $header->getScripts(); -$scripts->addFile('import.js'); +$scripts->addFile('import'); $import = new Import(); diff --git a/db_operations.php b/db_operations.php index ea199ea8e7..d7396bac80 100644 --- a/db_operations.php +++ b/db_operations.php @@ -38,7 +38,7 @@ require_once 'libraries/check_user_privileges.inc.php'; $response = Response::getInstance(); $header = $response->getHeader(); $scripts = $header->getScripts(); -$scripts->addFile('db_operations.js'); +$scripts->addFile('db_operations'); $sql_query = ''; diff --git a/db_tracking.php b/db_tracking.php index acab99c0ac..1cb9d704e7 100644 --- a/db_tracking.php +++ b/db_tracking.php @@ -24,8 +24,7 @@ require_once 'libraries/common.inc.php'; $response = Response::getInstance(); $header = $response->getHeader(); $scripts = $header->getScripts(); -$scripts->addFile('vendor/jquery/jquery.tablesorter.js'); -$scripts->addFile('db_tracking.js'); +$scripts->addFile('db_tracking'); $tracking = new Tracking(); diff --git a/js/functions.js b/js/functions.js index 49708ff9dc..5d39f13830 100644 --- a/js/functions.js +++ b/js/functions.js @@ -2693,196 +2693,6 @@ AJAX.registerTeardown('functions.js', function () { $(document).off('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]'); }); -/** - * jQuery coding for 'Create Table'. Used on db_operations.php, - * db_structure.php and db_tracking.php (i.e., wherever - * PhpMyAdmin\Display\CreateTable is used) - * - * Attach Ajax Event handlers for Create Table - */ -AJAX.registerOnload('functions.js', function () { - /** - * Attach event handler for submission of create table form (save) - */ - $(document).on('submit', 'form.create_table_form.ajax', function (event) { - event.preventDefault(); - - /** - * @var the_form object referring to the create table form - */ - var $form = $(this); - - /* - * First validate the form; if there is a problem, avoid submitting it - * - * checkTableEditForm() needs a pure element and not a jQuery object, - * this is why we pass $form[0] as a parameter (the jQuery object - * is actually an array of DOM elements) - */ - - if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) { - PMA_prepareForAjaxRequest($form); - if (PMA_checkReservedWordColumns($form)) { - PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); - // User wants to submit the form - $.post($form.attr('action'), $form.serialize() + PMA_commonParams.get('arg_separator') + 'do_save_data=1', function (data) { - if (typeof data !== 'undefined' && data.success === true) { - $('#properties_message') - .removeClass('error') - .html(''); - PMA_ajaxShowMessage(data.message); - // Only if the create table dialog (distinct panel) exists - var $createTableDialog = $('#create_table_dialog'); - if ($createTableDialog.length > 0) { - $createTableDialog.dialog('close').remove(); - } - $('#tableslistcontainer').before(data.formatted_sql); - - /** - * @var tables_table Object referring to the element that holds the list of tables - */ - var tables_table = $('#tablesForm').find('tbody').not('#tbl_summary_row'); - // this is the first table created in this db - if (tables_table.length === 0) { - PMA_commonActions.refreshMain( - PMA_commonParams.get('opendb_url') - ); - } else { - /** - * @var curr_last_row Object referring to the last element in {@link tables_table} - */ - var curr_last_row = $(tables_table).find('tr:last'); - /** - * @var curr_last_row_index_string String containing the index of {@link curr_last_row} - */ - var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0]; - /** - * @var curr_last_row_index Index of {@link curr_last_row} - */ - var curr_last_row_index = parseFloat(curr_last_row_index_string); - /** - * @var new_last_row_index Index of the new row to be appended to {@link tables_table} - */ - var new_last_row_index = curr_last_row_index + 1; - /** - * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table} - */ - var new_last_row_id = 'checkbox_tbl_' + new_last_row_index; - - data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id); - // append to table - $(data.new_table_string) - .appendTo(tables_table); - - // Sort the table - $(tables_table).PMA_sort_table('th'); - - // Adjust summary row - PMA_adjustTotals(); - } - - // Refresh navigation as a new table has been added - PMA_reloadNavigation(); - // Redirect to table structure page on creation of new table - var argsep = PMA_commonParams.get('arg_separator'); - var params_12 = 'ajax_request=true' + argsep + 'ajax_page_request=true'; - if (! (history && history.pushState)) { - params_12 += PMA_MicroHistory.menus.getRequestParam(); - } - tblStruct_url = 'tbl_structure.php?server=' + data._params.server + - argsep + 'db=' + data._params.db + argsep + 'token=' + data._params.token + - argsep + 'goto=db_structure.php' + argsep + 'table=' + data._params.table + ''; - $.get(tblStruct_url, params_12, AJAX.responseHandler); - } else { - PMA_ajaxShowMessage( - '
' + data.error + '
', - false - ); - } - }); // end $.post() - } - } // end if (checkTableEditForm() ) - }); // end create table form (save) - - /** - * Submits the intermediate changes in the table creation form - * to refresh the UI accordingly - */ - function submitChangesInCreateTableForm (actionParam) { - /** - * @var the_form object referring to the create table form - */ - var $form = $('form.create_table_form.ajax'); - - var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); - PMA_prepareForAjaxRequest($form); - - // User wants to add more fields to the table - $.post($form.attr('action'), $form.serialize() + '&' + actionParam, function (data) { - if (typeof data !== 'undefined' && data.success) { - var $pageContent = $('#page_content'); - $pageContent.html(data.message); - PMA_highlightSQL($pageContent); - PMA_verifyColumnsProperties(); - PMA_hideShowConnection($('.create_table_form select[name=tbl_storage_engine]')); - PMA_ajaxRemoveMessage($msgbox); - } else { - PMA_ajaxShowMessage(data.error); - } - }); // end $.post() - } - - /** - * Attach event handler for create table form (add fields) - */ - $(document).on('click', 'form.create_table_form.ajax input[name=submit_num_fields]', function (event) { - event.preventDefault(); - submitChangesInCreateTableForm('submit_num_fields=1'); - }); // end create table form (add fields) - - $(document).on('keydown', 'form.create_table_form.ajax input[name=added_fields]', function (event) { - if (event.keyCode === 13) { - event.preventDefault(); - event.stopImmediatePropagation(); - $(this) - .closest('form') - .find('input[name=submit_num_fields]') - .trigger('click'); - } - }); - - /** - * Attach event handler to manage changes in number of partitions and subpartitions - */ - $(document).on('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]', function (event) { - $this = $(this); - $form = $this.parents('form'); - if ($form.is('.create_table_form.ajax')) { - submitChangesInCreateTableForm('submit_partition_change=1'); - } else { - $form.submit(); - } - }); - - $(document).on('change', 'input[value=AUTO_INCREMENT]', function () { - if (this.checked) { - var col = /\d/.exec($(this).attr('name')); - col = col[0]; - var $selectFieldKey = $('select[name="field_key[' + col + ']"]'); - if ($selectFieldKey.val() === 'none_' + col) { - $selectFieldKey.val('primary_' + col).trigger('change'); - } - } - }); - $('body') - .off('click', 'input.preview_sql') - .on('click', 'input.preview_sql', function () { - var $form = $(this).closest('form'); - PMA_previewSQL($form); - }); -}); - - /** * Validates the password field in a form * diff --git a/js/src/ajax.js b/js/src/ajax.js index 337e3ab981..06aac9425d 100644 --- a/js/src/ajax.js +++ b/js/src/ajax.js @@ -16,6 +16,10 @@ import { PMA_ensureNaviSettings, } from './functions/navigation'; import { isStorageSupported } from './functions/config'; import PMA_MicroHistory from './classes/MicroHistory'; +import { escapeHtml } from './utils/Sanitise'; +import { PMA_sprintf } from './utils/sprintf'; +import { JsFileList } from './consts/files'; + /** * This object handles ajax requests for pages. It also * handles the reloading of the main menu and scripts. @@ -596,12 +600,7 @@ export let AJAX = { * @todo This condition is to be removed once all the files are modularised */ if (checkNewCode(file)) { - var fileImports = ['server_privileges', 'server_databases', 'error_report', 'navigation', 'server_status_advisor', - 'server_status_processes', 'server_status_variables', 'server_plugins', 'server_status_sorter', 'server_status_queries', - 'server_status_monitor', 'server_variables', 'server_user_groups', 'replication', 'export', 'import', 'config', - 'page_settings', 'shortcuts_handler', 'db_search', 'sql', 'functions', 'multi_column_sort' - ]; - if ($.inArray(file, fileImports) !== -1) { + if ($.inArray(file, JsFileList) !== -1) { // Dynamic import to load the files dynamically // This is used for the purpose of code splitting import(`./${file}`) @@ -757,6 +756,15 @@ export let AJAX = { } }; +/** + * @todo Below mentioned two events and functions are added in this file as these + * events and functions are making ajax call to the server for fetching resources + * and data. + * For now there are are two jquery instances, one which is globally available in + * the window object and another which is being exported as module. + * Once all the code work on new imported jQuery instance, these events and functions + * can be copied to index.js + */ /** * Attach a generic event handler to clicks * on pages and submissions of forms @@ -764,6 +772,32 @@ export let AJAX = { $(document).on('click', 'a', AJAX.requestHandler); $(document).on('submit', 'form', AJAX.requestHandler); +$(document).ajaxError(function (event, request, settings) { + if (AJAX._debug) { + console.log('AJAX error: status=' + request.status + ', text=' + request.statusText); + } + // Don't handle aborted requests + if (request.status !== 0 || request.statusText !== 'abort') { + var details = ''; + var state = request.state(); + if (request.status !== 0) { + details += '
' + escapeHtml(PMA_sprintf(PMA_messages.strErrorCode, request.status)) + '
'; + } + details += '
' + escapeHtml(PMA_sprintf(PMA_messages.strErrorText, request.statusText + ' (' + state + ')')) + '
'; + if (state === 'rejected' || state === 'timeout') { + details += '
' + escapeHtml(PMA_messages.strErrorConnection) + '
'; + } + PMA_ajaxShowMessage( + '
' + + PMA_messages.strErrorProcessingRequest + + details + + '
', + false + ); + AJAX.active = false; + AJAX.xhr = null; + } +}); /** * @todo this is to be removed when complete code is modularised * Exporsing module to window for use with non modular code diff --git a/js/src/classes/FileHandler.js b/js/src/classes/FileHandler.js new file mode 100644 index 0000000000..467b186e78 --- /dev/null +++ b/js/src/classes/FileHandler.js @@ -0,0 +1,82 @@ +/** + * FileHandler to handle loading of files on a particular page + * and for particular user preferences + */ +import { AJAX } from '../ajax'; +import { PhpToJsFileMapping } from '../consts/files'; + +export default class FileHandler { + constructor () { + this.fileMapping = PhpToJsFileMapping; + this.indexPage = null; + } + /** + * Method to initialise loading of files on first page + * + * @return void + */ + init () { + this.getIndexPage(); + this.addUserPreferenceFiles(); + this.loadCommonFiles(); + this.loafIndexFiles(); + } + + /** + * Method to find the first loading page. Possible values + * index.php?target=fileName.php + * fileName.php + * + * @return void + */ + getIndexPage () { + 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 (firstPage.toLocaleLowerCase() !== 'index') { + this.indexPage = firstPage; + } else { + this.indexPage = indexPage; + } + } + + /** + * Method to add user preference files. + * + * @return void + */ + addUserPreferenceFiles () { + /** + * Add files required on the basis of user preference like ErrorReport, + * PMA_Console, CodeMirror and so on. + */ + return; + } + /** + * Method to load Common files required for all the pages. + * + * @return void + */ + loadCommonFiles () { + /** + * Adding common files for every page + */ + for (let i in this.fileMapping.global) { + AJAX.scriptHandler.add(this.fileMapping.global[i], 1); + } + } + + /** + * Method to load page related files. + * + * @return void + */ + loafIndexFiles () { + if (typeof this.fileMapping[this.indexPage] !== 'undefined') { + for (let i in this.fileMapping[this.indexPage]) { + AJAX.scriptHandler.add(this.fileMapping[this.indexPage][i], 1); + } + } + } +} diff --git a/js/src/consts/files.js b/js/src/consts/files.js index 7b8c556a1c..6526284dfc 100644 --- a/js/src/consts/files.js +++ b/js/src/consts/files.js @@ -6,7 +6,7 @@ /** * @type {Object} files */ -const files = { +const PhpToJsFileMapping = { global: ['error_report', 'config', 'navigation', 'page_settings', 'shortcuts_handler', 'functions'], server_privileges: ['server_privileges'], server_databases: ['server_databases'], @@ -26,7 +26,46 @@ const files = { server_sql: ['sql', 'multi_column_sort'], tbl_sql: ['sql', 'multi_column_sort'], db_sql: ['sql', 'multi_column_sort'], - sql: ['sql', 'multi_column_sort'] + sql: ['sql', 'multi_column_sort'], + db_structure: ['db_structure'], + db_operations: ['db_operations'], + db_tracking: ['db_tracking'], + db_central_columns: ['db_central_columns'], + db_export: ['export'], + db_import: ['import'] }; -export default files; +const JsFileList = [ + 'server_privileges', + 'server_databases', + 'error_report', + 'navigation', + 'server_status_advisor', + 'server_status_processes', + 'server_status_variables', + 'server_plugins', + 'server_status_sorter', + 'server_status_queries', + 'server_status_monitor', + 'server_variables', + 'server_user_groups', + 'replication', + 'export', + 'import', + 'config', + 'page_settings', + 'shortcuts_handler', + 'db_search', + 'sql', + 'functions', + 'multi_column_sort', + 'db_structure', + 'db_operations', + 'db_tracking', + 'db_central_columns' +]; + +export { + PhpToJsFileMapping, + JsFileList +}; diff --git a/js/src/db_central_columns.js b/js/src/db_central_columns.js new file mode 100644 index 0000000000..cd92967974 --- /dev/null +++ b/js/src/db_central_columns.js @@ -0,0 +1,247 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +import { $ } from './utils/JqueryExtended'; +import './plugins/jquery/jquery.tablesorter'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; +import PMA_commonParams from './variables/common_params'; +import { AJAX } from './ajax'; +/** + * @fileoverview events handling from central columns page + * @name Central columns + * + * @requires jQuery + */ + +/** + * AJAX scripts for db_central_columns.php + * + * Actions ajaxified here: + * Inline Edit and save of a result row + * Delete a row + * Multiple edit and delete option + * + */ + +export function teardownDbCentralColumns () { + $('.edit').off('click'); + $('.edit_save_form').off('click'); + $('.edit_cancel_form').off('click'); + $('.del_row').off('click'); + $(document).off('keyup', '.filter_rows'); + $('.edit_cancel_form').off('click'); + $('#table-select').off('change'); + $('#column-select').off('change'); + $('#add_col_div').find('>a').off('click'); + $('#add_new').off('submit'); + $('#multi_edit_central_columns').off('submit'); + $('select.default_type').off('change'); + $('button[name=\'delete_central_columns\']').off('click'); + $('button[name=\'edit_central_columns\']').off('click'); +} + +export function onloadDbCentralColumns () { + $('#tableslistcontainer input,#tableslistcontainer select,#tableslistcontainer .default_value,#tableslistcontainer .open_enum_editor').hide(); + $('#tableslistcontainer').find('.checkall').show(); + $('#tableslistcontainer').find('.checkall_box').show(); + if ($('#table_columns').find('tbody tr').length > 0) { + $('#table_columns').tablesorter({ + headers: { + 0: { sorter: false }, + 1: { sorter: false }, // hidden column + 4: { sorter: 'integer' } + } + }); + } + $('#tableslistcontainer').find('button[name="delete_central_columns"]').on('click', function (event) { + event.preventDefault(); + var multi_delete_columns = $('.checkall:checkbox:checked').serialize(); + if (multi_delete_columns === '') { + PMA_ajaxShowMessage(PMA_messages.strRadioUnchecked); + return false; + } + PMA_ajaxShowMessage(); + $('#del_col_name').val(multi_delete_columns); + $('#del_form').submit(); + }); + $('#tableslistcontainer').find('button[name="edit_central_columns"]').on('click', function (event) { + event.preventDefault(); + var editColumnList = $('.checkall:checkbox:checked').serialize(); + if (editColumnList === '') { + PMA_ajaxShowMessage(PMA_messages.strRadioUnchecked); + return false; + } + var argsep = PMA_commonParams.get('arg_separator'); + var editColumnData = editColumnList + '' + argsep + 'edit_central_columns_page=true' + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'db=' + PMA_commonParams.get('db'); + PMA_ajaxShowMessage(); + AJAX.source = $(this); + $.get('db_central_columns.php', editColumnData, AJAX.responseHandler); + }); + $('#multi_edit_central_columns').submit(function (event) { + event.preventDefault(); + event.stopPropagation(); + var argsep = PMA_commonParams.get('arg_separator'); + var multi_column_edit_data = $('#multi_edit_central_columns').serialize() + argsep + 'multi_edit_central_column_save=true' + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'db=' + encodeURIComponent(PMA_commonParams.get('db')); + PMA_ajaxShowMessage(); + AJAX.source = $(this); + $.post('db_central_columns.php', multi_column_edit_data, AJAX.responseHandler); + }); + $('#add_new').find('td').each(function () { + if ($(this).attr('name') !== 'undefined') { + $(this).find('input,select:first').attr('name', $(this).attr('name')); + } + }); + $('#field_0_0').attr('required','required'); + $('#add_new input[type="text"], #add_new input[type="number"], #add_new select') + .css({ + 'width' : '10em', + '-moz-box-sizing' : 'border-box' + }); + window.scrollTo(0, 0); + $(document).on('keyup', '.filter_rows', function () { + // get the column names + var cols = $('th.column_heading').map(function () { + return $.trim($(this).text()); + }).get(); + $.uiTableFilter($('#table_columns'), $(this).val(), cols, null, 'td span'); + }); + $('.edit').on('click', function () { + var rownum = $(this).parent().data('rownum'); + $('#save_' + rownum).show(); + $(this).hide(); + $('#f_' + rownum + ' td span').hide(); + $('#f_' + rownum + ' input, #f_' + rownum + ' select, #f_' + rownum + ' .open_enum_editor').show(); + var attribute_val = $('#f_' + rownum + ' td[name=col_attribute] span').html(); + $('#f_' + rownum + ' select[name=field_attribute\\[' + rownum + '\\] ] option[value="' + attribute_val + '"]').attr('selected','selected'); + if ($('#f_' + rownum + ' .default_type').val() === 'USER_DEFINED') { + $('#f_' + rownum + ' .default_type').siblings('.default_value').show(); + } else { + $('#f_' + rownum + ' .default_type').siblings('.default_value').hide(); + } + }); + $('.del_row').on('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + var $td = $(this); + var question = PMA_messages.strDeleteCentralColumnWarning; + $td.PMA_confirm(question, null, function (url) { + var rownum = $td.data('rownum'); + $('#del_col_name').val('selected_fld%5B%5D=' + $('#checkbox_row_' + rownum).val()); + $('#del_form').submit(); + }); + }); + $('.edit_cancel_form').on('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + var rownum = $(this).data('rownum'); + $('#save_' + rownum).hide(); + $('#edit_' + rownum).show(); + $('#f_' + rownum + ' td span').show(); + $('#f_' + rownum + ' input, #f_' + rownum + ' select,#f_' + rownum + ' .default_value, #f_' + rownum + ' .open_enum_editor').hide(); + $('#tableslistcontainer').find('.checkall').show(); + }); + $('.edit_save_form').on('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + var rownum = $(this).data('rownum'); + $('#f_' + rownum + ' td').each(function () { + if ($(this).attr('name') !== 'undefined') { + $(this).find(':input[type!="hidden"],select:first') + .attr('name', $(this).attr('name')); + } + }); + + if ($('#f_' + rownum + ' .default_type').val() === 'USER_DEFINED') { + $('#f_' + rownum + ' .default_type').attr('name','col_default_sel'); + } else { + $('#f_' + rownum + ' .default_value').attr('name','col_default_val'); + } + + var datastring = $('#f_' + rownum + ' :input').serialize(); + $.ajax({ + type: 'POST', + url: 'db_central_columns.php', + data: datastring + PMA_commonParams.get('arg_separator') + 'ajax_request=true', + dataType: 'json', + success: function (data) { + if (data.message !== '1') { + PMA_ajaxShowMessage( + '
' + + data.message + + '
', + false + ); + } else { + $('#f_' + rownum + ' td input[id=checkbox_row_' + rownum + ']').val($('#f_' + rownum + ' input[name=col_name]').val()).html(); + $('#f_' + rownum + ' td[name=col_name] span').text($('#f_' + rownum + ' input[name=col_name]').val()).html(); + $('#f_' + rownum + ' td[name=col_type] span').text($('#f_' + rownum + ' select[name=col_type]').val()).html(); + $('#f_' + rownum + ' td[name=col_length] span').text($('#f_' + rownum + ' input[name=col_length]').val()).html(); + $('#f_' + rownum + ' td[name=collation] span').text($('#f_' + rownum + ' select[name=collation]').val()).html(); + $('#f_' + rownum + ' td[name=col_attribute] span').text($('#f_' + rownum + ' select[name=col_attribute]').val()).html(); + $('#f_' + rownum + ' td[name=col_isNull] span').text($('#f_' + rownum + ' input[name=col_isNull]').is(':checked') ? 'Yes' : 'No').html(); + $('#f_' + rownum + ' td[name=col_extra] span').text($('#f_' + rownum + ' input[name=col_extra]').is(':checked') ? 'auto_increment' : '').html(); + $('#f_' + rownum + ' td[name=col_default] span').text($('#f_' + rownum + ' :input[name=col_default]').val()).html(); + } + $('#save_' + rownum).hide(); + $('#edit_' + rownum).show(); + $('#f_' + rownum + ' td span').show(); + $('#f_' + rownum + ' input, #f_' + rownum + ' select,#f_' + rownum + ' .default_value, #f_' + rownum + ' .open_enum_editor').hide(); + $('#tableslistcontainer').find('.checkall').show(); + }, + error: function () { + PMA_ajaxShowMessage( + '
' + + PMA_messages.strErrorProcessingRequest + + '
', + false + ); + } + }); + }); + $('#table-select').on('change', function (e) { + var selectvalue = $(this).val(); + var default_column_select = $('#column-select').find('option:first'); + var href = 'db_central_columns.php'; + var params = { + 'ajax_request' : true, + 'server' : PMA_commonParams.get('server'), + 'db' : PMA_commonParams.get('db'), + 'selectedTable' : selectvalue, + 'populateColumns' : true + }; + $('#column-select').html(''); + if (selectvalue !== '') { + $.post(href, params, function (data) { + $('#column-select').empty().append(default_column_select); + $('#column-select').append(data.message); + }); + } + }); + $('#add_column').submit(function (e) { + var selectvalue = $('#column-select').val(); + if (selectvalue === '') { + e.preventDefault(); + e.stopPropagation(); + } + }); + $('#add_col_div').find('>a').on('click', function (event) { + $('#add_new').slideToggle('slow'); + var $addColDivLinkSpan = $('#add_col_div').find('>a span'); + if ($addColDivLinkSpan.html() === '+') { + $addColDivLinkSpan.html('-'); + } else { + $addColDivLinkSpan.html('+'); + } + }); + $('#add_new').submit(function (event) { + $('#add_new').toggle(); + }); + $('#tableslistcontainer').find('select.default_type').on('change', function () { + if ($(this).val() === 'USER_DEFINED') { + $(this).siblings('.default_value').attr('name','col_default'); + $(this).attr('name','col_default_sel'); + } else { + $(this).attr('name','col_default'); + $(this).siblings('.default_value').attr('name','col_default_val'); + } + }); +} diff --git a/js/src/db_operations.js b/js/src/db_operations.js new file mode 100644 index 0000000000..be2ec287a0 --- /dev/null +++ b/js/src/db_operations.js @@ -0,0 +1,176 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +import { $ } from './utils/JqueryExtended'; +import { PMA_commonActions } from './classes/CommonActions'; +import PMA_commonParams from './variables/common_params'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; +import { escapeHtml } from './utils/Sanitise'; +import { PMA_prepareForAjaxRequest } from './functions/AjaxRequest'; +import { PMA_sprintf } from './utils/sprintf'; +import { getJSConfirmCommonParam } from './functions/Common'; +/** + * @fileoverview function used in server privilege pages + * @name Database Operations + * + * @requires jQuery + * @requires jQueryUI + * @requires js/functions.js + * + */ + +/** + * Ajax event handlers here for db_operations.php + * + * Actions Ajaxified here: + * Rename Database + * Copy Database + * Change Charset + * Drop Database + */ + +/** + * Unbind all event handlers before tearing down a page + */ +export function teardownDbOperations () { + $(document).off('submit', '#rename_db_form.ajax'); + $(document).off('submit', '#copy_db_form.ajax'); + $(document).off('submit', '#change_db_charset_form.ajax'); + $(document).off('click', '#drop_db_anchor.ajax'); +} + +export function onloadDbOperations () { + /** + * Ajax event handlers for 'Rename Database' + */ + $(document).on('submit', '#rename_db_form.ajax', function (event) { + event.preventDefault(); + + var old_db_name = PMA_commonParams.get('db'); + var new_db_name = $('#new_db_name').val(); + + if (new_db_name === old_db_name) { + PMA_ajaxShowMessage(PMA_messages.strDatabaseRenameToSameName, false, 'error'); + return false; + } + + var $form = $(this); + + var question = escapeHtml('CREATE DATABASE ' + new_db_name + ' / DROP DATABASE ' + old_db_name); + + PMA_prepareForAjaxRequest($form); + + $form.PMA_confirm(question, $form.attr('action'), function (url) { + PMA_ajaxShowMessage(PMA_messages.strRenamingDatabases, false); + $.post(url, $('#rename_db_form').serialize() + PMA_commonParams.get('arg_separator') + 'is_js_confirmed=1', function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + PMA_commonParams.set('db', data.newname); + + PMA_reloadNavigation(function () { + $('#pma_navigation_tree') + .find('a:not(\'.expander\')') + .each(function (index) { + var $thisAnchor = $(this); + if ($thisAnchor.text() === data.newname) { + // simulate a click on the new db name + // in navigation + $thisAnchor.trigger('click'); + } + }); + }); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + }); + }); // end Rename Database + + /** + * Ajax Event Handler for 'Copy Database' + */ + $(document).on('submit', '#copy_db_form.ajax', function (event) { + event.preventDefault(); + PMA_ajaxShowMessage(PMA_messages.strCopyingDatabase, false); + var $form = $(this); + PMA_prepareForAjaxRequest($form); + $.post($form.attr('action'), $form.serialize(), function (data) { + // use messages that stay on screen + $('div.success, div.error').fadeOut(); + if (typeof data !== 'undefined' && data.success === true) { + if ($('#checkbox_switch').is(':checked')) { + PMA_commonParams.set('db', data.newname); + PMA_commonActions.refreshMain(false, function () { + PMA_ajaxShowMessage(data.message); + }); + } else { + PMA_commonParams.set('db', data.db); + PMA_ajaxShowMessage(data.message); + } + PMA_reloadNavigation(); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + }); // end copy database + + /** + * Change tables columns visible only if change tables is checked + */ + $('#span_change_all_tables_columns_collations').hide(); + $('#checkbox_change_all_tables_collations').on('click', function () { + $('#span_change_all_tables_columns_collations').toggle(); + }); + + /** + * Ajax Event handler for 'Change Charset' of the database + */ + $(document).on('submit', '#change_db_charset_form.ajax', function (event) { + event.preventDefault(); + var $form = $(this); + PMA_prepareForAjaxRequest($form); + PMA_ajaxShowMessage(PMA_messages.strChangingCharset); + $.post($form.attr('action'), $form.serialize() + PMA_commonParams.get('arg_separator') + 'submitcollation=1', function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); // end $.post() + }); // end change charset + + /** + * Ajax event handlers for Drop Database + */ + $(document).on('click', '#drop_db_anchor.ajax', function (event) { + event.preventDefault(); + var $link = $(this); + /** + * @var question String containing the question to be asked for confirmation + */ + var question = PMA_messages.strDropDatabaseStrongWarning + ' '; + question += PMA_sprintf( + PMA_messages.strDoYouReally, + 'DROP DATABASE `' + escapeHtml(PMA_commonParams.get('db') + '`') + ); + var params = getJSConfirmCommonParam(this, $link.getPostData()); + + $(this).PMA_confirm(question, $(this).attr('href'), function (url) { + PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + $.post(url, params, function (data) { + if (typeof data !== 'undefined' && data.success) { + // Database deleted successfully, refresh both the frames + PMA_reloadNavigation(); + PMA_commonParams.set('db', ''); + PMA_commonActions.refreshMain( + 'server_databases.php', + function () { + PMA_ajaxShowMessage(data.message); + } + ); + } else { + PMA_ajaxShowMessage(data.error, false); + } + }); + }); + }); +} diff --git a/js/src/db_search.js b/js/src/db_search.js index 174b4f9f39..113f9b8958 100644 --- a/js/src/db_search.js +++ b/js/src/db_search.js @@ -14,7 +14,7 @@ * Actions ajaxified here: * Retrieve result of SQL query */ - +import { $ } from './utils/JqueryExtended'; import { PMA_Messages as messages } from './variables/export_variables'; import { PMA_ajaxShowMessage, PMA_sprintf, PMA_ajaxRemoveMessage } from './utils/show_ajax_messages'; import PMA_commonParams from './variables/common_params'; diff --git a/js/src/db_structure.js b/js/src/db_structure.js new file mode 100644 index 0000000000..a5bceaf31a --- /dev/null +++ b/js/src/db_structure.js @@ -0,0 +1,285 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +import { $ } from './utils/JqueryExtended'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_sprintf } from './utils/sprintf'; +import { escapeHtml } from './utils/Sanitise'; +import { PMA_ajaxShowMessage, PMA_ajaxRemoveMessage, PMA_tooltip } from './utils/show_ajax_messages'; +import { getForeignKeyCheckboxLoader, loadForeignKeyCheckbox, getJSConfirmCommonParam } from './functions/Sql/ForeignKey'; +import { PMA_adjustTotals, PMA_fetchRealRowCount } from './functions/Database/Structure'; +import { printPreview } from './functions/Print'; +import { PMA_reloadNavigation } from './functions/navigation'; +/** + * @fileoverview functions used on the database structure page + * @name Database Structure + * + * @requires jQuery + * @requires jQueryUI + * @required js/functions.js + */ + +/** + * AJAX scripts for db_structure.php + * + * Actions ajaxified here: + * Drop Database + * Truncate Table + * Drop Table + * + */ + +/** + * Unbind all event handlers before tearing down a page + */ +export function teardownDbStructure () { + $(document).off('click', 'a.truncate_table_anchor.ajax'); + $(document).off('click', 'a.drop_table_anchor.ajax'); + $(document).off('click', '#real_end_input'); + $(document).off('click', 'a.favorite_table_anchor.ajax'); + $(document).off('click', '#printView'); + $('a.real_row_count').off('click'); + $('a.row_count_sum').off('click'); + $('select[name=submit_mult]').off('change'); +} + +export function onloadDbStructure () { +/** + * function to open the confirmation dialog for making table consistent with central list + * + * @param string msg message text to be displayed to user + * @param function success function to be called on success + * + */ + var jqConfirm = function (msg, success) { + var dialogObj = $('
' + msg + '
'); + $('body').append(dialogObj); + var buttonOptions = {}; + buttonOptions[PMA_messages.strContinue] = function () { + success(); + $(this).dialog('close'); + }; + buttonOptions[PMA_messages.strCancel] = function () { + $(this).dialog('close'); + $('#tablesForm')[0].reset(); + }; + $(dialogObj).dialog({ + resizable: false, + modal: true, + title: PMA_messages.confirmTitle, + buttons: buttonOptions + }); + }; + + /** + * Event handler on select of "Make consistent with central list" + */ + $('select[name=submit_mult]').on('change', function (event) { + if ($(this).val() === 'make_consistent_with_central_list') { + event.preventDefault(); + event.stopPropagation(); + jqConfirm( + PMA_messages.makeConsistentMessage, function () { + $('#tablesForm').submit(); + } + ); + return false; + } else if ($(this).val() === 'copy_tbl' || $(this).val() === 'add_prefix_tbl' || $(this).val() === 'replace_prefix_tbl' || $(this).val() === 'copy_tbl_change_prefix') { + event.preventDefault(); + event.stopPropagation(); + if ($('input[name="selected_tbl[]"]:checked').length === 0) { + return false; + } + var formData = $('#tablesForm').serialize(); + var modalTitle = ''; + if ($(this).val() === 'copy_tbl') { + modalTitle = PMA_messages.strCopyTablesTo; + } else if ($(this).val() === 'add_prefix_tbl') { + modalTitle = PMA_messages.strAddPrefix; + } else if ($(this).val() === 'replace_prefix_tbl') { + modalTitle = PMA_messages.strReplacePrefix; + } else if ($(this).val() === 'copy_tbl_change_prefix') { + modalTitle = PMA_messages.strCopyPrefix; + } + $.ajax({ + type: 'POST', + url: 'db_structure.php', + dataType: 'html', + data: formData + + }).done(function (data) { + var dialogObj = $('
' + data + '
'); + $('body').append(dialogObj); + var buttonOptions = {}; + buttonOptions[PMA_messages.strContinue] = function () { + $('#ajax_form').submit(); + $(this).dialog('close'); + }; + buttonOptions[PMA_messages.strCancel] = function () { + $(this).dialog('close'); + $('#tablesForm')[0].reset(); + }; + $(dialogObj).dialog({ + minWidth: 500, + resizable: false, + modal: true, + title: modalTitle, + buttons: buttonOptions + }); + }); + } else { + $('#tablesForm').submit(); + } + }); + + /** + * Ajax Event handler for 'Truncate Table' + */ + $(document).on('click', 'a.truncate_table_anchor.ajax', function (event) { + event.preventDefault(); + + /** + * @var $this_anchor Object referring to the anchor clicked + */ + var $this_anchor = $(this); + + // extract current table name and build the question string + /** + * @var curr_table_name String containing the name of the table to be truncated + */ + var curr_table_name = $this_anchor.parents('tr').children('th').children('a').text(); + /** + * @var question String containing the question to be asked for confirmation + */ + var question = PMA_messages.strTruncateTableStrongWarning + ' ' + + PMA_sprintf(PMA_messages.strDoYouReally, 'TRUNCATE `' + escapeHtml(curr_table_name) + '`') + + getForeignKeyCheckboxLoader(); + + $this_anchor.PMA_confirm(question, $this_anchor.attr('href'), function (url) { + PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + + var params = getJSConfirmCommonParam(this, $this_anchor.getPostData()); + + $.post(url, params, function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + // Adjust table statistics + var $tr = $this_anchor.closest('tr'); + $tr.find('.tbl_rows').text('0'); + $tr.find('.tbl_size, .tbl_overhead').text('-'); + // Fetch inner span of this anchor + // and replace the icon with its disabled version + var span = $this_anchor.html().replace(/b_empty/, 'bd_empty'); + // To disable further attempts to truncate the table, + // replace the a element with its inner span (modified) + $this_anchor + .replaceWith(span) + .removeClass('truncate_table_anchor'); + PMA_adjustTotals(); + } else { + PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest + ' : ' + data.error, false); + } + }); // end $.post() + }, loadForeignKeyCheckbox); // end $.PMA_confirm() + }); // end of Truncate Table Ajax action + + /** + * Ajax Event handler for 'Drop Table' or 'Drop View' + */ + $(document).on('click', 'a.drop_table_anchor.ajax', function (event) { + event.preventDefault(); + + var $this_anchor = $(this); + + // extract current table name and build the question string + /** + * @var $curr_row Object containing reference to the current row + */ + var $curr_row = $this_anchor.parents('tr'); + /** + * @var curr_table_name String containing the name of the table to be truncated + */ + var curr_table_name = $curr_row.children('th').children('a').text(); + /** + * @var is_view Boolean telling if we have a view + */ + var is_view = $curr_row.hasClass('is_view') || $this_anchor.hasClass('view'); + /** + * @var question String containing the question to be asked for confirmation + */ + var question; + if (! is_view) { + question = PMA_messages.strDropTableStrongWarning + ' ' + + PMA_sprintf(PMA_messages.strDoYouReally, 'DROP TABLE `' + escapeHtml(curr_table_name) + '`'); + } else { + question = + PMA_sprintf(PMA_messages.strDoYouReally, 'DROP VIEW `' + escapeHtml(curr_table_name) + '`'); + } + question += getForeignKeyCheckboxLoader(); + + $this_anchor.PMA_confirm(question, $this_anchor.attr('href'), function (url) { + var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + + var params = getJSConfirmCommonParam(this, $this_anchor.getPostData()); + + $.post(url, params, function (data) { + if (typeof data !== 'undefined' && data.success === true) { + PMA_ajaxShowMessage(data.message); + $curr_row.hide('medium').remove(); + PMA_adjustTotals(); + PMA_reloadNavigation(); + PMA_ajaxRemoveMessage($msg); + } else { + PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest + ' : ' + data.error, false); + } + }); // end $.post() + }, loadForeignKeyCheckbox); // end $.PMA_confirm() + }); // end of Drop Table Ajax action + + /** + * Attach Event Handler for 'Print' link + */ + $(document).on('click', '#printView', function (event) { + event.preventDefault(); + + // Take to preview mode + printPreview(); + }); // end of Print View action + + // Calculate Real End for InnoDB + /** + * Ajax Event handler for calculating the real end for a InnoDB table + * + */ + $(document).on('click', '#real_end_input', function (event) { + event.preventDefault(); + + /** + * @var question String containing the question to be asked for confirmation + */ + var question = PMA_messages.strOperationTakesLongTime; + + $(this).PMA_confirm(question, '', function () { + return true; + }); + return false; + }); // end Calculate Real End for InnoDB + + // Add tooltip to favorite icons. + $('.favorite_table_anchor').each(function () { + PMA_tooltip( + $(this), + 'a', + $(this).attr('title') + ); + }); + + // Get real row count via Ajax. + $('a.real_row_count').on('click', function (event) { + event.preventDefault(); + PMA_fetchRealRowCount($(this)); + }); + // Get all real row count. + $('a.row_count_sum').on('click', function (event) { + event.preventDefault(); + PMA_fetchRealRowCount($(this)); + }); +} // end $() diff --git a/js/src/db_tracking.js b/js/src/db_tracking.js new file mode 100644 index 0000000000..a61dbd124f --- /dev/null +++ b/js/src/db_tracking.js @@ -0,0 +1,101 @@ +import { $ } from './utils/JqueryExtended'; +import './plugins/jquery/jquery.tablesorter'; +import PMA_commonParams from './variables/common_params'; +import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; +import { AJAX } from './ajax'; + +/** + * Unbind all event handlers before tearing down the page + */ +export function teardownDbTracking () { + $('body').off('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]'); + $('body').off('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]'); + $('body').off('click', 'a.delete_tracking_anchor.ajax'); +} + +/** + * Bind event handlers + */ +export function onloadDbTracking () { + var $versions = $('#versions'); + $versions.find('tr:first th').append($('
')); + $versions.tablesorter({ + sortList: [[1, 0]], + headers: { + 0: { sorter: false }, + 2: { sorter: 'integer' }, + 5: { sorter: false }, + 6: { sorter: false }, + 7: { sorter: false } + } + }); + + var $noVersions = $('#noversions'); + $noVersions.find('tr:first th').append($('
')); + $noVersions.tablesorter({ + sortList: [[1, 0]], + headers: { + 0: { sorter: false }, + 2: { sorter: false } + } + }); + + var $body = $('body'); + + /** + * Handles multi submit for tracked tables + */ + $body.on('click', '#trackedForm.ajax button[name="submit_mult"], #trackedForm.ajax input[name="submit_mult"]', function (e) { + e.preventDefault(); + var $button = $(this); + var $form = $button.parent('form'); + var argsep = PMA_commonParams.get('arg_separator'); + var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val(); + + if ($button.val() === 'delete_tracking') { + var question = PMA_messages.strDeleteTrackingDataMultiple; + $button.PMA_confirm(question, $form.attr('action'), function (url) { + PMA_ajaxShowMessage(PMA_messages.strDeletingTrackingData); + AJAX.source = $form; + $.post(url, submitData, AJAX.responseHandler); + }); + } else { + PMA_ajaxShowMessage(); + AJAX.source = $form; + $.post($form.attr('action'), submitData, AJAX.responseHandler); + } + }); + + /** + * Handles multi submit for untracked tables + */ + $body.on('click', '#untrackedForm.ajax button[name="submit_mult"], #untrackedForm.ajax input[name="submit_mult"]', function (e) { + e.preventDefault(); + var $button = $(this); + var $form = $button.parent('form'); + var argsep = PMA_commonParams.get('arg_separator'); + var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true' + argsep + 'submit_mult=' + $button.val(); + PMA_ajaxShowMessage(); + AJAX.source = $form; + $.post($form.attr('action'), submitData, AJAX.responseHandler); + }); + + /** + * Ajax Event handler for 'Delete tracking' + */ + $body.on('click', 'a.delete_tracking_anchor.ajax', function (e) { + e.preventDefault(); + var $anchor = $(this); + var question = PMA_messages.strDeleteTrackingData; + $anchor.PMA_confirm(question, $anchor.attr('href'), function (url) { + PMA_ajaxShowMessage(PMA_messages.strDeletingTrackingData); + AJAX.source = $anchor; + var params = { + 'ajax_page_request': true, + 'ajax_request': true + }; + $.post(url, params, AJAX.responseHandler); + }); + }); +} diff --git a/js/src/export.js b/js/src/export.js index f14e24185b..758d8a187c 100644 --- a/js/src/export.js +++ b/js/src/export.js @@ -136,7 +136,7 @@ function onloadExportTemplate () { // Handle submit of form to export sql $('form[name=\'dump\']').on('submit', function (e) { var timeout = $('input[type=\'submit\'][id=\'buttonGo\']').data().timeout; - check_time_out(timeout); + Export.check_time_out(timeout); }); } diff --git a/js/src/functions.js b/js/src/functions.js index 798272e516..ad0e2f5418 100644 --- a/js/src/functions.js +++ b/js/src/functions.js @@ -2,6 +2,10 @@ import { AJAX } from './ajax'; import { $ } from './utils/JqueryExtended'; import CommonParams from './variables/common_params'; import { PMA_Messages as PMA_messages } from './variables/export_variables'; +import { PMA_prepareForAjaxRequest } from './functions/AjaxRequest'; +import { PMA_ajaxShowMessage, PMA_ajaxRemoveMessage } from './utils/show_ajax_messages'; +import PMA_MicroHistory from './classes/MicroHistory'; +import { PMA_commonActions } from './classes/CommonActions'; // Sql based imports import { PMA_getSQLEditor, bindCodeMirrorToInlineEditor } from './functions/Sql/SqlEditor'; @@ -9,6 +13,12 @@ import { sqlQueryOptions, updateQueryParameters, PMA_highlightSQL } from './util import { PMA_handleSimulateQueryButton, insertQuery, checkSqlQuery } from './functions/Sql/SqlQuery'; import { escapeHtml } from './utils/Sanitise'; import { getForeignKeyCheckboxLoader, loadForeignKeyCheckbox } from './functions/Sql/ForeignKey'; +import { PMA_previewSQL } from './functions/Sql/PreviewSql'; + +// Create Table based imports +import { checkTableEditForm, PMA_checkReservedWordColumns } from './functions/Table/CreateTable'; +import { PMA_adjustTotals } from './functions/Database/Structure'; +import { PMA_verifyColumnsProperties, PMA_hideShowConnection } from './functions/Table/TableColumns'; /** * Here we register a function that will remove the onsubmit event from all @@ -117,6 +127,7 @@ export function onload1 () { }); } +/* *************************************** CODEMIRROR EDITOR START *************************************** */ /** * Attach CodeMirror2 editor to SQL edit area. */ @@ -256,6 +267,7 @@ export function onloadSqlInlineEditor () { } } } +/* *************************************** CODEMIRROR EDITOR ENDS *************************************** */ /** * Unbind all event handlers before tearing down a page @@ -279,3 +291,205 @@ export function onloadCtrlEnterFormSubmit () { } }); } + +/* *************************************** CREATE TABLE STARTS *************************************** */ +/** + * Unbind all event handlers before tearing down a page + */ +export function teardownCreateTable () { + $(document).off('submit', '#create_table_form_minimal.ajax'); + $(document).off('submit', 'form.create_table_form.ajax'); + $(document).off('click', 'form.create_table_form.ajax input[name=submit_num_fields]'); + $(document).off('keyup', 'form.create_table_form.ajax input'); + $(document).off('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]'); +} + +/** + * jQuery coding for 'Create Table'. Used on db_operations.php, + * db_structure.php and db_tracking.php (i.e., wherever + * PhpMyAdmin\Display\CreateTable is used) + * + * Attach Ajax Event handlers for Create Table + */ +export function onloadCreateTable () { + /** + * Attach event handler for submission of create table form (save) + */ + $(document).on('submit', 'form.create_table_form.ajax', function (event) { + event.preventDefault(); + + /** + * @var the_form object referring to the create table form + */ + var $form = $(this); + + /* + * First validate the form; if there is a problem, avoid submitting it + * + * checkTableEditForm() needs a pure element and not a jQuery object, + * this is why we pass $form[0] as a parameter (the jQuery object + * is actually an array of DOM elements) + */ + + if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) { + PMA_prepareForAjaxRequest($form); + if (PMA_checkReservedWordColumns($form)) { + PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + // User wants to submit the form + $.post($form.attr('action'), $form.serialize() + CommonParams.get('arg_separator') + 'do_save_data=1', function (data) { + if (typeof data !== 'undefined' && data.success === true) { + $('#properties_message') + .removeClass('error') + .html(''); + PMA_ajaxShowMessage(data.message); + // Only if the create table dialog (distinct panel) exists + var $createTableDialog = $('#create_table_dialog'); + if ($createTableDialog.length > 0) { + $createTableDialog.dialog('close').remove(); + } + $('#tableslistcontainer').before(data.formatted_sql); + + /** + * @var tables_table Object referring to the element that holds the list of tables + */ + var tables_table = $('#tablesForm').find('tbody').not('#tbl_summary_row'); + // this is the first table created in this db + if (tables_table.length === 0) { + PMA_commonActions.refreshMain( + CommonParams.get('opendb_url') + ); + } else { + /** + * @var curr_last_row Object referring to the last element in {@link tables_table} + */ + var curr_last_row = $(tables_table).find('tr:last'); + /** + * @var curr_last_row_index_string String containing the index of {@link curr_last_row} + */ + var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0]; + /** + * @var curr_last_row_index Index of {@link curr_last_row} + */ + var curr_last_row_index = parseFloat(curr_last_row_index_string); + /** + * @var new_last_row_index Index of the new row to be appended to {@link tables_table} + */ + var new_last_row_index = curr_last_row_index + 1; + /** + * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table} + */ + var new_last_row_id = 'checkbox_tbl_' + new_last_row_index; + + data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id); + // append to table + $(data.new_table_string) + .appendTo(tables_table); + + // Sort the table + $(tables_table).PMA_sort_table('th'); + + // Adjust summary row + PMA_adjustTotals(); + } + + // Refresh navigation as a new table has been added + PMA_reloadNavigation(); + // Redirect to table structure page on creation of new table + var argsep = CommonParams.get('arg_separator'); + var params_12 = 'ajax_request=true' + argsep + 'ajax_page_request=true'; + if (! (history && history.pushState)) { + params_12 += PMA_MicroHistory.menus.getRequestParam(); + } + var tblStruct_url = 'tbl_structure.php?server=' + data._params.server + + argsep + 'db=' + data._params.db + argsep + 'token=' + data._params.token + + argsep + 'goto=db_structure.php' + argsep + 'table=' + data._params.table + ''; + $.get(tblStruct_url, params_12, AJAX.responseHandler); + } else { + PMA_ajaxShowMessage( + '
' + data.error + '
', + false + ); + } + }); // end $.post() + } + } // end if (checkTableEditForm() ) + }); // end create table form (save) + + /** + * Submits the intermediate changes in the table creation form + * to refresh the UI accordingly + */ + function submitChangesInCreateTableForm (actionParam) { + /** + * @var the_form object referring to the create table form + */ + var $form = $('form.create_table_form.ajax'); + + var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); + PMA_prepareForAjaxRequest($form); + + // User wants to add more fields to the table + $.post($form.attr('action'), $form.serialize() + '&' + actionParam, function (data) { + if (typeof data !== 'undefined' && data.success) { + var $pageContent = $('#page_content'); + $pageContent.html(data.message); + PMA_highlightSQL($pageContent); + PMA_verifyColumnsProperties(); + PMA_hideShowConnection($('.create_table_form select[name=tbl_storage_engine]')); + PMA_ajaxRemoveMessage($msgbox); + } else { + PMA_ajaxShowMessage(data.error); + } + }); // end $.post() + } + + /** + * Attach event handler for create table form (add fields) + */ + $(document).on('click', 'form.create_table_form.ajax input[name=submit_num_fields]', function (event) { + event.preventDefault(); + submitChangesInCreateTableForm('submit_num_fields=1'); + }); // end create table form (add fields) + + $(document).on('keydown', 'form.create_table_form.ajax input[name=added_fields]', function (event) { + if (event.keyCode === 13) { + event.preventDefault(); + event.stopImmediatePropagation(); + $(this) + .closest('form') + .find('input[name=submit_num_fields]') + .trigger('click'); + } + }); + + /** + * Attach event handler to manage changes in number of partitions and subpartitions + */ + $(document).on('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]', function (event) { + var $this = $(this); + var $form = $this.parents('form'); + if ($form.is('.create_table_form.ajax')) { + submitChangesInCreateTableForm('submit_partition_change=1'); + } else { + $form.submit(); + } + }); + + $(document).on('change', 'input[value=AUTO_INCREMENT]', function () { + if (this.checked) { + var col = /\d/.exec($(this).attr('name')); + col = col[0]; + var $selectFieldKey = $('select[name="field_key[' + col + ']"]'); + if ($selectFieldKey.val() === 'none_' + col) { + $selectFieldKey.val('primary_' + col).trigger('change'); + } + } + }); + $('body') + .off('click', 'input.preview_sql') + .on('click', 'input.preview_sql', function () { + var $form = $(this).closest('form'); + PMA_previewSQL($form); + }); +} +/* *************************************** CREATE TABLE STARTS *************************************** */ diff --git a/js/src/functions/AjaxRequest.js b/js/src/functions/AjaxRequest.js new file mode 100644 index 0000000000..1786446074 --- /dev/null +++ b/js/src/functions/AjaxRequest.js @@ -0,0 +1,11 @@ +/** + * 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 $form object the form + */ +export function PMA_prepareForAjaxRequest ($form) { + if (! $form.find('input:hidden').is('#ajax_request_hidden')) { + $form.append(''); + } +} diff --git a/js/src/functions/Database/Structure.js b/js/src/functions/Database/Structure.js new file mode 100644 index 0000000000..011d7177a0 --- /dev/null +++ b/js/src/functions/Database/Structure.js @@ -0,0 +1,153 @@ +import { PMA_Messages as PMA_messages } from '../../variables/export_variables'; +import { PMA_sprintf } from '../../utils/sprintf'; +import { PMA_ajaxShowMessage } from '../../utils/show_ajax_messages'; +/** + * Adjust number of rows and total size in the summary + * when truncating, creating, dropping or inserting into a table + */ +export function PMA_adjustTotals () { + var byteUnits = [ + PMA_messages.strB, + PMA_messages.strKiB, + PMA_messages.strMiB, + PMA_messages.strGiB, + PMA_messages.strTiB, + PMA_messages.strPiB, + PMA_messages.strEiB + ]; + /** + * @var $allTr jQuery object that references all the rows in the list of tables + */ + var $allTr = $('#tablesForm').find('table.data tbody:first tr'); + // New summary values for the table + var tableSum = $allTr.size(); + var rowsSum = 0; + var sizeSum = 0; + var overheadSum = 0; + var rowSumApproximated = false; + + $allTr.each(function () { + var $this = $(this); + var i; + var tmpVal; + // Get the number of rows for this SQL table + var strRows = $this.find('.tbl_rows').text(); + // If the value is approximated + if (strRows.indexOf('~') === 0) { + rowSumApproximated = true; + // The approximated value contains a preceding ~ (Eg 100 --> ~100) + strRows = strRows.substring(1, strRows.length); + } + strRows = strRows.replace(/[,.]/g, ''); + var intRow = parseInt(strRows, 10); + if (! isNaN(intRow)) { + rowsSum += intRow; + } + // Extract the size and overhead + var valSize = 0; + var valOverhead = 0; + var strSize = $.trim($this.find('.tbl_size span:not(.unit)').text()); + var strSizeUnit = $.trim($this.find('.tbl_size span.unit').text()); + var strOverhead = $.trim($this.find('.tbl_overhead span:not(.unit)').text()); + var strOverheadUnit = $.trim($this.find('.tbl_overhead span.unit').text()); + // Given a value and a unit, such as 100 and KiB, for the table size + // and overhead calculate their numeric values in bytes, such as 102400 + for (i = 0; i < byteUnits.length; i++) { + if (strSizeUnit === byteUnits[i]) { + tmpVal = parseFloat(strSize); + valSize = tmpVal * Math.pow(1024, i); + break; + } + } + for (i = 0; i < byteUnits.length; i++) { + if (strOverheadUnit === byteUnits[i]) { + tmpVal = parseFloat(strOverhead); + valOverhead = tmpVal * Math.pow(1024, i); + break; + } + } + sizeSum += valSize; + overheadSum += valOverhead; + }); + // Add some commas for readability: + // 1000000 becomes 1,000,000 + var strRowSum = rowsSum + ''; + var regex = /(\d+)(\d{3})/; + while (regex.test(strRowSum)) { + strRowSum = strRowSum.replace(regex, '$1' + ',' + '$2'); + } + // If approximated total value add ~ in front + if (rowSumApproximated) { + strRowSum = '~' + strRowSum; + } + // Calculate the magnitude for the size and overhead values + var size_magnitude = 0; + var overhead_magnitude = 0; + while (sizeSum >= 1024) { + sizeSum /= 1024; + size_magnitude++; + } + while (overheadSum >= 1024) { + overheadSum /= 1024; + overhead_magnitude++; + } + + sizeSum = Math.round(sizeSum * 10) / 10; + overheadSum = Math.round(overheadSum * 10) / 10; + + // Update summary with new data + var $summary = $('#tbl_summary_row'); + $summary.find('.tbl_num').text(PMA_sprintf(PMA_messages.strNTables, tableSum)); + if (rowSumApproximated) { + $summary.find('.row_count_sum').text(strRowSum); + } else { + $summary.find('.tbl_rows').text(strRowSum); + } + $summary.find('.tbl_size').text(sizeSum + ' ' + byteUnits[size_magnitude]); + $summary.find('.tbl_overhead').text(overheadSum + ' ' + byteUnits[overhead_magnitude]); +} + +/** + * Gets the real row count for a table or DB. + * @param object $target Target for appending the real count value. + */ +export function PMA_fetchRealRowCount ($target) { + var $throbber = $('#pma_navigation').find('.throbber') + .first() + .clone() + .css({ visibility: 'visible', display: 'inline-block' }) + .on('click', false); + $target.html($throbber); + $.ajax({ + type: 'GET', + url: $target.attr('href'), + cache: false, + dataType: 'json', + success: function (response) { + if (response.success) { + // If to update all row counts for a DB. + if (response.real_row_count_all) { + $.each(JSON.parse(response.real_row_count_all), + function (index, table) { + // Update each table row count. + $('table.data td[data-table*="' + table.table + '"]') + .text(table.row_count); + } + ); + } + // If to update a particular table's row count. + if (response.real_row_count) { + // Append the parent cell with real row count. + $target.parent().text(response.real_row_count); + } + // Adjust the 'Sum' displayed at the bottom. + PMA_adjustTotals(); + } else { + PMA_ajaxShowMessage(PMA_messages.strErrorRealRowCount); + } + }, + error: function () { + PMA_ajaxShowMessage(PMA_messages.strErrorRealRowCount); + } + }); +} diff --git a/js/src/functions/Sql/ForeignKey.js b/js/src/functions/Sql/ForeignKey.js index 946155511f..ea40ef815e 100644 --- a/js/src/functions/Sql/ForeignKey.js +++ b/js/src/functions/Sql/ForeignKey.js @@ -32,7 +32,7 @@ export function loadForeignKeyCheckbox () { }); } -function getJSConfirmCommonParam (elem, params) { +export function getJSConfirmCommonParam (elem, params) { var $elem = $(elem); var sep = PMA_commonParams.get('arg_separator'); if (params) { diff --git a/js/src/functions/Sql/PreviewSql.js b/js/src/functions/Sql/PreviewSql.js new file mode 100644 index 0000000000..dac3acf560 --- /dev/null +++ b/js/src/functions/Sql/PreviewSql.js @@ -0,0 +1,54 @@ +import { PMA_Messages as PMA_messages } from '../../variables/export_variables'; +import PMA_commonParams from '../../variables/common_params'; +import { PMA_ajaxShowMessage, PMA_ajaxRemoveMessage } from '../../utils/show_ajax_messages'; +/** + * Requests SQL for previewing before executing. + * + * @param jQuery Object $form Form containing query data + * + * @return void + */ +export function PMA_previewSQL ($form) { + var form_url = $form.attr('action'); + var sep = PMA_commonParams.get('arg_separator'); + var form_data = $form.serialize() + + sep + 'do_save_data=1' + + sep + 'preview_sql=1' + + sep + 'ajax_request=1'; + var $msgbox = PMA_ajaxShowMessage(); + $.ajax({ + type: 'POST', + url: form_url, + data: form_data, + success: function (response) { + PMA_ajaxRemoveMessage($msgbox); + if (response.success) { + var $dialog_content = $('
') + .append(response.sql_data); + var button_options = {}; + button_options[PMA_messages.strClose] = function () { + $(this).dialog('close'); + }; + var $response_dialog = $dialog_content.dialog({ + minWidth: 550, + maxHeight: 400, + modal: true, + buttons: button_options, + title: PMA_messages.strPreviewSQL, + close: function () { + $(this).remove(); + }, + open: function () { + // Pretty SQL printing. + PMA_highlightSQL($(this)); + } + }); + } else { + PMA_ajaxShowMessage(response.message); + } + }, + error: function () { + PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest); + } + }); +} diff --git a/js/src/functions/Table/CreateTable.js b/js/src/functions/Table/CreateTable.js new file mode 100644 index 0000000000..f1e3ddbb2f --- /dev/null +++ b/js/src/functions/Table/CreateTable.js @@ -0,0 +1,93 @@ +import PMA_commonParams from '../../variables/common_params'; +import { PMA_Messages as PMA_messages } from '../../variables/export_variables'; +/** + * Check if a form's element is empty. + * An element containing only spaces is also considered empty + * + * @param object the form + * @param string the name of the form field to put the focus on + * + * @return boolean whether the form field is empty or not + */ +function emptyCheckTheField (theForm, theFieldName) { + var theField = theForm.elements[theFieldName]; + var space_re = new RegExp('\\s+'); + return theField.value.replace(space_re, '') === ''; +} // end of the 'emptyCheckTheField()' function + +export function checkTableEditForm (theForm, fieldsCnt) { + // TODO: avoid sending a message if user just wants to add a line + // on the form but has not completed at least one field name + + var atLeastOneField = 0; + var i; + var elm; + var elm2; + var elm3; + var val; + var id; + + for (i = 0; i < fieldsCnt; i++) { + id = '#field_' + i + '_2'; + elm = $(id); + val = elm.val(); + if (val === 'VARCHAR' || val === 'CHAR' || val === 'BIT' || val === 'VARBINARY' || val === 'BINARY') { + elm2 = $('#field_' + i + '_3'); + val = parseInt(elm2.val(), 10); + elm3 = $('#field_' + i + '_1'); + if (isNaN(val) && elm3.val() !== '') { + elm2.select(); + alert(PMA_messages.strEnterValidLength); + elm2.focus(); + return false; + } + } + + if (atLeastOneField === 0) { + id = 'field_' + i + '_1'; + if (!emptyCheckTheField(theForm, id)) { + atLeastOneField = 1; + } + } + } + if (atLeastOneField === 0) { + var theField = theForm.elements.field_0_1; + alert(PMA_messages.strFormEmpty); + theField.focus(); + return false; + } + + // at least this section is under jQuery + var $input = $('input.textfield[name=\'table\']'); + if ($input.val() === '') { + alert(PMA_messages.strFormEmpty); + $input.focus(); + return false; + } + + return true; +} // enf of the 'checkTableEditForm()' function + +/** + * check for reserved keyword column name + * + * @param jQuery Object $form Form + * + * @returns true|false + */ + +export function PMA_checkReservedWordColumns ($form) { + var is_confirmed = true; + $.ajax({ + type: 'POST', + url: 'tbl_structure.php', + data: $form.serialize() + PMA_commonParams.get('arg_separator') + 'reserved_word_check=1', + success: function (data) { + if (typeof data.success !== 'undefined' && data.success === true) { + is_confirmed = confirm(data.message); + } + }, + async:false + }); + return is_confirmed; +} diff --git a/js/src/functions/Table/TableColumns.js b/js/src/functions/Table/TableColumns.js new file mode 100644 index 0000000000..ae672cee64 --- /dev/null +++ b/js/src/functions/Table/TableColumns.js @@ -0,0 +1,81 @@ +/** + * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected + */ +function PMA_showNoticeForEnum (selectElement) { + var enum_notice_id = selectElement.attr('id').split('_')[1]; + enum_notice_id += '_' + (parseInt(selectElement.attr('id').split('_')[2], 10) + 1); + var selectedType = selectElement.val(); + if (selectedType === 'ENUM' || selectedType === 'SET') { + $('p#enum_notice_' + enum_notice_id).show(); + } else { + $('p#enum_notice_' + enum_notice_id).hide(); + } +} + +/** + * Hides/shows the default value input field, depending on the default type + * Ticks the NULL checkbox if NULL is chosen as default value. + */ +function PMA_hideShowDefaultValue ($default_type) { + if ($default_type.val() === 'USER_DEFINED') { + $default_type.siblings('.default_value').show().focus(); + } else { + $default_type.siblings('.default_value').hide(); + if ($default_type.val() === 'NULL') { + var $null_checkbox = $default_type.closest('tr').find('.allow_null'); + $null_checkbox.prop('checked', true); + } + } +} + +/** + * Hides/shows the input field for column expression based on whether + * VIRTUAL/PERSISTENT is selected + * + * @param $virtuality virtuality dropdown + */ +function PMA_hideShowExpression ($virtuality) { + if ($virtuality.val() === '') { + $virtuality.siblings('.expression').hide(); + } else { + $virtuality.siblings('.expression').show(); + } +} + +/** + * Show notices for ENUM columns; add/hide the default value + * + */ +export function PMA_verifyColumnsProperties () { + $('select.column_type').each(function () { + PMA_showNoticeForEnum($(this)); + }); + $('select.default_type').each(function () { + PMA_hideShowDefaultValue($(this)); + }); + $('select.virtuality').each(function () { + PMA_hideShowExpression($(this)); + }); +} + +/** + * If the chosen storage engine is FEDERATED show connection field. Hide otherwise + * + * @param $engine_selector storage engine selector + */ +export function PMA_hideShowConnection ($engine_selector) { + var $connection = $('.create_table_form input[name=connection]'); + var index = $connection.parent('td').index() + 1; + var $labelTh = $connection.parents('tr').prev('tr').children('th:nth-child(' + index + ')'); + if ($engine_selector.val() !== 'FEDERATED') { + $connection + .prop('disabled', true) + .parent('td').hide(); + $labelTh.hide(); + } else { + $connection + .prop('disabled', false) + .parent('td').show(); + $labelTh.show(); + } +} diff --git a/js/src/index.js b/js/src/index.js index 3fc890bad1..2015c6f302 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -8,14 +8,11 @@ import { $ } from './utils/JqueryExtended'; import { AJAX } from './ajax'; import './variables/get_config'; import './variables/InlineScripting'; -import files from './consts/files'; import Console from './console'; -import { PMA_sprintf } from './utils/sprintf'; -import { PMA_Messages as PMA_messages } from './variables/export_variables'; -import { escapeHtml } from './utils/Sanitise'; import { PMA_ajaxShowMessage } from './utils/show_ajax_messages'; import PMA_commonParams from './variables/common_params'; import PMA_MicroHistory from './classes/MicroHistory'; +import FileHandler from './classes/FileHandler'; /** * Page load event handler @@ -81,57 +78,8 @@ $(function () { } }); -$(document).ajaxError(function (event, request, settings) { - if (AJAX._debug) { - console.log('AJAX error: status=' + request.status + ', text=' + request.statusText); - } - // Don't handle aborted requests - if (request.status !== 0 || request.statusText !== 'abort') { - var details = ''; - var state = request.state(); - - if (request.status !== 0) { - details += '
' + escapeHtml(PMA_sprintf(PMA_messages.strErrorCode, request.status)) + '
'; - } - details += '
' + escapeHtml(PMA_sprintf(PMA_messages.strErrorText, request.statusText + ' (' + state + ')')) + '
'; - if (state === 'rejected' || state === 'timeout') { - details += '
' + escapeHtml(PMA_messages.strErrorConnection) + '
'; - } - PMA_ajaxShowMessage( - '
' + - PMA_messages.strErrorProcessingRequest + - details + - '
', - false - ); - AJAX.active = false; - AJAX.xhr = null; - } -}); - -/** - * Adding common files for every page - */ -for (let i in files.global) { - AJAX.scriptHandler.add(files.global[i], 1); -} -/** - * 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); - } -} +let fileHandler = new FileHandler(); +fileHandler.init(); $(function () { Console.initialize(); diff --git a/libraries/classes/CentralColumns.php b/libraries/classes/CentralColumns.php index 7fcd788968..c8df7d4119 100644 --- a/libraries/classes/CentralColumns.php +++ b/libraries/classes/CentralColumns.php @@ -1078,6 +1078,33 @@ class CentralColumns return -1; } + /** + * build dropdown select html to select column in selected table, + * include only columns which are not already in central list + * + * @param string $db current database to which selected table belongs + * @param string $selected_tbl selected table + * + * @return string html to select column + */ + public function getHtmlForColumnDropdown($db, $selected_tbl) + { + $existing_cols = $this->getFromTable($db, $selected_tbl); + $this->dbi->selectDb($db); + $columns = (array) $this->dbi->getColumnNames( + $db, $selected_tbl + ); + $selectColHtml = ""; + foreach ($columns as $column) { + if (!in_array($column, $existing_cols)) { + $selectColHtml .= ''; + } + } + return $selectColHtml; + } + /** * build html for adding a new user defined column to central list * diff --git a/libraries/classes/Controllers/Database/DatabaseStructureController.php b/libraries/classes/Controllers/Database/DatabaseStructureController.php index 6c641c55d8..914850101b 100644 --- a/libraries/classes/Controllers/Database/DatabaseStructureController.php +++ b/libraries/classes/Controllers/Database/DatabaseStructureController.php @@ -144,7 +144,7 @@ class DatabaseStructureController extends DatabaseController $this->response->getHeader()->getScripts()->addFiles( [ - 'db_structure.js', + 'db_structure', 'tbl_change.js', ] );