structure.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. "use strict";
  2. /**
  3. * @fileoverview functions used on the table structure page
  4. * @name Table Structure
  5. *
  6. * @requires jQuery
  7. * @requires jQueryUI
  8. * @required js/functions.js
  9. */
  10. // eslint-disable-next-line no-unused-vars
  11. /* global primaryIndexes:writable, indexes:writable, fulltextIndexes:writable, spatialIndexes:writable */
  12. // js/functions.js
  13. /* global sprintf */
  14. // js/vendor/sprintf.js
  15. /**
  16. * AJAX scripts for /table/structure
  17. *
  18. * Actions ajaxified here:
  19. * Drop Column
  20. * Add Primary Key
  21. * Drop Primary Key/Index
  22. *
  23. */
  24. /**
  25. * Reload fields table
  26. */
  27. function reloadFieldForm() {
  28. $.post($('#fieldsForm').attr('action'), $('#fieldsForm').serialize() + CommonParams.get('arg_separator') + 'ajax_request=true', function (formData) {
  29. var $tempDiv = $('<div id=\'temp_div\'><div>').append(formData.message);
  30. $('#fieldsForm').replaceWith($tempDiv.find('#fieldsForm'));
  31. $('#addColumns').replaceWith($tempDiv.find('#addColumns'));
  32. $('#move_columns_dialog').find('ul').replaceWith($tempDiv.find('#move_columns_dialog ul'));
  33. $('#moveColumns').removeClass('move-active');
  34. });
  35. $('#page_content').show();
  36. }
  37. function checkFirst() {
  38. if ($('select[name=after_field] option:selected').data('pos') === 'first') {
  39. $('input[name=field_where]').val('first');
  40. } else {
  41. $('input[name=field_where]').val('after');
  42. }
  43. }
  44. /**
  45. * Unbind all event handlers before tearing down a page
  46. */
  47. AJAX.registerTeardown('table/structure.js', function () {
  48. $(document).off('click', 'a.drop_column_anchor.ajax');
  49. $(document).off('click', 'a.add_key.ajax');
  50. $(document).off('click', '#move_columns_anchor');
  51. $(document).off('click', '#printView');
  52. $(document).off('submit', '.append_fields_form.ajax');
  53. $('body').off('click', '#fieldsForm.ajax button');
  54. $(document).off('click', 'a[id^=partition_action].ajax');
  55. $(document).off('click', '#remove_partitioning.ajax');
  56. });
  57. AJAX.registerOnload('table/structure.js', function () {
  58. // Re-initialize variables.
  59. primaryIndexes = [];
  60. indexes = [];
  61. fulltextIndexes = [];
  62. spatialIndexes = [];
  63. /**
  64. *Ajax action for submitting the "Column Change" and "Add Column" form
  65. */
  66. $('.append_fields_form.ajax').off();
  67. $(document).on('submit', '.append_fields_form.ajax', function (event) {
  68. event.preventDefault();
  69. /**
  70. * @var the_form object referring to the export form
  71. */
  72. var $form = $(this);
  73. var fieldCnt = $form.find('input[name=orig_num_fields]').val();
  74. function submitForm() {
  75. var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  76. $.post($form.attr('action'), $form.serialize() + CommonParams.get('arg_separator') + 'do_save_data=1', function (data) {
  77. if ($('.sqlqueryresults').length !== 0) {
  78. $('.sqlqueryresults').remove();
  79. } else if ($('.error:not(.tab)').length !== 0) {
  80. $('.error:not(.tab)').remove();
  81. }
  82. if (typeof data.success !== 'undefined' && data.success === true) {
  83. $('#page_content').empty().append(data.message).show();
  84. Functions.highlightSql($('#page_content'));
  85. $('.result_query .alert-primary').remove();
  86. reloadFieldForm();
  87. $form.remove();
  88. Functions.ajaxRemoveMessage($msg);
  89. Functions.initSlider();
  90. Navigation.reload();
  91. CommonActions.refreshMain('index.php?route=/table/structure');
  92. } else {
  93. Functions.ajaxShowMessage(data.error, false);
  94. }
  95. }); // end $.post()
  96. }
  97. function checkIfConfirmRequired($form) {
  98. var i = 0;
  99. var id;
  100. var elm;
  101. var val;
  102. var nameOrig;
  103. var elmOrig;
  104. var valOrig;
  105. var checkRequired = false;
  106. for (i = 0; i < fieldCnt; i++) {
  107. id = '#field_' + i + '_5';
  108. elm = $(id);
  109. val = elm.val();
  110. nameOrig = 'input[name=field_collation_orig\\[' + i + '\\]]';
  111. elmOrig = $form.find(nameOrig);
  112. valOrig = elmOrig.val();
  113. if (val && valOrig && val !== valOrig) {
  114. checkRequired = true;
  115. break;
  116. }
  117. }
  118. return checkRequired;
  119. }
  120. /*
  121. * First validate the form; if there is a problem, avoid submitting it
  122. *
  123. * Functions.checkTableEditForm() needs a pure element and not a jQuery object,
  124. * this is why we pass $form[0] as a parameter (the jQuery object
  125. * is actually an array of DOM elements)
  126. */
  127. if (Functions.checkTableEditForm($form[0], fieldCnt)) {
  128. // OK, form passed validation step
  129. Functions.prepareForAjaxRequest($form);
  130. if (Functions.checkReservedWordColumns($form)) {
  131. // User wants to submit the form
  132. // If Collation is changed, Warn and Confirm
  133. if (checkIfConfirmRequired($form)) {
  134. var question = sprintf(Messages.strChangeColumnCollation, 'https://wiki.phpmyadmin.net/pma/Garbled_data');
  135. $form.confirm(question, $form.attr('action'), function () {
  136. submitForm();
  137. });
  138. } else {
  139. submitForm();
  140. }
  141. }
  142. }
  143. }); // end change table button "do_save_data"
  144. /**
  145. * Attach Event Handler for 'Drop Column'
  146. */
  147. $(document).on('click', 'a.drop_column_anchor.ajax', function (event) {
  148. event.preventDefault();
  149. /**
  150. * @var curr_table_name String containing the name of the current table
  151. */
  152. var currTableName = $(this).closest('form').find('input[name=table]').val();
  153. /**
  154. * @var curr_row Object reference to the currently selected row (i.e. field in the table)
  155. */
  156. var $currRow = $(this).parents('tr');
  157. /**
  158. * @var curr_column_name String containing name of the field referred to by {@link curr_row}
  159. */
  160. var currColumnName = $currRow.children('th').children('label').text().trim();
  161. currColumnName = Functions.escapeHtml(currColumnName);
  162. /**
  163. * @var $after_field_item Corresponding entry in the 'After' field.
  164. */
  165. var $afterFieldItem = $('select[name=\'after_field\'] option[value=\'' + currColumnName + '\']');
  166. /**
  167. * @var question String containing the question to be asked for confirmation
  168. */
  169. var question = Functions.sprintf(Messages.strDoYouReally, 'ALTER TABLE `' + Functions.escapeHtml(currTableName) + '` DROP `' + Functions.escapeHtml(currColumnName) + '`;');
  170. var $thisAnchor = $(this);
  171. $thisAnchor.confirm(question, $thisAnchor.attr('href'), function (url) {
  172. var $msg = Functions.ajaxShowMessage(Messages.strDroppingColumn, false);
  173. var params = Functions.getJsConfirmCommonParam(this, $thisAnchor.getPostData());
  174. params += CommonParams.get('arg_separator') + 'ajax_page_request=1';
  175. $.post(url, params, function (data) {
  176. if (typeof data !== 'undefined' && data.success === true) {
  177. Functions.ajaxRemoveMessage($msg);
  178. if ($('.result_query').length) {
  179. $('.result_query').remove();
  180. }
  181. if (data.sql_query) {
  182. $('<div class="result_query"></div>').html(data.sql_query).prependTo('#structure_content');
  183. Functions.highlightSql($('#page_content'));
  184. } // Adjust the row numbers
  185. for (var $row = $currRow.next(); $row.length > 0; $row = $row.next()) {
  186. var newVal = parseInt($row.find('td').eq(1).text(), 10) - 1;
  187. $row.find('td').eq(1).text(newVal);
  188. }
  189. $afterFieldItem.remove();
  190. $currRow.hide('medium').remove(); // Remove the dropped column from select menu for 'after field'
  191. $('select[name=after_field]').find('[value="' + currColumnName + '"]').remove(); // by default select the (new) last option to add new column
  192. // (in case last column is dropped)
  193. $('select[name=after_field] option').last().attr('selected', 'selected'); // refresh table stats
  194. if (data.tableStat) {
  195. $('#tablestatistics').html(data.tableStat);
  196. } // refresh the list of indexes (comes from /sql)
  197. $('.index_info').replaceWith(data.indexes_list);
  198. Navigation.reload();
  199. } else {
  200. Functions.ajaxShowMessage(Messages.strErrorProcessingRequest + ' : ' + data.error, false);
  201. }
  202. }); // end $.post()
  203. });
  204. }); // end of Drop Column Anchor action
  205. /**
  206. * Attach Event Handler for 'Print' link
  207. */
  208. $(document).on('click', '#printView', function (event) {
  209. event.preventDefault(); // Take to preview mode
  210. Functions.printPreview();
  211. }); // end of Print View action
  212. /**
  213. * Ajax Event handler for adding keys
  214. */
  215. $(document).on('click', 'a.add_key.ajax', function (event) {
  216. event.preventDefault();
  217. var $this = $(this);
  218. var currTableName = $this.closest('form').find('input[name=table]').val();
  219. var currColumnName = $this.parents('tr').children('th').children('label').text().trim();
  220. var addClause = '';
  221. if ($this.is('.add_primary_key_anchor')) {
  222. addClause = 'ADD PRIMARY KEY';
  223. } else if ($this.is('.add_index_anchor')) {
  224. addClause = 'ADD INDEX';
  225. } else if ($this.is('.add_unique_anchor')) {
  226. addClause = 'ADD UNIQUE';
  227. } else if ($this.is('.add_spatial_anchor')) {
  228. addClause = 'ADD SPATIAL';
  229. } else if ($this.is('.add_fulltext_anchor')) {
  230. addClause = 'ADD FULLTEXT';
  231. }
  232. var question = Functions.sprintf(Messages.strDoYouReally, 'ALTER TABLE `' + Functions.escapeHtml(currTableName) + '` ' + addClause + '(`' + Functions.escapeHtml(currColumnName) + '`);');
  233. var $thisAnchor = $(this);
  234. $thisAnchor.confirm(question, $thisAnchor.attr('href'), function (url) {
  235. Functions.ajaxShowMessage();
  236. AJAX.source = $this;
  237. var params = Functions.getJsConfirmCommonParam(this, $thisAnchor.getPostData());
  238. params += CommonParams.get('arg_separator') + 'ajax_page_request=1';
  239. $.post(url, params, AJAX.responseHandler);
  240. });
  241. }); // end Add key
  242. /**
  243. * Inline move columns
  244. **/
  245. $(document).on('click', '#move_columns_anchor', function (e) {
  246. e.preventDefault();
  247. if ($(this).hasClass('move-active')) {
  248. return;
  249. }
  250. /**
  251. * @var button_options Object that stores the options passed to jQueryUI
  252. * dialog
  253. */
  254. var buttonOptions = {};
  255. buttonOptions[Messages.strGo] = function (event) {
  256. event.preventDefault();
  257. var $msgbox = Functions.ajaxShowMessage();
  258. var $this = $(this);
  259. var $form = $this.find('form');
  260. var serialized = $form.serialize(); // check if any columns were moved at all
  261. if (serialized === $form.data('serialized-unmoved')) {
  262. Functions.ajaxRemoveMessage($msgbox);
  263. $this.dialog('close');
  264. return;
  265. }
  266. $.post($form.prop('action'), serialized + CommonParams.get('arg_separator') + 'ajax_request=true', function (data) {
  267. if (data.success === false) {
  268. Functions.ajaxRemoveMessage($msgbox);
  269. $this.clone().html(data.error).dialog({
  270. title: $(this).prop('title'),
  271. height: 230,
  272. width: 900,
  273. modal: true,
  274. buttons: buttonOptionsError
  275. }); // end dialog options
  276. } else {
  277. // sort the fields table
  278. var $fieldsTable = $('table#tablestructure tbody'); // remove all existing rows and remember them
  279. var $rows = $fieldsTable.find('tr').remove(); // loop through the correct order
  280. for (var i in data.columns) {
  281. var theColumn = data.columns[i];
  282. var $theRow = $rows.find('input:checkbox[value=\'' + theColumn + '\']').closest('tr'); // append the row for this column to the table
  283. $fieldsTable.append($theRow);
  284. }
  285. var $firstrow = $fieldsTable.find('tr').eq(0); // Adjust the row numbers and colors
  286. for (var $row = $firstrow; $row.length > 0; $row = $row.next()) {
  287. $row.find('td').eq(1).text($row.index() + 1).end().removeClass('odd even').addClass($row.index() % 2 === 0 ? 'odd' : 'even');
  288. }
  289. Functions.ajaxShowMessage(data.message);
  290. $this.dialog('close');
  291. }
  292. });
  293. };
  294. buttonOptions[Messages.strPreviewSQL] = function () {
  295. // Function for Previewing SQL
  296. var $form = $('#move_column_form');
  297. Functions.previewSql($form);
  298. };
  299. buttonOptions[Messages.strCancel] = function () {
  300. $(this).dialog('close');
  301. };
  302. var buttonOptionsError = {};
  303. buttonOptionsError[Messages.strOK] = function () {
  304. $(this).dialog('close').remove();
  305. };
  306. var columns = [];
  307. $('#tablestructure').find('tbody tr').each(function () {
  308. var colName = $(this).find('input:checkbox').eq(0).val();
  309. var hiddenInput = $('<input>').prop({
  310. name: 'move_columns[]',
  311. type: 'hidden'
  312. }).val(colName);
  313. columns[columns.length] = $('<li></li>').addClass('placeholderDrag').text(colName).append(hiddenInput);
  314. });
  315. var colList = $('#move_columns_dialog').find('ul').find('li').remove().end();
  316. for (var i in columns) {
  317. colList.append(columns[i]);
  318. }
  319. colList.sortable({
  320. axis: 'y',
  321. containment: $('#move_columns_dialog').find('div'),
  322. tolerance: 'pointer'
  323. }).disableSelection();
  324. var $form = $('#move_columns_dialog').find('form');
  325. $form.data('serialized-unmoved', $form.serialize());
  326. $('#move_columns_dialog').dialog({
  327. modal: true,
  328. buttons: buttonOptions,
  329. open: function open() {
  330. if ($('#move_columns_dialog').parents('.ui-dialog').height() > $(window).height()) {
  331. $('#move_columns_dialog').dialog('option', 'height', $(window).height());
  332. }
  333. },
  334. beforeClose: function beforeClose() {
  335. $('#move_columns_anchor').removeClass('move-active');
  336. }
  337. });
  338. });
  339. /**
  340. * Handles multi submits in table structure page such as change, browse, drop, primary etc.
  341. */
  342. $('body').on('click', '#fieldsForm.ajax button', function (e) {
  343. e.preventDefault();
  344. var $form = $(this).parents('form');
  345. var argsep = CommonParams.get('arg_separator');
  346. var submitData = $form.serialize() + argsep + 'ajax_request=true' + argsep + 'ajax_page_request=true';
  347. Functions.ajaxShowMessage();
  348. AJAX.source = $form;
  349. $.post(this.formAction, submitData, AJAX.responseHandler);
  350. });
  351. /**
  352. * Handles clicks on Action links in partition table
  353. */
  354. $(document).on('click', 'a[id^=partition_action].ajax', function (e) {
  355. e.preventDefault();
  356. var $link = $(this);
  357. function submitPartitionAction(url) {
  358. var params = 'ajax_request=true&ajax_page_request=true&' + $link.getPostData();
  359. Functions.ajaxShowMessage();
  360. AJAX.source = $link;
  361. $.post(url, params, AJAX.responseHandler);
  362. }
  363. if ($link.is('#partition_action_DROP')) {
  364. $link.confirm(Messages.strDropPartitionWarning, $link.attr('href'), function (url) {
  365. submitPartitionAction(url);
  366. });
  367. } else if ($link.is('#partition_action_TRUNCATE')) {
  368. $link.confirm(Messages.strTruncatePartitionWarning, $link.attr('href'), function (url) {
  369. submitPartitionAction(url);
  370. });
  371. } else {
  372. submitPartitionAction($link.attr('href'));
  373. }
  374. });
  375. /**
  376. * Handles remove partitioning
  377. */
  378. $(document).on('click', '#remove_partitioning.ajax', function (e) {
  379. e.preventDefault();
  380. var $link = $(this);
  381. var question = Messages.strRemovePartitioningWarning;
  382. $link.confirm(question, $link.attr('href'), function (url) {
  383. var params = Functions.getJsConfirmCommonParam({
  384. 'ajax_request': true,
  385. 'ajax_page_request': true
  386. }, $link.getPostData());
  387. Functions.ajaxShowMessage();
  388. AJAX.source = $link;
  389. $.post(url, params, AJAX.responseHandler);
  390. });
  391. });
  392. $(document).on('change', 'select[name=after_field]', function () {
  393. checkFirst();
  394. });
  395. });
  396. /** Handler for "More" dropdown in structure table rows */
  397. AJAX.registerOnload('table/structure.js', function () {
  398. var windowwidth = $(window).width();
  399. if (windowwidth > 768) {
  400. if (!$('#fieldsForm').hasClass('HideStructureActions')) {
  401. $('.table-structure-actions').width(function () {
  402. var width = 5;
  403. $(this).find('li').each(function () {
  404. width += $(this).outerWidth(true);
  405. });
  406. return width;
  407. });
  408. }
  409. }
  410. $('.jsresponsive').css('max-width', windowwidth - 35 + 'px');
  411. var tableRows = $('.central_columns');
  412. $.each(tableRows, function (index, item) {
  413. if ($(item).hasClass('add_button')) {
  414. $(item).on('click', function () {
  415. $('input:checkbox').prop('checked', false);
  416. $('#checkbox_row_' + (index + 1)).prop('checked', true);
  417. $('button[value=add_to_central_columns]').trigger('click');
  418. });
  419. } else {
  420. $(item).on('click', function () {
  421. $('input:checkbox').prop('checked', false);
  422. $('#checkbox_row_' + (index + 1)).prop('checked', true);
  423. $('button[value=remove_from_central_columns]').trigger('click');
  424. });
  425. }
  426. });
  427. });