The blog.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

85 lines
2.2 KiB

  1. from jinja2 import contextfilter, environmentfilter
  2. from jinja2.ext import Extension
  3. from io import StringIO
  4. from lxml import etree
  5. @contextfilter
  6. def rellinktoabs(context, value):
  7. env = context.environment
  8. # get the path for this context
  9. rel_path = context['resource'].relative_path
  10. content_url = env.globals['content_url'](context, rel_path)
  11. # Note that this could be parsed w/ fragment_fromstring
  12. # (https://lxml.de/3.1/api/private/lxml.html-module.html#fragment_fromstring)
  13. # But is would require using create_parent, and stripping that
  14. # instead, or fragments_fromstring, but iterating over those
  15. # to strings. Not one is a solution to the problem.
  16. html = etree.HTML(value)
  17. # get all the fragment urls
  18. r = html.xpath("//a[@href[starts-with(.,'#')]]")
  19. for i in r:
  20. # prefix them w/ the content_url
  21. i.attrib['href'] = content_url + i.attrib['href']
  22. res = etree.tostring(html, encoding='unicode', method='html')
  23. # lxml.HTML wraps the html w/ html/body tags, strip them
  24. # if present
  25. startstr = '<html><body>'
  26. endstr = '</body></html>'
  27. startpos = 0
  28. endpos = None
  29. if res.startswith(startstr):
  30. startpos = len(startstr)
  31. if res.endswith(endstr):
  32. endpos = -len(endstr)
  33. res = res[startpos:endpos]
  34. return res
  35. # mostly copied from hyde.ext.templates.jinja.py Markdown
  36. # and using docs from:
  37. # https://jinja.palletsprojects.com/en/2.10.x/extensions/#example-extension
  38. # to get the filter installed
  39. class RelLinktoAbs(Extension):
  40. """
  41. A wrapper around the rellinktoabs filter for syntactic sugar.
  42. """
  43. tags = { 'rellinktoabs' }
  44. def __init__(self, env):
  45. super(RelLinktoAbs, self).__init__(env)
  46. env.filters['rellinktoabs'] = rellinktoabs
  47. def parse(self, parser):
  48. """
  49. Parses the statements and defers to the callback
  50. for rellinktoabs processing.
  51. """
  52. lineno = next(parser.stream).lineno
  53. body = parser.parse_statements(['name:endrellinktoabs'], drop_needle=True)
  54. return nodes.CallBlock(
  55. self.call_method('_render_rellinktoabs'),
  56. [], [], body).set_lineno(lineno)
  57. def _render_rellinktoabs(self, caller=None):
  58. """
  59. Calls the rellinktoabs filter to transform the output.
  60. """
  61. if not caller:
  62. return ''
  63. output = caller().strip()
  64. return rellinktoabs(self.environment, output)