diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst index 9a9a6236e7b0470204f13cc1886142643b528634..f97d0cca5003b3bc0b47aa1a9cb47520d1bc0658 100644 --- a/extension/Documentation/Manual.rst +++ b/extension/Documentation/Manual.rst @@ -9,12 +9,16 @@ .. .. -------------------------------------------------- .. Best Practice T3 reST https://docs.typo3.org/typo3cms/drafts/github/xperseguers/RstPrimer/ -.. External Links: `Bootstrap <http://getbootstrap.com/>`_: +.. External Links: `Bootstrap <http://getbootstrap.com/>`_ .. Add Images: https://wiki.typo3.org/ReST_Syntax#Images ... .. .. Admonitions (https://docs.typo3.org/typo3cms/drafts/github/xperseguers/RstPrimer/#admonitions) .. .. note:: .. important:: .. tip:: .. warning:: .. +.. Definition: +.. some text becomes strong (only one line) +.. description has to indented + .. -*- coding: utf-8 -*- with BOM. @@ -7416,6 +7420,182 @@ last used (STORE_USER) or (first time call during browser session) takes the def tail = </div><p></p> } + +.. _`rest`: + +REST +==== + +Via `REST https://en.wikipedia.org/wiki/Representational_state_transfer`_ it's possible to access the QFQ based +application. Each REST API endpoint has to be defined as a QFQ Form. The QFQ REST api implements the +four most used REST HTTP methods: + +GET - Read + Shows a list of database records or a single record. The QFQ form holds the definition which and what to show. + + List: ``curl -X GET "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/`` + + Record with id=123: ``curl -X GET "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/123`` + +POST - Create new record + The QFQ form defines wich columns will be written in which table. Most of QFQ Form functionality can be used. Example: + + ``curl -X POST "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/" -d '{"name":"Miller","firstname":"Joe"}'`` + +PUT - Update a record + Similar to POST, but a given record will be updated. + + ``curl -X PUT "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/123" -d '{"name":"Miller","firstname":"Joe"}'`` + +DELETE - Delete a record + Similar to a QFQ Delete form. + + ``curl -X DELETE "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/123"`` + +All data is exported in JSON notation. + +To define a QFQ form becomes a REST form by enable one or more of: + +Form: Access > Permit REST: get / insert / update / delete + +Endpoint +^^^^^^^^ + +.. tip:: + + The basic REST API endpoint: ``<domain>/typo3conf/ext/qfq/Source/api/rest.ph`` + + ``<domain>/typo3conf/ext/qfq/Source/api/rest.php/<level1>/<id1>/<level2>/<id2>/.../?<var1>=<value1>&...`` + + +Append level names and ids after `.../rest.php/`, each separated by '/' . + +E.g.: + +1. List of all persons: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person` +2. Data of person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123` +3. Adresses of person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123/address` +4. Adress details of address 45 from person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123/address/45` + +QFQ 'Forms' are used as a 'container' (to define all details). + +.. tip:: + + The QFQ ``form name`` represents the level name. + +Only the last <level> of an URI will be processed. The former ones are just to fulfil a good looking REST API. + +.. note:: + + Each level name (=form name) is available via STORE_CLIENT and name `_formX`. E.g. in example + (1) `{{_form1:C:alnumx}}=person` and `{{_form2:C:alnumx}}=address`. + + Each level id is available via STORE_CLIENT and name `_idX`. E.g. in example + (2) `{{_id1:C}}=123` and `{{_id2:C}}=45`. + + Also the `id` after the last `level` in the URI path, 123 in example (2) and 45 in example (4), is copied to + variable `r` in STORE_TYPO3, access it via `{{r:T}}`. + + +GET - Read +^^^^^^^^^^ + +A REST (GET) form has two modes: :: + +data + Specific content to a given id. Defined via 'form.parameter.restSqlData'. This mode is selected if there is an + id>0 given. + +list + A list of records will be exported. Defined via 'form.parameter.restSqlList'. This mode is selected if there is no + id or id=0. + +There are *no* FormElements. + +To simplify access to id parameter of the URI, a mapping is possible via 'form.parameter.restParam'. +E.g. `restParam=pId,adrId` with example d) makes `{{pId:C}}=123` and `{{adrId:C}}=45`. The order of variable +names corresponds to the position in the URI. `_id1` is always mapped to the first parameter name, `_id2` to +the second one and so on. + +GET Variables provided via URL are available via STORE_CLIENT as usual. + +Form: + ++-------------------+------------------------------------------------------------------------------+ +| Attribute | Description | ++===================+==============================================================================+ +| name=<level> | Level name in URI | ++-------------------+------------------------------------------------------------------------------+ +| permitNew=rest | The form can be loaded in REST mode with mising parameter 'id' or 'id=0' | ++-------------------+------------------------------------------------------------------------------+ +| permitEdit=rest | The form can be loaded in REST mode with parameter 'id' > 0 | ++-------------------+------------------------------------------------------------------------------+ + + +Form.parameter: + ++-------------------+------------------------------------------------------------------------------+ +| Attribute | Description | ++===================+==============================================================================+ +| restSqlData | SQL query selects content shown in data mode. | +| | `restSqlData={{!SELECT id, name, gender FROM Person WHERE id='{{r:T0}}'' }}` | ++-------------------+------------------------------------------------------------------------------+ +| restSqlList | SQL query selects content shown in data mode. | +| | `restSqlData={{!SELECT id, name FROM Person }}` | ++-------------------+------------------------------------------------------------------------------+ +| restParam | Optional. CSV list of variable names. E.g.: `restParam=pId,adrId` | ++-------------------+------------------------------------------------------------------------------+ +| restToken | Optional. User defined string. For dynamic token see below. | ++-------------------+------------------------------------------------------------------------------+ + +.. note: + + There are no `special-column-names`_ available in `restSqlData` or `restSqlList`. Also there are no + SIPs possible, cause REST typically does not offer sessions/cookies (which are necessary for SIPs). + +Authorization +------------- + +By default, the REST API is public accessible. + +If this is not wished, HTTP AUTH might be used (configured via webserver) or the +QFQ internal 'HTTP header token based authorization'. + +Token based authorization +^^^^^^^^^^^^^^^^^^^^^^^^^ + +A form will require a 'token based authorization', as soon as there is a `form.parameter.restToken` defined. +Therefore the HTTP Header 'Authorization' has to be set with `token=<secret token>`. The 'secret token' will +be checked against the server. Using HTTPS, such token can't be sniffed and will typically not be logged in +any server logs. + +Example: :: + + form.parameter.restToken=myCrypticString0123456789 + + Test via commandline: curl -X GET -H 'Authorization: Token token=myCrypticString0123456789' "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/123/address/" + +The static setup with `form.parameter.restToken=myCrypticString0123456789 is fine, as long as only one token +exist. In case of multiple tokens, replace the static string against a SQL query. + +General: The HTML Header Authorization token is available in STORE_CLIENT via '`{{Authorization:C:alnumx}}`. + +For example all created tokens are saved in a table 'Auth' with a column 'token'. Define: :: + + form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' }} + +To restrict access to a subset of data, just save the limitations inside the Auth record and update the query +to check it: + +.. code-block:: pmysql + + form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}'}} + form.parameter.restSqlList={{!SELECT p.id, p.name, p.email FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute}} + form.parameter.restSqlData={{!SELECT p.* FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute AND p.id='{{r:T0}}' }} + +If authorization is denied, the request will be answered with a delay of 3 seconds (configured via securityFailedAuthDelay). + + .. _`system`: System @@ -7605,146 +7785,6 @@ AutoCron / website: HTTPS protocol This is useful if there is a general 'HTTP >> HTTPS' redirection configured and the website is accessed via `https://localhost/...` - -.. _`rest`: - -REST ----- - -QFQ offers an API endpoint for GET (and later POST,PUT,DELETE) operations. :: - - <domain>/typo3conf/ext/qfq/Source/api/rest.php/<level1>/<id1>/<level2>/<id2>/.../?<var1>=<value1>&... - -Append level names and ids after `rest.php/...`, separated by '/' each. - -E.g.: - -1. List of all persons: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person` -2. Data of person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123` -3. Adresses of person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123/address` -4. Adress details of address 45 from person 123: `<domain>/typo3conf/ext/qfq/Source/api/rest.php/person/123/address/45` - - -QFQ 'Forms' are used as a 'container' to configure all necessary export/import details per 'level'. -Each 'level' is represented by a QFQ Form. - -Only the last <level> of an URI will be processed. The former ones are just to fulfil a good looking REST API. - -.. note:: - - The level name is the QFQ form name. - -Each level name (=form name) is available via STORE_CLIENT and name `_formX`. E.g. in example -(1) `{{_form1:C:alnumx}}=person` and `{{_form2:C:alnumx}}=address`. - -Each level id is available via STORE_CLIENT and name `_idX`. E.g. in example -(2) `{{_id1:C}}=123` and `{{_id2:C}}=45`. - -Also the `id` after the last `level` in the URI path, 123 in example (2) and 45 in example (4), is copied to -variable `r` in STORE_TYPO3, access it via `{{r:T}}`. - - -Export (GET) -^^^^^^^^^^^^ - -All data is exported in JSON notation. - -A REST (GET) form has two modes: :: - -data - Specific content to a given id. Defined via 'form.parameter.restSqlData'. This mode is selected if there is an - id>0 given. - -list - A list of records will be exported. Defined via 'form.parameter.restSqlList'. This mode is selected if there is no - id or id=0. - -There are *no* FormElements. - -To simplify access to id parameter of the URI, a mapping is possible via 'form.parameter.restParam'. -E.g. `restParam=pId,adrId` with example d) makes `{{pId:C}}=123` and `{{adrId:C}}=45`. The order of variable -names corresponds to the position in the URI. `_id1` is always mapped to the first parameter name, `_id2` to -the second one and so on. - -GET Variables provided via URL are available via STORE_CLIENT as usual. - -Form: - -+-------------------+------------------------------------------------------------------------------+ -| Attribute | Description | -+===================+==============================================================================+ -| name=<level> | Level name in URI | -+-------------------+------------------------------------------------------------------------------+ -| permitNew=rest | The form can be loaded in REST mode with mising parameter 'id' or 'id=0' | -+-------------------+------------------------------------------------------------------------------+ -| permitEdit=rest | The form can be loaded in REST mode with parameter 'id' > 0 | -+-------------------+------------------------------------------------------------------------------+ - - -Form.parameter: - -+-------------------+------------------------------------------------------------------------------+ -| Attribute | Description | -+===================+==============================================================================+ -| restSqlData | SQL query selects content shown in data mode. | -| | `restSqlData={{!SELECT id, name, gender FROM Person WHERE id='{{r:T0}}'' }}` | -+-------------------+------------------------------------------------------------------------------+ -| restSqlList | SQL query selects content shown in data mode. | -| | `restSqlData={{!SELECT id, name FROM Person }}` | -+-------------------+------------------------------------------------------------------------------+ -| restParam | Optional. CSV list of variable names. E.g.: `restParam=pId,adrId` | -+-------------------+------------------------------------------------------------------------------+ -| restToken | Optional. User defined string. For dynamic token see below. | -+-------------------+------------------------------------------------------------------------------+ - -.. note: - - There are no `special-column-names`_ available in `restSqlData` or `restSqlList`. Also there are no - SIPs possible, cause REST typically does not offer sessions/cookies (which are necessary for SIPs). - -Authorization -^^^^^^^^^^^^^ - -By default, the REST API is public accessible. - -If this is not wished, HTTP AUTH might be used (configured via webserver) or the -QFQ internal 'HTTP header token based authorization'. - -Token based authorization -''''''''''''''''''''''''' - -A form will require a 'token based authorization', as soon as there is a `form.parameter.restToken` defined. -Therefore the HTTP Header 'Authorization' has to be set with `token=<secret token>`. The 'secret token' will -be checked against the server. Using HTTPS, such token can't be sniffed and will typically not be logged in -any server logs. - -Example: :: - - form.parameter.restToken=myCrypticString0123456789 - - Test via commandline: curl -X GET -H 'Authorization: Token token=myCrypticString0123456789' "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/person/123/address/" - -The static setup with `form.parameter.restToken=myCrypticString0123456789 is fine, as long as only one token -exist. In case of multiple tokens, replace the static string against a SQL query. - -General: The HTML Header Authorization token is available in STORE_CLIENT via '`{{Authorization:C:alnumx}}`. - -For example all created tokens are saved in a table 'Auth' with a column 'token'. Define: :: - - form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' }} - -To restrict access to a subset of data, just save the limitations inside the Auth record and update the query -to check it: - -.. code-block:: pmysql - - form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}'}} - form.parameter.restSqlList={{!SELECT p.id, p.name, p.email FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute}} - form.parameter.restSqlData={{!SELECT p.* FROM Person AS p, Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' AND a.attribute=p.attribute AND p.id='{{r:T0}}' }} - -If authorization is denied, the request will be answered with a delay of 3 seconds (configured via securityFailedAuthDelay). - - .. _applicationTest: Application Test diff --git a/extension/Source/core/Constants.php b/extension/Source/core/Constants.php index aebdfb7390f5b4dc460b79d268dbfec0e6e4dc80..3f8c294271d0af2851185b06c219e8eab1bea737 100644 --- a/extension/Source/core/Constants.php +++ b/extension/Source/core/Constants.php @@ -67,7 +67,7 @@ const SQL_FORM_ELEMENT_BY_ID = "SELECT * FROM FormElement AS fe WHERE fe.id = ?" const SQL_FORM_ELEMENT_RAW = "SELECT * FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' 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_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_SIMPLE_ALL_CONTAINER = "SELECT fe.id, fe.feIdContainer, fe.name, fe.value, 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_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'"; diff --git a/extension/Source/core/QuickFormQuery.php b/extension/Source/core/QuickFormQuery.php index d9636c2b12863ea4152a0b7a914707b1a3ce3a8b..c9654862d86f0791907f2c54e226941d596f65e3 100644 --- a/extension/Source/core/QuickFormQuery.php +++ b/extension/Source/core/QuickFormQuery.php @@ -1454,31 +1454,6 @@ class QuickFormQuery { } } - switch ($permitMode) { - case FORM_PERMISSION_SIP: - if (!$sipFound || $formNameFoundInStore !== STORE_SIP || $recordIdFoundInStore !== STORE_SIP) { - throw new UserFormException("SIP Parameter needed for this form.", ERROR_SIP_NEEDED_FOR_THIS_FORM); - } - break; - case FORM_PERMISSION_LOGGED_IN: - if (!$feUserLoggedIn) { - throw new UserFormException("User not logged in.", ERROR_USER_NOT_LOGGED_IN); - } - break; - case FORM_PERMISSION_LOGGED_OUT: - if ($feUserLoggedIn) { - throw new UserFormException("User logged in.", ERROR_USER_LOGGED_IN); - } - break; - case FORM_PERMISSION_ALWAYS: - break; - case FORM_PERMISSION_NEVER: - throw new UserFormException("Loading form forbidden.", ERROR_FORM_FORBIDDEN); - break; - default: - throw new CodeException("Unknown permission mode: '" . $permitMode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE); - } - if ($formMode == FORM_REST) { $method = $this->store::getVar(CLIENT_REQUEST_METHOD, STORE_CLIENT); @@ -1501,6 +1476,33 @@ class QuickFormQuery { default: throw new CodeException('Unknown Request Method: ' . $method, ERROR_UNKNOWN_MODE); } + + } else { + + switch ($permitMode) { + case FORM_PERMISSION_SIP: + if (!$sipFound || $formNameFoundInStore !== STORE_SIP || $recordIdFoundInStore !== STORE_SIP) { + throw new UserFormException("SIP Parameter needed for this form.", ERROR_SIP_NEEDED_FOR_THIS_FORM); + } + break; + case FORM_PERMISSION_LOGGED_IN: + if (!$feUserLoggedIn) { + throw new UserFormException("User not logged in.", ERROR_USER_NOT_LOGGED_IN); + } + break; + case FORM_PERMISSION_LOGGED_OUT: + if ($feUserLoggedIn) { + throw new UserFormException("User logged in.", ERROR_USER_LOGGED_IN); + } + break; + case FORM_PERMISSION_ALWAYS: + break; + case FORM_PERMISSION_NEVER: + throw new UserFormException("Loading form forbidden.", ERROR_FORM_FORBIDDEN); + break; + default: + throw new CodeException("Unknown permission mode: '" . $permitMode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE); + } } // Form Definition valid? @@ -1512,7 +1514,8 @@ class QuickFormQuery { return $sipFound; } $sipArray = $this->store->getStore(STORE_SIP); -// Check: requiredParameter: '' or 'form' or 'form,grId' or 'form #formname for form,grId' + + // Check: requiredParameter: '' or 'form' or 'form,grId' or 'form #formname for form,grId' $requiredParameter = ($r > 0) ? $this->formSpec[F_REQUIRED_PARAMETER_EDIT] : $this->formSpec[F_REQUIRED_PARAMETER_NEW]; if (trim($requiredParameter) == '') { diff --git a/extension/Source/core/Save.php b/extension/Source/core/Save.php index f0b2504bea97b763eac3213a174af547fa409dfb..0bac3994598b32042fe66000df3cc445bb2b6e31 100644 --- a/extension/Source/core/Save.php +++ b/extension/Source/core/Save.php @@ -936,10 +936,10 @@ class Save { return; } - $fileDestinationSplit = $this->evaluate->parse($formElement[FE_FILE_DESTINATION_SPLIT]); - $fileSplitType = $this->evaluate->parse($formElement[FE_FILE_SPLIT]); - $fileSplitTypeOptions = $this->evaluate->parse($formElement[FE_FILE_SPLIT_OPTIONS]); - $fileSplitTableName = $this->evaluate->parse($formElement[FE_FILE_SPLIT_TABLE_NAME]); + $fileDestinationSplit = $this->evaluate->parse($formElement[FE_FILE_DESTINATION_SPLIT]??''); + $fileSplitType = $this->evaluate->parse($formElement[FE_FILE_SPLIT]??''); + $fileSplitTypeOptions = $this->evaluate->parse($formElement[FE_FILE_SPLIT_OPTIONS]??''); + $fileSplitTableName = $this->evaluate->parse($formElement[FE_FILE_SPLIT_TABLE_NAME]??''); if (empty($fileSplitTableName)) { $fileSplitTableName = $this->formSpec[F_TABLE_NAME]; diff --git a/extension/Source/core/store/FillStoreForm.php b/extension/Source/core/store/FillStoreForm.php index 5426cfed4056036343d230b5efb8ecac760e8327..606dbc6fde3615e303a4e6651d11983f0bc3366a 100644 --- a/extension/Source/core/store/FillStoreForm.php +++ b/extension/Source/core/store/FillStoreForm.php @@ -250,7 +250,7 @@ class FillStoreForm { throw new CodeException("Missing the " . FE_TYPE_EXTRA . " field '" . $formElement[FE_NAME] . "' in SIP.", ERROR_MISSING_HIDDEN_FIELD_IN_SIP); } - $newValues[$formElement[FE_NAME]] = $sipValues[$formElement[FE_NAME]]; + $newValues[$formElement[FE_NAME]] = $sipValues[$formElement[FE_NAME]] ?? ''; continue; } @@ -276,6 +276,13 @@ class FillStoreForm { // } // } + + // FORM_REST: typically form elements are filled and created on form load. This does not exist for REST Forms. + // If a FE.value is defined, this has precedence over client supplied content. + if ($formMode == FORM_REST && $formElement[FE_VALUE]!='') { + $clientValues[$clientFieldName] = $this->evaluate->parse($formElement[FE_VALUE]); + } + // copy value to $newValues if (isset($clientValues[$clientFieldName])) { diff --git a/extension/Source/sql/formEditor.sql b/extension/Source/sql/formEditor.sql index fe3ec40c87205d0ae9e8874b7e14e38aa74a8b12..0b252e6cc38ac672caf3ec080763f9aa5cb51e32 100644 --- a/extension/Source/sql/formEditor.sql +++ b/extension/Source/sql/formEditor.sql @@ -262,18 +262,23 @@ VALUES (1, 'permitEdit', 'Permit Edit', 'show', 'radio', 'all', 'native', 360, 0, 10, '<a href="{{documentation:Y}}#form-permitnewedit">Info</a>', '', '', '', 'buttonClass=btn-default', 4, '', '', '', 'specialchar', 'no', ''), - (1, 'escapeTypeDefault', 'Escape type default', 'show', 'radio', 'all', 'native', 370, 0, 10, + + (1, 'prestMethod', 'Permit REST', 'show', 'checkbox', 'all', 'native', 370, 0, 10, + '<a href="{{documentation:Y}}#rest">Info</a>', '', '', '', 'buttonClass=btn-default\nitemList=get,post:insert,put:update,delete', 4, '', '', '', + 'specialchar', 'no', ''), + + (1, 'escapeTypeDefault', 'Escape type default', 'show', 'radio', 'all', 'native', 380, 0, 10, '<a href="{{documentation:Y}}#variable-escape">Info</a>', '', '', '', 'itemList=c:config,s:single,d:double,l:ldap search,L:ldap value,m:mysql realEscapeString,-:none\nbuttonClass=btn-default', 4, '', '', '', 'specialchar', 'no', ''), - (1, 'dirtyMode', 'Record Locking', 'show', 'radio', 'all', 'native', 380, 0, 10, + (1, 'dirtyMode', 'Record Locking', 'show', 'radio', 'all', 'native', 390, 0, 10, '<a href="{{documentation:Y}}#locking-record">Info</a>', '', '', '', 'buttonClass=btn-default', 4, '', '', '', 'specialchar', 'no', ''), - (1, 'recordLockTimeoutSeconds', 'Lock timeout (seconds)', 'show', 'text', 'all', 'native', 390, 0, 0, + (1, 'recordLockTimeoutSeconds', 'Lock timeout (seconds)', 'show', 'text', 'all', 'native', 400, 0, 0, '<a href="{{documentation:Y}}#locking-record">Info</a>', '', '{{SELECT IF("{{recordLockTimeoutSeconds:R0}}"=0,"{{recordLockTimeoutSeconds:Y0}}","{{recordLockTimeoutSeconds:R0}}")}}', '', '', 4, '', '', '', 'specialchar', 'no', ''), - (1, 'primaryKey', 'Primary Key', 'show', 'text', 'all', 'native', 400, 0, 0, + (1, 'primaryKey', 'Primary Key', 'show', 'text', 'all', 'native', 410, 0, 0, '<a href="{{documentation:Y}}#form-primary-key">Info</a>', '', '', '', '', 4, '', '', 'id', 'specialchar', 'no', ''), # Multi