Commit 434cac36 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Implement 'encode=specialchar' - new option per formElement.

Play: ALTER TABLE  `FormElement` ADD  `encode` ENUM(  'none',  'specialchar' ) NOT NULL DEFAULT  'specialchar' AFTER  `subrecordOption` ;
Play: formEditor.sql

Attention: FEs with text=editor needs actions - the default of 'specialchar' prohibits saving of HTML tags.

FillStoreForm.php: Submitted values will be specialchars() before copying to STORE_FORM.
AbstractBuildForm.php: Counterpart of FillStoreForm.php - will htmlspecialchars_decode() values read from database. Replace 'checkType' and 'checkPattern' with CONSTANTS.
formEditor.sql: Added new column in FormElement. Add new FormElement 'encode' in FormElement-Editor. Add column 'encode' to all FormElement records.
parent 3c54867b
...@@ -444,7 +444,11 @@ abstract class AbstractBuildForm { ...@@ -444,7 +444,11 @@ abstract class AbstractBuildForm {
if ($storeUse == STORE_USE_DEFAULT && $this->store->getVar($formElement[FE_NAME], STORE_TABLE_COLUMN_TYPES) === false) { if ($storeUse == STORE_USE_DEFAULT && $this->store->getVar($formElement[FE_NAME], STORE_TABLE_COLUMN_TYPES) === false) {
$storeUse = str_replace(STORE_TABLE_DEFAULT, '', $storeUse); // Remove STORE_DEFAULT $storeUse = str_replace(STORE_TABLE_DEFAULT, '', $storeUse); // Remove STORE_DEFAULT
} }
$value = $this->store->getVar($name, $storeUse, $formElement['checkType']); // Retrieve value via FSRVD
$value = $this->store->getVar($name, $storeUse, $formElement[FE_CHECK_TYPE], $foundInStore);
if ($foundInStore == STORE_RECORD && $formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
$value = htmlspecialchars_decode($value, ENT_QUOTES);
}
} }
// Typically: $htmlElementNameIdZero = true // Typically: $htmlElementNameIdZero = true
...@@ -883,7 +887,7 @@ abstract class AbstractBuildForm { ...@@ -883,7 +887,7 @@ abstract class AbstractBuildForm {
$attribute .= $this->getAttributeList($formElement, [F_FE_DATA_PATTERN_ERROR, F_FE_DATA_REQUIRED_ERROR, F_FE_DATA_MATCH_ERROR, F_FE_DATA_ERROR]); $attribute .= $this->getAttributeList($formElement, [F_FE_DATA_PATTERN_ERROR, F_FE_DATA_REQUIRED_ERROR, F_FE_DATA_MATCH_ERROR, F_FE_DATA_ERROR]);
$attribute .= Support::doAttribute('data-load', ($formElement[FE_DYNAMIC_UPDATE] === 'yes') ? 'data-load' : ''); $attribute .= Support::doAttribute('data-load', ($formElement[FE_DYNAMIC_UPDATE] === 'yes') ? 'data-load' : '');
$attribute .= Support::doAttribute('title', $formElement['tooltip']); $attribute .= Support::doAttribute('title', $formElement['tooltip']);
$attribute .= $this->getInputCheckPattern($formElement['checkType'], $formElement['checkPattern']); $attribute .= $this->getInputCheckPattern($formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN]);
$attribute .= $this->getAttributeFeMode($formElement[FE_MODE]); $attribute .= $this->getAttributeFeMode($formElement[FE_MODE]);
...@@ -2533,16 +2537,16 @@ abstract class AbstractBuildForm { ...@@ -2533,16 +2537,16 @@ abstract class AbstractBuildForm {
$showTime = ($formElement[FE_TYPE] == 'time' || $formElement[FE_TYPE] == 'datetime') ? 1 : 0; $showTime = ($formElement[FE_TYPE] == 'time' || $formElement[FE_TYPE] == 'datetime') ? 1 : 0;
$value = Support::convertDateTime($value, $formElement[FE_DATE_FORMAT], $formElement[FE_SHOW_ZERO], $showTime, $formElement[FE_SHOW_SECONDS]); $value = Support::convertDateTime($value, $formElement[FE_DATE_FORMAT], $formElement[FE_SHOW_ZERO], $showTime, $formElement[FE_SHOW_SECONDS]);
$tmpPattern = $formElement['checkPattern']; $tmpPattern = $formElement[FE_CHECK_PATTERN];
$formElement['checkPattern'] = Support::dateTimeRegexp($formElement[FE_TYPE], $formElement[FE_DATE_FORMAT]); $formElement[FE_CHECK_PATTERN] = Support::dateTimeRegexp($formElement[FE_TYPE], $formElement[FE_DATE_FORMAT]);
switch ($formElement['checkType']) { switch ($formElement[FE_CHECK_TYPE]) {
case SANITIZE_ALLOW_PATTERN: case SANITIZE_ALLOW_PATTERN:
$formElement['checkPattern'] = $tmpPattern; $formElement[FE_CHECK_PATTERN] = $tmpPattern;
break; break;
case SANITIZE_ALLOW_MIN_MAX_DATE: case SANITIZE_ALLOW_MIN_MAX_DATE:
$arrMinMax = explode('|', $formElement['checkPattern'], 2); $arrMinMax = explode('|', $formElement[FE_CHECK_PATTERN], 2);
if (count($arrMinMax) != 2) { if (count($arrMinMax) != 2) {
throw new UserFormException('Missing min|max definition', ERROR_MISSING_MIN_MAX); throw new UserFormException('Missing min|max definition', ERROR_MISSING_MIN_MAX);
} }
...@@ -2550,10 +2554,10 @@ abstract class AbstractBuildForm { ...@@ -2550,10 +2554,10 @@ abstract class AbstractBuildForm {
case SANITIZE_ALLOW_ALL: case SANITIZE_ALLOW_ALL:
case SANITIZE_ALLOW_ALNUMX: case SANITIZE_ALLOW_ALNUMX:
case SANITIZE_ALLOW_ALLBUT: case SANITIZE_ALLOW_ALLBUT:
$formElement['checkType'] = SANITIZE_ALLOW_PATTERN; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_PATTERN;
break; break;
default: default:
throw new UserFormException("Checktype not applicable for date/time: '" . $formElement['checkType'] . "'", ERROR_NOT_APPLICABLE); throw new UserFormException("Checktype not applicable for date/time: '" . $formElement[FE_CHECK_TYPE] . "'", ERROR_NOT_APPLICABLE);
} }
// truncate if necessary // truncate if necessary
...@@ -2597,7 +2601,7 @@ abstract class AbstractBuildForm { ...@@ -2597,7 +2601,7 @@ abstract class AbstractBuildForm {
$attribute .= $this->getAttributeList($formElement, ['autocomplete', 'autofocus', 'placeholder']); $attribute .= $this->getAttributeList($formElement, ['autocomplete', 'autofocus', 'placeholder']);
$attribute .= Support::doAttribute('data-load', ($formElement[FE_DYNAMIC_UPDATE] === 'yes') ? 'data-load' : ''); $attribute .= Support::doAttribute('data-load', ($formElement[FE_DYNAMIC_UPDATE] === 'yes') ? 'data-load' : '');
$attribute .= Support::doAttribute('title', $formElement['tooltip']); $attribute .= Support::doAttribute('title', $formElement['tooltip']);
$attribute .= $this->getInputCheckPattern($formElement['checkType'], $formElement['checkPattern']); $attribute .= $this->getInputCheckPattern($formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN]);
if (is_array($arrMinMax)) { if (is_array($arrMinMax)) {
$attribute .= Support::doAttribute('min', $arrMinMax[0]); $attribute .= Support::doAttribute('min', $arrMinMax[0]);
......
...@@ -46,7 +46,7 @@ const RETURN_ARRAY = 'return_array'; ...@@ -46,7 +46,7 @@ const RETURN_ARRAY = 'return_array';
const SQL_FORM_ELEMENT_SPECIFIC_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.feIdContainer = ? AND fe.enabled='yes' ORDER BY fe.ord, fe.id"; const SQL_FORM_ELEMENT_SPECIFIC_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.feIdContainer = ? AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_ALL_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.enabled='yes' ORDER BY fe.ord, fe.id"; const SQL_FORM_ELEMENT_ALL_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_SIMPLE_ALL_CONTAINER = "SELECT fe.id, fe.feIdContainer, fe.name, fe.label, fe.type, fe.checkType, fe.checkPattern, fe.mode, fe.modeSql, fe.parameter, fe.dynamicUpdate FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes' ORDER BY fe.ord, fe.id"; const SQL_FORM_ELEMENT_SIMPLE_ALL_CONTAINER = "SELECT fe.id, fe.feIdContainer, fe.name, fe.label, fe.type, fe.encode, fe.checkType, fe.checkPattern, fe.mode, fe.modeSql, fe.parameter, fe.dynamicUpdate FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_CONTAINER_TEMPLATE_GROUP = "SELECT fe.id, fe.name, fe.label, fe.maxLength, fe.parameter FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' ORDER BY fe.ord, fe.id"; const SQL_FORM_ELEMENT_CONTAINER_TEMPLATE_GROUP = "SELECT fe.id, fe.name, fe.label, fe.maxLength, fe.parameter FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID = "SELECT * FROM FormElement AS fe WHERE fe.id = ? AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' "; const SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID = "SELECT * FROM FormElement AS fe WHERE fe.id = ? AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' ";
const SQL_FORM_ELEMENT_NATIVE_TG_COUNT = "SELECT fe.*, IFNULL(feTg.maxLength,0) AS _tgCopies FROM FormElement AS fe LEFT JOIN FormElement AS feTg ON fe.feIdContainer=feTg.id AND feTg.deleted = 'no' AND feTg.class = 'container' AND feTg.type='templateGroup' AND feTg.enabled='yes' WHERE fe.formId = ? AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes'"; const SQL_FORM_ELEMENT_NATIVE_TG_COUNT = "SELECT fe.*, IFNULL(feTg.maxLength,0) AS _tgCopies FROM FormElement AS fe LEFT JOIN FormElement AS feTg ON fe.feIdContainer=feTg.id AND feTg.deleted = 'no' AND feTg.class = 'container' AND feTg.type='templateGroup' AND feTg.enabled='yes' WHERE fe.formId = ? AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes'";
...@@ -651,6 +651,9 @@ const FE_BS_NOTE_COLUMNS = F_BS_NOTE_COLUMNS; ...@@ -651,6 +651,9 @@ const FE_BS_NOTE_COLUMNS = F_BS_NOTE_COLUMNS;
const FE_WRAP_ROW_LABEL_INPUT_NOW = 'rowLabelInputNote'; const FE_WRAP_ROW_LABEL_INPUT_NOW = 'rowLabelInputNote';
const FE_MAX_LENGTH = 'maxLength'; const FE_MAX_LENGTH = 'maxLength';
const FE_PARAMETER = 'parameter'; const FE_PARAMETER = 'parameter';
const FE_ENCODE = 'encode';
const FE_CHECK_TYPE = 'checkType';
const FE_CHECK_PATTERN = 'checkPattern';
const FE_TYPE_CHECKBOX = 'checkbox'; const FE_TYPE_CHECKBOX = 'checkbox';
...@@ -727,6 +730,10 @@ const TYPEAHEAD_PLACEHOLDER = '?'; ...@@ -727,6 +730,10 @@ const TYPEAHEAD_PLACEHOLDER = '?';
const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime. const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime.
// Values
const FE_ENCODE_SPECIALCHAR = 'specialchar';
const FE_ENCODE_NONE = 'none';
// FormElement Types // FormElement Types
const FE_TYPE_UPLOAD = 'upload'; const FE_TYPE_UPLOAD = 'upload';
const FE_TYPE_EXTRA = 'extra'; const FE_TYPE_EXTRA = 'extra';
......
...@@ -220,13 +220,15 @@ class FillStoreForm { ...@@ -220,13 +220,15 @@ class FillStoreForm {
$newValues[$formElement[FE_NAME]] = $this->doDateTime($formElement, $clientValues[$clientFieldName]); $newValues[$formElement[FE_NAME]] = $this->doDateTime($formElement, $clientValues[$clientFieldName]);
break; break;
default: default:
$val = $clientValues[$clientFieldName];
// Check only if their is something // Check only if their is something
if($clientValues[$clientFieldName] !== '') { if ($val !== '') {
$newValues[$formElement[FE_NAME]] = Sanitize::sanitize($clientValues[$clientFieldName], $val = Sanitize::sanitize($val, $formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN], SANATIZE_EXCEPTION);
$formElement['checkType'], $formElement['checkPattern'], SANATIZE_EXCEPTION); if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
} else { $val = htmlspecialchars($val, ENT_QUOTES);
$newValues[$formElement[FE_NAME]] = ''; }
} }
$newValues[$formElement[FE_NAME]] = $val;
break; break;
} }
} }
......
This diff is collapsed.
...@@ -87,32 +87,32 @@ class BuildFormPlainTest extends AbstractDatabaseTest { ...@@ -87,32 +87,32 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', API_ELEMENT_UPDATE => $label], $json); $this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', API_ELEMENT_UPDATE => $label], $json);
// CheckType // CheckType
$formElement['checkType'] = SANITIZE_ALLOW_MIN_MAX; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_MIN_MAX;
$formElement['checkPattern'] = '1|10'; $formElement[FE_CHECK_PATTERN] = '1|10';
$result = $build->buildInput($formElement, 'name:1', '', $json); $result = $build->buildInput($formElement, 'name:1', '', $json);
$this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" min="1" max="10" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result); $this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" min="1" max="10" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result);
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json); $this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json);
$formElement['checkType'] = SANITIZE_ALLOW_PATTERN; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_PATTERN;
$formElement['checkPattern'] = '^[a-z]*$'; $formElement[FE_CHECK_PATTERN] = '^[a-z]*$';
$result = $build->buildInput($formElement, 'name:1', '', $json); $result = $build->buildInput($formElement, 'name:1', '', $json);
$this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[a-z]*$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result); $this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[a-z]*$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result);
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json); $this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json);
$formElement['checkType'] = SANITIZE_ALLOW_DIGIT; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_DIGIT;
$formElement['checkPattern'] = ''; $formElement[FE_CHECK_PATTERN] = '';
$result = $build->buildInput($formElement, 'name:1', '', $json); $result = $build->buildInput($formElement, 'name:1', '', $json);
$this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[\d]*$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result); $this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[\d]*$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result);
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json); $this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json);
$formElement['checkType'] = SANITIZE_ALLOW_EMAIL; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_EMAIL;
$formElement['checkPattern'] = ''; $formElement[FE_CHECK_PATTERN] = '';
$result = $build->buildInput($formElement, 'name:1', '', $json); $result = $build->buildInput($formElement, 'name:1', '', $json);
$this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result); $this->assertEquals('<input id="123" name="name:1" class="form-control" maxlength="255" type="input" value="" pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors hidden"></div>', $result);
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json); $this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => '', 'disabled' => false, API_ELEMENT_UPDATE => $label], $json);
$formElement['checkType'] = ''; $formElement[FE_CHECK_TYPE] = '';
$formElement['checkPattern'] = ''; $formElement[FE_CHECK_PATTERN] = '';
// Explizit size // Explizit size
...@@ -221,8 +221,8 @@ class BuildFormPlainTest extends AbstractDatabaseTest { ...@@ -221,8 +221,8 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
'maxLength' => '255', 'maxLength' => '255',
'tooltip' => '', 'tooltip' => '',
'placeholder' => '', 'placeholder' => '',
'checkType' => '', FE_CHECK_TYPE => '',
'checkPattern' => '', FE_CHECK_PATTERN => '',
FE_HTML_ID => '123', FE_HTML_ID => '123',
'tabindex' => 0 'tabindex' => 0
...@@ -243,8 +243,8 @@ class BuildFormPlainTest extends AbstractDatabaseTest { ...@@ -243,8 +243,8 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]); $build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
$formElement['checkType'] = SANITIZE_ALLOW_MIN_MAX; $formElement[FE_CHECK_TYPE] = SANITIZE_ALLOW_MIN_MAX;
$formElement['checkPattern'] = ''; $formElement[FE_CHECK_PATTERN] = '';
$result = $build->buildInput($formElement, 'name:1', '', $json); $result = $build->buildInput($formElement, 'name:1', '', $json);
} }
......
...@@ -73,6 +73,7 @@ CREATE TABLE IF NOT EXISTS `FormElement` ( ...@@ -73,6 +73,7 @@ CREATE TABLE IF NOT EXISTS `FormElement` (
'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete',
'sendmail') NOT NULL DEFAULT 'text', 'sendmail') NOT NULL DEFAULT 'text',
`subrecordOption` SET('edit', 'delete', 'new') NOT NULL DEFAULT '', `subrecordOption` SET('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM('none', 'specialchar') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM('alnumx', 'digit', 'numerical', 'email', 'min|max', 'min|max date', 'pattern', 'all') NOT NULL DEFAULT 'alnumx', `checkType` ENUM('alnumx', 'digit', 'numerical', 'email', 'min|max', 'min|max date', 'pattern', 'all') NOT NULL DEFAULT 'alnumx',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '', `checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment