Commit 0d386f88 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Refs #12636. Add stored procedure. Add OnString::escapeSingleTickInHtml()...

Refs #12636. Add stored procedure. Add OnString::escapeSingleTickInHtml() incl. unit test. Next step: use  escapeSingleTickInHtml().
parent 0d1069f6
Pipeline #5276 passed with stages
in 3 minutes and 33 seconds
......@@ -1899,7 +1899,7 @@ Output::
31.12.2019 23:55 / - / -
.. _qlugify:
.. _qslugify:
QSLUGIFY: clean a string
^^^^^^^^^^^^^^^^^^^^^^^^
......@@ -1915,6 +1915,71 @@ Output::
abcd-abcd-ae-a-oe-o-ue-u-z-z
.. _qent_squote:
QENT_SQUOTE: convert single tick to HTML entity '
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Convert all single ticks in a string to the HTML entity "'"
Example::
10.sql = SELECT QENT_SQUOTE("John's car")
Output::
John's car
.. _qent_dquote:
QENT_DQUOTE: convert double tick to HTML entity "
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Convert all double ticks in a string to the HTML entity """
Example::
10.sql = SELECT QENT_SQUOTE('A "nice" event')
Output::
A "nice" event
.. _qesc_squote:
QESC_SQUOTE: escape single tick
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Escape all single ticks with a backslash. Double escaped single ticks (two backslashes) will be replaced by a single
escaped single tick.
Example::
Be Music.style = "Rock'n' Roll"
10.sql = SELECT QESC_SQUOTE(style) FROM Music
Output::
Rock\'n\'n Roll
.. _qesc_dquote:
QESC_SQUOTE: escape double tick
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Escape all double ticks with a backslash. Double escaped double ticks (two backslashes) will replaced by a single
escaped double tick.
Example::
Set Comment.note = 'A "nice" event'
10.sql = SELECT QESC_DQUOTE(style) FROM Music
Output::
Rock\'n\'n Roll
.. _strip_tags:
strip_tags: strip html tags
......@@ -1962,11 +2027,12 @@ Example tt-content record for the function::
render = api
100 {
  sql = SELECT p.firstName AS _firstName
               , NOW() AS now, CONCAT('p:{{pageAlias:T}}&form=person&r=', p.id ) AS '_pagee|_hide|myLink'            
          FROM Person AS p
          WHERE p.id={{pId:R}}
}                       
sql = SELECT p.firstName AS _firstName
, NOW() AS now
, CONCAT('p:{{pageAlias:T}}&form=person&r=', p.id ) AS '_pagee|_hide|myLink'
FROM Person AS p
WHERE p.id={{pId:R}}
}
Example tt-content record for the calling report::
......@@ -1976,24 +2042,24 @@ Example tt-content record for the calling report::
#
10 {
  sql = SELECT p.id AS _pId, p.name FROM Person AS p ORDER BY p.name
  head = <table class="table"><tr><th>Name</th><th>Firstname</th><th>Link (final)</th><th>Link (source)</th><th>NOW() (via Output)</th></tr>
  tail = </table>
  rbeg = <tr>
  renr = </tr>
  fbeg = <td>
  fend = </td>
                                                                    
  20 {
    function = getFirstName(pId) => firstName, myLink    
  }
                                                                    
  30 {
    sql = SELECT '{{firstName:R}}', "{{myLink:R}}", "{{&myLink:R}}", '{{_output:R}}'
    fbeg = <td>
    fend = </td>
  }
}                                                                    
sql = SELECT p.id AS _pId, p.name FROM Person AS p ORDER BY p.name
head = <table class="table"><tr><th>Name</th><th>Firstname</th><th>Link (final)</th><th>Link (source)</th><th>NOW() (via Output)</th></tr>
tail = </table>
rbeg = <tr>
renr = </tr>
fbeg = <td>
fend = </td>
20 {
function = getFirstName(pId) => firstName, myLink
}
30 {
sql = SELECT '{{firstName:R}}', "{{myLink:R}}", "{{&myLink:R}}", '{{_output:R}}'
fbeg = <td>
fend = </td>
}
}
Explanation:
......
......@@ -405,10 +405,12 @@ class OnString {
if (is_string($value)) {
// Process all escape requests in the given order.
for ($ii = 0; $ii < strlen($escapeTypes); $ii++) {
$escape = $escapeTypes[$ii];
if ($escape == TOKEN_ESCAPE_CONFIG) {
$escape = self::getEscapeTypeDefault();
}
switch ($escape) {
case TOKEN_ESCAPE_SINGLE_TICK:
$value = str_replace("'", "\\'", $value);
......@@ -507,5 +509,113 @@ class OnString {
return $tz;
}
/**
* @param $line
* @return string
*/
public static function escapeSingleTickInHtml($line) {
$flagTag = false;
$flagAttribute = false;
$flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false;
$flagDoubleTickInAttribute = false;
$flagAttibuteNoQuote = false;
$posStartSingleTick = null;
$new = '';
$length = strlen($line);
for ($ii = 0; $ii < $length; $ii++) {
$c = $line[$ii];
switch ($c) {
case '>':
// HTML tag ends here: close all open flags
$flagTag = false;
$flagAttribute = false;
$flagDoubleTickInAttribute = false;
$flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false;
$flagAttibuteNoQuote = false;
$posStartSingleTick = null;
break;
case '<':
// HTML tag starts here
$flagTag = true;
break;
case '=':
if ($flagTag && !$flagAttribute) {
// Inside a tag and outside a running attribute: here starts a new attribute
$flagAttribute = true;
}
break;
case ' ':
if ($flagTag && ($flagAttributeStartSingleTick || $flagAttributeInsideDoubleTick)) {
$flagAttribute = false;
$flagDoubleTickInAttribute = false;
$flagAttributeStartSingleTick = false;
$flagAttributeStartDoubleTick = false;
$flagAttributeInsideDoubleTick = false;
$flagAttibuteNoQuote = false;
$posStartSingleTick = null;
}
break;
case "'":
if ($flagTag) {
if ($flagAttribute && !$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) {
$flagAttributeStartSingleTick = true;
$posStartSingleTick = strlen($new);
break;
}
if ($flagAttribute && $flagAttributeStartSingleTick && !$flagDoubleTickInAttribute) {
$new[$posStartSingleTick] = '"';
$new .= '"';
$flagAttributeStartSingleTick = false;
continue 2;
}
if ($flagAttribute && !$flagAttributeStartSingleTick) {
$new .= '&apos;';
continue 2;
}
} else {
$new .= '&apos;';
continue 2;
}
break;
case '"':
if ($flagAttribute) {
if (!$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) {
$flagAttributeStartDoubleTick = true;
break;
}
if ($flagAttributeStartSingleTick) {
$flagDoubleTickInAttribute = true;
break;
}
}
break;
default:
if ($flagAttribute) {
if (!$flagAttributeStartSingleTick && !$flagAttributeInsideDoubleTick) {
$flagAttibuteNoQuote = true;
break;
}
}
}
$new .= $c;
}
return $new;
}
}
......@@ -77,7 +77,8 @@ class Variables {
// $str = preg_replace_callback("/{{(([a-zA-Z0-9.:_])*)}}/", array($this, 'replaceVariables'), $text);
// Process all {{x[.x].name}}
// $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)\s*}}/', 'self::replaceVariables', $text);
$str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:.*)*\s*}}/', 'self::replaceVariables', $text);
// $str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:.*)*\s*}}/', 'self::replaceVariables', $text);
$str = preg_replace_callback('/{{\s*(([0-9]+.)+[a-zA-Z0-9_.]+)(:[a-zA-Z-]*)*\s*}}/', 'self::replaceVariables', $text);
// Try the Stores
return $this->eval->parse($str);
......@@ -112,7 +113,8 @@ class Variables {
//
$data = $this->resultArray[$fullLevel][$varName];
if (isset($matches[3])) {
$data = OnString::escape($matches[3], $this->resultArray[$fullLevel][$varName], $rcFlagWipe);
$arr = explode(':', $matches[3]);
$data = OnString::escape($arr[1] ?? '', $this->resultArray[$fullLevel][$varName], $rcFlagWipe);
}
}
}
......
......@@ -248,3 +248,61 @@ BEGIN
Return temp_string;
END;
###
# QENT_SQUOTE(text)
# Replaces single tick by html entity &apos;
#
DROP FUNCTION IF EXISTS `QENT_SQUOTE`;
CREATE FUNCTION `QENT_SQUOTE`(`input` TEXT) RETURNS text CHARSET utf8
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE output TEXT;
SET output = REPLACE(input, "'", '&apos;');
RETURN output;
End;
###
# QENT_DQUOTE(text)
# Replaces double tick by html entity &quot;
#
DROP FUNCTION IF EXISTS `QENT_DQUOTE`;
CREATE FUNCTION `QENT_DQUOTE`(`input` TEXT) RETURNS text CHARSET utf8
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE output TEXT;
SET output = REPLACE(input, '"', '&quot;');
RETURN output;
END;
###
# QENT_DQUOTE(text)
# Replaces double tick by html entity &quot;
#
DROP FUNCTION IF EXISTS `QESC_SQUOTE`;
CREATE FUNCTION `QESC_SQUOTE`(`input` TEXT) RETURNS text CHARSET utf8
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE output TEXT;
SET output = REPLACE(REPLACE(input, "'", "\\'"), "\\\\'", "\\'");
RETURN output;
END;
###
# QENT_DQUOTE(text)
# Replaces double tick by html entity &quot;
#
DROP FUNCTION IF EXISTS `QESC_DQUOTE`;
CREATE FUNCTION `QESC_DQUOTE`(`input` TEXT) RETURNS text CHARSET utf8
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE output TEXT;
SET output = REPLACE(REPLACE(input, '"', '\\"'), '\\\\"', '\\"');
RETURN output;
END;
......@@ -234,4 +234,44 @@ class OnStringTest extends TestCase {
// $this->assertEquals('CEST', $eval->substitute('start:F:all:t', $foundInStore));
}
/**
*
*/
public function testEscapeSingleTickInHtml() {
$this->assertEquals('<b>hello</b>', OnString::escapeSingleTickInHtml('<b>hello</b>'));
$this->assertEquals("<b>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b>hel'lo</b>"));
$this->assertEquals('<b>hel"lo</b>', OnString::escapeSingleTickInHtml('<b>hel"lo</b>'));
$this->assertEquals('<b>hel&apos;l"o</b>', OnString::escapeSingleTickInHtml('<b>hel\'l"o</b>'));
$this->assertEquals("<b title=test>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=test>hel'lo</b>"));
$this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='test'>hel'lo</b>"));
$this->assertEquals("<b title=\"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\">hel'lo</b>"));
$this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'test'>hel'lo</b>"));
$this->assertEquals("<b title= \"test\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\">hel'lo</b>"));
$this->assertEquals("<b title=\"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"test\" >hel'lo</b>"));
$this->assertEquals("<b title= \"test\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"test\" >hel'lo</b>"));
$this->assertEquals("<b title=\"te'st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title=\"te'st\">hel'lo</b>"));
$this->assertEquals("<b title= \"te'st\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\">hel'lo</b>"));
$this->assertEquals("<b title= \"te'st\" >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= \"te'st\" >hel'lo</b>"));
$this->assertEquals("<b title='te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title='te\"st'>hel'lo</b>"));
$this->assertEquals("<b title= 'te\"st'>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'te\"st'>hel'lo</b>"));
$this->assertEquals("<b title= 'te\"st' >hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b title= 'te\"st' >hel'lo</b>"));
$this->assertEquals("<b src=gif title=\"test\" alt=jpg>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=gif title='test' alt=jpg>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"test\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title='test' alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"test\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title='test' alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"test\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title='test' alt=\"jpg\">hel'lo</b>"));
$this->assertEquals("<b src=gif title='te\"st' alt=jpg>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=gif title='te\"st' alt=jpg>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title='te\"st' alt=\"jpg\">hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title='te\"st' alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title='te\"st' alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=gif title=\"te'st\" alt=jpg>hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=gif title=\"te'st\" alt=jpg>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src=\"gif\" title=\"te'st\" alt='jpg'>hel'lo</b>"));
$this->assertEquals("<b src=\"gif\" title=\"te'st\" alt=\"jpg\">hel&apos;lo</b>", OnString::escapeSingleTickInHtml("<b src='gif' title=\"te'st\" alt=\"jpg\">hel'lo</b>"));
}
}
\ No newline at end of file
......@@ -1435,7 +1435,6 @@ EOF;
10.10.tail = Dynamic tail
10.10.althead = No record found
10.10.altsql = SELECT 'alt sql fired'
EOF;
$result = $this->report->process($line);
......@@ -1443,6 +1442,76 @@ EOF;
$this->assertEquals($expect, $result);
}
/**
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
* @throws \UserReportException
*/
public function testReportContent() {
$line = <<<EOF
10.sql = SELECT 'Hello'
20.sql = SELECT 'World'
20.tail = {{10.line.content}}
EOF;
$result = $this->report->process($line);
$expect = "HelloWorld{{10.line.content}}";
$this->assertEquals($expect, $result);
$line = <<<EOF
10.sql = SELECT 'Hello'
10.content = hide
20.sql = SELECT 'World'
20.tail = {{10.line.content}}
EOF;
$result = $this->report->process($line);
$expect = "WorldHello";
$this->assertEquals($expect, $result);
$line = <<<EOF
10.sql = SELECT 'Hello'
10.content = show
20.sql = SELECT 'World'
20.tail = {{10.line.content}}
EOF;
$result = $this->report->process($line);
$expect = "HelloWorldHello";
$this->assertEquals($expect, $result);
// Check that current row can be reused in head, tail, rbeg, rend, renr
$line = <<<EOF
10.sql = SELECT 'Hello'
10.content = show
10.head = {{10.line.content}}
10.tail = {{10.line.content}}
10.rbeg = {{10.line.content}}
10.rend = {{10.line.content}}
10.renr = {{10.line.content}}
EOF;
$result = $this->report->process($line);
$expect = "HelloHelloHelloHelloHelloHello";
$this->assertEquals($expect, $result);
// Check single tick escape
$line = <<<EOF
10.sql = SELECT "Hel'lo"
10.content = hide
20.sql = SELECT '--{{10.line.content::s}}--', "--{{10.line.content}}--", "--{{10.line.content::s}}--"
EOF;
$result = $this->report->process($line);
$expect = "--Hel'lo----Hel'lo----Hel'lo--";
$this->assertEquals($expect, $result);
}
/**
* @throws \CodeException
* @throws \DbException
......@@ -1470,7 +1539,6 @@ EOF;
10.fend = </td>
10.fsep = ++
10.fskipwrap = 1,3
EOF;
$result = $this->report->process($line);
......@@ -1505,7 +1573,6 @@ EOF;
10.fend = </td>
10.fsep = ++
10.fskipwrap = 3
EOF;
$result = $this->report->process($line);
......
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