Example #1
0
    def process_response(self, request, response):
        """
        Transform the response with Diazo if transformable
        """
        content = response
        if check_themes_enabled(request):
            theme = get_active_theme(request)
            if theme:
                rules_file = os.path.join(theme.theme_path(), 'rules.xml')
                if theme.id != self.theme_id or not os.path.exists(
                        rules_file) or theme.debug:
                    if not theme.builtin:
                        if theme.rules:
                            fp = open(rules_file, 'w')
                            try:
                                fp.write(theme.rules.serialize())
                            finally:
                                fp.close()

                    self.theme_id = theme.id

                    self.diazo = DiazoMiddleware(
                        app=self.app,
                        global_conf=None,
                        rules=rules_file,
                        prefix=theme.theme_url(),
                        doctype=DOCTYPE,
                    )
                    compiled_theme = self.diazo.compile_theme()
                    self.transform = etree.XSLT(
                        compiled_theme,
                        access_control=self.diazo.access_control)
                    self.params = {}
                    for key, value in self.diazo.environ_param_map.items():
                        if key in request.environ:
                            if value in self.diazo.unquoted_params:
                                self.params[value] = request.environ[key]
                            else:
                                self.params[value] = quote_param(
                                    request.environ[key])

                try:
                    if isinstance(response, etree._Element):
                        response = HttpResponse()
                    else:
                        parser = etree.HTMLParser(remove_blank_text=True,
                                                  remove_comments=True)
                        content = etree.fromstring(response.content, parser)
                    result = self.transform(content.decode('utf-8'),
                                            **self.params)
                    response.content = XMLSerializer(
                        result, doctype=DOCTYPE).serialize()
                except Exception, e:
                    getLogger('django_diazo').error(e)
Example #2
0
 def __call__(self, environ, start_response):
     request = Request(environ)
     response = request.get_response(self.app)
     
     app_iter = response(environ, start_response)
     
     if self.should_ignore(request) or not self.should_transform(response):
         return app_iter
     
     # Set up parameters
     
     params = {}
     for key, value in self.environ_param_map.items():
         if key in environ:
             params[value] = quote_param(environ[key])
     for key, value in self.params.items():
         params[key] = quote_param(value)
     
     # Apply the transformation
     app_iter = getHTMLSerializer(app_iter)
     tree = self.transform(app_iter.tree, **params)
     
     # Set content type and choose XHTML or HTML serializer
     serializer = html.tostring
     response.headers['Content-Type'] = 'text/html'
     
     if tree.docinfo.doctype and 'XHTML' in tree.docinfo.doctype:
         serializer = etree.tostring
         response.headers['Content-Type'] = 'application/xhtml+xml'
     
     app_iter = XMLSerializer(tree, serializer=serializer)
     
     # Calculate the content length - we still return the parsed tree
     # so that other middleware could avoid having to re-parse, even if
     # we take a hit on serialising here
     if self.update_content_length and 'Content-Length' in response.headers:
         response.headers['Content-Length'] = str(len(str(app_iter)))
     
     # Return a repoze.xmliter XMLSerializer, which helps avoid re-parsing
     # the content tree in later middleware stages
     return app_iter
Example #3
0
def build_xsl_params_document(xsl_params):
    if xsl_params is None:
        xsl_params = {}
    if not 'path' in xsl_params:
        xsl_params['path'] = ''
    known_params = etree.XML('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" />')
    for param_name, param_value in xsl_params.items():
        param_element = etree.SubElement(known_params, "{http://www.w3.org/1999/XSL/Transform}param")
        param_element.attrib['name'] = param_name
        if isinstance(param_value, basestring):
            param_element.text = param_value
        else:
            param_element.attrib['select'] = str(quote_param(param_value))
        param_element.tail = '\n'
    
    return known_params    
Example #4
0
    def process_response(self, request, response):
        """
        Transform the response with Diazo if transformable
        """
        content = response
        if check_themes_enabled(request):
            theme = get_active_theme(request)
            if theme:
                rules_file = os.path.join(theme.theme_path(), 'rules.xml')
                if theme.id != self.theme_id or not os.path.exists(rules_file) or theme.debug:
                    if not theme.builtin:
                        if theme.rules:
                            fp = open(rules_file, 'w')
                            try:
                                fp.write(theme.rules.serialize())
                            finally:
                                fp.close()

                    self.theme_id = theme.id

                    self.diazo = DiazoMiddleware(
                        app=self.app,
                        global_conf=None,
                        rules=rules_file,
                        prefix=theme.theme_url(),
                        doctype=DOCTYPE,
                    )
                    compiled_theme = self.diazo.compile_theme()
                    self.transform = etree.XSLT(compiled_theme, access_control=self.diazo.access_control)
                    self.params = {}
                    for key, value in self.diazo.environ_param_map.items():
                        if key in request.environ:
                            if value in self.diazo.unquoted_params:
                                self.params[value] = request.environ[key]
                            else:
                                self.params[value] = quote_param(request.environ[key])

                try:
                    if isinstance(response, etree._Element):
                        response = HttpResponse()
                    else:
                        parser = etree.HTMLParser(remove_blank_text=True, remove_comments=True)
                        content = etree.fromstring(response.content, parser)
                    result = self.transform(content, **self.params)
                    response.content = XMLSerializer(result, doctype=DOCTYPE).serialize()
                except Exception, e:
                    getLogger('django_diazo').error(e)
Example #5
0
def build_xsl_params_document(xsl_params):
    if xsl_params is None:
        xsl_params = {}
    if not 'path' in xsl_params:
        xsl_params['path'] = ''
    known_params = etree.XML(
        '<xsl:stylesheet version="1.0" '
        'xmlns:xsl="http://www.w3.org/1999/XSL/Transform" />')
    for param_name, param_value in xsl_params.items():
        param_element = etree.SubElement(
            known_params, "{http://www.w3.org/1999/XSL/Transform}param")
        param_element.attrib['name'] = param_name
        if isinstance(param_value, basestring):
            param_element.text = param_value
        else:
            param_element.attrib['select'] = str(quote_param(param_value))
        param_element.tail = '\n'

    return known_params
Example #6
0
File: run.py Project: nkabir/diazo
def main():
    """Called from console script
    """
    op = _createOptionParser(usage=usage)
    op.add_option("-x", "--xsl", metavar="transform.xsl", help="XSL transform", dest="xsl", default=None)
    op.add_option("--path", metavar="PATH", help="URI path", dest="path", default=None)
    op.add_option(
        "--parameters",
        metavar="param1=val1,param2=val2",
        help="Set the values of arbitrary parameters",
        dest="parameters",
        default=None,
    )
    op.add_option(
        "--runtrace-xml",
        metavar="runtrace.xml",
        help="Write an xml format runtrace to file",
        dest="runtrace_xml",
        default=None,
    )
    op.add_option(
        "--runtrace-html",
        metavar="runtrace.html",
        help="Write an html format runtrace to file",
        dest="runtrace_html",
        default=None,
    )
    (options, args) = op.parse_args()

    if len(args) > 2:
        op.error("Wrong number of arguments.")
    elif len(args) == 2:
        if options.xsl or options.rules:
            op.error("Wrong number of arguments.")
        path, content = args
        if path.lower().endswith(".xsl"):
            options.xsl = path
        else:
            options.rules = path
    elif len(args) == 1:
        content, = args
    else:
        op.error("Wrong number of arguments.")
    if options.rules is None and options.xsl is None:
        op.error("Must supply either options or rules")

    if options.trace:
        logger.setLevel(logging.DEBUG)

    runtrace = False
    if options.runtrace_xml or options.runtrace_html:
        runtrace = True

    parser = etree.HTMLParser()
    parser.resolvers.add(RunResolver(os.path.dirname(content)))

    if options.xsl is not None:
        output_xslt = etree.parse(options.xsl)
    else:

        xsl_params = None
        if options.xsl_params:
            xsl_params = split_params(options.xsl_params)

        output_xslt = compile_theme(
            rules=options.rules,
            theme=options.theme,
            extra=options.extra,
            parser=parser,
            read_network=options.read_network,
            absolute_prefix=options.absolute_prefix,
            includemode=options.includemode,
            indent=options.pretty_print,
            xsl_params=xsl_params,
            runtrace=runtrace,
        )

    if content == "-":
        content = sys.stdin

    if options.read_network:
        access_control = AC_READ_NET
    else:
        access_control = AC_READ_FILE

    transform = etree.XSLT(output_xslt, access_control=access_control)
    content_doc = etree.parse(content, parser=parser)
    params = {}
    if options.path is not None:
        params["path"] = "'%s'" % options.path

    if options.parameters:
        for key, value in split_params(options.parameters).items():
            params[key] = quote_param(value)

    output_html = transform(content_doc, **params)
    if isinstance(options.output, basestring):
        out = open(options.output, "wt")
    else:
        out = options.output
    out.write(str(output_html))

    if runtrace:
        runtrace_doc = diazo.runtrace.generate_runtrace(rules=options.rules, error_log=transform.error_log)
        if options.runtrace_xml:
            if options.runtrace_xml == "-":
                out = sys.stdout
            else:
                out = open(options.runtrace_xml, "wt")
            runtrace_doc.write(out, encoding="utf-8", pretty_print=options.pretty_print)
        if options.runtrace_html:
            if options.runtrace_html == "-":
                out = sys.stdout
            else:
                out = open(options.runtrace_html, "wt")
            out.write(str(diazo.runtrace.runtrace_to_html(runtrace_doc)))

    for msg in transform.error_log:
        if not msg.message.startswith("<runtrace "):
            logger.warn(msg)
Example #7
0
    def testAll(self):
        self.errors = StringIO()
        config = ConfigParser.ConfigParser()
        config.read([defaultsfn, os.path.join(self.testdir, "options.cfg")])
        
        themefn = None
        if config.get('diazotest', 'theme'):
            themefn = os.path.join(self.testdir, config.get('diazotest', 'theme'))
        contentfn = os.path.join(self.testdir, "content.html")
        rulesfn = os.path.join(self.testdir, "rules.xml")
        xpathsfn = os.path.join(self.testdir, "xpaths.txt")
        xslfn = os.path.join(self.testdir, "compiled.xsl")
        outputfn = os.path.join(self.testdir, "output.html")
        
        xsl_params = {}
        extra_params = config.get('diazotest', 'extra-params')
        if extra_params:
            for token in extra_params.split(' '):
                token_split = token.split(':')
                xsl_params[token_split[0]] = len(token_split) > 1 and token_split[1] or None
        
        if not os.path.exists(rulesfn):
            return
        
        contentdoc = etree.parse(source=contentfn, base_url=contentfn,
                                       parser=etree.HTMLParser())

        # Make a compiled version
        theme_parser = etree.HTMLParser()
        ct = diazo.compiler.compile_theme(
            rules=rulesfn,
            theme=themefn,
            parser=theme_parser,
            absolute_prefix=config.get('diazotest', 'absolute-prefix'),
            indent=config.getboolean('diazotest', 'pretty-print'),
            xsl_params=xsl_params,
            )
        
        # Serialize / parse the theme - this can catch problems with escaping.
        cts = etree.tostring(ct)
        parser = etree.XMLParser()
        etree.fromstring(cts, parser=parser)

        # Compare to previous version
        if os.path.exists(xslfn):
            old = open(xslfn).read()
            new = cts
            if old != new:
                if self.writefiles:
                    open(xslfn + '.old', 'w').write(old)
                if self.warnings:
                    print "WARNING:", "compiled.xsl has CHANGED"
                    for line in difflib.unified_diff(old.split('\n'), new.split('\n'), xslfn, 'now'):
                        print line

        # Write the compiled xsl out to catch unexpected changes
        if self.writefiles:
            open(xslfn, 'w').write(cts)

        # Apply the compiled version, then test against desired output
        theme_parser.resolvers.add(diazo.run.RunResolver(self.testdir))
        processor = etree.XSLT(ct)
        params = {}
        params['path'] = "'%s'" % config.get('diazotest', 'path')
        
        for key in xsl_params:
            try:
                params[key] = quote_param(config.get('diazotest', key))
            except ConfigParser.NoOptionError:
                pass
        
        result = processor(contentdoc, **params)

        # Read the whole thing to strip off xhtml namespace.
        # If we had xslt 2.0 then we could use xpath-default-namespace.
        self.themed_string = str(result)
        self.themed_content = etree.ElementTree(file=StringIO(self.themed_string), 
                                                parser=etree.HTMLParser())

        # remove the extra meta content type

        metas = self.themed_content.xpath("/html/head/meta[@http-equiv='Content-Type']")
        if metas:
            meta = metas[0]
            meta.getparent().remove(meta)

        if os.path.exists(xpathsfn):
            for xpath in open(xpathsfn).readlines():
                # Read the XPaths from the file, skipping blank lines and
                # comments
                this_xpath = xpath.strip()
                if not this_xpath or this_xpath[0] == '#':
                    continue
                assert self.themed_content.xpath(this_xpath), "%s: %s" % (xpathsfn, this_xpath)

        # Compare to previous version
        if os.path.exists(outputfn):
            old = open(outputfn).read()
            new = self.themed_string
            if old != new:
                #if self.writefiles:
                #    open(outputfn + '.old', 'w').write(old)
                for line in difflib.unified_diff(old.split('\n'), new.split('\n'), outputfn, 'now'):
                    print  line
                assert old == new, "output.html has CHANGED"

        # Write out the result to catch unexpected changes
        if self.writefiles:
            open(outputfn, 'w').write(self.themed_string)
Example #8
0
    def transformIterable(self, result, encoding):
        """Apply the transform if required
        """

        result = self.parseTree(result)
        if result is None:
            return None

        transform = self.setupTransform()
        if transform is None:
            return None

        # Find real or virtual path - PATH_INFO has VHM elements in it
        url = self.request.get('ACTUAL_URL', '')

        # Find the host name
        base = self.request.get('BASE1', '')
        path = url[len(base):]
        parts = urlsplit(base.lower())

        params = dict(
                url=quote_param(url),
                base=quote_param(base),
                path=quote_param(path),
                scheme=quote_param(parts.scheme),
                host=quote_param(parts.netloc),
            )

        # Add expression-based parameters

        toadapt = (getSite(), self.request)
        settings = getMultiAdapter(toadapt, IThemeSettings)
        if settings.doctype:
            result.doctype = settings.doctype
            if not result.doctype.endswith('\n'):
                result.doctype += '\n'
        parameterExpressions = settings.parameterExpressions or {}
        if parameterExpressions:
            cache = getCache(settings)
            DevelopmentMode = Globals.DevelopmentMode

            # Compile and cache expressions
            expressions = None
            if not DevelopmentMode:
                expressions = cache.expressions

            if expressions is None:
                expressions = {}
                for name, expressionText in parameterExpressions.items():
                    expressions[name] = compileExpression(expressionText)

                if not DevelopmentMode:
                    cache.updateExpressions(expressions)

            # Execute all expressions
            context = findContext(self.published)
            expressionContext = createExpressionContext(context, self.request)
            for name, expression in expressions.items():
                params[name] = quote_param(expression(expressionContext))

        transformed = transform(result.tree, **params)
        if transformed is None:
            return None

        result.tree = transformed

        return result
Example #9
0
def main():
    """Called from console script
    """
    op = _createOptionParser(usage=usage)
    op.add_option("-x",
                  "--xsl",
                  metavar="transform.xsl",
                  help="XSL transform",
                  dest="xsl",
                  default=None)
    op.add_option("--path",
                  metavar="PATH",
                  help="URI path",
                  dest="path",
                  default=None)
    op.add_option("--parameters",
                  metavar="param1=val1,param2=val2",
                  help="Set the values of arbitrary parameters",
                  dest="parameters",
                  default=None)
    (options, args) = op.parse_args()

    if len(args) > 2:
        op.error("Wrong number of arguments.")
    elif len(args) == 2:
        if options.xsl or options.rules:
            op.error("Wrong number of arguments.")
        path, content = args
        if path.lower().endswith('.xsl'):
            options.xsl = path
        else:
            options.rules = path
    elif len(args) == 1:
        content, = args
    else:
        op.error("Wrong number of arguments.")
    if options.rules is None and options.xsl is None:
        op.error("Must supply either options or rules")

    if options.trace:
        logger.setLevel(logging.DEBUG)

    parser = etree.HTMLParser()
    parser.resolvers.add(RunResolver(os.path.dirname(content)))

    if options.xsl is not None:
        output_xslt = etree.parse(options.xsl)
    else:

        xsl_params = None
        if options.xsl_params:
            xsl_params = split_params(options.xsl_params)

        output_xslt = compile_theme(
            rules=options.rules,
            theme=options.theme,
            extra=options.extra,
            parser=parser,
            read_network=options.read_network,
            absolute_prefix=options.absolute_prefix,
            includemode=options.includemode,
            indent=options.pretty_print,
            xsl_params=xsl_params,
        )

    if content == '-':
        content = sys.stdin

    if options.read_network:
        access_control = AC_READ_NET
    else:
        access_control = AC_READ_FILE

    transform = etree.XSLT(output_xslt, access_control=access_control)
    content_doc = etree.parse(content, parser=parser)
    params = {}
    if options.path is not None:
        params['path'] = "'%s'" % options.path

    if options.parameters:
        for key, value in split_params(options.parameters).items():
            params[key] = quote_param(value)

    output_html = transform(content_doc, **params)
    if isinstance(options.output, basestring):
        out = open(options.output, 'wt')
    else:
        out = options.output
    out.write(str(output_html))
    for msg in transform.error_log:
        logger.warn(msg)
Example #10
0
def compile_theme(rules, theme=None, extra=None, css=True, xinclude=True,
                  absolute_prefix=None, update=True, trace=False,
                  includemode=None, parser=None, compiler_parser=None,
                  rules_parser=None, access_control=None, read_network=False,
                  indent=None, xsl_params=None, runtrace=False):
    """Invoke the diazo compiler.

    * ``rules`` is the rules file
    * ``theme`` is the theme file
    * ``extra`` is an optional XSLT file with Diazo extensions (depracated, use
      inline xsl in the rules instead)
    * ``css``   can be set to False to disable CSS syntax support (providing a
      moderate speed gain)
    * ``xinclude`` can be set to False to disable XInclude support during the
      compile phase (providing a moderate speed gain)
    * ``absolute_prefix`` can be set to a string that will be prefixed to any
      *relative* URL referenced in an image, link or stylesheet in the theme
      HTML file before the theme is passed to the compiler. This allows a
      theme to be written so that it can be opened and views standalone on the
      filesystem, even if at runtime its static resources are going to be
      served from some other location. For example, an
      ``<img src="images/foo.jpg" />`` can be turned into
      ``<img src="/static/images/foo.jpg" />`` with an ``absolute_prefix`` of
      "/static".
    * ``update`` can be set to False to disable the automatic update support
      for the old Deliverance 0.2 namespace (for a moderate speed gain)
    * ``trace`` can be set to True to enable compiler trace information
    * ``runtrace`` can be set to True to add tracing into the XSL output
    * ``includemode`` can be set to 'document', 'esi' or 'ssi' to change the
      way in which includes are processed
    * ``parser`` can be set to an lxml parser instance; the default is an
      HTMLParser
    * ``compiler_parser``` can be set to an lxml parser instance; the default
      is a XMLParser
    * ``rules_parser`` can be set to an lxml parser instance; the default is a
      XMLParser.
    * ``xsl_params`` can be set to a dictionary of parameters that will be
      known to the compiled theme transform. The keys should be the parameter
      names. Values are default values.
    """
    if access_control is not None:
        read_network = access_control.options['read_network']
    rules_doc = process_rules(
        rules=rules,
        theme=theme,
        extra=extra,
        css=css,
        xinclude=xinclude,
        absolute_prefix=absolute_prefix,
        update=update,
        trace=trace,
        includemode=includemode,
        parser=parser,
        rules_parser=rules_parser,
        read_network=read_network,
    )

    # Build a document with all the <xsl:param /> values to set the defaults
    # for every value passed in as xsl_params
    known_params = build_xsl_params_document(xsl_params)

    # Create a pseudo resolver for this
    known_params_url = 'file:///__diazo_known_params__'
    emit_stylesheet_resolver = CustomResolver({
        known_params_url: etree.tostring(known_params)})
    emit_stylesheet_parser = etree.XMLParser()
    emit_stylesheet_parser.resolvers.add(emit_stylesheet_resolver)

    # Set up parameters
    params = {}
    if indent is not None:
        params['indent'] = indent and "'yes'" or "'no'"
    params['known_params_url'] = quote_param(known_params_url)
    params['runtrace'] = '1' if runtrace else '0'

    # Run the final stage compiler
    emit_stylesheet = pkg_xsl(
        'emit-stylesheet.xsl', parser=emit_stylesheet_parser)
    compiled_doc = emit_stylesheet(rules_doc, **params)
    compiled_doc = set_parser(etree.tostring(compiled_doc), parser,
                              compiler_parser)

    return compiled_doc
Example #11
0
    def getFrame(self):
        """AJAX method to load a frame's contents

        Expects two query string parameters: ``path`` - the path to fetch - and
        ``theme``, which can be 'off', to disable the theme and 'apply' to
        apply the current theme to the response.

        Additionally:

        - a query string parameter ``links`` can be set to one of ``disable``
          or ``replace``. The former will disable hyperlinks; the latter will
          replace them with links using the ``@@themeing-controlpanel-getframe``
          view.
        - a query string parameter ``forms`` can be set to one of ``disable``
          or ``replace``. The former will disable forms ; the latter will
          replace them with links using the ``@@themeing-controlpanel-getframe``
          view.
        - a query string parameter ``title`` can be set to give a new page
          title
        """

        processInputs(self.request)

        path = self.request.form.get('path', None)
        theme = self.request.form.get('theme', 'off')
        links = self.request.form.get('links', None)
        forms = self.request.form.get('forms', None)
        title = self.request.form.get('title', None)

        if not path:
            return "<html><head></head><body></body></html>"

        portal = getPortal()
        portal_url = portal.absolute_url()
        response = subrequest(path, root=portal)

        result = response.getBody()
        content_type = response.headers.get('content-type')
        encoding = None
        if content_type is not None and ';' in content_type:
            content_type, encoding = content_type.split(';', 1)
        if encoding is None:
            encoding = 'utf-8'
        else:
            # e.g. charset=utf-8
            encoding = encoding.split('=', 1)[1].strip()

        # Not HTML? Return as-is
        if content_type is None or not content_type.startswith('text/html'):
            if len(result) == 0:
                result = ' '  # Zope does not deal well with empty responses
            return result

        result = result.decode(encoding).encode('ascii', 'xmlcharrefreplace')
        if len(result) == 0:
            result = ' '  # Zope does not deal well with empty responses

        if theme == 'off':
            self.request.response.setHeader('X-Theme-Disabled', '1')
        elif theme == 'apply':
            self.request.response.setHeader('X-Theme-Disabled', '1')
            themeInfo = getThemeFromResourceDirectory(self.context)

            registry = getUtility(IRegistry)
            settings = registry.forInterface(IThemeSettings, False)

            context = self.context
            try:
                context = findContext(portal.restrictedTraverse(path))
            except (KeyError, NotFound,):
                pass

            serializer = getHTMLSerializer([result], pretty_print=False)

            try:
                transform = compileThemeTransform(themeInfo.rules, themeInfo.absolutePrefix, settings.readNetwork, themeInfo.parameterExpressions or {})
            except lxml.etree.XMLSyntaxError, e:
                return self.theme_error_template(error=e.msg)

            params = prepareThemeParameters(context, self.request, themeInfo.parameterExpressions or {})

            # Fix url and path since the request gave us this view
            params['url'] = quote_param("%s%s" % (portal_url, path,))
            params['path'] = quote_param("%s%s" % (portal.absolute_url_path(), path,))

            if themeInfo.doctype:
                serializer.doctype = themeInfo.doctype
                if not serializer.doctype.endswith('\n'):
                    serializer.doctype += '\n'

            serializer.tree = transform(serializer.tree, **params)
            result = ''.join(serializer)
Example #12
0
def main():
    """Called from console script
    """
    op = _createOptionParser(usage=usage)
    op.add_option(
        '-x',
        '--xsl',
        metavar='transform.xsl',
        help='XSL transform',
        dest='xsl',
        default=None,
    )
    op.add_option(
        '--path',
        metavar='PATH',
        help='URI path',
        dest='path',
        default=None,
    )
    op.add_option(
        '--parameters',
        metavar='param1=val1,param2=val2',
        help='Set the values of arbitrary parameters',
        dest='parameters',
        default=None,
    )
    op.add_option(
        '--runtrace-xml',
        metavar='runtrace.xml',
        help='Write an xml format runtrace to file',
        dest='runtrace_xml',
        default=None,
    )
    op.add_option(
        '--runtrace-html',
        metavar='runtrace.html',
        help='Write an html format runtrace to file',
        dest='runtrace_html',
        default=None,
    )
    (options, args) = op.parse_args()

    if len(args) > 2:
        op.error('Wrong number of arguments.')
    elif len(args) == 2:
        if options.xsl or options.rules:
            op.error('Wrong number of arguments.')
        path, content = args
        if path.lower().endswith('.xsl'):
            options.xsl = path
        else:
            options.rules = path
    elif len(args) == 1:
        content, = args
    else:
        op.error('Wrong number of arguments.')
    if options.rules is None and options.xsl is None:
        op.error('Must supply either options or rules')

    if options.trace:
        logger.setLevel(logging.DEBUG)

    runtrace = False
    if options.runtrace_xml or options.runtrace_html:
        runtrace = True

    parser = etree.HTMLParser()
    parser.resolvers.add(RunResolver(os.path.dirname(content)))

    if options.xsl is not None:
        output_xslt = etree.parse(options.xsl)
    else:
        xsl_params = None
        if options.xsl_params:
            xsl_params = split_params(options.xsl_params)

        output_xslt = compile_theme(
            rules=options.rules,
            theme=options.theme,
            extra=options.extra,
            parser=parser,
            read_network=options.read_network,
            absolute_prefix=options.absolute_prefix,
            includemode=options.includemode,
            indent=options.pretty_print,
            xsl_params=xsl_params,
            runtrace=runtrace,
        )

    if content == '-':
        content = sys.stdin

    if options.read_network:
        access_control = AC_READ_NET
    else:
        access_control = AC_READ_FILE

    transform = etree.XSLT(output_xslt, access_control=access_control)
    content_doc = etree.parse(content, parser=parser)
    params = {}
    if options.path is not None:
        params['path'] = "'{path}'".format(path=options.path)

    if options.parameters:
        for key, value in split_params(options.parameters).items():
            params[key] = quote_param(value)

    output_html = transform(content_doc, **params)
    if isinstance(options.output, string_types):
        out = open(options.output, 'wt')
    else:
        out = options.output
    out.write(str(output_html))

    if runtrace:
        runtrace_doc = diazo.runtrace.generate_runtrace(
            rules=options.rules,
            error_log=transform.error_log,
        )
        if options.runtrace_xml:
            if options.runtrace_xml == '-':
                out = sys.stdout
            else:
                out = open(options.runtrace_xml, 'wt')
            runtrace_doc.write(
                out,
                encoding='utf-8',
                pretty_print=options.pretty_print,
            )
        if options.runtrace_html:
            if options.runtrace_html == '-':
                out = sys.stdout
            else:
                out = open(options.runtrace_html, 'wt')
            out.write(str(diazo.runtrace.runtrace_to_html(runtrace_doc)))

    for msg in transform.error_log:
        if not msg.message.startswith('<runtrace '):
            logger.warn(msg)
Example #13
0
    def getFrame(self):
        """AJAX method to load a frame's contents

        Expects two query string parameters: ``path`` - the path to fetch - and
        ``theme``, which can be 'off', to disable the theme and 'apply' to
        apply the current theme to the response.

        Additionally:

        - a query string parameter ``links`` can be set to one of ``disable``
          or ``replace``. The former will disable hyperlinks; the latter will
          replace them with links using the
          ``@@themeing-controlpanel-getframe`` view.
        - a query string parameter ``forms`` can be set to one of ``disable``
          or ``replace``. The former will disable forms ; the latter will
          replace them with links using the
          ``@@themeing-controlpanel-getframe`` view.
        - a query string parameter ``title`` can be set to give a new page
          title
        """

        processInputs(self.request)

        path = self.request.form.get('path', None)
        theme = self.request.form.get('theme', 'off')
        links = self.request.form.get('links', None)
        forms = self.request.form.get('forms', None)
        title = self.request.form.get('title', None)

        if not path:
            return "<html><head></head><body></body></html>"

        portal = getPortal()
        portal_url = portal.absolute_url()
        response = subrequest(path, root=portal)

        result = response.getBody()
        content_type = response.headers.get('content-type')
        encoding = None
        if content_type is not None and ';' in content_type:
            content_type, encoding = content_type.split(';', 1)
        if encoding is None:
            encoding = 'utf-8'
        else:
            # e.g. charset=utf-8
            encoding = encoding.split('=', 1)[1].strip()

        # Not HTML? Return as-is
        if content_type is None or not content_type.startswith('text/html'):
            if len(result) == 0:
                result = ' '  # Zope does not deal well with empty responses
            return result

        result = result.decode(encoding).encode('ascii', 'xmlcharrefreplace')
        if len(result) == 0:
            result = ' '  # Zope does not deal well with empty responses

        if theme == 'off':
            self.request.response.setHeader('X-Theme-Disabled', '1')
        elif theme == 'apply':
            self.request.response.setHeader('X-Theme-Disabled', '1')
            themeInfo = getThemeFromResourceDirectory(self.context)

            policy = theming_policy(self.request)
            settings = policy.getSettings()

            context = self.context
            try:
                context = findContext(portal.restrictedTraverse(path))
            except (KeyError, NotFound,):
                pass

            serializer = getHTMLSerializer([result], pretty_print=False)

            try:
                transform = compileThemeTransform(
                    themeInfo.rules, themeInfo.absolutePrefix,
                    settings.readNetwork, themeInfo.parameterExpressions or {})
            except lxml.etree.XMLSyntaxError as e:
                return self.theme_error_template(error=e.msg)

            params = prepareThemeParameters(
                context, self.request, themeInfo.parameterExpressions or {})

            # Fix url and path since the request gave us this view
            params['url'] = quote_param(''.join((portal_url, path,)))
            params['path'] = quote_param(
                ''.join((portal.absolute_url_path(), path,))
            )

            if themeInfo.doctype:
                serializer.doctype = themeInfo.doctype
                if not serializer.doctype.endswith('\n'):
                    serializer.doctype += '\n'

            serializer.tree = transform(serializer.tree, **params)
            result = ''.join(serializer)

        if title or links or forms:
            tree = lxml.html.fromstring(result)

            def encodeUrl(orig):
                origUrl = urllib.parse.urlparse(orig)
                newPath = origUrl.path
                newQuery = urllib.parse.parse_qs(origUrl.query)

                # relative?
                if not origUrl.netloc:
                    newPath = urllib.parse.urljoin(
                        path.rstrip("/") + "/", newPath.lstrip("/"))
                elif not orig.lower().startswith(portal_url.lower()):
                    # Not an internal URL - ignore
                    return orig

                newQuery['path'] = newPath
                newQuery['theme'] = theme
                if links:
                    newQuery['links'] = links
                if forms:
                    newQuery['forms'] = forms
                if title:
                    if isinstance(title, six.text_type):
                        newQuery['title'] = title.encode('utf-8', 'replace')
                    else:
                        newQuery['title'] = title

                return self.request.getURL() + '?' + urllib.parse.urlencode(newQuery)

            if title:
                titleElement = tree.cssselect("html head title")
                if titleElement:
                    titleElement[0].text = title
                else:
                    headElement = tree.cssselect("html head")
                    if headElement:
                        headElement[0].append(lxml.html.builder.TITLE(title))

            if links:
                for n in tree.cssselect("a[href]"):
                    if links == 'disable':
                        n.attrib['href'] = '#'
                    elif links == 'replace':
                        n.attrib['href'] = encodeUrl(n.attrib['href'])

            if forms:
                for n in tree.cssselect("form[action]"):
                    if forms == 'disable':
                        n.attrib['action'] = '#'
                        n.attrib['onsubmit'] = 'javascript:return false;'
                    elif forms == 'replace':
                        n.attrib['action'] = encodeUrl(n.attrib['action'])

            result = lxml.html.tostring(tree)

        return result
Example #14
0
File: wsgi.py Project: plone/diazo
    def __call__(self, environ, start_response):
        request = Request(environ)
        if self.should_ignore(request):
            return self.app(environ, start_response)

        if self.remove_conditional_headers:
            request.remove_conditional_headers()
        else:
            # Always remove Range and Accept-Encoding headers
            request.remove_conditional_headers(
                remove_encoding=True,
                remove_range=False,
                remove_match=False,
                remove_modified=True,
            )

        response = request.get_response(self.app)
        if not self.should_transform(response):
            return response(environ, start_response)
        try:
            input_encoding = response.charset

            # Note, the Content-Length header will not be set
            if request.method == 'HEAD':
                self.reset_headers(response)
                return response(environ, start_response)

            # Prepare the serializer
            try:
                serializer = getHTMLSerializer(
                    response.app_iter,
                    encoding=input_encoding,
                )
            except etree.XMLSyntaxError:
                # Abort transform on syntax error for empty response
                # Headers should be left intact
                return response(environ, start_response)
        finally:
            if getattr(response.app_iter, 'close', None):
                response.app_iter.close()

        self.reset_headers(response)

        # Set up parameters

        params = {}
        for key, value in self.environ_param_map.items():
            if key in environ:
                if value in self.unquoted_params:
                    params[value] = environ[key]
                else:
                    params[value] = quote_param(environ[key])
        for key, value in self.params.items():
            if key in self.unquoted_params:
                params[key] = value
            else:
                params[key] = quote_param(value)

        # Apply the transformation
        tree = self.transform(serializer.tree, **params)

        # Set content type (normally inferred from stylesheet)
        # Unfortunately lxml does not expose docinfo.mediaType
        if self.content_type is None:
            if tree.getroot().tag == 'html':
                response.content_type = 'text/html'
            else:
                response.content_type = 'text/xml'
        response.charset = tree.docinfo.encoding or self.charset

        # Return a repoze.xmliter XMLSerializer, which helps avoid re-parsing
        # the content tree in later middleware stages.
        response.app_iter = XMLSerializer(tree, doctype=self.doctype)

        # Calculate the content length - we still return the parsed tree
        # so that other middleware could avoid having to re-parse, even if
        # we take a hit on serialising here
        if self.update_content_length:
            response.content_length = len(bytes(response.app_iter))

        return response(environ, start_response)
Example #15
0
def main():
    """Called from console script
    """
    op = _createOptionParser(usage=usage)
    op.add_option("-x", "--xsl", metavar="transform.xsl",
                      help="XSL transform", 
                      dest="xsl", default=None)
    op.add_option("--path", metavar="PATH",
                      help="URI path", 
                      dest="path", default=None)
    op.add_option("--parameters", metavar="param1=val1,param2=val2",
                      help="Set the values of arbitrary parameters",
                      dest="parameters", default=None)
    (options, args) = op.parse_args()

    if len(args) > 2:
        op.error("Wrong number of arguments.")
    elif len(args) == 2:
        if options.xsl or options.rules:
            op.error("Wrong number of arguments.")
        path, content = args
        if path.lower().endswith('.xsl'):
            options.xsl = path
        else:
            options.rules = path
    elif len(args) == 1:
        content, = args
    else:
        op.error("Wrong number of arguments.")
    if options.rules is None and options.xsl is None:
        op.error("Must supply either options or rules")

    if options.trace:
        logger.setLevel(logging.DEBUG)

    parser = etree.HTMLParser()
    parser.resolvers.add(RunResolver(os.path.dirname(content)))

    if options.xsl is not None:
        output_xslt = etree.parse(options.xsl)
    else:
        
        xsl_params=None
        if options.xsl_params:
            xsl_params = split_params(options.xsl_params)
        
        output_xslt = compile_theme(
            rules=options.rules,
            theme=options.theme,
            extra=options.extra,
            parser=parser,
            read_network=options.read_network,
            absolute_prefix=options.absolute_prefix,
            includemode=options.includemode,
            indent=options.pretty_print,
            xsl_params=xsl_params,
            )

    if content == '-':
        content = sys.stdin

    if options.read_network:
        access_control = AC_READ_NET
    else:
        access_control = AC_READ_FILE

    transform = etree.XSLT(output_xslt, access_control=access_control)
    content_doc = etree.parse(content, parser=parser)
    params = {}
    if options.path is not None:
        params['path'] = "'%s'" % options.path
    
    if options.parameters:
        for key, value in split_params(options.parameters).items():
            params[key] = quote_param(value)
    
    output_html = transform(content_doc, **params)
    if isinstance(options.output, basestring):
        out = open(options.output, 'wt')
    else:
        out = options.output
    out.write(str(output_html))
    for msg in transform.error_log:
        logger.warn(msg)
Example #16
0
    def getFrame(self):
        """AJAX method to load a frame's contents

        Expects two query string parameters: ``path`` - the path to fetch - and
        ``theme``, which can be 'off', to disable the theme and 'apply' to
        apply the current theme to the response.

        Additionally:

        - a query string parameter ``links`` can be set to one of ``disable``
          or ``replace``. The former will disable hyperlinks; the latter will
          replace them with links using the
          ``@@themeing-controlpanel-getframe`` view.
        - a query string parameter ``forms`` can be set to one of ``disable``
          or ``replace``. The former will disable forms ; the latter will
          replace them with links using the
          ``@@themeing-controlpanel-getframe`` view.
        - a query string parameter ``title`` can be set to give a new page
          title
        """

        processInputs(self.request)

        path = self.request.form.get('path', None)
        theme = self.request.form.get('theme', 'off')
        links = self.request.form.get('links', None)
        forms = self.request.form.get('forms', None)
        title = self.request.form.get('title', None)

        if not path:
            return "<html><head></head><body></body></html>"

        portal = getPortal()
        portal_url = portal.absolute_url()
        response = subrequest(path, root=portal)

        result = response.getBody()
        content_type = response.headers.get('content-type')
        encoding = None
        if content_type is not None and ';' in content_type:
            content_type, encoding = content_type.split(';', 1)
        if encoding is None:
            encoding = 'utf-8'
        else:
            # e.g. charset=utf-8
            encoding = encoding.split('=', 1)[1].strip()

        # Not HTML? Return as-is
        if content_type is None or not content_type.startswith('text/html'):
            if len(result) == 0:
                result = ' '  # Zope does not deal well with empty responses
            return result

        result = result.decode(encoding).encode('ascii', 'xmlcharrefreplace')
        if len(result) == 0:
            result = ' '  # Zope does not deal well with empty responses

        if theme == 'off':
            self.request.response.setHeader('X-Theme-Disabled', '1')
        elif theme == 'apply':
            self.request.response.setHeader('X-Theme-Disabled', '1')
            themeInfo = getThemeFromResourceDirectory(self.context)

            policy = theming_policy(self.request)
            settings = policy.getSettings()

            context = self.context
            try:
                context = findContext(portal.restrictedTraverse(path))
            except (KeyError, NotFound,):
                pass

            serializer = getHTMLSerializer([result], pretty_print=False)

            try:
                transform = compileThemeTransform(
                    themeInfo.rules, themeInfo.absolutePrefix,
                    settings.readNetwork, themeInfo.parameterExpressions or {})
            except lxml.etree.XMLSyntaxError as e:
                return self.theme_error_template(error=e.msg)

            params = prepareThemeParameters(
                context, self.request, themeInfo.parameterExpressions or {})

            # Fix url and path since the request gave us this view
            params['url'] = quote_param(''.join((portal_url, path,)))
            params['path'] = quote_param(
                ''.join((portal.absolute_url_path(), path,))
            )

            if themeInfo.doctype:
                serializer.doctype = themeInfo.doctype
                if not serializer.doctype.endswith('\n'):
                    serializer.doctype += '\n'

            serializer.tree = transform(serializer.tree, **params)
            result = ''.join(serializer)

        if title or links or forms:
            tree = lxml.html.fromstring(result)

            def encodeUrl(orig):
                origUrl = urllib.parse.urlparse(orig)
                newPath = origUrl.path
                newQuery = urllib.parse.parse_qs(origUrl.query)

                # relative?
                if not origUrl.netloc:
                    newPath = urllib.parse.urljoin(
                        path.rstrip("/") + "/", newPath.lstrip("/"))
                elif not orig.lower().startswith(portal_url.lower()):
                    # Not an internal URL - ignore
                    return orig

                newQuery['path'] = newPath
                newQuery['theme'] = theme
                if links:
                    newQuery['links'] = links
                if forms:
                    newQuery['forms'] = forms
                if title:
                    if isinstance(title, six.text_type):
                        newQuery['title'] = title.encode('utf-8', 'replace')
                    else:
                        newQuery['title'] = title

                return self.request.getURL() + '?' + urllib.parse.urlencode(newQuery)

            if title:
                titleElement = tree.cssselect("html head title")
                if titleElement:
                    titleElement[0].text = title
                else:
                    headElement = tree.cssselect("html head")
                    if headElement:
                        headElement[0].append(lxml.html.builder.TITLE(title))

            if links:
                for n in tree.cssselect("a[href]"):
                    if links == 'disable':
                        n.attrib['href'] = '#'
                    elif links == 'replace':
                        n.attrib['href'] = encodeUrl(n.attrib['href'])

            if forms:
                for n in tree.cssselect("form[action]"):
                    if forms == 'disable':
                        n.attrib['action'] = '#'
                        n.attrib['onsubmit'] = 'javascript:return false;'
                    elif forms == 'replace':
                        n.attrib['action'] = encodeUrl(n.attrib['action'])

            result = lxml.html.tostring(tree)

        return result
Example #17
0
File: wsgi.py Project: ericof/diazo
    def __call__(self, environ, start_response):
        request = Request(environ)
        
        ignore = self.should_ignore(request)

        if not ignore:
            # We do not deal with Range requests
            try:
                del request.headers['Range']
            except KeyError:
                pass

        response = request.get_response(self.app)

        sr = self._sr(start_response)
        app_iter = response(environ, sr)
        
        if ignore or not self.should_transform(response):
            start_response(self._status,
                           self._response_headers,
                           self._exc_info)
            return app_iter
        
        # Set up parameters
        
        params = {}
        for key, value in self.environ_param_map.items():
            if key in environ:
                if value in self.unquoted_params:
                    params[value] = environ[key]
                else:
                    params[value] = quote_param(environ[key])
        for key, value in self.params.items():
            if key in self.unquoted_params:
                params[key] = value
            else:
                params[key] = quote_param(value)
        
        # Apply the transformation
        app_iter = getHTMLSerializer(app_iter)
        tree = self.transform(app_iter.tree, **params)
        
        # Set content type
        # Unfortunately lxml does not expose docinfo.mediaType
        content_type = self.content_type
        if content_type is None:
            if tree.getroot().tag == 'html':
                content_type = 'text/html'
            else:
                content_type = 'text/xml'
        encoding = tree.docinfo.encoding
        if not encoding:
            encoding = "UTF-8"
        response.headers['Content-Type'] = '%s; charset=%s' % (content_type, encoding)
        
        app_iter = XMLSerializer(tree, doctype=self.doctype)
        
        # Calculate the content length - we still return the parsed tree
        # so that other middleware could avoid having to re-parse, even if
        # we take a hit on serialising here
        if self.update_content_length and 'Content-Length' in response.headers:
            response.headers['Content-Length'] = str(len(str(app_iter)))
        
        # Remove Content-Range if set by the application we theme
        if self.update_content_length and 'Content-Range' in response.headers:
            del(response.headers['Content-Range'])

        # Start response here, after we update response headers
        self._response_headers = response.headers.items()
        start_response(self._status,
                       self._response_headers,
                       self._exc_info)
        # Return a repoze.xmliter XMLSerializer, which helps avoid re-parsing
        # the content tree in later middleware stages
        return app_iter
Example #18
0
    def testAll(self):
        self.errors = BytesIO()
        config = configparser.ConfigParser()
        config.read([defaultsfn, os.path.join(self.testdir, "options.cfg")])

        themefn = None
        if config.get('diazotest', 'theme'):
            themefn = os.path.join(self.testdir,
                                   config.get('diazotest', 'theme'))
        contentfn = os.path.join(self.testdir, "content.html")
        rulesfn = os.path.join(self.testdir, "rules.xml")
        xpathsfn = os.path.join(self.testdir, "xpaths.txt")
        xslfn = os.path.join(self.testdir, "compiled.xsl")
        outputfn = os.path.join(self.testdir, "output.html")

        xsl_params = {}
        extra_params = config.get('diazotest', 'extra-params')
        if extra_params:
            for token in extra_params.split(' '):
                token_split = token.split(':')
                xsl_params[token_split[0]] = len(token_split) > 1 and \
                    token_split[1] or None

        if not os.path.exists(rulesfn):
            return

        contentdoc = etree.parse(source=contentfn,
                                 base_url=contentfn,
                                 parser=etree.HTMLParser())

        # Make a compiled version
        theme_parser = etree.HTMLParser()
        ct = diazo.compiler.compile_theme(
            rules=rulesfn,
            theme=themefn,
            parser=theme_parser,
            absolute_prefix=config.get('diazotest', 'absolute-prefix'),
            indent=config.getboolean('diazotest', 'pretty-print'),
            xsl_params=xsl_params,
        )

        # Serialize / parse the theme - this can catch problems with escaping.
        cts = etree.tostring(ct, encoding='unicode')
        parser = etree.XMLParser()
        etree.fromstring(cts, parser=parser)

        # Compare to previous version
        if os.path.exists(xslfn):
            with open(xslfn) as f:
                old = f.read()
            new = cts
            if old != new:
                if self.writefiles:
                    with open(xslfn + '.old', 'w') as f:
                        f.write(old)
                if self.warnings:
                    print("WARNING:", "compiled.xsl has CHANGED")
                    for line in difflib.unified_diff(old.split(u'\n'),
                                                     new.split(u'\n'), xslfn,
                                                     'now'):
                        print(line)

        # Write the compiled xsl out to catch unexpected changes
        if self.writefiles:
            with open(xslfn, 'w') as f:
                f.write(cts)

        # Apply the compiled version, then test against desired output
        theme_parser.resolvers.add(diazo.run.RunResolver(self.testdir))
        processor = etree.XSLT(ct)
        params = {}
        params['path'] = "'%s'" % config.get('diazotest', 'path')

        for key in xsl_params:
            try:
                params[key] = quote_param(config.get('diazotest', key))
            except configparser.NoOptionError:
                pass

        result = processor(contentdoc, **params)

        # Read the whole thing to strip off xhtml namespace.
        # If we had xslt 2.0 then we could use xpath-default-namespace.
        self.themed_string = str(result)
        self.themed_content = etree.ElementTree(file=StringIO(
            self.themed_string),
                                                parser=etree.HTMLParser())

        # remove the extra meta content type

        metas = self.themed_content.xpath(
            "/html/head/meta[@http-equiv='Content-Type']")
        if metas:
            meta = metas[0]
            meta.getparent().remove(meta)

        if os.path.exists(xpathsfn):
            with open(xpathsfn) as f:
                for xpath in f.readlines():
                    # Read the XPaths from the file, skipping blank lines and
                    # comments
                    this_xpath = xpath.strip()
                    if not this_xpath or this_xpath[0] == '#':
                        continue
                    assert self.themed_content.xpath(
                        this_xpath), "%s: %s" % (xpathsfn, this_xpath)

        # Compare to previous version
        if os.path.exists(outputfn):
            with open(outputfn) as f:
                old = f.read()
            new = self.themed_string
            if not xml_compare(etree.fromstring(old.strip()),
                               etree.fromstring(new.strip())):
                # if self.writefiles:
                #    open(outputfn + '.old', 'w').write(old)
                for line in difflib.unified_diff(old.split(u'\n'),
                                                 new.split(u'\n'), outputfn,
                                                 'now'):
                    print(line)
                assert old == new, "output.html has CHANGED"

        # Write out the result to catch unexpected changes
        if self.writefiles:
            with open(outputfn, 'w') as f:
                f.write(self.themed_string)
Example #19
0
def compile_theme(rules,
                  theme=None,
                  extra=None,
                  css=True,
                  xinclude=True,
                  absolute_prefix=None,
                  update=True,
                  trace=False,
                  includemode=None,
                  parser=None,
                  compiler_parser=None,
                  rules_parser=None,
                  access_control=None,
                  read_network=False,
                  indent=None,
                  xsl_params=None,
                  runtrace=False):
    """Invoke the diazo compiler.

    * ``rules`` is the rules file
    * ``theme`` is the theme file
    * ``extra`` is an optional XSLT file with Diazo extensions (depracated, use
      inline xsl in the rules instead)
    * ``css``   can be set to False to disable CSS syntax support (providing a
      moderate speed gain)
    * ``xinclude`` can be set to False to disable XInclude support during the
      compile phase (providing a moderate speed gain)
    * ``absolute_prefix`` can be set to a string that will be prefixed to any
      *relative* URL referenced in an image, link or stylesheet in the theme
      HTML file before the theme is passed to the compiler. This allows a
      theme to be written so that it can be opened and views standalone on the
      filesystem, even if at runtime its static resources are going to be
      served from some other location. For example, an
      ``<img src="images/foo.jpg" />`` can be turned into
      ``<img src="/static/images/foo.jpg" />`` with an ``absolute_prefix`` of
      "/static".
    * ``update`` can be set to False to disable the automatic update support
      for the old Deliverance 0.2 namespace (for a moderate speed gain)
    * ``trace`` can be set to True to enable compiler trace information
    * ``runtrace`` can be set to True to add tracing into the XSL output
    * ``includemode`` can be set to 'document', 'esi' or 'ssi' to change the
      way in which includes are processed
    * ``parser`` can be set to an lxml parser instance; the default is an
      HTMLParser
    * ``compiler_parser``` can be set to an lxml parser instance; the default
      is a XMLParser
    * ``rules_parser`` can be set to an lxml parser instance; the default is a
      XMLParser.
    * ``xsl_params`` can be set to a dictionary of parameters that will be
      known to the compiled theme transform. The keys should be the parameter
      names. Values are default values.
    """
    if access_control is not None:
        read_network = access_control.options['read_network']
    rules_doc = process_rules(
        rules=rules,
        theme=theme,
        extra=extra,
        css=css,
        xinclude=xinclude,
        absolute_prefix=absolute_prefix,
        update=update,
        trace=trace,
        includemode=includemode,
        parser=parser,
        rules_parser=rules_parser,
        read_network=read_network,
    )

    # Build a document with all the <xsl:param /> values to set the defaults
    # for every value passed in as xsl_params
    known_params = build_xsl_params_document(xsl_params)

    # Create a pseudo resolver for this
    known_params_url = 'file:///__diazo_known_params__'
    emit_stylesheet_resolver = CustomResolver(
        {known_params_url: etree.tostring(known_params)})
    emit_stylesheet_parser = etree.XMLParser()
    emit_stylesheet_parser.resolvers.add(emit_stylesheet_resolver)

    # Set up parameters
    params = {}
    if indent is not None:
        params['indent'] = indent and "'yes'" or "'no'"
    params['known_params_url'] = quote_param(known_params_url)
    params['runtrace'] = '1' if runtrace else '0'

    # Run the final stage compiler
    emit_stylesheet = pkg_xsl('emit-stylesheet.xsl',
                              parser=emit_stylesheet_parser)
    compiled_doc = emit_stylesheet(rules_doc, **params)
    compiled_doc = set_parser(etree.tostring(compiled_doc), parser,
                              compiler_parser)

    return compiled_doc
Example #20
0
    def __call__(self, environ, start_response):
        request = Request(environ)
        if self.should_ignore(request):
            return self.app(environ, start_response)

        if self.remove_conditional_headers:
            request.remove_conditional_headers()
        else:
            # Always remove Range and Accept-Encoding headers
            request.remove_conditional_headers(
                remove_encoding=True,
                remove_range=False,
                remove_match=False,
                remove_modified=True,
            )

        response = request.get_response(self.app)
        if not self.should_transform(response):
            return response(environ, start_response)
        try:
            input_encoding = response.charset

            # Note, the Content-Length header will not be set
            if request.method == 'HEAD':
                self.reset_headers(response)
                return response(environ, start_response)

            # Prepare the serializer
            try:
                serializer = getHTMLSerializer(response.app_iter,
                                               encoding=input_encoding)
            except etree.XMLSyntaxError:
                # Abort transform on syntax error for empty response
                # Headers should be left intact
                return response(environ, start_response)
        finally:
            if hasattr(response.app_iter, 'close'):
                response.app_iter.close()

        self.reset_headers(response)

        # Set up parameters

        params = {}
        for key, value in self.environ_param_map.items():
            if key in environ:
                if value in self.unquoted_params:
                    params[value] = environ[key]
                else:
                    params[value] = quote_param(environ[key])
        for key, value in self.params.items():
            if key in self.unquoted_params:
                params[key] = value
            else:
                params[key] = quote_param(value)

        # Apply the transformation
        tree = self.transform(serializer.tree, **params)

        # Set content type (normally inferred from stylesheet)
        # Unfortunately lxml does not expose docinfo.mediaType
        if self.content_type is None:
            if tree.getroot().tag == 'html':
                response.content_type = 'text/html'
            else:
                response.content_type = 'text/xml'
        response.charset = tree.docinfo.encoding or self.charset

        # Return a repoze.xmliter XMLSerializer, which helps avoid re-parsing
        # the content tree in later middleware stages.
        response.app_iter = XMLSerializer(tree, doctype=self.doctype)

        # Calculate the content length - we still return the parsed tree
        # so that other middleware could avoid having to re-parse, even if
        # we take a hit on serialising here
        if self.update_content_length:
            response.content_length = len(str(response.app_iter))

        return response(environ, start_response)