Rough in HTMLTMPL support

This commit is contained in:
Sam Ruby 2006-08-29 01:56:39 -04:00
parent 70180abcb1
commit d17d509adc
45 changed files with 2239 additions and 46 deletions

1
.bzrignore Normal file
View File

@ -0,0 +1 @@
*.tmplc

1480
planet/htmltmpl.py Normal file

File diff suppressed because it is too large Load Diff

23
planet/shell/__init__.py Normal file
View File

@ -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)

190
planet/shell/tmpl.py Normal file
View File

@ -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))

34
planet/shell/xslt.py Normal file
View File

@ -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 File

@ -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():

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,13 @@
<!--
Description: xhtml content
Expect: Items[0]['content'] == 'A <b>very</b> bad day'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">A <b>very</b> bad day</div>
</content>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: id
Expect: Items[0]['id'] == 'http://example.com/1'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<id>http://example.com/1</id>
</entry>
</feed>

View File

@ -0,0 +1,13 @@
<!--
Description: id generated from content
Expect: Items[0]['content'] == 'content'
-->
<rss xml:base="http://example.com/">
<channel>
<item xmlns:content="http://purl.org/rss/1.0/modules/content/">
<content:encoded>content</content>
</item>
</channel>
</rss>

View File

@ -0,0 +1,13 @@
<!--
Description: id generated from description
Expect: Items[0]['content'] == 'description'
-->
<rss xml:base="http://example.com/">
<channel>
<item>
<description>description</description>
</item>
</channel>
</rss>

View File

@ -0,0 +1,13 @@
<!--
Description: id generated from link
Expect: Items[0]['link'] == 'http://example.com/1'
-->
<rss>
<channel>
<item>
<link>http://example.com/1</link>
</item>
</channel>
</rss>

View File

@ -0,0 +1,13 @@
<!--
Description: id generated from title
Expect: Items[0]['title'] == 'title' and Items[0]['title_plain'] == 'title'
-->
<rss xml:base="http://example.com/">
<channel>
<item>
<title>title</title>
</item>
</channel>
</rss>

View File

@ -0,0 +1,11 @@
<!--
Description: link relationship
Expect: Items[0]['link'] == 'http://example.com/1'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<link href="http://example.com/1"/>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: link relationship
Expect: Items[0]['link'] == 'http://example.com/1'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<link href="http://example.com/1"/>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: link relationship
Expect: Items[0]['link'] == 'http://example.com/1'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<link href="http://example.com/1"/>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: id
Expect: Channels[0]['name'] == 'foo'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<planet:name>foo</planet:name>
<entry>
<source>
<planet:name>foo</planet:name>
</source>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: published, rollover past midnight on feb 28 in leap year
Expect: Items[0]['published_iso'] == '2004-02-29T02:14:55+00:00' and Items[0]['published_822'] == 'Sun, 29 Feb 2004 02:14:55 +0000' and Items[0]['published'] == 'February 29, 2004 02:14 AM'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<published>2004-02-28T18:14:55-08:00</published>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: rights
Expect: Items[0]['rights'] == '&copy; 2006'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<rights type="html">&amp;copy; 2006</rights>
</entry>
</feed>

View File

@ -0,0 +1,18 @@
<!--
Description: source author
Expect: Channels[0]['author'] == 'John Doe' and Channels[0]['author_name'] == 'John Doe' and Items[0]['channel_author'] == 'John Doe' and Items[0]['channel_author_name'] == 'John Doe'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<author>
<name>John Doe</name>
</author>
<entry>
<source>
<author>
<name>John Doe</name>
</author>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source icon
Expect: Channels[0]['icon'] == 'http://www.example.com/favicon.ico' and Items[0]['channel_icon'] == 'http://www.example.com/favicon.ico'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<icon>http://www.example.com/favicon.ico</icon>
<entry>
<source>
<icon>http://www.example.com/favicon.ico</icon>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source id
Expect: Channels[0]['id'] == 'http://example.com/' and Items[0]['channel_id'] == 'http://example.com/'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<id>http://example.com/</id>
<entry>
<source>
<id>http://example.com/</id>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source logo
Expect: Channels[0]['logo'] == 'http://www.example.com/logo.jpg' and Items[0]['channel_logo'] == 'http://www.example.com/logo.jpg'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<logo>http://www.example.com/logo.jpg</logo>
<entry>
<source>
<logo>http://www.example.com/logo.jpg</logo>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: id
Expect: Channels[0]['name'] == 'foo' and Items[0]['channel_name'] == 'foo'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<planet:name>foo</planet:name>
<entry>
<source>
<planet:name>foo</planet:name>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source rights
Expect: Channels[0]['rights'] == '&copy; 2006' and Items[0]['channel_rights'] == '&copy; 2006'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<rights type="html">&amp;copy; 2006</rights>
<entry>
<source>
<rights type="html">&amp;copy; 2006</rights>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source subtitle
Expect: Channels[0]['subtitle'] == 'snarky phrase' and Items[0]['channel_subtitle'] == 'snarky phrase'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<subtitle>snarky phrase</subtitle>
<entry>
<source>
<subtitle>snarky phrase</subtitle>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source title
Expect: Channels[0]['title'] == 'visible name' and Channels[0]['title_plain'] == 'visible name' and Items[0]['channel_title_plain'] == 'visible name' and Items[0]['channel_title'] == 'visible name'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<title>visible name</title>
<entry>
<source>
<title>visible name</title>
</source>
</entry>
</feed>

View File

@ -0,0 +1,14 @@
<!--
Description: source updated, rollover past midnight on feb 28 in leap year
Expect: Channels[0]['last_updated_iso'] == '2004-02-29T02:14:55+00:00' and Channels[0]['last_updated_822'] == 'Sun, 29 Feb 2004 02:14:55 +0000' and Items[0]['channel_last_updated_iso'] == '2004-02-29T02:14:55+00:00' and Items[0]['channel_last_updated_822'] == 'Sun, 29 Feb 2004 02:14:55 +0000'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<updated>2004-02-28T18:14:55-08:00</updated>
<entry>
<source>
<updated>2004-02-28T18:14:55-08:00</updated>
</source>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: entity encoded html summary
Expect: Items[0]['content'] == 'D&eacute;tente'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<summary type="html">D&amp;eacute;tente</summary>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: summary value
Expect: Items[0]['content'] == 'foo' and Items[0]['summary_language'] == 'en-us'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<summary xml:lang="en-us">foo</summary>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: plain text summary
Expect: Items[0]['content'] == 'AT&amp;T'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<summary type="text">AT&amp;T</summary>
</entry>
</feed>

View File

@ -0,0 +1,13 @@
<!--
Description: xhtml summary
Expect: Items[0]['content'] == 'A <b>very</b> bad day'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<summary type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">A <b>very</b> bad day</div>
</summary>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: entity encoded html title
Expect: Items[0]['title'] == 'D&eacute;tente' and Items[0]['title_plain'] == 'D\xc3\xa9tente'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<title type="html">D&amp;eacute;tente</title>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: title value
Expect: Items[0]['title'] == 'foo' and Items[0]['title_language'] == 'en-us' and Items[0]['title_plain'] == 'foo'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<title xml:lang="en-us">foo</title>
</entry>
</feed>

View File

@ -0,0 +1,10 @@
<!--
Description: plain text title
Expect: Items[0]['title'] == 'AT&amp;T' and Items[0]['title_plain'] == 'AT&T'
-->
<feed xmns="http://www.w3.org/2005/Atom">
<entry>
<title type="text">AT&amp;T</title>
</entry>
</feed>

View File

@ -0,0 +1,13 @@
<!--
Description: xhtml title
Expect: Items[0]['title'] == 'A <b>very</b> bad day' and Items[0]['title_plain'] == 'A very bad day'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<title type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">A <b>very</b> bad day</div>
</title>
</entry>
</feed>

View File

@ -0,0 +1,11 @@
<!--
Description: updated, rollover past midnight on feb 28 in leap year
Expect: Items[0]['updated_822'] == 'Sun, 29 Feb 2004 02:14:55 +0000' and Items[0]['updated_iso'] == '2004-02-29T02:14:55+00:00' and Items[0]['updated'] == 'February 29, 2004 02:14 AM'
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<updated>2004-02-28T18:14:55-08:00</updated>
</entry>
</feed>

37
tests/test_filter_tmpl.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
import unittest, os, sys, glob, new, re, StringIO, time
from planet.shell import tmpl
testfiles = 'tests/data/filter/tmpl/%s.xml'
class FilterTmplTest(unittest.TestCase):
desc_re = re.compile("Description:\s*(.*?)\s*Expect:\s*(.*)\s*-->")
simple_re = re.compile("^(\S+) == (u?'[^']*'|\([0-9, ]+\))$")
def eval(self, name):
# read the test case
try:
testcase = open(testfiles % name)
data = testcase.read()
description, expect = self.desc_re.search(data).groups()
testcase.close()
except:
raise RuntimeError, "can't parse %s" % name
# map to template info
results = tmpl.template_info(data)
# verify the results
if not self.simple_re.match(expect):
self.assertTrue(eval(expect, results), expect)
else:
lhs, rhs = self.simple_re.match(expect).groups()
self.assertEqual(eval(rhs), eval(lhs, results))
# build a test method for each test file
for testcase in glob.glob(testfiles % '*'):
root = os.path.splitext(os.path.basename(testcase))[0]
func = lambda self, name=root: self.eval(name)
method = new.instancemethod(func, None, FilterTmplTest)
setattr(FilterTmplTest, "test_" + root, method)

View File

@ -4,6 +4,7 @@
[Planet]
template_files:
atom.xml.xslt
rss20.xml.tmpl
foafroll.xml.xslt
index.html.xslt
opml.xml.xslt

View File

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title><TMPL_VAR name></title>
<link><TMPL_VAR link ESCAPE="HTML"></link>
<language>en</language>
<description><TMPL_VAR name ESCAPE="HTML"> - <TMPL_VAR link ESCAPE="HTML"></description>
<TMPL_LOOP Items>
<item>
<title><TMPL_VAR channel_name ESCAPE="HTML"><TMPL_IF title>: <TMPL_VAR title_plain ESCAPE="HTML"></TMPL_IF></title>
<guid><TMPL_VAR id ESCAPE="HTML"></guid>
<link><TMPL_VAR link ESCAPE="HTML"></link>
<TMPL_IF content>
<description><TMPL_VAR content ESCAPE="HTML"></description>
</TMPL_IF>
<pubDate><TMPL_VAR date_822></pubDate>
<TMPL_IF author_email>
<TMPL_IF author_name>
<author><TMPL_VAR author_email> (<TMPL_VAR author_name>)</author>
<TMPL_ELSE>
<author><TMPL_VAR author_email></author>
</TMPL_IF>
</TMPL_IF>
</item>
</TMPL_LOOP>
</channel>
</rss>