Exemple #1
0
 def parse_xml(cls, el, source_location):
     """
     Creates a Rule object from a parsed XML <rule> element.
     """
     assert el.tag == 'rule'
     classes = el.get('class', '').split()
     if not classes:
         classes = ['default']
     theme = None
     actions = []
     suppress_standard = asbool(el.get('suppress-standard'))
     for child in el.iterchildren():
         if child.tag == 'theme':
             ## FIXME: error if more than one theme
             theme = Theme.parse_xml(child, source_location)
             continue
         if child.tag is etree.Comment:
             continue
         action = parse_action(child, source_location)
         actions.append(action)
     match = None
     inst = cls(classes, actions, theme, match, suppress_standard, source_location)
     for attr in RuleMatch.match_attrs:
         if el.get(attr):
             inst.match = RuleMatch.parse_xml(inst, el, source_location)
             ## FIXME: would last="1" be a good alternative to suppress-standard?
             break
     return inst
Exemple #2
0
 def parse_xml(cls, doc, source_location):
     """
     Parses the given XML/etree document into an instance of this
     class.
     """
     assert doc.tag == 'ruleset'
     matchers = []
     clientsides = []
     rules = []
     default_theme = None
     for el in doc.iterchildren():
         if el.tag == 'match':
             matcher = Match.parse_xml(el, source_location)
             matchers.append(matcher)
         elif el.tag == 'clientside':
             matcher = ClientsideMatch.parse_xml(el, source_location)
             clientsides.append(matcher)
         elif el.tag == 'rule':
             rule = Rule.parse_xml(el, source_location)
             rules.append(rule)
         elif el.tag == 'theme':
             ## FIXME: Add parse error
             default_theme = Theme.parse_xml(el, source_location)
         elif el.tag in ('proxy', 'server-settings', Comment):
             # Handled elsewhere, so we just ignore this element
             continue
         else:
             ## FIXME: source location?
             raise DeliveranceSyntaxError(
                 "Invalid tag %s (unknown tag name %r)" %
                 (tostring(el).split('>', 1)[0] + '>', el.tag),
                 element=el)
     rules_by_class = {}
     for rule in rules:
         for class_name in rule.classes:
             rules_by_class.setdefault(class_name, []).append(rule)
     return cls(matchers,
                clientsides,
                rules_by_class,
                default_theme=default_theme,
                source_location=source_location)
Exemple #3
0
 def parse_xml(cls, doc, source_location):
     """
     Parses the given XML/etree document into an instance of this
     class.
     """
     assert doc.tag == 'ruleset'
     matchers = []
     clientsides = []
     rules = []
     default_theme = None
     for el in doc.iterchildren():
         if el.tag == 'match':
             matcher = Match.parse_xml(el, source_location)
             matchers.append(matcher)
         elif el.tag == 'clientside':
             matcher = ClientsideMatch.parse_xml(el, source_location)
             clientsides.append(matcher)
         elif el.tag == 'rule':
             rule = Rule.parse_xml(el, source_location)
             rules.append(rule)
         elif el.tag == 'theme':
             ## FIXME: Add parse error
             default_theme = Theme.parse_xml(el, source_location)
         elif el.tag in ('proxy', 'server-settings', Comment):
             # Handled elsewhere, so we just ignore this element
             continue
         else:
             ## FIXME: source location?
             raise DeliveranceSyntaxError(
                 "Invalid tag %s (unknown tag name %r)" 
                 % (tostring(el).split('>', 1)[0]+'>', el.tag),
                 element=el)
     rules_by_class = {}
     for rule in rules:
         for class_name in rule.classes:
             rules_by_class.setdefault(class_name, []).append(rule)
     return cls(matchers, clientsides, rules_by_class, default_theme=default_theme,
                source_location=source_location)
Exemple #4
0
    def apply_rules(self, req, resp, resource_fetcher, log, default_theme=None):
        """
        Apply the whatever the appropriate rules are to the request/response.
        """
        extra_headers = parse_meta_headers(resp.body)
        if extra_headers:
            response_headers = HeaderDict(resp.headerlist + extra_headers)
        else:
            response_headers = resp.headers
        try:
            classes = run_matches(self.matchers, req, resp, response_headers, log)
        except AbortTheme:
            return resp
        if 'X-Deliverance-Page-Class' in response_headers:
            log.debug(self, "Found page class %s in headers", response_headers['X-Deliverance-Page-Class'].strip())
            classes.extend(response_headers['X-Deliverance-Page-Class'].strip().split())
        if 'deliverance.page_classes' in req.environ:
            log.debug(self, "Found page class in WSGI environ: %s", ' '.join(req.environ["deliverance.page_classes"]))
            classes.extend(req.environ['deliverance.page_classes'])
        if not classes:
            classes = ['default']
        rules = []
        theme = None
        for class_name in classes:
            ## FIXME: handle case of unknown classes
            ## Or do that during compilation?
            for rule in self.rules_by_class.get(class_name, []):
                if rule not in rules:
                    rules.append(rule)
                    if rule.theme:
                        theme = rule.theme
        if theme is None:
            theme = self.default_theme

        if theme is None and default_theme is not None:
            theme = Theme(href=default_theme, 
                          source_location=self.source_location)
            
        if theme is None:
            log.error(self, "No theme has been defined for the request")
            return resp

        try:
            theme_href = theme.resolve_href(req, resp, log)
            original_theme_resp = self.get_theme_response(
                theme_href, resource_fetcher, log)
            theme_doc = self.get_theme_doc(
                original_theme_resp, theme_href,
                should_escape_cdata=True,
                should_fix_meta_charset_position=True)

            resp = force_charset(resp)
            body = resp.unicode_body
            body = escape_cdata(body)
            body = fix_meta_charset_position(body)
            content_doc = self.parse_document(body, req.url)

            run_standard = True
            for rule in rules:
                if rule.match is not None:
                    matches = rule.match(req, resp, response_headers, log)
                    if not matches:
                        log.debug(rule, "Skipping <rule>")
                        continue
                rule.apply(content_doc, theme_doc, resource_fetcher, log)
                if rule.suppress_standard:
                    run_standard = False
            if run_standard:
                ## FIXME: should it be possible to put the standard rule in the ruleset?
                standard_rule.apply(content_doc, theme_doc, resource_fetcher, log)
        except AbortTheme:
            return resp
        remove_content_attribs(theme_doc)
        ## FIXME: handle caching?

        if original_theme_resp.body.strip().startswith("<!DOCTYPE"):
            tree = theme_doc.getroottree()
        else:
            tree = content_doc.getroottree()

        if "XHTML" in tree.docinfo.doctype:
            method = "xml"
        else:
            method = "html"

        theme_str = tostring(theme_doc, include_meta_content_type=True)
        theme_str = tree.docinfo.doctype + theme_str
        theme_doc = document_fromstring(theme_str)
        tree = theme_doc.getroottree()

        resp.body = tostring(tree, method=method, include_meta_content_type=True)
        resp.body = unescape_cdata(resp.body)

        return resp
Exemple #5
0
    def apply_rules(self, req, resp, resource_fetcher, log, default_theme=None):
        """
        Apply the whatever the appropriate rules are to the request/response.
        """
        extra_headers = parse_meta_headers(resp.body)
        if extra_headers:
            response_headers = ResponseHeaders(resp.headerlist + extra_headers)
        else:
            response_headers = resp.headers
        try:
            classes = run_matches(self.matchers, req, resp, response_headers, log)
        except AbortTheme:
            return resp
        if 'X-Deliverance-Page-Class' in response_headers:
            log.debug(self, "Found page class %s in headers", response_headers['X-Deliverance-Page-Class'].strip())
            classes.extend(response_headers['X-Deliverance-Page-Class'].strip().split())
        if 'deliverance.page_classes' in req.environ:
            log.debug(self, "Found page class in WSGI environ: %s", ' '.join(req.environ["deliverance.page_classes"]))
            classes.extend(req.environ['deliverance.page_classes'])
        if not classes:
            classes = ['default']
        rules = []
        theme = None
        for class_name in classes:
            ## FIXME: handle case of unknown classes
            ## Or do that during compilation?
            for rule in self.rules_by_class.get(class_name, []):
                if rule not in rules:
                    rules.append(rule)
                    if rule.theme:
                        theme = rule.theme
        if theme is None:
            theme = self.default_theme

        if theme is None and default_theme is not None:
            theme = Theme(href=default_theme, 
                          source_location=self.source_location)
            
        if theme is None:
            log.error(self, "No theme has been defined for the request")
            return resp

        try:
            theme_href = theme.resolve_href(req, resp, log)
            original_theme_resp = self.get_theme_response(
                theme_href, resource_fetcher, log)
            theme_doc = self.get_theme_doc(
                original_theme_resp, theme_href,
                should_escape_cdata=True,
                should_fix_meta_charset_position=True)

            resp = force_charset(resp)
            body = resp.unicode_body
            body = escape_cdata(body)
            body = fix_meta_charset_position(body)
            content_doc = self.parse_document(body, req.url)

            run_standard = True
            for rule in rules:
                if rule.match is not None:
                    matches = rule.match(req, resp, response_headers, log)
                    if not matches:
                        log.debug(rule, "Skipping <rule>")
                        continue
                rule.apply(content_doc, theme_doc, resource_fetcher, log)
                if rule.suppress_standard:
                    run_standard = False
            if run_standard:
                ## FIXME: should it be possible to put the standard rule in the ruleset?
                standard_rule.apply(content_doc, theme_doc, resource_fetcher, log)
        except AbortTheme:
            return resp
        remove_content_attribs(theme_doc)
        ## FIXME: handle caching?

        if original_theme_resp.body.strip().startswith("<!DOCTYPE"):
            tree = theme_doc.getroottree()
        else:
            tree = content_doc.getroottree()

        if "XHTML" in tree.docinfo.doctype:
            method = "xml"
        else:
            method = "html"

        theme_str = tostring(theme_doc, include_meta_content_type=True)
        theme_str = tree.docinfo.doctype + theme_str
        theme_doc = document_fromstring(theme_str)
        tree = theme_doc.getroottree()

        resp.body = tostring(tree, method=method, include_meta_content_type=True)
        resp.body = unescape_cdata(resp.body)

        return resp