diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91aae13e1d8622fb22a0f79265c7b9962a033f1e..d3943e20cb7e1ced36048156e74d3cb4c95d40e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@
 ..
 .. --------------------------------------------------
 .. 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
 ..
 .. -*- coding: utf-8 -*- with BOM.
@@ -36,11 +36,63 @@ Features
 Bug Fixes
 ^^^^^^^^^
 
-Version 19.2.1
+
+Version 19.2.3
 --------------
 
+Date: 22.02.2019
+
+Notes
+^^^^^
+
+New: `QFQ REST <https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest>`_
+interface implemented.
+
+Features
+^^^^^^^^
+
+* Rest Implementation with GET,PUT,POST,DELETE and authorization token
+* REST.md: add
+* Manual.rst: reformat all sql code to use tyoposcript - it seems mysql does not work
+* Manual.rst: Add some admonitions
+
+Bug Fixes
+^^^^^^^^^
+
+* #7925 / fixed:
+
+  1. change CWD during split reduced to splitting only.
+  1. fixed problem in mkDirParent() with absolute paths.
+  1. make logger independent of CWD.
+  1. fixed problem with `mktemp --tmpdir`(difference Ubuntu 16 / 18) by using PHP function again.
+  1. fixed some 'undefined index' problems.
+
+Version 19.2.2
+--------------
+
+Date: 19.02.2019
+
+Notes
+^^^^^
+
+* QFQ now offers a basic REST API. Check https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest
+
+Features
+^^^^^^^^
+
+* 7910 / Check for double form names
+* 7904 / REST api export. Manual.rst: describe QFQ REST API
+* Latest phpStorm IDE complains about missing ext-json in composer.json. Added.
+* Manual.rst: Example how to use 'password' escape class.
+
+Bug Fixes
+^^^^^^^^^
+
+
+Version 19.2.1
+--------------
 
-Date: 16.2.19
+Date: 18.02.2019
 
 Notes
 ^^^^^
diff --git a/doc/NewVersion.md b/doc/NewVersion.md
index 69fa34e405729cfd857fda9369fd81d633510f14..6be3e5c0df0a8cad9d79e2410b23c015bac9cce8 100644
--- a/doc/NewVersion.md
+++ b/doc/NewVersion.md
@@ -53,12 +53,12 @@ Neue Versionsnummer
    * Update the version number in this document (topic 6)
    * Commit & Push new version changes to master branch: 
    
-      New version 19.2.1
+      New version 19.2.3
 
 6) **New Tag**: 
 
-   git tag v19.2.1
-   git push -u origin v19.2.1
+   git tag v19.2.3
+   git push -u origin v19.2.3
 
 7) Tickets: 
    * Schliessen und der QFQ Version zuweisen.
diff --git a/doc/REST.md b/doc/REST.md
new file mode 100644
index 0000000000000000000000000000000000000000..ffdbc195d4a07e3415a051723775301de8750c31
--- /dev/null
+++ b/doc/REST.md
@@ -0,0 +1,125 @@
+====
+REST
+====
+
+* https://en.wikipedia.org/wiki/Representational_state_transfer
+* https://restfulapi.net
+* https://poe-php.de/tutorial/rest-einfuehrung-in-die-api-erstellung
+* https://blog.restcase.com/top-5-rest-api-security-guidelines/
+
+General Concept
+===============
+
+* There is one PHP file to handle all REST calls:
+
+    typo3conf/ext/qfq/Source/api/rest.php
+
+* All further endpoints are appended after rest.php, seperated by '/'. Example:
+
+    http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson/1/restAddress/123?myEmail=jonni@miller.com
+
+    The argument 'myEmail' is just to show how  GET variables will be submitted.
+    
+* Each `level` is a QFQ form. In the above example: `restPerson` and `restAddress`     
+* A QFQ form will be enabled for REST calls via field 'Permit REST'. Possible options: get, insert (post), update (put), delete
+* An optional HTML header token based 'authorization' is supported.
+* At least one `level` (= form name) has to be given. 
+* Multiple `level/id` tuple are possible.
+* Only the last level will be used. The last `level` becomes automatically `form` in STORE_TYPO3.
+* The last `id` becomes automatically `r` in STORE_TYPO3.
+* Previous `level` and `id` are accessible via `{{_id1:C}}`, `{{_form1:C:alnumx}}`,`{{_id2:C}}`, `{{_form2:C:alnumx}}`, ...
+* Import/Export data has to be/is JSON encoded.
+* The following settings has no impact to QFQ forms called via REST: `form.Permit New`, `form.Permit Edit`  
+
+HTML Requests
+=============
+
+GET - export
+------------
+ 
+Example:
+
+   curl -X GET "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson" 
+
+Details:
+
+* no `id` or `id=0` (example: 1, 123): The result of `Form.parameter.restSqlList` will be generated.
+* `id>0` (example: 1, 123): the result of `Form.parameter.restSqlData` will be generated.
+* The whole resultset will be JSON encoded.
+* It's not possible to render subrecords. This has to be done via a sub level (next form). 
+* Future: If this is not sufficient, a possible solution might be a `report`-notation (special FormElement), which do 
+  not implode all output, but leave the rows/cells intact as an array - the json_encode will then to the rest. 
+
+POST - insert
+-------------
+
+Example:
+
+   curl -X POST "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson" -d '{"name":"Miller","firstname":"Jonni"}'
+
+Details:
+
+* The data has to be JSON encoded transferred to the REST API. 
+* The JSON stream will be decoded to an array and copied to $_POST.
+* The further process is identically to a standard 'form submit'.
+* There should be no `id` given or `id=0`.
+
+PUT - update
+------------
+
+Example:
+
+   curl -X PUT "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson/1" -d '{"name":"Miller","firstname":"Jonni"}'
+
+Details:
+
+* The data has to be JSON encoded transferred to the REST API. 
+* The JSON stream will be decoded to an array and copied to $_POST.
+* The further process is identically to a standard 'form submit'.
+* There have to be an `id>0`.
+
+Delete
+------
+
+Example:
+
+   curl -X DELETE "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson/1" 
+
+Details:
+
+* The data has to be JSON encoded transferred to the REST API. 
+* The JSON stream will be decoded to an array and copied to $_POST.
+* The further process is identically to a standard 'form submit'.
+* There have to be an `id>0`.
+
+Header Token Authorization 
+==========================
+
+Example:
+
+    curl -X GET -H 'Authorization: Token token="mySuperSecretToken"' "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson/"
+
+Static token
+------------
+    
+Per form configure `form.parameter.restToken=mySuperSecretToken`.
+
+Dynamic token
+-------------
+
+The client supplied authorization token is available via the client store: `{{Authorization:C:alnumx}}`.
+
+Take the Client token and check if it saved in a table with all user token:   
+ 
+    form.parameter.restToken={{SELECT a.token FROM Auth AS a WHERE a.token='{{Authorization:C:alnumx}}' }}
+
+DEBUG
+=====
+
+Append the GET variable `?XDEBUG_SESSION_START=1`
+
+Example: 
+
+    curl -X POST "http://localhost/qfq/typo3conf/ext/qfq/Source/api/rest.php/restPerson?XDEBUG_SESSION_START=1" -d '{"name":"Miller","firstname":"Jonni"}'
+
+PhpStorm with activated debugger will stop at any breakpoint and 'stepping' through the code is possible.
diff --git a/doc/T3_DOC.md b/doc/T3_DOC.md
index b2a4f5d72775487ff1ff97c1537e63f0b6181d82..2a55a8858bfd844ee45f3afa683a1f1ed4b43697 100644
--- a/doc/T3_DOC.md
+++ b/doc/T3_DOC.md
@@ -1,4 +1,5 @@
-# T3 Documentation
+T3 Documentation
+================
 
 * QFQ Documentation: https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Index.html
 * Github Extension Repo to build project documentation: https://github.com/T3DocumentationStarter/Public-Info-053
@@ -6,16 +7,21 @@
 
   * https://github.com/T3DocumentationStarter/Public-Info-001/issues/4
   
-# Manual trigger of documentation rebuild:
+Manual trigger of documentation rebuild
+---------------------------------------
 
 * Open: https://docs.typo3.org/~mbless/github.com/T3DocumentationStarter/Public-Info-053.git.make/request_rebuild.php
 * For a few seconds a file 'REBUILD_REQUESTED' appears.
 * After the file disappeared, the documentation should be updated.
 
-# Log errors
+Log errors
+----------
+
 Check:  
 * https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/stable/_buildinfo/  
 * https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/latest/_buildinfo//PDFPROJECT.log.txt
 
-Rebuild: 
-https://docs.typo3.org/~mbless/github.com/T3DocumentationStarter/Public-Info-039.git.make/request_rebuild.php
+Best Practice T3 reST
+---------------------
+
+* https://docs.typo3.org/typo3cms/drafts/github/xperseguers/RstPrimer/
\ No newline at end of file
diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst
index 18e29261a907497d0d71fe41a5134d834184f98b..9bbe84c2b78993825faf2f02f3257444dc2fc11e 100644
--- a/extension/Documentation/Manual.rst
+++ b/extension/Documentation/Manual.rst
@@ -3,15 +3,26 @@
 .. ==
 ..  --
 ..   ^^
-..    ''
+..    ""
 ..     ;;
 ..      ,,
 ..
 .. --------------------------------------------------
 .. Best Practice T3 reST  https://docs.typo3.org/typo3cms/drafts/github/xperseguers/RstPrimer/
-.. External Links: `Bootstrap <http://getbootstrap.com/>`_:
+.. Italic *italic*are part of key or value
+.. Bold **bold**
+.. Code ``text``
+.. 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::
+.. Color:   (grey)       (orange)           (green)      (red)
+..
+.. Definition:
+.. some text becomes strong (only one line)
+..      description has to indented
+
 .. -*- coding: utf-8 -*- with BOM.
 
 
@@ -48,31 +59,17 @@ Preparation
 Report & Form
 ^^^^^^^^^^^^^
 
-In PHP 5.x the QFQ extension needs  the PHP MySQL native driver. The following functions are used and are only available with the
-native driver (see also: http://dev.mysql.com/downloads/connector/php-mysqlnd/):
-
-* mysqli::get_result (important),
-* mysqli::fetch_all (nice to use)
-
-To normalize UTF8 input, the *php5-intl* resp. *php7.0-intl* package is needed by
+To normalize UTF8 input, *php-intl* package is needed by
 
 * normalizer::normalize()
 
 For the `download`_ function, the programs `pdftk` and `file` are necessary to concatenate PDF files.
 
-Preparation for Ubuntu 14.04::
-
-	sudo apt-get install php5-mysqlnd php5-intl
-	sudo apt-get install pdftk file                  # for file upload and PDF
-	sudo apt-get install inkscape imagemagick     # to render thumbnails
-	sudo php5enmod mysqlnd
-	sudo service apache2 restart
-
-Preparation for Ubuntu 16.04::
+Preparation for Ubuntu::
 
-	sudo apt install php7.0-intl
-	sudo apt install pdftk libxrender1 file pdf2svg  # for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
-	sudo apt install inkscape imagemagick            # to render thumbnails
+  sudo apt install php-intl
+  sudo apt install pdftk libxrender1 file pdf2svg  # for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
+  sudo apt install inkscape imagemagick            # to render thumbnails
 
 .. _wkhtml:
 
@@ -89,58 +86,64 @@ The program is not included in QFQ and has to be manually installed.
   * The current version 0.12.4 might have trouble with https connections. Version 0.12.5-dev (github master branch)
     seems more reliable. Please contact the QFQ authors if you need a compiled Ubuntu version of wkhtmltopdf.
 
-In `configuration`_ specify the: ::
+In `configuration`_ specify the::
 
-    cmdWkhtmltopdf=/opt/wkhtmltox/bin/wkhtmltopdf`.
-    baseUrl=http://www.example.com/`.
+    config.cmdWkhtmltopdf=/opt/wkhtmltox/bin/wkhtmltopdf`.
+    config.baseUrl=http://www.example.com/`.
 
 If wkhtml has been compiled with dedicated libraries (not part of LD_LIBRARY_PATH), specify the LD_LIBRARY_PATH together
 with the path-filename: ::
 
     cmdWkhtmltopdf=LD_LIBRARY_PATH=/opt/wkhtmltox/lib /opt/wkhtmltox/bin/wkhtmltopdf
 
-**Important**: To access FE_GROUP protected pages or content, it's necessary to disable the `[FE][lockIP]` check! `wkhtml`
-will access the Typo3 page locally (localhost) and that IP address is different from the client (=user) IP.
+.. important::
+
+    To access FE_GROUP protected pages or content, it's necessary to disable the `[FE][lockIP]` check! `wkhtml`
+    will access the Typo3 page locally (localhost) and that IP address is different from the client (=user) IP.
 
 Configure via Typo3 Installtool `All configuration > $TYPO3_CONF_VARS['FE']`: ::
 
    [FE][lockIP] = 0
 
-**Warning**: this disables an important anti-'session hijacking' protection. The security level of the whole installation
-will be *lowered*! Again, this is only needed if `wkhtml` needs access to FE_GROUP protected pages & content. As an
-alternative to lower the security level, create a separated page subtree which is only accessible (configured via
-Typoscript) from specific IPs **or** if a FE-User is logged in.
+.. warning::
+
+    ``[FE][lockIP] = 0`` disables an important anti-'session hijacking' protection. The security level of the whole installation
+    will be *lowered*! Again, this is only needed if `wkhtml` needs access to FE_GROUP protected pages & content. As an
+    alternative to lower the security level, create a separated page subtree which is only accessible (configured via
+    Typoscript) from specific IPs **or** if a FE-User is logged in.
 
 If there are problems with converting/downloading FE_GROUP protected pages, check `SHOW_DEBUG_INFO = download` to debug.
 
-**Important**: Converting HTML to PDF gives no error message but RC=-1? Check carefully all includes of CSS, JS, images
-and so on! Typically some of them fails to load and wkhtml stops running!
+.. note::
+
+    Converting HTML to PDF gives no error message but RC=-1? Check carefully all includes of CSS, JS, images
+    and so on! Typically some of them fails to load and wkhtml stops running!
 
 
 HTML to PDF conversion
-''''''''''''''''''''''
+""""""""""""""""""""""
 
 `wkhtmltopdf` converts a website (local or remote) to a (multi)-page PDF file. It's mainly used in `download`_.
 
 Print
-'''''
+"""""
 
 Different browser prints the same page in different variations. To prevent this, QFQ implements a small PHP wrapper
 `print.php` with uses `wkhtmltopdf` to convert HTML to PDF.
 
 Provide a `print this page`-link (replace 'current pageId' )::
 
-	<a href="typo3conf/ext/qfq/Source/api/print.php?id={current pageId}">Print this page</a>
+  <a href="typo3conf/ext/qfq/Source/api/print.php?id={current pageId}">Print this page</a>
 
 Any parameter specified after `print.php` will be delivered to `wkhtmltopdf` as part of the URL.
 
 Typoscript code to implement a print link on every page::
 
-	10 = TEXT
-	10 {
-		wrap = <a href="typo3conf/ext/qfq/Source/api/print.php?id=|&type=99"><span class="glyphicon glyphicon-print" aria-hidden="true"></span> Printview</a>
-		data = page:uid
-	}
+  10 = TEXT
+  10 {
+    wrap = <a href="typo3conf/ext/qfq/Source/api/print.php?id=|&type=99"><span class="glyphicon glyphicon-print" aria-hidden="true"></span> Printview</a>
+    data = page:uid
+  }
 
 Send Email
 ^^^^^^^^^^
@@ -236,7 +239,7 @@ Setup a *report* to manage all *forms*:
 * Create a Typo3 page.
 * Set the 'URL Alias' to `form` (default) or the individual defined value in parameter `editFormPage` (configuration_).
 * Insert a content record of type *qfq*.
-* In the bodytext insert the following code: ::
+* In the bodytext insert the following code::
 
     # If there is a form given by SIP: show
     form={{form:SE}}
@@ -259,7 +262,7 @@ Setup a *report* to manage all *forms*:
         10 {
             # All forms
             sql = SELECT CONCAT('p:{{pageAlias:T}}&form=form&r=', f.id) as _pagee
-                         , f.id, f.name, f.title, f.tableName
+                         , f.id, f.name, f.title AS '_striptags', f.tableName
                          , CONCAT('U:form=form&r=', f.id) as _paged
                       FROM Form AS f
                       ORDER BY f.name
@@ -278,7 +281,7 @@ Installation: Check List
 * Protect the directory `<T3 installation>/fileadmin/protected` in Apache against direct file access.
 
   * `<T3 installation>/fileadmin/protected/` should be used for confidential (uploaded / generated) data.
-  * `<T3 installation>/fileadmin/protected/log/...` is the default place for QFQ logfiles.
+  * `<T3 installation>/fileadmin/protected/log/...` is the default place for QFQ log files.
 
 * Protect the directory `<T3 installation>/fileadmin` in Apache to not execute PHP Scripts - malicious uploads won't be executed.
 * Setup a log rotation rule for `sqlLog`.
@@ -342,6 +345,7 @@ Example: *typo3conf/config.qfq.php*: ::
 Extension Manager: QFQ Configuration
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | Keyword                           | Default / Example                                     | Description                                                                |
 +===================================+=======================================================+============================================================================+
@@ -384,7 +388,7 @@ Extension Manager: QFQ Configuration
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | formSubmitLogMode                 | all                                                   | | *all*: every form submission will be logged.                             |
 |                                   |                                                       | | *none*: no logging.                                                      |
-|                                   |                                                       | | See `Form Submit Log page`_ for example QFQ code to display the log.     |
+|                                   |                                                       | | See `form-submit-log-page`_ for example QFQ code to display the log.     |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | redirectAllMailTo                 | john@doe.com                                          | If set, redirect all QFQ generated mails (Form, Report) to the specified.  |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
@@ -433,6 +437,8 @@ Extension Manager: QFQ Configuration
 | securityGetMaxLength              | 50                                                    | GET vars longer than 'x' chars triggers an `attack-recognized`.            |
 |                                   |                                                       | `ExceptionMaxLength`_.                                                     |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
+| securityFailedAuthDelay           | 3                                                     | If authorization fails, sleep 'x' seconds before answering the request.    |
++-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | Form-Config                                                                                                                                                            |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | recordLockTimeoutSeconds          | 900                                                   | Timeout for record locking. After this time, a record will be replaced.    |
@@ -482,11 +488,11 @@ Extension Manager: QFQ Configuration
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | Form-Language                                                                                                                                                          |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| formLanguage[ABCD]Id              | -  E.g.: 1                                            | In Typo3 configured pageLanguage id. The number after the 'L' parameter.   |
+| formLanguage[ABCD]Id              | E.g.: 1                                               | In Typo3 configured pageLanguage id. The number after the 'L' parameter.   |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| formLanguage[ABCD]Label           | -  E.G.: english                                      | Label shown in *Form editor*, on the 'basic' tab.                          |
+| formLanguage[ABCD]Label           | E.G.: english                                         | Label shown in *Form editor*, on the 'basic' tab.                          |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| saveButtonText                    | -                                                     | Text on the form save button. Typically none.                              |
+| saveButtonText                    |                                                       | Text on the form save button. Typically none.                              |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | saveButtonTooltip                 | Save                                                  | Tooltip on the form save button.                                           |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
@@ -496,7 +502,7 @@ Extension Manager: QFQ Configuration
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | saveButtonGlyphIcon               | glyphicon-ok                                          | Icon for the form save button.                                             |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| closeButtonText                   | -                                                     | Text on the form close button. Typically none.                             |
+| closeButtonText                   |                                                       | Text on the form close button. Typically none.                             |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | closeButtonTooltip                | close                                                 | Tooltip on the form close button.                                          |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
@@ -504,7 +510,7 @@ Extension Manager: QFQ Configuration
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | closeButtonGlyphIcon              | glyphicon-remove                                      | Icon for the form close button.                                            |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| deleteButtonText                  | -                                                     | Text on the form delete button. Typically none.                            |
+| deleteButtonText                  |                                                       | Text on the form delete button. Typically none.                            |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | deleteButtonTooltip               | delete                                                | Tooltip on the form delete button.                                         |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
@@ -512,7 +518,7 @@ Extension Manager: QFQ Configuration
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | deleteButtonGlyphIcon             | glyphicon-trash                                       | Icon for the form delete button.                                           |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
-| newButtonText                     | -                                                     | Text on the form new button. Typically none.                               |
+| newButtonText                     |                                                       | Text on the form new button. Typically none.                               |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
 | newButtonTooltip                  | new                                                   | Tooltip on the form new button.                                            |
 +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+
@@ -582,7 +588,7 @@ error message: ::
 .. _`periodId`:
 
 periodId
-''''''''
+""""""""
 
 This is
 
@@ -599,7 +605,7 @@ Websites, delivering semester data, school year schedules, or any other type or
 
 In configuration_: ::
 
-	fillStoreSystemBySql1: SELECT id AS periodId FROM Period WHERE start<=NOW() ORDER BY start DESC LIMIT 1
+  fillStoreSystemBySql1: SELECT id AS periodId FROM Period WHERE start<=NOW() ORDER BY start DESC LIMIT 1
 
 a variable 'periodId' will automatically computed and filled in STORE SYSTEM. Access it via `{{periodId:Y0}}`.
 To get the name and current period: ::
@@ -608,7 +614,8 @@ To get the name and current period: ::
 
 Typically, it's necessary to offer a 'previous' / 'next' link. In this example, the STORE SIP holds the new periodId: ::
 
-  SELECT CONCAT('p:{{pageAlias:T}}&periodId=', {{periodId:SY0}}-1, '|Next') AS _page, ' ', name, ' ', CONCAT('p:{{pageAlias:T}}&periodId=', {{periodId:SY0}}+1, '|Next') AS _page FROM Period AS s WHERE s.id={{periodId:SY0}}
+  SELECT CONCAT('p:{{pageAlias:T}}&periodId=', {{periodId:SY0}}-1, '|Next') AS _page, ' ', name, ' ',
+    CONCAT('p:{{pageAlias:T}}&periodId=', {{periodId:SY0}}+1, '|Next') AS _page FROM Period AS s WHERE s.id={{periodId:SY0}}
 
 Take care for minimum and maximum indexes (do not render the links if out of range).
 
@@ -821,9 +828,9 @@ System tables
 | Split         | Persistent | Data       |
 +---------------+------------+------------+
 
-See `Mail Log page`_ and `Form Submit Log page`_ for some Frontend views for these tables.
+See `mail-log-page`_ and `form-submit-log-page`_ for some Frontend views for these tables.
 
-* Check Bug #5459 - support of system tables in different DBs not supported.
+* Check Bug #5459 / support of system tables in different DBs not supported.
 
 .. _`multi-database`:
 
@@ -831,14 +838,14 @@ Multi Database
 ^^^^^^^^^^^^^^
 
 Base: T3 & QFQ
-''''''''''''''
+""""""""""""""
 
 QFQ typically interacts with one database, the QFQ database. The database used by Typo3 is typically a separate one.
 Theoretically it might be the same (never tested), but it's strongly recommended to use a separated QFQ database to have
 no problems on Typo3 updates and to have a clean separation between Typo3 and QFQ.
 
 QFQ: System & Data
-''''''''''''''''''
+""""""""""""""""""
 
 QFQ itself can be separated in 'QFQ system' (see `system-tables`_) and 'QFQ data' databases (even more than one are
 possible). The 'QFQ system' stores the forms, record locking, log tables and so on - `QFQ data` is for the rest.
@@ -846,7 +853,7 @@ possible). The 'QFQ system' stores the forms, record locking, log tables and so
 A `Multi Database` setup is given, if 'QFQ system' is different from 'QFQ data'.
 
 Data: Data1, Data2, ..., Data n
-'''''''''''''''''''''''''''''''
+"""""""""""""""""""""""""""""""
 
 Every database needs to be configured via `configuration`_ with it's own `index`.
 
@@ -920,7 +927,7 @@ Debug
 SQL Logging
 -----------
 
-configuration_
+Setup in configuration_
 
 .. _SQL_LOG:
 
@@ -968,14 +975,14 @@ configuration_
   * *download*:
 
     * During a download (especially by using wkhtml), temporary files are not deleted automatically. Also the
-      `wkhtmltopdf` and `pdftk` commandlines will be logged to `SQL_LOG`_. Use this only to debug problems on download.
+      ``wkhtmltopdf`` and ``pdftk`` commandlines will be logged to `SQL_LOG`_. Use this only to debug problems on download.
 
 .. _REDIRECT_ALL_MAIL_TO:
 
 Redirect all mail to (catch all)
 --------------------------------
 
-configuration_
+Setup in configuration_
 
 * *redirectAllMailTo=john@doe.com*
 
@@ -988,13 +995,13 @@ configuration_
     * Clear 'CC' and 'Bcc'
     * Write a note and the original configured receiver at the top of the email body.
 
-_`Mail Log page`
+_`mail-log-page`
 
 Mail Log page
 -------------
 
 For debugging purposes you may like to add a Mail Log page in the frontend.
-The following QFQ code could be used for that purpose (put it in a QFQ PageContent element): ::
+The following QFQ code could be used for that purpose (put it in a QFQ PageContent element)::
 
     # Page parameters
     1.sql = SELECT @grId := '{{grId:C0:digit}}' AS _grId
@@ -1006,7 +1013,8 @@ The following QFQ code could be used for that purpose (put it in a QFQ PageConte
                FROM gGroup AS gr
                INNER JOIN MailLog AS ml ON ml.grId = gr.id
                GROUP BY gr.id
-      head = <form onchange='this.submit();' class='form-inline'><input type='hidden' name='id' value='{{pageAlias:T0}}'>Filter By Group: <select name='grId' class='form-control'><option value=''></option>
+      head = <form onchange='this.submit();' class='form-inline'><input type='hidden' name='id' value='{{pageAlias:T0}}'>
+               Filter By Group: <select name='grId' class='form-control'><option value=''></option>
       rbeg = <option value='
       rend = </option>
       tail = </select>
@@ -1022,23 +1030,26 @@ The following QFQ code could be used for that purpose (put it in a QFQ PageConte
       sql = SELECT id, '</td><td>', grId, '</td><td>', xId, '</td><td>',
                 REPLACE(receiver, ',', '<br>'), '</td><td>', REPLACE(sender, ',', '<br>'), '</td><td>',
                 DATE_FORMAT(modified, '%d.%m.%Y<br>%H:%i:%s'), '</td><td style="word-break:break-word;">',
-                CONCAT('<b>', subject, '</b><br>', IF(@summary = 'true', CONCAT(SUBSTR(body, 1, LEAST(IF(INSTR(body, '\n') = 0, 50, INSTR(body, '\n')), IF(INSTR(body, '<br>') = 0, 50, INSTR(body, '<br>')))-1), ' ...'), CONCAT('<br>', REPLACE(body, '\n', '<br>'))) )
+                CONCAT('<b>', subject, '</b><br>', IF(@summary = 'true', CONCAT(SUBSTR(body, 1,
+                       LEAST(IF(INSTR(body, '\n') = 0, 50, INSTR(body, '\n')), IF(INSTR(body, '<br>') = 0, 50,
+                       INSTR(body, '<br>')))-1), ' ...'), CONCAT('<br>', REPLACE(body, '\n', '<br>'))) )
               FROM MailLog WHERE (grId = @grId OR @grId = 0)
               ORDER BY modified DESC
               LIMIT 100
-      head = <table class="table table-condensed table-hover"><tr><th>Id</th><th>grId</th><th>xId</th><th>To</th><th>From</th><th>Date</th><th>E-Mail</th></tr>
+      head = <table class="table table-condensed table-hover"><tr>
+                 <th>Id</th><th>grId</th><th>xId</th><th>To</th><th>From</th><th>Date</th><th>E-Mail</th></tr>
       tail = </table>
       rbeg = <tr><td>
       rend = </td></tr>
     }
 
-_`Form Submit Log page`
+_`form-submit-log-page`
 
 Form Submit Log page
 --------------------
 
 For debugging purposes you may like to add a Form Submit Log page in the frontend.
-The following QFQ code could be used for that purpose (put it in a QFQ PageContent element): ::
+The following QFQ code could be used for that purpose (put it in a QFQ PageContent element)::
 
     # Filters
     20.shead = <form onchange='this.submit()' class='form-inline'><input type='hidden' name='id' value='{{pageAlias:T0}}'>
@@ -1221,11 +1232,11 @@ Rules for CheckType Auto (by priority):
 
 * TypeAheadSQL or TypeAheadLDAP defined: **alnumx**
 * Table definition
-	* integer type: **digit**
-	* floating point number: **numerical**
+  * integer type: **digit**
+  * floating point number: **numerical**
 * FE Type
-	* 'password', 'note': **all**
-	* 'editor', 'text' and encode = 'specialchar': **all**
+  * 'password', 'note': **all**
+  * 'editor', 'text' and encode = 'specialchar': **all**
 * None of the above: **alnumx**
 
 
@@ -1249,16 +1260,16 @@ manipulate FE user passwords via QFQ. See `setFeUserPassword`_
 
 The following `escape` and `hashing` types are available:
 
-	* 'm' - `real_escape_string() <http://php.net/manual/en/mysqli.real-escape-string.php>`_ (m = mysql)
-	* 'l' - LDAP search filter values: `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_FILTER).
-	* 'L' - LDAP DN values. `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_DN).
-	* 's' - Single ticks ' will be escaped against \\'.
-	* 'd' - double ticks " will be escaped against \\".
-	* 'C' - colon ':' will be escaped against \\:.
-	* 'c' - config - the escape type configured in `configuration`_.
-	* 'p' - password hashing: depends on the hashing type in the Typo3 installation, includes salting if configured.
-	* ''  - the escape type configured in `configuration`_.
-	* '-' - no escaping.
+  * 'm' - `real_escape_string() <http://php.net/manual/en/mysqli.real-escape-string.php>`_ (m = mysql)
+  * 'l' - LDAP search filter values: `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_FILTER).
+  * 'L' - LDAP DN values. `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_DN).
+  * 's' - Single ticks ' will be escaped against \\'.
+  * 'd' - double ticks " will be escaped against \\".
+  * 'C' - colon ':' will be escaped against \\:.
+  * 'c' - config - the escape type configured in `configuration`_.
+  * 'p' - password hashing: depends on the hashing type in the Typo3 installation, includes salting if configured.
+  * ''  - the escape type configured in `configuration`_.
+  * '-' - no escaping.
 
 * The `escape` type is defined by the fourth parameter of the variable. E.g.: `{{name:FE:alnumx:m}}` (m = mysql).
 * It's possible to combine different `escape` types, they will be processed in the order given. E.g. `{{name:FE:alnumx:Ls}}` (L, s).
@@ -1290,8 +1301,8 @@ If a value violates the sanitize class, instead of content on of the following t
  * 'c' - The violated class will be set as content, surrounded by '!!'. E.g. '!!digit!!'. This is the default.
  * 'e' - Instead of the value an empty string will be set as content.
  * '0' - Instead of the value the string '0' will be set as content.
- * 'custom text ...' - Instead of the value the custom text will be set as content. If the text contains a ':', that one needs to
-    be escaped by '\'. Check `variable-escape`_ qualifier 'C' to let QFQ do the colon escaping.
+ * 'custom text ...' - Instead of the value the custom text will be set as content. If the text contains a ':', that one
+   needs to be escaped by '\'. Check `variable-escape`_ qualifier 'C' to let QFQ do the colon escaping.
 
 .. _`sql-variables`:
 
@@ -1311,13 +1322,13 @@ SQL variables
 * The number of variables inside an input field or a SQL statement is not limited.
 
 Result: string
-''''''''''''''
+""""""""""""""
 
 A result of a SQL statement will be imploded over all: concat all columns of a row, concat all rows - there is no
 glue string.
 
 Result: row
-'''''''''''
+"""""""""""
 
 A few functions needs more than a returned string, instead separate columns are necessary. To indicate an array
 result, specify those with an '!': ::
@@ -1328,12 +1339,12 @@ This manual will specify the individual QFQ elements, who needs an array instead
 a string where an array is needed and vice versa.
 
 Database index
-''''''''''''''
+""""""""""""""
 
 To access different databases in a `multi-database`_  setup, the database index can be specified after the opening curly
 braces. ::
 
-	{{[1]SELECT ... }}
+  {{[1]SELECT ... }}
 
 For using the indexData and indexQfq (configuration_), it's a good practice to specify the variable name
 instead of the numeric index. ::
@@ -1344,7 +1355,7 @@ If no dbIndex is given, `{{indexData:Y}}` is used.
 
 
 Example
-'''''''
+"""""""
 
 ::
 
@@ -1358,7 +1369,7 @@ Example
 .. _`row-column-variables`:
 
 Row column variables
-^^^^^^^^^^^^^^^^^^^^
+""""""""""""""""""""
 
 Syntax:  *{{<level>.<column>}}*
 
@@ -1385,11 +1396,11 @@ Link column variables
 These variables return a link, completely rendered in HTML. The syntax and all features of `column-link`_ are available.
 The following code will render a 'new person' button::
 
-	{{p:form&form=Person|s|N|t:new person AS link}}
+  {{p:form&form=Person|s|N|t:new person AS link}}
 
 For better reading, the format string might be wrapped in single or double quotes (this is optional): ::
 
-	{{"p:form&form=Person|s|N|t:new person" AS link}}
+  {{"p:form&form=Person|s|N|t:new person" AS link}}
 
 These variables are especially helpful in:
 
@@ -1502,16 +1513,20 @@ If you only have access to `.htaccess`, create a file `<site path>/fileadmin/pro
          Require all denied
     </IfModule>
 
-**Important**: all QFQ uploads should save files in or below such a directory.
+.. important::
+
+    All QFQ uploads should save files only in/below such a protected directory.
 
 To offer download of those files, use the reserved column name '_download' (see `download`_) or variants.
 
-**Important**: To protect the installation against executing of uploaded malicious script code, disable PHP for the final
-upload directory. E.g. `fileadmin` (Apache): ::
+.. important::
+
+    To protect the installation against executing of uploaded malicious script code, disable PHP for the final
+    upload directory. E.g. `fileadmin` (Apache)::
 
-		<Directory "/var/www/html/fileadmin">
-			php_admin_flag engine Off
-		</Directory>
+        <Directory "/var/www/html/fileadmin">
+          php_admin_flag engine Off
+        </Directory>
 
 This is in general a good security improvement for directories with user supplied content.
 
@@ -1685,7 +1700,9 @@ Store: *CLIENT* - C
  +=========================+==========================================================================================================================================+
  | s                       | =SIP                                                                                                                                     |
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
- | r                       | record id. Typically stored in SIP, rarely specified on the URL                                                                          |
+ | r                       | record id. Only if specified as GET parameter - typically stored in SIP (=STORE_SIP)                                                     |
+ +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
+ | form                    | Name of form to load. Only if specified as GET parameter - typically stored in SIP (=STORE_SIP)                                          |
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
  | HTTP_HOST               | current HTTP HOST                                                                                                                        |
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
@@ -1693,7 +1710,7 @@ Store: *CLIENT* - C
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
  | '$_SERVER[*]'           | All other variables accessible by *$_SERVER[]*. Only the often used have a pre-defined sanitize class.                                   |
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
- | form                    | Unique name of current form                                                                                                              |
+ | Authorization           | Value of the HTTP Header 'Authorization'. This is typically not set. Mostly used for authentication of REST requests                     |
  +-------------------------+------------------------------------------------------------------------------------------------------------------------------------------+
 
 .. _STORE_TYPO3:
@@ -1859,13 +1876,13 @@ To decide which Parameter should be placed on *Form.parameter* and which on *For
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 | ldapUseBindCredentials      | ldapUseBindCredentials=1         | Use LDAP_1_* credentials from config-qfq-php_ for ldap_bind() | x    | x           | TA, FSL  |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
-| typeAheadLdap               | -                                | Enable LDAP as 'Typeahead' data source                        |      | x           | TA       |
+| typeAheadLdap               |                                  | Enable LDAP as 'Typeahead' data source                        |      | x           | TA       |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 | typeAheadLdapSearch         | `(|(cn=*?*)(mail=*?*))`          | Regular LDAP search expression, returns upto typeAheadLimit   | x    | x           | TA       |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 | typeAheadLdapSearchPrefetch | `(mail=?)`                       | Regular LDAP search expression, typically return one record   | x    | x           | TA       |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
-| typeAheadLdapSearchPerToken | -                                | Split search value in token and OR-combine every search with  | x    | x           | TA       |
+| typeAheadLdapSearchPerToken |                                  | Split search value in token and OR-combine every search with  | x    | x           | TA       |
 |                             |                                  |  the individual tokens                                        |      |             |          |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 | typeAheadLdapValuePrintf    | `'%s / %s', cn, mail`            | Custom format to display attributes, as `value`               | x    | x           | TA       |
@@ -1878,7 +1895,7 @@ To decide which Parameter should be placed on *Form.parameter* and which on *For
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 | typeAheadMinLength          | 2 (default)                      | Minimum number of characters before starting the search       | x    | x           | TA       |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
-| fillStoreLdap               | -                                | Activate `Fill STORE LDAP` with the first retrieved record    |      | x           | FSL      |
+| fillStoreLdap               |                                  | Activate `Fill STORE LDAP` with the first retrieved record    |      | x           | FSL      |
 +-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
 
 * *typeAheadLimit*: there might be a hard limit on the server side (e.g. 100) - which can't be extended.
@@ -1997,9 +2014,11 @@ Also, the STORE LDAP remains filled, during the whole form processing time. E.g.
 name and email, it's sufficient to fire one FSL on the first FormElement Action element, and use the same values during further FormElement
 Action Elements.
 
-Important: LDAP access might slow down the *Form* processing on load, update or save! The timeout (default: 3 seconds) have
-to be multiplied by the number of accesses. E.g. a broken LDAP connection and 3 *FormElements* with *FSL*
-results to 9 seconds delay on save. Also be prepared not to receive the expected data.
+.. important::
+
+    LDAP access might slow down the *Form* processing on load, update or save! The timeout (default: 3 seconds) have
+    to be multiplied by the number of accesses. E.g. a broken LDAP connection and 3 *FormElements* with *FSL*
+    results to 9 seconds delay on save. Also be prepared not to receive the expected data.
 
 * *FormElement.parameter.fillStoreLdap* - activate the mode *Fill S* - no value is needed, the existence is sufficient.
 * *Form.parameter* or *FormElement.parameter*:
@@ -2267,7 +2286,7 @@ Forward: Save / Close
 ^^^^^^^^^^^^^^^^^^^^^
 
 Forward (=forwardMode)
-''''''''''''''''''''''
+""""""""""""""""""""""
 
 After the user presses *Save*, *Close*, *Delete* or *New*, different actions are possible where the browser redirects to.
 
@@ -2288,7 +2307,7 @@ After the user presses *Save*, *Close*, *Delete* or *New*, different actions are
 Only with `Forward` == `url` | `url-skip-history`, the definition of `Forward URL / Page` becomes active.
 
 Forward URL / Page (=forwardPage)
-'''''''''''''''''''''''''''''''''
+"""""""""""""""""""""""""""""""""
 
 Format: [<url>] or [<mode>|<url>]
 
@@ -2374,7 +2393,7 @@ Parameter
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | ldapTimeLimit               | int    | Maximum time to wait for an answer of the LDAP Server.                                                   |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| typeAheadLdap               | -      | Enable LDAP as 'Typeahead' data source.                                                                  |
+| typeAheadLdap               |        | Enable LDAP as 'Typeahead' data source.                                                                  |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | typeAheadLdapSearch         | string | Regular LDAP search expresssion. E.g.:  `(|(cn=*?*)(mail=*?*))`                                          |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
@@ -2382,7 +2401,7 @@ Parameter
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | typeAheadLdapIdPrintf       | string | Key formatting of LDAP result, per entry. E.g.: `'%s', mail`                                             |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| typeAheadLdapSearchPerToken | -      | Split search value in token and OR-combine every search with the individual tokens.                      |
+| typeAheadLdapSearchPerToken |        | Split search value in token and OR-combine every search with the individual tokens.                      |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | typeAheadLimit              | int    | Maximum number of entries. The limit is applied to the server (LDAP or SQL) and the Client.              |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
@@ -2395,7 +2414,7 @@ Parameter
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | submitButtonText            | string | Show a save button at the bottom of the form, with <submitButtonText> . See `submitButtonText`_.         |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| saveButtonActive            | -      | 0: off, 1: Make the 'save'-button active on *Form* load (instead of waiting for the first user change).  |
+| saveButtonActive            |        | 0: off, 1: Make the 'save'-button active on *Form* load (instead of waiting for the first user change).  |
 |                             |        | The save button is still 'gray' (record not dirty), but the user can click 'save'.                       |
 +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
 | saveButtonText              | string | Overwrite default from configuration_                                                                    |
@@ -2450,7 +2469,7 @@ Parameter
 .. _submitButtonText:
 
 submitButtonText
-''''''''''''''''
+""""""""""""""""
 
 If specified and non empty, display a regular submit button at the bottom of the page with the given text.
 This gives the form a ordinary HTML-form look'n' feel. With this option, the standard buttons on the top right border
@@ -2460,7 +2479,7 @@ should be hided to not confuse the user.
 * Default: Empty
 
 class
-'''''
+"""""
 
 * Optional.
 * Default: `container`
@@ -2472,7 +2491,7 @@ class
   * Expand the form over the whole area: `container-fluid`
 
 classPill
-'''''''''
+"""""""""
 
 * Optional.
 * Default: `qfq-color-grey-1`
@@ -2484,7 +2503,7 @@ classPill
 * `classPill` is only visible on forms with container elements of type 'Pill'.
 
 classBody
-'''''''''
+"""""""""
 
 * Optional.
 * Default: `qfq-color-grey-2`
@@ -2499,7 +2518,7 @@ classBody
   `qfq-color-blue-1` (dark), `qfq-color-blue-2`. (light)
 
 extraDeleteForm
-'''''''''''''''
+"""""""""""""""
 
 Depending on the database definition, it might be necessary to delete the primary record *and* corresponding slave records.
 To not repeat such 'slave record delete definition', an 'extraDeleteForm' can be specified. If the user opens a record
@@ -2515,7 +2534,7 @@ See also: `delete-record`_.
 .. _form-mode-global:
 
 Form mode global
-''''''''''''''''
+""""""""""""""""
 
 The Form global mode `mode` is given by default with `{{formModeGlobal:SE:alnumx}}`.
 
@@ -2536,10 +2555,10 @@ Optional it might be defined via *Form.parameter* ::
 
 The following shows the same *Form* in the `regular`, `readonly` and `requiredOff` mode::
 
-	10.sql = SELECT CONCAT('p:{{pageAlias:T}}&form=person&r=', p.id, '|Regular') as _pagee,
-	                CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=readonly&r=', p.id, '|Readonly') as _pagee,
-	                CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=requiredOff&r=', p.id, '|Required off') as _pagee
-	                FROM Person AS p
+  10.sql = SELECT CONCAT('p:{{pageAlias:T}}&form=person&r=', p.id, '|Regular') as _pagee,
+                  CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=readonly&r=', p.id, '|Readonly') as _pagee,
+                  CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=requiredOff&r=', p.id, '|Required off') as _pagee
+                  FROM Person AS p
 
 ..
 
@@ -2660,14 +2679,14 @@ The placeholder `%d` can also be used in the *FormElement.label*
 Example of styling the Add/ Delete Button: :ref:`example_class_template_group`
 
 Column: primary record
-''''''''''''''''''''''
+""""""""""""""""""""""
 
 If the columns `<name>%d` are real columns on the primary table, saving and delete (=empty string) are done automatically.
 E.g. if there are up to five elements `grade1, ..., grade5` and the user inputs only the first three, the remaining will be set
 to an empty string.
 
 Column: non primary record
-''''''''''''''''''''''''''
+""""""""""""""""""""""""""
 
 Additional logic is required to load, update, insert and/or delete slave records.
 
@@ -2687,11 +2706,11 @@ Add an *action* record, type='afterSave', and assign the record to the given *te
 
 In the parameter field define: ::
 
-		slaveId = {{SELECT id FROM Address WHERE personId={{id}} ORDER BY id LIMIT %D,1}}
-		sqlHonorFormElements = city%d, street%d
-		sqlUpdate = {{UPDATE Address SET city='{{city%d:FE:alnumx:s}}', street='{{street%d:FE:alnumx:s}}'  WHERE id={{slaveId}} LIMIT 1}}
-		sqlInsert = {{INSERT INTO Address (`personId`, `city`, `street`) VALUES ({{id}}, '{{city%d:FE:alnumx:s}}' , '{{street%d:FE:alnumx:s}}') }}
-		sqlDelete = {{DELETE FROM Address WHERE id={{slaveId}} LIMIT 1}}
+    slaveId = {{SELECT id FROM Address WHERE personId={{id}} ORDER BY id LIMIT %D,1}}
+    sqlHonorFormElements = city%d, street%d
+    sqlUpdate = {{UPDATE Address SET city='{{city%d:FE:alnumx:s}}', street='{{street%d:FE:alnumx:s}}'  WHERE id={{slaveId}} LIMIT 1}}
+    sqlInsert = {{INSERT INTO Address (`personId`, `city`, `street`) VALUES ({{id}}, '{{city%d:FE:alnumx:s}}' , '{{street%d:FE:alnumx:s}}') }}
+    sqlDelete = {{DELETE FROM Address WHERE id={{slaveId}} LIMIT 1}}
 
 The `slaveId` needs attention: the placeholder `%d` starts always at 1. The `LIMIT` directive starts at 0 - therefore
 use `%D` instead of `%d`, cause `%D` is always one below `%d` - but can **only** be used on the action element.
@@ -2808,7 +2827,7 @@ FE: 'Report' notation
 ^^^^^^^^^^^^^^^^^^^^^
 
 The FE fields 'value' and 'note' understand the `Report`_ syntax. Nested SQL queries as well as links with SIP encoding
-are possible. To distinguish between 'Form' and 'Report' syntax, the first line has to be `#!report` ::
+are possible. To distinguish between 'Form' and 'Report' syntax, the first line has to be `#!report`::
 
     #!report
 
@@ -2957,7 +2976,8 @@ See also at specific *FormElement* definitions.
  * `s/d/n`: string or date or number.
 
 Native *FormElements*
-'''''''''''''''''''''
+"""""""""""""""""""""
+
 * Like 'input', 'checkbox', ...
 
 .. _`input-option-autofocus`:
@@ -3052,7 +3072,7 @@ Checkboxes can be rendered in mode:
       * ``itemList=red,blue,orange``
       * ``itemList=1:red,2:blue,3:orange``
       * If ':' or ',' are part of key or value, it needs to escaped by '\\'.
-        E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange``
+      | E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange``
 
   * *FormElement.sql1* = ``{{!SELECT id, value FROM someTable}}``
   * *FormElement.maxlength* - vertical or horizontal alignment:
@@ -3155,7 +3175,7 @@ Type: text
 .. _`input-typeahead`:
 
 Type Ahead
-''''''''''
+""""""""""
 
 Activating `typeahead` functionality offers an instant lookup of data and displaying them to the user, while the user is
 typing, a drop-down box offers the results. As datasource the regular SQL connection or a LDAP query can be used.
@@ -3246,23 +3266,73 @@ Type: editor
 Type: annotate
 ^^^^^^^^^^^^^^
 
-The `Formelement`.type=`annotate` is a simple grafic editor which, for example, can be used to annotate images. All modifications to
-an image are saved as a JSON fabric.js data string in the current FormElement column.
+Annotate image or text. Typically the image or text has been uploaded during a previous step. The annotation will be
+saved in *FormElement.name* column of the current record. The uploaded file itself will not be modified. The annotations
+can be shown in edit or readonly mode and modified again.
+
+Two modes are available:
+
+image
+    A simple grafic editor to paint on top of the image (best by a tablet with pen or grafic tablet). The uploaded image
+    is shown in the background. All drawings are saved as a JSON fabric.js data string. Supported file types:
+    **png, svg**. PDF files can be easily divided into per page SVG files during upload - see `split-pdf-upload`_
+
+text
+    Per code line, annotation(s) can be added. Different users can add individual annotations. A user can only edit the
+    own annotations. The annotations are saved as QFQ internal JSON string.
+
+
+.. note::
+
+    Drawing with fabric.js might produce a lot data. Take care the column type/size is big enough (>=64kB).
+
+
+Image
+"""""
 
 An image, specified by `FormElement.parameter`: imageSource={{pathFileName}}, will be displayed in the background. On
 form load, both, the image and an optional already given JSON fabric.js data string, will be displayed. The image is SIP
 protected and will be loaded on demand.
 
-The original image file is not modified. The user drawings are stored in the fabric.js data string.
+**Form.parameter**
 
-* *FormElement.parameter*:
++-------------------+-----------------------+----------------------------------------------------------------------------------+
+| Attribute         | Value                 | Description                                                                      |
++===================+=======================+==================================================================================+
+| annotateType      | grafic                | *grafic|text*. Default is *grafic*. Select mode.                                 |
++-------------------+-----------------------+----------------------------------------------------------------------------------+
+| imageSource       | {{pathFileName2}}     | Background image. E.g. `fileadmin/images/scan.png`                               |
++-------------------+-----------------------+----------------------------------------------------------------------------------+
+| defaultPenColor   |  <rgb hex value>      | Pen default color, after loading the fabric element. Default is '0000FF' (blue). |
++-------------------+----------------------------------------------------------------------------------------------------------+
 
-  * *imageSource* ={{pathFileName2}} -  Background image. E.g. `fileadmin/images/scan.jpg`.
-  * *defaultPenColor* = <rgb hex value> -  Pen default color, after loading the fabric element. Default is '0000FF' (blue).
+.. note::
 
-By using the the `FormElement` `annotate`, the JS code `fabric.min.js` and `qfq.fabric.min.js` has to be included.
+    By using the the `FormElement` `annotate`, the JS code `fabric.min.js` and `qfq.fabric.min.js` has to be included.
 See setup-css-js_.
 
+Code
+""""
+
+**Form.parameter**
+
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| Attribute          | Value                 | Description                                                                      |
++====================+=======================+==================================================================================+
+| annotateType       | text                  | *grafic|text*. Default is *grafic*. Select mode.                                 |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| textSource         | <fileadmin/code.m>    | Text file to annotate.                                                           |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| annotateUserName   | <john doe>            | Will be shown at annotation line.                                                |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| annotateUserUid    | <123>                 | Will be shown at annotation line.                                                |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| annotateUserAvatar | <https://gravatar...> | Will be shown at annotation line.                                                |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+| highlight          | auto                  | off,auto,javascript,qfq,python,matlab                                            |
++--------------------+-----------------------+----------------------------------------------------------------------------------+
+
+
 Type: imageCut
 ^^^^^^^^^^^^^^
 
@@ -3309,7 +3379,7 @@ Type: radio
 
     * *itemList* = `<attribute>` E.g.: *itemList=red,blue,orange* or *itemList=1:red,2:blue,3:orange*
     * If ':' or ',' are part of key or value, it needs to escaped by '\\'.
-        E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange`
+    | E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange`
 
   3. Definition of the *enum* or *set* field (only labels, ids are not possible).
 
@@ -3373,7 +3443,7 @@ Type: select
 
     * *itemList* = `<attribute>` - E.g.: *itemList=red,blue,orange* or *itemList=1:red,2:blue:3:orange*
     * If ':' or ',' are part of key or value, it needs to escaped by '\\'.
-        E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange`
+    | E.g.: `itemList=1:red\\: (with colon),2:blue\\, (with comma),3:orange`
 
   * Definition of the *enum* or *set* field (only labels, ids are not possible).
 
@@ -3388,6 +3458,8 @@ Type: select
   * *emptyItemAtEnd*: Existence of this item inserts an empty entry at the end of the selectlist.
   * *emptyHide*: Existence of this item hides the empty entry. This is useful for e.g. Enums, which have an empty
     entry and the empty value should not be an option to be selected.
+  * *datalist*: Similar to 'typeAhead'. Enables the user to select a predefined option (sql1, itemList) or supply any
+     free text. Attention: Safari (and some other) browsers do not support this fully - https://caniuse.com/#search=datalist.
 
 .. _`subrecord-option`:
 
@@ -3480,11 +3552,11 @@ will be rendered inside the form as a HTML table.
   * *subrecordTableClass*: Optional. Default: 'table table-hover qfq-subrecord-table'. If given, the default will be
      overwritten. Example: ::
 
-	  subrecordTableClass = table table-hover qfq-subrecord-table qfq-table-50
+        subrecordTableClass = table table-hover qfq-subrecord-table qfq-table-50
 
   * Tablesorter in Subrecord:
 
-  	   subrecordTableClass = table table-hover qfq-subrecord-table tablesorter tablesorter-pager tablesorter-filter
+       subrecordTableClass = table table-hover qfq-subrecord-table tablesorter tablesorter-pager tablesorter-filter
 
   * *subrecordColumnTitleEdit*: Optional. Will be rendered as the column title for the new/edit column.
   * *subrecordColumnTitleDelete*: Optional. Will be rendered as the column title for the delete column.
@@ -3553,8 +3625,8 @@ and will be processed after saving the primary record and before any action Form
 * *FormElement.value* = `<string>` - By default, the full path of any already uploaded file is shown. To show something
   different, e.g. only the filename, define: ::
 
-	 a) {{filenameBase:V}}
-	 b) {{SELECT SUBSTRING_INDEX( '{{pathFileName:R}}', '/', -1)  }}
+   a) {{filenameBase:V}}
+   b) {{SELECT SUBSTRING_INDEX( '{{pathFileName:R}}', '/', -1)  }}
 
 See also `downloadButton`_ to offer a download of an uploaded file.
 
@@ -3623,7 +3695,7 @@ See also `downloadButton`_ to offer a download of an uploaded file.
 
   * *chmodFile* = <unix file permission mode> - e.g. `660` for owner and group read and writeable. Only the numeric mode is allowed.
   * *chmodDir* = <unix file permission mode> - e.g. `770` for owner and group read, writeable and executable. Only the
-     numeric mode is allowed. Will be applied to all new created directories.
+    numeric mode is allowed. Will be applied to all new created directories.
 
   * autoOrient: images might contain EXIF data (e.g. captured via mobile phones) incl. an orientation tag like TopLeft,
     BottomRight and so on. Web-Browser and other grafic programs often understand and respect those information and rotate
@@ -3683,7 +3755,7 @@ The maximum size is defined by the minimum of `upload_max_filesize`, `post_max_s
 In case of broken uploads, please also check `max_input_time` in php.ini.
 
 Deleting a record and the referenced file
-'''''''''''''''''''''''''''''''''''''''''
+"""""""""""""""""""""""""""""""""""""""""
 
 If the user deletes a record (e.g. pressing the delete button on a form) which contains reference(s) to files, such files
 are deleted too. Slave records, which might be also deleted through a 'delete'-form, are *not* checked for file references
@@ -3698,7 +3770,7 @@ have multiple references to a single file. Therefore this check is just a fallba
 .. _Upload simple mode:
 
 Upload simple mode
-''''''''''''''''''
+""""""""""""""""""
 
 Requires: *'upload'-FormElement.name = 'column name'* of an column in the primary table.
 
@@ -3716,7 +3788,7 @@ Multiple 'upload'-FormElements per form are possible. Each of it needs an own ta
 .. _Upload advanced mode:
 
 Upload advanced mode
-''''''''''''''''''''
+""""""""""""""""""""
 
 Requires: *'upload'-FormElement.name* is unknown as a column in the primary table.
 
@@ -3761,7 +3833,7 @@ A typical name for such an 'upload'-FormElement, to show that the name does not
 .. _split-pdf-upload:
 
 Split PDF Upload
-''''''''''''''''
+""""""""""""""""
 
 Additional to the upload, it's possible to split the uploaded file (only PDF files) into several SVG or JPEG files, one
 file per PDF page. The split is done via http://www.cityinthesky.co.uk/opensource/pdf2svg/ or Image Magick `convert`.
@@ -3831,7 +3903,7 @@ Types:
 .. _sqlValidate:
 
 Parameter: sqlValidate
-''''''''''''''''''''''
+""""""""""""""""""""""
 
   Perform checks by fireing a SQL query and expecting a predefined number of selected records.
 
@@ -3857,7 +3929,7 @@ Parameter: sqlValidate
 .. _slave-id:
 
 Parameter: slaveId
-''''''''''''''''''
+""""""""""""""""""
 
 *FormElement.parameter*:
 
@@ -3878,7 +3950,7 @@ Note:
   * After an INSERT the `last_insert_id()` becomes the *slaveId*).
 
 Parameter: sqlBefore / sqlInsert / sqlUpdate / sqlDelete / sqlAfter
-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
   * Save values of a form to different record(s), optionally on different table(s).
   * Typically useful on 'afterSave' - be careful when using it earlier, e.g. beforeLoad.
@@ -3907,7 +3979,7 @@ Parameter: sqlBefore / sqlInsert / sqlUpdate / sqlDelete / sqlAfter
 
 
 Example
-'''''''
+"""""""
 
 Situation 1: master.xId=slave.id (1:1)
 
@@ -4014,7 +4086,7 @@ from the www.example.com and ?export (with the attachment filename 'personal.pdf
   sendMailAttachmemt = F:fileadmin/file1.pdf|d:readme.pdf|C|u:http://www.example.com|p:?id=export&r=123&_sip=1|d:personal.pdf
 
 Type: paste
-'''''''''''
+"""""""""""
 
 See also `copy-form`_.
 
@@ -4051,7 +4123,7 @@ Parameter
   `pId` in the link who calls the address form. The following creates a 'new' button for an address for all persons, and
   the pId will be automatically saved in the address table: ::
 
-		SELECT CONCAT('p:{{pageAlias:T}}&form=address&r=0&pId=', p.id) AS _pagen FROM Person AS p
+    SELECT CONCAT('p:{{pageAlias:T}}&form=address&r=0&pId=', p.id) AS _pagen FROM Person AS p
 
   Such parameter, which the form expects to be in the SIP url, should be specified in Form.permitNew and/or Form.permitEdit.
   It's only a check for the webmaster, not to forgot a parameter in a SIP url.
@@ -4108,11 +4180,11 @@ Assuming the Typo3 page has the
 
 Configuration in configuration_: ::
 
-		formLanguageAId = 1
-		formLanguageALabel = English
+    formLanguageAId = 1
+    formLanguageALabel = English
 
-		formLanguageBId = 2
-		formLanguageBLabel = Spanish
+    formLanguageBId = 2
+    formLanguageBLabel = Spanish
 
 The default language is not covered in configuration_.
 
@@ -4122,31 +4194,31 @@ missing definition means 'take the default'. E.g.:
 
 * Form: 'person'
 
-	+--------------------+--------------------------+
-	| Column             | Value                    |
-	+====================+==========================+
-	| title              | Eingabe Person           |
-	+--------------------+--------------------------+
-	| languageParameterA | title=Input Person       |
-	+--------------------+--------------------------+
-	| languageParameterB | title=Persona de entrada |
-	+--------------------+--------------------------+
+  +--------------------+--------------------------+
+  | Column             | Value                    |
+  +====================+==========================+
+  | title              | Eingabe Person           |
+  +--------------------+--------------------------+
+  | languageParameterA | title=Input Person       |
+  +--------------------+--------------------------+
+  | languageParameterB | title=Persona de entrada |
+  +--------------------+--------------------------+
 
 * FormElement 'firstname' in Form 'person':
 
-	+--------------------+------------------------------------------------+
-	| Column             | Value                                          |
-	+====================+================================================+
-	| title              | Vorname                                        |
-	+--------------------+------------------------------------------------+
-	| note               | Bitte alle Vornamen erfassen                   |
-	+--------------------+------------------------------------------------+
-	| languageParameterA | | title=Firstname                              |
-	|                    | | note=Please give all firstnames              |
-	+--------------------+------------------------------------------------+
-	| languageParameterB | | title=Persona de entrada                     |
-	|                    | | note=Por favor, introduzca todos los nombres |
-	+--------------------+------------------------------------------------+
+  +--------------------+------------------------------------------------+
+  | Column             | Value                                          |
+  +====================+================================================+
+  | title              | Vorname                                        |
+  +--------------------+------------------------------------------------+
+  | note               | Bitte alle Vornamen erfassen                   |
+  +--------------------+------------------------------------------------+
+  | languageParameterA | | title=Firstname                              |
+  |                    | | note=Please give all firstnames              |
+  +--------------------+------------------------------------------------+
+  | languageParameterB | | title=Persona de entrada                     |
+  |                    | | note=Por favor, introduzca todos los nombres |
+  +--------------------+------------------------------------------------+
 
 
 The following fields are possible:
@@ -4204,7 +4276,7 @@ Examples
 * Master FormElement 'music' is a radio/enum of 'classic', 'jazz', 'pop'.
 
 Content of a select list
-''''''''''''''''''''''''
+""""""""""""""""""""""""
 
 * Slave FormElement 'interpret' is 'select'-list, depending of 'music'
 
@@ -4213,7 +4285,7 @@ Content of a select list
    sql={{!SELECT name FROM interpret WHERE music={{music:FE:alnumx}} ORDER BY name}}
 
 Show / Hide a *FormElement*
-'''''''''''''''''''''''''''
+"""""""""""""""""""""""""""
 
 * Slave 'interpret' is displayed only for 'pop'. Field 'modeSql':
 
@@ -4433,7 +4505,7 @@ Custom default value only for 'new records'
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Method 1
-''''''''
+""""""""
 
 On `Form.parameter` define a `fillStoreVar` query with a column name equal to a form field. That's all.
 
@@ -4446,7 +4518,8 @@ What we use here is the default STORE prio FSRVD. If the form loads with r=0, 'F
 If r>0, than 'F' and 'S' are empty and 'R' is filled.
 
 Method 2
-''''''''
+""""""""
+
 In the specific `FormElement` set `value={{columnName:RSE}}`. The link to the form should be rendered with
 '"...&columnName=<data>&..." AS _page'. The trick is that the STORE_RECORD is empty for new records, and therefore the
 corresponding value from STORE_SIP will be returned. Existing records will use the already saved value.
@@ -4512,7 +4585,7 @@ Requirement: new records should automatically get the highest number plus 10 for
 should not be altered.
 
 Version 1
-'''''''''
+"""""""""
 
 Compute the next 'ord' in advance in the subrecord field of the primary form. Submit that value to the new record
 via SIP parameter to the secondary form.
@@ -4533,7 +4606,7 @@ unchanged.
    `{{ord:RS0}}`.
 
 Version 2
-'''''''''
+"""""""""
 
 Compute the next 'ord' as default value direct inside the secondary form. No change is needed for the primary form.
 
@@ -4662,7 +4735,6 @@ Chart
 
   * Don't nest the HTML & JavaScript code - bad workaround, this is not human readable.
   * Select different nesting token, e.g. '<' (check the first line on the following example).
-
 ::
 
      # <
@@ -4905,9 +4977,7 @@ Table: Person
 
     * Type: text
     * Label: Name
-    * Parameter::
-
-       typeAheadSql = SELECT name FROM Person WHERE name LIKE ? OR firstName LIKE ? LIMIT 100
+    * Parameter: ``typeAheadSql = SELECT name FROM Person WHERE name LIKE ? OR firstName LIKE ? LIMIT 100``
 
 Typeahead: LDAP with additional values
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -4948,8 +5018,8 @@ Table: Person
 
        # Typeahead
        typeAheadLdapSearch = (|(cn=*?*)(mail=*?*))
-       typeAheadLdapValuePrintf	‘%s / %s’, cn, email
-       typeAheadLdapIdPrintf	‘%s’, email
+       typeAheadLdapValuePrintf ‘%s / %s’, cn, email
+       typeAheadLdapIdPrintf  ‘%s’, email
 
        # dynamicUpdate: show note
        fillStoreLdap
@@ -5017,8 +5087,7 @@ To display a report on any given TYPO3 page, create a content element of type 'Q
 A simple example
 ^^^^^^^^^^^^^^^^
 
-Assume that the database has a table person with columns firstName and lastName. To create a simple list of all persons, we can do the following:
-::
+Assume that the database has a table person with columns firstName and lastName. To create a simple list of all persons, we can do the following::
 
     10.sql = SELECT id AS pId, CONCAT(firstName, " ", lastName, " ") AS name FROM person
 
@@ -5026,29 +5095,23 @@ The '10' indicates a *root level* of the report (see section `Structure`_). The
 for the specific level. When the query is executed, it will return a result having one single column name containing first and last name
 separated by a space character.
 
-The HTML output, displayed on the page, resulting from only this definition, could look as follows:
-::
+The HTML output, displayed on the page, resulting from only this definition, could look as follows::
 
     John DoeJane MillerFrank Star
 
-..
 
 I.e., QFQ will simply output the content of the SQL result row after row for each single level.
 
 However, we can modify (wrap) the output by setting the values of various keys for each level: 10.rsep=<br/> for example
-tells QFQ to separate the rows of the result by a HTML-line break. The final result in this case is:
-
-::
+tells QFQ to separate the rows of the result by a HTML-line break. The final result in this case is::
 
     10.sql = SELECT id AS personId, CONCAT(firstName, " ", lastName, " ") AS name FROM person
     10.rsep = <br>
 
-HTML output:
-::
+HTML output::
 
     John Doe<br>Jane Miller<br>Frank Star
 
-..
 
 .. _`syntax-of-report`:
 
@@ -5100,9 +5163,9 @@ Only SELECT and SHOW queries will fire subqueries.
 
 Processing of the resulting rows and columns:
 
-	* In general, all columns of all rows will be printed out sequentially.
-	* On a per column base, printing of columns can be suppressed by starting the column name with an underscore '_'. E.g.
-	  `SELECT id AS _id`.
+  * In general, all columns of all rows will be printed out sequentially.
+  * On a per column base, printing of columns can be suppressed by starting the column name with an underscore '_'. E.g.
+    `SELECT id AS _id`.
 
      This might be useful to store values, which will be used later on in another query via the `{{id:R}}` or
      `{{<level>.columnName}}` variable. To suppress printing of a column, use a underscore as column name prefix. E.g.
@@ -5145,9 +5208,7 @@ A report can be divided into several levels. This can make report definitions mo
 splitting of otherwise excessively long SQL queries. For example, if your SQL query on the root level selects a number
 of person records from your person table, you can use the SQL query on the second level to look up the city where each person lives.
 
-See the example below:
-
-::
+See the example below::
 
     10.sql = SELECT id AS _pId, CONCAT(firstName, " ", lastName, " ") AS name FROM person
     10.rsep = <br>
@@ -5156,17 +5217,13 @@ See the example below:
     10.10.rbeg = (
     10.10.rend = )
 
-..
 
-This would result in
-
-::
+This would result in::
 
     John Doe (3004 Bern)
     Jane Miller (8008 Zürich)
     Frank Star (3012 Bern)
 
-..
 
 Text across several lines
 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -5194,32 +5251,32 @@ Example::
     20.tail = </h3>
 
 Join mode: SQL
-''''''''''''''
+""""""""""""""
 
-This is the default. All lines are joined with a *space* in between. E.g.: ::
+This is the default. All lines are joined with a *space* in between. E.g.::
 
     10.sql = SELECT 'hello world'
                FROM mastertable
 
-Results to: `10.sql = SELECT 'hello world' FROM mastertable`
+Results to: ``10.sql = SELECT 'hello world' FROM mastertable``
 
 Notice the space between "...world'" and "FROM ...".
 
 Join mode: strip whitespace
-'''''''''''''''''''''''''''
+"""""""""""""""""""""""""""
 
 Ending a line with a '\\' strips all leading and trailing whitespaces of that line joins the line directly (no extra
-space in between). E.g.: ::
+space in between). E.g.::
 
     10.sql = SELECT 'hello world', 'd:final.pdf \
                                     |p:id=export  \
                                     |t:Download' AS _pdf \
 
-Results to: `10.sql = SELECT 'hello world', 'd:final.pdf|p:id=export|t:Download' AS _pdf`
+Results to: ``10.sql = SELECT 'hello world', 'd:final.pdf|p:id=export|t:Download' AS _pdf``
 
 Note: the '\\' does not force the joining, it only removes the whitespaces.
 
-To get the same result, the following is also possible: ::
+To get the same result, the following is also possible::
 
     10.sql = SELECT 'hello world', CONCAT('d:final.pdf'
                                     '|p:id=export',
@@ -5228,7 +5285,7 @@ To get the same result, the following is also possible: ::
 Nesting of levels
 ^^^^^^^^^^^^^^^^^
 
-Levels can be nested. E.g.: ::
+Levels can be nested. E.g.::
 
   10 {
     sql = SELECT ...
@@ -5238,7 +5295,7 @@ Levels can be nested. E.g.: ::
     }
   }
 
-This is equal to: ::
+This is equal to::
 
   10.sql = SELECT ...
   10.5.sql = SELECT ...
@@ -5248,11 +5305,11 @@ Leading / trailing spaces
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 By default, leading or trailing whitespaces are removed from strings behind '='. E.g. 'rend =  test ' becomes 'test' for
-rend. To prevent any leading or trailing spaces, surround them by using single or double ticks. Example: ::
+rend. To prevent any leading or trailing spaces, surround them by using single or double ticks. Example::
 
-	10.sql = SELECT name FROM Person
-	10.rsep = ' '
-	10.head = "Names: "
+  10.sql = SELECT name FROM Person
+  10.rsep = ' '
+  10.head = "Names: "
 
 
 Braces character for nesting
@@ -5260,7 +5317,7 @@ Braces character for nesting
 
 By default, curly braces '{}' are used for nesting. Alternatively angle braces '<>', round braces '()' or square
 braces '[]' are also possible. To define the braces to use, the **first line** of the bodytext has to be a comment line and the
-last character of that line must be one of '{[(<'. The corresponding braces are used for that QFQ record. E.g.: ::
+last character of that line must be one of '{[(<'. The corresponding braces are used for that QFQ record. E.g.::
 
     # Specific code. >
     10 <
@@ -5280,7 +5337,7 @@ Per QFQ tt-content record, only one type of nesting braces can be used.
 Be careful to:
 
 * write nothing else than whitespaces/newline behind an **open brace**
-* the **closing brace** has to be alone on a line. ::
+* the **closing brace** has to be alone on a line::
 
    10.sql = SELECT 'Yearly Report'
 
@@ -5323,7 +5380,7 @@ The STORE_RECORD will always be merged with previous content. The Level Keys are
 
 Multiple columns, with the same column name, can't be accessed individually. Only the last column is available.
 
-Retrieving the *final* value of `special-column-names`_ is possible via '{{&<column>:R}}. Example: ::
+Retrieving the *final* value of `special-column-names`_ is possible via '{{&<column>:R}}. Example::
 
   10.sql = SELECT 'p:home&form=Person|s|b:success|t:Edit' AS _link
   10.20.sql = SELECT '{{link:R}}', '{{&link:R}}'
@@ -5331,7 +5388,7 @@ Retrieving the *final* value of `special-column-names`_ is possible via '{{&<col
 The first column of row `10.20` returns 'p:home&form=Person|s|b:success|t:Edit',the second column returns
 '<span class="btn btn-success"><a href="?home&s=badcaffee1234">Edit</a></span>'.
 
-Example STORE_RECORD: ::
+Example STORE_RECORD::
 
   10.sql= SELECT p.id AS _pId, p.name FROM Person AS p
   10.5.sql = SELECT adr.city, 'dummy' AS _pId FROM Address AS adr WHERE adr.pId={{pId:R}}
@@ -5342,7 +5399,7 @@ The line '10.10' will output 'dummy' in cases where there is at least one corres
 If there are no addresses (all persons) it reports the person id.
 If there is at least one address, it reports 'dummy', cause that's the last stored content.
 
-Example 'level': ::
+Example 'level'::
 
   10.sql= SELECT p.id AS _pId, p.name FROM Person AS p
   10.5.sql = SELECT adr.city, 'dummy' AS _pId FROM Address AS adr WHERE adr.pId={{10.pId}}
@@ -5368,7 +5425,7 @@ Notes to the level:
 |             | *30.5.1*  will be executed as many times as *30.5* has row numbers.                                                    |
 +-------------+------------------------------------------------------------------------------------------------------------------------+
 
-Report Example 1: ::
+Report Example 1::
 
     # Displays current date
     10.sql = SELECT CURDATE()
@@ -5497,7 +5554,7 @@ Column: _link
 |x  |   |Copy to       |y:[some content]                   |y:this will be copied      |Click on it copies the value of 'y:' to the clipboard. Optional a file ('F:...') might be specified as source.                          |
 |   |   |clipboard     |                                   |                           |See `copyToClipboard`_.                                                                                                                 |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
-|   |   |Text          |t:<text>                           |t:Firstname Lastname       |-                                                                                                                                       |
+|   |   |Text          |t:<text>                           |t:Firstname Lastname       |                                                                                                                                        |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
 |   |   |Render        |r:<mode>                           |r:3                        |See: `render-mode`_, Default: 0                                                                                                         |
 +---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
@@ -5587,7 +5644,7 @@ render mode might dynamically control the rendered link.
 |7           | pure url            |pure url            |                  |no link, pure url                                                          |
 +------------+---------------------+--------------------+------------------+---------------------------------------------------------------------------+
 
-::
+Example::
 
     10.sql = SELECT CONCAT('u:', p.homepage, IF(p.showHomepage='yes', '|r:0', '|r:5') ) AS _link FROM Person AS p
 
@@ -5702,9 +5759,7 @@ Columns: _page[X]
 The colum name is composed of the string *page* and a trailing character to specify the type of the link.
 
 
-**Syntax**
-
-::
+**Syntax**::
 
     10.sql = SELECT "[options]" AS _page[<link type>]
 
@@ -5712,7 +5767,7 @@ The colum name is composed of the string *page* and a trailing character to spec
 
     <link type> = c,d,e,h,i,n,s
 
-..
+
 
 +---------------+-----------------------------------------------+-------------------------------------+----------------------------------------------+
 |  column name  |  Purpose                                      |default value of question parameter  |  Mandatory parameters                        |
@@ -5770,9 +5825,7 @@ Column: _paged
 These column offers a link, with a confirmation question, to delete one record (mode 'table') or a bunch of records
 (mode 'form'). After deleting the record(s), the current page will be reloaded in the browser.
 
-**Syntax**
-
-::
+**Syntax** ::
 
     10.sql = SELECT "U:table=<tablename>&r=<record id>|q:<question>|..." AS _paged
     10.sql = SELECT "U:form=<formname>&r=<record id>|q:<question>|..." AS _paged
@@ -5785,7 +5838,7 @@ file is multiple times referenced, than the file is not deleted (it would break
 references are not found, if they use different colummnnames or tablenames.
 
 Mode: table
-'''''''''''
+"""""""""""
 
 * `table=<table name>`
 * `r=<record id>`
@@ -5793,7 +5846,7 @@ Mode: table
 Deletes the record with id '<record id>' from table '<table name>'.
 
 Mode: form
-''''''''''
+""""""""""
 
 * `form=<form name>`
 * `r=<record id>`
@@ -5801,8 +5854,8 @@ Mode: form
 Deletes the record with id '<record id>' from the table specified in form '<form name>' as primary table.
 Additional action *FormElement* of type *beforeDelete* or *afterDelete* will be fired too.
 
-Examples:
-'''''''''
+Examples
+""""""""
 
 ::
 
@@ -5842,9 +5895,7 @@ Column: _vertical
 Render text vertically. This is useful for tables with limited column width. The vertical rendering is achieved via CSS tranformations
 (rotation) defined in the style attribute of the wrapping tag. You can optionally specify the rotation angle.
 
-**Syntax**
-
-::
+**Syntax** ::
 
     10.sql = SELECT "<text>|[<angle>]" AS _vertical
 
@@ -5861,10 +5912,7 @@ Render text vertically. This is useful for tables with limited column width. The
 The text is surrounded by some HTML tags in an effort to make other elements position appropriately around it.
 This works best for angles close to 270 or 90.
 
-**Minimal Example**
-
-::
-
+**Minimal Example** ::
 
     10.sql = SELECT "Hallo" AS _vertical
     20.sql = SELECT "Hallo|90" AS _vertical
@@ -5879,10 +5927,7 @@ Column: _mailto
 
 Easily create Email links.
 
-**Syntax**
-
-::
-
+**Syntax** ::
 
     10.sql = SELECT "<email address>|[<link text>]" AS _mailto
 
@@ -5902,32 +5947,23 @@ Easily create Email links.
 +--------------+----------------------------------------------------------------------------------------+-------------+
 
 
-**Minimal Example**
-
-::
-
+**Minimal Example** ::
 
     10.sql = SELECT "john.doe@example.com" AS _mailto
 
-..
-
-
 
-**Advanced Example**
-
-::
 
+**Advanced Example** ::
 
     10.sql = SELECT "john.doe@example.com|John Doe" AS _mailto
 
-..
 
 .. _column_sendmail:
 
 Column: _sendmail
 ^^^^^^^^^^^^^^^^^
 
-Format: ::
+Format::
 
     t:<TO:email[,email]>|f:<FROM:email>|s:<subject>|b:<body>
         [|c:<CC:email[,email]]>[|B:<BCC:email[,email]]>[|r:<REPLY-TO:email>]
@@ -5935,7 +5971,7 @@ Format: ::
         [|e:<subject encode: encode/decode/none>][E:<body encode: encode/decode/none>][|mode:html]
         [|C][d:<filename of the attachment>][|F:<file to attach>][|u:<url>][|p:<T3 uri>]
 
-The following parameters can also be written as complete words for ease of use: ::
+The following parameters can also be written as complete words for ease of use::
 
     to:<email[,email]>|from:<email>|subject:<subject>|body:<body>
         [|cc:<email[,email]]>[|bcc:<email[,email]]>[|reply-to:<email>]
@@ -5944,9 +5980,7 @@ The following parameters can also be written as complete words for ease of use:
 
 Send emails. Every mail will be logged in the table `mailLog`. Attachments are supported.
 
-**Syntax**
-
-::
+**Syntax** ::
 
     10.sql = SELECT "t:john@doe.com|f:jane@doe.com|s:Reminder tomorrow|b:Please dont miss the meeting tomorrow" AS _sendmail
     10.sql = SELECT "t:john@doe.com|f:jane@doe.com|s:Reminder tomorrow|b:Please dont miss the meeting tomorrow|A:off|g:1|x:2|y:3|z:4" AS _sendmail
@@ -6019,25 +6053,17 @@ Send emails. Every mail will be logged in the table `mailLog`. Attachments are s
   If this is not wished, it can be turned off by `e=none` and/or `E=none`.
 
 
-**Minimal Example**
-
-::
+**Minimal Example** ::
 
     10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available." AS _sendmail
 
-..
-
 This will send an email with subject *Latest News* from company@example.com to john.doe@example.com.
 
-**Advanced Examples**
-
-::
+**Advanced Examples** ::
 
     10.sql = SELECT "t:customer1@example.com,Firstname Lastname <customer2@example.com>, Firstname Lastname <customer3@example.com>| \\
                      f:company@example.com|s:Latest News|b:The new version is now available.|r:sales@example.com|A:on|g:101|x:222|c:ceo@example.com|B:backup@example.com" AS _sendmail
 
-..
-
 This will send an email with subject *Latest News* from company@example.com to customer1, customer2 and customer3 by
 using a realname for customer2 and customer3 and suppress generating of OoO answer if any receiver is on vacation.
 Additional the CEO as well as backup will receive the mail via CC and BCC.
@@ -6054,7 +6080,7 @@ The subsequent contents will be interpreted as HTML and is rendered correctly by
 .. _attachment:
 
 Attachment
-''''''''''
+""""""""""
 
 The following options are provided to attach files to an email:
 
@@ -6081,23 +6107,23 @@ Optional any number of sources can be concatenated to a single PDF file: 'C|F:<f
 
 Examples in Report::
 
-	# One file attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf" AS _sendmail
+  # One file attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf" AS _sendmail
 
-	# Two files attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf|F:fileadmin/detail.pdf" AS _sendmail
+  # Two files attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf|F:fileadmin/detail.pdf" AS _sendmail
 
-	# Two files and a webpage (converted to PDF) are attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf|F:fileadmin/detail.pdf|p:?id=export&r=123|d:person.pdf" AS _sendmail
+  # Two files and a webpage (converted to PDF) are attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|F:fileadmin/summary.pdf|F:fileadmin/detail.pdf|p:?id=export&r=123|d:person.pdf" AS _sendmail
 
-	# Two webpages (converted to PDF) are attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|p:?id=export&r=123|d:person123.pdf|p:?id=export&r=234|d:person234.pdf" AS _sendmail
+  # Two webpages (converted to PDF) are attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|p:?id=export&r=123|d:person123.pdf|p:?id=export&r=234|d:person234.pdf" AS _sendmail
 
-	# One file and two webpages (converted to PDF) are *concatenated* to one PDF and attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|C|F:fileadmin/summary.pdf|p:?id=export&r=123|p:?id=export&r=234|d:complete.pdf" AS _sendmail
+  # One file and two webpages (converted to PDF) are *concatenated* to one PDF and attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|C|F:fileadmin/summary.pdf|p:?id=export&r=123|p:?id=export&r=234|d:complete.pdf" AS _sendmail
 
-	# One T3 webpage, protected by a SIP, are attached.
-	10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|p:?id=export&r=123&_sip=1|d:person123.pdf" AS _sendmail
+  # One T3 webpage, protected by a SIP, are attached.
+  10.sql = SELECT "t:john.doe@example.com|f:company@example.com|s:Latest News|b:The new version is now available.|p:?id=export&r=123&_sip=1|d:person123.pdf" AS _sendmail
 
 .. _column_img:
 
@@ -6109,16 +6135,10 @@ Renders images. Allows to define an alternative text and a title attribute for t
 *   If no alternative text is defined, an empty alt attribute is rendered in the img tag (since this attribute is mandatory in HTML).
 *   If no title text is defined, the title attribute will not be rendered at all.
 
-**Syntax**
-
-::
-
+**Syntax** ::
 
     10.sql = SELECT "<path to image>|[<alt text>]|[<title text>]" AS _img
 
-..
-
-
 
 +-------------+-------------------------------------------------------------------------------------------+---------------------------+
 |**Parameter**|**Description**                                                                            |**Default value/behaviour**|
@@ -6131,21 +6151,12 @@ Renders images. Allows to define an alternative text and a title attribute for t
 +-------------+-------------------------------------------------------------------------------------------+---------------------------+
 
 
-**Minimal Example**
-
-::
-
+**Minimal Example** ::
 
     10.sql = SELECT "fileadmin/img/img.jpg" AS _img
 
-..
-
-
-
-**Advanced Examples**
-
-::
 
+**Advanced Examples** ::
 
     10.sql = SELECT "fileadmin/img/img.jpg|Aternative Text" AS _img            # alt="Alternative Text, no title
     20.sql = SELECT "fileadmin/img/img.jpg|Aternative Text|" AS _img           # alt="Alternative Text, no title
@@ -6156,7 +6167,6 @@ Renders images. Allows to define an alternative text and a title attribute for t
     70.sql = SELECT "fileadmin/img/img.jpg||Title Text" AS _img                # empty alt, title="Title Text"
     80.sql = SELECT "fileadmin/img/img.jpg||" AS _img                          # empty alt, no title
 
-..
 
 .. _column_exec:
 
@@ -6183,15 +6193,11 @@ Runs batch files or executables on the webserver. In case of an error, returncod
 +-------------+--------------------------------------------------+-----------------+
 
 
-**Minimal Examples**
-
-::
-
+**Minimal Examples** ::
 
     10.sql = SELECT "ls -s" AS _exec
     20.sql = SELECT "./batchfile.sh" AS _exec
 
-..
 
 .. _column_pdf:
 
@@ -6210,17 +6216,17 @@ Most of the other Link-Class attributes can be used to customize the link. ::
 * Parameter are position independent.
 * *<params>*: see `download-parameter-files`_
 * For column `_pdf` and `_zip`, the element sources `p:...`, `U:...`, `u:...`, `F:...` might repeated multiple times.
-* Example: ::
+* Example::
 
-		10.sql = SELECT "F:fileadmin/test.pdf" as _pdf,  "F:fileadmin/test.pdf" as _file,  "F:fileadmin/test.pdf" as _zip
-		10.sql = SELECT "p:id=export&r=1" as _pdf,  "p:id=export&r=1" as _file,  "p:id=export&r=1" as _zip
+    10.sql = SELECT "F:fileadmin/test.pdf" as _pdf,  "F:fileadmin/test.pdf" as _file,  "F:fileadmin/test.pdf" as _zip
+    10.sql = SELECT "p:id=export&r=1" as _pdf,  "p:id=export&r=1" as _file,  "p:id=export&r=1" as _zip
 
-		10.sql = SELECT "t:Download PDF|F:fileadmin/test.pdf" as _pdf,  "t:Download PDF|F:fileadmin/test.pdf" as _file,  "t:Download ZIP|F:fileadmin/test.pdf" as _zip
-		10.sql = SELECT "t:Download PDF|p:id=export&r=1" as _pdf,  "t:Download PDF|p:id=export&r=1" as _file,  "t:Download ZIP|p:id=export&r=1" as _zip
+    10.sql = SELECT "t:Download PDF|F:fileadmin/test.pdf" as _pdf,  "t:Download PDF|F:fileadmin/test.pdf" as _file,  "t:Download ZIP|F:fileadmin/test.pdf" as _zip
+    10.sql = SELECT "t:Download PDF|p:id=export&r=1" as _pdf,  "t:Download PDF|p:id=export&r=1" as _file,  "t:Download ZIP|p:id=export&r=1" as _zip
 
-		10.sql = SELECT "d:complete.pdf|t:Download PDF|F:fileadmin/test1.pdf|F:fileadmin/test2.pdf" as _pdf, "d:complete.zip|t:Download ZIP|F:fileadmin/test1.pdf|F:fileadmin/test2.pdf" as _zip
+    10.sql = SELECT "d:complete.pdf|t:Download PDF|F:fileadmin/test1.pdf|F:fileadmin/test2.pdf" as _pdf, "d:complete.zip|t:Download ZIP|F:fileadmin/test1.pdf|F:fileadmin/test2.pdf" as _zip
 
-		10.sql = SELECT "d:complete.pdf|t:Download PDF|F:fileadmin/test.pdf|p:id=export&r=1|u:www.example.com" AS _pdf
+    10.sql = SELECT "d:complete.pdf|t:Download PDF|F:fileadmin/test.pdf|p:id=export&r=1|u:www.example.com" AS _pdf
 
 .. _column-save-pdf:
 
@@ -6240,10 +6246,10 @@ Tips:
 * If the target file already exists it will be overwriten. To save individual files, choose a new filename,
   for example by adding a timestamp.
 
-Examples: ::
+Example::
 
-	SELECT "d:fileadmin/result.pdf|F:fileadmin/_temp_/test.pdf" AS _savePdf
-	SELECT "d:fileadmin/result.pdf|F:fileadmin/_temp_/test.pdf|U:id=test&--orientation=landscape" AS _savePdf
+  SELECT "d:fileadmin/result.pdf|F:fileadmin/_temp_/test.pdf" AS _savePdf
+  SELECT "d:fileadmin/result.pdf|F:fileadmin/_temp_/test.pdf|U:id=test&--orientation=landscape" AS _savePdf
 
 
 .. _column-thumbnail:
@@ -6287,37 +6293,37 @@ The render mode '7' is useful, if the URL of the thumbnail have to be used in an
 tag. Something like `<body style="background-image:url(bgimage.jpg)">` could be solved with
 `SELECT "<body style="background-image:url(", 'T:fileadmin/file3.pdf' AS _thumbnail, ')">'`
 
-Example: ::
+Example::
 
-	# SIP protected, IMG tag, thumbnail width 150px
-	10.sql = SELECT 'T:fileadmin/file3.pdf' AS _thumbnail
+  # SIP protected, IMG tag, thumbnail width 150px
+  10.sql = SELECT 'T:fileadmin/file3.pdf' AS _thumbnail
 
-	# SIP protected, IMG tag, thumbnail width 50px
-	20.sql = SELECT 'T:fileadmin/file3.pdf|W:50' AS _thumbnail
+  # SIP protected, IMG tag, thumbnail width 50px
+  20.sql = SELECT 'T:fileadmin/file3.pdf|W:50' AS _thumbnail
 
-	# No SIP protection, IMG tag, thumbnail width 150px
-	30.sql = SELECT 'T:fileadmin/file3.pdf|s:0' AS _thumbnail
+  # No SIP protection, IMG tag, thumbnail width 150px
+  30.sql = SELECT 'T:fileadmin/file3.pdf|s:0' AS _thumbnail
 
-	# SIP protected, only the URL to the image, thumbnail width 150px
-	40.sql = SELECT 'T:fileadmin/file3.pdf|s:1|r:7' AS _thumbnail
+  # SIP protected, only the URL to the image, thumbnail width 150px
+  40.sql = SELECT 'T:fileadmin/file3.pdf|s:1|r:7' AS _thumbnail
 
 
 Dimension
-'''''''''
+"""""""""
 
 ImageMagick support various settings to force the thumbnail size.
 See https://www.imagemagick.org/script/command-line-processing.php#geometry or http://www.graphicsmagick.org/GraphicsMagick.html#details-geometry.
 
 Cleaning
-''''''''
+""""""""
 
 By default, the thumbnail directories are never cleaned. It's a good idea to install a cronjob which purges all files
 older than 1 year: ::
 
-	find /path/to/files -type f -mtime +365 -delete
+  find /path/to/files -type f -mtime +365 -delete
 
 Render
-''''''
+""""""
 
 `Public` thumbnails are rendered at the time when the T3 QFQ record is executed. `Secure` thumbnails are rendered when the
 'download.php?s=...' is called. The difference is, that the 'public' thumbnails blocks the page load until all thumbnails
@@ -6328,7 +6334,7 @@ A way to *pre render* thumbnails, is a periodically called (hidden) T3 page, whi
 triggers the rendering via column `_thumbnail`.
 
 Thumbnail: secure
-'''''''''''''''''
+"""""""""""""""""
 
 Mode 'secure' is activated via enabling SIP (`s:1`, default). The thumbnail is saved under the path `thumbnailDirSecure`
 as configured in configuration_.
@@ -6337,10 +6343,10 @@ The secure path needs to be protected against direct file access by the webmaste
 
 QFQ returns a HTML 'img'-tag: ::
 
-	<img src="api/download.php?s=badcaffee1234">
+  <img src="api/download.php?s=badcaffee1234">
 
 Thumbnail: public
-'''''''''''''''''
+"""""""""""""""""
 
 Mode 'public' has to be explicit activated by specifying `s:0`. The thumbnail is saved under the path `thumbnailDirPublic`
 as configured in configuration_.
@@ -6385,7 +6391,7 @@ Copy to clipboard
 | F:<pathFileName>  | F:fileadmin/protected/data.R   | pathFileName in DocumentRoot                                               |
 +-------------------+--------------------------------+----------------------------------------------------------------------------+
 
-Example: ::
+Example::
 
     10.sql = SELECT 'y:hello world (yank)|t:content direct (yank)' AS _yank,
                     'y:hello world (link)|t:content direct (link)' AS _link,
@@ -6490,49 +6496,49 @@ Parameter and (element) sources
       the key/value tuple in `p:...`, `u:...` or `U:...` has to be separated by '='. Please see last example below.
     * If an option contains an '&' it must be escaped with double '\\'. See example.
 
-	Most of the other Link-Class attributes can be used to customize the link as well.
+  Most of the other Link-Class attributes can be used to customize the link as well.
 
 Example `_link`: ::
 
-	# single `file`. Specifying a popup message window text is not necessary, cause a file directly accessed is fast.
-	SELECT "d:file.pdf|s|t:Download|F:fileadmin/pdf/test.pdf" AS _link
+  # single `file`. Specifying a popup message window text is not necessary, cause a file directly accessed is fast.
+  SELECT "d:file.pdf|s|t:Download|F:fileadmin/pdf/test.pdf" AS _link
 
-	# single `file`, with mode
-	SELECT "d:file.pdf|M:pdf|s|t:Download|F:fileadmin/pdf/test.pdf" AS _link
+  # single `file`, with mode
+  SELECT "d:file.pdf|M:pdf|s|t:Download|F:fileadmin/pdf/test.pdf" AS _link
 
-	# three sources: two pages and one file
-	SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1|F:fileadmin/pdf/test.pdf" AS _link
+  # three sources: two pages and one file
+  SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1|F:fileadmin/pdf/test.pdf" AS _link
 
-	# three sources: two pages and one file
-	SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1|F:fileadmin/pdf/test.pdf" AS _link
+  # three sources: two pages and one file
+  SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1|F:fileadmin/pdf/test.pdf" AS _link
 
-	# three sources: two pages and one file, parameter to wkhtml will be SIP encoded
-	SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1&_sip=1|p:id=detail2&r=1&_sip=1|F:fileadmin/pdf/test.pdf" AS _link
+  # three sources: two pages and one file, parameter to wkhtml will be SIP encoded
+  SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1&_sip=1|p:id=detail2&r=1&_sip=1|F:fileadmin/pdf/test.pdf" AS _link
 
-	# three sources: two pages and one file, the second page will be in landscape and pagesize A3
-	SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1&--orientation=Landscape&--page-size=A3|F:fileadmin/pdf/test.pdf" AS _link
+  # three sources: two pages and one file, the second page will be in landscape and pagesize A3
+  SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail&r=1|p:id=detail2&r=1&--orientation=Landscape&--page-size=A3|F:fileadmin/pdf/test.pdf" AS _link
 
-	# One source and a header file. Note: the parameter to the header URL is escaped with double backslash.
-	SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail2&r=1&--orientation=Landscape&--header={{URL:R}}?indexp.php?id=head\\&L=1|F:fileadmin/pdf/test.pdf" AS _link
+  # One source and a header file. Note: the parameter to the header URL is escaped with double backslash.
+  SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail2&r=1&--orientation=Landscape&--header={{URL:R}}?indexp.php?id=head\\&L=1|F:fileadmin/pdf/test.pdf" AS _link
 
 ..
 
 Example `_pdf`, `_zip`: ::
 
-	# File 1: p:id=1&--orientation=Landscape&--page-size=A3
-	# File 2: p:id=form
-	# File 3: F:fileadmin/file.pdf
-	SELECT 't:PDF|a:Creating a new PDF|p:id=1&--orientation=Landscape&--page-size=A3|p:id=form|F:fileadmin/file.pdf' AS _pdf
+  # File 1: p:id=1&--orientation=Landscape&--page-size=A3
+  # File 2: p:id=form
+  # File 3: F:fileadmin/file.pdf
+  SELECT 't:PDF|a:Creating a new PDF|p:id=1&--orientation=Landscape&--page-size=A3|p:id=form|F:fileadmin/file.pdf' AS _pdf
 
-	# File 1: p:id=1
-	# File 2: u:http://www.example.com
-	# File 3: F:fileadmin/file.pdf
-	SELECT 't:PDF - 3 Files|a:Please be patient|p:id=1|u:http://www.example.com|F:fileadmin/file.pdf' AS _pdf
+  # File 1: p:id=1
+  # File 2: u:http://www.example.com
+  # File 3: F:fileadmin/file.pdf
+  SELECT 't:PDF - 3 Files|a:Please be patient|p:id=1|u:http://www.example.com|F:fileadmin/file.pdf' AS _pdf
 
-	# File 1: p:id=1
-	# File 2: p:id=form
-	# File 3: F:fileadmin/file.pdf
-	SELECT CONCAT('t:ZIP - 3 Pages|a:Please be patient|p:id=1|p:id=form|F:', p.pathFileName) AS _zip
+  # File 1: p:id=1
+  # File 2: p:id=form
+  # File 3: F:fileadmin/file.pdf
+  SELECT CONCAT('t:ZIP - 3 Pages|a:Please be patient|p:id=1|p:id=form|F:', p.pathFileName) AS _zip
 
 ..
 
@@ -6551,70 +6557,70 @@ Best practice:
 
 #. Create a clean (=no menu, no website layout) letter layout in a separated T3 branch: ::
 
-	page = PAGE
-	page.typeNum = 0
-	page.includeCSS {
-	  10 = typo3conf/ext/qfq/Resources/Public/Css/qfq-letter.css
-	}
+      page = PAGE
+      page.typeNum = 0
+      page.includeCSS {
+        10 = typo3conf/ext/qfq/Resources/Public/Css/qfq-letter.css
+      }
 
-	// Grant access to any logged in user or specific development IPs
-	[usergroup = *] || [IP = 127.0.0.1,192.168.1.* ]
-	  page.10 < styles.content.get
-	[else]
-	  page.10 = TEXT
-	  page.10.value = access forbidden
-	[global]
+      // Grant access to any logged in user or specific development IPs
+      [usergroup = *] || [IP = 127.0.0.1,192.168.1.* ]
+        page.10 < styles.content.get
+      [else]
+        page.10 = TEXT
+        page.10.value = access forbidden
+      [global]
 
 #. Create a T3 `body` page (e.g. page alias: 'letterbody') with some content. Example static HTML content: ::
 
-	<div class="letter-receiver">
-	  <p>Address</p>
-	</div>
-	<div class="letter-sender">
-	 <p><b>firstName name</b><br>
-	  Phone +00 00 000 00 00<br>
-	  Fax +00 00 000 00 00<br>
-	 </p>
-	</div>
-
-	<div class="letter-date">
-	  Zurich, 01.12.2017
-	</div>
-
-	<div class="letter-body">
-	 <h1>Subject</h1>
-
-	 <p>Dear Mrs...</p>
-	 <p>Lucas ipsum dolor sit amet organa solo skywalker darth c-3p0 anakin jabba mara greedo skywalker.</p>
-
-	 <div class="letter-no-break">
-	 <p>Regards</p>
-	 <p>Company</p>
-	 <img class="letter-signature" src="">
-	 <p>Firstname Name<br>Function</p>
-	 </div>
-	</div>
+      <div class="letter-receiver">
+        <p>Address</p>
+      </div>
+      <div class="letter-sender">
+       <p><b>firstName name</b><br>
+        Phone +00 00 000 00 00<br>
+        Fax +00 00 000 00 00<br>
+       </p>
+      </div>
+
+      <div class="letter-date">
+        Zurich, 01.12.2017
+      </div>
+
+      <div class="letter-body">
+       <h1>Subject</h1>
+
+       <p>Dear Mrs...</p>
+       <p>Lucas ipsum dolor sit amet organa solo skywalker darth c-3p0 anakin jabba mara greedo skywalker.</p>
+
+       <div class="letter-no-break">
+       <p>Regards</p>
+       <p>Company</p>
+       <img class="letter-signature" src="">
+       <p>Firstname Name<br>Function</p>
+       </div>
+      </div>
 
 #. Create a T3 letter-`header` page (e.g. page alias: 'letterheader') , with only the header information: ::
 
-		<header>
-		<img src="fileadmin/logo.png" class="letter-logo">
-
-		<div class="letter-unit">
-		  <p class="letter-title">Department</p>
-		  <p>
-			 Company name<br>
-			 Company department<br>
-			 Street<br>
-			 City
-		  </p>
-		</div>
-		</header>
+        <header>
+        <img src="fileadmin/logo.png" class="letter-logo">
+
+        <div class="letter-unit">
+          <p class="letter-title">Department</p>
+          <p>
+           Company name<br>
+           Company department<br>
+           Street<br>
+           City
+          </p>
+        </div>
+        </header>
 
 #. Create a) a link (Report) to the PDF letter or b) attach the PDF (on the fly rendered) to a mail. Both will call the
    `wkhtml` via the `download` mode and forwards the necessary parameter.
 
-Use in `report`: ::
+Use in `report`::
 
   sql = SELECT CONCAT('d:Letter.pdf|t:',p.firstName, ' ', p.name,
                        '|p:id=letterbody&pId=', p.id, '&_sip=1&--margin-top=50mm&--margin-bottom=20mm&',
@@ -6626,9 +6632,9 @@ Use in `report`: ::
 
 Sendmail. Parameter: ::
 
-	sendMailAttachment={{SELECT 'd:Letter.pdf|t:', p.firstName, ' ', p.name, '|p:id=letterbody&pId=', p.id, '&_sip=1&--margin-top=50mm&--margin-bottom=20mm&--header-html={{BASE_URL_PRINT:Y}}?id=letterheader&--footer-right="Seite: [page]/[toPage]"&--footer-font-size=8&--footer-spacing=10' FROM Person AS p WHERE p.id={{id:S}} }}
+  sendMailAttachment={{SELECT 'd:Letter.pdf|t:', p.firstName, ' ', p.name, '|p:id=letterbody&pId=', p.id, '&_sip=1&--margin-top=50mm&--margin-bottom=20mm&--header-html={{BASE_URL_PRINT:Y}}?id=letterheader&--footer-right="Seite: [page]/[toPage]"&--footer-font-size=8&--footer-spacing=10' FROM Person AS p WHERE p.id={{id:S}} }}
 
-Replace the static content elements from 2. and 3. by QFQ Content elements as needed: ::
+Replace the static content elements from 2. and 3. by QFQ Content elements as needed::
 
   10.sql = SELECT '<div class="letter-receiver"><p>', p.name AS '_+br', p.street AS '_+br', p.city AS '_+br', '</p>'
             FROM Person AS p WHERE p.id={{pId:S}}
@@ -6675,10 +6681,10 @@ If the export file has to be customized (colors, pictures, headlines, ...), the
 It's much easier to do all customizations via Excel and creating a template than by coding in QFQ / Excel export notation.
 
 Setup
-'''''
+"""""
 
 * Create a special column name `_excel` (or `_link`) in QFQ/Report. As a source, define a T3 PageContent, which has to
-  deliver the dynamic content (also `_excel-export-sample`). ::
+  deliver the dynamic content (also `excel-export-sample`_). ::
 
     SELECT CONCAT('d:final.xlsx|M:excel|s:1|t:Excel (new)|uid:43') AS _link
 
@@ -6730,7 +6736,7 @@ Create a output like this: ::
 
 This fills D11, E11, F11, D12
 
-In Report Syntax: ::
+In Report Syntax::
 
     # With ... AS _XLS (token explicit given)
     10.sql = SELECT 'position=D10' AS _XLS,
@@ -6754,9 +6760,9 @@ In Report Syntax: ::
     30.sql = SELECT 'position=D30' AS _XLS,
                      '<some content with special characters like newline/carriage return>' AS _XLSb
 
-.. _excel-export-sample:
+.. _`excel-export-sample`:
 
-Excel export samples: ::
+Excel export samples::
 
     # From scratch (both are the same, one with '_excel' the other with '_link')
     SELECT CONCAT('d:new.xlsx|t:Excel (new)|uid:54') AS _excel
@@ -6789,7 +6795,7 @@ Functionality divides into:
   access control via SIP. The form is automatically called via AJAX.
 
 Part 1: Display list
-''''''''''''''''''''
+""""""""""""""""""""
 
 Display the list of elements via a regular QFQ content record. All 'drag and drop' elements together have to be nested by a HTML
 element. Such HTML element:
@@ -6815,7 +6821,7 @@ A `<div>` example HTML output (HTML send to the browser): ::
     </div>
 
 
-A typical QFQ report which generates those `<div>` HTML: ::
+A typical QFQ report which generates those `<div>` HTML::
 
     10 {
       sql = SELECT '<div id="anytag-', n.id,'" data-dnd-id="', n.id,'">' , n.note, '</div>'
@@ -6845,7 +6851,7 @@ which is the same column width as the outer table. ::
         </tbody>
     </table>
 
-A typical QFQ report which generates this HTML: ::
+A typical QFQ report which generates this HTML::
 
     10 {
       sql = SELECT '<tr id="anytag-', n.id,'" data-dnd-id="', n.id,'" data-columns="3">' , n.id AS '_+td', n.note AS '_+td', n.ord AS '_+td', '</tr>'
@@ -6866,7 +6872,7 @@ predefined html id has to be assigned them. After an update, all changed order n
 be updated via AJAX.
 
 The html id per element is defined by `qfq-dnd-ord-id-<id>` where `<id>` is the record id. Same example as above, but
-with an updated `n.ord` column: ::
+with an updated `n.ord` column::
 
     10 {
       sql = SELECT '<tr id="anytag-', n.id,'" data-dnd-id="', n.id,'" data-columns="3">' , n.id AS '_+td', n.note AS '_+td',
@@ -6880,7 +6886,7 @@ with an updated `n.ord` column: ::
     }
 
 Part 2: Order records
-'''''''''''''''''''''
+"""""""""""""""""""""
 
 A dedicated `Form`, without any `FormElements`, is used to define the reorder logic (database update definition).
 
@@ -6939,12 +6945,14 @@ Bootstrap
 * Table > hover: `table-hover`
 * Table > condensed: `table-condensed`
 
-E.g.::
+Example::
 
   10.sql = SELECT id, name, firstName, ...
   10.head = <table class='table table-condensed qfq-table-50'>
 
-* `qfq-100`, `qfq-left` - makes e.g. a button full width and aligns the text left. ::
+* `qfq-100`, `qfq-left` - makes e.g. a button full width and aligns the text left.
+
+Example::
 
     10.sql = SELECT "p:home&r=0|t:Home|c:qfq-100 qfq-left" AS _pagev
 
@@ -6984,7 +6992,7 @@ Customization:
   as well as your own `$(document).ready()` function with the desired config. In this case, it is recommended not to
   use the above *tablesorter* classes since the QFQ javascript code could interfere with your javascript code.
 
-Example: ::
+Example::
 
     10 {
       sql = SELECT id, CONCAT('form&form=person&r=', id) AS _Pagee, lastName, title FROM person
@@ -7007,16 +7015,16 @@ Monitor
 Display a (log)file from the server, inside the browser, which updates automatically by a user defined interval. Access
 to the file is SIP protected. Any file on the server is possible.
 
-* On a Typo3 page, define a HTML element with a unique html-id. E.g.: ::
+* On a Typo3 page, define a HTML element with a unique html-id. E.g.::
 
     10.head = <pre id="monitor-1">Please wait</pre>
 
-* On the same Typo3 page, define a SQL column '_monitor' with the necessary parameter: ::
+* On the same Typo3 page, define a SQL column '_monitor' with the necessary parameter::
 
     10.sql = SELECT 'file:fileadmin/protected/log/sql.log|tail:50|append:1|refresh:1000|htmlId:monitor-1' AS _monitor
 
 
-* Short version with all defaults used to display system configured sql.log: ::
+* Short version with all defaults used to display system configured sql.log::
 
     10.sql = SELECT 'file:{{sqlLog:Y}}' AS _monitor, '<pre id="monitor-1" style="white-space: pre-wrap;">Please wait</pre>'
 
@@ -7028,44 +7036,22 @@ The following section gives some examples of typical reports.
 Basic Queries
 ^^^^^^^^^^^^^
 
-*   One simple query
-
-::
-
+One simple query::
 
     10.sql = SELECT "Hello World"
 
-..
-
-
-
-Result:
-
-::
 
+Result::
 
     Hello World
 
-..
-
-
-
-    Two simple queries
-
-::
 
+Two simple queries::
 
     10.sql = SELECT "Hello World"
     20.sql = SELECT "Say hello"
 
-..
-
-
-
-    Result:
-
-::
-
+Result::
 
     Hello WorldSay hello
 
@@ -7073,76 +7059,39 @@ Result:
 
 
 
-    Two simple queries, with break
-
-::
-
+Two simple queries, with break::
 
     10.sql = SELECT "Hello World<br>"
     20.sql = SELECT "Say hello"
 
-..
-
-
-
-    Result:
-
-::
 
+Result::
 
     Hello World
     Say hello
 
-..
-
-
 
 Accessing the database
 ^^^^^^^^^^^^^^^^^^^^^^
 
-    Real data, one single column
-
-::
-
+Real data, one single column::
 
     10.sql = SELECT p.firstName FROM exp_person AS p
 
-..
-
-
-
-    Result:
-
-::
 
+Result::
 
     BillieElvisLouisDiana
 
-..
-
-
-
-    Real data, two columns
-
-::
 
+Real data, two columns::
 
     10.sql = SELECT p.firstName, p.lastName FROM exp_person AS p
 
-..
-
-
-
-    Result:
-
-::
-
+Result::
 
     BillieHolidayElvisPresleyLouisArmstrongDianaRoss
 
-..
-
-
 
 The result of the SQL query is an output, row by row and column by column, without adding any formatting information.
 See `Formatting Examples`_ for examples of how the output can be formatted.
@@ -7158,58 +7107,34 @@ concat to concatenate data and formatting output in a single column.
 One can use 'level' keys to define formatting information that will be put before/after/between all rows/columns of the
 actual levels result.
 
-Two columns
-
-::
-
+Two columns::
 
     # Add the formatting information as a column
     10.sql = SELECT p.firstName, " " , p.lastName, "<br>" FROM exp_person AS p
 
-..
-
-
-
-Result:
-
-::
 
+Result::
 
     Billie Holiday
     Elvis Presley
     Louis Armstrong
     Diana Ross
 
-..
-
-
-
-One column 'rend'
-
-::
 
+One column 'rend'::
 
     10.sql = SELECT p.firstName, " " , p.lastName FROM exp_person AS p
     10.rend = <br>
 
-..
-
-Result:
-
-::
-
+Result::
 
     Billie Holiday
     Elvis Presley
     Louis Armstrong
     Diana Ross
 
-..
-
-More HTML
-
-::
 
+More HTML::
 
     10.sql = SELECT p.name FROM exp_person AS p
     10.head = <ul>
@@ -7217,9 +7142,7 @@ More HTML
     10.rbeg = <li>
     10.rend = </li>
 
-..
-
-Result: ::
+Result::
 
     o Billie Holiday
     o Elvis Presley
@@ -7236,14 +7159,14 @@ The same as above, but with braces::
     rend = </li>
   }
 
-Two queries: ::
+Two queries::
 
     10.sql = SELECT p.name FROM exp_person AS p
     10.rend = <br>
     20.sql = SELECT a.street FROM exp_address AS a
     20.rend = <br>
 
-Two queries: nested ::
+Two queries: nested::
 
     # outer query
     10.sql = SELECT p.name FROM exp_person AS p
@@ -7255,7 +7178,7 @@ Two queries: nested ::
 
 * For every record of '10', all records of 10.10 will be printed.
 
-Two queries: nested with variables ::
+Two queries: nested with variables::
 
     # outer query
     10.sql = SELECT p.id, p.name FROM exp_person AS p
@@ -7267,7 +7190,7 @@ Two queries: nested with variables ::
 
 * For every record of '10', all assigned records of 10.10 will be printed.
 
-Two queries: nested with hidden variables in a table ::
+Two queries: nested with hidden variables in a table::
 
     10.sql = SELECT p.id AS _pId, p.name FROM exp_person AS p
     10.rend = <br>
@@ -7276,7 +7199,7 @@ Two queries: nested with hidden variables in a table ::
     10.10.sql = SELECT a.street FROM exp_address AS a WHERE a.pId='{{10.pId}}'
     10.10.rend = <br>
 
-Same as above, but written in the nested notation ::
+Same as above, but written in the nested notation::
 
   10 {
     sql = SELECT p.id AS _pId, p.name FROM exp_person AS p
@@ -7289,7 +7212,7 @@ Same as above, but written in the nested notation ::
     }
   }
 
-Best practice *recommendation* for using parameter - see `access-column-values`_ ::
+Best practice *recommendation* for using parameter - see `access-column-values`_::
 
   10 {
     sql = SELECT p.id AS _pId, p.name FROM exp_person AS p
@@ -7310,18 +7233,18 @@ Recent List
 ^^^^^^^^^^^
 
 A nice feature is to show a list with last changed records. The following will show the 10 last modified (Form or
-FormElement) forms: ::
-
-	10 {
-	  sql = SELECT CONCAT('p:{{pageAlias:T}}&form=form&r=', f.id, '|t:', f.name,'|o:', GREATEST(MAX(fe.modified), f.modified)) AS _page
-				  FROM Form AS f
-				  LEFT JOIN FormElement AS fe ON fe.formId = f.id
-				  GROUP BY f.id
-				  ORDER BY GREATEST(MAX(fe.modified), f.modified) DESC
-				  LIMIT 10
-	  head = <h3>Recent Forms</h3>
-	  rsep = ,&ensp;
-	}
+FormElement) forms::
+
+  10 {
+    sql = SELECT CONCAT('p:{{pageAlias:T}}&form=form&r=', f.id, '|t:', f.name,'|o:', GREATEST(MAX(fe.modified), f.modified)) AS _page
+          FROM Form AS f
+          LEFT JOIN FormElement AS fe ON fe.formId = f.id
+          GROUP BY f.id
+          ORDER BY GREATEST(MAX(fe.modified), f.modified) DESC
+          LIMIT 10
+    head = <h3>Recent Forms</h3>
+    rsep = ,&ensp;
+  }
 
 .. _`store_user_examples`:
 
@@ -7331,16 +7254,16 @@ STORE_USER examples
 Keep variables per user session.
 
 Two pages (pass variable)
-'''''''''''''''''''''''''
+"""""""""""""""""""""""""
 
 Sometimes it's useful to have variables per user (=browser session). Set a variable on page 'A' and retrieve the value
 on page 'B'.
 
-Page 'A' - set the variable: ::
+Page 'A' - set the variable::
 
     10.sql = SELECT 'hello' AS '_=greeting'
 
-Page 'B' - get the value: ::
+Page 'B' - get the value::
 
     10.sql = SELECT '{{greeting:UE}}'
 
@@ -7348,9 +7271,9 @@ If page 'A' has never been opened with the current browser session, nothing is p
 If page 'A' is called, page 'B' will print 'hello'.
 
 One page (collect variables)
-''''''''''''''''''''''''''''
+""""""""""""""""""""""""""""
 
-A page will be called with several SIP variables, but not at all at the same time. To still get all variables at any time: ::
+A page will be called with several SIP variables, but not at all at the same time. To still get all variables at any time::
 
     # Normalize
     10.sql = SELECT '{{order:USE:::sum}}' AS '_=order', '{{step:USE:::5}}' AS _step, '{{direction:USE:::ASC}}' AS _direction
@@ -7366,11 +7289,11 @@ A page will be called with several SIP variables, but not at all at the same tim
     30.sql = SELECT * FROM items ORDER BY {{order:U}} {{direction:U}} LIMIT {{step:U}}
 
 Simulate/switch user: feUser
-''''''''''''''''''''''''''''
+""""""""""""""""""""""""""""
 
 Just set the STORE_USER variable 'feUser'.
 
-All places with `{{feUser:Y}}` has to be replaced by `{{feUser:UY}}`: ::
+All places with `{{feUser:Y}}` has to be replaced by `{{feUser:UY}}`::
 
     # Normalize
     10.sql = SELECT '{{feUser:UT}}' AS '_=feUser'
@@ -7381,12 +7304,12 @@ All places with `{{feUser:Y}}` has to be replaced by `{{feUser:UY}}`: ::
 
 
 Semester switch (remember last choice)
-''''''''''''''''''''''''''''''''''''''
+""""""""""""""""""""""""""""""""""""""
 
 A current semester is defined via configuration in STORE_SYSTEM '{{semId:Y}}'. The first column in 10.sql
 `'{{semId:SUY}}' AS '_=semId'` saves
 the semester to STORE_USER via '_=semId'. The priority 'SUY' takes either the latest choose (STORE_SIP) or reuse the
-last used (STORE_USER) or (first time call during browser session) takes the default from config (STORE_SYSTEM): ::
+last used (STORE_USER) or (first time call during browser session) takes the default from config (STORE_SYSTEM)::
 
     # Semester switch
     10 {
@@ -7403,6 +7326,296 @@ last used (STORE_USER) or (first time call during browser session) takes the def
       tail = </div><p></p>
     }
 
+
+.. _`restApi`:
+
+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/``
+
+    Data (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 will imported / exported in JSON notation.
+
+Any QFQ form becomes a REST form via:  ``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.
+
+.. note::
+
+    There are  *no* native-FormElements necessary or loaded. Action FormElements will be processed.
+
+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>* Mandatory. Level name (Endpoint) in URI.                           |
++-------------------+------------------------------------------------------------------------------+
+| table             | Mandatory. Name of the primary table                                         |
++-------------------+------------------------------------------------------------------------------+
+| Permit REST       | *get* Mandatory. The form can be loaded in REST mode.                        |
++-------------------+------------------------------------------------------------------------------+
+
+
+**Form.parameter**
+
++-------------------+----------------------------------------------------------------------------------+
+| Attribute         | Description                                                                      |
++===================+==================================================================================+
+| restSqlData       | Mandatory. SQL query selects content shown in data mode.                         |
+|                   | | ``restSqlData={{!SELECT id, name, gender FROM Person WHERE id='{{r:T0}}'' }}`` |
++-------------------+----------------------------------------------------------------------------------+
+| restSqlList       | Mandatory. 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 or dynamic token (see `restAuthorization`_).       |
++-------------------+----------------------------------------------------------------------------------+
+
+.. 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).
+
+
+.. important::
+
+    If there is an ``ìd`` given, a record in the named primary with the specified table has to exist.
+    If not, an error is thrown.
+
+POST - Insert
+-------------
+
+**Form**
+
++-------------------+------------------------------------------------------------------------------+
+| Attribute         | Description                                                                  |
++===================+==============================================================================+
+| name              | *<level>* Mandatory. Level name (Endpoint) in URI.                           |
++-------------------+------------------------------------------------------------------------------+
+| table             | Mandatory. Name of the primary table                                         |
++-------------------+------------------------------------------------------------------------------+
+| Permit REST       | *insert* Mandatory. The form can be loaded in REST mode.                     |
++-------------------+------------------------------------------------------------------------------+
+| id                | Missing or '0'.                                                              |
++-------------------+------------------------------------------------------------------------------+
+
+**Form.parameter**
+
++-------------------+----------------------------------------------------------------------------------+
+| Attribute         | Description                                                                      |
++===================+==================================================================================+
+| restParam         | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId``              |
++-------------------+----------------------------------------------------------------------------------+
+| restToken         | Optional. User defined string or dynamic token (see `restAuthorization`_).       |
++-------------------+----------------------------------------------------------------------------------+
+
+FormElement:
+
+* For each column to save one FormElement with ``FE.name=<column>`` is necessary.
+* A regular QFQ form can be used as REST Post endpoint
+
+
+PUT - Update
+------------
+
+**Form**
+
++-------------------+------------------------------------------------------------------------------+
+| Attribute         | Description                                                                  |
++===================+==============================================================================+
+| name              | *<level>* Mandatory. Level name (Endpoint) in URI.                           |
++-------------------+------------------------------------------------------------------------------+
+| table             | Mandatory. Name of the primary table                                         |
++-------------------+------------------------------------------------------------------------------+
+| Permit REST       | *update* Mandatory. The form can be loaded in REST mode.                     |
++-------------------+------------------------------------------------------------------------------+
+| id                | >0                                                                           |
++-------------------+------------------------------------------------------------------------------+
+
+**Form.parameter**
+
++-------------------+----------------------------------------------------------------------------------+
+| Attribute         | Description                                                                      |
++===================+==================================================================================+
+| restParam         | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId``              |
++-------------------+----------------------------------------------------------------------------------+
+| restToken         | Optional. User defined string or dynamic token (see `restAuthorization`_).       |
++-------------------+----------------------------------------------------------------------------------+
+
+FormElement:
+
+* For each column to save one FormElement with ``FE.name=<column>`` is necessary.
+* A regular QFQ form can be used as REST Post endpoint
+
+DELETE - Delete
+---------------
+
+**Form**
+
++-------------------+------------------------------------------------------------------------------+
+| Attribute         | Description                                                                  |
++===================+==============================================================================+
+| name              | *<level>* Mandatory. Level name (Endpoint) in URI.                           |
++-------------------+------------------------------------------------------------------------------+
+| table             | Mandatory. Name of the primary table                                         |
++-------------------+------------------------------------------------------------------------------+
+| Permit REST       | *delete* Mandatory. The form can be loaded in REST mode.                     |
++-------------------+------------------------------------------------------------------------------+
+| id                | >0                                                                           |
++-------------------+------------------------------------------------------------------------------+
+
+**Form.parameter**
+
++-------------------+----------------------------------------------------------------------------------+
+| Attribute         | Description                                                                      |
++===================+==================================================================================+
+| restParam         | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId``              |
++-------------------+----------------------------------------------------------------------------------+
+| restToken         | Optional. User defined string or dynamic token (see `restAuthorization`_).       |
++-------------------+----------------------------------------------------------------------------------+
+
+.. note::
+
+    There are  *no* native-FormElements necessary - but might exist for dependent records to delete. Action FormElements
+    will be processed.
+
+
+.. _`restAuthorization`:
+
+Authorization
+-------------
+
+A QFQ form is only acessible via REST API, if ``Form.permitRest`` enables on of the HTTP Method: **get, post, put, delete**
+
+``Permit New`` or ``Permit Edit`` don't apply to QFQ forms called via REST.
+
+.. important::
+
+    By default, the REST API is public accessible.
+
+If this is not wished:
+
+* HTTP AUTH might be used (configured via webserver)
+* Any other webserver based access restriction method
+* 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.
+
+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.
+
+.. tip::
+
+    The HTML Header Authorization token is available in STORE_CLIENT via '`{{Authorization:C:alnumx}}`.
+
+Best Practice: 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::
+
+  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
@@ -7453,14 +7666,15 @@ Create / edit `AutoCron` jobs
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Create a T3 page with a QFQ record (similar to the formeditor). Such page should be access restricted and is only needed
-to edit `AutoCron` jobs: ::
+to edit `AutoCron` jobs::
 
     dbIndex={{indexQfq:Y}}
     form={{form:S}}
 
     10 {
         # Table header.
-        sql = SELECT CONCAT('p:{{pageAlias:T}}&form=cron') AS _pagen, 'id', 'Next run','Frequency','Comment','Last run','In progress', 'Status' FROM (SELECT 1) AS fake WHERE '{{form:SE}}'=''
+        sql = SELECT CONCAT('p:{{pageAlias:T}}&form=cron') AS _pagen, 'id', 'Next run','Frequency','Comment',
+                     'Last run','In progress', 'Status' FROM (SELECT 1) AS fake WHERE '{{form:SE}}'=''
         head = <table class='table table-hover qfq-table-50'>
         tail = </table>
         rbeg = <thead><tr>
@@ -7507,7 +7721,7 @@ Due to checking `inProgress`, jobs will never run in parallel, even if a job nee
 cron).
 
 Job: repeating
-''''''''''''''
+""""""""""""""
 
 * frequency: '1 MINUTE', '2 DAY', '3 MONTH', ....
 
@@ -7516,7 +7730,7 @@ increased by `frequency` again, until it points to the future.
 
 
 Job: asynchronous
-'''''''''''''''''
+"""""""""""""""""
 
 * frequency: <empty>
 
@@ -7533,7 +7747,7 @@ system cron run.
 
 
 Type: Mail
-''''''''''
+""""""""""
 
 At the moment there is a special sendmail notation - this will change in the future.
 
@@ -7546,7 +7760,7 @@ the mail text.
 
 
 Type: Website
-'''''''''''''
+"""""""""""""
 
 The page specified in `URL` will be opened.
 
@@ -7570,16 +7784,16 @@ Access restriction
 To protect AutoCron pages not to be triggered accidental or by unprivileged access, access to those page tree might be
 limited to localhost. Some example Typoscript: ::
 
-	# Access allowed for any logged in user or via 'localhost'
-	[usergroup = *] || [IP = 127.0.0.1]
-	  page.10 < styles.content.get
-	[else]
-	  # Error Message
-	  page.10 = TEXT
-	  page.10.value = <h2>Access denied</h2>Please log in or access this page from an authorized host. Your current IP address:&nbsp;
-	  page.20 = TEXT
-	  page.20.data = getenv : REMOTE_ADDR
-	[global]
+  # Access allowed for any logged in user or via 'localhost'
+  [usergroup = *] || [IP = 127.0.0.1]
+    page.10 < styles.content.get
+  [else]
+    # Error Message
+    page.10 = TEXT
+    page.10.value = <h2>Access denied</h2>Please log in or access this page from an authorized host. Your current IP address:&nbsp;
+    page.20 = TEXT
+    page.20.data = getenv : REMOTE_ADDR
+  [global]
 
 
 
@@ -7590,6 +7804,7 @@ AutoCron / website: HTTPS protocol
 * All certificates are accepted, even self signed without a correct chain or hostnames, not listed in the certificate.
   This is useful if there is a general 'HTTP >> HTTPS' redirection configured and the website is accessed via `https://localhost/...`
 
+
 .. _applicationTest:
 
 Application Test
@@ -7597,7 +7812,7 @@ Application Test
 
 With a framework like https://www.seleniumhq.org/ it's possible to play and verify unattended  test cases.
 
-To assist such frameworks and to make the tests reliable, an individual tag might be assigned to elements which have to
+To assist such frameworks and to make the tests reliable, an individual tag might be assigned to HTML elements which have to
 interact with the test framework.
 
 Form
@@ -7630,8 +7845,8 @@ Tips:
 
 * On general errors:
 
-	* Always check the Javascript console of your browser, see `javascriptProblem`_.
-	* Always check the Webserver log files.
+  * Always check the Javascript console of your browser, see `javascriptProblem`_.
+  * Always check the Webserver log files.
 
 Caching
 -------
@@ -7672,7 +7887,7 @@ the content. For SQL statements, remove the outer token (e.g. only one curly bra
 
   FE.title: Person { SELECT ... WHERE id={{buggyVar:alnumx}} }
 
-Tip on Report: In case the query did not contain any double ticks, just wrap all but 'SELECT' in double ticks: ::
+Tip on Report: In case the query did not contain any double ticks, just wrap all but 'SELECT' in double ticks::
 
  Buggy query:  10.sql = SELECT id, ... FROM myTable WHERE status={{myVar}} ORDER BY status
  Debug query:  10.sql = SELECT "id, ... FROM myTable WHERE status={{myVar}} ORDER BY status"
@@ -7699,7 +7914,7 @@ Note the '-' in '{{text:RE::-}}', this will prevent that QFQ escapes any charact
 TypeAhead list with T3 page alias names - use of the T3 DB
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-To define a typeahead list of T3 page alias names: ::
+To define a typeahead list of T3 page alias names::
 
     FE.type = text
     FE.parameter.typeAheadSql = SELECT p.alias FROM {{dbNameT3:Y}}.pages AS p WHERE p.deleted=0 AND p.alias!='' AND p.alias LIKE ? ORDER BY p.alias LIMIT 20
@@ -7727,7 +7942,7 @@ To offer an FE User the possibility to change the own T3 FE password, create a f
 
     fe[2].class = action
     fe[2].type = afterSave
-    fe[2].parameter = sqlAfter={{UPDATE {{dbNameT3:Y}}.fe_users SET password='{{myPassword:FE:all:p}} WHERE username='{{feUser:T}}' AND deleted=0
+    fe[2].parameter = sqlAfter={{UPDATE {{dbNameT3:Y}}.fe_users SET password='{{myPassword:FE:all:p}}' WHERE username='{{feUser:T}}' AND deleted=0
 
 Call the form via SIP on an existing record. Often QFQ has an own table for persons and also the current user exist in T3
 fe_users table.
@@ -7745,7 +7960,7 @@ Especially if you got a blank page (no rendering at all), this is typically an u
 and report the bug (https://qfq.io > Contact).
 
 Call to undefined function qfq\\mb_internal_encoding()
-''''''''''''''''''''''''''''''''''''''''''''''''''''''
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
 Check that all required php modules are installed. See `preparation`_.
 
diff --git a/extension/Documentation/Release.rst b/extension/Documentation/Release.rst
index 91aae13e1d8622fb22a0f79265c7b9962a033f1e..56624a1172b16e093013cb5b6fea2532fae41170 100644
--- a/extension/Documentation/Release.rst
+++ b/extension/Documentation/Release.rst
@@ -9,7 +9,7 @@
 ..
 .. --------------------------------------------------
 .. 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
 ..
 .. -*- coding: utf-8 -*- with BOM.
@@ -36,11 +36,61 @@ Features
 Bug Fixes
 ^^^^^^^^^
 
-Version 19.2.1
+
+Version 19.2.3
+--------------
+
+Date: 22.02.2019
+
+Notes
+^^^^^
+
+New: `QFQ REST <https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest>`_
+interface implemented.
+
+Features
+^^^^^^^^
+
+* Rest Implementation with GET,PUT,POST,DELETE and authorization token
+* REST.md: add
+* Manual.rst: reformat all sql code to use tyoposcript - it seems mysql does not work
+* Manual.rst: Add some admonitions
+
+Bug Fixes
+^^^^^^^^^
+
+* #7925 / Change CWD during split reduced to splitting only.
+* #7925 / Fixed problem in mkDirParent() with absolute paths.
+* #7925 / Make logger independent of CWD.
+* #7925 / Fixed problem with `mktemp --tmpdir`(difference Ubuntu 16 / 18) by using PHP function again.
+* #7925 / Fixed some 'undefined index' problems.
+
+Version 19.2.2
 --------------
 
+Date: 19.02.2019
+
+Notes
+^^^^^
+
+* QFQ now offers a basic REST API. Check https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest
+
+Features
+^^^^^^^^
+
+* 7910 / Check for double form names
+* 7904 / REST api export. Manual.rst: describe QFQ REST API
+* Latest phpStorm IDE complains about missing ext-json in composer.json. Added.
+* Manual.rst: Example how to use 'password' escape class.
+
+Bug Fixes
+^^^^^^^^^
+
+
+Version 19.2.1
+--------------
 
-Date: 16.2.19
+Date: 18.02.2019
 
 Notes
 ^^^^^
diff --git a/extension/Documentation/Settings.cfg b/extension/Documentation/Settings.cfg
index 7b81aae50acb56121e4584a6fc3e7a59e876d933..dcb0c7adec8f95df62bff45a10fd327403a0ab94 100644
--- a/extension/Documentation/Settings.cfg
+++ b/extension/Documentation/Settings.cfg
@@ -3,7 +3,7 @@
 
 project     = QFQ - Quick Form Query
 version     = 19.2
-release     = 19.2.1
+release     = 19.2.3
 t3author    = Carsten Rose
 copyright   = since 2017 by the author
 
diff --git a/extension/Documentation/_make/conf.py b/extension/Documentation/_make/conf.py
index 9e0894eacd1120ab9d5acf470fd8858969bb68a9..dea854255883259d367b9bb1df2ec732ca04e2cd 100644
--- a/extension/Documentation/_make/conf.py
+++ b/extension/Documentation/_make/conf.py
@@ -59,7 +59,7 @@ copyright = u'2017, Carsten Rose'
 # The short X.Y version.
 version = '19.2'
 # The full version, including alpha/beta/rc tags.
-release = '19.2.1'
+release = '19.2.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/extension/RELEASE.txt b/extension/RELEASE.txt
index 91aae13e1d8622fb22a0f79265c7b9962a033f1e..d3943e20cb7e1ced36048156e74d3cb4c95d40e1 100644
--- a/extension/RELEASE.txt
+++ b/extension/RELEASE.txt
@@ -9,7 +9,7 @@
 ..
 .. --------------------------------------------------
 .. 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
 ..
 .. -*- coding: utf-8 -*- with BOM.
@@ -36,11 +36,63 @@ Features
 Bug Fixes
 ^^^^^^^^^
 
-Version 19.2.1
+
+Version 19.2.3
 --------------
 
+Date: 22.02.2019
+
+Notes
+^^^^^
+
+New: `QFQ REST <https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest>`_
+interface implemented.
+
+Features
+^^^^^^^^
+
+* Rest Implementation with GET,PUT,POST,DELETE and authorization token
+* REST.md: add
+* Manual.rst: reformat all sql code to use tyoposcript - it seems mysql does not work
+* Manual.rst: Add some admonitions
+
+Bug Fixes
+^^^^^^^^^
+
+* #7925 / fixed:
+
+  1. change CWD during split reduced to splitting only.
+  1. fixed problem in mkDirParent() with absolute paths.
+  1. make logger independent of CWD.
+  1. fixed problem with `mktemp --tmpdir`(difference Ubuntu 16 / 18) by using PHP function again.
+  1. fixed some 'undefined index' problems.
+
+Version 19.2.2
+--------------
+
+Date: 19.02.2019
+
+Notes
+^^^^^
+
+* QFQ now offers a basic REST API. Check https://docs.typo3.org/typo3cms/drafts/github/T3DocumentationStarter/Public-Info-053/Manual.html#rest
+
+Features
+^^^^^^^^
+
+* 7910 / Check for double form names
+* 7904 / REST api export. Manual.rst: describe QFQ REST API
+* Latest phpStorm IDE complains about missing ext-json in composer.json. Added.
+* Manual.rst: Example how to use 'password' escape class.
+
+Bug Fixes
+^^^^^^^^^
+
+
+Version 19.2.1
+--------------
 
-Date: 16.2.19
+Date: 18.02.2019
 
 Notes
 ^^^^^
diff --git a/extension/Source/api/rest.php b/extension/Source/api/rest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ff99752e5470403b24b2d49544fbd9b3b2ab6e80
--- /dev/null
+++ b/extension/Source/api/rest.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: crose
+ * Date: 17.02.19
+ * Time: 15:40
+ */
+
+namespace qfq;
+
+use qfq;
+
+require_once(__DIR__ . '/../core/QuickFormQuery.php');
+require_once(__DIR__ . '/../core/exceptions/UserFormException.php');
+require_once(__DIR__ . '/../core/exceptions/CodeException.php');
+require_once(__DIR__ . '/../core/exceptions/DbException.php');
+
+$restId = array();
+$restForm = array();
+
+$status = HTTP_400_BAD_REQUEST;
+$data = array();
+
+try {
+    try {
+        $form = OnString::splitPathInfoToIdForm($_SERVER['PATH_INFO'], $restId, $restForm);
+
+        // get latest `ìd`
+        $id = end($restId);
+
+        // 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:
+                $status = HTTP_200_OK;
+                break;
+
+            case REQUEST_METHOD_POST:
+                if ($id != 0) {
+                    throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Forbidden: id>0 with HTTP method $method",
+                        ERROR_MESSAGE_SUPPORT => '',
+                        ERROR_MESSAGE_HTTP_STATUS => HTTP_400_BAD_REQUEST
+                    ]), ERROR_REST_INVALID_ID);
+                }
+
+                $data = json_decode(file_get_contents('php://input'), true);
+                $status = HTTP_201_CREATED;
+                break;
+
+            case REQUEST_METHOD_PUT:
+                if ($id == 0) {
+                    throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Forbidden: id==0 with HTTP method $method",
+                        ERROR_MESSAGE_SUPPORT => '',
+                        ERROR_MESSAGE_HTTP_STATUS => HTTP_400_BAD_REQUEST
+                    ]), ERROR_REST_INVALID_ID);
+                }
+                $data = json_decode(file_get_contents('php://input'), true);
+                $status = HTTP_200_OK;
+                break;
+
+            case REQUEST_METHOD_DELETE:
+                if ($id == 0) {
+                    throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Forbidden: id==0 with HTTP method $method",
+                        ERROR_MESSAGE_SUPPORT => '',
+                        ERROR_MESSAGE_HTTP_STATUS => HTTP_400_BAD_REQUEST
+                    ]), ERROR_REST_INVALID_ID);
+                }
+                $status = HTTP_200_OK;
+                break;
+
+            default:
+                throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'Unsupported/unknown HTTP request method',
+                    ERROR_MESSAGE_SUPPORT => 'HTTP Code: ' . $method,
+                    ERROR_MESSAGE_HTTP_STATUS => HTTP_403_METHOD_NOT_ALLOWED
+                ]), ERROR_UNKNOWN_MODE);
+                break;
+        }
+
+        if ($data === null) {
+            throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Missing or broken JSON",
+                ERROR_MESSAGE_SUPPORT => json_last_error_msg(),
+                ERROR_MESSAGE_HTTP_STATUS => HTTP_400_BAD_REQUEST
+            ]), ERROR_BROKEN_PARAMETER);
+        }
+
+        if (!empty($data)) {
+            $_POST = $data;
+        }
+
+        $qfq = new QuickFormQuery(['bodytext' => $bodytext]);
+        $answer = $qfq->rest($restId, $restForm);
+
+    } catch (qfq\CodeException $e) {
+        $answer[API_MESSAGE] = $e->formatMessage();
+        $status = $e->getHttpStatus();
+
+    } catch (qfq\UserFormException $e) {
+        $answer[API_MESSAGE] = $e->formatMessage();
+        $status = $e->getHttpStatus();
+
+    } catch (qfq\DbException $e) {
+        $answer[API_MESSAGE] = $e->formatMessage();
+        $status = $e->getHttpStatus();
+    }
+
+} catch (\Exception $e) {
+    $answer[API_MESSAGE] = "Generic Exception: " . $e->getMessage();
+}
+
+header('HTTP/1.0 ' . $status);
+header("Content-Type: application/json");
+echo json_encode($answer);
diff --git a/extension/Source/core/AbstractBuildForm.php b/extension/Source/core/AbstractBuildForm.php
index 83b0f7cbdf232b989fc8a8f44cedf3c5885333cb..34b64340f7046668aa66a67f03bb0c4d95259144 100644
--- a/extension/Source/core/AbstractBuildForm.php
+++ b/extension/Source/core/AbstractBuildForm.php
@@ -2448,9 +2448,17 @@ abstract class AbstractBuildForm {
 
         $formElement = HelperFormElement::prepareExtraButton($formElement, false);
         $attribute .= $this->getAttributeFeMode($formElement[FE_MODE]);
-        $html = '<select ' . $attribute . '>' . $option . '</select>';
+        if (isset($formElement["datalist"])) {
+            if ($formElement[FE_DYNAMIC_UPDATE] === 'yes') {
+                throw new UserFormException("Datalist funktionert nicht mit dynamic update", ERROR_NOT_IMPLEMENTED);
+            }
+            $datalistId = $formElement[FE_HTML_ID] . '-datalist';
+            $html = '<input ' . Support::doAttribute('list', $datalistId) . $attribute . '><datalist '
+                . Support::doAttribute('id', $datalistId) . '>' . $option . '</datalist>';
+        } else {
+            $html = '<select ' . $attribute . '>' . $option . '</select>';
+        }
         $html = $html . $this->getHelpBlock() . $formElement[FE_TMP_EXTRA_BUTTON_HTML];
-
         return $html . $formElement[FE_INPUT_EXTRA_BUTTON_INFO];
     }
 
@@ -3182,7 +3190,7 @@ abstract class AbstractBuildForm {
 
         Support::setIfNotSet($formElement, FE_ANNOTATE_USER_UID);
         Support::setIfNotSet($formElement, FE_ANNOTATE_USER_NAME);
-        Support::setIfNotSet($formElement, FE_ANNOTATE_USER_NAME);
+        Support::setIfNotSet($formElement, FE_ANNOTATE_USER_AVATAR);
 
         $dataHighlight=HelperFile::getFileTypeHighlight($formElement[FE_HIGHLIGHT]??'',$formElement[FE_TEXT_SOURCE] );
 
diff --git a/extension/Source/core/Constants.php b/extension/Source/core/Constants.php
index a30d55f00deeaec0b10a54eda81882faa5369183..38ad1f896e425937a35a13e3137b4284c2a18d50 100644
--- a/extension/Source/core/Constants.php
+++ b/extension/Source/core/Constants.php
@@ -32,16 +32,20 @@ const TABLE_NAME_FORM = 'Form';
 const TABLE_NAME_FORM_ELEMENT = 'FormElement';
 const TABLE_NAME_SPLIT = 'Split';
 
+// Form Mode
 const FORM_LOAD = 'form_load';
 const FORM_SAVE = 'form_save';
 const FORM_UPDATE = 'form_update';
 const FORM_DELETE = 'form_delete';
 const FORM_DRAG_AND_DROP = 'form_drag_and_drop';
+const FORM_REST = 'form_rest';
+
 const FORM_PERMISSION_SIP = 'sip';
 const FORM_PERMISSION_LOGGED_IN = 'logged_id';
 const FORM_PERMISSION_LOGGED_OUT = 'logged_out';
 const FORM_PERMISSION_ALWAYS = 'always';
 const FORM_PERMISSION_NEVER = 'never';
+const FORM_PERMISSION_REST = 'rest';
 const FORM_BUTTON_NEW = 'new';
 const FORM_BUTTON_DELETE = 'delete';
 const FORM_BUTTON_CLOSE = 'close';
@@ -63,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'";
@@ -137,6 +141,7 @@ const KVP_VALUE_GIVEN = 'value_given';
 const ERROR_MESSAGE_TO_USER = 'toUser'; // always shown to the user.
 const ERROR_MESSAGE_SUPPORT = 'support'; // Message to help the developer to understand the problem.
 const ERROR_MESSAGE_OS = 'os'; // Error message from the OS - like 'file not found' or specific SQL problem
+const ERROR_MESSAGE_HTTP_STATUS = 'httpStatus'; // HTTP Status Code to report
 
 // QFQ Error Codes
 const ERROR_UNKNOW_SANITIZE_CLASS = 1001;
@@ -358,6 +363,10 @@ const ERROR_FORM_RESERVED_NAME = 2800;
 const ERROR_IMPORT_MISSING_EXPLICIT_TYPE = 2900;
 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
 //
@@ -413,12 +422,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';
 
@@ -536,6 +551,7 @@ const SYSTEM_SECURITY_ATTACK_DELAY_DEFAULT = 5; // Detected attack causes x seco
 const SYSTEM_SECURITY_SHOW_MESSAGE = 'securityShowMessage'; // Detected attack shows an error message
 const SYSTEM_SECURITY_GET_MAX_LENGTH = 'securityGetMaxLength'; // Trim every character (before conversion) to SECURITY_GET_MAX_LENGTH chars;
 const SYSTEM_SECURITY_GET_MAX_LENGTH_DEFAULT = 50; // Default max length for get variables
+const SYSTEM_SECURITY_FAILED_AUTH_DELAY = 'securityFailedAuthDelay'; // Failed auth causes x seconds delay
 
 const GET_EXTRA_LENGTH_TOKEN = '_';
 
@@ -871,6 +887,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';
@@ -983,6 +1000,13 @@ const F_ORDER_COLUMN_NAME = 'ord';
 
 const F_SHOW_ID_IN_FORM_TITLE = SYSTEM_SHOW_ID_IN_FORM_TITLE;
 
+const F_REST_SQL_LIST = 'restSqlList';
+const F_REST_SQL_DATA = 'restSqlData';
+const F_REST_PARAM = 'restParam';
+const F_REST_TOKEN = 'restToken';
+const CLIENT_REST_ID = '_id';
+const CLIENT_REST_FORM = '_form';
+
 // FORM_ELEMENT_STATI
 const FE_MODE_SHOW = 'show';
 const FE_MODE_READONLY = 'readonly';
@@ -1734,4 +1758,17 @@ const DND_SUBRECORD_FORM_ID = 'dnd-subrecord-form-id';
 const DND_ORD_HTML_ID_PREFIX = 'qfq-dnd-ord-id-';
 
 // Application Test: SELENIUM
-const ATTRIBUTE_DATA_REFERENCE = 'data-reference';
\ No newline at end of file
+const ATTRIBUTE_DATA_REFERENCE = 'data-reference';
+
+// REST
+const HTTP_HEADER_AUTHORIZATION = 'Authorization';
+
+const HTTP_200_OK = '200 OK';
+const HTTP_201_CREATED = '201 Created';
+
+const HTTP_400_BAD_REQUEST = '400 Bad Request';
+const HTTP_401_UNAUTHORIZED = '401 Unauthorized';
+const HTTP_403_FORBIDDEN = '403 Forbidden';
+const HTTP_403_METHOD_NOT_ALLOWED = '405 Method Not Allowed';
+const HTTP_404_NOT_FOUND = '404 Not Found';
+const HTTP_409_CONFLICT = '409 Conflict';
\ No newline at end of file
diff --git a/extension/Source/core/QuickFormQuery.php b/extension/Source/core/QuickFormQuery.php
index a4414abde97f7c6dbe0b893eeed3dc1fe7107848..a514d4bbf35e72cad1e0978089be167ebf4b4a3e 100644
--- a/extension/Source/core/QuickFormQuery.php
+++ b/extension/Source/core/QuickFormQuery.php
@@ -168,8 +168,11 @@ class QuickFormQuery {
 
         $this->store = Store::getInstance($bodytext, $phpUnit);
 
+        $timeout = $this->store::getVar(SYSTEM_SESSION_TIMEOUT_SECONDS, STORE_SYSTEM);
+        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() || Session::checkSessionExpired($timeout)) {
+        if (Session::getAndDestroyFlagFeUserHasChanged()) {
             $this->store->unsetStore(STORE_USER);
         }
 
@@ -341,9 +344,13 @@ class QuickFormQuery {
         $flagApiStructureReGroup = true;
 
         // Fill STORE_FORM
-        if ($formMode === FORM_UPDATE || $formMode === FORM_SAVE) {
-            $fillStoreForm = new FillStoreForm();
-            $fillStoreForm->process($formMode);
+        switch ($formMode) {
+            case FORM_UPDATE:
+            case FORM_SAVE:
+            case FORM_REST:
+                $fillStoreForm = new FillStoreForm();
+                $fillStoreForm->process($formMode);
+                break;
         }
 
         $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT . STORE_ZERO);
@@ -365,12 +372,13 @@ class QuickFormQuery {
             }
         }
 
-        // Session Expire happens quite late, cause it can be configured per form.
+        // 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);
+            // Validate (only if there is a 'real' form, not a FORM_DELETE with only a tablename).
+            // Attention: $formModeNew will be set
+            $sipFound = $this->validateForm($foundInStore, $formMode, $formModeNew);
 
         } else {
             // FORM_DELETE without a form definition: Fake the form with only a tableName.
@@ -398,20 +406,23 @@ class QuickFormQuery {
 
         // For 'new' record always create a new Browser TAB-uniq (for this current form, nowhere else used) SIP.
         // With such a Browser TAB-uniq SIP, multiple Browser TABs and following repeated NEWs are easily implemented.
-        if (!$sipFound || ($formMode == FORM_LOAD && $recordId === 0)) {
-            $this->store->createSipAfterFormLoad($formName);
+        if ($formMode != FORM_REST) {
+            if (!$sipFound || ($formMode == FORM_LOAD && $recordId === 0)) {
+                $this->store->createSipAfterFormLoad($formName);
+            }
         }
 
+        // Fill STORE_BEFORE
         if ($this->store->getVar($this->formSpec[F_PRIMARY_KEY], STORE_BEFORE) === false) {
             $this->store->fillStoreWithRecord($this->formSpec[F_TABLE_NAME], $recordId,
                 $this->dbArray[$this->dbIndexData], $this->formSpec[F_PRIMARY_KEY], STORE_BEFORE);
         }
 
         // Check (and release) dirtyRecord.
-        if ($formMode === FORM_DELETE || $formMode === FORM_SAVE) {
+        if ($formModeNew === FORM_DELETE || $formModeNew === FORM_SAVE) {
             $dirty = new Dirty(false, $this->dbIndexData, $this->dbIndexQfq);
 
-            $answer = $dirty->checkDirtyAndRelease($formMode, $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS],
+            $answer = $dirty->checkDirtyAndRelease($formModeNew, $this->formSpec[F_RECORD_LOCK_TIMEOUT_SECONDS],
                 $this->formSpec[F_DIRTY_MODE], $this->formSpec[F_TABLE_NAME], $this->formSpec[F_PRIMARY_KEY], $recordId, true);
 
             // In case of a conflict, return immediately
@@ -422,8 +433,8 @@ class QuickFormQuery {
             }
         }
 
-        // FORM_LOAD: if there is an foreign exclusive record lock - show form in F_MODE_READONLY mode.
-        if ($formMode === FORM_LOAD) {
+        // FORM_LOAD: if there is a foreign exclusive record lock - show form in F_MODE_READONLY mode.
+        if ($formModeNew === FORM_LOAD) {
             $dirty = new Dirty(false, $this->dbIndexData, $this->dbIndexQfq);
             $recordDirty = array();
             $rcLockFound = $dirty->getCheckDirty($this->formSpec[F_TABLE_NAME], $recordId, $recordDirty, $msg);
@@ -432,34 +443,43 @@ class QuickFormQuery {
             }
         }
 
-        if ($formMode === FORM_DELETE) {
-
-            $build = new Delete($this->dbIndexData);
-
-        } else {
-            $tableDefinition = $this->dbArray[$this->dbIndexData]->getTableDefinition($this->formSpec[F_TABLE_NAME]);
-            $this->store->fillStoreTableDefaultColumnType($tableDefinition);
+        switch ($formModeNew) {
+            case FORM_DELETE:
+                $build = new Delete($this->dbIndexData);
+                break;
+            case FORM_REST:
+                break;
+            case FORM_LOAD:
+            case FORM_SAVE:
+            case FORM_UPDATE:
+            case FORM_DRAG_AND_DROP:
 
-            switch ($this->formSpec['render']) {
-                case 'plain':
-                    $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
-                    break;
-                case 'table':
-                    $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
-                    break;
-                case 'bootstrap':
-                    $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
-                    break;
-                default:
-                    throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
-            }
+                $tableDefinition = $this->dbArray[$this->dbIndexData]->getTableDefinition($this->formSpec[F_TABLE_NAME]);
+                $this->store->fillStoreTableDefaultColumnType($tableDefinition);
+
+                switch ($this->formSpec['render']) {
+                    case 'plain':
+                        $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
+                        break;
+                    case 'table':
+                        $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
+                        break;
+                    case 'bootstrap':
+                        $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->dbArray);
+                        break;
+                    default:
+                        throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
+                }
+                break;
+            default:
+                throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
         }
 
         $formAction = new FormAction($this->formSpec, $this->dbArray[$this->dbIndexData], $this->phpUnit);
-        switch ($formMode) {
+        switch ($formModeNew) {
             case FORM_LOAD:
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_BEFORE_LOAD);
-                $data = $build->process($formMode);
+                $data = $build->process($formModeNew);
                 $tmpClass = is_numeric($this->formSpec[F_BS_COLUMNS]) ? ('col-md-' . $this->formSpec[F_BS_COLUMNS]) : $this->formSpec[F_BS_COLUMNS];
 //                $data = Support::wrapTag("<div class='" . 'col-md-' . $this->formSpec[F_BS_COLUMNS] . "'>", $data);
                 $data = Support::wrapTag('<div class="' . $tmpClass . '">', $data);
@@ -470,7 +490,7 @@ class QuickFormQuery {
             case FORM_UPDATE:
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_BEFORE_LOAD);
                 // data['form-update']=....
-                $data = $build->process($formMode);
+                $data = $build->process($formModeNew);
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_LOAD);
                 break;
 
@@ -485,7 +505,7 @@ class QuickFormQuery {
             case FORM_SAVE:
                 $this->logFormSubmitRequest();
 
-                $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
+                $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3);
 
                 // Action: Before
                 $feTypeList = FE_TYPE_BEFORE_SAVE . ',' . ($recordId == 0 ? FE_TYPE_BEFORE_INSERT : FE_TYPE_BEFORE_UPDATE);
@@ -524,6 +544,11 @@ class QuickFormQuery {
                 // Action: Sendmail
                 $formAction->elements($rc, $this->feSpecAction, FE_TYPE_SENDMAIL);
 
+                if ($formMode == FORM_REST) {
+                    $data = ['id' => $rc];
+                    $flagApiStructureReGroup = false;
+                    break;
+                }
 
                 $customForward = $this->setForwardModePage();
 
@@ -543,16 +568,12 @@ class QuickFormQuery {
                 if ($getJson) {
 
                     // Values of FormElements might be changed during 'afterSave': rebuild the form to load the new values. Especially for non primary template groups.
-//                    $this->loadFormSpecification($formMode, $recordId, $foundInStore);
                     $feSpecNative = $this->getNativeFormElements(SQL_FORM_ELEMENT_NATIVE_TG_COUNT, [$this->formSpec[F_ID]], $this->formSpec);
                     $parameterLanguageFieldName = $this->store->getVar(SYSTEM_PARAMETER_LANGUAGE_FIELD_NAME, STORE_SYSTEM);
                     $feSpecNative = HelperFormElement::setLanguage($feSpecNative, $parameterLanguageFieldName);
                     $this->feSpecNative = HelperFormElement::setFeContainerFormElementId($feSpecNative, $this->formSpec[F_ID], $recordId);
 
-                    // Retrieve FE Values as JSON
-                    // $data['form-update']=...
-                    // $data = $build->process($formMode, $htmlElementNameIdZero);
-                    $data = $build->process($formMode, false, $this->feSpecNative);
+                    $data = $build->process($formModeNew, false, $this->feSpecNative);
                 }
                 break;
 
@@ -566,6 +587,11 @@ class QuickFormQuery {
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_LOAD);
                 break;
 
+            case FORM_REST:
+                $flagApiStructureReGroup = false;
+                $data = $this->doRestGet();
+                break;
+
             default:
                 throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
         }
@@ -578,6 +604,93 @@ class QuickFormQuery {
         return $data;
     }
 
+    /**
+     * @param array $restIds
+     * @return array
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     * @throws UserReportException
+     */
+    private function doRestGet() {
+
+        $this->nameGenericRestParam();
+
+        $r = $this->store::getVar(TYPO3_RECORD_ID, STORE_TYPO3);
+        $key = empty($r) ? F_REST_SQL_LIST : F_REST_SQL_DATA;
+
+        if (!isset($this->formSpec[$key])) {
+            throw new UserFormException("Missing Parameter '$key'", ERROR_INVALID_VALUE);
+        }
+
+        return $this->evaluate->parse($this->formSpec[$key]);
+
+    }
+
+    /**
+     * Checks if $serverToken matches HTTP_HEADER_AUTHORIZATION,
+     * If not: throw an exception.
+     *
+     * @param string|array $serverToken
+     * @throws CodeException
+     * @throws UserFormException
+     */
+    private function restCheckAuthToken($serverToken) {
+
+        // No serverToken: no check necessary
+        if ($serverToken === '') {
+            return;
+        }
+
+        $clientToken = $this->store::getVar(HTTP_HEADER_AUTHORIZATION, STORE_CLIENT, SANITIZE_ALLOW_ALL);
+        if ($serverToken === $clientToken) {
+            return;
+        }
+
+        // Delay before answering.
+        $seconds = $this->store::getVar(SYSTEM_SECURITY_FAILED_AUTH_DELAY, STORE_SYSTEM);
+        sleep($seconds);
+
+        if ($clientToken == false) {
+            throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'Missing authorization token',
+                ERROR_MESSAGE_SUPPORT => "Missing HTTP Header: " . HTTP_HEADER_AUTHORIZATION,
+                ERROR_MESSAGE_HTTP_STATUS => HTTP_401_UNAUTHORIZED
+            ]), ERROR_REST_AUTHORIZATION);
+        }
+
+        throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'Authorization token not accepted',
+            ERROR_MESSAGE_SUPPORT => "Missing HTTP Header: " . HTTP_HEADER_AUTHORIZATION,
+            ERROR_MESSAGE_HTTP_STATUS => HTTP_401_UNAUTHORIZED
+        ]), ERROR_REST_AUTHORIZATION);
+    }
+
+    /**
+     * STORE_CLIENT: copy parameter _id1,_id2,...,_idN to named variables, specified via $this->formSpec[F_REST_PARAM] (CSV list)
+     *
+     * @throws CodeException
+     * @throws UserFormException
+     * @throws UserReportException
+     */
+    private function nameGenericRestParam() {
+
+        $paramNames = explode(',', $this->formSpec[F_REST_PARAM] ?? '');
+
+        $ii = 1;
+        foreach ($paramNames as $key) {
+            switch ($key) {
+                case CLIENT_FORM:
+                case CLIENT_RECORD_ID:
+                    throw new UserFormException("Name '$key' is forbidden in " . F_REST_PARAM, ERROR_INVALID_VALUE);
+                    break;
+                default:
+                    break;
+            }
+            $val = $this->store::getVar(CLIENT_REST_ID . $ii, STORE_CLIENT);
+            $this->store::setVar($key, $val, STORE_CLIENT);
+            $ii++;
+        }
+    }
+
     /**
      * Copies state 'hidden' from a FE pill to all FE child elements of that pill.
      *
@@ -838,7 +951,7 @@ class QuickFormQuery {
      * Loaded 'action' FormElements are in $this->feSpecAction
      * Loaded 'native' FormElements are in $this->feSpecNative
      *
-     * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE
+     * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE|FORM_REST
      * @param int $recordId
      * @param string $foundInStore
      * @param string $formLogMode
@@ -873,7 +986,7 @@ class QuickFormQuery {
 
         // Check if there is a recordId specified in Bodytext - as variable or query.
         $rTmp = $this->store->getVar(CLIENT_RECORD_ID, STORE_TYPO3, SANITIZE_ALLOW_ALL);
-        if (false !== $rTmp && !is_int($rTmp)) {
+        if (false !== $rTmp && !ctype_digit($rTmp)) {
             $rTmp = $this->evaluate->parse($rTmp);
             $this->store->setVar(CLIENT_RECORD_ID, $rTmp, STORE_TYPO3);
         }
@@ -886,16 +999,15 @@ class QuickFormQuery {
         $form = $this->checkFormLogMode($form);
         $form = $this->modeCleanFormConfig($mode, $form);
 
-        // Save specific elements to be expanded later.
-        $parseLater = OnArray::getArrayItems($form, [F_FORWARD_PAGE]);
-        $form[F_FORWARD_PAGE] = '';
-
         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, 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] = '';
 
         // Setting defaults later is too late.
         if (empty($form[F_DB_INDEX])) {
@@ -923,8 +1035,17 @@ class QuickFormQuery {
         // and for evaluating variables in the Form title
         $this->store->fillStoreWithRecord($form[F_TABLE_NAME], $recordId, $this->dbArray[$this->dbIndexData], $form[F_PRIMARY_KEY]);
 
+        // In case $form[F_REST_TOKEN] is a query which results to an empty answer; every token will fail.
+        $flagRestToken = !empty($form[F_REST_TOKEN]);
+
+        // Evaluate all fields
         $formSpec = $this->evaluate->parseArray($form);
 
+        // If it is empty, set it to true to force the TOKEN check (which will always fail)
+        if ($flagRestToken && $form[F_REST_TOKEN] == '') {
+            $form[F_REST_TOKEN] = true;
+        }
+
         $parameterLanguageFieldName = $this->store->getVar(SYSTEM_PARAMETER_LANGUAGE_FIELD_NAME, STORE_SYSTEM);
         $formSpec = HelperFormElement::setLanguage($formSpec, $parameterLanguageFieldName);
 
@@ -947,8 +1068,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
@@ -985,6 +1109,7 @@ class QuickFormQuery {
 
             case FORM_SAVE:
             case FORM_UPDATE:
+            case FORM_REST:
                 $feSpecNative = $this->getNativeFormElements(SQL_FORM_ELEMENT_NATIVE_TG_COUNT, [$this->formSpec[F_ID]], $this->formSpec);
                 break;
 
@@ -1104,7 +1229,7 @@ class QuickFormQuery {
      *   Specified in SIP
      *
      *
-     * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE
+     * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE|FORM_REST
      * @param string $foundInStore
      *
      * @return bool|string  Formname (Form.name) or FALSE (if no formname found)
@@ -1118,6 +1243,7 @@ class QuickFormQuery {
 
         switch ($mode) {
             case FORM_LOAD:
+            case FORM_REST:
                 $store = STORE_TYPO3;
                 break;
             case FORM_SAVE:
@@ -1240,7 +1366,7 @@ class QuickFormQuery {
             $formSpec[F_FE_LABEL_ALIGN] = $this->store->getVar(SYSTEM_LABEL_ALIGN, STORE_SYSTEM . STORE_EMPTY);
         }
 
-        $storeSystem=$this->store::getStore(STORE_SYSTEM);
+        $storeSystem = $this->store::getStore(STORE_SYSTEM);
 
         foreach ($keys as $key) {
 
@@ -1248,7 +1374,7 @@ class QuickFormQuery {
                 $this->store->setVar($key, $formSpec[$key], STORE_SYSTEM);
             } else {
                 // if not found set ''
-                $formSpec[$key] = $storeSystem[$key]??'';
+                $formSpec[$key] = $storeSystem[$key] ?? '';
             }
         }
 
@@ -1302,22 +1428,23 @@ class QuickFormQuery {
     }
 
     /**
-     * Check if loading of the given form is permitted. If not, throw an exception.
+     * Check if the form loading is permitted. If not, throw an exception.
      *
      * @param string $formNameFoundInStore
      * @param string $formMode
      *
      * @return bool 'true' if SIP exists, else 'false'
-     * @throws \qfq\CodeException
-     * @throws \qfq\UserFormException
-     * @internal param $foundInStore
+     * @throws CodeException
+     * @throws UserFormException
      */
-    private function validateForm($formNameFoundInStore, $formMode) {
+    private function validateForm($formNameFoundInStore, $formMode, &$formModeNew) {
 
-        // Retrieve record_id either from SIP (prefered) or via URL
+        $formModeNew = $formMode;
+
+        // 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);
@@ -1337,28 +1464,63 @@ 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);
-            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);
+            if (false === Support::findInSet(strtolower($method), $this->formSpec[F_REST_METHOD])) {
+
+                throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'Invalid HTTP method',
+                    ERROR_MESSAGE_SUPPORT => "Endpoint '" . $this->formSpec[F_NAME] . "' is not allowed with HTTP method '$method'",
+                    ERROR_MESSAGE_HTTP_STATUS => HTTP_401_UNAUTHORIZED
+                ]), ERROR_FORM_REST);
+
+            }
+
+            $this->restCheckAuthToken($this->formSpec[F_REST_TOKEN] ?? '');
+
+            switch ($method) {
+                case REQUEST_METHOD_GET:
+                    break;
+
+                case REQUEST_METHOD_POST:
+                case REQUEST_METHOD_PUT:
+                    $formModeNew = FORM_SAVE;
+                    break;
+
+                case REQUEST_METHOD_DELETE:
+                    $formModeNew = FORM_DELETE;
+                    break;
+
+                default:
+                    throw new CodeException('This code should never be reached', ERROR_CODE_SHOULD_NOT_HAPPEN);
+            }
+
+        } 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?
@@ -1370,6 +1532,7 @@ class QuickFormQuery {
             return $sipFound;
         }
         $sipArray = $this->store->getStore(STORE_SIP);
+
         // 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];
 
@@ -1746,5 +1909,40 @@ EOF;
 
     }
 
+    /**
+     * @param array $restId
+     * @param string $method
+     * @return array|string
+     * @throws CodeException
+     * @throws DbException
+     * @throws DownloadException
+     * @throws UserFormException
+     * @throws UserReportException
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+     */
+    public function rest(array $restId, array $restForm) {
+
+        // Copy 'id' from REST Url to STORE_CLIENT. Naming is generic with '_idX'
+        $ii = 1;
+        foreach ($restId as $val) {
+            $this->store::setVar(CLIENT_REST_ID . $ii, $val, STORE_CLIENT);
+            $ii++;
+        }
+
+        // Copy 'form' from REST Url to STORE_CLIENT. Naming is generic with '_formX'
+        $ii = 1;
+        foreach ($restForm as $val) {
+            $this->store::setVar(CLIENT_REST_FORM . $ii, $val, STORE_CLIENT);
+            $ii++;
+        }
+
+        $this->store::setVar(SIP_FORM, end($restForm), STORE_SIP);
+        $this->store::setVar(SIP_RECORD_ID, end($restId), STORE_SIP);
+
+        return $this->doForm(FORM_REST);
+
+    }
 
 }
\ No newline at end of file
diff --git a/extension/Source/core/Save.php b/extension/Source/core/Save.php
index 9df3c42d02f44e3ecc0ba40a22722df3c2dbf8cd..8372e7015a10ee9f3a177998b14811e077dc35ed 100644
--- a/extension/Source/core/Save.php
+++ b/extension/Source/core/Save.php
@@ -64,7 +64,7 @@ class Save {
     }
 
     /**
-     * Starts save process. On succcess, returns forwardmode/page.
+     * Starts save process. Returns recordId.
      *
      * @return int
      * @throws CodeException
@@ -386,6 +386,8 @@ class Save {
                 // No new upload and no existing: take care to remove previous upload file statistics.
                 $this->store->unsetVar(VAR_FILE_MIME_TYPE, STORE_VAR);
                 $this->store->unsetVar(VAR_FILE_SIZE, STORE_VAR);
+                $vars[VAR_FILE_SIZE] = 0;
+                $vars[VAR_FILE_MIME_TYPE] = '';
             } else {
                 $vars = HelperFile::getFileStat($pathFileNameTmp);
                 $this->store->appendToStore($vars, STORE_VAR);
@@ -823,8 +825,7 @@ class Save {
      * @throws UserFormException
      * @throws UserReportException
      */
-    private
-    function copyUploadFile(array $formElement, array $statusUpload) {
+    private function copyUploadFile(array $formElement, array $statusUpload) {
         $pathFileName = '';
 
         if (!isset($statusUpload[FILES_TMP_NAME]) || $statusUpload[FILES_TMP_NAME] === '') {
@@ -887,8 +888,7 @@ class Save {
      * @throws UserFormException
      * @throws UserReportException
      */
-    private
-    function autoOrient(array $formElement, $pathFileName) {
+    private function autoOrient(array $formElement, $pathFileName) {
 
         // 'autoOrient' wished?
         if (!isset($formElement[FE_FILE_AUTO_ORIENT]) || $formElement[FE_FILE_AUTO_ORIENT] == '0') {
@@ -929,17 +929,16 @@ class Save {
      * @throws UserFormException
      * @throws UserReportException
      */
-    private
-    function splitUpload(array $formElement, $pathFileName, $chmod, array $statusUpload) {
+    private function splitUpload(array $formElement, $pathFileName, $chmod, array $statusUpload) {
 
         if (empty($formElement[FE_FILE_SPLIT]) || $statusUpload[FILES_TYPE] != MIME_TYPE_SPLIT_CAPABLE) {
             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];
@@ -956,12 +955,10 @@ class Save {
         $cwd = getcwd();
 
         // Create temporary directory
-        $tempDir = Support::createTempDir();
+        $tempDir = HelperFile::mktempdir();
         $newSrc = $tempDir . DIRECTORY_SEPARATOR . QFQ_TEMP_SOURCE;
         HelperFile::copy($pathFileName, $newSrc);
 
-        HelperFile::chdir($tempDir);
-
         // Split destination.
         $pathParts = pathinfo($fileDestinationSplit);
         if (empty($pathParts['filename']) || empty($pathParts['basename'])) {
@@ -986,7 +983,10 @@ class Save {
         }
 
         // Split PDF
+        HelperFile::chdir($tempDir);
         $output = Support::qfqExec($cmd, $rc);
+        HelperFile::chdir($cwd);
+
         if ($rc != 0) {
             throw new UserFormException(
                 json_encode([ERROR_MESSAGE_TO_USER => 'pdf2svg failed', ERROR_MESSAGE_SUPPORT => "[$cwd][cmd=$cmd]$output"]),
@@ -994,7 +994,7 @@ class Save {
         }
 
         // Array of created file names.
-        if (false === ($files = scandir('.'))) {
+        if (false === ($files = scandir($tempDir))) {
             throw new UserFormException(
                 json_encode([ERROR_MESSAGE_TO_USER => 'Splitted files not found', ERROR_MESSAGE_SUPPORT => "[cwd=$cwd] scandir(.)" . HelperFile::errorGetLastAsString()]),
                 ERROR_PDF2JPEG);
@@ -1025,16 +1025,13 @@ class Save {
                 $fileDestination = $file;
             }
 
-            Support::moveFile($file, Support::joinPath($cwd, $fileDestination), true);
+            Support::moveFile($tempDir . DIRECTORY_SEPARATOR . $file, Support::joinPath($cwd, $fileDestination), true);
             HelperFile::chmod($fileDestination, $chmod);
 
             // Insert records.
             $this->db->sql($sql, ROW_REGULAR, [$fileSplitTableName, $xId, $fileDestination]);
         }
 
-        // Pop directory
-        HelperFile::chdir($cwd);
-
         // Remove duplicated source
         HelperFile::unlink($newSrc);
 
@@ -1053,8 +1050,7 @@ class Save {
      * @throws UserFormException
      * @throws UserReportException
      */
-    private
-    function doUploadSlave(array $fe, $modeUpload) {
+    private function doUploadSlave(array $fe, $modeUpload) {
         $sql = '';
         $flagUpdateSlaveId = false;
         $flagSlaveDeleted = false;
diff --git a/extension/Source/core/database/DatabaseUpdateData.php b/extension/Source/core/database/DatabaseUpdateData.php
index 7974419353681611a72830f235260dc41c27c78f..464916771b2923f15b2669433d91ee8d988199ef 100644
--- a/extension/Source/core/database/DatabaseUpdateData.php
+++ b/extension/Source/core/database/DatabaseUpdateData.php
@@ -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`; ",
+    ],
+
 );
 
 
diff --git a/extension/Source/core/exceptions/AbstractException.php b/extension/Source/core/exceptions/AbstractException.php
index cb0243ba3a3dc25c65559d249df26ba82a6459f5..000192af75da38767db9beddf4899baf7ca2e966 100644
--- a/extension/Source/core/exceptions/AbstractException.php
+++ b/extension/Source/core/exceptions/AbstractException.php
@@ -29,10 +29,11 @@ require_once(__DIR__ . '/../helper/Support.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                    ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                ERROR_IO_CHMOD);
  *
  * @package qfq
  */
@@ -46,6 +47,8 @@ class AbstractException extends \Exception {
     protected $file = '';
     protected $line = '';
 
+    protected $httpStatusCode = '400 Bad Request';
+
     /**
      * $this->getMessage() might give
      *   a) a simple string, or
@@ -92,10 +95,17 @@ class AbstractException extends \Exception {
         $msg = $this->getMessage();
         $arrMsg = json_decode($msg, true);
         if ($arrMsg === null) {
+
             $arrShow[EXCEPTION_MESSAGE] = $msg;
             $arrMsg[ERROR_MESSAGE_TO_USER] = $msg;
+
         } else {
             $arrShow[EXCEPTION_MESSAGE] = $arrMsg[ERROR_MESSAGE_TO_USER];
+
+            if (isset($arrMsg[ERROR_MESSAGE_HTTP_STATUS])) {
+                $this->httpStatusCode = $arrMsg[ERROR_MESSAGE_HTTP_STATUS];
+            }
+
         }
 
         $arrDebugHidden[EXCEPTION_FILE] = $this->getFile();
@@ -137,7 +147,7 @@ class AbstractException extends \Exception {
 
                 if (!empty($os = $arrMerged[ERROR_MESSAGE_OS] ?? '')) {
                     // [ mysqli: 1146 ] Table 'qfq_db.UNKNOWN_TABLE' doesn't exist
-                    $before=$this->getTableToken( html_entity_decode($arrMerged[ERROR_MESSAGE_OS],ENT_QUOTES));
+                    $before = $this->getTableToken(html_entity_decode($arrMerged[ERROR_MESSAGE_OS], ENT_QUOTES));
                     $arrMerged[EXCEPTION_SQL_FINAL] = $this->sqlHighlightError($arrMerged[ERROR_MESSAGE_OS], 'mysqli: 1146', $arrMerged[EXCEPTION_SQL_FINAL], $before, "' doesn't exist");
                     $arrMerged[EXCEPTION_SQL_FINAL] = $this->sqlHighlightError($arrMerged[ERROR_MESSAGE_OS], 'mysqli: 1064', $arrMerged[EXCEPTION_SQL_FINAL], "the right syntax to use near '", "' at line [0-9]*$");
                     // [ mysqli: 1054 ] Unknown column "noPsp.pspElement' in 'field list" | "... in 'order clause'"
@@ -173,6 +183,13 @@ class AbstractException extends \Exception {
 
     }
 
+    /**
+     * @return string
+     */
+    public function getHttpStatus() {
+        return $this->httpStatusCode;
+    }
+
     /**
      * Extract 'beforeMatch', incl. dynamic db name as token to do underlining later.
      * E.g.:  "[ mysqli: 1146 ] Table 'qfq_db.UNKNOWN_TABLE' doesn't exist"
@@ -184,8 +201,8 @@ class AbstractException extends \Exception {
     private function getTableToken($os) {
         $subject = "Table '.*' ";
         $arr = preg_match("/$subject/", $os, $matches);
-        $arr= explode('.', $matches[0]??'');
-        return ($arr[0]??'') . '.';
+        $arr = explode('.', $matches[0] ?? '');
+        return ($arr[0] ?? '') . '.';
     }
 
     /**
diff --git a/extension/Source/core/exceptions/CodeException.php b/extension/Source/core/exceptions/CodeException.php
index 125a96331a85a93c5fbe6b1a099337a67c091fa6..cea02cc255c063c4f5508ae0a0a5b4891710408a 100644
--- a/extension/Source/core/exceptions/CodeException.php
+++ b/extension/Source/core/exceptions/CodeException.php
@@ -21,10 +21,10 @@ require_once(__DIR__ . '/AbstractException.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
+                                ERROR_IO_CHMOD);
  *
  * @package qfq\exceptions
  */
diff --git a/extension/Source/core/exceptions/DbException.php b/extension/Source/core/exceptions/DbException.php
index b9d07e0985e46a0dc70aa9d65239685c9776a524..bfbbfae13088bbbcae3a6dc85cd933afd289f5de 100644
--- a/extension/Source/core/exceptions/DbException.php
+++ b/extension/Source/core/exceptions/DbException.php
@@ -38,6 +38,13 @@ class DbException extends AbstractException {
      *   [ERROR_MESSAGE_TO_USER] 'toUser' - shown in the client to the user - no details here!!!
      *   [ERROR_MESSAGE_SUPPORT] 'support' - help for the developer
      *   [ERROR_MESSAGE_OS] 'os' - message from the OS, like 'file not found'
+     *
+      throw new UserFormException(  json_encode(
+                                       [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                        ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                        ERROR_MESSAGE_OS => 'os' - message from the OS, like 'file not found'
+                                        ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                    ERROR_IO_CHMOD);
      *
      * @return string HTML formatted error string
      * @return string
diff --git a/extension/Source/core/exceptions/DownloadException.php b/extension/Source/core/exceptions/DownloadException.php
index 15c3d767a69203940245d98eeeb60c4ed6a214be..cb9a76def4ce437c5cd2b6d662faba131d53c2c0 100644
--- a/extension/Source/core/exceptions/DownloadException.php
+++ b/extension/Source/core/exceptions/DownloadException.php
@@ -21,10 +21,11 @@ require_once(__DIR__ . '/AbstractException.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                    ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                ERROR_IO_CHMOD);
  *
  * @package qfq\exceptions
  */
diff --git a/extension/Source/core/exceptions/ShellException.php b/extension/Source/core/exceptions/ShellException.php
index 4b98a6daaaf02de9f109f34561f9e04282f26b10..77e131852b4f896bce54408a12ab6164af013561 100644
--- a/extension/Source/core/exceptions/ShellException.php
+++ b/extension/Source/core/exceptions/ShellException.php
@@ -21,10 +21,11 @@ require_once(__DIR__ . '/AbstractException.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                    ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                ERROR_IO_CHMOD);
  *
  * @package qfq\exceptions
  */
diff --git a/extension/Source/core/exceptions/UserFormException.php b/extension/Source/core/exceptions/UserFormException.php
index f2711d62fc4d17f26b04f4b90d7559dca9eaeb79..855baacd3f790b8b5c6eb1048f93d90b8d263ad1 100644
--- a/extension/Source/core/exceptions/UserFormException.php
+++ b/extension/Source/core/exceptions/UserFormException.php
@@ -22,10 +22,11 @@ require_once(__DIR__ . '/AbstractException.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                    ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                ERROR_IO_CHMOD);
  *
  * Call
  *
diff --git a/extension/Source/core/exceptions/UserReportException.php b/extension/Source/core/exceptions/UserReportException.php
index c3f5bdc1b5cc4643f66666cb3f1e6400fed322e5..3f10a32f986c52697a715de774885259191e6652 100644
--- a/extension/Source/core/exceptions/UserReportException.php
+++ b/extension/Source/core/exceptions/UserReportException.php
@@ -21,10 +21,11 @@ require_once(__DIR__ . '/AbstractException.php');
  *
  * Throw with message for User and message for Support.
  *
- * throw new UserFormException(  json_encode(
- *                                  [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
- *                                   ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'"]),
- *                               ERROR_IO_CHMOD);
+  throw new UserFormException(  json_encode(
+                                   [ERROR_MESSAGE_TO_USER => 'Failed: chmod',
+                                    ERROR_MESSAGE_SUPPORT => "Failed: chmod $mode '$pathFileName'",
+                                    ERROR_MESSAGE_HTTP_STATUS => 'HTTP/1.0 409 Bad Request' ]),
+                                ERROR_IO_CHMOD);
  *
  * @package qfq\exceptions
  */
diff --git a/extension/Source/core/helper/HelperFile.php b/extension/Source/core/helper/HelperFile.php
index fffaa38db2b4f1256d04779154e49f6336a5b75f..5f7e89464c0003cab4e602c9df31cb524e192b8a 100644
--- a/extension/Source/core/helper/HelperFile.php
+++ b/extension/Source/core/helper/HelperFile.php
@@ -61,6 +61,8 @@ class HelperFile {
 
     /**
      * Creates a temporary directory.
+     * Be aware: '/tmp' is under systemd/apache2 (Ubuntu 18...) remapped to something like: '/tmp/systemd-private-...-apache2.service-.../tmp'
+     *
      * @throws UserFormException
      */
     public static function mktempdir() {
@@ -375,10 +377,18 @@ class HelperFile {
      */
     public static function mkDirParent($pathFileName, $chmodDir = false) {
         $path = "";
+        $cwd = '';
+
+        // Leading '/' will be removed - chdir to / to still use correct path
+        if ($pathFileName[0] == '/') {
+            $cwd = getcwd();
+            self::chdir('/');
+        }
 
         // Teile "Directory/File.Extension" auf
         $pathParts = pathinfo($pathFileName);
 
+
         // Zerlege Pfad in einzelne Directories
         $arr = explode("/", $pathParts["dirname"]);
 
@@ -409,6 +419,10 @@ class HelperFile {
             }
             $path .= "/";
         }
+
+        if ($cwd != '') {
+            self::chdir($cwd);
+        }
     }
 
     /**
diff --git a/extension/Source/core/helper/Logger.php b/extension/Source/core/helper/Logger.php
index d6dc807f34be0b7da762e3684d6de7ef85d09997..1307479186669eadda2f9f7e9322721343d97623 100644
--- a/extension/Source/core/helper/Logger.php
+++ b/extension/Source/core/helper/Logger.php
@@ -21,13 +21,28 @@ require_once(__DIR__ . '/../helper/Support.php');
 class Logger {
 
     /**
-     * Append $msg to $filename.
+     * @var String
+     */
+    private static $systemSitePath = '';
+
+    /**
+     * Copy the SystemSitePath to a local variable.
+     *
+     * @param $path
+     */
+    public static function setSystemSitePath($path) {
+        self::$systemSitePath = $path;
+    }
+
+    /**
+     * Append $msg to $filename. Create the file it it not exist.
      *
      * @param $msg
      * @param $filename
      *
      * @param string $mode
      * @param bool $recursion
+     * @throws CodeException
      * @throws UserFormException
      */
     public static function logMessage($msg, $filename, $mode = FILE_MODE_APPEND, $recursion = false) {
@@ -38,17 +53,19 @@ class Logger {
             return;
         }
 
-        $filename = self::relativeToT3Dir($filename);
+        $filename = self::makePathAbsolute($filename);
 
         try {
             $handle = fopen($filename, $mode);
         } catch (\Exception $e) {
-            $dummy=1;
+            $dummy = 1;
         }
 
-        if($handle===false) {
+        $cwd1 = getcwd();
+
+        if ($handle === false) {
 
-            if($recursion){
+            if ($recursion) {
                 throw new UserFormException(
                     json_encode([ERROR_MESSAGE_TO_USER => 'Error: cannot open file',
                         ERROR_MESSAGE_SUPPORT => "Error - cannot open. File: " . $filename .
@@ -73,9 +90,12 @@ class Logger {
     }
 
     /**
+     * Prefix every message with linePre().
+     *
      * @param $msg
      * @param $filename
      * @param string $mode
+     * @throws CodeException
      * @throws UserFormException
      */
     public static function logMessageWithPrefix($msg, $filename, $mode = FILE_MODE_APPEND) {
@@ -87,19 +107,33 @@ class Logger {
      *
      * @param $filename
      * @return string
+     * @throws CodeException
      */
-    private static function relativeToT3Dir($filename) {
+    private static function makePathAbsolute($filename) {
 
         if (isset($filename[0]) && $filename[0] != '/') {
-            if (strpos(getcwd(), 'qfq/' . API_DIR_EXT) !== false) {
-                return ('../../../../../' . $filename);
+
+            if (self::$systemSitePath == '') {
+
+                if (defined('PHPUNIT_QFQ')) {
+                    if (strpos(getcwd(), 'qfq/' . API_DIR_EXT) !== false) {
+                        return ('../../../../../' . $filename);
+                    }
+                    return $filename;
+                }
+
+                throw new CodeException('SystemSitePath is not set and the given logfile should be made absolute.', ERROR_MISSING_VALUE);
             }
+
+            return self::$systemSitePath . DIRECTORY_SEPARATOR . $filename;
         }
 
         return $filename;
     }
 
     /**
+     * Returns a timestamp, IP, cookie.
+     *
      * @return string
      */
     public static function linePre() {
@@ -118,16 +152,14 @@ class Logger {
     }
 
     /**
-     * @param array $fe
+     * Format details of a FormElement.
      *
+     * @param array $fe
      * @return string
      */
     public static function formatFormElementName(array $fe) {
-        Support::setIfNotSet($fe, 'id');
-        Support::setIfNotSet($fe, FE_NAME);
-        Support::setIfNotSet($fe, FE_LABEL);
 
-        return $fe['id'] . ' / ' . $fe[FE_NAME] . ' / ' . $fe[FE_LABEL];
+        return ($fe['id']??'') . ' / ' . ($fe[FE_NAME]??'') . ' / ' . ($fe[FE_LABEL]??'');
     }
 
     /**
@@ -138,6 +170,7 @@ class Logger {
      * @param $pre
      * @param $data
      * @param bool $flagNewLineFirst
+     * @throws CodeException
      * @throws UserFormException
      */
     public static function logFormLine(array $form, $pre, $data, $flagNewLineFirst = false) {
diff --git a/extension/Source/core/helper/OnString.php b/extension/Source/core/helper/OnString.php
index 56a1c901bfc5bb3697870c503d0f7d0a6ee29b1a..6cecfcd299efdcc319a039513199aa69b7c75768 100644
--- a/extension/Source/core/helper/OnString.php
+++ b/extension/Source/core/helper/OnString.php
@@ -94,11 +94,11 @@ class OnString {
      * @param &$row - return the digit part of $pos
      * @return bool - true if a alpha string and a numeric string is found, else false.
      */
-    public static function splitExcelPos($pos, &$column, &$row){
+    public static function splitExcelPos($pos, &$column, &$row) {
 
         preg_match_all('/[A-Z]+|\d+/', $pos, $matches);
 
-        if(count($matches[0])!=2) {
+        if (count($matches[0]) != 2) {
             return false;
         }
 
@@ -164,7 +164,7 @@ class OnString {
         $nestingStart = 0;
 
         // Process the string one start/end delimiter at a time
-        while(true) {
+        while (true) {
             // find the next start/end delimiter
             $nextDelimStartPos = strpos($str, $delimStart, $lastDelimPos + strlen($delimStart));
             $nextDelimEndPos = strpos($str, $delimEnd, $lastDelimPos + strlen($delimEnd));
@@ -183,7 +183,6 @@ class OnString {
                         ERROR_MESSAGE_SUPPORT => "in '$str'"]), ERROR_MISSING_OPEN_DELIMITER);
 
 
-
                     break;
                 } elseif ($exprDepth == 0) {
                     // end of nesting -> replace \n inside nested expression with space
@@ -199,11 +198,74 @@ class OnString {
 
             $lastDelimPos = $nextDelimPos;
         }
-        if ($exprDepth > 0 ) {
+        if ($exprDepth > 0) {
             throw new UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Missing close delimiter '$delimEnd'",
                 ERROR_MESSAGE_SUPPORT => "in '$str'"]), ERROR_MISSING_CLOSE_DELIMITER);
         }
 
         return $str;
     }
+
+    /**
+     * Split a $_SERVER['PATH_INFO'] of the form '/form1/id1/form2/id2/form3/id3/.../formN[/idN])' to
+     *   $rcArrrIds=[ id1, id2, ..., idN]
+     *   return: 'formN'
+     *
+     * @param $pathInfo
+     * @param array $rcArrId
+     * @param array $rcArrForm
+     * @return string
+     * @throws UserFormException
+     */
+    public static function splitPathInfoToIdForm($pathInfo, array &$rcArrId, array &$rcArrForm) {
+
+        // Empty: do nothing
+        if ($pathInfo == '') {
+            return '';
+        }
+
+        // Remove optional leading '/'
+        if ($pathInfo[0] == '/') {
+            $pathInfo = substr($pathInfo, 1);
+        }
+
+        // Remove optional trailing '/'
+        $len = strlen($pathInfo);
+        if ($len > 0 && $pathInfo[$len - 1] == '/') {
+            $pathInfo = substr($pathInfo, 0, $len - 1);
+        }
+
+        // Empty? do nothing
+       if ($pathInfo == '') {
+            return '';
+        }
+
+        $param = explode('/', $pathInfo);
+        $cnt = count($param);
+
+        // No 'id'. Append '0'
+        if ($cnt % 2 == 1) {
+            array_push($param, 0);
+        }
+
+        $rcArrId = array();
+        $rcArrForm = array();
+
+        while (count($param)>0) {
+
+            $form= array_shift($param);
+            if (!ctype_alnum($form)) {
+                throw new UserFormException('Expect alphanumeric string', ERROR_BROKEN_PARAMETER);
+            }
+            $rcArrForm[]=$form;
+
+            $id = array_shift($param);
+            if (!ctype_digit((string) $id)) {
+                throw new UserFormException('Expect numerical id', ERROR_BROKEN_PARAMETER);
+            }
+            $rcArrId[] = $id;
+        }
+
+        return $form;
+    }
 }
diff --git a/extension/Source/core/helper/Support.php b/extension/Source/core/helper/Support.php
index 18ebf2d6cc3fcea7a09422e39463884346887c8f..ec901d6a59c35ce63748d83a921d6017cfab00c3 100644
--- a/extension/Source/core/helper/Support.php
+++ b/extension/Source/core/helper/Support.php
@@ -209,8 +209,8 @@ class Support {
      *
      * @param string $type
      * @param string|array $value
-     * @param bool $flagOmitEmpty   true|false
-     * @param string $modeEscape    ESCAPE_WITH_BACKSLASH | ESCAPE_WITH_HTML_QUOTE
+     * @param bool $flagOmitEmpty true|false
+     * @param string $modeEscape ESCAPE_WITH_BACKSLASH | ESCAPE_WITH_HTML_QUOTE
      *
      * @return string correctly formatted attribute. Space at the end.
      * @throws CodeException
@@ -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
@@ -990,7 +990,7 @@ class Support {
         }
 
         // If min or max is set and if there is the standard error text given, define a more detailed error text.
-        if (($formElement[FE_MIN] != '' || $formElement[FE_MAX] != '') && ($formElement[F_FE_DATA_ERROR]??'') == F_FE_DATA_ERROR_DEFAULT) {
+        if (($formElement[FE_MIN] != '' || $formElement[FE_MAX] != '') && ($formElement[F_FE_DATA_ERROR] ?? '') == F_FE_DATA_ERROR_DEFAULT) {
             $formElement[F_FE_DATA_ERROR] = F_FE_DATA_ERROR_DEFAULT . ' - allowed values: ' . $formElement[FE_MIN] . '...' . $formElement[FE_MAX];
         }
     }
@@ -1281,15 +1281,6 @@ class Support {
         HelperFile::unlink($srcFile);
     }
 
-    /**
-     *
-     */
-    public static function createTempDir() {
-
-        return exec("mktemp -d --tmpdir " . QFQ_TEMP_FILE_PATTERN);
-
-    }
-
     /**
      * Convert 'false' and '<empty string>' to '0'.
      *
diff --git a/extension/Source/core/store/Client.php b/extension/Source/core/store/Client.php
index 93416dc0ad139194d287a36c867120622c010711..6ec3be4f89be41d78441164159fb08687c4a1383 100644
--- a/extension/Source/core/store/Client.php
+++ b/extension/Source/core/store/Client.php
@@ -34,20 +34,20 @@ class Client {
         Sanitize::digitCheckAndCleanGet(CLIENT_PAGE_TYPE);
         Sanitize::digitCheckAndCleanGet(CLIENT_PAGE_LANGUAGE);
 
+        $header = self::getHeader();
+
         if (isset($_GET)) {
             $get = $_GET; // do not use urldecode() - http://php.net/manual/de/function.urldecode.php#refsect1-function.urldecode-notes
         }
 
         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.
         }
@@ -57,8 +57,40 @@ class Client {
             $server[CLIENT_REMOTE_ADDRESS] = '0.0.0.0';
         }
 
-        $arr = array_merge($get, $post, $cookie, $server);
+        // 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);
     }
+
+    /**
+     * @return array
+     */
+    private static function getHeader() {
+
+        $arr = array();
+
+        // getallheaders() does not exist for phpunit tests
+        if (!function_exists('getallheaders')) {
+            return array();
+        }
+
+        $headers = getallheaders();
+
+        foreach ([HTTP_HEADER_AUTHORIZATION] as $key) {
+            if (isset($headers[$key])) {
+                $line = $headers[$key];
+
+                $delimiter = (strpos($line, '=') === false) ? ':' : '=';
+
+                // Header: 'Authorization: Token token=1234'
+                $split = explode($delimiter, $line, 2);
+                if (isset($split[1])) {
+                    $arr[$key] = OnString::trimQuote($split[1]);
+                }
+            }
+        }
+
+        return $arr;
+    }
 }
\ No newline at end of file
diff --git a/extension/Source/core/store/Config.php b/extension/Source/core/store/Config.php
index baa587aa3aaf701cf6adddaa342f11352bfab466..6ca346320f89f87d4bfc8651ce9755680c8cf8c0 100644
--- a/extension/Source/core/store/Config.php
+++ b/extension/Source/core/store/Config.php
@@ -363,6 +363,8 @@ class Config {
 
             SYSTEM_FLAG_PRODUCTION => 'yes',
             SYSTEM_THROW_GENERAL_ERROR => 'auto',
+
+            SYSTEM_SECURITY_FAILED_AUTH_DELAY => '3',
         ];
 
         // To let run legacy code
diff --git a/extension/Source/core/store/FillStoreForm.php b/extension/Source/core/store/FillStoreForm.php
index 16ca6dd8719761b202812120b76348ff5115cd93..606dbc6fde3615e303a4e6651d11983f0bc3366a 100644
--- a/extension/Source/core/store/FillStoreForm.php
+++ b/extension/Source/core/store/FillStoreForm.php
@@ -182,11 +182,12 @@ class FillStoreForm {
         $formModeGlobal = $this->store->getVar(F_MODE_GLOBAL, STORE_SIP . STORE_EMPTY);
 
         if ($formMode == FORM_UPDATE && $formModeGlobal == '') {
+            # During 'update': fake all elements to be not 'required'.
             $formModeGlobal = F_MODE_REQUIRED_OFF;
         }
 
         // If called through 'api/...': get STORE_TYPO3 via SIP parameter.
-        if (isset($clientValues[CLIENT_TYPO3VARS])) {
+        if (isset($clientValues[CLIENT_TYPO3VARS]) && $formMode != FORM_REST) {
             $this->store->fillTypo3StoreFromSip($clientValues[CLIENT_TYPO3VARS]);
         }
 
@@ -209,10 +210,12 @@ class FillStoreForm {
             }
         }
 
-        // Check if there is a 'new record already saved' situation:
-        // yes: the names of the input fields are submitted with '<fieldname>:0' instead of '<fieldname>:<id>'
-        // no: regular situation, take real 'recordid'
-        $fakeRecordId = isset($sipValues[SIP_MAKE_URLPARAM_UNIQ]) ? 0 : $sipValues[SIP_RECORD_ID];
+        if ($formMode != FORM_REST) {
+            // Check if there is a 'new record already saved' situation:
+            // yes: the names of the input fields are submitted with '<fieldname>:0' instead of '<fieldname>:<id>'
+            // no: regular situation, take real 'recordid'
+            $fakeRecordId = isset($sipValues[SIP_MAKE_URLPARAM_UNIQ]) ? 0 : $sipValues[SIP_RECORD_ID];
+        }
 
         // Iterate over all FormElements. Sanatize values. Built an assoc array $newValues.
         foreach ($this->feSpecNative AS $formElement) {
@@ -229,7 +232,7 @@ class FillStoreForm {
             $formElement = $this->evaluate->parseArray($formElement, $skip, $debugStack);
 
             // Get related formElement. Construct the field name used in the form.
-            $clientFieldName = HelperFormElement::buildFormElementName($formElement, $fakeRecordId);
+            $clientFieldName = ($formMode == FORM_REST) ? $formElement[FE_NAME] : HelperFormElement::buildFormElementName($formElement, $fakeRecordId);
 
             // Some Defaults
             $formElement = Support::setFeDefaults($formElement, [F_MODE => $formModeGlobal]);
@@ -247,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;
             }
 
@@ -273,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])) {
 
@@ -309,7 +319,7 @@ class FillStoreForm {
                             // Check only if there is something.
                             if ($val !== '' && $formMode != FORM_UPDATE && $formElement[FE_MODE] != FE_MODE_HIDDEN) {
                                 $val = Sanitize::sanitize($val, $formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN],
-                                    $formElement[FE_DECIMAL_FORMAT], SANITIZE_EXCEPTION, $formElement[F_FE_DATA_PATTERN_ERROR]??'');
+                                    $formElement[FE_DECIMAL_FORMAT], SANITIZE_EXCEPTION, $formElement[F_FE_DATA_PATTERN_ERROR] ?? '');
 
                                 if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
 //                                    $val = htmlspecialchars($val, ENT_QUOTES);
diff --git a/extension/Source/core/store/Store.php b/extension/Source/core/store/Store.php
index a48ecd201834521f1a2b653ae608d8af38aaf1f6..8460533faf4e68ec250db29530f796201b390ff9 100644
--- a/extension/Source/core/store/Store.php
+++ b/extension/Source/core/store/Store.php
@@ -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,
@@ -281,6 +282,8 @@ class Store {
             }
         }
 
+        Logger::setSystemSitePath($config[SYSTEM_SITE_PATH]);
+
         return $config;
     }
 
diff --git a/extension/Source/sql/formEditor.sql b/extension/Source/sql/formEditor.sql
index 7c626b9865acd0b901cdcaa2080a05e09e93e914..884e52421f88d42b55dfd58e62b94bb4014be18a 100644
--- a/extension/Source/sql/formEditor.sql
+++ b/extension/Source/sql/formEditor.sql
@@ -7,17 +7,18 @@ CREATE TABLE IF NOT EXISTS `Form`
   `id`                       INT(11)                                                     NOT NULL AUTO_INCREMENT,
   `name`                     VARCHAR(255)                                                NOT NULL DEFAULT '',
   `title`                    VARCHAR(511)                                                NOT NULL DEFAULT '',
-  `noteInternal`             TEXT                                                        NOT NULL,
-  `tableName`                VARCHAR(255)                                                NOT NULL DEFAULT '',
-  `primaryKey`               VARCHAR(255)                                                NOT NULL DEFAULT '',
-
-  `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',
-  `escapeTypeDefault`        VARCHAR(32)                                                 NOT NULL DEFAULT 'c',
-  `render`                   ENUM ('bootstrap', 'table', 'plain')                        NOT NULL DEFAULT 'bootstrap',
-  `requiredParameterNew`     VARCHAR(255)                                                NOT NULL DEFAULT '',
-  `requiredParameterEdit`    VARCHAR(255)                                                NOT NULL DEFAULT '',
-  `dirtyMode`                ENUM ('exclusive', 'advisory', 'none')                      NOT NULL DEFAULT 'exclusive',
+  `noteInternal`          TEXT                                                       NOT NULL,
+  `tableName`             VARCHAR(255)                                               NOT NULL DEFAULT '',
+  `primaryKey`            VARCHAR(255)                                               NOT NULL DEFAULT '',
+
+  `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 '',
+  `requiredParameterEdit` VARCHAR(255)                                               NOT NULL DEFAULT '',
+  `dirtyMode`             ENUM ('exclusive', 'advisory', 'none')                     NOT NULL DEFAULT 'exclusive',
   `showButton`               SET ('new', 'delete', 'close', 'save')                      NOT NULL DEFAULT 'new,delete,close,save',
   `multiMode`                ENUM ('none', 'horizontal', 'vertical')                     NOT NULL DEFAULT 'none',
   `multiSql`                 TEXT                                                        NOT NULL,
@@ -63,29 +64,29 @@ CREATE TABLE IF NOT EXISTS `FormElement`
   `enabled`            ENUM ('yes', 'no')                                                         NOT NULL DEFAULT 'yes',
 
   `name`               VARCHAR(255)                                                               NOT NULL DEFAULT '',
-  `label`              VARCHAR(511)                                                               NOT NULL DEFAULT '',
+  `label`              VARCHAR(511)                                    NOT NULL DEFAULT '',
 
-  `mode`               ENUM ('show', 'required', 'readonly', 'hidden')                            NOT NULL DEFAULT 'show',
-  `modeSql`            TEXT                                                                       NOT NULL,
-  `class`              ENUM ('native', 'action', 'container')                                     NOT NULL DEFAULT 'native',
+  `mode`               ENUM ('show', 'required', 'readonly', 'hidden') NOT NULL DEFAULT 'show',
+  `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',
-  `subrecordOption`    SET ('edit', 'delete', 'new')                                              NOT NULL DEFAULT '',
-  `encode`             ENUM ('none', 'specialchar')                                               NOT NULL DEFAULT 'specialchar',
+                             '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',
-  `checkPattern`       VARCHAR(255)                                                               NOT NULL DEFAULT '',
+                             'all')                                    NOT NULL DEFAULT 'auto',
+  `checkPattern`       VARCHAR(255)                                    NOT NULL DEFAULT '',
 
-  `onChange`           VARCHAR(255)                                                               NOT NULL DEFAULT '',
+  `onChange`           VARCHAR(255)                                    NOT NULL DEFAULT '',
 
-  `ord`                INT(11)                                                                    NOT NULL DEFAULT '0',
-  `tabindex`           INT(11)                                                                    NOT NULL DEFAULT '0',
+  `ord`                INT(11)                                         NOT NULL DEFAULT '0',
+  `tabindex`           INT(11)                                         NOT NULL DEFAULT '0',
 
-  `size`               VARCHAR(255)                                                               NOT NULL DEFAULT '',
+  `size`               VARCHAR(255)                                    NOT NULL DEFAULT '',
   `maxLength`          VARCHAR(255)                                                               NOT NULL DEFAULT '',
   `labelAlign`         ENUM ('default', 'left', 'center', 'right')                                NOT NULL DEFAULT 'default',
   `bsLabelColumns`     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,
@@ -185,10 +185,15 @@ INSERT INTO FormElement (formId, name, label, mode, type, checkType, class, ord,
                          checkPattern)
 VALUES
   # Make the form a 'delete form' for records Form/FormElement.
-  (1, 'Delete FE', '', 'show', 'beforeDelete', 'all', 'action', 10, 0, 0, '', '', '', '',
+  (1, '', 'Delete FE', 'show', 'beforeDelete', 'all', 'action', 10, 0, 0, '', '', '', '',
    'sqlAfter={{DELETE FROM FormElement WHERE formId={{id:R}} }}',
    0, '', '', '', 'none', 'no', ''),
 
+  # Check for double form name
+  (1, '', 'Check for double form name', 'show', 'beforeSave', 'all', 'action', 20, 0, 0, '', '', '', '',
+   'sqlValidate={{!SELECT f.id FROM Form AS f WHERE  f.name!="" AND f.name="{{name:F:alnumx}}" AND f.id!={{id:R0}}  }}\nexpectRecords=0\nmessageFail=There is already another form with the name "{{name:F:alnumx}}".',
+   0, '', '', '', 'none', 'no', ''),
+
   # Basic
   (1, 'name', 'Name', 'required', 'text', 'pattern', 'native', 110, 0, 0,
    '<a href="{{documentation:Y}}#form-name">Info</a>', '', '', '', 'autofocus',
@@ -257,18 +262,24 @@ 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
@@ -288,136 +299,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,
+        '<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');
 
 INSERT INTO `FormElement` (`id`, `formId`, `feIdContainer`, `dynamicUpdate`, `enabled`, `name`, `label`, `mode`,
                            `modeSql`, `class`, `type`, `subrecordOption`, `encode`, `checkType`, `checkPattern`,
@@ -426,11 +452,11 @@ INSERT INTO `FormElement` (`id`, `formId`, `feIdContainer`, `dynamicUpdate`, `en
                            `sql1`, `parameter`, `parameterLanguageA`, `parameterLanguageB`, `parameterLanguageC`,
                            `parameterLanguageD`, `clientJs`, `feGroup`, `deleted`)
 
-VALUES
-(NULL, '2', '0', 'no', 'yes', 'Check Name Conflict', '', 'show', '', 'action', 'beforeSave', '', 'specialchar', 'auto',
- '', '', '650', '0', '', '', '', '', '', 'row,label,/label,input,/input,note,/note,/row', '', '', '', '', '', '',
- 'sqlValidate={{!SELECT fe.id FROM FormElement AS fe WHERE "{{class:F:alnumx}}"=fe.class AND fe.formId={{formId:RF}} AND fe.name!="" AND fe.name="{{name:F:alnumx}}" AND fe.id!={{id:R0}} }}\r\n\r\nexpectRecords=0\r\n\r\nmessageFail=There is already another {{class:F:alnumx}} form element whith name "{{name:F:alnumx}}".',
- '', '', '', '', '', '', 'no');
+VALUES (NULL, '2', '0', 'no', 'yes', 'Check Name Conflict', '', 'show', '', 'action', 'beforeSave', '', 'specialchar',
+        'auto',
+        '', '', '650', '0', '', '', '', '', '', 'row,label,/label,input,/input,note,/note,/row', '', '', '', '', '', '',
+        'sqlValidate={{!SELECT fe.id FROM FormElement AS fe WHERE "{{class:F:alnumx}}"=fe.class AND fe.formId={{formId:RF}} AND fe.name!="" AND fe.name="{{name:F:alnumx}}" AND fe.id!={{id:R0}} }}\r\n\r\nexpectRecords=0\r\n\r\nmessageFail=There is already another {{class:F:alnumx}} form element whith name "{{name:F:alnumx}}".',
+        '', '', '', '', '', '', 'no');
 
 # ----------------------------------------
 # MailLog
@@ -444,6 +470,8 @@ CREATE TABLE IF NOT EXISTS `MailLog`
   `xId2`     INT(11)       NOT NULL DEFAULT '0',
   `xId3`     INT(11)       NOT NULL DEFAULT '0',
   `receiver` TEXT          NOT NULL,
+  `cc`       TEXT          NOT NULL,
+  `bcc`      TEXT          NOT NULL,
   `sender`   VARCHAR(255)  NOT NULL DEFAULT '',
   `subject`  VARCHAR(255)  NOT NULL DEFAULT '',
   `body`     TEXT          NOT NULL,
@@ -501,25 +529,24 @@ CREATE TABLE IF NOT EXISTS `Clipboard`
 
 # Form: CopyForm
 INSERT INTO Form (id, name, title, tableName, showButton, forwardMode, forwardPage, parameter)
-VALUES
-(3, 'copyForm', 'Copy a form', 'Clipboard', 'close,save', 'url-sip', '?id={{pageId:T}}&form=form&r={{formId:P0}}',
- 'submitButtonText = Copy Form');
+VALUES (3, 'copyForm', 'Copy a form', 'Clipboard', 'close,save', 'url-sip',
+        '?id={{pageId:T}}&form=form&r={{formId:P0}}',
+        'submitButtonText = Copy Form');
 
 # FormElements: CopyForm
 INSERT INTO FormElement (formId, name, label, type, class, ord, sql1, parameter)
-VALUES
-(3, 'idSrc', 'Source Form', 'select', 'native', 10,
- '{{!SELECT f.id, CONCAT(f.name, " / ", f.title) FROM Form AS f ORDER BY f.name}}', ''),
-(3, 'myNewFormName', 'New Form Name', 'text', 'native', 20, '', ''),
-(3, 'clearClipboard', '', 'beforeSave', 'action', 100, '',
- 'sqlValidate={{!SELECT f.id FROM Form AS f WHERE f.name LIKE "{{myName:FE:alnumx}}" LIMIT 1}}\nexpectRecords = 0\nmessageFail = There is already a form with this name\nsqlAfter={{DELETE FROM Clipboard WHERE cookie="{{cookieQfq:C0:alnumx}}" }}'),
-(3, 'updateClipboardRecord', '', 'afterSave', 'action', 110, '',
- 'sqlAfter={{UPDATE Clipboard AS c, Form AS f SET c.cookie="{{cookieQfq:C0:alnumx}}", c.formIdPaste=f.id /* PasteForm */  WHERE c.id={{id:R}} AND f.name="{{form:SE}}" }}'),
-(3, 'formId', '', 'paste', 'action', 200, '{{!SELECT {{id:P}} AS id, "{{myNewFormName:FE:allbut}}" AS name}}',
- 'recordDestinationTable=Form'),
-(3, 'formElementId', '', 'paste', 'action', 210,
- '{{!SELECT fe.id AS id, {{formId:P}} AS formId FROM FormElement AS fe WHERE fe.formId={{id:P}} ORDER BY fe.ord}}',
- 'recordDestinationTable=FormElement\ntranslateIdColumn=feIdContainer');
+VALUES (3, 'idSrc', 'Source Form', 'select', 'native', 10,
+        '{{!SELECT f.id, CONCAT(f.name, " / ", f.title) FROM Form AS f ORDER BY f.name}}', ''),
+       (3, 'myNewFormName', 'New Form Name', 'text', 'native', 20, '', ''),
+       (3, 'clearClipboard', '', 'beforeSave', 'action', 100, '',
+        'sqlValidate={{!SELECT f.id FROM Form AS f WHERE f.name LIKE "{{myName:FE:alnumx}}" LIMIT 1}}\nexpectRecords = 0\nmessageFail = There is already a form with this name\nsqlAfter={{DELETE FROM Clipboard WHERE cookie="{{cookieQfq:C0:alnumx}}" }}'),
+       (3, 'updateClipboardRecord', '', 'afterSave', 'action', 110, '',
+        'sqlAfter={{UPDATE Clipboard AS c, Form AS f SET c.cookie="{{cookieQfq:C0:alnumx}}", c.formIdPaste=f.id /* PasteForm */  WHERE c.id={{id:R}} AND f.name="{{form:SE}}" }}'),
+       (3, 'formId', '', 'paste', 'action', 200, '{{!SELECT {{id:P}} AS id, "{{myNewFormName:FE:allbut}}" AS name}}',
+        'recordDestinationTable=Form'),
+       (3, 'formElementId', '', 'paste', 'action', 210,
+        '{{!SELECT fe.id AS id, {{formId:P}} AS formId FROM FormElement AS fe WHERE fe.formId={{id:P}} ORDER BY fe.ord}}',
+        'recordDestinationTable=FormElement\ntranslateIdColumn=feIdContainer');
 
 # AutoCRON
 CREATE TABLE IF NOT EXISTS `Cron`
@@ -550,47 +577,50 @@ CREATE TABLE IF NOT EXISTS `Cron`
 
 # Form: AutoCron
 INSERT INTO Form (id, name, title, tableName, parameter, dirtyMode)
-VALUES
-(4, 'cron', 'autoCron', 'Cron', 'dbIndex={{indexQfq:Y}}',
- 'none');
+VALUES (4, 'cron', 'autoCron', 'Cron', 'dbIndex={{indexQfq:Y}}',
+        'none');
 
 # FormElements: AutoCron
 INSERT INTO FormElement (formId, name, label, mode, modeSql, type, encode, checkType, ord, parameter, size, note,
                          dynamicUpdate, bsLabelColumns, bsInputColumns, bsNoteColumns)
-VALUES
-(4, 'status', 'Enabled', 'show', '', 'checkbox', 'specialchar', 'alnumx', 10, '', '', '', 'no', '', '', ''),
-(4, 'type', 'Type', 'show', '', 'radio', 'specialchar', 'alnumx', 20, 'buttonClass=btn-default', '', '', 'yes', '', '',
- ''),
-(4, 'nextRun', 'Next run', 'show', '', 'text', 'specialchar', 'alnumx', 30,
- 'extraButtonInfo = Cronjob will be started if specified timestamp is over. If timestamp=0: Job will never be started<br>Every time the jobs runs, this timestamp will be increased automatically by "frequency".',
- '', '', 'no', '', '', ''),
-(4, 'frequency', 'Frequency', 'show', '', 'text', 'specialchar', 'alnumx', 40,
- 'extraButtonInfo = Repeat AutoCron-job with the specified interval. If empty: no repeating.<br>E.g.: "1 DAY", "15 MINUTE'', "6 MONTH" - used directly in SQL-Function "DATE_ADD(&lt;nextrun&gt;, INTERVAL &lt;frequency&gt;)"',
- '', '', 'no', '', '', ''),
-(4, 'comment', 'Comment', 'show', '', 'text', 'specialchar', 'allbut', 50, '', '', '', 'no', '', '', ''),
-(4, 'sql1', 'Mail', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","show","hidden") }}', 'text', 'none', 'all', 60,
- 'extraButtonInfo = Query: &#123;&#123;!SELECT ... as sendMailTo...&#125;&#125;<br><b>sendMailTo / sendMailCc / sendMailBcc</b>: Separate multiple by comma.<br><b>sendMailFrom</b><br><b>sendMailSubject</b><br><b>sendMailReplyTo</b>: Optional<br><b>sendMailFlagAutoSubmit</b>: Optional. on|off. Default on - if "on", suppresses OoO answers from receivers.<br><b>sendMailGrId</b>: Optional<br><b>sendMailXId</b>: Optional',
- '60,4', '', 'yes', '', '', ''),
-(4, 'content', '{{SELECT IF("{{type:FR:alnumx}}"="mail","Mail body","URL") }}', 'show', '', 'text', 'none', 'all', 70,
- '', '40,4',
- 'Website: URL absolute like "http://..." or relative like "?id=pagealias..."<br>Mail: Static Body or &#123;{SELECT ...&#125;}',
- 'yes', '', '', ''),
-
-(4, 'outputFile', 'Log output to file', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}', 'text',
- 'none', 'all', 80, '', '', 'CWD: Site installation directory', 'yes', '', '', ''),
-(4, 'outputMode', 'Mode output', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}', 'radio',
- 'specialchar', 'alnumx', 90, 'buttonClass=btn-default', '', '', 'yes', '', '', ''),
-(4, 'outputPattern', 'Pattern to look for on output', 'show',
- '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}', 'text', 'none', 'all', 100, '', '',
- 'If pattern isn\'t found, return an error.<br>Check <a href="https://secure.php.net/manual/en/pcre.pattern.php">pcre</a> / <a href="https://regexp101.com">regexp101.com</a> ',
- 'yes', '', '', ''),
-
-(4, 'lastRun', 'Last run', 'readonly', '', 'text', 'specialchar', 'alnumx', 120, '', '', '', 'no', '', '', ''),
-(4, 'lastStatus', 'Laststatus', 'readonly', '', 'text', 'specialchar', 'alnumx', 130, '', '50,6', '', 'no', '3', '9',
- '0'),
-(4, 'inProgress', 'In progress since', 'show', '', 'text', 'specialchar', 'alnumx', 140,
- 'extraButtonInfo = Start time of a running job. When job is finished, this will be set back to 0. A new job will only be started, if this is 0. A progress duration >10mins will be treated as an error.',
- '', '', 'no', '', '', '');
+VALUES (4, 'status', 'Enabled', 'show', '', 'checkbox', 'specialchar', 'alnumx', 10, '', '', '', 'no', '', '', ''),
+       (4, 'type', 'Type', 'show', '', 'radio', 'specialchar', 'alnumx', 20, 'buttonClass=btn-default', '', '', 'yes',
+        '', '',
+        ''),
+       (4, 'nextRun', 'Next run', 'show', '', 'text', 'specialchar', 'alnumx', 30,
+        'extraButtonInfo = Cronjob will be started if specified timestamp is over. If timestamp=0: Job will never be started<br>Every time the jobs runs, this timestamp will be increased automatically by "frequency".',
+        '', '', 'no', '', '', ''),
+       (4, 'frequency', 'Frequency', 'show', '', 'text', 'specialchar', 'alnumx', 40,
+        'extraButtonInfo = Repeat AutoCron-job with the specified interval. If empty: no repeating.<br>E.g.: "1 DAY", "15 MINUTE'', "6 MONTH" - used directly in SQL-Function "DATE_ADD(&lt;nextrun&gt;, INTERVAL &lt;frequency&gt;)"',
+        '', '', 'no', '', '', ''),
+       (4, 'comment', 'Comment', 'show', '', 'text', 'specialchar', 'allbut', 50, '', '', '', 'no', '', '', ''),
+       (4, 'sql1', 'Mail', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","show","hidden") }}', 'text', 'none', 'all',
+        60,
+        'extraButtonInfo = Query: &#123;&#123;!SELECT ... as sendMailTo...&#125;&#125;<br><b>sendMailTo / sendMailCc / sendMailBcc</b>: Separate multiple by comma.<br><b>sendMailFrom</b><br><b>sendMailSubject</b><br><b>sendMailReplyTo</b>: Optional<br><b>sendMailFlagAutoSubmit</b>: Optional. on|off. Default on - if "on", suppresses OoO answers from receivers.<br><b>sendMailGrId</b>: Optional<br><b>sendMailXId</b>: Optional',
+        '60,4', '', 'yes', '', '', ''),
+       (4, 'content', '{{SELECT IF("{{type:FR:alnumx}}"="mail","Mail body","URL") }}', 'show', '', 'text', 'none',
+        'all', 70,
+        '', '40,4',
+        'Website: URL absolute like "http://..." or relative like "?id=pagealias..."<br>Mail: Static Body or &#123;{SELECT ...&#125;}',
+        'yes', '', '', ''),
+
+       (4, 'outputFile', 'Log output to file', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}',
+        'text',
+        'none', 'all', 80, '', '', 'CWD: Site installation directory', 'yes', '', '', ''),
+       (4, 'outputMode', 'Mode output', 'show', '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}', 'radio',
+        'specialchar', 'alnumx', 90, 'buttonClass=btn-default', '', '', 'yes', '', '', ''),
+       (4, 'outputPattern', 'Pattern to look for on output', 'show',
+        '{{SELECT IF("{{type:FR:alnumx}}"="mail","hidden","show") }}', 'text', 'none', 'all', 100, '', '',
+        'If pattern isn\'t found, return an error.<br>Check <a href="https://secure.php.net/manual/en/pcre.pattern.php">pcre</a> / <a href="https://regexp101.com">regexp101.com</a> ',
+        'yes', '', '', ''),
+
+       (4, 'lastRun', 'Last run', 'readonly', '', 'text', 'specialchar', 'alnumx', 120, '', '', '', 'no', '', '', ''),
+       (4, 'lastStatus', 'Laststatus', 'readonly', '', 'text', 'specialchar', 'alnumx', 130, '', '50,6', '', 'no', '3',
+        '9',
+        '0'),
+       (4, 'inProgress', 'In progress since', 'show', '', 'text', 'specialchar', 'alnumx', 140,
+        'extraButtonInfo = Start time of a running job. When job is finished, this will be set back to 0. A new job will only be started, if this is 0. A progress duration >10mins will be treated as an error.',
+        '', '', 'no', '', '', '');
 
 CREATE TABLE IF NOT EXISTS `Split`
 (
diff --git a/extension/Tests/unit/core/BuildFormPlainTest.php b/extension/Tests/unit/core/BuildFormPlainTest.php
index d3291aecf95d593f13faacb7201c8d746dc1af76..acb3050b5150456cc8cea23204c5516bd91ff671 100644
--- a/extension/Tests/unit/core/BuildFormPlainTest.php
+++ b/extension/Tests/unit/core/BuildFormPlainTest.php
@@ -544,6 +544,7 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
 
         $result = $build->buildSubrecord($formElement, 'name:1', '', $json);
         $this->assertEquals('<table class="table table-hover qfq-subrecord-table" ><thead><tr><th>id</th><th>name</th><th>firstName</th></tr></thead><tbody ><tr class="record" ><td><span class="text-muted">1</span></td><td>Doe</td><td>John</td></tr><tr class="record" ><td><span class="text-muted">2</span></td><td>Smith</td><td>Jane</td></tr></tbody></table>', $result);
+//        $this->assertEquals('Please save this record first.', $result);
 
         $this->store->setStore(['id' => 1], STORE_RECORD, true);
         $result = $build->buildSubrecord($formElement, 'name:1', '', $json);
diff --git a/extension/Tests/unit/core/database/AbstractDatabaseTest.php b/extension/Tests/unit/core/database/AbstractDatabaseTest.php
index ffd86b357c08d78d5f5b722d0a7ffd3e1600660f..0f68e57073a94c473888ea2dfbe2b5c87ea81d4c 100644
--- a/extension/Tests/unit/core/database/AbstractDatabaseTest.php
+++ b/extension/Tests/unit/core/database/AbstractDatabaseTest.php
@@ -19,7 +19,7 @@ abstract class AbstractDatabaseTest extends TestCase {
     static protected $mysqli = null;
 
     /**
-     * @var Database
+     * @var array
      */
     protected $dbArray = array();
 
diff --git a/extension/Tests/unit/core/helper/OnStringTest.php b/extension/Tests/unit/core/helper/OnStringTest.php
index bfdbabf9f6e1db575d8a1749b66a444956312a27..f6da80295e3e85e7c90f2bc44b86786ea1eeeca4 100644
--- a/extension/Tests/unit/core/helper/OnStringTest.php
+++ b/extension/Tests/unit/core/helper/OnStringTest.php
@@ -78,6 +78,79 @@ class OnStringTest extends TestCase {
             OnString::removeNewlinesInNestedExpression("sqlInsert = {{SELECT *\nFROM test\nWHERE '{{var}}'='true'}}\nparam1=abc"));
     }
 
+    /**
+     * @throws UserFormException
+     */
+    public function testExtractFormRecordId() {
+        $arrId = array();
+        $arrForm = array();
+
+        $this->assertEquals('', OnString::splitPathInfoToIdForm('', $arrId, $arrForm));
+        $this->assertEquals([], $arrId);
+        $this->assertEquals([], $arrForm);
+
+        $this->assertEquals('', OnString::splitPathInfoToIdForm('/', $arrId, $arrForm));
+        $this->assertEquals([], $arrId);
+        $this->assertEquals([], $arrForm);
+
+        $this->assertEquals('', OnString::splitPathInfoToIdForm('//', $arrId, $arrForm));
+        $this->assertEquals([], $arrId);
+        $this->assertEquals([], $arrForm);
+
+        $this->assertEquals('path1', OnString::splitPathInfoToIdForm('/path1', $arrId, $arrForm));
+        $this->assertEquals([0], $arrId);
+        $this->assertEquals(['path1'], $arrForm);
+
+        $this->assertEquals('path1', OnString::splitPathInfoToIdForm('/path1/', $arrId, $arrForm));
+        $this->assertEquals([0], $arrId);
+        $this->assertEquals(['path1'], $arrForm);
+
+        $this->assertEquals('path1', OnString::splitPathInfoToIdForm('path1/', $arrId, $arrForm));
+        $this->assertEquals([0], $arrId);
+        $this->assertEquals(['path1'], $arrForm);
+
+        $this->assertEquals('path1', OnString::splitPathInfoToIdForm('/path1/12', $arrId, $arrForm));
+        $this->assertEquals([12], $arrId);
+        $this->assertEquals(['path1'], $arrForm);
+
+        $this->assertEquals('path1', OnString::splitPathInfoToIdForm('/path1/12/', $arrId, $arrForm));
+        $this->assertEquals([12], $arrId);
+        $this->assertEquals(['path1'], $arrForm);
+
+        $this->assertEquals('path2', OnString::splitPathInfoToIdForm('/path1/12/path2', $arrId, $arrForm));
+        $this->assertEquals([12, 0], $arrId);
+        $this->assertEquals(['path1', 'path2'], $arrForm);
+
+        $this->assertEquals('path2', OnString::splitPathInfoToIdForm('/path1/12/path2/34', $arrId, $arrForm));
+        $this->assertEquals([12, 34], $arrId);
+        $this->assertEquals(['path1', 'path2'], $arrForm);
+
+    }
+
+    /**
+     * @expectedException \qfq\UserFormException
+     *
+     */
+    public function testExtractFormRecordId_1() {
+        $arrId = array();
+        $arrForm = array();
+
+        # An alnum string is requested as path
+        $this->assertEquals('', OnString::splitPathInfoToIdForm('/%', $arrId, $arrForm));
+    }
+
+    /**
+     * @expectedException \qfq\UserFormException
+     *
+     */
+    public function testExtractFormRecordId_2() {
+        $arrId = array();
+        $arrForm = array();
+
+        # A numerical value is requested as id
+        $this->assertEquals('', OnString::splitPathInfoToIdForm('/path1/path2', $arrId, $arrForm));
+    }
+
     /**
      * @expectedException \qfq\UserFormException
      *
@@ -85,6 +158,7 @@ class OnStringTest extends TestCase {
     public function testRemoveNewlinesInNestedExpression_missingClosing_1() {
         $str = OnString::removeNewlinesInNestedExpression("Hi! {{Test ");
     }
+
     /**
      * @expectedException \qfq\UserFormException
      *
@@ -92,6 +166,7 @@ class OnStringTest extends TestCase {
     public function testRemoveNewlinesInNestedExpression_missingClosing_2() {
         $str = OnString::removeNewlinesInNestedExpression("Hi! {{Test}");
     }
+
     /**
      * @expectedException \qfq\UserFormException
      *
@@ -107,6 +182,7 @@ class OnStringTest extends TestCase {
     public function testRemoveNewlinesInNestedExpression_missingOpening_1() {
         $str = OnString::removeNewlinesInNestedExpression("}}");
     }
+
     /**
      * @expectedException \qfq\UserFormException
      *
@@ -114,6 +190,7 @@ class OnStringTest extends TestCase {
     public function testRemoveNewlinesInNestedExpression_missingOpening_2() {
         $str = OnString::removeNewlinesInNestedExpression("{}}");
     }
+
     /**
      * @expectedException \qfq\UserFormException
      *
diff --git a/extension/Tests/unit/core/store/StoreTest.php b/extension/Tests/unit/core/store/StoreTest.php
index 25f025a32f64c76e39bc865c78ac9380277dfebe..c9b0a68daaadf1517a0e3e86dcae63bd4ebd5fb5 100644
--- a/extension/Tests/unit/core/store/StoreTest.php
+++ b/extension/Tests/unit/core/store/StoreTest.php
@@ -10,6 +10,8 @@ namespace qfq;
 
 use PHPUnit\Framework\TestCase;
 
+#require_once(__DIR__ . '/../../../../Source/core/Constants.php');
+
 /**
  * Class StoreTest
  * @package qfq
@@ -31,7 +33,6 @@ class StoreTest extends TestCase {
     public function setUp() {
         // Client Variables has to setup before the first instantiation of 'Store'
         $_GET[CLIENT_RECORD_ID] = '1234';
-//        $_GET[CLIENT_SIP] = '12badcaffee34';
         $_GET['key01'] = '1234';
         $_POST['key02'] = '2345';
         $_POST['key03'] = '3456';
@@ -400,8 +401,9 @@ class StoreTest extends TestCase {
             'LDAP_1_RDN' => 'LDAP_1_RDN',
             'LDAP_1_PASSWORD' => 'LDAP_1_PASSWORD',
             SYSTEM_LABEL_ALIGN => SYSTEM_LABEL_ALIGN_LEFT,
-            F_FE_DATA_PATTERN_ERROR=> F_FE_DATA_PATTERN_ERROR_DEFAULT,
-            F_FE_DATA_PATTERN_ERROR_SYSTEM=> F_FE_DATA_PATTERN_ERROR_DEFAULT,
+            F_FE_DATA_PATTERN_ERROR => F_FE_DATA_PATTERN_ERROR_DEFAULT,
+            F_FE_DATA_PATTERN_ERROR_SYSTEM => F_FE_DATA_PATTERN_ERROR_DEFAULT,
+            SYSTEM_SECURITY_FAILED_AUTH_DELAY => '3',
         ];
 
         $body = <<< EOT
@@ -439,7 +441,7 @@ EOT;
         # The following won't be checked by content, cause they will change on different installations.
 //        foreach ([ SYSTEM_SQL_LOG, SYSTEM_MAIL_LOG, SYSTEM_SITE_PATH, SYSTEM_EXT_PATH, SYSTEM_SEND_E_MAIL] as $key) {
         foreach ([SYSTEM_SITE_PATH, SYSTEM_EXT_PATH, SYSTEM_SEND_E_MAIL, SYSTEM_SESSION_TIMEOUT_SECONDS] as $key) {
-            $this->assertTrue(isset($config[$key]), "Missing default value for '$key' "  );
+            $this->assertTrue(isset($config[$key]), "Missing default value for '$key' ");
             unset ($config[$key]);
         }
         // check default values
diff --git a/extension/composer.json b/extension/composer.json
index 4d7f9be159cb5011e343fec52e3f71faf00bb61a..7a4d37ab298f30bde3d206f0b3f1b56f9fed1d57 100644
--- a/extension/composer.json
+++ b/extension/composer.json
@@ -1,23 +1,24 @@
 {
-    "require": {
-        "phpoffice/phpspreadsheet": "^1.3"
-    },
-    "require-dev": {
-        "phpunit/phpunit": "^6.5"
-    },
-    "autoload": {
-        "psr-4": {
-            "qfq\\": ["qfq/",
-                "Source/api/",
-                "Source/external/",
-                "Source/core/",
-                "Source/core/database/",
-                "Source/core/exceptions/",
-                "Source/core/form/",
-                "Source/core/helper/",
-                "Source/core/report/",
-                "Source/core/store/",
-                "Source/core/typo3/"]
-        }
+  "require": {
+    "phpoffice/phpspreadsheet": "^1.3",
+    "ext-json": "*"
+  },
+  "require-dev": {
+    "phpunit/phpunit": "^6.5"
+  },
+  "autoload": {
+    "psr-4": {
+      "qfq\\": ["qfq/",
+        "Source/api/",
+        "Source/external/",
+        "Source/core/",
+        "Source/core/database/",
+        "Source/core/exceptions/",
+        "Source/core/form/",
+        "Source/core/helper/",
+        "Source/core/report/",
+        "Source/core/store/",
+        "Source/core/typo3/"]
     }
+  }
 }
diff --git a/extension/ext_conf_template.txt b/extension/ext_conf_template.txt
index 68abad4f0fee03c2fbde355b0b241da84446c8c7..2f680469f10c81da671a44a8392f3521494b77e7 100644
--- a/extension/ext_conf_template.txt
+++ b/extension/ext_conf_template.txt
@@ -103,6 +103,9 @@ securityShowMessage = true
 # cat=security/security; type=string; label='GET'-Parameter max length:Default is '50'. GET vars longer than 'x' character triggers an `attack-detected`.
 securityGetMaxLength = 50
 
+# cat=security/security; type=string; label=Failed auth delay in seconds:Default is '3'.
+securityFailedAuthDelay = 3
+
 # cat=security/security; type=string; label=Session Timeout in seconds:Default is empty to take the php.ini system value (minimum of  'session.cookie_lifetime' and 'session.gc_maxlifetime').
 sessionTimeoutSeconds =
 
diff --git a/extension/ext_emconf.php b/extension/ext_emconf.php
index 20d8b4384ebda07a80a64d0e42034682d29fddd0..cdb5a8ba98301bb7164c64b6ca5638f561cac36c 100644
--- a/extension/ext_emconf.php
+++ b/extension/ext_emconf.php
@@ -12,7 +12,7 @@ $EM_CONF[$_EXTKEY] = array(
     'dependencies' => 'fluid,extbase',
     'clearcacheonload' => true,
     'state' => 'stable',
-    'version' => '19.2.1',
+    'version' => '19.2.3',
     'constraints' => [
         'depends' => [
             'typo3' => '6.0.0-9.2.99',
diff --git a/version b/version
index 34716091d45cab9391a33ac72f571d91bba9c9ae..de562110479e33a1771add8df28fa71aed493fe2 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-19.2.1
+19.2.3