def renderTiles(request, tree): """Find all tiles in the given response, contained in the lxml element tree `tree`, and insert them into the ouput. Assumes panel merging has already happened. """ # Optionally enable ESI rendering in tiles that support this if not request.getHeader(ESI_HEADER): registry = queryUtility(IRegistry) if registry is not None: if registry.forInterface(IBlocksSettings).esi: request.environ[ESI_HEADER_KEY] = 'true' root = tree.getroot() headNode = root.find('head') baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' for tileNode in utils.headTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except NotFound: continue if tileTree is not None: tileRoot = tileTree.getroot() utils.replace_content(tileNode, tileRoot.find('head')) for tileNode in utils.bodyTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except NotFound: continue if tileTree is not None: tileRoot = tileTree.getroot() tileHead = tileRoot.find('head') if tileHead is not None: for tileHeadChild in tileHead: headNode.append(tileHeadChild) utils.replace_with_children(tileNode, tileRoot.find('body')) return tree
def renderTiles(request, tree): """Find all tiles in the given response, contained in the lxml element tree `tree`, and insert them into the ouput. Assumes panel merging has already happened. """ # Optionally enable ESI rendering in tiles that support this if not request.getHeader(ESI_HEADER): registry = queryUtility(IRegistry) if registry is not None: if registry.forInterface(IBlocksSettings).esi: request.environ[ESI_HEADER_KEY] = 'true' root = tree.getroot() headNode = root.find('head') baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' for tileNode in utils.headTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except NotFound: continue if tileTree is not None: tileRoot = tileTree.getroot() utils.replace_content(tileNode, tileRoot.find('head')) for tileNode in utils.bodyTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except NotFound: continue if tileTree is not None: tileRoot = tileTree.getroot() tileHead = tileRoot.find('head') if tileHead is not None: for tileHeadChild in tileHead: headNode.append(tileHeadChild) utils.replace_content(tileNode, tileRoot.find('body')) return tree
def merge(request, pageTree, removePanelLinks=False, removeLayoutLink=True): """Perform panel merging for the given page. Returns None if the page has no layout. """ # Find layout node layoutHref = utils.xpath1(utils.layoutXPath, pageTree) if layoutHref is None: return None # Resolve layout tree baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' layoutHref = parse.urljoin(baseURL, layoutHref) # noqa: turn the link absolute # Pass special ajax_load parameter forward to allow layout indirection # views to select, for example, default AJAX layout instead of full layout. if request.form.get('ajax_load'): parts = list(parse.urlparse(layoutHref)) query = parse.parse_qs(parts[4]) query['ajax_load'] = request.form.get('ajax_load') parts[4] = parse.urlencode(query) layoutHref = parse.urlunparse(parts) layoutTree = utils.resolve(layoutHref) if layoutTree is None: return None # Map page panels onto the layout pagePanels = dict( (node.attrib['data-panel'], node) for node in utils.panelXPath(pageTree) ) layoutPanels = dict( (node.attrib['data-panel'], node) for node in utils.panelXPath(layoutTree) ) # Site layout should always have element with data-panel="content" # Note: This could be more generic, but that would empower editors too much if 'content' in pagePanels and 'content' not in layoutPanels: for node in layoutTree.xpath('//*[@id="content"]'): node.attrib['data-panel'] = 'content' layoutPanels['content'] = node break for panelId, layoutPanelNode in layoutPanels.items(): pagePanelNode = pagePanels.get(panelId, None) if pagePanelNode is not None: utils.replace_content(layoutPanelNode, pagePanelNode) if removePanelLinks: del layoutPanelNode.attrib['data-panel'] if removeLayoutLink: del pageTree.getroot().attrib[utils.layoutAttrib] return layoutTree
def test_resolve_utf8_unicode(self): content_layout = u""" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html data-layout="./@@default-site-layout"> <body> <h1>Ä</h1> </body> </html>""" text = resolve('', content_layout).xpath('//h1')[0].text self.assertEqual(u'Ä', text)
def merge(request, pageTree, removePanelLinks=False, removeLayoutLink=True): """Perform panel merging for the given page. Returns None if the page has no layout. """ # Find layout node layoutHref = utils.xpath1(utils.layoutXPath, pageTree) if layoutHref is None: return None # Resolve layout tree baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' layoutHref = urljoin(baseURL, layoutHref) # turn the link absolute if request.form.get('ajax_load'): parts = list(urlparse(layoutHref)) query = parse_qs(parts[4]) query['ajax_load'] = request.form.get('ajax_load') parts[4] = urlencode(query) layoutHref = urlunparse(parts) layoutTree = utils.resolve(layoutHref) if layoutTree is None: return None # Map page panels onto the layout pagePanels = dict((node.attrib['data-panel'], node) for node in utils.panelXPath(pageTree)) layoutPanels = dict((node.attrib['data-panel'], node) for node in utils.panelXPath(layoutTree)) # Site layout should always have element with data-panel="content" # Note: This could be more generic, but that would empower editors too much if 'content' in pagePanels and 'content' not in layoutPanels: for node in layoutTree.xpath('//*[@id="content"]'): node.attrib['data-panel'] = 'content' layoutPanels['content'] = node break for panelId, layoutPanelNode in layoutPanels.items(): pagePanelNode = pagePanels.get(panelId, None) if pagePanelNode is not None: utils.replace_content(layoutPanelNode, pagePanelNode) if removePanelLinks: del layoutPanelNode.attrib['data-panel'] if removeLayoutLink: del pageTree.getroot().attrib[utils.layoutAttrib] return layoutTree
def test_resolve_utf8_bytestring(self): """Test fix for issue where layouts with non-ascii characters were not properly parsed resulting in double encoding """ content_layout = u""" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html data-layout="./@@default-site-layout"> <body> <h1>Ä</h1> </body> </html>""".encode('utf-8') text = resolve('', content_layout).xpath('//h1')[0].text self.assertEqual(u'Ä', text)
def applyTilePersistent(path, resolved): """Append X-Tile-Persistent into resolved layout's tile URLs to allow context specific tile configuration overrides. (Path is required for proper error message when lxml parser fails.) """ from plone.app.blocks.utils import tileAttrib from plone.app.blocks.utils import bodyTileXPath from plone.app.blocks.utils import resolve tree = resolve(path, resolved=resolved) for node in bodyTileXPath(tree): url = node.attrib[tileAttrib] if 'X-Tile-Persistent' not in url: if '?' in url: url += '&X-Tile-Persistent=yes' else: url += '?X-Tile-Persistent=yes' node.attrib[tileAttrib] = url return html.tostring(tree)
def tiles_instances(self): tiles = {} baseURL = self.request.getURL() tree = lxml.html.fromstring(self.content) for panelNode in utils.panelXPath(tree): panelName = panelNode.attrib['data-panel'] for tileNode in utils.bodyTileXPath(panelNode): tileName = tileNode.attrib['data-tile'] tileTree = utils.resolve(urljoin(baseURL, tileName)) tile = tileTree.find('body') if panelName not in tiles.keys(): tiles[panelName] = {} tiles[panelName][tileName] = (tile.text or '') + \ ''.join([lxml.html.tostring(child) for child in tile]) return json.dumps(tiles)
def merge(request, pageTree, removePanelLinks=False, removeLayoutLink=True): """Perform panel merging for the given page. Returns None if the page has no layout. """ # Find layout node layoutHref = utils.xpath1(utils.layoutXPath, pageTree) if layoutHref is None: return None # Resolve layout tree baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' layoutHref = urljoin(baseURL, layoutHref) # turn the link absolute layoutTree = utils.resolve(layoutHref) if layoutTree is None: return None # Map page panels onto the layout pagePanels = dict( (node.attrib['data-panel'], node) for node in utils.panelXPath(pageTree) ) for layoutPanelNode in utils.panelXPath(layoutTree): panelId = layoutPanelNode.attrib['data-panel'] pagePanelNode = pagePanels.get(panelId, None) if pagePanelNode is not None: utils.replace_content(layoutPanelNode, pagePanelNode) if removePanelLinks: del layoutPanelNode.attrib['data-panel'] if removeLayoutLink: del pageTree.getroot().attrib[utils.layoutAttrib] return layoutTree
def resolveTiles(request, tree): """Given a request and an lxml tree with the body, find all tile placehodlers and resolve them to actual tiles. """ # renderView = None # renderedRequestKey = None # Optionally enable ESI rendering # registry = queryUtility(IRegistry) # if registry is not None: # if registry.forInterface(IBlocksSettings).esi: # renderView = 'plone.app.blocks.esirenderer' # renderedRequestKey = 'plone.app.blocks.esi' baseURL = request.getURL() root = tree.getroot() headNode = root.find('head') # Find all tile placeholders for tilePlaceholderNode in root.cssselect('img.mceTile'): try: tileHref = tilePlaceholderNode.get('alt', None) if not tileHref: log.error('Could not render tile at %s', tileHref) continue tileHref = urljoin(baseURL, tileHref) tileTree = utils.resolve(tileHref) resolveTile(tilePlaceholderNode, tileHref, tileTree, headNode) except ConflictError: raise except Exception: log.exception('Could not render tile at %s', tileHref) continue return tree
def merge(request, pageTree, removePanelLinks=False, removeLayoutLink=True): """Perform panel merging for the given page. Returns None if the page has no layout. """ # Find layout node layoutHref = utils.xpath1(utils.layoutXPath, pageTree) if layoutHref is None: return None # Resolve layout tree baseURL = request.getURL() layoutHref = urljoin(baseURL, layoutHref) # turn the link absolute layoutTree = utils.resolve(layoutHref) if layoutTree is None: return None # Map page panels onto the layout pagePanels = dict( (node.attrib['data-panel'], node) for node in utils.panelXPath(pageTree) ) for layoutPanelNode in utils.panelXPath(layoutTree): panelId = layoutPanelNode.attrib['data-panel'] pagePanelNode = pagePanels.get(panelId, None) if pagePanelNode is not None: utils.replace_content(layoutPanelNode, pagePanelNode) if removePanelLinks: del layoutPanelNode.attrib['data-panel'] if removeLayoutLink: del pageTree.getroot().attrib[utils.layoutAttrib] return layoutTree
def renderTiles(request, tree): """Find all tiles in the given response, contained in the lxml element tree `tree`, and insert them into the output. Assumes panel merging has already happened. """ # Optionally enable ESI rendering in tiles that support this if not request.getHeader(ESI_HEADER): registry = queryUtility(IRegistry) if registry is not None: if registry.forInterface(IBlocksSettings, check=False).esi: request.environ[ESI_HEADER_KEY] = 'true' root = tree.getroot() headNode = root.find('head') baseURL = request.getURL() if request.getVirtualRoot(): # plone.subrequest deals with VHM requests baseURL = '' for tileNode in utils.headTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except RuntimeError: tileTree = None except NotFound: logger.warn( 'NotFound while trying to render tile: {0}'.format( tileHref ) ) continue if tileTree is not None: tileRoot = tileTree.getroot() utils.replace_with_children(tileNode, tileRoot.find('head')) for tileNode in utils.bodyTileXPath(tree): tileHref = tileNode.attrib[utils.tileAttrib] tileRulesHref = tileNode.attrib.get(utils.tileRulesAttrib) if not tileHref.startswith('/'): tileHref = urljoin(baseURL, tileHref) try: tileTree = utils.resolve(tileHref) except RuntimeError: tileTree = errorTile(request) except NotFound: continue if tileRulesHref: if not tileRulesHref.startswith('/'): tileRulesHref = urljoin(baseURL, tileRulesHref) try: tileTransform = resolve_transform(tileRulesHref, tileNode) except NotFound: tileTransform = None del tileNode.attrib[utils.tileRulesAttrib] else: tileTransform = None if tileTree is not None: tileRoot = tileTree.getroot() tileHead = tileRoot.find('head') tileBody = tileRoot.find('body') if tileHead is None and tileBody is None: tileBody = tileRoot if tileTransform is not None: result = tileTransform(tileBody).getroot() del tileBody[:] tileBody.append(result) if tileHead is not None: for tileHeadChild in tileHead: headNode.append(tileHeadChild) utils.replace_with_children(tileNode, tileBody) return tree