# -*- coding: utf-8 -*- from Products.PortalTransforms.interfaces import ITransform from docutils.core import publish_parts from zope.interface import implementer import six @implementer(ITransform) class rest(object): r"""Converts from reST to HTML. >>> transform = rest() >>> class D: ... def setData(self, data): ... self.value = data >>> data = transform.convert('*hello world*', D()) >>> print(data.value) <p><em>hello world</em></p> <BLANKLINE> We want the 'raw' and 'include' directives to be disabled by default: >>> try: ... out = transform.convert('.. raw:: html\n :file: <isonum.txt>', D()) # noqa ... except NotImplementedError: ... print('Good') ... else: ... if ""raw" directive disabled." in out.value: ... print('Good') ... else: ... print('Failure') Good >>> try: ... out = transform.convert('.. include:: <isonum.txt>', D()) ... except NotImplementedError: ... print('Good') ... else: ... if ""include" directive disabled." in out.value: ... print('Good') ... else: ... print('Failure') Good """ __name__ = "rest_to_html" inputs = ("text/x-rst", "text/restructured",) output = "text/html" def __init__(self, name=None, **kwargs): if name: self.__name__ = name self.config = { 'inputs': self.inputs, 'output': self.output, 'report_level': 2, 'initial_header_level': 2, } self.config_metadata = { 'inputs': ('list', 'Inputs', 'Input(s) MIME type. Change with care.'), 'initial_header_level': ('int', 'Initial Header Level', 'Level of first header tag. Setting it to "2" will make ' 'the first header be "<h2>".'), 'report_level': ('int', 'Report Level', 'Level of error reporting. Set to "1" will display all ' 'messages. Setting it to "5" will display no messages.'), } self.config.update(kwargs) def name(self): return self.__name__ def convert(self, orig, data, **kwargs): # do the format writer_name = kwargs.get('writer_name', 'html4css1') encoding = kwargs.get('encoding', 'utf-8') input_encoding = kwargs.get('input_encoding', encoding) output_encoding = kwargs.get('output_encoding', encoding) language = kwargs.get('language', 'en') initial_header_level = int(self.config.get('initial_header_level', 2)) report_level = int(self.config.get('report_level', 2)) # Note: we must NOT use warning_stream and stylesheet, because an attacker can abuse them. # See https://sourceforge.net/p/docutils/bugs/413/ # Part of PloneHotfix20210518. # It would be okay if we can be sure this method is called from trusted Python code, # but we cannot be sure. # We keep them in the settings, to be sure nothing changes due to this fix. settings = { 'documentclass': '', 'traceback': 1, 'input_encoding': input_encoding, 'output_encoding': output_encoding, 'stylesheet': None, 'stylesheet_path': None, 'file_insertion_enabled': 0, 'raw_enabled': 0, 'language_code': language, # starting level for <H> elements: 'initial_header_level': initial_header_level + 1, # set the reporting level to something sane: 'report_level': report_level, # don't break if we get errors: 'halt_level': 6, # remember warnings: 'warning_stream': None } parts = publish_parts( source=orig, writer_name=writer_name, settings_overrides=settings, config_section='zope application' ) header = '<h%(level)s class="title">%(title)s</h%(level)s>\n' % { 'level': initial_header_level, 'title': parts['title']} subheader = '<h%(level)s class="subtitle">%(subtitle)s</h%(level)s>\n' % { # noqa 'level': initial_header_level+1, 'subtitle': parts['subtitle']} body = '%(docinfo)s%(body)s' % { 'docinfo': parts['docinfo'], 'body': parts['body']} html = '' if parts['title']: html = html + header if parts['subtitle']: html = html + subheader html = html + body # TODO: check if this unicode condition works on Python 3. if six.PY2 and output_encoding != 'unicode': html = html.encode(output_encoding) html = html.replace(' class="document"', '', 1) data.setData(html) return data def register(): return rest()