Commit 657266b7 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Manual.rst: restructered the part explaining QFQ variables.

parent 0710528e
......@@ -341,6 +341,7 @@ config.qfq.ini
| | | contains a local copy: typo3conf/ext/qfq/Documentation/html/Manual.html |
+-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+
Example: *typo3conf/config.qfq.ini*
::
......@@ -431,6 +432,19 @@ E.g. to setup a contact address and reuse the information inside your installati
{{ADMINISTRATIVE_CONTACT:Y}}, {{ADMINISTRATIVE_ADDRESS:Y}}, {{ADMINISTRATIVE_NAME}}
..
DB USER privileges
^^^^^^^^^^^^^^^^^^
The specified DB User needs privileges to the database of at least: SELECT / INSERT / UPDATE / DELETE / SHOW.
To apply automatically QFQ-'DB UPDATE' the following rights are mandatory too: CREATE / ALTER
To get access to the Typo3 installation, 'dbuser' should also have acces to the Typo3 Database with at least SELECT / INSERT / UPDATE / DELETE.
.. _`ExceptionMaxLength`:
Exception for SECURITY_GET_MAX_LENGTH
......@@ -622,106 +636,143 @@ File: `config.qfq.ini`_
.. _variables:
QFQ Variables
-------------
Variable
========
Most elements of a form or report specification might contain (QFQ) variables. Such a variable is surrounded by
double curly braces. There are three types of QFQ variables:
Most elements of a Form, FormElement or Report might contain (QFQ) variables. Such variables are surrounded by
double curly braces. Three different types of functionality are provided. Access to:
* STORE: zero or more stores might be specified to be searched for the given VarName. Syntax:
* `store-variables`_
* `sql-variables`_
* `column-variables`_
*{{VarName[:<store / prio>[:<sanitize class>[:<escape>]]]}}*
Some examples, including nesting::
* If no store is specified, the default is: FSRVD (=FORM > SIP > RECORD > VARS > DEFAULT)
* If the VarName not found in one store, the next store is searched,
until a value is found.
* If the varname is not found in any store, nothing is replaced - the '{{<varname>}}' remains.
* If anywhere along the line an empty string is found, this **is** a value: therefore, the search will stop.
# Store
#---------------------------------------------
{{r}}
{{index:FS}}
{{name:FS:alnumx:s}}
# SQL
#---------------------------------------------
{{SELECT name FROM person WHERE id=1234}}
* SQL statements. See also `sql-statement`_.
# Columns
#---------------------------------------------
{{10.pId}}
{{10.20.pId}}
*{{SQL Statement}}*
# Nesting
#---------------------------------------------
{{SELECT name FROM person WHERE id={{r}} }}
{{SELECT name FROM person WHERE id={{key1:C:alnumx}} }} # explained below
{{SELECT name FROM person WHERE id={{SELECT id FROM pf LIMIT 1}} }} # it's more efficient to use only one query
* Fields in a report statement. See `access-to-upper-column-values`_ and `syntax-of-report`_.
*{{<line identifier>.<column>}}*
Leading and trailing spaces inside curly braces are removed.
There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for '<line identifier>',
than for SQL keywords and than for VarNames.
* *{{ SELECT "Hello World" }}* becomes *{{SELECT "Hello World"}}*
* *{{ varname }}* becomes *{{varname}}*
All types might be nested with each other. There is no limit of nesting variables.
Types
-----
Very specific: Also, it's possible that the content of a variable is again (including curly braces) a not already
substituted variable - this is sometimes used in text templates, where the template is retrieved from a record and
specific locations in the text will be (automatically by QFQ) replaced by values from other sources.
.. _`store-variables`:
* General examples:
Store variables
^^^^^^^^^^^^^^^
*{{r}}*
Syntax: *{{VarName[:<store / prio>[:<sanitize class>[:<escape>]]]}}*
*{{index:FS}}*
* Example::
*{{name:FS:alnumx:s}}*
{{pId}}
{{pId:FSE}}
{{pId:FSE:digit}}
{{name:FSE:alnumx:m}}
*{{SELECT name FROM person WHERE id=1234}}*
* Zero or more stores might be specified to be searched for the given VarName.
* If no store is specified, the by default searched stores are: **FSRVD** (=FORM > SIP > RECORD > VARS > DEFAULT).
* If the VarName is not found in one store, the next store is searched, up to the last specified store.
* If the VarName is not found in any store, nothing is replaced - the string '{{<VarName>}}' remains.
* If anywhere along the line an empty string is found, this **is** a value: therefore, the search will stop.
*{{SELECT name FROM person WHERE id={{r}} }}* - nested variables.
See also:
*{{SELECT name FROM person WHERE id={{key1:C:alnumx}} }}* - nested variables.
* `store`_
* `variable-escape`_
* `sanitize-class`_
*{{10.pId}}*
*{{10.20.pId}}*
.. _`sql-variables`:
* Leading and trailing spaces inside curly braces are removed.
SQL variables
^^^^^^^^^^^^^
* *{{ SELECT "Hello World" }}* acts as *{{SELECT "Hello World"}}*
* *{{ varname }}* acts as *{{varname}}*
* The detection of an SQL command is case *insensitive*.
* Leading whitespace will be skipped.
* The following commands are interpreted as SQL commands:
URL Parameter
^^^^^^^^^^^^^
* SELECT
* INSERT, UPDATE, DELETE, REPLACE, TRUNCATE
* SHOW, DESCRIBE, EXPLAIN, SET
* URL (=GET) Parameter can be used in *forms* and *reports* as variables.
* If a value violates a parameter sanitize class, the value becomes an empty string.
* A SQL Statement might contain parameters, including additional SQL statements. Inner SQL queries will be executed first.
* All variables will be substituted one by one from inner to outer.
* The number of variables inside an input field or a SQL statement is not limited.
* A resultset of a SQL statement will be imploded over all: concat all columns of a row, concat all rows - there is no glue string.
* Example::
.. _`variable-escape`:
{{SELECT id, name FROM Person}}
{{SELECT id, name, IF({{feUser}}=0,'Yes','No') FROM Person WHERE id={{r:S}} }}
{{SELECT id, city FROM Address AS adr WHERE adr.accId={{SELECT id FROM Account AS acc WHERE acc.name={{feUser}} }} }}
Escape
^^^^^^
* Special case for `SELECT` input fields and FormElement.type=action `sqlValidate`. To deliver a result array specify an '!' before the SELECT: ::
Variables used in SQL Statements might cause trouble by using: NUL (ASCII 0), \\n, \\r, \\, ', ", and Control-Z.
{{!SELECT ...}}
To protect the web application the following `escape` types are available:
* 'm' - `real_escape_string() <http://php.net/manual/en/mysqli.real-escape-string.php>`_ (m = mysql)
* 'l' - LDAP search filter values will be escaped: `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_FILTER).
* 'L' - LDAP DN values will be escaped. `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_DN).
* 's' - single ticks will be escaped. str_replace() of ' against \\'.
* 'd' - double ticks will be escaped: str_replace() of " against \\".
* '-' - no escaping.
.. _`column-variables`:
* 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).
* Escaping is typically necessary for SQL or LDAP queries.
* Be careful when escaping nested variables. Best is to escape **only** the most outer variable.
* In `config.qfq.ini`_ a global `ESCAPE_TYPE_DEFAULT` can be defined. The configured escape type applies to all substituted
variables, who *do not* contain a *specific* escape type.
* Additionally a `defaultEscapeType` can be defined per `Form` (separate field in the Form Editor). This overwrites the
global definition of `config.qfq.ini`. By default, every `Form.defaultEscapeType` = 'c' (=config), which means the setting
in `config.qfq.ini`_.
* To suppress a default escape type, define the `escape type` = '-' on the specific variable. E.g.: `{{name:FE:alnumx:-}}`.
Column variables
^^^^^^^^^^^^^^^^
Syntax: *{{<level>.<column>}}*
only used in report to access outer columns. See `access-column-values`_:
* Fields in a report statement. See `access-column-values`_ and `syntax-of-report`_.
There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for '<line identifier>',
than for SQL keywords and than for VarNames.
All types might be nested with each other. There is no limit of nesting variables.
Very specific: Also, it's possible that the content of a variable is again (including curly braces) a not already
substituted variable - this is sometimes used in text templates, where the template is retrieved from a record and
specific locations in the text will be (automatically by QFQ) replaced by values from other sources.
.. _`sanitize-class`:
Sanitize class
^^^^^^^^^^^^^^
--------------
* If a value violates a parameter sanitize class, the value becomes an empty string.
* Per store there is a default if sanitizing applies and if yes, which class.
* Store *C* (Client=Browser) and store *F* (Form) will be sanitized with 'digit'.
* All values in Store *C* (Client=Browser) and store *F* (Form) will be sanitized:
* All `predefined-variable-names`_ have a specific default sanitize class. For these variables, it's not necessary
to specify a sanitize class.
* All other variables (Store: C, F) get by default the sanitize class defined in the corresponding form. If not defined
* All other variables (Store: C, F) get by default the sanitize class defined in the corresponding form. If not defined,
the default class is 'digit'.
* A default sanitize class can be overwritten by individual definition: *{{a:C:all}}*
* If there is a sanitized class specified, it applies to all given stores.
For QFQ variables and FormElements:
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| Name | Form | Query | Pattern |
......@@ -731,6 +782,15 @@ Sanitize class
| **digit** | Form | Query | [0-9] |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **numerical** | Form | Query | [0-9.-+] |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **allbut** | Form | Query | All characters allowed, but not [ ] { } % & \ #. The used regexp: '^[^\[\]{}%&\\#]+$', |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **all** | Form | Query | no sanitizing |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
Only in FormElement:
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **email** | Form | Query | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
......@@ -740,12 +800,33 @@ Sanitize class
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **pattern** | Form | | Compares the value against a regexp. |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **allbut** | Form | Query | All characters allowed, but not [ ] { } % & \ #. The used regexp: '^[^\[\]{}%&\\#]+$', |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **all** | Form | Query | no sanitizing |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
..
.. _`variable-escape`:
Escape
------
Variables used in SQL Statements might cause trouble by using: NUL (ASCII 0), \\n, \\r, \\, ', ", and Control-Z.
To protect the web application the following `escape` types are available:
* 'm' - `real_escape_string() <http://php.net/manual/en/mysqli.real-escape-string.php>`_ (m = mysql)
* 'l' - LDAP search filter values will be escaped: `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_FILTER).
* 'L' - LDAP DN values will be escaped. `ldap-escape() <http://php.net/manual/en/function.ldap-escape.php>`_ (LDAP_ESCAPE_DN).
* 's' - single ticks will be escaped. str_replace() of ' against \\'.
* 'd' - double ticks will be escaped: str_replace() of " against \\".
* '-' - 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).
* Escaping is typically necessary for SQL or LDAP queries.
* Be careful when escaping nested variables. Best is to escape **only** the most outer variable.
* In `config.qfq.ini`_ a global `ESCAPE_TYPE_DEFAULT` can be defined. The configured escape type applies to all substituted
variables, who *do not* contain a *specific* escape type.
* Additionally a `defaultEscapeType` can be defined per `Form` (separate field in the Form Editor). This overwrites the
global definition of `config.qfq.ini`. By default, every `Form.defaultEscapeType` = 'c' (=config), which means the setting
in `config.qfq.ini`_.
* To suppress a default escape type, define the `escape type` = '-' on the specific variable. E.g.: `{{name:FE:alnumx:-}}`.
Security
========
......@@ -861,7 +942,9 @@ By default the mime type of every uploaded file is checked against a whitelist o
a file can be (easily) faked by an attacker. This check is good to handle regular user file upload for specific file types. To
prevent attacks against uploading and executing malicous code this won't help.
Intstead prohibit the execution of user contributed files by the webserver config (`SecureDirectFileAccess`_).
Instead prohibit the execution of user contributed files by the webserver config (`SecureDirectFileAccess`_).
.. _`store`:
Store
=====
......@@ -1055,7 +1138,8 @@ Store: *TYPO3* (Bodytext) - T
| feUserGroup | FE groups of logged in Typo3 FE User | |
+-------------------------+-------------------------------------------------------------------+----------+
* **note**: not available
* **note**: not available:
* in :ref:`dynamic-update` or
* by *FormElement* class 'action' with type 'beforeSave', 'afterSave', 'beforeDelete', 'afterDelete'.
......@@ -1159,35 +1243,6 @@ Store: *SYSTEM* - Y
| sqlCount | computed during runtime, used for error reporting |
+-------------------------+--------------------------------------------------------------------------+
.. _`sql-statement`:
SQL Statement
-------------
* The detection of an SQL command is case *insensitive*.
* Leading whitespace will be skipped.
* The following commands are interpreted as SQL commands:
* SELECT
* INSERT, UPDATE, DELETE, REPLACE, TRUNCATE
* SHOW, DESCRIBE, EXPLAIN, SET
* A SQL Statement might contain parameters, including additional SQL statements. Inner SQL queries will be executed first.
* All variables will be substituted one by one from inner to outer.
* The number of variables inside an input field or a SQL statement is not limited.
* A resultset of a SQL statement will be imploded over all: concat all columns of a row, concat all rows - there is no glue string.
* Example::
{{SELECT id, name FROM Person}}
{{SELECT id, name, IF({{feUser}}=0,'Yes','No') FROM Vorlesung WHERE sem_id={{keySemId:Y}} }}
{{SELECT id, city FROM Address AS adr WHERE adr.pId={{SELECT id FROM Account AS acc WHERE acc.name={{feUser}} }} }}
* Special case for `SELECT` input fields and FormElement.type=action `sqlValidate`. To deliver a result array specify an '!' before the SELECT: ::
{{!SELECT ...}}
.. _LDAP:
LDAP
......@@ -3707,6 +3762,7 @@ FAQ
* A: The sanitize rule is violeted and therefore the value has been removed. Set {{<var>:<store>:all}} as a test.
Only STORE_CLIENT and STORE_FORM will be sanitized.
.. _`report`
Report
======
......@@ -3919,24 +3975,26 @@ Be careful to:
* write nothing else than whitespaces/newline behind an **open brace**
* the **closing brace** has to be alone on a line. ::
10.sql = SELECT 'hello world'
10.sql = SELECT 'Yearly Report'
20 {
sql = SELECT 'a new query'
sql = SELECT companyName FORM Company LIMIT 1
head = <h1>
tail = </h1>
}
30 {
sql = SELECT 'a third query'
head = <h1>
tail = </h1>
40 {
sql = SELECT 'a nested nested query'
sql = SELECT depName FROM Department
head = <p>
tail = </p>
5 {
sql = SELECT 'detailed information for department'
1.sql = SELECT name FROM Person LIMIT 7
1.head = Employees:
}
}
30.40.tail = End
30.5.tail = More will follow
50
......@@ -3944,31 +4002,30 @@ Be careful to:
sql = SELECT 'A query with braces on their own'
}
.. _`access-to-upper-column-values`:
.. _`access-column-values`:
Access to upper column values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Access column values
^^^^^^^^^^^^^^^^^^^^
Columns of the upper level result can be accessed via variables, eg. {{10.pId}} will be replaced by the value in the pId column.
Columns of the upper / outer level result can be accessed via variables, eg. {{10.pId}} will be replaced by the value in the pId column.
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Levels** |A report is divided into levels. Example 1 has 3 levels **10**, **20.25**, **20.25.10** |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Qualifier**|A level is divided into qualifiers **20.30.10** has 3 qualifiers **20**, **30**, **10** |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Levels** |A report is divided into levels. The Example has levels *10*, *20*, *30*, *30.5*, *30.5.1*, *50*
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Qualifier**|A level is divided into qualifiers *30.5.1* has 3 qualifiers *30*, *5*, *1*
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Root |Is a level with one qualifier. E.g.: 10 |
|levels** | |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Sub |Is a level with more than one qualifier. E.g. levels **20.25** and **20.30.10** |
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Sub |Is a level with more than one qualifier. E.g. levels *30.5* or *30.5.1*
|levels** | |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Child** |The level **20** has one child **20.25** |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Parent** |The level 20.25 has a parent **20** |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|**Example |**10** and **20** are root level and will be executed independently. **10** don't have a sub level. **20.25** will be executed as many times as **20** has row numbers. **20.30.10** won't be executed because there isn't |
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Child** |The level *30* has one child and child child: *30.5* and *30.5.1*
+-------------+------------------------------------------------------------------------------------------------------------------------+
|**Example |*10*, *20*, *30*, *50** are root level and will be completely processed one after each other.
| |**20.25** will be executed as many times as **20** has row numbers. **20.30.10** won't be executed because there isn't |
|explanation**|any **20.30** level |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+-------------+------------------------------------------------------------------------------------------------------------------------+
Report Example 1:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment