Commit d3a79910 authored by Elias Villiger's avatar Elias Villiger
Browse files

Merge branch '6465-newline-in-action-query' into 'master'

Bug #6465 - Allow newlines in form action queries (e.g. sqlInsert)

See merge request !63
parents 7d13df4c ccee1f41
Pipeline #839 passed with stage
in 1 minute and 39 seconds
......@@ -14,6 +14,7 @@ use qfq;
require_once(__DIR__ . '/../exceptions/UserFormException.php');
require_once(__DIR__ . '/../../qfq/Constants.php');
require_once(__DIR__ . '/../../qfq/helper/OnString.php');
/**
* Class KeyValueStringParser
......@@ -102,6 +103,9 @@ class KeyValueStringParser {
// Clean any "\r\n" to "\n"
$keyValueString = str_replace("\r\n", "\n", $keyValueString);
// Allow {{ }} expressions to span several lines (replace \n inside these expressions)
$keyValueString = OnString::removeNewlinesInNestedExpression($keyValueString);
// Check if there are 'escaped delimiter', If yes, use the more expensive explodeEscape().
if (strpos($keyValueString, '\\' . $listDelimiter) !== false) {
$keyValuePairs = self::explodeEscape($listDelimiter, $keyValueString, 2);
......
......@@ -30,7 +30,7 @@ class OnString {
return '';
}
return substr($haystack, strrpos($haystack, $needle) + 1);
return substr($haystack, strrpos($haystack, $needle) + strlen($needle));
}
/**
......@@ -145,6 +145,63 @@ class OnString {
return $urlParamNew;
}
/**
* Removes new lines (\n) from expressions like {{var:SC0:alnumx}} and replaces them with spaces.
* Handles nested expressions like {{SELECT {{var::alnumx}}}}
* This function is useful to allow multiline expressions in form definitions such as:
* sqlInsert = {{INSERT INTO test (grId)
* VALUES(123) }}
*
* @param $str - the string to be parsed
* @return string - the resulting string with new lines in expressions replaced
* @throws UserFormException - alerts the user if the delimiters are not balanced
*/
public static function removeNewlinesInNestedExpression($str) {
$delimStart = '{{';
$delimEnd = '}}';
$lastDelimPos = -strlen($delimStart);
$exprDepth = 0; // keeps count of the level of expression depth
$nestingStart = 0;
// Process the string one start/end delimiter at a time
while(true) {
// find the next start/end delimiter
$nextDelimStartPos = strpos($str, $delimStart, $lastDelimPos + strlen($delimStart));
$nextDelimEndPos = strpos($str, $delimEnd, $lastDelimPos + strlen($delimEnd));
if ($nextDelimStartPos === false && $nextDelimEndPos === false) break;
$nextDelimPos = min($nextDelimStartPos, $nextDelimEndPos);
if ($nextDelimStartPos === false) $nextDelimPos = $nextDelimEndPos;
if ($nextDelimEndPos === false) $nextDelimPos = $nextDelimStartPos;
if ($nextDelimPos == $nextDelimStartPos) { // opening delimiter
if ($exprDepth == 0) $nestingStart = $nextDelimPos;
$exprDepth++;
} else { // closing delimiter
$exprDepth--;
if ($exprDepth < 0) {
throw new UserFormException("Too many closing delimiters '$delimEnd' in '" . $str . "'", ERROR_MISSING_OPEN_DELIMITER);
break;
} elseif ($exprDepth == 0) {
// end of nesting -> replace \n inside nested expression with space
$pre = substr($str, 0, $nestingStart);
$nest = substr($str, $nestingStart, $nextDelimPos - $nestingStart);
$nestNew = str_replace('\n', ' ', $nest);
$post = substr($str, $nextDelimPos);
$str = substr($str, 0, $nestingStart) .
str_replace("\n", " ", substr($str, $nestingStart, $nextDelimPos - $nestingStart)) .
substr($str, $nextDelimPos);
}
}
$lastDelimPos = $nextDelimPos;
}
if ($exprDepth > 0 ) {
throw new UserFormException("Missing close delimiter '$delimEnd' in '" . $str . "'", ERROR_MISSING_CLOSE_DELIMITER);
}
return $str;
}
/**
* Replace character ' and " by the unicode representation. This is useful for JS code, embedded in HTML code.
* <button onclick="new QfqNS.Clipboard({text: 'single tick -\u0027-' });">
......@@ -159,5 +216,4 @@ class OnString {
return ($str);
}
}
......@@ -9,6 +9,7 @@
namespace qfq;
require_once(__DIR__ . '/../../qfq/helper/OnString.php');
require_once(__DIR__ . '/../../qfq/exceptions/UserFormException.php');
use qfq;
......@@ -69,4 +70,69 @@ class OnStringTest extends TestCase {
$this->assertEquals(' te\'st ', OnString::trimQuote("' te'st '"));
}
/**
*
*/
public function testRemoveNewlinesInNestedExpression() {
$this->assertEquals("", OnString::removeNewlinesInNestedExpression(""));
$this->assertEquals("test", OnString::removeNewlinesInNestedExpression("test"));
$this->assertEquals("{{test}}", OnString::removeNewlinesInNestedExpression("{{test}}"));
$this->assertEquals("{{test }}", OnString::removeNewlinesInNestedExpression("{{test\n}}"));
$this->assertEquals("{{test two}}", OnString::removeNewlinesInNestedExpression("{{test\ntwo}}"));
$this->assertEquals("Pre{{test two}}Post", OnString::removeNewlinesInNestedExpression("Pre{{test\ntwo}}Post"));
$this->assertEquals("{{test{{two}}}}", OnString::removeNewlinesInNestedExpression("{{test{{two}}}}"));
$this->assertEquals("{{a {{b }}}}",
OnString::removeNewlinesInNestedExpression("{{a\n{{b\n}}}}"));
$this->assertEquals("{{a b{{c d}}{{e f}} h}}",
OnString::removeNewlinesInNestedExpression("{{a\nb{{c\nd}}{{e\nf}}\nh}}"));
$this->assertEquals("param1=abc\nsqlInsert = {{SELECT * FROM test WHERE '{{var}}'='true'}}",
OnString::removeNewlinesInNestedExpression("param1=abc\nsqlInsert = {{SELECT *\nFROM test\nWHERE '{{var}}'='true'}}"));
$this->assertEquals("sqlInsert = {{SELECT * FROM test WHERE '{{var}}'='true'}}\nparam1=abc",
OnString::removeNewlinesInNestedExpression("sqlInsert = {{SELECT *\nFROM test\nWHERE '{{var}}'='true'}}\nparam1=abc"));
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingClosing_1() {
$str = OnString::removeNewlinesInNestedExpression("Hi! {{Test ");
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingClosing_2() {
$str = OnString::removeNewlinesInNestedExpression("Hi! {{Test}");
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingClosing_3() {
$str = OnString::removeNewlinesInNestedExpression("Hi! {{Test {{var }}");
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingOpening_1() {
$str = OnString::removeNewlinesInNestedExpression("}}");
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingOpening_2() {
$str = OnString::removeNewlinesInNestedExpression("{}}");
}
/**
* @expectedException \qfq\UserFormException
*
*/
public function testRemoveNewlinesInNestedExpression_missingOpening_3() {
$str = OnString::removeNewlinesInNestedExpression("{{Test}}}}");
}
}
\ 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