MDL-52136 core: Add support for quoting variables in mustache helpers

This is required for when helpers include json-encoded variables as arguments.
As an example, imagine a template with content:

{{# str }} somekey, someidentifier, { "fullname": "{{ fullname }}" } {{/ str }}

If the fullname variable were to include the double-quote character (e.g.
John "Trevor" Doe) because of the way in which mustache renders content, it
would become:

{{# str }} somekey, someidentifier, { "fullname": "John "Trevor" Doe" } {{/ str }}

This results in an invalid JSON structure.

To work around this issue, the quote characters in the passed variable
must be escaped:

{{# str }} somekey, someidentifier, { "fullname": "John \"Trevor\" Doe" } {{/ str }}

Unfortunately, Mustache provides no way of doing so natively.

With this function, we can quote the text as appropriate:

{{# str }} somekey, someidentifier, { "fullname": {{# quote }}{{ fullname }}{{/ quote }} } {{/ str }}

This also handles the case where the quoted content includes the Mustache
delimeter ({{ or }}).

For example:
fullname = 'John "}}Trevor{{" Doe'

Ordinarily this would be rendered as:
{{# str }} somekey, someidentifier, { "fullname": "John "}}Trevor{{" Doe" } {{/ str }}

This rendering is both a JSON error, and also a mustache syntax error because of the mustache delimeters.

The quote helper also escapes these by wrapping them in change delimeter
tags:

{{# str }} somekey, someidentifier, { "fullname": "John "{{=<% %>=}}}}<%={{ }}=%>Trevor{{=<% %>=}}{{{{=<% %>=}}" Doe" } {{/ str }}
This commit is contained in:
Andrew Nicols 2016-02-12 12:42:41 +08:00
parent 03b8b55f10
commit 0b4bff8ca9
4 changed files with 79 additions and 1 deletions

View File

@ -222,6 +222,27 @@ define([ 'core/mustache',
return '{{_s' + index + '}}';
};
/**
* Quote helper used to wrap content in quotes, and escape all quotes present in the content.
*
* @method quoteHelper
* @private
* @param {string} sectionText The text to parse the arguments from.
* @param {function} helper Used to render subsections of the text.
* @return {string}
*/
var quoteHelper = function(sectionText, helper) {
var content = helper(sectionText.trim(), this);
// Escape the {{ and the ".
// This involves wrapping {{, and }} in change delimeter tags.
content = content
.replace('"', '\\"')
.replace(/([\{\}]{2,3})/g, '{{=<% %>=}}$1<%={{ }}=%>')
;
return '"' + content + '"';
};
/**
* Add some common helper functions to all context objects passed to templates.
* These helpers match exactly the helpers available in php.
@ -239,6 +260,7 @@ define([ 'core/mustache',
context.str = function() { return stringHelper; };
context.pix = function() { return pixHelper; };
context.js = function() { return jsHelper; };
context.quote = function() { return quoteHelper; };
context.globals = { config : config };
context.currentTheme = themeName;
};

View File

@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Wrap content in quotes, and escape all quotes used.
*
* @package core
* @category output
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Wrap content in quotes, and escape all quotes used.
*
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_quote_helper {
/**
* Wrap content in quotes, and escape all quotes used.
*
* Note: This helper is only compatible with the standard {{ }} delimeters.
*
* @param string $text The text to parse for arguments.
* @param Mustache_LambdaHelper $helper Used to render nested mustache variables.
* @return string
*/
public function quote($text, \Mustache_LambdaHelper $helper) {
// Split the text into an array of variables.
$content = trim($text);
$content = $helper->render($content);
// Escape the {{ and the ".
$content = str_replace('"', '\\"', $content);
$content = preg_replace('([{}]{2,3})', '{{=<% %>=}}${0}<%={{ }}=%>', $content);
return '"' . $content . '"';
}
}

View File

@ -76,4 +76,3 @@ class mustache_string_helper {
return get_string($key, $component, $a);
}
}

View File

@ -91,6 +91,7 @@ class renderer_base {
$loader = new \core\output\mustache_filesystem_loader();
$stringhelper = new \core\output\mustache_string_helper();
$quotehelper = new \core\output\mustache_quote_helper();
$jshelper = new \core\output\mustache_javascript_helper($this->page->requires);
$pixhelper = new \core\output\mustache_pix_helper($this);
@ -99,6 +100,7 @@ class renderer_base {
$helpers = array('config' => $safeconfig,
'str' => array($stringhelper, 'str'),
'quote' => array($quotehelper, 'quote'),
'js' => array($jshelper, 'help'),
'pix' => array($pixhelper, 'pix'));