Commit 89162ba2 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Feature: #4014 / Bodoytext: values after '=' might be surrounded by single/double ticks

parent 3a6ad35e
...@@ -1043,6 +1043,9 @@ Very specific: Also, it's possible that the content of a variable is again (incl ...@@ -1043,6 +1043,9 @@ Very specific: Also, it's possible that the content of a variable is again (incl
is sometimes used in text templates, where the template is retrieved from a record and 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. specific locations in the text will be (automatically by QFQ) replaced by values from other sources.
General note: using this type of variables is only the second choice. First choice is `{{column:R}}` (see
`access-column-values`_) - using the STORE_RECORD is more portable cause no renumbering is needed if the level keys change.
.. _`sanitize-class`: .. _`sanitize-class`:
Sanitize class Sanitize class
...@@ -1345,7 +1348,8 @@ Store: *RECORD* - R ...@@ -1345,7 +1348,8 @@ Store: *RECORD* - R
* Sanitized: *no* * Sanitized: *no*
* Current record loaded in Form. * *Form*: Current record.
* *Report*: See `access-column-values`_
* If r=0, all values are empty. * If r=0, all values are empty.
+------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
...@@ -4664,11 +4668,7 @@ There is a set of **variables** that will get replaced before the SQL-Query gets ...@@ -4664,11 +4668,7 @@ There is a set of **variables** that will get replaced before the SQL-Query gets
``{{<name>[:<store/s>[:...]]}}`` ``{{<name>[:<store/s>[:...]]}}``
Variables from specific stores. Variables from specific stores.
``{{<name>:R}}`` - use case of the above generic definition. ``{{<name>:R}}`` - use case of the above generic definition. See also `access-column-values`_.
STORE_RECORD is automatically merged with a) the existing STORE_RECORD content and b) the *current row*. Use the STORE_RECORD
to access outer and/or previous level values.
In case of previous level values: only the one of the last record is available.
Always access variables without an optional leading '_' - it's removed before the variable is copied to STORE_RECORD.
``{{<level>.<columnname>}}`` ``{{<level>.<columnname>}}``
Similar to ``{{<name>:R}}`` but more specific. There is no sanitize class, escape mode or default value. Similar to ``{{<name>:R}}`` but more specific. There is no sanitize class, escape mode or default value.
...@@ -4697,10 +4697,11 @@ Processing of the resulting rows and columns: ...@@ -4697,10 +4697,11 @@ Processing of the resulting rows and columns:
`SELECT id AS _id`. `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 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. `{{<level>.columnname}}` variable. To suppress printing of a column, use a underscore as column name prefix. E.g.
`SELECT id AS _id` `SELECT id AS _id`
*Reserved column names* have a special meaning and will be processed in a special way. See `Processing of columns in the SQL result`_ for details. *Reserved column names* have a special meaning and will be processed in a special way. See
`Processing of columns in the SQL result`_ for details.
There are extensive ways to wrap columns and rows. See :ref:`wrapping-rows-and-columns` There are extensive ways to wrap columns and rows. See :ref:`wrapping-rows-and-columns`
...@@ -4764,7 +4765,7 @@ Example:: ...@@ -4764,7 +4765,7 @@ Example::
20.sql = SELECT 'a warm welcome' 20.sql = SELECT 'a warm welcome'
'some additional', 'columns' 'some additional', 'columns'
FROM smartTable FROM anotherable
WHERE id>100 WHERE id>100
20.head = <h3> 20.head = <h3>
...@@ -4773,7 +4774,7 @@ Example:: ...@@ -4773,7 +4774,7 @@ Example::
Join mode: SQL 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' 10.sql = SELECT 'hello world'
FROM mastertable FROM mastertable
...@@ -4785,8 +4786,8 @@ Notice the space between "...world'" and "FROM ...". ...@@ -4785,8 +4786,8 @@ Notice the space between "...world'" and "FROM ...".
Join mode: strip whitespace 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 Ending a line with a '\\' strips all leading and trailing whitespaces of that line joins the line directly (no extra
extra space in between. E.g.: :: space in between). E.g.: ::
10.sql = SELECT 'hello world', 'd:final.pdf \ 10.sql = SELECT 'hello world', 'd:final.pdf \
|p:id=export \ |p:id=export \
...@@ -4821,6 +4822,20 @@ This is equal to: :: ...@@ -4821,6 +4822,20 @@ This is equal to: ::
10.5.sql = SELECT ... 10.5.sql = SELECT ...
10.5.head = ... 10.5.head = ...
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: ::
10.sql = SELECT name FROM Person
10.rsep = ' '
10.head = "Names: "
Braces character for nesting
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, curly braces '{}' are used for nesting. Alternatively angle braces '<>', round braces '()' or square 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 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.: ::
...@@ -4893,7 +4908,7 @@ Example STORE_RECORD: :: ...@@ -4893,7 +4908,7 @@ Example STORE_RECORD: ::
The line '10.10' will output 'dummy' in cases where there is at least one corresponding address. The line '10.10' will output 'dummy' in cases where there is at least one corresponding address.
If there are no addresses (all persons) it reports the person id. 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. This behaviour might change in the future. If there is at least one address, it reports 'dummy', cause that's the last stored content.
Example 'Level Key': :: Example 'Level Key': ::
...@@ -4942,7 +4957,7 @@ Wrapping rows and columns: Level keys ...@@ -4942,7 +4957,7 @@ Wrapping rows and columns: Level keys
Order and nesting of queries, will be defined with a typoscript-like syntax: level.sublevel1.subsublevel2. ... Order and nesting of queries, will be defined with a typoscript-like syntax: level.sublevel1.subsublevel2. ...
Each 'level' directive needs a final key, e.g: 20.30.10. **sql**. A key **sql** is necessary in order to process a level. Each 'level' directive needs a final key, e.g: 20.30.10. **sql**. A key **sql** is necessary in order to process a level.
All `QFQ Keywords (Bodytext)`_. See all `QFQ Keywords (Bodytext)`_.
Processing of columns in the SQL result Processing of columns in the SQL result
--------------------------------------- ---------------------------------------
...@@ -5328,8 +5343,8 @@ Example `_pdf`, `_zip`: :: ...@@ -5328,8 +5343,8 @@ Example `_pdf`, `_zip`: ::
Use the `--print-media-type` as wkhtml option to access the page with media type 'printer'. Depending on the website Use the `--print-media-type` as wkhtml option to access the page with media type 'printer'. Depending on the website
configuration this switches off navigation and background images. configuration this switches off navigation and background images.
Rendering 'official' look-alike PDF letters Rendering PDF letters
''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''
`wkhtmltopdf`, with the header and footer options, can be used to render multi page PDF letters (repeating header, `wkhtmltopdf`, with the header and footer options, can be used to render multi page PDF letters (repeating header,
pagination) in combination with dynamic content. Such PDFs might look-alike official letters, together with logo and signature. pagination) in combination with dynamic content. Such PDFs might look-alike official letters, together with logo and signature.
...@@ -6265,7 +6280,7 @@ Two columns ...@@ -6265,7 +6280,7 @@ Two columns
:: ::
# Add the formating information as a coloum # Add the formatting information as a coloum
10.sql = SELECT p.firstName, " " , p.lastName, "<br>" FROM exp_person AS p 10.sql = SELECT p.firstName, " " , p.lastName, "<br>" FROM exp_person AS p
.. ..
...@@ -6291,7 +6306,7 @@ One column 'rend' ...@@ -6291,7 +6306,7 @@ One column 'rend'
:: ::
10.sql = SELECT p.name FROM exp_person AS p 10.sql = SELECT p.firstName, " " , p.lastName FROM exp_person AS p
10.rend = <br> 10.rend = <br>
.. ..
...@@ -6321,10 +6336,7 @@ More HTML ...@@ -6321,10 +6336,7 @@ More HTML
.. ..
Result: Result: ::
::
o Billie Holiday o Billie Holiday
o Elvis Presley o Elvis Presley
...@@ -6394,6 +6406,21 @@ Same as above, but written in the nested notation :: ...@@ -6394,6 +6406,21 @@ Same as above, but written in the nested notation ::
} }
} }
Best practice *recommendation* for using parameter - see `access-column-values`_
10 {
sql = SELECT p.id AS _pId, p.name FROM exp_person AS p
rend = <br>
10 {
# inner query
sql = SELECT a.street FROM exp_address AS a WHERE a.pId='{{pId:R}}'
rend = <br>
}
}
* Columns starting with a '_' won't be printed but can be accessed as regular columns. * Columns starting with a '_' won't be printed but can be accessed as regular columns.
Recent List Recent List
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
namespace qfq; namespace qfq;
require_once(__DIR__ . '/../Constants.php');
class OnString { class OnString {
/** /**
...@@ -26,4 +28,56 @@ class OnString { ...@@ -26,4 +28,56 @@ class OnString {
return substr($haystack, strrpos($haystack, $needle) + 1); return substr($haystack, strrpos($haystack, $needle) + 1);
} }
/**
* Strips the first char $c from $data if the first char is equal to $c.
* Example: with $c='_' the $data='_pId' becomes 'pId'
*
* @param $c
* @param $data
* @return string
*/
public static function stripFirstCharIf($c, $data) {
if (empty($data)) {
return $data;
}
if ($data[0] == $c) {
$data = substr($data, 1);
}
return $data;
}
/**
* If the given $str is enclosed in SINGLE_TICK or DOUBLE_TICK, remove it.
*
* @param string $str
* @return string remove unquoted string
*/
public static function trimQuote($str) {
$len = strlen($str);
if ($len < 2) {
return $str;
}
switch ($str[0]) {
case SINGLE_TICK:
if ($str[$len - 1] == SINGLE_TICK) {
return substr($str, 1, $len - 2);
}
break;
case DOUBLE_TICK:
if ($str[$len - 1] == DOUBLE_TICK) {
return substr($str, 1, $len - 2);
}
break;
default:
break;
}
return $str;
}
} }
...@@ -1338,26 +1338,6 @@ class Support { ...@@ -1338,26 +1338,6 @@ class Support {
} }
} }
/**
* Strips the first char $c from $data if the first char is equal to $c.
* Example: with $c='_' the $data='_pId' becomes 'pId'
*
* @param $c
* @param $data
* @return string
*/
public static function stripFirstCharIf($c, $data) {
if (empty($data)) {
return $data;
}
if ($data[0] == $c) {
$data = substr($data, 1);
}
return $data;
}
/** /**
* @param $mode * @param $mode
......
...@@ -210,7 +210,7 @@ class Report { ...@@ -210,7 +210,7 @@ class Report {
foreach ($ttLineArray as $index => $line) { foreach ($ttLineArray as $index => $line) {
// Fill $frArray, $indexArray, $resultArray // Fill $frArray, $indexArray, $resultArray
$this->parseFRLine($line); $this->parseLine($line);
} }
// Sort array // Sort array
...@@ -230,7 +230,7 @@ class Report { ...@@ -230,7 +230,7 @@ class Report {
* *
* @throws UserReportException * @throws UserReportException
*/ */
private function parseFRLine($ttLine) { private function parseLine($ttLine) {
// 10.50.5.sql = select ... // 10.50.5.sql = select ...
$arr = explode("=", trim($ttLine), 2); $arr = explode("=", trim($ttLine), 2);
...@@ -248,8 +248,9 @@ class Report { ...@@ -248,8 +248,9 @@ class Report {
return; return;
} }
// select ... // select ... - if needed, trim surrounding single ticks
$value = trim($arr[1]); $value = trim($arr[1]);
$value = OnString::trimQuote($value);
// 10.50.5.sql // 10.50.5.sql
$arrKey = explode('.', $key); $arrKey = explode('.', $key);
...@@ -613,7 +614,7 @@ class Report { ...@@ -613,7 +614,7 @@ class Report {
$flagOutput = false; $flagOutput = false;
$renderedColumn = $this->renderColumn($ii, $keys[$ii], $row[$ii], $full_level, $rowIndex, $flagOutput); $renderedColumn = $this->renderColumn($ii, $keys[$ii], $row[$ii], $full_level, $rowIndex, $flagOutput);
$keyAssoc = Support::stripFirstCharIf(TOKEN_COLUMN_CTRL, $keys[$ii]); $keyAssoc = OnString::stripFirstCharIf(TOKEN_COLUMN_CTRL, $keys[$ii]);
if ($keyAssoc != '') { if ($keyAssoc != '') {
$assoc[$keyAssoc] = $row[$ii]; $assoc[$keyAssoc] = $row[$ii];
} }
......
...@@ -21,6 +21,9 @@ use qfq; ...@@ -21,6 +21,9 @@ use qfq;
class OnStringTest extends \PHPUnit_Framework_TestCase { class OnStringTest extends \PHPUnit_Framework_TestCase {
/**
*
*/
public function testStrrstr() { public function testStrrstr() {
$this->assertEquals('', OnString::strrstr('', '')); $this->assertEquals('', OnString::strrstr('', ''));
...@@ -28,4 +31,34 @@ class OnStringTest extends \PHPUnit_Framework_TestCase { ...@@ -28,4 +31,34 @@ class OnStringTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals('limit', OnString::strrstr('hello world to the limit', ' ')); $this->assertEquals('limit', OnString::strrstr('hello world to the limit', ' '));
$this->assertEquals('', OnString::strrstr('', ' ')); $this->assertEquals('', OnString::strrstr('', ' '));
} }
/**
*
*/
public function testStripFirstCharIf() {
$this->assertEquals('', OnString::stripFirstCharIf('', ''));
$this->assertEquals('', OnString::stripFirstCharIf('c', ''));
$this->assertEquals('', OnString::stripFirstCharIf('c', 'c'));
$this->assertEquals('def', OnString::stripFirstCharIf('c', 'cdef'));
$this->assertEquals('def', OnString::stripFirstCharIf('c', 'def'));
$this->assertEquals('def', OnString::stripFirstCharIf('cd', 'def'));
}
/**
*
*/
public function testTrimQuote() {
$this->assertEquals('', OnString::trimQuote(''));
$this->assertEquals('test', OnString::trimQuote('test'));
$this->assertEquals(' test ', OnString::trimQuote(' test '));
$this->assertEquals('" test ', OnString::trimQuote('" test '));
$this->assertEquals('" test \'', OnString::trimQuote('" test \''));
$this->assertEquals('\' test "', OnString::trimQuote('\' test "'));
$this->assertEquals(' test ', OnString::trimQuote('" test "'));
$this->assertEquals(' test ', OnString::trimQuote("' test '"));
$this->assertEquals(' te"st ', OnString::trimQuote("' te\"st '"));
$this->assertEquals(' te\'st ', OnString::trimQuote("' te'st '"));
}
} }
\ No newline at end of file
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