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, '')
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, '')
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(' ', '\n', re.sub(' \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
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 ]
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
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)
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')
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)
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)
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))
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))
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
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
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
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ö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)))
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
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))
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
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 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')
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
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
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
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 ''
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
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 []
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 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
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
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
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)
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
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
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
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)
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
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()
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
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
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
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)
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)