Commit c6bc071a authored by Carsten  Rose's avatar Carsten Rose
Browse files

Feature #5083 / Bodytext / Report: join lines without spaces - implemented

Manual.rst:
parent 255247c8
...@@ -616,7 +616,8 @@ Typo3 QFQ content element ...@@ -616,7 +616,8 @@ Typo3 QFQ content element
Insert one or more QFQ content elements on a Typo3 page. Specify column and language per content record as wished. Insert one or more QFQ content elements on a Typo3 page. Specify column and language per content record as wished.
The title of the QFQ content element will not be rendered. It's only visible in the backend for orientation. The title of the QFQ content element will not be rendered on the frontend. It's only visible to the webmaster in the
backend for orientation.
QFQ Keywords (Bodytext) QFQ Keywords (Bodytext)
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
...@@ -2975,7 +2976,7 @@ will be rendered inside the form as a HTML table. ...@@ -2975,7 +2976,7 @@ will be rendered inside the form as a HTML table.
* Columnname: *[title=]<title>[|[maxLength=]<number>][|width=<number>][|nostrip][|icon][|link][|url][|mailto][|_rowClass][|_rowTooltip]* * Columnname: *[title=]<title>[|[maxLength=]<number>][|width=<number>][|nostrip][|icon][|link][|url][|mailto][|_rowClass][|_rowTooltip]*
* If the keyword is used, all parameter are position independent. * If the keyword is used, all parameter are position independent.
* Parameter are seperated by '|'. * Parameter are separated by '|'.
* *[title=]<text>*: Title of the column. The keyword 'title=' is optional. Columns with a title starting with '_' won't be rendered. * *[title=]<text>*: Title of the column. The keyword 'title=' is optional. Columns with a title starting with '_' won't be rendered.
* *[maxLength=]<number>*: Max. number of characters displayed per cell. The keyword 'maxLength=' is optional. Default * *[maxLength=]<number>*: Max. number of characters displayed per cell. The keyword 'maxLength=' is optional. Default
maxLength '20'. A value of '0' means no limit. This setting also affects the title of the column. maxLength '20'. A value of '0' means no limit. This setting also affects the title of the column.
...@@ -3081,7 +3082,7 @@ and will be processed after saving the primary record and before any action Form ...@@ -3081,7 +3082,7 @@ and will be processed after saving the primary record and before any action Form
* List of mime types (also known as 'media types'): http://www.iana.org/assignments/media-types/media-types.xhtml * List of mime types (also known as 'media types'): http://www.iana.org/assignments/media-types/media-types.xhtml
* If none is specified, 'application/pdf' is set. This forces that always (!) one type is specified. * If none is specified, 'application/pdf' is set. This forces that always (!) one type is specified.
* One or more media types might be specified, seperated by ','. * One or more media types might be specified, separated by ','.
* Different browser respect the given definitions in different ways. Typically the 'file choose' dialog offer: * Different browser respect the given definitions in different ways. Typically the 'file choose' dialog offer:
* the specified mime type (some browers only show 'custom', if more than one mime type is given), * the specified mime type (some browers only show 'custom', if more than one mime type is given),
...@@ -4321,21 +4322,21 @@ Assume that the database has a table person with columns firstName and lastName. ...@@ -4321,21 +4322,21 @@ Assume that the database has a table person with columns firstName and lastName.
10.sql = SELECT id AS pId, CONCAT(firstName, " ", lastName, " ") AS name FROM person 10.sql = SELECT id AS pId, CONCAT(firstName, " ", lastName, " ") AS name FROM person
10 Stands for a *root level* of the report (see section `Structure`_). 10.sql defines a SQL query for this specific level. When the query is executed it will return a result having one single column name containing first and last name The '10' indicates a *root level* of the report (see section `Structure`_). The expresssion '10.sql' defines a SQL query
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. 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 John DoeJane MillerFrank Star
.. ..
I.e., QFQ will simply output the content of the SQL result row after row for each single level. 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 seperate the rows of the result by a HTML-line break. The final result in this case is: 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:
:: ::
...@@ -4442,7 +4443,7 @@ This would result in ...@@ -4442,7 +4443,7 @@ This would result in
Text across several lines Text across several lines
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
To make SQL queries, or QFQ records in general, more readable, it's possible to split a line across several lines. Lines To get better human readable SQL queries, it's possible to split a line across several lines. Lines
with keywords are on their own (`QFQ Keywords (Bodytext)`_ start a new line). If a line is not a 'keyword' line, it will with keywords are on their own (`QFQ Keywords (Bodytext)`_ start a new line). If a line is not a 'keyword' line, it will
be appended to the last keyword line. 'Keyword' lines are detected on: be appended to the last keyword line. 'Keyword' lines are detected on:
...@@ -4464,6 +4465,38 @@ Example:: ...@@ -4464,6 +4465,38 @@ Example::
20.head = <h3> 20.head = <h3>
20.tail = </h3> 20.tail = </h3>
Join mode: SQL
''''''''''''''
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`
Notice the space between "...world'" and "FROM ...".
Join mode: strip whitespace
'''''''''''''''''''''''''''
Ending a line with a '\' forces the removing off all leading and trailing whitespaces in that line and do not insert an
extra 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`
Note: the `\` does not force the joining, it only removes the whitespaces.
To get the same result, the following is also possible: ::
10.sql = SELECT 'hello world', CONCAT('d:final.pdf'
'|p:id=export',
'|t:Download') AS _pdf
Nesting of levels Nesting of levels
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
...@@ -4816,7 +4849,7 @@ Question ...@@ -4816,7 +4849,7 @@ Question
* If a user clicks on a link, an alert is shown. If the user answers the alert by clicking on the 'positive button', the browser opens the specified link. * If a user clicks on a link, an alert is shown. If the user answers the alert by clicking on the 'positive button', the browser opens the specified link.
If the user click on the negative answer (or waits for timout), the alert is closed and the browser does nothing. If the user click on the negative answer (or waits for timout), the alert is closed and the browser does nothing.
* All parameter are optional. * All parameter are optional.
* Parameter are seperated by ':' * Parameter are separated by ':'
* To use ':' inside the text, the colon has to be escaped by '\\'. E.g. 'ok\\: I understand'. * To use ':' inside the text, the colon has to be escaped by '\\'. E.g. 'ok\\: I understand'.
+----------------------+--------------------------------------------------------------------------------------------------------------------------+ +----------------------+--------------------------------------------------------------------------------------------------------------------------+
......
...@@ -128,7 +128,7 @@ class BodytextParser { ...@@ -128,7 +128,7 @@ class BodytextParser {
} }
/** /**
* Join lines. Preservers Nesting. * Join lines. Nesting isn't changed.
* *
* Iterates over all lines. * Iterates over all lines.
* Is a line a 'new line'? * Is a line a 'new line'?
...@@ -150,13 +150,14 @@ class BodytextParser { ...@@ -150,13 +150,14 @@ class BodytextParser {
* c,d,e,f: ^\d+(\.\d+)*(\s*{)?$ * c,d,e,f: ^\d+(\.\d+)*(\s*{)?$
* g,h: ^(\d+\.)*(sql|head)\s*= * g,h: ^(\d+\.)*(sql|head)\s*=
* *
* @param array $bodytextArray * @param $bodyText
* * @param $nestingOpen
* @param $nestingClose
* @return string * @return string
*/ */
private function joinLine($bodytext, $nestingOpen, $nestingClose) { private function joinLine($bodyText, $nestingOpen, $nestingClose) {
$data = array(); $data = array();
$bodytextArray = explode(PHP_EOL, $bodytext); $bodytextArray = explode(PHP_EOL, $bodyText);
$nestingOpenRegexp = $nestingOpen; $nestingOpenRegexp = $nestingOpen;
if ($nestingOpen === '(' || $nestingOpen === '[') { if ($nestingOpen === '(' || $nestingOpen === '[') {
...@@ -164,35 +165,42 @@ class BodytextParser { ...@@ -164,35 +165,42 @@ class BodytextParser {
} }
$full = ''; $full = '';
$joinDelimiter = ' ';
foreach ($bodytextArray as $row) { foreach ($bodytextArray as $row) {
// Line end with '\'?
// if ((1 === preg_match('/^\s*(\d*(\.)?)*\s*(' . TOKEN_VALID_LIST . ') *=/', $row)) if (substr($row, -1) == '\\') {
// || (1 === preg_match('/^\s*(\d*(\.)?)*\s*(' . $nestingOpen . '|' . $nestingClose . ')/', $row)) $row = trim(substr($row, 0, -1)); // remove last char and trim
// || (1 === preg_match('/^\s*(\d+(\.)?)+/', $row)) $joinDelimiterNext = '';
} else {
$joinDelimiterNext = ' ';
}
if (($row == $nestingOpen || $row == $nestingClose) if (($row == $nestingOpen || $row == $nestingClose)
|| (1 === preg_match('/^\d+(\.\d+)*(\s*' . $nestingOpenRegexp . ')?$/', $row)) || (1 === preg_match('/^\d+(\.\d+)*(\s*' . $nestingOpenRegexp . ')?$/', $row))
|| (1 === preg_match('/^(\d+\.)*(' . TOKEN_VALID_LIST . ')\s*=/', $row)) || (1 === preg_match('/^(\d+\.)*(' . TOKEN_VALID_LIST . ')\s*=/', $row))
) { ) {
// if there is already something: save this // if there is already something: save this.
if ($full !== '') if ($full !== '') {
$data[] = $full; $data[] = $full;
}
// start new line // start new line
$full = $row; $full = $row;
} else { } else {
// continue row: concat - the space is necessary to join SQL statements correctly: 'SELECT ... FROM ... WHERE ... AND\np.id=...' - here a 'AND' and 'p.id' need a space. // continue row: concat - the space is necessary to join SQL statements correctly: 'SELECT ... FROM ... WHERE ... AND\np.id=...' - here a 'AND' and 'p.id' need a space.
$full .= ' ' . $row; $full .= $joinDelimiter . $row;
} }
$joinDelimiter = $joinDelimiterNext;
} }
// Save last line // Save last line
if ($full !== '') if ($full !== '') {
$data[] = $full; $data[] = $full;
}
return implode(PHP_EOL, $data); return implode(PHP_EOL, $data);
} }
......
...@@ -344,6 +344,25 @@ EOF; ...@@ -344,6 +344,25 @@ EOF;
} }
public function testProcessPlainEscape() {
$btp = new BodytextParser();
// Simple row, nothing to remove
$given = "10.sql = SELECT 'Hello World', 'p:id=1&\\\ngrId=2";
$expected = "10.sql = SELECT 'Hello World', 'p:id=1&grId=2";
$result = $btp->process($given);
$this->assertEquals($expected, $result);
$given = "10.sql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 ";
$expected = "10.sql = SELECT 'Hello World', 'p:id=1&grId=2";
$result = $btp->process($given);
$this->assertEquals($expected, $result);
$given = "10.sql = SELECT 'Hello World', 'p:id=1& \\\n grId=2 \\\n 20.sql = SELECT ''";
$expected = "10.sql = SELECT 'Hello World', 'p:id=1&grId=2\n20.sql = SELECT ''";
$result = $btp->process($given);
$this->assertEquals($expected, $result);
}
/** /**
* @expectedException \qfq\UserFormException * @expectedException \qfq\UserFormException
...@@ -352,7 +371,7 @@ EOF; ...@@ -352,7 +371,7 @@ EOF;
public function testProcessExceptionClose() { public function testProcessExceptionClose() {
$btp = new BodytextParser(); $btp = new BodytextParser();
// Nested: unclosed close bracket // Nested: unmatched close bracket
$btp->process("10.sql = SELECT 'Hello World'\n } \n30.sql = SELECT 'Hello World'\n"); $btp->process("10.sql = SELECT 'Hello World'\n } \n30.sql = SELECT 'Hello World'\n");
} }
...@@ -364,7 +383,7 @@ EOF; ...@@ -364,7 +383,7 @@ EOF;
public function testProcessExceptionOpen() { public function testProcessExceptionOpen() {
$btp = new BodytextParser(); $btp = new BodytextParser();
// Nested: unclosed open bracket // Nested: unmatched open bracket
$btp->process("10.sql = SELECT 'Hello World'\n20 { \n30.sql = SELECT 'Hello World'\n"); $btp->process("10.sql = SELECT 'Hello World'\n20 { \n30.sql = SELECT 'Hello World'\n");
} }
......
Supports Markdown
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