Permalink
Browse files

Rough in HTMLTMPL support

  • Loading branch information...
1 parent 70180ab commit d17d509adc5b08b6ca8c32e8cef01f9626b14ab2 Sam Ruby committed Aug 29, 2006
Showing with 2,239 additions and 46 deletions.
  1. +1 −0 .bzrignore
  2. +1,480 −0 planet/htmltmpl.py
  3. +23 −0 planet/shell/__init__.py
  4. +190 −0 planet/shell/tmpl.py
  5. +34 −0 planet/shell/xslt.py
  6. +2 −46 planet/splice.py
  7. +13 −0 tests/data/filter/tmpl/author_email.xml
  8. +13 −0 tests/data/filter/tmpl/author_name.xml
  9. +13 −0 tests/data/filter/tmpl/author_uri.xml
  10. +10 −0 tests/data/filter/tmpl/content_html.xml
  11. +10 −0 tests/data/filter/tmpl/content_lang.xml
  12. +10 −0 tests/data/filter/tmpl/content_text.xml
  13. +13 −0 tests/data/filter/tmpl/content_xhtml.xml
  14. +11 −0 tests/data/filter/tmpl/id.xml
  15. +13 −0 tests/data/filter/tmpl/id_only_content.xml
  16. +13 −0 tests/data/filter/tmpl/id_only_description.xml
  17. +13 −0 tests/data/filter/tmpl/id_only_link.xml
  18. +13 −0 tests/data/filter/tmpl/id_only_title.xml
  19. +11 −0 tests/data/filter/tmpl/link_href.xml
  20. +11 −0 tests/data/filter/tmpl/link_rel.xml
  21. +11 −0 tests/data/filter/tmpl/link_type.xml
  22. +14 −0 tests/data/filter/tmpl/planet_name.xml
  23. +11 −0 tests/data/filter/tmpl/published.xml
  24. +11 −0 tests/data/filter/tmpl/rights.xml
  25. +18 −0 tests/data/filter/tmpl/source_author.xml
  26. +14 −0 tests/data/filter/tmpl/source_icon.xml
  27. +14 −0 tests/data/filter/tmpl/source_id.xml
  28. +14 −0 tests/data/filter/tmpl/source_logo.xml
  29. +14 −0 tests/data/filter/tmpl/source_planet_name.xml
  30. +14 −0 tests/data/filter/tmpl/source_rights.xml
  31. +14 −0 tests/data/filter/tmpl/source_subtitle.xml
  32. +14 −0 tests/data/filter/tmpl/source_title.xml
  33. +14 −0 tests/data/filter/tmpl/source_updated.xml
  34. +10 −0 tests/data/filter/tmpl/summary_html.xml
  35. +10 −0 tests/data/filter/tmpl/summary_lang.xml
  36. +10 −0 tests/data/filter/tmpl/summary_text.xml
  37. +13 −0 tests/data/filter/tmpl/summary_xhtml.xml
  38. +10 −0 tests/data/filter/tmpl/title_html.xml
  39. +10 −0 tests/data/filter/tmpl/title_lang.xml
  40. +10 −0 tests/data/filter/tmpl/title_text.xml
  41. +13 −0 tests/data/filter/tmpl/title_xhtml.xml
  42. +11 −0 tests/data/filter/tmpl/updated.xml
  43. +37 −0 tests/test_filter_tmpl.py
  44. +1 −0 themes/asf/config.ini
  45. +30 −0 themes/common/rss20.xml.tmpl
View
@@ -0,0 +1 @@
+*.tmplc
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,23 @@
+import planet
+import os
+
+def run(template_file, doc):
+ """ select a template module based on file extension and execute it """
+ log = planet.getLogger(planet.config.log_level())
+
+ for template_dir in planet.config.template_directories():
+ template_resolved = os.path.join(template_dir, template_file)
+ if os.path.exists(template_resolved): break
+ else:
+ return log.error("Unable to locate template %s", template_file)
+
+ base,ext = os.path.splitext(os.path.basename(template_resolved))
+ try:
+ module = __import__('planet/shell/' + ext[1:])
+ except:
+ return log.error("Skipping template %s", template_resolved)
+
+ log.info("Processing template %s", template_resolved)
+ output_dir = planet.config.output_dir()
+ output_file = os.path.join(output_dir, base)
+ module.run(template_resolved, doc, output_file)
View
@@ -0,0 +1,190 @@
+from xml.sax.saxutils import escape
+import sgmllib, time, os, sys
+from planet import config, feedparser, htmltmpl
+
+class stripHtml(sgmllib.SGMLParser):
+ "remove all tags from the data"
+ def __init__(self, data):
+ sgmllib.SGMLParser.__init__(self)
+ self.result=''
+ if isinstance(data, str):
+ try:
+ self.feed(data.decode('utf-8'))
+ except:
+ self.feed(data)
+ else:
+ self.feed(data)
+ self.close()
+ def __str__(self):
+ if isinstance(self.result, unicode):
+ return self.result.encode('utf-8')
+ return self.result
+ def handle_entityref(self, ref):
+ import htmlentitydefs
+ if ref in htmlentitydefs.entitydefs:
+ ref=htmlentitydefs.entitydefs[ref]
+ if len(ref)==1:
+ self.result+=unichr(ord(ref))
+ elif ref.startswith('&#') and ref.endswith(';'):
+ self.handle_charref(ref[2:-1])
+ else:
+ self.result+='&%s;' % ref
+ else:
+ self.result+='&%s;' % ref
+ def handle_charref(self, ref):
+ try:
+ if ref.startswith('x'):
+ self.result+=unichr(int(ref[1:],16))
+ else:
+ self.result+=unichr(int(ref))
+ except:
+ self.result+='&#%s;' % ref
+ def handle_data(self, data):
+ if data: self.result+=data
+
+# Data format mappers
+
+def String(value):
+ if isinstance(value, unicode): return value.encode('utf-8')
+ return value
+
+def Plain(value):
+ return str(stripHtml(value))
+
+def PlanetDate(value):
+ return time.strftime(config.date_format(), value)
+
+def Rfc822(value):
+ return time.strftime("%a, %d %b %Y %H:%M:%S +0000", value)
+
+def Rfc3399(value):
+ return time.strftime("%Y-%m-%dT%H:%M:%S+00:00", value)
+
+# Map from FeedParser path to Planet tmpl names
+Base = [
+ ['author', String, 'author'],
+ ['author_name', String, 'author_detail', 'name'],
+ ['feed', String, 'links', {'rel':'self'}, 'href'],
+ ['generator', String, 'generator'],
+ ['id', String, 'id'],
+ ['icon', String, 'icon'],
+ ['last_updated_822', Rfc822, 'updated_parsed'],
+ ['last_updated_iso', Rfc3399, 'updated_parsed'],
+ ['last_updated', PlanetDate, 'updated_parsed'],
+ ['logo', String, 'logo'],
+ ['rights', String, 'rights_detail', 'value'],
+ ['subtitle', String, 'subtitle_detail', 'value'],
+ ['title', String, 'title_detail', 'value'],
+ ['title_plain', Plain, 'title_detail', 'value'],
+]
+
+# ? new_date, new_channel
+Items = [
+ ['author', String, 'author'],
+ ['author_email', String, 'author_detail', 'email'],
+ ['author_name', String, 'author_detail', 'name'],
+ ['author_uri', String, 'author_detail', 'href'],
+ ['content_language', String, 'content', 0, 'language'],
+ ['content', String, 'summary_detail', 'value'],
+ ['content', String, 'content', 0, 'value'],
+ ['date', PlanetDate, 'published_parsed'],
+ ['date', PlanetDate, 'updated_parsed'],
+ ['date_822', Rfc822, 'published_parsed'],
+ ['date_822', Rfc822, 'updated_parsed'],
+ ['date_iso', Rfc3399, 'published_parsed'],
+ ['date_iso', Rfc3399, 'updated_parsed'],
+ ['id', String, 'id'],
+ ['link', String, 'links', {'rel': 'alternate'}, 'href'],
+ ['rights', String, 'rights_detail', 'value'],
+ ['title_language', String, 'title_detail', 'language'],
+ ['title_plain', Plain, 'title_detail', 'value'],
+ ['title', String, 'title_detail', 'value'],
+ ['summary_language', String, 'summary_detail', 'language'],
+ ['updated', PlanetDate, 'updated_parsed'],
+ ['updated_822', Rfc822, 'updated_parsed'],
+ ['updated_iso', Rfc3399, 'updated_parsed'],
+ ['published', PlanetDate, 'published_parsed'],
+ ['published_822', Rfc822, 'published_parsed'],
+ ['published_iso', Rfc3399, 'published_parsed'],
+]
+
+Channels = [
+ ['url', None],
+ ['link', None],
+ ['message', None],
+ ['title_plain', None],
+ ['name', None],
+]
+
+# Add additional rules for source information
+for rule in Base:
+ Items.append(['channel_'+rule[0], rule[1], 'source'] + rule[2:])
+
+def tmpl_mapper(source, rules):
+ "Apply specified rules to the source, and return a template dictionary"
+ output = {}
+
+ for rule in rules:
+ node = source
+ for path in rule[2:]:
+ if isinstance(path, str) and path in node:
+ if path == 'value' and node.get('type','')=='text/plain':
+ node['value'] = escape(node['value'])
+ node['type'] = 'text/html'
+ node = node[path]
+ elif isinstance(path, int):
+ node = node[path]
+ elif isinstance(path, dict):
+ for test in node:
+ for key, value in path.items():
+ if test.get(key,None) != value: break
+ else:
+ node = test
+ break
+ else:
+ break
+ else:
+ break
+ else:
+ if node: output[rule[0]] = rule[1](node)
+
+ # copy over all planet namespaced elements from parent source
+ for name,value in source.items():
+ if name.startswith('planet_'):
+ output[name[7:]] = String(value)
+
+ # copy over all planet namespaced elements from child source element
+ if 'source' in source:
+ for name,value in source.source.items():
+ if name.startswith('planet_'):
+ output['channel_' + name[7:]] = String(value)
+
+ return output
+
+def template_info(source):
+ """ get template information from a feedparser output """
+ data=feedparser.parse(source)
+ output = {'Channels': [], 'Items': []}
+ output['Channels'].append(tmpl_mapper(data.feed, Base))
+ for entry in data.entries:
+ output['Items'].append(tmpl_mapper(entry, Items))
+ return output
+
+def run(script, doc, output_file=None):
+ """ process an HTMLTMPL file """
+ manager = htmltmpl.TemplateManager()
+ template = manager.prepare(script)
+ tp = htmltmpl.TemplateProcessor(html_escape=0)
+ for key,value in template_info(doc).items():
+ tp.set(key, value)
+ output = open(output_file, "w")
+ output.write(tp.process(template))
+ output.close()
+
+if __name__ == '__main__':
+ sys.path.insert(0, os.path.split(sys.path[0])[0])
+
+ for test in sys.argv[1:]:
+ from pprint import pprint
+ pprint(template_info('/home/rubys/bzr/venus/tests/data/filter/tmpl/'+test))
+
View
@@ -0,0 +1,34 @@
+import os
+
+def run(script, doc, output_file=None):
+ """ process an XSLT stylesheet """
+
+ try:
+ # if available, use the python interface to libxslt
+ import libxml2
+ import libxslt
+ dom = libxml2.parseDoc(doc)
+ docfile = None
+ except:
+ # otherwise, use the command line interface
+ dom = None
+ import warnings
+ warnings.simplefilter('ignore', RuntimeWarning)
+ docfile = os.tmpnam()
+ file = open(docfile,'w')
+ file.write(doc)
+ file.close()
+
+ # do it
+ if dom:
+ styledoc = libxml2.parseFile(script)
+ style = libxslt.parseStylesheetDoc(styledoc)
+ result = style.applyStylesheet(dom, None)
+ style.saveResultToFilename(output_file, result, 0)
+ style.freeStylesheet()
+ result.freeDoc()
+ else:
+ os.system('xsltproc %s %s > %s' % (script, docfile, output_file))
+
+ if dom: dom.freeDoc()
+ if docfile: os.unlink(docfile)
View
@@ -1,7 +1,7 @@
""" Splice together a planet from a cache of feed entries """
import glob, os, time, shutil
from xml.dom import minidom
-import planet, config, feedparser, reconstitute
+import planet, config, feedparser, reconstitute, shell
from reconstitute import createTextElement, date
from spider import filename
@@ -57,53 +57,9 @@ def apply(doc):
if not os.path.exists(output_dir): os.makedirs(output_dir)
log = planet.getLogger(config.log_level())
- try:
- # if available, use the python interface to libxslt
- import libxml2
- import libxslt
- dom = libxml2.parseDoc(doc)
- docfile = None
- except:
- # otherwise, use the command line interface
- dom = None
- import warnings
- warnings.simplefilter('ignore', RuntimeWarning)
- docfile = os.tmpnam()
- file = open(docfile,'w')
- file.write(doc)
- file.close()
-
# Go-go-gadget-template
for template_file in config.template_files():
- for template_dir in config.template_directories():
- template_resolved = os.path.join(template_dir, template_file)
- if os.path.exists(template_resolved): break
- else:
- log.error("Unable to locate template %s", template_file)
- continue
-
- base,ext = os.path.splitext(os.path.basename(template_resolved))
- if ext != '.xslt':
- log.warning("Skipping template %s", template_resolved)
- continue
-
- log.info("Processing template %s", template_resolved)
- output_file = os.path.join(output_dir, base)
- if dom:
- styledoc = libxml2.parseFile(template_resolved)
- style = libxslt.parseStylesheetDoc(styledoc)
- result = style.applyStylesheet(dom, None)
- log.info("Writing %s", output_file)
- style.saveResultToFilename(output_file, result, 0)
- style.freeStylesheet()
- result.freeDoc()
- else:
- log.info("Writing %s", output_file)
- os.system('xsltproc %s %s > %s' %
- (template_resolved, docfile, output_file))
-
- if dom: dom.freeDoc()
- if docfile: os.unlink(docfile)
+ shell.run(template_file, doc)
# Process bill of materials
for copy_file in config.bill_of_materials():
@@ -0,0 +1,13 @@
+<!--
+Description: author name
+Expect: Items[0]['author'] == 'john@example.com' and Items[0]['author_email'] == 'john@example.com'
+-->
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <entry>
+ <author>
+ <email>john@example.com</email>
+ </author>
+ </entry>
+</feed>
+
@@ -0,0 +1,13 @@
+<!--
+Description: author name
+Expect: Items[0]['author_name'] == 'John Doe' and Items[0]['author'] == 'John Doe'
+-->
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <entry>
+ <author>
+ <name>John Doe</name>
+ </author>
+ </entry>
+</feed>
+
@@ -0,0 +1,13 @@
+<!--
+Description: author name
+Expect: Items[0]['author_uri'] == 'http://example.com/~john/'
+-->
+
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <entry>
+ <author>
+ <uri>http://example.com/~john/</uri>
+ </author>
+ </entry>
+</feed>
+
@@ -0,0 +1,10 @@
+<!--
+Description: entity encoded html content
+Expect: Items[0]['content'] == 'D&eacute;tente'
+-->
+
+<feed xmns="http://www.w3.org/2005/Atom">
+ <entry>
+ <content type="html">D&amp;eacute;tente</content>
+ </entry>
+</feed>
@@ -0,0 +1,10 @@
+<!--
+Description: content value
+Expect: Items[0]['content'] == 'foo' and Items[0]['content_language'] == 'en-us'
+-->
+
+<feed xmns="http://www.w3.org/2005/Atom">
+ <entry>
+ <content xml:lang="en-us">foo</content>
+ </entry>
+</feed>
@@ -0,0 +1,10 @@
+<!--
+Description: plain text content
+Expect: Items[0]['content'] == 'AT&amp;T'
+-->
+
+<feed xmns="http://www.w3.org/2005/Atom">
+ <entry>
+ <content type="text">AT&amp;T</content>
+ </entry>
+</feed>
Oops, something went wrong.

0 comments on commit d17d509

Please sign in to comment.