Merge pull request #1312 from D-storm/validation

Improve validation on insert page
This commit is contained in:
Isaac Bennetch 2014-08-03 17:29:58 -04:00
commit 484d34bcd4
9 changed files with 178 additions and 72 deletions

View File

@ -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)

67
js/big_ints.js Normal file
View File

@ -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);
}
};

View File

@ -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 = "<br><input type=text name=salt[multi_edit][" + multi_edit + "][" + urlField + "]" +
" id=salt_" + target.id + " placeholder='" + PMA_messages.strEncryptionKey + "'>";
//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");

View File

@ -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"
. '<textarea name="fields' . $column_name_appendix . '"'
. ' class="' . $the_class . '"'
. (isset($maxlength) ? ' maxlength="' . $maxlength . '"' : '')
. (isset($maxlength) ? ' data-maxlength="' . $maxlength . '"' : '')
. ' rows="' . $textAreaRows . '"'
. ' cols="' . $textareaCols . '"'
. ' dir="' . $text_dir . '"'
. ' id="field_' . ($idindex) . '_3"'
. ' ' . $unnullify_trigger
. ' tabindex="' . ($tabindex + $tabindex_for_value) . '">'
. ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
. ' data-type="' . $data_type . '">'
. $special_chars_encoded
. '</textarea>';
@ -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 '<input type="' . $input_type . '"'
. ' name="fields' . $column_name_appendix . '"'
. ' value="' . $special_chars . '" size="' . $fieldsize . '"'
. ((isset($column['is_char']) && $column['is_char'])
? ' maxlength="' . $fieldsize . '"'
? ' data-maxlength="' . $fieldsize . '"'
: '')
. ($input_min_max !== false ? ' ' . $input_min_max : '')
. ' data-type="' . $data_type . '"'
. ($input_type === 'time' ? ' step="1"' : '')
. ' class="' . $the_class . '" ' . $unnullify_trigger
. ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
@ -1297,7 +1300,7 @@ function PMA_getMaxUploadSize($column, $biggest_max_file_size)
* @param string $column_name_appendix the name atttibute
* @param string $unnullify_trigger validation string
* @param integer $tabindex tab index
* @param array $special_chars special characters
* @param string $special_chars special characters
* @param integer $tabindex_for_value offset for the values tabindex
* @param integer $idindex id index
* @param string $text_dir text direction
@ -1316,6 +1319,8 @@ function PMA_getValueColumnForOtherDatatypes($column, $default_char_editing,
$tabindex_for_value, $idindex, $text_dir, $special_chars_encoded, $data,
$extracted_columnspec
) {
// HTML5 data-* attribute data-type
$data_type = $GLOBALS['PMA_Types']->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') {

View File

@ -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');

View File

@ -446,6 +446,7 @@ if (! empty($return_to_sql_query)) {
}
$scripts->addFile('tbl_change.js');
$scripts->addFile('big_ints.js');
$active_page = $goto_include;

View File

@ -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(
'<textarea name="fieldsb" class="char" '
. 'maxlength="10" rows="5" cols="1" dir="abc/" '
. 'id="field_1_3" tabindex="2">',
. 'data-maxlength="10" rows="5" cols="1" dir="abc/" '
. 'id="field_1_3" tabindex="2" data-type="CHAR">',
array('content' => 'foobar')
),
$result
@ -1237,12 +1238,11 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase
$this->assertEquals(
"\na\n"
. '<textarea name="fieldsb" class="char" '
. 'maxlength="255" rows="5" cols="1" dir="/" '
. 'id="field_1_3" c tabindex="3"></textarea><input type="hidden" '
. 'name="fields_typeb" value="hex" /><br /><input type="file" '
. 'name="fields_uploadfoo[123]" class="textfield" id="field_1_3" '
. 'size="10" c/>&nbsp;(Max: 64KiB)' . "\n",
. '<textarea name="fieldsb" class="char" data-maxlength="255" rows="5" '
. 'cols="1" dir="/" id="field_1_3" c tabindex="3" data-type="HEX">'
. '</textarea><input type="hidden" name="fields_typeb" value="hex" />'
. '<br /><input type="file" name="fields_uploadfoo[123]" class="text'
. 'field" id="field_1_3" size="10" c/>&nbsp;(Max: 64KiB)' . "\n",
$result
);
@ -1264,7 +1264,8 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase
$this->assertEquals(
"\na\n"
. '<textarea name="fieldsb" class="" rows="20" cols="10" dir="/" '
. 'id="field_1_3" c tabindex="3"></textarea><input type="hidden" '
. 'id="field_1_3" c tabindex="3" data-type="HEX">'
. '</textarea><input type="hidden" '
. 'name="fields_typeb" value="hex" />',
$result
);
@ -1288,8 +1289,8 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase
$this->assertEquals(
"\na\n"
. '<input type="text" name="fieldsb" value="" size="10" class='
. '"textfield" c tabindex="3" id="field_1_3" />'
. '<input type="text" name="fieldsb" value="" size="10" data-type='
. '"HEX" class="textfield" c tabindex="3" id="field_1_3" />'
. '<input type="hidden" name="fields_typeb" value="hex" />',
$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(
'<input type="text" name="fieldsa" value="b" size="30" class='
. '"textfield datefield" c tabindex="25" id="field_0_3" />',
'<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
. ' class="textfield datefield" c tabindex="25" id="field_0_3" />',
$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(
'<input type="text" name="fieldsa" value="b" size="30" class='
. '"textfield datetimefield" c tabindex="25" id="field_0_3" />',
'<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
. ' class="textfield datetimefield" c tabindex="25" id="field_0_3" />',
$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(
'<input type="text" name="fieldsa" value="b" size="30" class='
. '"textfield datetimefield" c tabindex="25" id="field_0_3" />',
'<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
. ' class="textfield datetimefield" c tabindex="25" id="field_0_3" />',
$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"
. '<textarea name="fieldsb" class="char" '
. 'maxlength="25" rows="5" cols="1" dir="/" '
. 'id="field_1_3" c tabindex="34">&lt;</textarea>',
. 'data-maxlength="25" rows="5" cols="1" dir="/" '
. 'id="field_1_3" c tabindex="34" data-type="CHAR">'
. '&lt;</textarea>',
$result
);
@ -1411,10 +1414,10 @@ class PMA_InsertEditTest extends PHPUnit_Framework_TestCase
$this->assertEquals(
"a\n"
. '<input type="text" name="fieldsb" value="&lt;" size="20" class="text'
. 'field datetimefield" c tabindex="34" id="field_1_3" /><input type='
. '"hidden" name="auto_incrementb" value="1" /><input type="hidden" name'
. '="fields_typeb" value="timestamp" />',
. '<input type="text" name="fieldsb" value="&lt;" size="20" data-type="'
. 'DATE" class="textfield datetimefield" c tabindex="34" id="field_1_3"'
. ' /><input type="hidden" name="auto_incrementb" value="1" />'
. '<input type="hidden" name="fields_typeb" value="timestamp" />',
$result
);

View File

@ -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;
}

View File

@ -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;
}