Commit fa635e98 authored by Carsten  Rose's avatar Carsten Rose

Merge branch 'F11119_REST_GET_POST_call_via_Report' into 'master'

F11119 rest get post call via report

See merge request !284
parents ef98e435 faf44578
Pipeline #3780 passed with stages
in 4 minutes and 2 seconds
......@@ -38,8 +38,11 @@ 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:
application. Each REST API endpoint has to be defined as a QFQ Form.
This describes the server side (=QFQ is server). For client access check :ref:`rest_client`.
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.
......@@ -77,14 +80,14 @@ Endpoint
``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/<level1>/<id1>/<level2>/<id2>/.../?<var1>=<value1>&...``
Append level names and ids after `.../rest.php/`, each separated by '/' .
Append level names and ids after ``.../rest.php/``, each separated by '/' .
E.g.:
1. List of all persons: `<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person`
2. Data of person 123: `<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123`
3. Adresses of person 123: `<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address`
4. Adress details of address 45 from person 123: `<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address/45`
1. List of all persons: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person``
2. Data of person 123: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123``
3. Adresses of person 123: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address``
4. Adress details of address 45 from person 123: ``<domain>/typo3conf/ext/qfq/Classes/Api/rest.php/person/123/address/45``
QFQ 'Forms' are used as a 'container' (to define all details).
......@@ -96,14 +99,14 @@ Only the last <level> of an URI will be processed. The former ones are just to f
.. 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 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`.
(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}}`.
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
......@@ -124,8 +127,8 @@ list
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
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.
......@@ -156,12 +159,12 @@ GET Variables provided via URL are available via STORE_CLIENT as usual.
+-------------------+----------------------------------------------------------------------------------+
| restParam | Optional. CSV list of variable names. E.g.: ``restParam=pId,adrId`` |
+-------------------+----------------------------------------------------------------------------------+
| restToken | Optional. User defined string or dynamic token (see :ref:`restAuthorization`). |
| restToken | Optional. User defined string or dynamic token (see :ref:``restAuthorization``). |
+-------------------+----------------------------------------------------------------------------------+
.. note::
There are no :ref:`special-column-names` available in `restSqlData` or `restSqlList`. Also there are no
There are no :ref:`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).
......@@ -305,7 +308,7 @@ 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}}`.
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::
......
......@@ -716,7 +716,7 @@ Column: _link
+---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
|x | |Page |p:<pageId> |p:impressum |Prepend '?' or '?id=', no hostname qualifier (automatically set by browser) |
+---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
|x | |Download |d:[<exportFilename>] |d:complete.pdf |Link points to `api/download.php`. Additional parameter are encoded into a SIP. 'Download' needs an enabled SIP. See :ref:`download`. |
|x | |Download |d:[<exportFilename>] |d:complete.pdf |Link points to `.../typo3conf/ext/qfq/Api/download.php`. Additional parameter SIP encoded. 'Download' needs SIP. See :ref:`download`. |
+---+---+--------------+-----------------------------------+---------------------------+----------------------------------------------------------------------------------------------------------------------------------------+
|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 :ref:`copyToClipboard`. |
......@@ -816,6 +816,8 @@ render mode might dynamically control the rendered link.
+------------+---------------------+--------------------+------------------+---------------------------------------------------------------------------+
|7 | pure url |pure url | |no link, pure url |
+------------+---------------------+--------------------+------------------+---------------------------------------------------------------------------+
|8 | pure sip |pure sip | |no link, no html, only the 13 digit sip code. |
+------------+---------------------+--------------------+------------------+---------------------------------------------------------------------------+
Example::
......@@ -878,8 +880,8 @@ Link Examples
.. _question:
Question
^^^^^^^^
Alert: Question
^^^^^^^^^^^^^^^
**Syntax**
......@@ -1600,6 +1602,107 @@ Example::
FROM Person AS p  
.. _api_call_qfq_report:
API Call QFQ Report (e.g. AJAX)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. note::
QFQ Report functionality protected by SIP offered to simple API calls: ``typo3conf/ext/qfq/Api/dataReport.php?s=....``
General use API call to fire a specific QFQ tt-content record. Useful for e.g. AJAX calls. No Typo3 is involved.
*No FE-Group access control*.
This describes the client side (=QFQ is client). For server function check :ref:`restApi`.
Example QFQ record JS::
# Register SIP with given arguments.
10.sql = SELECT 'U:uid=12345&arg1=Hello&arg2=World|s|r:8' AS '_link|col1'
# Build JS
10.tail = <script>
function writeYourOwnAjax(){
$.ajax({
url: 'typo3conf/ext/qfq/Api/dataReport.php?s={{col1:RE}}',
data: {arg3:456, arg4:567},
method: 'POST',
dataType: 'JSON',
success: function(response) {ajaxSuccess(response);},
error: function(jqXHR, textStatus, errorThrown) {ajaxError(jqXHR, textStatus, errorThrown);}
});
}
</script>
Example QFQ record called by above AJAX::
# Create a dedicated tt-content record (on any T3 page, might be on the same page as the JS code).
# The example above assumes that this record has the tt_content.uid=12345.
render = api
10.sql = SELECT '{{arg1:S}} {{arg2:S}} {{arg3:C}} {{arg4:C}}', NOW()
.. _rest_client:
REST Client
^^^^^^^^^^^
.. note::
POST and GET data to external REST interfaces or other API services.
Access to external services via HTTP / HTTPS is triggered via special column name *restClient*. The received data might
be processed in subsequent calls.
Example::
# Retrieve information. Received data is delivered in JSON and decoded / copied on the fly to STORE_CLIENT
10.sql = SELECT 'n:https://www.dummy.ord/rest/person/id/123' AS _restClient
20.sql = SELECT 'Status: {{http-status:C}}<br>Name: {{name:C:alnumx}}<br>Surname: {{surname:C:alnumx}}'
# Simple POST request via https. Result is printed on the page.
10.sql = SELECT 'n:https://www.dummy.ord/rest/person/id/123|method:POST|content:{"name":"John";"surname":"Doe"}' AS _restClient
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| Token | Example | Comment |
+===================+================================+============================================================================+
| n | n:https://www.dummy.ord/rest/person | |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| method | method:POST | GET or POST |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| content | content:{"name":"John";"surname":"Doe"} | Depending on the REST server JSON might be expected |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| header | *see below* | |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| timeout | timeout:5 | Default: 5 seconds. |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
| ssl | ssl:{"verify_peer":true,"allow_self_signed":false} | JSON config for SSL settings |
+-------------------+----------------------------------------------------+--------------------------------------------------------+
**Header**
* Each header must be separated by ``\r\n``.
* An explicit given header will overwrite the named default header.
* Default header:
* *content-type: application/json* - if *content* starts with a ``{``.
* *content-type: text/plain* - if *content* does not start with a ``{``.
* *connection: close* - Necessary for HTTP 1.1.
**Result received**
* After a *REST client* call is fired, QFQ will wait up to *timeout* seconds for the answer.
* By default, the whole received answer will be shown. To suppress the output: ``... AS '_restClient|_hide'``
* The variable ``{{http-status:C}}`` shows the `HTTP status code<https://en.wikipedia.org/wiki/List_of_HTTP_status_codes>`_.
A value starting with '2..' shows success.
* In case of an error, ``{{error-message:C:allbut}}`` shows some details.
* In case the returned answer is a valid JSON string, it's automatically copied STORE_CLIENT with corresponding key names.
JSON answer example::
Answer from Server: { 'name' : 'John'; 'street': 'Milky road' }
Retrieve the values via: {{name:C:alnumx}}, {{street:C:alnumx}}
.. _special-sql-functions:
Special SQL Functions (prepared statements)
......
......@@ -255,7 +255,10 @@ Store: *VARS* - V
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| Name | Explanation |
+=========================+============================================================================================================================================+
| random | Random string with length of 32 alphanum chars (lower & upper case). This is variable is always filled. |
| random | Random string with length of 32 alphanum chars (lower & upper case). This variable is always filled. Each access gives a new value. |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| randomUniq | Like {{random:V}} but it's guaranteed that it's uniq. Optional: as *default* define an expiration time. Example: ``{{randomUniq:V}}``, |
| | ``{{randomUniq:V:::2020-12-15}}``, ``{{randomUniq:V:::+ 10 sec}}`` (sec, min, day, ... - MySQL notation DATE_ADD() ). |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| slaveId | see :ref:`slave-id` |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
......
......@@ -86,7 +86,7 @@ Store variables
.. note::
Syntax: {{ variable name : :ref:`store` : :ref:`sanitize-class` : :ref:`variable-escape` : :ref:`variable-default` : :ref:`variable-type-message-violate` }}
{{ *variable name* : :ref:`store` : :ref:`sanitize-class` : :ref:`variable-escape` : :ref:`variable-default` : :ref:`variable-type-message-violate` }}
Example::
......@@ -105,7 +105,7 @@ Example::
* If no store is specified, the default for the 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 and a default value is given, the default is returned.
* If no value is found, nothing is replaced - the string '{{<VarName>}}' remains.
* If no value is found, 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.
.. _`sanitize-class`:
......@@ -117,7 +117,7 @@ Values in STORE_CLIENT *C* (Client=Browser) and STORE_FORM *F* (Form, HTTP 'post
sanitize class. Values from other stores are *not* checked against any sanitize class, even if a sanitize class is specified.
* Variables get by default the sanitize class defined in the corresponding `FormElement`. If not defined,
the default class is 'digit'.
the default class is ``digit``.
* A default sanitize class can be overwritten by individual definition: *{{a:C:alnumx}}*
* If a value violates the specific sanitize class, see :ref:`variable-type-message-violate` for default or customized message.
By default the value becomes `!!<name of sanitize class>!!`. E.g. `!!digit!!`.
......@@ -133,7 +133,7 @@ For QFQ variables and FormElements:
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **numerical** | Form | Query | [0-9.-+] |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **allbut** | Form | Query | All characters allowed, but not [ ] { } % \ #. The used regexp: '^[^\[\]{}%\\#]+$', |
| **allbut** | Form | Query | All characters allowed, but not [ ] { } % \ #. The used regexp: ``^[^\[\]{}%\\#]+$',`` |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
| **all** | Form | Query | no sanitizing |
+------------------+------+-------+-----------------------------------------------------------------------------------------+
......@@ -158,8 +158,8 @@ Rules for CheckType Auto (by priority):
* 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**
......@@ -216,9 +216,9 @@ The following `escape` & `action` types are available:
Escape
^^^^^^
To 'escape' a character typically means: a character, which have a special meaning/function, should not treated as a special
To *escape* a character typically means: a character, which have a special meaning/function, should not treated as a special
character.
E.g. a string is surrounded by single ticks '. If such a string should contain the same single tick inside,
E.g. a string is surrounded by single ticks ``'``. If such a string should contain the same single tick inside,
the inside single tick has to be escaped - if not, the string end's at the second tick, not the third. This is typically
done by a backlash: \\
......@@ -229,21 +229,21 @@ Especially variables used in SQL statements might cause trouble when using: NUL
Action
^^^^^^
* *password* - 'p': transforms the value of the variable into a Typo3 salted password hash. The hash function is the one
* *password* - ``p``: transforms the value of the variable into a Typo3 salted password hash. The hash function is the one
used by Typo3 to encrypt and salt a password. This is useful to manipulate FE user passwords via QFQ. See :ref:`setFeUserPassword`
* *stop replace* - 'S': typically QFQ will replace nested variables as long as there are variables to replace. This options
* *stop replace* - ``S``: typically QFQ will replace nested variables as long as there are variables to replace. This options
stops this
* *exception* - 'X': If a variable is not found in any given store, it's replace by a default value or an error message.
* *exception* - ``X``: If a variable is not found in any given store, it's replace by a default value or an error message.
In special situation it might be useful to do a full stop on all current actions (no further procession). A custom
message can be defined via: :ref:`variable-type-message-violate`.
.. _`variable-escape-wipe-key`:
* *wipe* - 'w': In special cases it might be useful to get a value via SIP only one time and after retrieving the value
it will be deleted in STORE SIP . Further access to the variable will return 'variable undefined'. At time of writing
only the STORE SIP supports the feature 'wipe'. This is useful to suppress any repeating events by using the browser history.
* *wipe* - ``w``: In special cases it might be useful to get a value via SIP only one time and after retrieving the value
it will be deleted in STORE SIP . Further access to the variable will return *variable undefined*. At time of writing
only the STORE SIP supports the feature *wipe*. This is useful to suppress any repeating events by using the browser history.
The following example will send a mail only the first when it is called with a given SIP::
10.sql = SELECT '...' AS _sendmail FROM Person AS p WHERE '{{action:S::w}}'='send' AND p.id={{pId:S}}
......@@ -258,7 +258,8 @@ Default
* Any string can be given to define a default value.
* If a default value is given, it makes no sense to define more than one store: with a default value given, only the
first store is considered.
* If the default value contains a ':', that one needs to be escaped by \\
* If the default value contains a ``:``, that one needs to be escaped by ``\``
* For dedicated variables this value has a special meaning. E.g. ``{{randomUniq:V}}`` uses this as ``expire`` argument.
.. _`variable-type-message-violate`:
......@@ -267,11 +268,11 @@ Type message violate
If a value violates the sanitize class, the following actions are possible:
* '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 :ref:`variable-escape` qualifier 'C' to let QFQ do the colon escaping.
* ``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 :ref:`variable-escape` qualifier ``C`` to let QFQ do the colon escaping.
.. _`sql-variables`:
......@@ -300,7 +301,7 @@ 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 '!': ::
result, specify those with an ``!``::
{{!SELECT ...}}
......@@ -329,7 +330,7 @@ Example
::
{{SELECT id, name FROM Person}}
{{SELECT id, name, IF({{feUser:T0}}=0,'Yes','No') FROM Person WHERE id={{r:S}} }}
{{SELECT id, name, IF({{feUser:T0}}=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:T0}} }} }}
{{!SELECT id, name FROM Person}}
{{[2]SELECT id, name FROM Form}}
......@@ -344,8 +345,8 @@ Syntax: *{{<level>.<column>}}*
Only used in report to access outer columns. See :ref:`access-column-values` and :ref:`syntax-of-report`.
There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for '<level>',
than for 'SQL keywords' and than for 'VarNames' in stores.
There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for *<level>*,
than for *SQL keywords* and than for *VarNames* in stores.
All types might be nested with each other. There is no limit of nesting variables.
......@@ -363,7 +364,7 @@ Link column variables
^^^^^^^^^^^^^^^^^^^^^
These variables return a link, completely rendered in HTML. The syntax and all features of :ref:`column-link` are available.
The following code will render a 'new person' button::
The following code will render a *new person* button::
{{p:form&form=Person|s|N|t:new person AS link}}
......
......@@ -11,7 +11,6 @@ namespace IMATHUZH\Qfq\Api;
require_once(__DIR__ . '/../../vendor/autoload.php');
use IMATHUZH\Qfq\Core\QuickFormQuery;
use IMATHUZH\Qfq\Core\Helper\OnString;
$restId = array();
......
......@@ -724,6 +724,7 @@ const SIP_EXCLUDE_XDEBUG_SESSION_START = 'XDEBUG_SESSION_START';
const ACTION_KEYWORD_SLAVE_ID = 'slaveId';
const VAR_RANDOM = 'random';
const VAR_RANDOM_UNIQ = 'randomUniq';
const VAR_FILE_DESTINATION = 'fileDestination';
const VAR_SLAVE_ID = ACTION_KEYWORD_SLAVE_ID;
const VAR_FILENAME = 'filename'; // Original filename of an uploaded file.
......@@ -1604,6 +1605,7 @@ const COLUMN_MAILTO = "mailto";
const COLUMN_SENDMAIL = "sendmail";
const COLUMN_VERTICAL = "vertical";
const COLUMN_WEBSOCKET = "websocket";
const COLUMN_REST_CLIENT = "restClient";
const COLUMN_NO_WRAP = "noWrap";
const COLUMN_HIDE = "hide";
......@@ -1760,6 +1762,7 @@ const TOKEN_DOWNLOAD = 'd';
const TOKEN_COPY_TO_CLIPBOARD = 'y';
const TOKEN_DROPDOWN = 'z';
const TOKEN_WEBSOCKET = 'w';
const TOKEN_REST_CLIENT = 'n';
const TOKEN_TEXT = 't';
const TOKEN_ALT_TEXT = 'a';
......@@ -1809,6 +1812,12 @@ const TOKEN_L_APPEND = 'append';
const TOKEN_L_INTERVAL = 'interval';
const TOKEN_L_HTML_ID = 'htmlId';
const TOKEN_L_METHOD = 'method';
const TOKEN_L_HEADER = 'header';
const TOKEN_L_CONTENT = 'content';
const TOKEN_L_TIMEOUT = 'timeout';
const TOKEN_L_SSL = 'ssl';
const MONITOR_MODE_APPEND_0 = '0';
const MONITOR_MODE_APPEND_1 = '1';
const MONITOR_SESSION_FILE_SEEK = 'monitor-seek-file';
......@@ -2008,3 +2017,5 @@ const I_ATTRIBUTE = 'attribute';
const I_CHECKED = 'checked';
const I_UNCHECKED = 'unchecked';
const HTTP_STATUS = 'http-status';
const ERROR_MESSAGE = 'error-message';
\ No newline at end of file
......@@ -8,7 +8,6 @@
namespace IMATHUZH\Qfq\Core\Database;
use IMATHUZH\Qfq\Core\Helper\BindParam;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\HelperFormElement;
......
......@@ -194,7 +194,9 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `FormElement` CHANGE `label` `label` VARCHAR(1023) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '';",
],
'20.9.0' => [
"CREATE TABLE `Uniq` (`id` int(11) NOT NULL AUTO_INCREMENT, `random` char(32) NOT NULL, `expire` datetime NOT NULL, `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `random` (`random`) USING BTREE, KEY `expire` (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;);",
],
);
......
......@@ -392,7 +392,8 @@ class Evaluate {
$typeMessageViolate = ($arrToken[VAR_INDEX_MESSAGE] === null || $arrToken[VAR_INDEX_MESSAGE] === '') ? SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS : $arrToken[VAR_INDEX_MESSAGE];
// search for value in stores
$value = $this->store::getVar($arrToken[VAR_INDEX_VALUE], $arrToken[VAR_INDEX_STORE], $arrToken[VAR_INDEX_SANATIZE], $foundInStore, $typeMessageViolate);
$value = $this->store::getVar($arrToken[VAR_INDEX_VALUE], $arrToken[VAR_INDEX_STORE], $arrToken[VAR_INDEX_SANATIZE],
$foundInStore, $typeMessageViolate, $arrToken[VAR_INDEX_DEFAULT]);
// escape ticks
if (is_string($value)) {
......
......@@ -58,7 +58,7 @@ use IMATHUZH\Qfq\Core\Store\Store;
* L:
* m:mailto
* M:Mode
* n:
* n:GET/POST Rest Call
* N:new
* o:ToolTip
* O:Monitor
......@@ -518,6 +518,16 @@ class Link {
return $this->renderLink(KeyValueStringParser::unparse($paramArr, PARAM_TOKEN_DELIMITER, PARAM_DELIMITER));
}
/**
* @param $str
* @return string
* @throws \UserFormException
* @throws \UserReportException
*/
public function processRestClient($str) {
}
/**
* @param $str
* @return string
......@@ -599,6 +609,11 @@ class Link {
case TOKEN_WEBSOCKET:
return $this->processWebSocket($str);
break;
case TOKEN_REST_CLIENT:
$restClient = new RestClient();
return $restClient->process($str);
break;
default:
break;
}
......
......@@ -939,6 +939,7 @@ class Report {
switch ($columnName) {
case COLUMN_LINK:
case COLUMN_WEBSOCKET:
case COLUMN_REST_CLIENT:
$content .= $this->link->renderLink($columnValue);
break;
......
<?php
namespace IMATHUZH\Qfq\Core\Report;
use Exception;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Store\Store;
/**
* Class RestClient
* @package IMATHUZH\Qfq\Core\Report
*/
class RestClient {
/**
* @var Store
*/
private $store = null;
/**
* RestClient constructor.
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function __construct() {
$this->store = Store::getInstance();
}
/**
* @param string $str
* @return string
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function process($str) {
$recvBuffer = '';
$recv = array();
$param = $this->parseArgument($str);
$options = array(
'http' => array(
'header' => $param[TOKEN_L_HEADER],
'method' => strtoupper($param[TOKEN_L_METHOD]),
'timeout' => $param[TOKEN_L_TIMEOUT],
)
);
if (isset($param[TOKEN_L_SSL])) {
$options['ssl'] = json_decode($param[TOKEN_L_SSL]);
}
// Add content only if there is one.
if (!empty($param[TOKEN_L_CONTENT])) {
$options['http']['content'] = $param[TOKEN_L_CONTENT];
}
$context = stream_context_create($options);
try {
if (false === ($recvBuffer = file_get_contents($param[TOKEN_REST_CLIENT], false, $context))) {
$recv[HTTP_STATUS] = 400;
$recv[ERROR_MESSAGE] = implode(", ", $http_response_header);
} else {
// If $recBuffer is no json - don't care, $recv will be null then.
$recv = json_decode($recvBuffer, true);
$recv[HTTP_STATUS] = 200;
}
} catch (Exception $e) {
$recvBuffer = '';
$recv[HTTP_STATUS] = $e->getCode();
$recv[ERROR_MESSAGE] = $e->getMessage();
}
// Copy new values to STORE_CLIENT
$this->store::setStore($recv, STORE_CLIENT, true);
return $recvBuffer;
}
/**
* Parses $str, fill some defaults and returns an array with given arguments.
*
* @param string $str
* @return array
* @throws \UserFormException
* @throws \UserReportException
*/
private function parseArgument($str) {
// "n:http://antmedia-dev.math.uzh.ch/WebRTCAppEE/rest/v2/broadcasts/create|
// content:{'streamId' => "ASDKLJfdlajfhdkhH"}|method:POST|header:Content-type: application/json\r\n"
// Split string
$param = KeyValueStringParser::parse($str, PARAM_TOKEN_DELIMITER, PARAM_DELIMITER);
if (empty($param[TOKEN_REST_CLIENT])) {
throw new \UserReportException("Missing RestClient target", ERROR_MISSING_VALUE);
}
$param[TOKEN_L_CONTENT] = trim($param[TOKEN_L_CONTENT]);
$param[TOKEN_L_HEADER] = trim($param[TOKEN_L_HEADER]);
if (empty($param[TOKEN_L_METHOD])) {
$param[TOKEN_L_METHOD] = 'GET';
}
// Default Timeout
if (empty($param[TOKEN_L_TIMEOUT])) {
$param[TOKEN_L_TIMEOUT] = 5;
}
// If 'Host' is missing in header: define - useful for Firewall/ Proxy
// CR: if a header 'host' is given, REST calls fails always.
// $header = KeyValueStringParser::parse($param[TOKEN_L_HEADER], ':', '\r\n');
// if (empty($header['host'])) {
// $urlParts = parse_url($param[TOKEN_REST_CLIENT]);
// $header['host'] = $urlParts['host'];
// }
// If 'Content-type' is missing in header: define.
if (empty($header['content-type'])) {
// Poor man guess: if no 'content-type' is explicit given and string starts with '{' >> 'application/json'
$mime = (($param[TOKEN_L_CONTENT][0] ?? '') == '{') ? 'application/json' : 'text/plain';
$header['content-type'] = $mime . '; charset: utf-8';
}
// If 'Connection' is missing in Header: define
if (empty($header['connection'])) {
$header['connection'] = 'close';
}
// Join all header arguments to one string
$param[TOKEN_L_HEADER] = KeyValueStringParser::unparse($header, ': ', '\r\n') . '\r\n';
return $param;
}
}
\ No newline at end of file
......@@ -9,12 +9,12 @@
namespace IMATHUZH\Qfq\Core\Store;
use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Logger;