diff --git a/ChangeLog b/ChangeLog index 3835aad031..6e7dd94de3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,7 @@ phpMyAdmin - ChangeLog + rfe #908 Improvements for the table editor (index creation) + rfe #1426 Navigation state lost on reload - bug #4439 Table list in left panel doesn't expand ++ rfe Improved validation when inserting data 4.2.8.0 (not yet released) diff --git a/js/big_ints.js b/js/big_ints.js new file mode 100644 index 0000000000..27576bca5f --- /dev/null +++ b/js/big_ints.js @@ -0,0 +1,67 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * phpMyAdmin's BigInts library + */ + +/** + * @var BigInts object to handle big integers (in string) + * as JS can handle upto 53 bits of precision only. + */ +var BigInts = { + + /** + * Compares two integer strings + * + * @param int1 the string representation of 1st integer + * @param int2 the string representation of 2nd integer + * + * @return int 0 if equal, < 0 if int1 < int2, else > 0 + */ + compare: function(int1, int2) { + // trim integers + int1 = int1.trim(); + int2 = int2.trim(); + // length of integer strings + var len1 = int1.length; + var len2 = int2.length; + // integer is -ve or not + var isNeg1 = int1[0] === '-' ? true : false; + var isNeg2 = int2[0] === '-' ? true : false; + // Sign of int1 != int2 then no actual comparison + // is needed we can return result directly + if (isNeg1 !== isNeg2) { + return (isNeg1 === true ? -1 : 1); + } + // replace - sign with 0 + int1[0] = isNeg1 ? '0' : int1[0]; + int2[0] = isNeg2 ? '0' : int2[0]; + // pad integers with 0 to make them + // equal length + int1 = BigInts.lpad(int1, len2); + int2 = BigInts.lpad(int2, len1); + // Now they are good to compare as strings + if (int1 !== int2) { + return (int1 < int2 ? -1 : 1); + } + return 0; + }, + + /** + * Adds leading zeros to a integer given a total length + * + * @param int the string representation of the integer + * @param total the total length required + * + * @return int the integer of length given with added leading + * zeros if necessary + */ + lpad: function(int, total){ + var len = int.length; + var pad = ''; + while(len < total) { + pad += '0'; + len++; + } + return (pad + int); + } +}; diff --git a/js/tbl_change.js b/js/tbl_change.js index c8d684e901..3c70eba503 100644 --- a/js/tbl_change.js +++ b/js/tbl_change.js @@ -151,19 +151,37 @@ function verificationsAfterFieldChange(urlField, multi_edit, theType) { var evt = window.event || arguments.callee.caller.arguments[0]; var target = evt.target || evt.srcElement; + var $this_input = $("input[name='fields[multi_edit][" + multi_edit + "][" + + urlField + "]']"); + // check if it is textarea rather than input + if ($this_input.length === 0) { + $this_input = $("textarea[name='fields[multi_edit][" + multi_edit + "][" + + urlField + "]']"); + } //To generate the textbox that can take the salt var new_salt_box = "
"; //If AES_ENCRYPT is Selected then append the new textbox for salt - if (target.value == "AES_DECRYPT" || target.value == "AES_ENCRYPT") { + if (target.value === 'AES_DECRYPT' || target.value === 'AES_ENCRYPT') { if (!($("#salt_" + target.id).length)) { - $("#" + target.id).parent().next("td").next("td").find("input[name*='fields']").after(new_salt_box); + $this_input.after(new_salt_box); } - + if ($this_input.data('type') !== 'HEX') { + $('#' + target.id).addClass('invalid_value'); + return false; + } + } else if(target.value === 'MD5' && + typeof $this_input.data('maxlength') !== 'undefined' && + $this_input.data('maxlength') < 32 + ){ + $('#' + target.id).addClass('invalid_value'); + return false; } else { + $('#' + target.id).removeClass('invalid_value'); //The value of the select is no longer AES_ENCRYPT, remove the textbox for salt + $('#salt_' + target.id).prev('br').remove(); $("#salt_" + target.id).remove(); } @@ -172,11 +190,6 @@ function verificationsAfterFieldChange(urlField, multi_edit, theType) // Unchecks the Ignore checkbox for the current row $("input[name='insert_ignore_" + multi_edit + "']").prop('checked', false); - var $this_input = $("input[name='fields[multi_edit][" + multi_edit + "][" + urlField + "]']"); - // check if it is textarea rather than input - if ($this_input.length === 0) { - $this_input = $("textarea[name='fields[multi_edit][" + multi_edit + "][" + urlField + "]']"); - } // Does this field come from datepicker? if ($this_input.data('comes_from') == 'datepicker') { @@ -224,16 +237,29 @@ function verificationsAfterFieldChange(urlField, multi_edit, theType) } } } - //validate for integer type - if (theType.substring(0, 3) == "int") { + //validation for integer type + if ($this_input.data('type') === 'INT') { + var min = $this_input.attr('min'); + var max = $this_input.attr('max'); + var value = $this_input.val(); $this_input.removeClass("invalid_value"); - if (isNaN($this_input.val())) { + if (isNaN(value) || BigInts.compare(value, min) < 0 || + BigInts.compare(value, max) > 0 + ) { $this_input.addClass("invalid_value"); return false; } - } - // validate binary & blob types - if (theType.indexOf('blob') > -1 || theType.indexOf('binary') > -1) { + //validation for CHAR types + } else if ($this_input.data('type') === 'CHAR') { + var len = $this_input.val().length; + var maxlen = $this_input.data('maxlength'); + $this_input.removeClass("invalid_value"); + if (typeof maxlen !== 'undefined' && len > maxlen) { + $this_input.addClass("invalid_value"); + return false; + } + // validate binary & blob types + } else if ($this_input.data('type') === 'HEX') { $this_input.removeClass("invalid_value"); if ($this_input.val().match(/^[a-f0-9]*$/i) === null) { $this_input.addClass("invalid_value"); diff --git a/libraries/insert_edit.lib.php b/libraries/insert_edit.lib.php index 6d8fe2d91a..98021d59a5 100644 --- a/libraries/insert_edit.lib.php +++ b/libraries/insert_edit.lib.php @@ -586,7 +586,7 @@ function PMA_getNullifyCodeForNullColumn($column, $foreigners, $foreignData) * @param integer $tabindex_for_value offset for the values tabindex * @param integer $idindex id index * @param array $data description of the column field - * @param array $special_chars special characters + * @param string $special_chars special characters * @param array $foreignData data about the foreign keys * @param boolean $odd_row whether row is odd * @param array $paramTableDbArray array containing $table and $db @@ -618,6 +618,8 @@ function PMA_getValueColumn($column, $backup_field, $column_name_appendix, $is_upload, $biggest_max_file_size, $default_char_editing, $no_support_types, $gis_data_types, $extracted_columnspec ) { + // HTML5 data-* attribute data-type + $data_type = $GLOBALS['PMA_Types']->getTypeClass($column['True_Type']); $html_output = ''; if ($foreignData['foreign_link'] == true) { @@ -644,7 +646,7 @@ function PMA_getValueColumn($column, $backup_field, $column_name_appendix, $html_output .= PMA_getTextarea( $column, $backup_field, $column_name_appendix, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, $text_dir, - $special_chars_encoded + $special_chars_encoded, $data_type ); } elseif (strstr($column['pma_type'], 'text')) { @@ -652,7 +654,7 @@ function PMA_getValueColumn($column, $backup_field, $column_name_appendix, $html_output .= PMA_getTextarea( $column, $backup_field, $column_name_appendix, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, $text_dir, - $special_chars_encoded + $special_chars_encoded, $data_type ); $html_output .= "\n"; if (strlen($special_chars) > 32000) { @@ -802,12 +804,13 @@ function PMA_dispRowForeignData($backup_field, $column_name_appendix, * @param string $text_dir text direction * @param string $special_chars_encoded replaced char if the string starts * with a \r\n pair (0x0d0a) add an extra \n + * @param string $data_type the html5 data-* attribute type * * @return string an html snippet */ function PMA_getTextarea($column, $backup_field, $column_name_appendix, - $unnullify_trigger, - $tabindex, $tabindex_for_value, $idindex, $text_dir, $special_chars_encoded + $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, + $text_dir, $special_chars_encoded, $data_type ) { $the_class = ''; $textAreaRows = $GLOBALS['cfg']['TextareaRows']; @@ -832,13 +835,14 @@ function PMA_getTextarea($column, $backup_field, $column_name_appendix, $html_output = $backup_field . "\n" . ''; @@ -1076,7 +1080,7 @@ function PMA_getColumnSetValueAndSelectSize($column, $extracted_columnspec) * * @param array $column description of column in given table * @param array $data data to edit - * @param array $special_chars special characters + * @param string $special_chars special characters * @param integer $biggest_max_file_size biggest max file size for uploading * @param string $backup_field hidden input field * @param string $column_name_appendix the name atttibute @@ -1125,14 +1129,14 @@ function PMA_getBinaryAndBlobColumn( $html_output .= "\n" . PMA_getTextarea( $column, $backup_field, $column_name_appendix, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, $text_dir, - $special_chars_encoded + $special_chars_encoded, 'HEX' ); } else { // field size should be at least 4 and max $GLOBALS['cfg']['LimitChars'] $fieldsize = min(max($column['len'], 4), $GLOBALS['cfg']['LimitChars']); $html_output .= "\n" . $backup_field . "\n" . PMA_getHTMLinput( $column, $column_name_appendix, $special_chars, $fieldsize, - $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex + $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, 'HEX' ); } $html_output .= sprintf($fields_type_html, $fields_type_val); @@ -1161,17 +1165,19 @@ function PMA_getBinaryAndBlobColumn( * * @param array $column description of column in given table * @param string $column_name_appendix the name attribute - * @param array $special_chars special characters + * @param string $special_chars special characters * @param integer $fieldsize html field size * @param string $unnullify_trigger validation string * @param integer $tabindex tab index * @param integer $tabindex_for_value offset for the values tabindex * @param integer $idindex id index + * @param string $data_type the html5 data-* attribute type * * @return string an html snippet */ -function PMA_getHTMLinput($column, $column_name_appendix, $special_chars, - $fieldsize, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex +function PMA_getHTMLinput( + $column, $column_name_appendix, $special_chars, $fieldsize, $unnullify_trigger, + $tabindex, $tabindex_for_value, $idindex, $data_type ) { $input_type = 'text'; // do not use the 'date' or 'time' types here; they have no effect on some @@ -1189,27 +1195,24 @@ function PMA_getHTMLinput($column, $column_name_appendix, $special_chars, $the_class .= ' datetimefield'; } $input_min_max = false; - if (!$GLOBALS['cfg']['ShowFunctionFields']) { - if (in_array( - $column['True_Type'], - $GLOBALS['PMA_Types']->getIntegerTypes() - )) { - $input_type = 'number'; - $is_unsigned = substr($column['pma_type'], -9) === ' unsigned'; - $min_max_values = $GLOBALS['PMA_Types']->getIntegerRange( - $column['True_Type'], ! $is_unsigned - ); - $input_min_max = 'min="' . $min_max_values[0] . '" ' - . 'max="' . $min_max_values[1] . '" '; - } + if (in_array($column['True_Type'], $GLOBALS['PMA_Types']->getIntegerTypes())) { + $extracted_columnspec = PMA_Util::extractColumnSpec($column['Type']); + $is_unsigned = $extracted_columnspec['unsigned']; + $min_max_values = $GLOBALS['PMA_Types']->getIntegerRange( + $column['True_Type'], ! $is_unsigned + ); + $input_min_max = 'min="' . $min_max_values[0] . '" ' + . 'max="' . $min_max_values[1] . '"'; + $data_type = 'INT'; } return 'getTypeClass($column['True_Type']); $fieldsize = PMA_getColumnSize($column, $extracted_columnspec); $html_output = $backup_field . "\n"; if ($column['is_char'] @@ -1327,12 +1332,12 @@ function PMA_getValueColumnForOtherDatatypes($column, $default_char_editing, $html_output .= PMA_getTextarea( $column, $backup_field, $column_name_appendix, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, $text_dir, - $special_chars_encoded + $special_chars_encoded, $data_type ); } else { $html_output .= PMA_getHTMLinput( - $column, $column_name_appendix, $special_chars, - $fieldsize, $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex + $column, $column_name_appendix, $special_chars, $fieldsize, + $unnullify_trigger, $tabindex, $tabindex_for_value, $idindex, $data_type ); if ($column['Extra'] == 'auto_increment') { diff --git a/tbl_change.php b/tbl_change.php index 135ee2568c..2bdbe52cd3 100644 --- a/tbl_change.php +++ b/tbl_change.php @@ -75,6 +75,7 @@ $scripts = $header->getScripts(); $scripts->addFile('functions.js'); $scripts->addFile('sql.js'); $scripts->addFile('tbl_change.js'); +$scripts->addFile('big_ints.js'); $scripts->addFile('jquery/jquery-ui-timepicker-addon.js'); $scripts->addFile('gis_data_editor.js'); diff --git a/tbl_replace.php b/tbl_replace.php index c8cfb84a92..37bac523ef 100644 --- a/tbl_replace.php +++ b/tbl_replace.php @@ -446,6 +446,7 @@ if (! empty($return_to_sql_query)) { } $scripts->addFile('tbl_change.js'); +$scripts->addFile('big_ints.js'); $active_page = $goto_include; diff --git a/test/libraries/PMA_insert_edit_test.php b/test/libraries/PMA_insert_edit_test.php index 695783aea4..69a9054690 100644 --- a/test/libraries/PMA_insert_edit_test.php +++ b/test/libraries/PMA_insert_edit_test.php @@ -848,15 +848,16 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $column = array(); $column['is_char'] = true; $column['Type'] = 'char(10)'; + $column['True_Type'] = 'char'; $result = PMA_getTextarea( - $column, 'a', 'b', 'd', 2, 0, 1, "abc/", 'foobar' + $column, 'a', 'b', 'd', 2, 0, 1, "abc/", 'foobar', 'CHAR' ); $this->assertTag( PMA_getTagArray( '
 (Max: 64KiB)' . "\n", + . '' + . '
 (Max: 64KiB)' . "\n", $result ); @@ -1264,7 +1264,8 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $this->assertEquals( "\na\n" . '' + . '', $result ); @@ -1288,8 +1289,8 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $this->assertEquals( "\na\n" - . '' + . '' . '', $result ); @@ -1306,31 +1307,31 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $column = array(); $column['pma_type'] = 'date'; $column['True_Type'] = 'date'; - $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0); + $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0, 'DATE'); $this->assertEquals( - '', + '', $result ); // case 2 datetime $column['pma_type'] = 'datetime'; $column['True_Type'] = 'datetime'; - $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0); + $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0, 'DATE'); $this->assertEquals( - '', + '', $result ); // case 3 timestamp $column['pma_type'] = 'timestamp'; $column['True_Type'] = 'timestamp'; - $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0); + $result = PMA_getHTMLinput($column, 'a', 'b', 30, 'c', 23, 2, 0, 'DATE'); $this->assertEquals( - '', + '', $result ); } @@ -1374,6 +1375,7 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $column['len'] = 20; $column['is_char'] = true; $column['Type'] = 'char(25)'; + $column['True_Type'] = 'char'; $GLOBALS['cfg']['CharEditing'] = ''; $GLOBALS['cfg']['MaxSizeForInputField'] = 30; $GLOBALS['cfg']['MinSizeForInputField'] = 10; @@ -1394,8 +1396,9 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $this->assertEquals( "a\n\na\n" . '', + . 'data-maxlength="25" rows="5" cols="1" dir="/" ' + . 'id="field_1_3" c tabindex="34" data-type="CHAR">' + . '<', $result ); @@ -1411,10 +1414,10 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase $this->assertEquals( "a\n" - . '', + . '' + . '', $result ); diff --git a/themes/original/css/common.css.php b/themes/original/css/common.css.php index 4f0a86aa06..c2d48c2c7c 100644 --- a/themes/original/css/common.css.php +++ b/themes/original/css/common.css.php @@ -1530,6 +1530,7 @@ input[type=text].invalid_value, input[type=password].invalid_value, input[type=number].invalid_value, input[type=date].invalid_value, +select.invalid_value, .invalid_value { background: #FFCCCC; } diff --git a/themes/pmahomme/css/common.css.php b/themes/pmahomme/css/common.css.php index b0197c30c1..4fc432cce4 100644 --- a/themes/pmahomme/css/common.css.php +++ b/themes/pmahomme/css/common.css.php @@ -1993,6 +1993,7 @@ input[type=text].invalid_value, input[type=password].invalid_value, input[type=number].invalid_value, input[type=date].invalid_value, +select.invalid_value, .invalid_value { background: #FFCCCC; }