functions/Indexes.js

  1. import Indexes from '../utils/IndexEnum';
  2. import { PMA_Messages as PMA_messages } from '../variables/export_variables';
  3. import PMA_commonParams from '../variables/common_params';
  4. import { PMA_ajaxShowMessage, PMA_ajaxRemoveMessage, PMA_showHints } from '../utils/show_ajax_messages';
  5. import { PMA_prepareForAjaxRequest } from './AjaxRequest';
  6. import { PMA_highlightSQL } from '../utils/sql';
  7. import { PMA_previewSQL } from './Sql/PreviewSql';
  8. import { PMA_verifyColumnsProperties } from './Table/TableColumns';
  9. import { PMA_sprintf } from '../utils/sprintf';
  10. import { PMA_init_slider } from '../utils/Slider';
  11. import { PMA_reloadNavigation } from './navigation';
  12. /**
  13. * Returns the array of indexes based on the index choice
  14. *
  15. * @param index_choice index choice
  16. */
  17. export function PMA_getIndexArray (index_choice) {
  18. var source_array = null;
  19. switch (index_choice.toLowerCase()) {
  20. case 'primary':
  21. source_array = Indexes.primary_indexes;
  22. break;
  23. case 'unique':
  24. source_array = Indexes.unique_indexes;
  25. break;
  26. case 'index':
  27. source_array = Indexes.indexes;
  28. break;
  29. case 'fulltext':
  30. source_array = Indexes.fulltext_indexes;
  31. break;
  32. case 'spatial':
  33. source_array = Indexes.spatial_indexes;
  34. break;
  35. default:
  36. return null;
  37. }
  38. return source_array;
  39. }
  40. /**
  41. * Hides/shows the inputs and submits appropriately depending
  42. * on whether the index type chosen is 'SPATIAL' or not.
  43. */
  44. export function checkIndexType () {
  45. /**
  46. * @var Object Dropdown to select the index choice.
  47. */
  48. var $select_index_choice = $('#select_index_choice');
  49. /**
  50. * @var Object Dropdown to select the index type.
  51. */
  52. var $select_index_type = $('#select_index_type');
  53. /**
  54. * @var Object Table header for the size column.
  55. */
  56. var $size_header = $('#index_columns').find('thead tr th:nth-child(2)');
  57. /**
  58. * @var Object Inputs to specify the columns for the index.
  59. */
  60. var $column_inputs = $('select[name="index[columns][names][]"]');
  61. /**
  62. * @var Object Inputs to specify sizes for columns of the index.
  63. */
  64. var $size_inputs = $('input[name="index[columns][sub_parts][]"]');
  65. /**
  66. * @var Object Footer containg the controllers to add more columns
  67. */
  68. var $add_more = $('#index_frm').find('.add_more');
  69. if ($select_index_choice.val() === 'SPATIAL') {
  70. // Disable and hide the size column
  71. $size_header.hide();
  72. $size_inputs.each(function () {
  73. $(this)
  74. .prop('disabled', true)
  75. .parent('td').hide();
  76. });
  77. // Disable and hide the columns of the index other than the first one
  78. var initial = true;
  79. $column_inputs.each(function () {
  80. var $column_input = $(this);
  81. if (! initial) {
  82. $column_input
  83. .prop('disabled', true)
  84. .parent('td').hide();
  85. } else {
  86. initial = false;
  87. }
  88. });
  89. // Hide controllers to add more columns
  90. $add_more.hide();
  91. } else {
  92. // Enable and show the size column
  93. $size_header.show();
  94. $size_inputs.each(function () {
  95. $(this)
  96. .prop('disabled', false)
  97. .parent('td').show();
  98. });
  99. // Enable and show the columns of the index
  100. $column_inputs.each(function () {
  101. $(this)
  102. .prop('disabled', false)
  103. .parent('td').show();
  104. });
  105. // Show controllers to add more columns
  106. $add_more.show();
  107. }
  108. if ($select_index_choice.val() === 'SPATIAL' ||
  109. $select_index_choice.val() === 'FULLTEXT') {
  110. $select_index_type.val('').prop('disabled', true);
  111. } else {
  112. $select_index_type.prop('disabled', false);
  113. }
  114. }
  115. /**
  116. * Sets current index information into form parameters.
  117. *
  118. * @param array source_array Array containing index columns
  119. * @param string index_choice Choice of index
  120. *
  121. * @return void
  122. */
  123. function PMA_setIndexFormParameters (source_array, index_choice) {
  124. if (index_choice === 'index') {
  125. $('input[name="indexes"]').val(JSON.stringify(source_array));
  126. } else {
  127. $('input[name="' + index_choice + '_indexes"]').val(JSON.stringify(source_array));
  128. }
  129. }
  130. /**
  131. * Removes a column from an Index.
  132. *
  133. * @param string col_index Index of column in form
  134. *
  135. * @return void
  136. */
  137. export function PMA_removeColumnFromIndex (col_index) {
  138. // Get previous index details.
  139. var previous_index = $('select[name="field_key[' + col_index + ']"]')
  140. .attr('data-index');
  141. if (previous_index.length) {
  142. previous_index = previous_index.split(',');
  143. var source_array = PMA_getIndexArray(previous_index[0]);
  144. if (source_array === null) {
  145. return;
  146. }
  147. // Remove column from index array.
  148. var source_length = source_array[previous_index[1]].columns.length;
  149. for (var i = 0; i < source_length; i++) {
  150. if (source_array[previous_index[1]].columns[i].col_index === col_index) {
  151. source_array[previous_index[1]].columns.splice(i, 1);
  152. }
  153. }
  154. // Remove index completely if no columns left.
  155. if (source_array[previous_index[1]].columns.length === 0) {
  156. source_array.splice(previous_index[1], 1);
  157. }
  158. // Update current index details.
  159. $('select[name="field_key[' + col_index + ']"]').attr('data-index', '');
  160. // Update form index parameters.
  161. PMA_setIndexFormParameters(source_array, previous_index[0].toLowerCase());
  162. }
  163. }
  164. /**
  165. * Adds a column to an Index.
  166. *
  167. * @param array source_array Array holding corresponding indexes
  168. * @param string array_index Index of an INDEX in array
  169. * @param string index_choice Choice of Index
  170. * @param string col_index Index of column on form
  171. *
  172. * @return void
  173. */
  174. function PMA_addColumnToIndex (source_array, array_index, index_choice, col_index) {
  175. if (col_index >= 0) {
  176. // Remove column from other indexes (if any).
  177. PMA_removeColumnFromIndex(col_index);
  178. }
  179. var index_name = $('input[name="index[Key_name]"]').val();
  180. var index_comment = $('input[name="index[Index_comment]"]').val();
  181. var key_block_size = $('input[name="index[Key_block_size]"]').val();
  182. var parser = $('input[name="index[Parser]"]').val();
  183. var index_type = $('select[name="index[Index_type]"]').val();
  184. var columns = [];
  185. $('#index_columns').find('tbody').find('tr').each(function () {
  186. // Get columns in particular order.
  187. var col_index = $(this).find('select[name="index[columns][names][]"]').val();
  188. var size = $(this).find('input[name="index[columns][sub_parts][]"]').val();
  189. columns.push({
  190. 'col_index': col_index,
  191. 'size': size
  192. });
  193. });
  194. // Update or create an index.
  195. source_array[array_index] = {
  196. 'Key_name': index_name,
  197. 'Index_comment': index_comment,
  198. 'Index_choice': index_choice.toUpperCase(),
  199. 'Key_block_size': key_block_size,
  200. 'Parser': parser,
  201. 'Index_type': index_type,
  202. 'columns': columns
  203. };
  204. // Display index name (or column list)
  205. var displayName = index_name;
  206. if (displayName === '') {
  207. var columnNames = [];
  208. $.each(columns, function () {
  209. columnNames.push($('input[name="field_name[' + this.col_index + ']"]').val());
  210. });
  211. displayName = '[' + columnNames.join(', ') + ']';
  212. }
  213. $.each(columns, function () {
  214. var id = 'index_name_' + this.col_index + '_8';
  215. var $name = $('#' + id);
  216. if ($name.length === 0) {
  217. $name = $('<a id="' + id + '" href="#" class="ajax show_index_dialog"></a>');
  218. $name.insertAfter($('select[name="field_key[' + this.col_index + ']"]'));
  219. }
  220. var $text = $('<small>').text(displayName);
  221. $name.html($text);
  222. });
  223. if (col_index >= 0) {
  224. // Update index details on form.
  225. $('select[name="field_key[' + col_index + ']"]')
  226. .attr('data-index', index_choice + ',' + array_index);
  227. }
  228. PMA_setIndexFormParameters(source_array, index_choice.toLowerCase());
  229. }
  230. /**
  231. * Get choices list for a column to create a composite index with.
  232. *
  233. * @param string index_choice Choice of index
  234. * @param array source_array Array hodling columns for particular index
  235. *
  236. * @return jQuery Object
  237. */
  238. function PMA_getCompositeIndexList (source_array, col_index) {
  239. // Remove any previous list.
  240. if ($('#composite_index_list').length) {
  241. $('#composite_index_list').remove();
  242. }
  243. // Html list.
  244. var $composite_index_list = $(
  245. '<ul id="composite_index_list">' +
  246. '<div>' + PMA_messages.strCompositeWith + '</div>' +
  247. '</ul>'
  248. );
  249. // Add each column to list available for composite index.
  250. var source_length = source_array.length;
  251. var already_present = false;
  252. for (var i = 0; i < source_length; i++) {
  253. var sub_array_len = source_array[i].columns.length;
  254. var column_names = [];
  255. for (var j = 0; j < sub_array_len; j++) {
  256. column_names.push(
  257. $('input[name="field_name[' + source_array[i].columns[j].col_index + ']"]').val()
  258. );
  259. if (col_index === source_array[i].columns[j].col_index) {
  260. already_present = true;
  261. }
  262. }
  263. $composite_index_list.append(
  264. '<li>' +
  265. '<input type="radio" name="composite_with" ' +
  266. (already_present ? 'checked="checked"' : '') +
  267. ' id="composite_index_' + i + '" value="' + i + '">' +
  268. '<label for="composite_index_' + i + '">' + column_names.join(', ') +
  269. '</lablel>' +
  270. '</li>'
  271. );
  272. }
  273. return $composite_index_list;
  274. }
  275. /**
  276. * Ensures indexes names are valid according to their type and, for a primary
  277. * key, lock index name to 'PRIMARY'
  278. * @param string form_id Variable which parses the form name as
  279. * the input
  280. * @return boolean false if there is no index form, true else
  281. */
  282. export function checkIndexName (form_id) {
  283. if ($('#' + form_id).length === 0) {
  284. return false;
  285. }
  286. // Gets the elements pointers
  287. var $the_idx_name = $('#input_index_name');
  288. var $the_idx_choice = $('#select_index_choice');
  289. // Index is a primary key
  290. if ($the_idx_choice.find('option:selected').val() === 'PRIMARY') {
  291. $the_idx_name.val('PRIMARY');
  292. $the_idx_name.prop('disabled', true);
  293. } else {
  294. if ($the_idx_name.val() === 'PRIMARY') {
  295. $the_idx_name.val('');
  296. }
  297. $the_idx_name.prop('disabled', false);
  298. }
  299. return true;
  300. } // end of the 'checkIndexName()' function
  301. /**
  302. * Shows 'Add Index' dialog.
  303. *
  304. * @param array source_array Array holding particluar index
  305. * @param string array_index Index of an INDEX in array
  306. * @param array target_columns Columns for an INDEX
  307. * @param string col_index Index of column on form
  308. * @param object index Index detail object
  309. *
  310. * @return void
  311. */
  312. export function PMA_showAddIndexDialog (source_array, array_index, target_columns, col_index, index) {
  313. // Prepare post-data.
  314. var $table = $('input[name="table"]');
  315. var table = $table.length > 0 ? $table.val() : '';
  316. var post_data = {
  317. server: PMA_commonParams.get('server'),
  318. db: $('input[name="db"]').val(),
  319. table: table,
  320. ajax_request: 1,
  321. create_edit_table: 1,
  322. index: index
  323. };
  324. var columns = {};
  325. for (var i = 0; i < target_columns.length; i++) {
  326. var column_name = $('input[name="field_name[' + target_columns[i] + ']"]').val();
  327. var column_type = $('select[name="field_type[' + target_columns[i] + ']"]').val().toLowerCase();
  328. columns[column_name] = [column_type, target_columns[i]];
  329. }
  330. post_data.columns = JSON.stringify(columns);
  331. var button_options = {};
  332. button_options[PMA_messages.strGo] = function () {
  333. var is_missing_value = false;
  334. $('select[name="index[columns][names][]"]').each(function () {
  335. if ($(this).val() === '') {
  336. is_missing_value = true;
  337. }
  338. });
  339. if (! is_missing_value) {
  340. PMA_addColumnToIndex(
  341. source_array,
  342. array_index,
  343. index.Index_choice,
  344. col_index
  345. );
  346. } else {
  347. PMA_ajaxShowMessage(
  348. '<div class="error"><img src="themes/dot.gif" title="" alt=""' +
  349. ' class="icon ic_s_error" /> ' + PMA_messages.strMissingColumn +
  350. ' </div>', false
  351. );
  352. return false;
  353. }
  354. $(this).dialog('close');
  355. };
  356. button_options[PMA_messages.strCancel] = function () {
  357. if (col_index >= 0) {
  358. // Handle state on 'Cancel'.
  359. var $select_list = $('select[name="field_key[' + col_index + ']"]');
  360. if (! $select_list.attr('data-index').length) {
  361. $select_list.find('option[value*="none"]').attr('selected', 'selected');
  362. } else {
  363. var previous_index = $select_list.attr('data-index').split(',');
  364. $select_list.find('option[value*="' + previous_index[0].toLowerCase() + '"]')
  365. .attr('selected', 'selected');
  366. }
  367. }
  368. $(this).dialog('close');
  369. };
  370. var $msgbox = PMA_ajaxShowMessage();
  371. $.post('tbl_indexes.php', post_data, function (data) {
  372. if (data.success === false) {
  373. // in the case of an error, show the error message returned.
  374. PMA_ajaxShowMessage(data.error, false);
  375. } else {
  376. PMA_ajaxRemoveMessage($msgbox);
  377. // Show dialog if the request was successful
  378. var $div = $('<div/>');
  379. $div
  380. .append(data.message)
  381. .dialog({
  382. title: PMA_messages.strAddIndex,
  383. width: 450,
  384. minHeight: 250,
  385. open: function () {
  386. checkIndexName('index_frm');
  387. PMA_showHints($div);
  388. PMA_init_slider();
  389. $('#index_columns').find('td').each(function () {
  390. $(this).css('width', $(this).width() + 'px');
  391. });
  392. $('#index_columns').find('tbody').sortable({
  393. axis: 'y',
  394. containment: $('#index_columns').find('tbody'),
  395. tolerance: 'pointer'
  396. });
  397. // We dont need the slider at this moment.
  398. $(this).find('fieldset.tblFooters').remove();
  399. },
  400. modal: true,
  401. buttons: button_options,
  402. close: function () {
  403. $(this).remove();
  404. }
  405. });
  406. }
  407. });
  408. }
  409. /**
  410. * Creates a advanced index type selection dialog.
  411. *
  412. * @param array source_array Array holding a particular type of indexes
  413. * @param string index_choice Choice of index
  414. * @param string col_index Index of new column on form
  415. *
  416. * @return void
  417. */
  418. export function PMA_indexTypeSelectionDialog (source_array, index_choice, col_index) {
  419. var $single_column_radio = $('<input type="radio" id="single_column" name="index_choice"' +
  420. ' checked="checked">' +
  421. '<label for="single_column">' + PMA_messages.strCreateSingleColumnIndex + '</label>');
  422. var $composite_index_radio = $('<input type="radio" id="composite_index"' +
  423. ' name="index_choice">' +
  424. '<label for="composite_index">' + PMA_messages.strCreateCompositeIndex + '</label>');
  425. var $dialog_content = $('<fieldset id="advance_index_creator"></fieldset>');
  426. $dialog_content.append('<legend>' + index_choice.toUpperCase() + '</legend>');
  427. // For UNIQUE/INDEX type, show choice for single-column and composite index.
  428. $dialog_content.append($single_column_radio);
  429. $dialog_content.append($composite_index_radio);
  430. var button_options = {};
  431. // 'OK' operation.
  432. button_options[PMA_messages.strGo] = function () {
  433. if ($('#single_column').is(':checked')) {
  434. var index = {
  435. 'Key_name': (index_choice === 'primary' ? 'PRIMARY' : ''),
  436. 'Index_choice': index_choice.toUpperCase()
  437. };
  438. PMA_showAddIndexDialog(source_array, (source_array.length), [col_index], col_index, index);
  439. }
  440. if ($('#composite_index').is(':checked')) {
  441. if ($('input[name="composite_with"]').length !== 0 && $('input[name="composite_with"]:checked').length === 0
  442. ) {
  443. PMA_ajaxShowMessage(
  444. '<div class="error"><img src="themes/dot.gif" title=""' +
  445. ' alt="" class="icon ic_s_error" /> ' +
  446. PMA_messages.strFormEmpty +
  447. ' </div>',
  448. false
  449. );
  450. return false;
  451. }
  452. var array_index = $('input[name="composite_with"]:checked').val();
  453. var source_length = source_array[array_index].columns.length;
  454. var target_columns = [];
  455. for (var i = 0; i < source_length; i++) {
  456. target_columns.push(source_array[array_index].columns[i].col_index);
  457. }
  458. target_columns.push(col_index);
  459. PMA_showAddIndexDialog(source_array, array_index, target_columns, col_index,
  460. source_array[array_index]);
  461. }
  462. $(this).remove();
  463. };
  464. button_options[PMA_messages.strCancel] = function () {
  465. // Handle state on 'Cancel'.
  466. var $select_list = $('select[name="field_key[' + col_index + ']"]');
  467. if (! $select_list.attr('data-index').length) {
  468. $select_list.find('option[value*="none"]').attr('selected', 'selected');
  469. } else {
  470. var previous_index = $select_list.attr('data-index').split(',');
  471. $select_list.find('option[value*="' + previous_index[0].toLowerCase() + '"]')
  472. .attr('selected', 'selected');
  473. }
  474. $(this).remove();
  475. };
  476. var $dialog = $('<div/>').append($dialog_content).dialog({
  477. minWidth: 525,
  478. minHeight: 200,
  479. modal: true,
  480. title: PMA_messages.strAddIndex,
  481. resizable: false,
  482. buttons: button_options,
  483. open: function () {
  484. $('#composite_index').on('change', function () {
  485. if ($(this).is(':checked')) {
  486. $dialog_content.append(PMA_getCompositeIndexList(source_array, col_index));
  487. }
  488. });
  489. $('#single_column').on('change', function () {
  490. if ($(this).is(':checked')) {
  491. if ($('#composite_index_list').length) {
  492. $('#composite_index_list').remove();
  493. }
  494. }
  495. });
  496. },
  497. close: function () {
  498. $('#composite_index').off('change');
  499. $('#single_column').off('change');
  500. $(this).remove();
  501. }
  502. });
  503. }
  504. export function showIndexEditDialog ($outer) {
  505. checkIndexType();
  506. checkIndexName('index_frm');
  507. var $indexColumns = $('#index_columns');
  508. $indexColumns.find('td').each(function () {
  509. $(this).css('width', $(this).width() + 'px');
  510. });
  511. $indexColumns.find('tbody').sortable({
  512. axis: 'y',
  513. containment: $indexColumns.find('tbody'),
  514. tolerance: 'pointer'
  515. });
  516. PMA_showHints($outer);
  517. PMA_init_slider();
  518. // Add a slider for selecting how many columns to add to the index
  519. $outer.find('.slider').slider({
  520. animate: true,
  521. value: 1,
  522. min: 1,
  523. max: 16,
  524. slide: function (event, ui) {
  525. $(this).closest('fieldset').find('input[type=submit]').val(
  526. PMA_sprintf(PMA_messages.strAddToIndex, ui.value)
  527. );
  528. }
  529. });
  530. $('div.add_fields').removeClass('hide');
  531. // focus index size input on column picked
  532. $outer.find('table#index_columns select').on('change', function () {
  533. if ($(this).find('option:selected').val() === '') {
  534. return true;
  535. }
  536. $(this).closest('tr').find('input').focus();
  537. });
  538. // Focus the slider, otherwise it looks nearly transparent
  539. $('a.ui-slider-handle').addClass('ui-state-focus');
  540. // set focus on index name input, if empty
  541. var input = $outer.find('input#input_index_name');
  542. if (! input.val()) {
  543. input.focus();
  544. }
  545. }
  546. export function indexEditorDialog (url, title, callback_success, callback_failure) {
  547. /* Remove the hidden dialogs if there are*/
  548. var $editIndexDialog = $('#edit_index_dialog');
  549. if ($editIndexDialog.length !== 0) {
  550. $editIndexDialog.remove();
  551. }
  552. var $div = $('<div id="edit_index_dialog"></div>');
  553. /**
  554. * @var button_options Object that stores the options
  555. * passed to jQueryUI dialog
  556. */
  557. var button_options = {};
  558. button_options[PMA_messages.strGo] = function () {
  559. /**
  560. * @var the_form object referring to the export form
  561. */
  562. var $form = $('#index_frm');
  563. var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest);
  564. PMA_prepareForAjaxRequest($form);
  565. // User wants to submit the form
  566. $.post($form.attr('action'), $form.serialize() + PMA_commonParams.get('arg_separator') + 'do_save_data=1', function (data) {
  567. var $sqlqueryresults = $('.sqlqueryresults');
  568. if ($sqlqueryresults.length !== 0) {
  569. $sqlqueryresults.remove();
  570. }
  571. if (typeof data !== 'undefined' && data.success === true) {
  572. PMA_ajaxShowMessage(data.message);
  573. var $resultQuery = $('.result_query');
  574. if ($resultQuery.length) {
  575. $resultQuery.remove();
  576. }
  577. if (data.sql_query) {
  578. $('<div class="result_query"></div>')
  579. .html(data.sql_query)
  580. .prependTo('#page_content');
  581. PMA_highlightSQL($('#page_content'));
  582. }
  583. $('.result_query .notice').remove();
  584. $resultQuery.prepend(data.message);
  585. /* Reload the field form*/
  586. $('#table_index').remove();
  587. $('<div id=\'temp_div\'><div>')
  588. .append(data.index_table)
  589. .find('#table_index')
  590. .insertAfter('#index_header');
  591. var $editIndexDialog = $('#edit_index_dialog');
  592. if ($editIndexDialog.length > 0) {
  593. $editIndexDialog.dialog('close');
  594. }
  595. $('div.no_indexes_defined').hide();
  596. if (callback_success) {
  597. callback_success();
  598. }
  599. PMA_reloadNavigation();
  600. } else {
  601. var $temp_div = $('<div id=\'temp_div\'><div>').append(data.error);
  602. var $error;
  603. if ($temp_div.find('.error code').length !== 0) {
  604. $error = $temp_div.find('.error code').addClass('error');
  605. } else {
  606. $error = $temp_div;
  607. }
  608. if (callback_failure) {
  609. callback_failure();
  610. }
  611. PMA_ajaxShowMessage($error, false);
  612. }
  613. }); // end $.post()
  614. };
  615. button_options[PMA_messages.strPreviewSQL] = function () {
  616. // Function for Previewing SQL
  617. var $form = $('#index_frm');
  618. PMA_previewSQL($form);
  619. };
  620. button_options[PMA_messages.strCancel] = function () {
  621. $(this).dialog('close');
  622. };
  623. var $msgbox = PMA_ajaxShowMessage();
  624. $.get('tbl_indexes.php', url, function (data) {
  625. if (typeof data !== 'undefined' && data.success === false) {
  626. // in the case of an error, show the error message returned.
  627. PMA_ajaxShowMessage(data.error, false);
  628. } else {
  629. PMA_ajaxRemoveMessage($msgbox);
  630. // Show dialog if the request was successful
  631. $div
  632. .append(data.message)
  633. .dialog({
  634. title: title,
  635. width: 'auto',
  636. open: PMA_verifyColumnsProperties,
  637. modal: true,
  638. buttons: button_options,
  639. close: function () {
  640. $(this).remove();
  641. }
  642. });
  643. $div.find('.tblFooters').remove();
  644. showIndexEditDialog($div);
  645. }
  646. }); // end $.get()
  647. }