def cook_layout(layout, ajax):
    """Return main_template compatible layout"""
    # Fix XHTML layouts with CR[+LF] line endings
    layout = re.sub('\r', '\n', re.sub('\r\n', '\n', layout))

    # Parse layout
    if isinstance(layout, unicode):
        result = getHTMLSerializer([layout.encode('utf-8')], encoding='utf-8')
    else:
        result = getHTMLSerializer([layout], encoding='utf-8')

    # Fix XHTML layouts with inline js (etree.tostring breaks all <![CDATA[)
    if '<![CDATA[' in layout:
        result.serializer = html.tostring

    # Wrap all panels with a metal:fill-slot -tag:
    all_slots = []
    for layoutPanelNode in slotsXPath(result.tree):
        data_slots = layoutPanelNode.attrib['data-slots']
        all_slots += wrap_append_prepend_slots(layoutPanelNode, data_slots)
        del layoutPanelNode.attrib['data-slots']

    # When no slots are explicitly defined, try to inject the very default
    # slots
    if len(all_slots) == 0:
        for node in result.tree.xpath('//*[@data-panel="content"]'):
            wrap_append_prepend_slots(
                node, 'content > body header main * content-core')

    # Append implicit slots
    head = result.tree.getroot().find('head')
    if not ajax and head is not None:
        for name in ['top_slot', 'head_slot',
                     'style_slot', 'javascript_head_slot']:
            slot = etree.Element('{%s}%s' % (NSMAP['metal'], name),
                                 nsmap=NSMAP)
            slot.attrib['define-slot'] = name
            head.append(slot)

    template = """\
<metal:page define-macro="master"
            tal:define="portal_state context/@@plone_portal_state;
                        context_state context/@@plone_context_state;
                        plone_view context/@@plone;
                        plone_layout context/@@plone_layout | nothing;
                        lang portal_state/language;
                        view nocall: view | nocall: plone_view;
                        dummy python:plone_view.mark_view(view);
                        portal_url portal_state/portal_url;
                        checkPermission nocall: context/portal_membership/checkPermission;
                        site_properties nocall: context/portal_properties/site_properties;
                        ajax_include_head request/ajax_include_head | nothing;
                        ajax_load request/ajax_load | python: False;
                        toolbar_class python:request.cookies.get('plone-toolbar', 'plone-toolbar-left pat-toolbar');
                        dummy python:request.RESPONSE.setHeader('X-UA-Compatible', 'IE=edge,chrome=1');">
%s
</metal:page>"""
    metal = 'xmlns:metal="http://namespaces.zope.org/metal"'
    return (template % ''.join(result)).replace(metal, '')
Пример #2
0
def cook_layout(layout):
    """Return main_template compatible layout"""
    result = getHTMLSerializer(layout, encoding='utf-8')
    nsmap = {'metal': 'http://namespaces.zope.org/metal'}

    # wrap all panels with a metal:fill-slot -tag
    for layoutPanelNode in panelXPath(result.tree):
        panelId = layoutPanelNode.attrib['data-panel']
        slot = etree.Element('{%s}%s' % (nsmap['metal'], panelId), nsmap=nsmap)
        slot.attrib['define-slot'] = panelId
        slot_parent = layoutPanelNode.getparent()
        slot_parent_index = slot_parent.index(layoutPanelNode)
        slot.append(layoutPanelNode)
        slot_parent.insert(slot_parent_index, slot)

    root = result.tree.getroot()
    root.attrib['tal:define'] = """\
portal_state python:context.restrictedTraverse('@@plone_portal_state');
context_state python:context.restrictedTraverse('@@plone_context_state');
plone_view python:context.restrictedTraverse('@@plone');
lang portal_state/language;
view nocall:view | nocall: plone_view;
dummy python: plone_view.mark_view(view);
portal_url portal_state/portal_url;
checkPermission nocall: context/portal_membership/checkPermission;
site_properties nocall:context/portal_properties/site_properties;
ajax_load request/ajax_load | nothing;
ajax_include_head request/ajax_include_head | nothing;
dummy python:request.RESPONSE.setHeader('X-UA-Compatible', 'IE=edge,chrome=1');
dummy python:options.update({'state': options.get('state', request.get('controller_state'))});
"""

    template = '<metal:page define-macro="master">\n%s\n</metal:page>'
    metal = 'xmlns:metal="http://namespaces.zope.org/metal"'
    return (template % ''.join(result)).replace(metal, '')
Пример #3
0
    def transformIterable(self, result, encoding):
        if self.request.get('plone.app.blocks.disabled', False):
            return None

        content_type = self.request.response.getHeader('Content-Type')
        if content_type is None or not content_type.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in ('zip', 'deflate',
                                                   'compress',):
            return None

        try:
            # Fix layouts with CR[+LF] line endings not to lose their heads
            # (this has been seen with downloaded themes with CR[+LF] endings)
            iterable = [re.sub('&#13;', '\n', re.sub('&#13;\n', '\n', item))
                        for item in result if item]
            result = getHTMLSerializer(
                iterable, pretty_print=self.pretty_print, encoding=encoding)
            # Fix XHTML layouts with where etree.tostring breaks <![CDATA[
            if any(['<![CDATA[' in item for item in iterable]):
                result.serializer = html.tostring
            self.request['plone.app.blocks.enabled'] = True
            return result
        except (AttributeError, TypeError, etree.ParseError):
            return None
Пример #4
0
    def transformIterable(self, result, encoding):

        try:
            parser = SnippetParser()
        except AttributeError:
            return result

        if self.request['PATH_INFO'].endswith('edit'):
            return result

        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        ce = self.request.response.getHeader('Content-Encoding')
        if ce and ce in ('zip', 'deflate', 'compress'):
            return None
        try:
            if result == ['']:
                return None

            result = getHTMLSerializer(result, pretty_print=False)
        except (TypeError):
            return None

        return [ parser.parsePage(r) for r in result ]
Пример #5
0
    def parseTree(self, result, encoding):
        # if it's a redirect, the result shall not be transformed
        request = getRequest()
        if request.response.status in (301, 302):
            return None

        if isinstance(result, XMLSerializer):
            return result

        # hhmmm, this is kind of taken right out of plone.app.theming
        # maybe this logic(parsing dom) should be someone central?
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in ('zip', 'deflate',
                                                   'compress',):
            return None

        try:
            result = getHTMLSerializer(
                result, pretty_print=False, encoding=encoding)
            # We are going to force html output here always as XHTML
            # output does odd character encodings
            result.serializer = html.tostring
            return result
        except (TypeError, etree.ParseError):
            # XXX handle something special?
            LOGGER.warn('error parsing dom, failure to add csrf '
                        'token to response for url %s' % self.request.URL)
            return None
Пример #6
0
    def render(self):
        # Render fields by iterating over the form field tiles, rendering
        # each one, and replacing the tile with the result.
        layout = ILayoutAware(self.context).content
        layoutTree = getHTMLSerializer(layout,
            pretty_print=True, encoding='utf8')
        
        for tileId, tile in self.context.field_tiles(layoutTree.tree):
            # XXX need to include the full Plone view of the field
            widget = self.widgets[tile.id]
            widgetRenderer = getMultiAdapter((widget, self.request), name=u'ploneform-render-widget')
            widgetHtml = widgetRenderer()
            tileTree = html.fromstring(widgetHtml).getroottree()
            tileTarget = xpath1("//*[@id='%s']" % tileId, layoutTree.tree)
            
            if tileTarget is None:
                continue
            
            # clear children, but keep attributes
            oldAttrib = dict(tileTarget.attrib)
            tileTarget.clear()
            tileTarget.attrib.update(oldAttrib)

            # insert tile target with tile body
            tileBody = tileTree.find('body')
            if tileBody is not None:
                tileTarget.text = tileBody.text
                for tileBodyChild in tileBody:
                    tileTarget.append(tileBodyChild)
        
        # TODO:
        # - create form tag
        # - fill in status message
        return str(layoutTree)
Пример #7
0
    def __init__(self, context, request, tile=None):
        self.context = context
        self.request = request
        self.tile = tile

        # Parse layout
        data_layout = (ILayoutAware(self.context).content or DATA_LAYOUT)
        self.storage = getHTMLSerializer([data_layout.encode('utf-8')],
                                         encoding='utf-8')
Пример #8
0
 def testRenderTiles(self):
     serializer = getHTMLSerializer([testLayout1])
     request = self.layer['request']
     tree = serializer.tree
     renderTiles(request, tree)
     result = serializer.serialize()
     self.assertIn('This is a demo tile with id tile2', result)
     self.assertIn('This is a demo tile with id tile3', result)
     self.assertIn('This is a demo tile with id tile4', result)
Пример #9
0
 def testRenderTilesError(self):
     serializer = getHTMLSerializer([testLayout2])
     request = self.layer['request']
     tree = serializer.tree
     renderTiles(request, tree)
     result = serializer.serialize()
     self.assertIn('This is a demo tile with id tile2', result)
     self.assertNotIn('This is a demo tile with id tile3', result)
     self.assertIn('There was an error while rendering this tile', result)
     self.assertIn('This is a demo tile with id tile4', result)
Пример #10
0
 def test_getHTMLSerializer_doctype_xhtml_serializes_to_xhtml(self):
     t = utils.getHTMLSerializer(self.create_iterable(preamble='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n', body='<img src="foo.png" />'), pretty_print=True)
     self.failUnless(isinstance(t, serializer.XMLSerializer))
     
     t2 = utils.getXMLSerializer(t)
     self.failUnless(t2 is t)
     
     self.assertEqual(
         '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml">\n  <head>\n    <meta http-equiv="Content-Type" content="text/html; charset=ASCII" />\n    <title>My homepage</title>\n  </head>\n  <body>Hello, world!<img src="foo.png" /></body>\n</html>\n',
         "".join(t2))
Пример #11
0
 def test_getHTMLSerializer(self):
     t = utils.getHTMLSerializer(self.create_iterable(body='<img src="foo.png" />'), pretty_print=True)
     self.failUnless(isinstance(t, serializer.XMLSerializer))
     
     t2 = utils.getXMLSerializer(t)
     self.failUnless(t2 is t)
     
     self.assertEqual(
         '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">\n<html>\n<head><title>My homepage</title></head>\n<body>Hello, world!<img src="foo.png">\n</body>\n</html>\n',
         "".join(t2))
Пример #12
0
    def _parse(self, result):
        """Create an XMLSerializer from an HTML string, if needed."""
        content_type = self.request.response.getHeader('Content-Type')
        if not content_type or not content_type.startswith('text/html'):
            return

        try:
            return getHTMLSerializer(result)
        except (AttributeError, TypeError, etree.ParseError):
            return
Пример #13
0
    def parseTree(self, result):
        contentType = self.request.response.getHeader("Content-Type")
        if contentType is None or not contentType.startswith("text/html"):
            return None

        contentEncoding = self.request.response.getHeader("Content-Encoding")
        if contentEncoding and contentEncoding in ("zip", "deflate", "compress"):
            return None

        try:
            return getHTMLSerializer(result, pretty_print=False)
        except (TypeError, etree.ParseError):
            return None
Пример #14
0
    def parseTree(self, result):
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in ('zip', 'deflate', 'compress',):
            return None

        try:
            return getHTMLSerializer(result, pretty_print=False)
        except (TypeError, etree.ParseError):
            return None
Пример #15
0
    def test_replace_doctype_blank(self):
        t = utils.getHTMLSerializer(self.create_iterable(preamble='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n', body='<img src="foo.png" />'), pretty_print=True, doctype="")
        self.failUnless(isinstance(t, serializer.XMLSerializer))

        t2 = utils.getXMLSerializer(t)
        self.failUnless(t2 is t)
        
        self.assertEqual(
            b'<html xmlns="http://www.w3.org/1999/xhtml">\n  <head>\n    <meta http-equiv="Content-Type" content="text/html; charset=ASCII" />\n    <title>My homepage</title>\n  </head>\n  <body>Hello, w&#246;rld!<img src="foo.png" /></body>\n</html>\n',
            b"".join(t2))

        self.assertEqual(
            u'<html xmlns="http://www.w3.org/1999/xhtml">\n  <head>\n    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\n    <title>My homepage</title>\n  </head>\n  <body>Hello, wörld!<img src="foo.png" /></body>\n</html>\n',
            u"".join(t2.serialize(encoding=unicode)))
Пример #16
0
def transform(doc, rules, extras={}, throw_errors=False):
    if isinstance(doc, basestring):
        doc = getHTMLSerializer([doc])

    root = doc.tree.getroot()

    for rule in rules:
        try:
            rule(root, extras)
        except:
            if throw_errors:
                raise

    return doc
Пример #17
0
 def test_xsl(self):
     t = utils.getHTMLSerializer(self.create_iterable(body='<br>'))
     transform = lxml.etree.XSLT(lxml.etree.XML(b'''
         <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
             <xsl:output method="xml" indent="no" omit-xml-declaration="yes"
                 media-type="text/html" encoding="UTF-8"
                 doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
                 doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
                 />
             <xsl:template match="@*|node()">
               <xsl:copy>
                 <xsl:apply-templates select="@*|node()"/>
               </xsl:copy>
             </xsl:template>
         </xsl:stylesheet>
         '''))
     t.tree = transform(t.tree)
     self.failUnless('<br />' in str(t))
Пример #18
0
    def transformIterable(self, result, encoding):
        if self.request.get("plone.app.blocks.disabled", False):
            return None

        content_type = self.request.response.getHeader("Content-Type")
        if content_type is None or not content_type.startswith("text/html"):
            return None

        contentEncoding = self.request.response.getHeader("Content-Encoding")
        if contentEncoding and contentEncoding in ("zip", "deflate", "compress"):
            return None

        try:
            result = getHTMLSerializer(result, pretty_print=self.pretty_print, encoding=encoding)
            self.request["plone.app.blocks.enabled"] = True
            return result
        except (TypeError, etree.ParseError):
            return None
Пример #19
0
    def parseTree(self, result):
        # hhmmm, this is kind of taken right out of plone.app.theming
        # maybe this logic(parsing dom) should be someone central?
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in ('zip', 'deflate',
                                                   'compress',):
            return None

        try:
            return getHTMLSerializer(result, pretty_print=False)
        except (TypeError, etree.ParseError):
            # XXX handle something special?
            LOGGER.warn('error parsing dom, failure to add csrf '
                        'token to response for url %s' % self.request.URL)
            return None
Пример #20
0
def protect(raw):
    if isinstance(raw, six.text_type):
        raw = raw.encode('utf-8')
    parser = getHTMLSerializer(
        [raw], pretty_print=False, encoding='utf-8')
    parser.serializer = html.tostring

    root = parser.tree.getroot()
    token = createToken()

    for form in root.cssselect('form'):
        authenticator = form.cssselect('[name="_authenticator"]')
        if len(authenticator) == 0:
            authenticator = etree.Element("input")
            authenticator.attrib['name'] = '_authenticator'
            authenticator.attrib['type'] = 'hidden'
            authenticator.attrib['value'] = token
            form.append(authenticator)
    return parser.serialize().decode('utf-8')
Пример #21
0
    def transformIterable(self, result, encoding):
        if self.request.get('plone.app.blocks.disabled', False):
            return None

        content_type = self.request.response.getHeader('Content-Type')
        if content_type is None or not content_type.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in ('zip', 'deflate',
                                                   'compress',):
            return None

        try:
            result = getHTMLSerializer(result, pretty_print=True,
                                       encoding=encoding)
            self.request['plone.app.blocks.enabled'] = True
            return result
        except (TypeError, etree.ParseError):
            return None
Пример #22
0
    def parseTree(self, result, encoding):
        # if it's a redirect, the result shall not be transformed
        request = self.request

        if request.response.status in (301, 302):
            return None

        if isinstance(result, XMLSerializer):
            return result

        # hhmmm, this is kind of taken right out of plone.app.theming
        # maybe this logic(parsing dom) should be someone central?
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in (
                'zip',
                'deflate',
                'compress',
        ):
            return None

        if isinstance(result, list) and len(result) == 1:
            # do not parse empty strings to omit warning log message
            if not result[0].strip():
                return None
        try:
            result = getHTMLSerializer(result,
                                       pretty_print=False,
                                       encoding=encoding)
            # We are going to force html output here always as XHTML
            # output does odd character encodings
            result.serializer = html.tostring
            return result
        except (AttributeError, TypeError, etree.ParseError):
            # XXX handle something special?
            logger.warn('error parsing dom, failure to add csrf '
                        'token to response for url %s' % self.request.URL)
            return None
Пример #23
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
Пример #24
0
    def render_content_core(self, site=None):
        try:
            layout = getLayout(self.context)
        except TypeError:
            return super(LayoutAwareItem, self).render_content_core()
        req = getRequest()
        filters = [f for _, f in getAdapters((self.context, req), IFilter)]
        layout = apply_filters(filters, layout)
        dom = getHTMLSerializer(layout)

        kwargs = {'baseURL': self.context.absolute_url() + '/layout_view'}
        if site is not None:
            kwargs['site'] = site

        tiles.renderTiles(req, dom.tree, **kwargs)
        gridsystem.merge(req, dom.tree)

        content = contentpanel_xpath(dom.tree)
        if len(content) > 0:
            return tostring(content[0])
        return ''
Пример #25
0
    def parseTree(self, result):
        # hhmmm, this is kind of taken right out of plone.app.theming
        # maybe this logic(parsing dom) should be someone central?
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in (
                'zip',
                'deflate',
                'compress',
        ):
            return None

        try:
            return getHTMLSerializer(result, pretty_print=False)
        except (TypeError, etree.ParseError):
            # XXX handle something special?
            LOGGER.warn('error parsing dom, failure to add csrf '
                        'token to response for url %s' % self.request.URL)
            return None
    def transformIterable(self, result, encoding):
        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        ce = self.request.response.getHeader('Content-Encoding')
        if ce and ce in ('zip', 'deflate', 'compress'):
            return None
        try:
            result = getHTMLSerializer(result, pretty_print=False)
        except (TypeError, etree.ParseError):
            return None

        portlets = _portlet_selector(result.tree)
        if len(portlets) > 0:
            site = getSite()
            ref_cat = getToolByName(site, 'reference_catalog')
            view = site.restrictedTraverse('@@plone')
            for tag in portlets:
                add_portlet(tag, self.request, site, ref_cat, view)

        return result
Пример #27
0
 def item_panels(self):
     default_view = self.default_view
     html = default_view()
     if isinstance(html, unicode):
         html = html.encode('utf-8')
     serializer = getHTMLSerializer([html], pretty_print=False,
                                    encoding='utf-8')
     panels = dict(
         (node.attrib['data-panel'], node)
         for node in utils.panelXPath(serializer.tree)
     )
     if panels:
         request = self.request.clone()
         request.URL = self.content_context.absolute_url() + '/'
         try:
             renderTiles(request, serializer.tree)
         except RuntimeError:  # maximum recursion depth exceeded
             return []
         clear = '<div style="clear: both;"></div>'
         return [''.join([serializer.serializer(child)
                          for child in node.getchildren()])
                 for name, node in panels.items()] + [clear]
     return []
Пример #28
0
    def transformIterable(self, result, encoding):
        if self.request['PATH_INFO'].endswith('edit'):
            return result

        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        ce = self.request.response.getHeader('Content-Encoding')
        if ce and ce in ('zip', 'deflate', 'compress'):
            return None
        try:
            if result == ['']:
                return None

            result = getHTMLSerializer(result, pretty_print=False)
        except (TypeError):
            return None

        root = result.tree.getroot()
        self.transformSnippets(root)

        return result
 def item_panels(self):
     default_view = self.default_view
     html = default_view()
     if isinstance(html, unicode):
         html = html.encode('utf-8')
     serializer = getHTMLSerializer([html], pretty_print=False,
                                    encoding='utf-8')
     panels = dict(
         (node.attrib['data-panel'], node)
         for node in utils.panelXPath(serializer.tree)
     )
     if panels:
         request = self.request.clone()
         request.URL = self.content_context.absolute_url() + '/'
         try:
             renderTiles(request, serializer.tree)
         except RuntimeError:  # maximum recursion depth exceeded
             return []
         clear = '<div style="clear: both;"></div>'
         return [''.join([serializer.serializer(child)
                          for child in node.getchildren()])
                 for name, node in panels.items()] + [clear]
     return []
Пример #30
0
    def transformIterable(self, result, encoding):
        if not isinstance(result, XMLSerializer):
            getHeader = self.request.response.getHeader

            content_type = getHeader('Content-Type')
            if content_type is None or \
                    not content_type.startswith('text/html'):
                return None

            contentEncoding = getHeader('Content-Encoding')
            if contentEncoding and \
                    contentEncoding in ('zip', 'deflate', 'compress',):
                return None

            try:
                result = getHTMLSerializer(result, encoding=encoding,
                        pretty_print=self.pretty_print)
            except (TypeError, etree.ParseError):
                return None

        resources = fanstatic.init_needed(
                base_url=self.request.getURL(),
                publisher_signature='/++fanstatic++/',
                )

        from plone.fanstatic import groups
        for group in groups:
            group.need()

        resources = etree.HTML(resources.render()).find('head').getchildren()
        head = resources and result.tree.getroot().find('head') or None
        # Ajax are without head...
        if head:
            for item in resources:
                head.append(item)

        return result
Пример #31
0
    def transformIterable(self, result, encoding):
        if self.request.get('plone.app.blocks.disabled', False):
            return None

        content_type = self.request.response.getHeader('Content-Type')
        if content_type is None or not content_type.startswith('text/html'):
            return None

        contentEncoding = self.request.response.getHeader('Content-Encoding')
        if contentEncoding and contentEncoding in (
                'zip',
                'deflate',
                'compress',
        ):
            return None

        try:
            result = getHTMLSerializer(result,
                                       pretty_print=self.pretty_print,
                                       encoding=encoding)
            self.request['plone.app.blocks.enabled'] = True
            return result
        except (TypeError, etree.ParseError):
            return None
Пример #32
0
    def render(self):
        # Render fields by iterating over the form field tiles, rendering
        # each one, and replacing the tile with the result.
        layout = ILayoutAware(self.context).content
        layoutTree = getHTMLSerializer(layout,
                                       pretty_print=True,
                                       encoding='utf8')

        for tileId, tile in self.context.field_tiles(layoutTree.tree):
            # XXX need to include the full Plone view of the field
            widget = self.widgets[tile.id]
            widgetRenderer = getMultiAdapter((widget, self.request),
                                             name=u'ploneform-render-widget')
            widgetHtml = widgetRenderer()
            tileTree = html.fromstring(widgetHtml).getroottree()
            tileTarget = xpath1("//*[@id='%s']" % tileId, layoutTree.tree)

            if tileTarget is None:
                continue

            # clear children, but keep attributes
            oldAttrib = dict(tileTarget.attrib)
            tileTarget.clear()
            tileTarget.attrib.update(oldAttrib)

            # insert tile target with tile body
            tileBody = tileTree.find('body')
            if tileBody is not None:
                tileTarget.text = tileBody.text
                for tileBodyChild in tileBody:
                    tileTarget.append(tileBodyChild)

        # TODO:
        # - create form tag
        # - fill in status message
        return str(layoutTree)
Пример #33
0
    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
Пример #34
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)
Пример #35
0
    def transformIterable(self, result, encoding):
        if self.request['PATH_INFO'].endswith('edit'):
            return result

        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        ce = self.request.response.getHeader('Content-Encoding')
        if ce and ce in ('zip', 'deflate', 'compress'):
            return None
        try:
            if result == ['']:
                return None

            result = getHTMLSerializer(result, pretty_print=False)
        except (TypeError):
            return None

        site = api.portal.get()
        site_path = '/'.join(site.getPhysicalPath())
        root = result.tree.getroot()
        rendered = {}

        registry = getUtility(IRegistry)

        evaluator = ExpressionEvaluator()
        expression = Expression(registry.get('uwosh.snippets.render_expression',
                                             'context/text/output|context/getText|nothing'))

        for el in root.cssselect('[data-type="snippet_tag"]'):
            snippet_name = el.attrib.get('data-snippet-id')
            header = el.attrib.get('data-header')
            if snippet_name not in rendered:
                ob = uuidToObject(snippet_name)
                if ob is None:
                    ob = site.restrictedTraverse('.snippets/' + snippet_name, None)
                if ob is not None:
                    rendered[snippet_name] = {
                        'html': evaluator.evaluate(expression, ob),
                        'ob': ob
                    }

            if snippet_name in rendered:
                data = rendered[snippet_name]
                ob = data['ob']
                val = data['html']
                if header:
                    val = get_header_from_text(val, header)

                snippet_container = etree.Element('div')

                className = 'snippet-container snippet-container-{}'.format(
                    ob.portal_type.lower().replace(' ', '-')
                )

                if not val:
                    val = '<p>Snippet could not be found</p>'
                    className += ' snippet-container-missing'

                snippet_container.attrib.update({
                    'class': className,
                    'data-source-uid': IUUID(ob),
                    'data-source-id': ob.getId(),
                    'data-source-title': ob.Title(),
                    'data-source-path': '/'.join(ob.getPhysicalPath())[len(site_path):],
                    'data-source-header': header or ''
                })

                content_el = fromstring(val)
                if content_el.tag == 'div':
                    # unwrap: fromstring auto adds div around content so we'll just take the
                    # inside of it instead
                    for inside_el in content_el:
                        snippet_container.append(inside_el)
                else:
                    snippet_container.append(content_el)

                if val:
                    parent = el.getparent()
                    idx = parent.index(el)
                    parent[idx] = snippet_container

        return result
Пример #36
0
    def get_field_data(self):
        from plone.dexterity.interfaces import IDexterityFTI
        from plone.behavior.interfaces import IBehaviorAssignable

        data = {}

        portal_type = self.obj.portal_type
        schema = getUtility(IDexterityFTI, name=portal_type).lookupSchema()
        for name, field in getFieldsInOrder(schema):
            if self.obj.portal_type == 'File' and name == 'file':
                fileobj = getattr(self.obj, name, None)
                if fileobj is not None:
                    data[name] = base64.b64encode(fileobj.data)
            else:
                data[name] = getattr(self.obj, name, None)

        behavior_assignable = IBehaviorAssignable(self.obj)
        for behavior in behavior_assignable.enumerateBehaviors():
            binst = behavior.interface(self.obj)
            bdata = {}
            for name, field in getFieldsInOrder(behavior.interface):
                if isinstance(field, RichText):
                    textfield = getattr(binst, name, None)
                    if textfield is not None:
                        bdata[name] = textfield.raw
                else:
                    bdata[name] = getattr(binst, name, None)
            data[behavior.interface.__identifier__] = bdata

        if ILayoutAware.providedBy(self.obj):
            from plone.tiles.data import ANNOTATIONS_KEY_PREFIX
            from plone.app.blocks.utils import getLayout
            from repoze.xmliter.utils import getHTMLSerializer
            from plone.app.blocks import tiles
            from plone.app.blocks import gridsystem
            from lxml.html import tostring
            tdata = {}
            annotations = IAnnotations(self.obj, {})
            for key in annotations.keys():
                if key.startswith(ANNOTATIONS_KEY_PREFIX):
                    adata = annotations[key]
                    tdata[key] = adata
            data['tile_data'] = tdata

            req = site.REQUEST
            layout = getLayout(self.obj)
            dom = getHTMLSerializer(layout)

            try:
                tiles.renderTiles(req,
                                  dom.tree,
                                  site=site,
                                  baseURL=self.obj.absolute_url() +
                                  '/layout_view')
            except TypeError:
                try:
                    tiles.renderTiles(req,
                                      dom.tree,
                                      baseURL=self.obj.absolute_url() +
                                      '/layout_view')
                except Exception:
                    tiles.renderTiles(req, dom.tree)
            gridsystem.merge(req, dom.tree)

            data['rendered_layout'] = tostring(dom.tree)

        return data
Пример #37
0
    def tile_render_tween(request):
        response = handler(request)
        if response.content_type == 'text/html':
            if isinstance(response, WSGIHTTPException):
                # the body of a WSGIHTTPException needs to be "prepared"
                response.prepare(request.environ)

            serializer = getHTMLSerializer(response.app_iter)
            tree = serializer.tree
            head_node = tree.getroot().find('head')

            for tile_node in TILE_XPATH(serializer.tree):
                # determine tile path
                tile_path = tile_node.attrib.get('path')
                tile_type = tile_node.attrib.get('type')
                if tile_path and tile_type:
                    if tile_path == '/':
                        path = '/tile:' + tile_type
                    else:
                        path = '/'.join((tile_path, 'tile:' + tile_type))
                elif tile_path:
                    path = tile_path
                elif tile_type:
                    path = request.resource_path(request.context, 'tile:' + tile_type)
                else:
                    # XXX how can we show a useful line number?
                    raise Exception('Tile must have a path or type')

                # fetch tile contents
                subrequest = Request.blank(path)
                subrequest.registry = registry
                tile_data = dict(tile_node.attrib)
                tile_data['innerHTML'] = (tile_node.text or '') + ''.join([html.tostring(child) for child in tile_node.iterchildren()])
                if tile_path:
                    edit_url = request.route_path(MANAGE_ROUTE_NAME, 'edit_tile', traverse=tile_path)
                else:
                    edit_url = request.mgmt_path(request.context, 'edit_tile')
                edit_url += '?' + urlencode(tile_data)
                del tile_data['type']
                subrequest.tile_data = tile_data
                tile_response = handler(subrequest)
                tile_tree = getHTMLSerializer(tile_response.app_iter).tree
                tile_root = tile_tree.getroot()
                tile_body = tile_root.find('body')

                # add edit link
                if has_permission('Edit tile', subrequest.context, request):
                    edit_link = builder.E.a('', href=edit_url)
                    edit_link.append(etree.Entity('#9997'))
                    tile_body.append(edit_link)

                # insert tile content
                tile_head = tile_root.find('head')
                if tile_head is not None:
                    for child in tile_head:
                        head_node.append(child)
                if tile_tree is not None:
                    replace_content_with_children(tile_node, tile_body)

            response.app_iter = [serializer.serialize()]

        return response
Пример #38
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 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)
Пример #39
0
    def __call__(self, request, result, context=None):
        if '++plone++' in request.ACTUAL_URL:
            return
        portal = api.portal.get()
        original_context = context
        if context is None or IResourceDirectory.providedBy(context):
            original_context = context = portal

        try:
            context_url = context.absolute_url()
        except AttributeError:
            # could be a form/browser class
            try:
                context = context.context
            except:
                context = aq_parent(context)
            context_url = context.absolute_url()

        if (not ISiteRoot.providedBy(context)
                and not IDexterityContent.providedBy(context)):
            context = aq_parent(context)
        portal_url = portal.absolute_url()

        raw = False
        if isinstance(result, basestring):
            raw = True
        else:
            self.rewrite(result, context.absolute_url() + '/')
            result = result.tree

        theme_base_url = '%s/++%s++%s/index.html' % (
            portal_url, THEME_RESOURCE_NAME, self.name)

        content = self.get_fill_content(result, raw)
        utils = getMultiAdapter((context, request), name='castle-utils')

        layout = self.get_layout(context, request=request)
        layout = layout(portal_url=portal_url,
                        site_url=portal_url,
                        context_url=context_url,
                        request=request,
                        context=context,
                        portal=portal,
                        site=portal,
                        theme_base_url=theme_base_url,
                        content=content,
                        anonymous=api.user.is_anonymous(),
                        debug=api.env.debug_mode(),
                        utils=utils)

        dom = getHTMLSerializer([layout])
        self.rewrite(dom, theme_base_url)
        if not raw:
            # old style things...
            self.bbb(dom.tree, result)

        dom.tree = tiles.renderTiles(request, dom.tree)

        self.add_body_classes(original_context, context, request, dom.tree,
                              result, raw)

        self.add_included_resources(dom.tree, portal, request)
        self.dynamic_grid(dom.tree)
        # #
        # dom.tree = tiles.renderTiles(request, dom.tree)

        return dom
Пример #40
0
def find_broken(site):
    setup_site(site)
    catalog = site.portal_catalog

    broken = []
    good_urls = []
    checked_urls = []

    req = getRequest()
    for brain in catalog(object_provides=ILayoutAware.__identifier__):
        ob = brain.getObject()
        clear_object_cache(ob)
        layout = getLayout(ob)
        dom = getHTMLSerializer(layout)
        tiles.renderTiles(req, dom.tree, ob.absolute_url() + '/layout_view')
        root = dom.tree.getroot()
        for anchor in root.cssselect('a'):
            if not anchor.attrib.get('href'):
                continue
            url = anchor.attrib['href']
            if (url[0] == '#' or url.startswith('data:')
                    or url.startswith('mailto:')):
                continue
            if url in good_urls:
                continue
            if url in checked_urls:
                print('skipping already checked {}'.format(url))
                continue
            checked_urls.append(url)
            if find_url(ob, url):
                good_urls.append(url)
            else:
                try:
                    text = unidecode(anchor.text_content())
                except Exception:
                    text = ''
                result = '{} linking to broken -> {}({})'.format(
                    brain.getPath(), url, text)
                broken.append(result)
                print(result)

        for img in root.cssselect('img'):
            if not img.attrib.get('src'):
                continue
            url = img.attrib['src']
            if url[0] == '#' or url.startswith('data:'):
                continue
            if url in checked_urls:
                print('skipping already checked {}'.format(url))
                continue
            checked_urls.append(url)
            if find_url(ob, url):
                good_urls.append(url)
            else:
                result = '{} linking to broken image -> {}'.format(
                    brain.getPath(), url)
                broken.append(result)
                print(result)

    now = datetime.datetime.now()
    filename = 'broken-links-{}.txt'.format(now.isoformat())
    fi = open(filename, 'w')
    fi.write('\n'.join(broken))
    fi.close()
Пример #41
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
Пример #42
0
    def tile_render_tween(request):
        response = handler(request)
        if response.content_type == 'text/html':
            if isinstance(response, WSGIHTTPException):
                # the body of a WSGIHTTPException needs to be "prepared"
                response.prepare(request.environ)

            serializer = getHTMLSerializer(response.app_iter)
            tree = serializer.tree
            head_node = tree.getroot().find('head')

            for tile_node in TILE_XPATH(serializer.tree):
                # determine tile path
                tile_path = tile_node.attrib.get('path')
                tile_type = tile_node.attrib.get('type')
                if tile_path and tile_type:
                    if tile_path == '/':
                        path = '/tile:' + tile_type
                    else:
                        path = '/'.join((tile_path, 'tile:' + tile_type))
                elif tile_path:
                    path = tile_path
                elif tile_type:
                    path = request.resource_path(request.context,
                                                 'tile:' + tile_type)
                else:
                    # XXX how can we show a useful line number?
                    raise Exception('Tile must have a path or type')

                # fetch tile contents
                subrequest = Request.blank(path)
                subrequest.registry = registry
                tile_data = dict(tile_node.attrib)
                tile_data['innerHTML'] = (tile_node.text or '') + ''.join([
                    html.tostring(child) for child in tile_node.iterchildren()
                ])
                if tile_path:
                    edit_url = request.route_path(MANAGE_ROUTE_NAME,
                                                  'edit_tile',
                                                  traverse=tile_path)
                else:
                    edit_url = request.mgmt_path(request.context, 'edit_tile')
                edit_url += '?' + urlencode(tile_data)
                del tile_data['type']
                subrequest.tile_data = tile_data
                tile_response = handler(subrequest)
                tile_tree = getHTMLSerializer(tile_response.app_iter).tree
                tile_root = tile_tree.getroot()
                tile_body = tile_root.find('body')

                # add edit link
                if has_permission('Edit tile', subrequest.context, request):
                    edit_link = builder.E.a('', href=edit_url)
                    edit_link.append(etree.Entity('#9997'))
                    tile_body.append(edit_link)

                # insert tile content
                tile_head = tile_root.find('head')
                if tile_head is not None:
                    for child in tile_head:
                        head_node.append(child)
                if tile_tree is not None:
                    replace_content_with_children(tile_node, tile_body)

            response.app_iter = [serializer.serialize()]

        return response
Пример #43
0
    def transformIterable(self, result, encoding):
        if self.request['PATH_INFO'].endswith('/edit'):
            return result

        contentType = self.request.response.getHeader('Content-Type')
        if contentType is None or not contentType.startswith('text/html'):
            return None

        ce = self.request.response.getHeader('Content-Encoding')
        if ce and ce in ('zip', 'deflate', 'compress'):
            return None
        try:
            if result == ['']:
                return None

            result = getHTMLSerializer(result, pretty_print=False)
        except (TypeError):
            return None

        site = api.portal.get()
        site_path = '/'.join(site.getPhysicalPath())
        root = result.tree.getroot()
        rendered = {}

        registry = getUtility(IRegistry)

        evaluator = ExpressionEvaluator()
        expression = Expression(
            registry.get('uwosh.snippets.render_expression',
                         'context/text/output|context/getText|nothing'))

        for el in root.cssselect('[data-type="snippet_tag"]'):
            snippet_name = el.attrib.get('data-snippet-id')
            header = el.attrib.get('data-header')
            if snippet_name not in rendered:
                ob = uuidToObject(snippet_name)
                if ob is None:
                    ob = site.restrictedTraverse('.snippets/' + snippet_name,
                                                 None)
                if ob is not None:
                    rendered[snippet_name] = {
                        'html': evaluator.evaluate(expression, ob),
                        'ob': ob
                    }

            if snippet_name in rendered:
                data = rendered[snippet_name]
                ob = data['ob']
                val = data['html']
                if header:
                    val = get_header_from_text(val, header)

                if el.attrib.get('data-iframe') == 'true':
                    self.display_in_iframe(el, ob)
                    continue

                snippet_container = etree.Element('div')

                className = 'snippet-container snippet-container-{}'.format(
                    ob.portal_type.lower().replace(' ', '-'))

                if not val:
                    val = '<p>Snippet could not be found</p>'
                    className += ' snippet-container-missing'

                snippet_container.attrib.update({
                    'class':
                    className,
                    'data-source-uid':
                    IUUID(ob),
                    'data-source-id':
                    ob.getId(),
                    'data-source-title':
                    ob.Title(),
                    'data-source-path':
                    '/'.join(ob.getPhysicalPath())[len(site_path):],
                    'data-source-header':
                    header or ''
                })

                content_el = fromstring(val)
                if content_el.tag == 'div':
                    # unwrap: fromstring auto adds div around content so we'll just take the
                    # inside of it instead
                    for inside_el in content_el:
                        snippet_container.append(inside_el)
                else:
                    snippet_container.append(content_el)

                if val:
                    parent = el.getparent()
                    idx = parent.index(el)
                    parent[idx] = snippet_container

        return result
Пример #44
0
    def __call__(self):
        self.request.response.setHeader('X-Theme-Applied', 'true')
        layout = self.request.get('layout')
        transform = theming.getTransform(self.context, self.request)

        layout = transform.get_layout(self.context,
                                      layout,
                                      request=self.request)
        portal = api.portal.get()
        portal_url = portal.absolute_url()
        context_url = self.context.absolute_url()

        theme_base_url = '%s/++%s++%s/index.html' % (
            portal_url, THEME_RESOURCE_NAME, transform.name)

        content = {'main': '<p>Content from page</p>', 'left': '', 'right': ''}

        utils = getMultiAdapter((self.context, self.request),
                                name='castle-utils')

        layout = layout(portal_url=portal_url,
                        site_url=portal_url,
                        context_url=context_url,
                        request=self.request,
                        context=self.context,
                        portal=portal,
                        site=portal,
                        theme_base_url=theme_base_url,
                        content=content,
                        anonymous=api.user.is_anonymous(),
                        debug=api.env.debug_mode(),
                        utils=utils,
                        raw=True)

        dom = getHTMLSerializer([layout])
        transform.rewrite(dom, theme_base_url)

        root = dom.tree.getroot()
        for tile in root.cssselect('[data-tile*="@@castle.cms.meta/"]'):
            slot_id = tile.attrib['data-tile'].split('/@@castle.cms.meta/')[-1]

            title = None
            if '?' in slot_id:
                slot_id, _, qs = slot_id.partition('?')
                query = dict(urllib.parse_qsl(qs))
                title = query.get('title')
            else:
                title = slot_id.replace('meta-',
                                        '').capitalize().replace('-', ' ')
            tile.attrib['data-tile-id'] = slot_id
            tile.attrib['data-tile-title'] = title
            tile.attrib['class'] = 'castle-tile-container'
            tile.text = 'Edit %s slot' % title

        overlay = etree.Element('div')
        overlay.attrib['class'] = 'expose-overlay'
        root.cssselect('body')[0].append(overlay)

        javascript = etree.Element('script')
        javascript.text = self.javascript
        root.cssselect('body')[0].append(javascript)

        style = etree.Element('style')
        style.text = self.styles
        root.cssselect('body')[0].append(style)

        transform.dynamic_grid(dom.tree)
        return tostring(dom.tree)
Пример #45
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)