Commit 446e7e7f authored by Carsten  Rose's avatar Carsten Rose
Browse files

Refactor to use form.restMethod instead of form.permNew|edit=rest. Next step...

Refactor to use form.restMethod instead of form.permNew|edit=rest. Next step will be to eliminate formMode=FORM_REST
parent 3e2d2805
......@@ -24,11 +24,38 @@ try {
try {
$form = OnString::splitPathInfoToIdForm($_SERVER['PATH_INFO'], $restId, $restForm);
// get latest `ìd`
$id=end($restId);
// Fake Bodytext setup
// Fake Bodytext setup
$bodytext = TYPO3_RECORD_ID . '=' . $id . PHP_EOL;
$bodytext .= TYPO3_FORM . '=' . $form . PHP_EOL;
$method=$_SERVER['REQUEST_METHOD'];
switch($method){
case REQUEST_METHOD_GET:
break;
case REQUEST_METHOD_POST:
if($id!=0){
throw new UserFormException('Method POST needs no id or id=0', ERROR_REST_INVALID_ID);
}
$_POST = json_decode(file_get_contents('php://input'), true);
break;
case REQUEST_METHOD_PUT:
if($id==0){
throw new UserFormException('Method PUT needs an id>0', ERROR_REST_INVALID_ID);
}
$_POST = json_decode(file_get_contents('php://input'), true);
break;
case REQUEST_METHOD_DELETE:
if($id==0){
throw new UserFormException('Method DELETE needs an id>0', ERROR_REST_INVALID_ID);
}
break;
default:
break;
}
$qfq = new QuickFormQuery(['bodytext' => $bodytext]);
$answer = $qfq->rest($restId, $restForm);
$status='HTTP/1.0 200 OK';
......@@ -50,4 +77,3 @@ try {
header($status);
header("Content-Type: application/json");
echo json_encode($answer);
......@@ -365,6 +365,7 @@ const ERROR_IMPORT_LIST_SHEET_NAMES = 2901;
// REST
const ERROR_FORM_REST = 3000;
const ERROR_REST_AUTHORIZATION = 3001;
const ERROR_REST_INVALID_ID = 3002;
//
// Store Names: Identifier
//
......@@ -420,12 +421,18 @@ const CLIENT_SERVER_ADDRESS = 'SERVER_ADDR';
const CLIENT_SERVER_PORT = 'SERVER_PORT';
const CLIENT_REMOTE_ADDRESS = 'REMOTE_ADDR';
const CLIENT_REQUEST_SCHEME = 'REQUEST_SCHEME';
const CLIENT_REQUEST_METHOD = 'REQUEST_METHOD';
const CLIENT_SCRIPT_FILENAME = 'SCRIPT_FILENAME';
const CLIENT_QUERY_STRING = 'QUERY_STRING';
const CLIENT_REQUEST_URI = 'REQUEST_URI';
const CLIENT_SCRIPT_NAME = 'SCRIPT_NAME';
const CLIENT_PHP_SELF = 'PHP_SELF';
const REQUEST_METHOD_GET = 'GET';
const REQUEST_METHOD_POST = 'POST';
const REQUEST_METHOD_PUT = 'PUT';
const REQUEST_METHOD_DELETE = 'DELETE';
// _COOKIE
const CLIENT_COOKIE_QFQ = 'cookieQfq';
......@@ -879,6 +886,7 @@ const F_TITLE = 'title';
const F_TABLE_NAME = 'tableName';
const F_PRIMARY_KEY = 'primaryKey';
const F_PRIMARY_KEY_DEFAULT = 'id';
const F_REST_METHOD = 'restMethod';
const F_REQUIRED_PARAMETER_NEW = 'requiredParameterNew';
const F_REQUIRED_PARAMETER_EDIT = 'requiredParameterEdit';
const F_EXTRA_DELETE_FORM = 'extraDeleteForm';
......
......@@ -172,7 +172,7 @@ class QuickFormQuery {
Session::checkSessionExpired($timeout);
// If an FE user logs out and a different user logs in (same browser session) - the old values has to be destroyed!
if (Session::getAndDestroyFlagFeUserHasChanged() ) {
if (Session::getAndDestroyFlagFeUserHasChanged()) {
$this->store->unsetStore(STORE_USER);
}
......@@ -371,7 +371,6 @@ class QuickFormQuery {
// Check 'session expire' happens quite late, cause it can be configured per form.
Session::checkSessionExpired($this->formSpec[F_SESSION_TIMEOUT_SECONDS]);
if ($formName !== false) {
// Validate only if there is a 'real' form (not a FORM_DELETE with only a tablename).
$sipFound = $this->validateForm($foundInStore, $formMode);
......@@ -984,13 +983,10 @@ class QuickFormQuery {
HelperFormElement::explodeParameter($form, F_PARAMETER);
unset($form[F_PARAMETER]);
if (isset($form[FE_FILL_STORE_VAR])) {
$fillStoreVar = $form[FE_FILL_STORE_VAR];
unset($form[FE_FILL_STORE_VAR]);
}
// Save specific elements to be expanded later.
$parseLater = OnArray::getArrayItems($form, [F_FORWARD_PAGE, F_REST_SQL_LIST, F_REST_SQL_DATA]);
$parseLater = OnArray::getArrayItems($form, [F_FORWARD_PAGE, FE_FILL_STORE_VAR, F_REST_SQL_LIST, F_REST_SQL_DATA]);
$form[FE_FILL_STORE_VAR] = '';
$form[F_FORWARD_PAGE] = '';
$form[F_REST_SQL_LIST] = '';
$form[F_REST_SQL_DATA] = '';
......@@ -1054,8 +1050,11 @@ class QuickFormQuery {
!empty($form[FORM_LOG_ACTIVE]) && Logger::logFormLine($form, "F:$mode:evaluated:" . date('Y-m-d H:i:s'), $form, true);
// Fire FE_FILL_STORE_VAR after the primary form record has been loaded
if (!empty($fillStoreVar)) {
$rows = $this->evaluate->parse($fillStoreVar, ROW_EXPECT_0_1);
if (!empty($formSpec[FE_FILL_STORE_VAR])) {
$rows = $this->evaluate->parse($formSpec[FE_FILL_STORE_VAR], ROW_EXPECT_0_1);
unset($formSpec[FE_FILL_STORE_VAR]);
if (is_array($rows)) {
$this->store->appendToStore($rows, STORE_VAR);
// LOG
......@@ -1422,10 +1421,10 @@ class QuickFormQuery {
*/
private function validateForm($formNameFoundInStore, $formMode) {
// Retrieve record_id either from SIP (prefered) or via URL
// Retrieve record_id either from SIP (preferred) or via URL
$r = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT, '', $recordIdFoundInStore);
// If not found: Fake a definition in STORE_TYPO3.
// no record id: Fake a definition in STORE_TYPO3.
if ($r === false) {
$r = 0;
$this->store->setVar(TYPO3_RECORD_ID, $r, STORE_TYPO3);
......@@ -1466,22 +1465,23 @@ class QuickFormQuery {
case FORM_PERMISSION_NEVER:
throw new UserFormException("Loading form forbidden.", ERROR_FORM_FORBIDDEN);
break;
case FORM_PERMISSION_REST:
if ($formMode != FORM_REST) {
throw new UserFormException("Try to load a REST form", ERROR_FORM_REST);
}
break;
// case FORM_PERMISSION_REST:
// if ($formMode != FORM_REST) {
// throw new UserFormException("Try to load a REST form", ERROR_FORM_REST);
// }
// break;
default:
throw new CodeException("Unknown permission mode: '" . $permitMode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE);
}
if ($formMode == FORM_REST) {
$this->restCheckAuthToken($this->formSpec[F_REST_TOKEN] ?? '');
if ($permitMode != FORM_PERMISSION_REST) {
throw new UserFormException("Try to load a non-REST form in REST mode", ERROR_FORM_REST);
$method = strtolower($this->store::getVar(CLIENT_REQUEST_METHOD, STORE_CLIENT));
if (false === Support::findInSet($method, $this->formSpec[F_REST_METHOD])) {
throw new UserFormException("Form '". $this->formSpec[F_NAME]. "' is not allowed with method '$method'", ERROR_FORM_REST);
}
$this->restCheckAuthToken($this->formSpec[F_REST_TOKEN] ?? '');
}
// Form Definition valid?
......@@ -1871,7 +1871,7 @@ EOF;
/**
* @param array $restId
* @param array $restForm
* @param string $method
* @return array|string
* @throws CodeException
* @throws DbException
......
......@@ -136,7 +136,11 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `Form` ADD `labelAlign` ENUM('default','left','center','right') NOT NULL DEFAULT 'default' AFTER `forwardPage`;",
"ALTER TABLE `FormElement` ADD `labelAlign` ENUM('default','left','center','right') NOT NULL DEFAULT 'default' AFTER `maxLength`;",
],
'19.2.3' => [
"ALTER TABLE `Form` ADD `restMethod` SET('get','post','put','delete') NOT NULL DEFAULT '' AFTER `permitEdit`; ",
],
);
......
......@@ -319,7 +319,7 @@ class Support {
/**
* Search for the parameter $needle in $haystack. The arguments has to be separated by ','.
*
* Returns false if not found or index of found place. Be careful: use unary operator to compare for 'false'
* Returns false if not found, or index (starting with 0) of found place. Be careful: use unary operator to compare for 'false'
*
* @param string $needle
* @param string $haystack
......
......@@ -42,14 +42,12 @@ class Client {
if (isset($_POST)) {
$post = $_POST;
// Logger::logMessage(var_export($post, true) . PHP_EOL . PHP_EOL,'post.txt');
}
if (isset($_COOKIE[SESSION_NAME])) {
$cookie[CLIENT_COOKIE_QFQ] = $_COOKIE[SESSION_NAME];
}
// It's important to merge the SERVER array last: those entries shall overwrite client values.
if (isset($_SERVER)) {
$server = Sanitize::htmlentitiesArr($_SERVER); // $_SERVER values might be compromised.
}
......@@ -59,6 +57,7 @@ class Client {
$server[CLIENT_REMOTE_ADDRESS] = '0.0.0.0';
}
// It's important to merge the SERVER array last: those entries shall overwrite client values.
$arr = array_merge($header, $get, $post, $cookie, $server);
return Sanitize::normalize($arr);
......
......@@ -124,6 +124,7 @@ class Store {
CLIENT_SERVER_PORT => SANITIZE_ALLOW_DIGIT,
CLIENT_REMOTE_ADDRESS => SANITIZE_ALLOW_ALNUMX,
CLIENT_REQUEST_SCHEME => SANITIZE_ALLOW_ALNUMX,
CLIENT_REQUEST_METHOD => SANITIZE_ALLOW_ALNUMX,
CLIENT_SCRIPT_FILENAME => SANITIZE_ALLOW_ALNUMX,
CLIENT_QUERY_STRING => SANITIZE_ALLOW_ALL,
CLIENT_REQUEST_URI => SANITIZE_ALLOW_ALL,
......
......@@ -13,6 +13,7 @@ CREATE TABLE IF NOT EXISTS `Form`
`permitNew` ENUM ('sip', 'logged_in', 'logged_out', 'always', 'never') NOT NULL DEFAULT 'sip',
`permitEdit` ENUM ('sip', 'logged_in', 'logged_out', 'always', 'never') NOT NULL DEFAULT 'sip',
`restMethod` SET ('get','post','put','delete') NOT NULL DEFAULT '',
`escapeTypeDefault` VARCHAR(32) NOT NULL DEFAULT 'c',
`render` ENUM ('bootstrap', 'table', 'plain') NOT NULL DEFAULT 'bootstrap',
`requiredParameterNew` VARCHAR(255) NOT NULL DEFAULT '',
......@@ -69,15 +70,15 @@ CREATE TABLE IF NOT EXISTS `FormElement`
`modeSql` TEXT NOT NULL,
`class` ENUM ('native', 'action', 'container') NOT NULL DEFAULT 'native',
`type` ENUM ('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra', 'gridJQW', 'text',
'editor', 'annotate', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload',
'annotate', 'imageCut', 'fieldset', 'pill', 'templateGroup',
'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad',
'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail',
'paste') NOT NULL DEFAULT 'text',
'editor', 'annotate', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload',
'annotate', 'imageCut', 'fieldset', 'pill', 'templateGroup',
'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad',
'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail',
'paste') NOT NULL DEFAULT 'text',
`subrecordOption` SET ('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM ('none', 'specialchar') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM ('auto', 'alnumx', 'digit', 'numerical', 'email', 'pattern', 'allbut',
'all') NOT NULL DEFAULT 'auto',
'all') NOT NULL DEFAULT 'auto',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
`onChange` VARCHAR(255) NOT NULL DEFAULT '',
......@@ -164,20 +165,19 @@ WHERE FIND_IN_SET(Form.name, 'form,formElement,copyForm,cron') > 0;
#
# FormEditor: Form
INSERT INTO Form (id, name, title, noteInternal, tableName, permitNew, permitEdit, render, multiSql, parameter)
VALUES
(1, 'form', 'Form Editor: {{SELECT id, " / ", name FROM Form WHERE id = {{r:S0}}}} (DB: {{dbNameQfq:Y}})',
'FormElement Editor',
'Form', 'sip', 'sip', 'bootstrap', '', 'maxVisiblePill=5\nclass=container-fluid\ndbIndex={{indexQfq:Y}}');
VALUES (1, 'form', 'Form Editor: {{SELECT id, " / ", name FROM Form WHERE id = {{r:S0}}}} (DB: {{dbNameQfq:Y}})',
'FormElement Editor',
'Form', 'sip', 'sip', 'bootstrap', '', 'maxVisiblePill=5\nclass=container-fluid\ndbIndex={{indexQfq:Y}}');
# FormEditor: FormElements for 'form'
INSERT INTO FormElement (id, formId, name, label, mode, type, checkType, class, ord, size, note, clientJs, value,
sql1, parameter, feIdContainer, subrecordOption, modeSql, placeholder)
VALUES
(1, 1, 'basic', 'Basic', 'show', 'pill', 'all', 'container', 100, 0, '', '', '', '', '', 0, '', '', ''),
(2, 1, 'formelement', 'Formelement', 'show', 'pill', 'all', 'container', 200, 0, '', '', '', '', '', 0, '', '', ''),
(3, 1, 'layout', 'Layout', 'show', 'pill', 'all', 'container', 300, 0, '', '', '', '', '', 0, '', '', ''),
(4, 1, 'access', 'Access', 'show', 'pill', 'all', 'container', 400, 0, '', '', '', '', '', 0, '', '', ''),
(5, 1, 'multi', 'Multi', 'hidden', 'pill', 'all', 'container', 500, 0, '', '', '', '', '', 0, '', '', '');
VALUES (1, 1, 'basic', 'Basic', 'show', 'pill', 'all', 'container', 100, 0, '', '', '', '', '', 0, '', '', ''),
(2, 1, 'formelement', 'Formelement', 'show', 'pill', 'all', 'container', 200, 0, '', '', '', '', '', 0, '', '',
''),
(3, 1, 'layout', 'Layout', 'show', 'pill', 'all', 'container', 300, 0, '', '', '', '', '', 0, '', '', ''),
(4, 1, 'access', 'Access', 'show', 'pill', 'all', 'container', 400, 0, '', '', '', '', '', 0, '', '', ''),
(5, 1, 'multi', 'Multi', 'hidden', 'pill', 'all', 'container', 500, 0, '', '', '', '', '', 0, '', '', '');
# FormEditor: FormElements for 'form'
INSERT INTO FormElement (formId, name, label, mode, type, checkType, class, ord, size, maxLength, note, clientJs, value,
......@@ -293,136 +293,151 @@ VALUES
# FormEditor: FormElement
INSERT INTO Form (id, name, title, noteInternal, tableName, permitNew, permitEdit, render, multiSql, parameter,
requiredParameterNew)
VALUES
(2, 'formElement',
'Form Element Editor. Form : {{SELECT f.id, " / ", f.name FROM Form AS f WHERE f.id = {{formId:S0}} }} (DB: {{dbNameQfq:Y}})',
'Please secure the form',
'FormElement', 'sip', 'sip', 'bootstrap', '',
'maxVisiblePill=5\nclassBody=qfq-color-blue-1\ndbIndex={{indexQfq:Y}}', 'formId');
VALUES (2, 'formElement',
'Form Element Editor. Form : {{SELECT f.id, " / ", f.name FROM Form AS f WHERE f.id = {{formId:S0}} }} (DB: {{dbNameQfq:Y}})',
'Please secure the form',
'FormElement', 'sip', 'sip', 'bootstrap', '',
'maxVisiblePill=5\nclassBody=qfq-color-blue-1\ndbIndex={{indexQfq:Y}}', 'formId');
# FormEditor: FormElements for 'formElement'
INSERT INTO FormElement (id, formId, name, label, mode, type, checkType, class, ord, size, note, clientJs, value,
sql1, parameter, feIdContainer, subrecordOption, modeSql)
VALUES
(100, 2, 'basic', 'Basic', 'show', 'pill', 'all', 'container', 10, 0, '', '', '', '', '', 0, '', ''),
(101, 2, 'check_order', 'Check & Order', 'show', 'pill', 'all', 'container', 290, 0, '', '', '', '', '', 0, '',
''),
(102, 2, 'layout', 'Layout', 'show', 'pill', 'all', 'container', 390, 0, '', '', '', '', '', 0, '', ''),
(103, 2, 'value', 'Value', 'show', 'pill', 'all', 'container', 490, 0, '', '', '', '', '', 0, '', '');
VALUES (100, 2, 'basic', 'Basic', 'show', 'pill', 'all', 'container', 10, 0, '', '', '', '', '', 0, '', ''),
(101, 2, 'check_order', 'Check & Order', 'show', 'pill', 'all', 'container', 290, 0, '', '', '', '', '', 0, '',
''),
(102, 2, 'layout', 'Layout', 'show', 'pill', 'all', 'container', 390, 0, '', '', '', '', '', 0, '', ''),
(103, 2, 'value', 'Value', 'show', 'pill', 'all', 'container', 490, 0, '', '', '', '', '', 0, '', '');
INSERT INTO FormElement (formId, name, label, mode, type, checkType, class, ord, size, maxLength, note, clientJs, value,
sql1, parameter, feIdContainer, subrecordOption, dynamicUpdate, bsLabelColumns, bsInputColumns,
bsNoteColumns, modeSql, placeholder, encode)
VALUES
(2, 'feIdContainer', 'Container', 'show', 'select', 'all', 'native', 120, 0, 0,
'<a href="{{documentation:Y}}#class-container">Info</a>', '', '',
'{{!SELECT fe.id, CONCAT(fe.type, " / ", fe.name, " (", COUNT(feSub.id), ")" ) FROM FormElement As fe LEFT JOIN FormElement As feSub ON feSub.feIdContainer=fe.id WHERE fe.formId={{formId:SR0}} AND fe.class="container" GROUP BY fe.id ORDER BY fe.type, fe.ord, fe.name }}',
'emptyItemAtStart', 100, '', 'no', '', '', '',
'{{SELECT IF(COUNT(fe.id)>0, "show", "hidden") FROM Form AS f LEFT JOIN FormElement AS fe ON f.id=fe.formId AND fe.class="container" WHERE f.id={{formId:S0}} GROUP BY f.id}}',
'', 'specialchar'),
(2, 'enabled', 'Enabled', 'show', 'checkbox', 'all', 'native', 130, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>', '', '', '', '', 100, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'dynamicUpdate', 'Dynamic Update', 'show', 'checkbox', 'all', 'native', 135, 0, 0,
'<a href="{{documentation:Y}}#dynamic-update">Info</a>',
'', '', '', '', 100, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'name', 'Name', 'show', 'text', 'all', 'native', 140, 0, 0, '<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '',
'typeAheadSql = [{{indexData:Y}}]SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = "{{DB_1_NAME:Y}}" AND table_name = "{{SELECT f.tableName FROM Form AS f WHERE f.id={{formId:S0}}}}" AND COLUMN_NAME LIKE ? ORDER BY COLUMN_NAME\ntypeAheadMinLength = 1\ntypeAheadLimit = 100\ntypeAheadPedantic = 0\n',
100, '<a href="{{documentation:Y}}#class-native">Info</a>', 'no', '', '', '', '', '', 'specialchar'),
(2, 'label', 'Label', 'show', 'text', 'all', 'native', 150, 0, 0, '<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '', '', 100, '', 'no', '', '', '', '', '', 'none'),
(2, 'mode', 'Mode', 'show', 'radio', 'all', 'native', 160, 0, 0, '<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '', 'buttonClass=btn-default', 100, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'modeSql', 'Mode sql', 'show', 'text', 'all', 'native', 170, '70,2', 0,
'<a href="{{documentation:Y}}#dynamic-update">Info</a>', '', '', '', '', 100, '', 'no', '', '', '', '', '', 'none'),
(2, 'class', 'Class', 'show', 'select', 'all', 'native', 180, 0, 0,
'<a href="{{documentation:Y}}#class-container">Info</a>', '', '{{class:FSRD0:alnumx}}', '', '', 100, '', 'yes', '', '',
'', '', '', 'none'),
(2, 'type', 'Type', 'show', 'select', 'all', 'native', 190, 0, 0,
'<a href="{{documentation:Y}}#class-native">Native</a>, <a href="{{documentation:Y}}#class-action">Action</a>, <a href="{{documentation:Y}}#class-container">Container</a>',
'', '', '',
'itemList={{SELECT IF( "{{class:FRD0:alnumx}}"="native","checkbox,date,time,datetime,dateJQW,datetimeJQW,extra,gridJQW,text,editor,annotate,imageCut,note,password,radio,select,subrecord,upload", IF("{{class:FRD0:alnumx}}"="action","beforeLoad,beforeSave,beforeInsert,beforeUpdate,beforeDelete,afterLoad,afterSave,afterInsert,afterUpdate,afterDelete,sendMail,paste", "fieldset,pill,templateGroup") ) }}',
100, '', 'yes', '', '', '', '', '', 'specialchar'),
(2, 'subrecordOption', 'Subrecord Option', 'show', 'checkbox', 'all', 'native', 200, 0, 0,
'<a href="{{documentation:Y}}#subrecord-option">Info</a>', '', '', '',
'', 100, '', 'yes', '', '', '',
'{{ SELECT IF("{{type:FRE:alnumx}}"="subrecord" AND "{{class:FRE:alnumx}}"="native", "show", "hidden") }}', '',
'specialchar'),
(2, 'parameterLanguageA', 'Language: {{formLanguageALabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageAId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageB', 'Language: {{formLanguageBLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageBId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageC', 'Language: {{formLanguageCLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageCId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageD', 'Language: {{formLanguageDLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageDId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'encode', 'Encode', 'show', 'radio', 'all', 'native', 300, 0, 0,
'<a href="{{documentation:Y}}#field-encode">Info</a>', '', '', '', 'buttonClass=btn-default', 101, '', 'no', '', '',
'', '', '', 'specialchar'),
(2, 'checkType', 'Check Type', 'show', 'radio', 'all', 'native', 310, 0, 0,
'<a href="{{documentation:Y}}#field-checktype">Info</a>', '', '', '', 'buttonClass=btn-default', 101, '', 'yes', '',
'', '', '', '', 'specialchar'),
(2, 'checkPattern', 'Check Pattern', 'show', 'text', 'all', 'native', 320, 0, 0,
'<a href="{{documentation:Y}}#field-checkpattern">Info</a>, <a href="https://regex101.com/">Regex101</a>', '', '', '',
'', 101, '', 'yes', '', '', '',
'{{ SELECT IF("{{checkType:FRE:alnumx}}"="pattern" OR "{{checkType:FRE:allbut}}" LIKE "min%", "show", "hidden") }}',
'', 'none'),
#(2, 'onChange', 'JS onChange', 'show', 'text', 'all', 'native', 330, 0, 0, '', '', '', '', '', 101, '', 'no', '', '', '', '', '', 'none'),
(2, 'ord', 'Order', 'show', 'text', 'all', 'native', 340, 0, 0, '<a href="{{documentation:Y}}#field-ord">Info</a>', '',
'{{SELECT IF({{ord:R0}}=0, MAX(IFNULL(fe.ord,0))+10,{{ord:R0}}) FROM (SELECT 1) AS a LEFT JOIN FormElement AS fe ON fe.formId={{formId:S0}} GROUP BY fe.formId}}',
'', '', 101, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'tabindex', 'tabindex', 'show', 'text', 'all', 'native', 350, 0, 0,
'<a href="{{documentation:Y}}#field-tabindex">Info</a>', '', '', '', '', 101, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'adminNote', 'Internal Note', 'show', 'text', 'all', 'native', 360, '60,4', 0, '', '', '', '', '', 101, '', 'no',
'', '',
'', '', '', 'specialchar'),
(2, 'labelAlign', 'Label Align', 'show', 'radio', 'all', 'native', 400, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>', '', '', '', 'buttonClass=btn-default', 102, '', 'no', '', '',
'', '', '', 'specialchar'),
(2, 'size', 'Size', 'show', 'text', 'all', 'native', 405, 0, 0, '<a href="{{documentation:Y}}#field-size">Info</a>', '',
'', '', '', 102, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'bsLabelColumns', 'BS Label Columns', 'show', 'text', 'all', 'native', 410, 0, 0,
'<a href="{{documentation:Y}}#field-bslabelcolumns">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '',
'{{SELECT IF(f.bsLabelColumns != '''', f.bsLabelColumns, ''{{bsLabelColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'bsInputColumns', 'BS Input Columns', 'show', 'text', 'all', 'native', 420, 0, 0, '', '', '', '', '', 102, '', 'no',
'', '', '', '',
'{{SELECT IF(f.bsInputColumns != '''', f.bsInputColumns, ''{{bsInputColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'bsNoteColumns', 'BS Note Columns', 'show', 'text', 'all', 'native', 430, 0, 0, '', '', '', '', '', 102, '', 'no',
'', '', '', '',
'{{SELECT IF(f.bsNoteColumns != '''', f.bsNoteColumns, ''{{bsNoteColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'rowLabelInputNote', 'Label / Input / Note', 'show', 'checkbox', 'alnumx', 'native', 440, 0, 10,
'<a href="{{documentation:Y}}#field-rowlabelinputnote">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'maxLength', 'Maxlength', 'show', 'text', 'all', 'native', 450, 0, 0,
'<a href="{{documentation:Y}}#field-maxlength">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'note', 'Note', 'show', 'text', 'all', 'native', 460, '40,5', 0,
'<a href="{{documentation:Y}}#field-note">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '', 'none'),
(2, 'tooltip', 'Tooltip', 'show', 'text', 'all', 'native', 470, 0, 0,
'<a href="{{documentation:Y}}#field-tooltip">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '', 'none'),
(2, 'placeholder', 'Placeholder', 'show', 'text', 'all', 'native', 480, 0, 0,
'<a href="{{documentation:Y}}#field-placeholder">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '', 'none'),
(2, 'value', 'value', 'show', 'text', 'all', 'native', 500, '40,2', 0,
'<a href="{{documentation:Y}}#field-value">Info</a>', '', '', '', '', 103, '', 'no', '', '', '', '', '', 'none'),
(2, 'sql1', 'sql1', 'show', 'text', 'all', 'native', 510, '40,5', 0,
'<a href="{{documentation:Y}}#sql1">Info</a><br><br>MariaDB: <a href="https://mariadb.com/kb/en/mariadb/select/">Select</a>, <a href="https://mariadb.com/kb/en/mariadb/functions-and-operators/">Functions</a>',
'', '', '', '', 103, '', 'no', '', '', '', '', '', 'none'),
(2, 'parameter', 'Parameter', 'show', 'text', 'all', 'native', 520, '40,8', 0,
'<a href="{{documentation:Y}}#fe-parameter-attributes">Info</a>',
'', '', '', '', 103, '', 'no', '', '', '', '', '', 'none');
VALUES (2, 'feIdContainer', 'Container', 'show', 'select', 'all', 'native', 120, 0, 0,
'<a href="{{documentation:Y}}#class-container">Info</a>', '', '',
'{{!SELECT fe.id, CONCAT(fe.type, " / ", fe.name, " (", COUNT(feSub.id), ")" ) FROM FormElement As fe LEFT JOIN FormElement As feSub ON feSub.feIdContainer=fe.id WHERE fe.formId={{formId:SR0}} AND fe.class="container" GROUP BY fe.id ORDER BY fe.type, fe.ord, fe.name }}',
'emptyItemAtStart', 100, '', 'no', '', '', '',
'{{SELECT IF(COUNT(fe.id)>0, "show", "hidden") FROM Form AS f LEFT JOIN FormElement AS fe ON f.id=fe.formId AND fe.class="container" WHERE f.id={{formId:S0}} GROUP BY f.id}}',
'', 'specialchar'),
(2, 'enabled', 'Enabled', 'show', 'checkbox', 'all', 'native', 130, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>', '', '', '', '', 100, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'dynamicUpdate', 'Dynamic Update', 'show', 'checkbox', 'all', 'native', 135, 0, 0,
'<a href="{{documentation:Y}}#dynamic-update">Info</a>',
'', '', '', '', 100, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'name', 'Name', 'show', 'text', 'all', 'native', 140, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '',
'typeAheadSql = [{{indexData:Y}}]SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = "{{DB_1_NAME:Y}}" AND table_name = "{{SELECT f.tableName FROM Form AS f WHERE f.id={{formId:S0}}}}" AND COLUMN_NAME LIKE ? ORDER BY COLUMN_NAME\ntypeAheadMinLength = 1\ntypeAheadLimit = 100\ntypeAheadPedantic = 0\n',
100, '<a href="{{documentation:Y}}#class-native">Info</a>', 'no', '', '', '', '', '', 'specialchar'),
(2, 'label', 'Label', 'show', 'text', 'all', 'native', 150, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '', '', 100, '', 'no', '', '', '', '', '', 'none'),
(2, 'mode', 'Mode', 'show', 'radio', 'all', 'native', 160, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>',
'', '', '', 'buttonClass=btn-default', 100, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'modeSql', 'Mode sql', 'show', 'text', 'all', 'native', 170, '70,2', 0,
'<a href="{{documentation:Y}}#dynamic-update">Info</a>', '', '', '', '', 100, '', 'no', '', '', '', '', '',
'none'),
(2, 'class', 'Class', 'show', 'select', 'all', 'native', 180, 0, 0,
'<a href="{{documentation:Y}}#class-container">Info</a>', '', '{{class:FSRD0:alnumx}}', '', '', 100, '', 'yes',
'', '',
'', '', '', 'none'),
(2, 'type', 'Type', 'show', 'select', 'all', 'native', 190, 0, 0,
'<a href="{{documentation:Y}}#class-native">Native</a>, <a href="{{documentation:Y}}#class-action">Action</a>, <a href="{{documentation:Y}}#class-container">Container</a>',
'', '', '',
'itemList={{SELECT IF( "{{class:FRD0:alnumx}}"="native","checkbox,date,time,datetime,dateJQW,datetimeJQW,extra,gridJQW,text,editor,annotate,imageCut,note,password,radio,select,subrecord,upload", IF("{{class:FRD0:alnumx}}"="action","beforeLoad,beforeSave,beforeInsert,beforeUpdate,beforeDelete,afterLoad,afterSave,afterInsert,afterUpdate,afterDelete,sendMail,paste", "fieldset,pill,templateGroup") ) }}',
100, '', 'yes', '', '', '', '', '', 'specialchar'),
(2, 'subrecordOption', 'Subrecord Option', 'show', 'checkbox', 'all', 'native', 200, 0, 0,
'<a href="{{documentation:Y}}#subrecord-option">Info</a>', '', '', '',
'', 100, '', 'yes', '', '', '',
'{{ SELECT IF("{{type:FRE:alnumx}}"="subrecord" AND "{{class:FRE:alnumx}}"="native", "show", "hidden") }}', '',
'specialchar'),
(2, 'parameterLanguageA', 'Language: {{formLanguageALabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageAId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageB', 'Language: {{formLanguageBLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageBId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageC', 'Language: {{formLanguageCLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageCId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'parameterLanguageD', 'Language: {{formLanguageDLabel:YE}}', 'show', 'text', 'all', 'native', 210, '60,2', 0,
'<a href="{{documentation:Y}}#multi-language-form">Info</a>', '', '', '', '', 100, '', 'no', '', '', '',
'{{SELECT IF("{{formLanguageDId:YE}}"="","hidden","show" ) }}', '', 'none'),
(2, 'encode', 'Encode', 'show', 'radio', 'all', 'native', 300, 0, 0,
'<a href="{{documentation:Y}}#field-encode">Info</a>', '', '', '', 'buttonClass=btn-default', 101, '', 'no', '',
'',
'', '', '', 'specialchar'),
(2, 'checkType', 'Check Type', 'show', 'radio', 'all', 'native', 310, 0, 0,
'<a href="{{documentation:Y}}#field-checktype">Info</a>', '', '', '', 'buttonClass=btn-default', 101, '', 'yes',
'',
'', '', '', '', 'specialchar'),
(2, 'checkPattern', 'Check Pattern', 'show', 'text', 'all', 'native', 320, 0, 0,
'<a href="{{documentation:Y}}#field-checkpattern">Info</a>, <a href="https://regex101.com/">Regex101</a>', '',
'', '',
'', 101, '', 'yes', '', '', '',
'{{ SELECT IF("{{checkType:FRE:alnumx}}"="pattern" OR "{{checkType:FRE:allbut}}" LIKE "min%", "show", "hidden") }}',
'', 'none'),
#(2, 'onChange', 'JS onChange', 'show', 'text', 'all', 'native', 330, 0, 0, '', '', '', '', '', 101, '', 'no', '', '', '', '', '', 'none'),
(2, 'ord', 'Order', 'show', 'text', 'all', 'native', 340, 0, 0,
'<a href="{{documentation:Y}}#field-ord">Info</a>', '',
'{{SELECT IF({{ord:R0}}=0, MAX(IFNULL(fe.ord,0))+10,{{ord:R0}}) FROM (SELECT 1) AS a LEFT JOIN FormElement AS fe ON fe.formId={{formId:S0}} GROUP BY fe.formId}}',
'', '', 101, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'tabindex', 'tabindex', 'show', 'text', 'all', 'native', 350, 0, 0,
'<a href="{{documentation:Y}}#field-tabindex">Info</a>', '', '', '', '', 101, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'adminNote', 'Internal Note', 'show', 'text', 'all', 'native', 360, '60,4', 0, '', '', '', '', '', 101, '',
'no',
'', '',
'', '', '', 'specialchar'),
(2, 'labelAlign', 'Label Align', 'show', 'radio', 'all', 'native', 400, 0, 0,
'<a href="{{documentation:Y}}#class-native">Info</a>', '', '', '', 'buttonClass=btn-default', 102, '', 'no', '',
'',
'', '', '', 'specialchar'),
(2, 'size', 'Size', 'show', 'text', 'all', 'native', 405, 0, 0,
'<a href="{{documentation:Y}}#field-size">Info</a>', '',
'', '', '', 102, '', 'no', '', '', '', '', '', 'specialchar'),
(2, 'bsLabelColumns', 'BS Label Columns', 'show', 'text', 'all', 'native', 410, 0, 0,
'<a href="{{documentation:Y}}#field-bslabelcolumns">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '',
'{{SELECT IF(f.bsLabelColumns != '''', f.bsLabelColumns, ''{{bsLabelColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'bsInputColumns', 'BS Input Columns', 'show', 'text', 'all', 'native', 420, 0, 0, '', '', '', '', '', 102,
'', 'no',
'', '', '', '',
'{{SELECT IF(f.bsInputColumns != '''', f.bsInputColumns, ''{{bsInputColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'bsNoteColumns', 'BS Note Columns', 'show', 'text', 'all', 'native', 430, 0, 0, '', '', '', '', '', 102, '',
'no',
'', '', '', '',
'{{SELECT IF(f.bsNoteColumns != '''', f.bsNoteColumns, ''{{bsNoteColumns:Y}}'') FROM Form AS f WHERE f.id = {{formId}} }}',
'specialchar'),
(2, 'rowLabelInputNote', 'Label / Input / Note', 'show', 'checkbox', 'alnumx', 'native', 440, 0, 10,
'<a href="{{documentation:Y}}#field-rowlabelinputnote">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '',
'',
'specialchar'),
(2, 'maxLength', 'Maxlength', 'show', 'text', 'all', 'native', 450, 0, 0,
'<a href="{{documentation:Y}}#field-maxlength">Info</a>', '', '', '', '', 102, '', 'no', '', '', '', '', '',
'specialchar'),
(2, 'note', 'Note', 'show', 'text', 'all', 'native', 460, '40,5', 0,