diff --git a/secretary.py b/secretary.py
index 760be55..1d732d9 100644
--- a/secretary.py
+++ b/secretary.py
@@ -29,12 +29,13 @@ import re
import sys
import logging
import zipfile
+import jinja2
from os import path
from mimetypes import guess_type, guess_extension
from uuid import uuid4
from xml.dom.minidom import parseString
from xml.parsers.expat import ExpatError, ErrorString
-from jinja2 import Environment, Undefined
+from jinja2 import Environment, Undefined, Markup
try:
if sys.version_info.major == 3:
@@ -135,7 +136,8 @@ class Renderer(object):
self.environment = environment
else:
self.environment = Environment(undefined=UndefinedSilently,
- autoescape=True)
+ autoescape=True,
+ finalize=self.finalize_value)
# Register filters
self.environment.filters['pad'] = pad_string
self.environment.filters['markdown'] = self.markdown_filter
@@ -146,6 +148,13 @@ class Renderer(object):
self._compile_tags_expressions()
+ @jinja2.evalcontextfilter
+ def finalize_value(self, value, *args):
+ """Escapes variables values."""
+ if isinstance(value, Markup):
+ return value
+
+ return Markup(self.get_escaped_var_value(value))
def media_loader(self, callback):
"""This sets the the media loader. A user defined function which
@@ -401,7 +410,6 @@ class Renderer(object):
# Finally, remove the placeholder
placeholder_parent.removeChild(placeholder)
-
def _unescape_entities(self, xml_text):
"""
Unescape links and '&', '<', '"' and '>' within jinja
@@ -434,17 +442,16 @@ class Renderer(object):
return xml_text
@staticmethod
- def _encode_escape_chars(xml_text):
+ def get_escaped_var_value(value):
"""
- Replace line feed and/or tabs within text:span entities.
+ Encodes XML reserved chars in value (eg. &, <, >) and also replaces
+ the control chars \n and \t control chars to their ODF counterparts.
"""
- find_pattern = r'(?is)([^>]*?([\n\t])[^<]*?)'
- for m in re.findall(find_pattern, xml_text):
- replacement = m[1].replace('\n', '')
- replacement = replacement.replace('\t', '')
- xml_text = xml_text.replace(m[1], replacement)
-
- return xml_text
+ value = Markup.escape(value)
+ return (
+ value.replace('\n', Markup(''))
+ .replace('\t', Markup(''))
+ )
def add_media_to_archive(self, media, mime, name=''):
@@ -568,7 +575,6 @@ class Renderer(object):
)
result = jinja_template.render(**kwargs)
- result = self._encode_escape_chars(result)
final_xml = parseString(result.encode('ascii', 'xmlcharrefreplace'))
if self.template_images:
diff --git a/test_secretary.py b/test_secretary.py
index 9000a0b..d3883e9 100644
--- a/test_secretary.py
+++ b/test_secretary.py
@@ -49,21 +49,6 @@ class RenderTestCase(TestCase):
for test, expect in test_samples.items():
assert self.engine._unescape_entities(test) == expect
- def test__encode_escape_chars(self):
- test_samples = {
- '\n': '',
- '\n': '',
- '\n': '',
- 'Hello\n': 'Hello',
- 'Hello\nWorld\n!': 'HelloWorld!',
- '\n': '',
- '\u0009': '',
- '\n': '',
- }
-
- for test, expect in test_samples.items():
- assert self.engine._encode_escape_chars(test) == expect
-
def _test_is_jinja_tag(self):
assert self._is_jinja_tag('{{ foo }}')==True
assert self._is_jinja_tag('{ foo }')==False
@@ -80,22 +65,22 @@ class RenderTestCase(TestCase):
assert self.engine.create_text_span_node(self.document, 'text').toxml() == 'text'
-class EncodeLFAndFWithinTextNamespace(TestCase):
- """Test encoding of line feed and tab chars within text: namespace"""
+class EscapingVariablesValues(TestCase):
+ """
+ Test encoding of line feed and tab variables valuess
+ """
def test_encode_linefeed_char(self):
- xml = 'This\nLF'
- espected = 'ThisLF'
- assert (Renderer._encode_escape_chars(xml) == espected)
+ xml = 'This\nLF'
+ expected = 'ThisLF'
+ assert (Renderer.get_escaped_var_value(xml) == expected)
- def test_encode_tab_char(self):
- xml = 'This\tTab'
- espected = 'ThisTab'
- assert (Renderer._encode_escape_chars(xml) == espected)
+ def test_encode_linefeed_char(self):
+ xml = 'This\tTab char'
+ expected = 'ThisTab char'
+ assert (Renderer.get_escaped_var_value(xml) == expected)
- def test_escape_elem_with_attributes(self):
- """A bug in _encode_escape_chars was preventing it from escaping
- LF and tabs inside text elements with tag attributes. See:
- https://github.com/christopher-ramirez/secretary/issues/39"""
- xml = 'This\nLF'
- espected = 'ThisLF'
- assert (Renderer._encode_escape_chars(xml) == espected)
+ def test_escape_xml_reserved_chars(self):
+ ''' Should also escape minor and mayor signs '''
+ xml = '1 is > than 0 & -1 is <'
+ expected = '1 is > than 0 & -1 is <'
+ assert (Renderer.get_escaped_var_value(xml) == expected)