Theme support

This commit is contained in:
Sam Ruby 2006-08-21 09:43:09 -04:00
parent 9fa9fb6117
commit 7cbc2e31bf
41 changed files with 1463 additions and 61 deletions

View File

@ -2,6 +2,9 @@ xmlns = 'http://planet.intertwingly.net/'
logger = None
import config
config.__init__()
def getLogger(level):
""" get a logger with the specified log level """
global logger

View File

@ -11,7 +11,7 @@ Usage:
config.load('config.ini')
# administrative / structural information
print config.templates()
print config.template_files()
print config.feeds()
# planet wide configuration
@ -37,6 +37,7 @@ def __init__():
"""define the struture of an ini file"""
import config
# underlying implementation
def get(section, option, default):
if section and parser.has_option(section, option):
return parser.get(section, option)
@ -49,6 +50,10 @@ def __init__():
setattr(config, name, lambda default=default: get(None,name,default))
planet_predefined_options.append(name)
def define_planet_list(name):
setattr(config, name, lambda : get(None,name,'').split())
planet_predefined_options.append(name)
def define_tmpl(name, default):
setattr(config, name, lambda section, default=default:
get(section,name,default))
@ -63,25 +68,57 @@ def __init__():
define_planet('cache_directory', "cache")
define_planet('log_level', "WARNING")
define_planet('feed_timeout', 20)
define_planet('date_format', "%B %d, %Y %I:%M %p")
define_planet('generator', 'Venus')
define_planet('generator_uri', 'http://intertwingly.net/code/venus/')
define_planet('owner_name', 'Anonymous Coward')
define_planet('owner_email', '')
define_planet('output_theme', '')
define_planet('output_dir', 'output')
define_planet_list('template_files')
define_planet_list('bill_of_materials')
define_planet_list('template_directories')
# template options
define_tmpl_int('days_per_page', 0)
define_tmpl_int('items_per_page', 60)
define_tmpl('encoding', 'utf-8')
# prevent re-initialization
setattr(config, '__init__', lambda: None)
def load(file):
def load(config_file):
""" initialize and load a configuration"""
__init__()
global parser
parser = ConfigParser()
parser.read(file)
parser.read(config_file)
def template_files():
""" list the templates defined """
return parser.get('Planet','template_files').split(' ')
if parser.has_option('Planet', 'output_theme'):
theme = parser.get('Planet', 'output_theme')
for path in ("", os.path.join(sys.path[0],'themes')):
theme_dir = os.path.join(path,theme)
theme_file = os.path.join(theme_dir,'config.ini')
if os.path.exists(theme_file):
# initial search list for theme directories
dirs = [theme_dir]
if parser.has_option('Planet', 'template_directories'):
dirs.insert(0,parser.get('Planet', 'template_directories'))
# read in the theme
parser = ConfigParser()
parser.read(theme_file)
# complete search list for theme directories
if parser.has_option('Planet', 'template_directories'):
dirs += [os.path.join(theme_dir,dir) for dir in
parser.get('Planet', 'template_directories').split()]
# merge configurations, allowing current one to override theme
parser.read(config_file)
parser.set('Planet', 'template_directories', ' '.join(dirs))
break
else:
import config, planet
log = planet.getLogger(config.log_level())
log.error('Unable to find theme %s', theme)
def cache_sources_directory():
if parser.has_option('Planet', 'cache_sources_directory'):

View File

@ -18,7 +18,7 @@ from xml.sax.saxutils import escape
from xml.dom import minidom
from BeautifulSoup import BeautifulSoup
from xml.parsers.expat import ExpatError
import planet
import planet, config
illegal_xml_chars = re.compile("[\x01-\x08\x0B\x0C\x0E-\x1F]")
@ -29,6 +29,7 @@ def createTextElement(parent, name, value):
xelement = xdoc.createElement(name)
xelement.appendChild(xdoc.createTextNode(value))
parent.appendChild(xelement)
return xelement
def invalidate(c):
""" replace invalid characters """
@ -98,7 +99,9 @@ def date(xentry, name, parsed):
""" insert a date-formated element into the entry """
if not parsed: return
formatted = time.strftime("%Y-%m-%dT%H:%M:%SZ", parsed)
createTextElement(xentry, name, formatted)
xdate = createTextElement(xentry, name, formatted)
formatted = time.strftime(config.date_format(), parsed)
xdate.setAttribute('planet:format', formatted)
def author(xentry, name, detail):
""" insert an author-like element into the entry """

View File

@ -1,8 +1,8 @@
""" Splice together a planet from a cache of feed entries """
import glob, os
import glob, os, time, shutil
from xml.dom import minidom
import planet, config, feedparser, reconstitute
from reconstitute import createTextElement
from reconstitute import createTextElement, date
from spider import filename
def splice(configFile):
@ -11,6 +11,7 @@ def splice(configFile):
config.load(configFile)
log = planet.getLogger(config.log_level())
log.info("Loading cached data")
cache = config.cache_directory()
dir=[(os.stat(file).st_mtime,file) for file in glob.glob(cache+"/*")
if not os.path.isdir(file)]
@ -18,17 +19,20 @@ def splice(configFile):
dir.reverse()
items=max([config.items_per_page(templ)
for templ in config.template_files()])
for templ in config.template_files() or ['Planet']])
doc = minidom.parseString('<feed xmlns="http://www.w3.org/2005/Atom"/>')
feed = doc.documentElement
# insert Google/LiveJournal's noindex
feed.setAttribute('indexing:index','no')
feed.setAttribute('xmlns:indexing','urn:atom-extension:indexing')
# insert feed information
createTextElement(feed, 'title', config.name())
date(feed, 'updated', time.gmtime())
gen = createTextElement(feed, 'generator', config.generator())
gen.setAttribute('uri', config.generator_uri())
author = doc.createElement('author')
createTextElement(author, 'name', config.owner_name())
createTextElement(author, 'email', config.owner_email())
feed.appendChild(author)
# insert entry information
for mtime,file in dir[:items]:
@ -47,3 +51,75 @@ def splice(configFile):
feed.appendChild(xdoc.documentElement)
return doc
def apply(doc):
output_dir = config.output_dir()
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)
# Process bill of materials
for copy_file in config.bill_of_materials():
dest = os.path.join(output_dir, copy_file)
for template_dir in config.template_directories():
source = os.path.join(template_dir, copy_file)
if os.path.exists(source): break
else:
log.error('Unable to locate %s', copy_file)
continue
mtime = os.stat(source).st_mtime
if not os.path.exists(dest) or os.stat(dest).st_mtime < mtime:
dest_dir = os.path.split(dest)[0]
if not os.path.exists(dest_dir): os.makedirs(dest_dir)
log.info("Copying %s to %s", source, dest)
shutil.copyfile(source, dest)
shutil.copystat(source, dest)

View File

@ -1,8 +1,11 @@
#!/usr/bin/env python
import glob, trace, unittest
import glob, trace, unittest, os, sys
# start in a consistent, predictable location
os.chdir(sys.path[0])
# find all of the planet test modules
modules = map(trace.fullmodname, glob.glob('tests/test_*.py'))
modules = map(trace.fullmodname, glob.glob(os.path.join('tests', 'test_*.py')))
# load all of the tests into a suite
suite = unittest.TestLoader().loadTestsFromNames(modules)

View File

@ -13,10 +13,7 @@ if __name__ == '__main__':
# at the moment, we don't have template support, so we cheat and
# simply insert a XSLT processing instruction
doc = splice.splice(sys.argv[1])
pi = doc.createProcessingInstruction(
'xml-stylesheet','type="text/xsl" href="planet.xslt"')
doc.insertBefore(pi, doc.firstChild)
print doc.toxml('utf-8')
splice.apply(doc.toxml('utf-8'))
else:
print "Usage:"
print " python %s config.ini" % sys.argv[0]

42
tests/capture.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python
"""
While unit tests are intended to be independently executable, it often
is helpful to ensure that some downstream tasks can be run with the
exact output produced by upstream tasks.
This script captures such output. It should be run whenever there is
a major change in the contract between stages
"""
import shutil, os, sys
# move up a directory
sys.path.insert(1, os.path.split(sys.path[0])[0])
os.chdir(sys.path[1])
# copy spider output to splice input
from planet import spider
spider.spiderPlanet('tests/data/spider/config.ini')
if os.path.exists('tests/data/splice/cache'):
shutil.rmtree('tests/data/splice/cache')
shutil.move('tests/work/spider/cache', 'tests/data/splice/cache')
source=open('tests/data/spider/config.ini')
dest1=open('tests/data/splice/config.ini', 'w')
dest1.write(source.read().replace('/work/spider/', '/data/splice/'))
dest1.close()
source.seek(0)
dest2=open('tests/data/apply/config.ini', 'w')
dest2.write(source.read().replace('[Planet]', '''[Planet]
output_theme = asf
output_dir = tests/work/apply'''))
dest2.close()
source.close()
# copy splice output to apply input
from planet import splice
file=open('tests/data/apply/feed.xml', 'w')
file.write(splice.splice('tests/data/splice/config.ini').toxml('utf-8'))
file.close()

View File

@ -0,0 +1,17 @@
[Planet]
output_theme = asf
output_dir = tests/work/apply
name = test planet
cache_directory = tests/work/spider/cache
[tests/data/spider/testfeed0.atom]
name = not found
[tests/data/spider/testfeed1b.atom]
name = one
[tests/data/spider/testfeed2.atom]
name = two
[tests/data/spider/testfeed3.rss]
name = three

215
tests/data/apply/feed.xml Normal file
View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/"><title>test planet</title><updated planet:format="August 21, 2006 12:54 PM">2006-08-21T12:54:31Z</updated><generator uri="http://intertwingly.net/code/venus/">Venus</generator><author><name>Anonymous Coward</name></author><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed3/2</id>
<link href="http://example.com/2" rel="alternate" type="text/html"/>
<title>Venus</title>
<summary>the Morning Star</summary>
<updated planet:format="August 21, 2006 12:54 PM">2006-08-21T12:54:31Z</updated>
<source>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss" rel="alternate" type="text/html"/>
<link href="tests/data/spider/testfeed3.rss" rel="self" type="application/atom+xml"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<planet:name>three</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>http://example.com/4</id>
<link href="http://example.com/4" rel="alternate" type="text/html"/>
<title>Mars</title>
<summary>the Red Planet</summary>
<updated planet:format="August 21, 2006 12:54 PM">2006-08-21T12:54:31Z</updated>
<source>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss" rel="alternate" type="text/html"/>
<link href="tests/data/spider/testfeed3.rss" rel="self" type="application/atom+xml"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<planet:name>three</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed1/2</id>
<link href="http://example.com/2" rel="alternate" type="text/html"/>
<title>Venus</title>
<content>the Jewel of the Sky</content>
<updated planet:format="February 02, 2006 12:00 AM">2006-02-02T00:00:00Z</updated>
<published planet:format="January 02, 2006 12:00 AM">2006-01-02T00:00:00Z</published>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed1a.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed2/4</id>
<link href="http://example.com/4" rel="alternate" type="text/html"/>
<title>Mars</title>
<content>the Red Planet</content>
<updated planet:format="January 04, 2006 12:00 AM">2006-01-04T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed2.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed1/4</id>
<link href="http://example.com/4" rel="alternate" type="text/html"/>
<title>Mars</title>
<content>the Red Planet</content>
<updated planet:format="January 04, 2006 12:00 AM">2006-01-04T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed1a.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed2/3</id>
<link href="http://example.com/3" rel="alternate" type="text/html"/>
<title>Earth</title>
<content>the Blue Planet</content>
<updated planet:format="January 03, 2006 12:00 AM">2006-01-03T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed2.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed1/3</id>
<link href="http://example.com/3" rel="alternate" type="text/html"/>
<title>Earth</title>
<content>the Blue Planet</content>
<updated planet:format="January 03, 2006 12:00 AM">2006-01-03T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed1a.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>http://example.com/3</id>
<link href="http://example.com/3" rel="alternate" type="text/html"/>
<title>Earth</title>
<summary>the Blue Planet</summary>
<updated planet:format="January 03, 2006 12:00 AM">2006-01-03T00:00:00Z</updated>
<source>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss" rel="alternate" type="text/html"/>
<link href="tests/data/spider/testfeed3.rss" rel="self" type="application/atom+xml"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<planet:name>three</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed2/2</id>
<link href="http://example.com/2" rel="alternate" type="text/html"/>
<title>Venus</title>
<content>the Morning Star</content>
<updated planet:format="January 02, 2006 12:00 AM">2006-01-02T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed2.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed3/1</id>
<link href="http://example.com/1" rel="alternate" type="text/html"/>
<title>Mercury</title>
<summary>Messenger of the Roman Gods</summary>
<updated planet:format="January 01, 2006 12:00 AM">2006-01-01T00:00:00Z</updated>
<source>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss" rel="alternate" type="text/html"/>
<link href="tests/data/spider/testfeed3.rss" rel="self" type="application/atom+xml"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<planet:name>three</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed2/1</id>
<link href="http://example.com/1" rel="alternate" type="text/html"/>
<title>Mercury</title>
<content>Messenger of the Roman Gods</content>
<updated planet:format="January 01, 2006 12:00 AM">2006-01-01T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed2.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/">
<id>tag:planet.intertwingly.net,2006:testfeed1/1</id>
<link href="http://example.com/1" rel="alternate" type="text/html"/>
<title>Mercury</title>
<content>Messenger of the Roman Gods</content>
<updated planet:format="January 01, 2006 12:00 AM">2006-01-01T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
<name>Sam Ruby</name>
<email>rubys@intertwingly.net</email>
<uri>http://www.intertwingly.net/blog/</uri>
</author>
<link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed1a.atom" rel="self" type="application/atom+xml"/>
<link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry><planet:source xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/"><id>tag:planet.intertwingly.net,2006:testfeed2</id><author><name>Sam Ruby</name><email>rubys@intertwingly.net</email><uri>http://www.intertwingly.net/blog/</uri></author><link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed2.atom" rel="self" type="application/atom+xml"/><link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/><subtitle>Its just data</subtitle><title>Sam Ruby</title><updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated><planet:name>two</planet:name></planet:source><planet:source xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/"><link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss" rel="alternate" type="text/html"/><link href="tests/data/spider/testfeed3.rss" rel="self" type="application/atom+xml"/><subtitle>Its just data</subtitle><title>Sam Ruby</title><planet:name>three</planet:name></planet:source><planet:source xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/"><id>tag:planet.intertwingly.net,2006:testfeed1</id><author><name>Sam Ruby</name><email>rubys@intertwingly.net</email><uri>http://www.intertwingly.net/blog/</uri></author><link href="http://intertwingly.net/code/venus/tests/data/spider/testfeed1a.atom" rel="self" type="application/atom+xml"/><link href="http://www.intertwingly.net/blog/" rel="alternate" type="text/html"/><subtitle>Its just data</subtitle><title>Sam Ruby</title><updated planet:format="June 17, 2006 12:15 AM">2006-06-17T00:15:18Z</updated><planet:name>one</planet:name></planet:source></feed>

View File

@ -0,0 +1,14 @@
[Planet]
name = Test Configuration
output_theme = asf
items_per_page = 50
template_directories = /foo /bar
[index.html.xslt]
days_per_page = 7
[feed1]
name = one
[feed2]
name = two

View File

@ -1,6 +1,6 @@
[Planet]
name = test planet
cache_directory = tests/work/spider/cache
template_files =
[tests/data/spider/testfeed0.atom]
name = not found

View File

@ -4,7 +4,7 @@
<link href='http://example.com/3' type='text/html' rel='alternate'/>
<title>Earth</title>
<summary>the Blue Planet</summary>
<updated>2006-01-03T00:00:00Z</updated>
<updated planet:format='January 03, 2006 12:00 AM'>2006-01-03T00:00:00Z</updated>
<source>
<link href='http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss' type='text/html' rel='alternate'/>
<link href='tests/data/spider/testfeed3.rss' type='application/atom+xml' rel='self'/>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/4' type='text/html' rel='alternate'/>
<title>Mars</title>
<summary>the Red Planet</summary>
<updated>2006-08-18T18:30:50Z</updated>
<updated planet:format='August 21, 2006 12:54 PM'>2006-08-21T12:54:31Z</updated>
<source>
<link href='http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss' type='text/html' rel='alternate'/>
<link href='tests/data/spider/testfeed3.rss' type='application/atom+xml' rel='self'/>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/1' type='text/html' rel='alternate'/>
<title>Mercury</title>
<content>Messenger of the Roman Gods</content>
<updated>2006-01-01T00:00:00Z</updated>
<updated planet:format='January 01, 2006 12:00 AM'>2006-01-01T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry>

View File

@ -4,8 +4,8 @@
<link href='http://example.com/2' type='text/html' rel='alternate'/>
<title>Venus</title>
<content>the Jewel of the Sky</content>
<updated>2006-02-02T00:00:00Z</updated>
<published>2006-01-02T00:00:00Z</published>
<updated planet:format='February 02, 2006 12:00 AM'>2006-02-02T00:00:00Z</updated>
<published planet:format='January 02, 2006 12:00 AM'>2006-01-02T00:00:00Z</published>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
@ -17,7 +17,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/3' type='text/html' rel='alternate'/>
<title>Earth</title>
<content>the Blue Planet</content>
<updated>2006-01-03T00:00:00Z</updated>
<updated planet:format='January 03, 2006 12:00 AM'>2006-01-03T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/4' type='text/html' rel='alternate'/>
<title>Mars</title>
<content>the Red Planet</content>
<updated>2006-01-04T00:00:00Z</updated>
<updated planet:format='January 04, 2006 12:00 AM'>2006-01-04T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed1</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/1' type='text/html' rel='alternate'/>
<title>Mercury</title>
<content>Messenger of the Roman Gods</content>
<updated>2006-01-01T00:00:00Z</updated>
<updated planet:format='January 01, 2006 12:00 AM'>2006-01-01T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/2' type='text/html' rel='alternate'/>
<title>Venus</title>
<content>the Morning Star</content>
<updated>2006-01-02T00:00:00Z</updated>
<updated planet:format='January 02, 2006 12:00 AM'>2006-01-02T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/3' type='text/html' rel='alternate'/>
<title>Earth</title>
<content>the Blue Planet</content>
<updated>2006-01-03T00:00:00Z</updated>
<updated planet:format='January 03, 2006 12:00 AM'>2006-01-03T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/4' type='text/html' rel='alternate'/>
<title>Mars</title>
<content>the Red Planet</content>
<updated>2006-01-04T00:00:00Z</updated>
<updated planet:format='January 04, 2006 12:00 AM'>2006-01-04T00:00:00Z</updated>
<source>
<id>tag:planet.intertwingly.net,2006:testfeed2</id>
<author>
@ -16,7 +16,7 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</source>
</entry>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/1' type='text/html' rel='alternate'/>
<title>Mercury</title>
<summary>Messenger of the Roman Gods</summary>
<updated>2006-01-01T00:00:00Z</updated>
<updated planet:format='January 01, 2006 12:00 AM'>2006-01-01T00:00:00Z</updated>
<source>
<link href='http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss' type='text/html' rel='alternate'/>
<link href='tests/data/spider/testfeed3.rss' type='application/atom+xml' rel='self'/>

View File

@ -4,7 +4,7 @@
<link href='http://example.com/2' type='text/html' rel='alternate'/>
<title>Venus</title>
<summary>the Morning Star</summary>
<updated>2006-08-18T18:30:50Z</updated>
<updated planet:format='August 21, 2006 12:54 PM'>2006-08-21T12:54:31Z</updated>
<source>
<link href='http://intertwingly.net/code/venus/tests/data/spider/testfeed3.rss' type='text/html' rel='alternate'/>
<link href='tests/data/spider/testfeed3.rss' type='application/atom+xml' rel='self'/>

View File

@ -10,6 +10,6 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>one</planet:name>
</feed>

View File

@ -10,6 +10,6 @@
<link href='http://www.intertwingly.net/blog/' type='text/html' rel='alternate'/>
<subtitle>Its just data</subtitle>
<title>Sam Ruby</title>
<updated>2006-06-17T00:15:18Z</updated>
<updated planet:format='June 17, 2006 12:15 AM'>2006-06-17T00:15:18Z</updated>
<planet:name>two</planet:name>
</feed>

View File

@ -1,7 +1,6 @@
[Planet]
name = test planet
cache_directory = tests/data/splice/cache
template_files =
[tests/data/spider/testfeed0.atom]
name = not found

33
tests/test_apply.py Normal file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env python
import unittest, os, shutil
from planet import config, splice
workdir = 'tests/work/apply'
configfile = 'tests/data/apply/config.ini'
testfeed = 'tests/data/apply/feed.xml'
class ApplyTest(unittest.TestCase):
def setUp(self):
try:
os.makedirs(workdir)
except:
self.tearDown()
os.makedirs(workdir)
def tearDown(self):
shutil.rmtree(workdir)
os.removedirs(os.path.split(workdir)[0])
def test_apply(self):
testfile = open(testfeed)
feeddata = testfile.read()
testfile.close()
config.load(configfile)
splice.apply(feeddata)
for file in ['index.html', 'default.css', 'images/foaf.png']:
path = os.path.join(workdir, file)
self.assertTrue(os.path.exists(path))
self.assertTrue(os.stat(path).st_size > 0)

View File

@ -3,8 +3,6 @@
import unittest
from planet import config
workdir = 'tests/work/spider/cache'
class ConfigTest(unittest.TestCase):
def setUp(self):
config.load('tests/data/config/basic.ini')
@ -16,7 +14,9 @@ class ConfigTest(unittest.TestCase):
config.template_files())
def test_feeds(self):
self.assertEqual(['feed1', 'feed2'], config.feeds())
feeds = config.feeds()
feeds.sort()
self.assertEqual(['feed1', 'feed2'], feeds)
# planet wide configuration

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
import unittest, os, glob, calendar
import unittest, os, glob, calendar, shutil
from planet.spider import filename, spiderFeed, spiderPlanet
from planet import feedparser, config
@ -17,13 +17,8 @@ class SpiderTest(unittest.TestCase):
os.makedirs(workdir)
def tearDown(self):
for file in glob.glob(workdir+"/sources/*"):
os.unlink(file)
if os.path.exists(workdir+"/sources"):
os.rmdir(workdir+"/sources")
for file in glob.glob(workdir+"/*"):
os.unlink(file)
os.removedirs(workdir)
shutil.rmtree(workdir)
os.removedirs(os.path.split(workdir)[0])
def test_filename(self):
self.assertEqual('./example.com,index.html',

58
tests/test_themes.py Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
import unittest
from planet import config
from os.path import split
class ConfigTest(unittest.TestCase):
def setUp(self):
config.load('tests/data/config/themed.ini')
# template directories
def test_template_directories(self):
self.assertEqual(['foo', 'bar', 'asf', 'common'],
[split(dir)[1] for dir in config.template_directories()])
# administrivia
def test_template(self):
self.assertTrue('index.html.xslt' in config.template_files())
def test_feeds(self):
feeds = config.feeds()
feeds.sort()
self.assertEqual(['feed1', 'feed2'], feeds)
# planet wide configuration
def test_name(self):
self.assertEqual('Test Configuration', config.name())
def test_link(self):
self.assertEqual('Unconfigured Planet', config.link())
# per template configuration
def test_days_per_page(self):
self.assertEqual(7, config.days_per_page('index.html.xslt'))
self.assertEqual(0, config.days_per_page('atom.xml.xslt'))
def test_items_per_page(self):
self.assertEqual(50, config.items_per_page('index.html.xslt'))
self.assertEqual(50, config.items_per_page('atom.xml.xslt'))
def test_encoding(self):
self.assertEqual('utf-8', config.encoding('index.html.xslt'))
self.assertEqual('utf-8', config.encoding('atom.xml.xslt'))
# dictionaries
def test_feed_options(self):
self.assertEqual('one', config.feed_options('feed1')['name'])
self.assertEqual('two', config.feed_options('feed2')['name'])
def test_template_options(self):
option = config.template_options('index.html.xslt')
self.assertEqual('7', option['days_per_page'])
self.assertEqual('50', option['items_per_page'])

19
themes/asf/config.ini Normal file
View File

@ -0,0 +1,19 @@
# This template is based on the one originally developed by Stefano Mazzocci
# for planetapache.org, and modified by Sam Ruby for planet.intertwingly.net
[Planet]
template_files:
atom.xml.xslt
foafroll.xml.xslt
index.html.xslt
opml.xml.xslt
template_directories:
../common
bill_of_materials:
default.css
personalize.js
images/feed-icon-10x10.png
images/opml.png
images/foaf.png

429
themes/asf/default.css Normal file
View File

@ -0,0 +1,429 @@
/*
* Written by Stefano Mazzocchi <stefano at apache dot org>
*/
/* ----------------------------- Global Definitions -------------------- */
body {
margin: 0px;
padding: 0px;
color: #222;
background-color: #fff;
quotes: "\201C" "\201E" "\2018" "\2019";
}
a:link {
color: #222;
}
a:visited {
color: #555;
}
a:hover {
color: #000;
}
a:active {
}
a:focus {
}
h1 {
font-size: x-large;
text-transform: uppercase;
letter-spacing: 0.25em;
padding: 10px;
margin: 0px 0px 0px 0px;
color: #000;
font-weight: normal;
background-color: #eee;
border-bottom: 2px solid #bbb
}
/* ----------------------------- Sidebar --------------------------- */
#sidebar {
float: right;
top: 150px;
right: 0px;
width: 210px;
background-color: white;
padding: 0px 0px 20px 0px;
margin: 0px 0px 20px 20px;
border-left: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
#sidebar h2 {
letter-spacing: 0.15em;
text-transform: uppercase;
font-size: x-small;
color: #666;
font-weight: normal;
padding: 2px 0px 2px 4px;
margin: 15px 0px 5px 10px;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
#sidebar p {
font-size: x-small;
padding-left: 20px;
padding-right: 5px;
}
#sidebar ul {
font-family: sans-serif;
margin-left: 5px;
padding-left: 25px;
}
#sidebar li {
margin-left: 0px;
text-indent: -15px;
list-style-type: none;
font-size: x-small;
}
#sidebar ul li a {
text-decoration: none;
}
#sidebar ul li a:hover {
text-decoration: underline;
}
#sidebar img {
border: 0;
}
#sidebar dl {
font-size: x-small;
padding-left: 1.0em;
}
#sidebar dl ul {
padding-left: 1em;
}
#sidebar dt {
margin-top: 1em;
font-weight: bold;
padding-left: 1.0em;
}
#sidebar dd {
margin-left: 2.5em;
}
#sidebar .message {
cursor: help;
border-bottom: 1px dashed red;
}
#sidebar a.message:hover {
cursor: help;
background-color: #ffD0D0;
border: 1px dashed red !important;
text-decoration: none !important;
}
/* ----------------------------- Body ---------------------------- */
#body {
margin-top: 10px;
}
.admin {
text-align: right;
}
#body h2.date {
text-transform: none;
font-size: medium;
color: #333;
font-weight: bold;
text-align: right;
border-top: 1px solid #ccc;
background-color: #eee;
border-bottom: 1px solid #ccc;
padding: 1px 15px 1px 5px;
margin: 0;
}
/* ----------------------------- News ---------------------------- */
.news {
margin: 30px 10px 30px 10px;
clear: left;
}
.news > h3 {
text-indent: -10px;
margin: 12px;
padding: 0px;
font-size: medium;
}
.news > h3 > a:first-child {
margin-left: 10px
}
.news > h3 > a:first-child:before {
content: '⌘';
color: #D70;
margin-left: -18px;
margin-right: 2px;
text-decoration: none;
}
img.icon {
height: 16px;
width: 16px;
margin-left: -8px;
margin-bottom: -2px;
margin-right: 3px;
}
.news .content {
margin: 5px 5px 5px 15px;
padding: 0px 5px 0px 5px;
border-left: 1px solid #ccc;
line-height: 1.2em;
font-size: small;
font-family: sans-serif;
}
.news .links {
}
.news .permalink {
text-align: right;
}
/* ----------------------------- News Content ---------------------------- */
.news .content p {
line-height: 1.2em;
}
.news .content img {
margin: 5px;
}
.news .content blockquote {
margin: 10px 35px 10px 35px;
padding: 5px;
}
.news .content pre {
font-family: monospace;
font-size: medium;
font-weight: bold;
border: 1px solid #ddd;
padding: 10px;
margin: 10px 20px 10px 20px;
background-color: #f8f8f8;
overflow: auto;
}
.news .content ul, .news .content ol {
margin: 5px 35px 5px 35px;
padding: 5px;
counter-reset: item;
}
.news .content ul > ul, .news .content ul > ol, .news .content ol > ul, .news .content ol > ol {
margin: 0px 0px 0px 35px;
padding: 0px;
}
.news .content li {
padding: 1px;
line-height: 1.2em;
}
.news code {
font-family: monospace;
font-size: medium;
font-weight: bold;
}
.news .content a {
text-decoration: none;
color: #000;
border-bottom: 1px dotted #777;
margin: 0px 2px 0px 2px;
padding: 1px 1px 1px 1px;
}
.news .content a:hover {
border: 1px dotted #000;
background-color: #eee;
padding: 1px 2px 1px 2px;
margin: 0px;
}
.news .content a:active {
background-color: #ccc !important;
position: relative;
top: 1px;
left: 1px;
padding: 1px 2px 1px 2px;
margin: 0px;
}
.news .content a:focus {
border: 1px solid #fff !important;
background-color: #ccc !important;
padding: 1px 2px 1px 2px;
margin: 0px;
}
/* --------------------------- Accomodations ----------------------- */
/* boing boing */
br {
clear: none !important;
}
/* engadget */
h6 {
clear: left !important;
}
/* cadenhead */
p.sourcecode {
font-family: monospace;
font-size: medium;
font-weight: bold;
border: 1px solid #ddd;
padding: 10px;
margin: 10px 20px 10px 20px;
background-color: #f8f8f8;
overflow: auto;
}
/* cadenhead */
span.sourcecode {
font-family: monospace;
font-size: medium;
font-weight: bold;
font-size: large;
background-color: #f8f8f8;
}
/* hsivonen */
ul p, ol p {
margin-top: 0.3em;
margin-bottom: 0.3em;
}
/* programmableweb */
.imgRight {
float: right;
}
/* gizmodo */
img.left {
float: left;
}
/* gizmodo */
img.right {
float: right;
}
/* gizmodo */
img.center {
display: block;
margin-left: auto;
margin-right: auto;
}
/* wikipedia */
table {
width: auto !important;
}
/* del.icio.us */
.delicious-tags {
font-size: x-small;
text-align: right;
}
/* musings */
img.mathlogo, img.svglogo {
float: right;
border: 0;
}
math {
white-space: nowrap;
}
math[display=block] {
overflow: auto;
}
.eqno {
float: right;
}
/* sutor */
img.post-img-right {
float:right;
}
/* niall */
img.floatright {
float: right;
}
/* jason kolb */
.FeaturedPost > li {
list-style-type: none;
background-color: #f8f8f8;
}
/* GigaOM */
p img {
float: left;
}
/* Tantek */
ul.tags,ul.tags li,h4.tags {
display:inline;
font-size: x-small
}
ul.tags a:link, ul.tags a:visited {
color:green
}
/* DiveIntoMark */
.framed {
float: none;
}
/* ----------------------------- Footer ---------------------------- */
#footer {
padding: 0px;
margin: 30px 0px 50px 50px;
}
#footer p {
padding: 2px 2px 2px 5px;
background-color: #ccc;
border-top: 1px solid #aaa;
border-bottom: 1px solid #aaa;
border-left: 1px solid #aaa;
letter-spacing: 0.15em;
text-transform: uppercase;
text-align: left;
}

153
themes/asf/index.html.xslt Normal file
View File

@ -0,0 +1,153 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:planet="http://planet.intertwingly.net/"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output indent="yes" method="html"/>
<xsl:template match="atom:feed">
<html xmlns="http://www.w3.org/1999/xhtml">
<xsl:text>&#10;</xsl:text>
<head>
<link rel="stylesheet" href="default.css" type="text/css" />
<title><xsl:value-of select="atom:title"/></title>
<meta name="generator" content="{atom:generator}" />
<xsl:if test="atom:link[@rel='self']">
<link rel="alternate" href="{atom:link[@rel='self']/@uri}"
title="{atom:title}" type="{atom:link[@rel='self']/@type}" />
</xsl:if>
<link rel="shortcut icon" href="/favicon.ico" />
<script type="text/javascript" src="personalize.js"></script>
</head>
<xsl:text>&#10;&#10;</xsl:text>
<body>
<xsl:text>&#10;</xsl:text>
<h1><xsl:value-of select="atom:title"/></h1>
<xsl:text>&#10;</xsl:text>
<div id="sidebar">
<xsl:text>&#10;&#10;</xsl:text>
<h2>Subscriptions</h2>
<ul>
<xsl:for-each select="planet:source">
<xsl:sort select="planet:name"/>
<xsl:text>&#10;</xsl:text>
<li>
<a href="{atom:link[@rel='self']/@href}" title="subscribe">
<img src="images/feed-icon-10x10.png" alt="(feed)"/>
</a>
<a href="{atom:link[@rel='alternate']/@href}">
<xsl:value-of select="planet:name"/>
</a>
</li>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
</ul>
<xsl:text>&#10;&#10;</xsl:text>
<h2>Info</h2>
<dl>
<dt>Last updated:</dt>
<dd><span class="date" title="GMT"><xsl:value-of select="atom:updated"/></span></dd>
<dt>Powered by:</dt>
<dd><a href="http://intertwingly.net/code/planet/"><img src="images/planet.png" width="80" height="15" alt="Planet" border="0" /></a></dd>
<dt>Export:</dt>
<dd>
<ul>
<li><a href="opml.xml"><img src="images/opml.png" alt="OPML" /></a></li>
<li><a href="foafroll.xml"><img src="images/foaf.png" alt="FOAF" /></a></li>
</ul>
</dd>
</dl>
</div>
<xsl:apply-templates select="atom:entry"/>
</body>
</html>
</xsl:template>
<xsl:template match="atom:entry">
<!-- date header -->
<xsl:variable name="date" select="substring(atom:updated,1,10)"/>
<xsl:if test="not(preceding-sibling::atom:entry
[substring(atom:updated,1,10) = $date])">
<xsl:text>&#10;&#10;</xsl:text>
<h2 class="date"><xsl:value-of select="$date"/></h2>
</xsl:if>
<xsl:text>&#10;&#10;</xsl:text>
<div class="news">
<xsl:if test="@xml:lang">
<xsl:attribute name="xml:lang" select="{@xml:lang}"/>
</xsl:if>
<!-- entry title -->
<xsl:text>&#10;</xsl:text>
<h3>
<xsl:if test="atom:source/atom:icon">
<img src="{atom:source/atom:icon}" class="icon"/>
</xsl:if>
<a href="{atom:source/atom:link['alternate']/@href}" class="icon">
<xsl:attribute name="title" select="{atom:source/atom:title}"/>
<xsl:value-of select="atom:source/planet:name"/>
</a>
<xsl:if test="atom:title">
<xsl:text>&#x2014;</xsl:text>
<a href="{atom:link[@rel='alternate']/@href}">
<xsl:if test="atom:title/@xml:lang != @xml:lang">
<xsl:attribute name="xml:lang" select="{atom:title/@xml:lang}"/>
</xsl:if>
<xsl:value-of select="atom:title"/>
</a>
</xsl:if>
</h3>
<!-- entry content -->
<xsl:text>&#10;</xsl:text>
<div class="content">
<xsl:choose>
<xsl:when test="atom:content">
<xsl:if test="atom:content/@xml:lang != @xml:lang">
<xsl:attribute name="xml:lang" select="{atom:content/@xml:lang}"/>
</xsl:if>
<xsl:copy-of select="atom:content/*"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="atom:summary/@xml:lang != @xml:lang">
<xsl:attribute name="xml:lang" select="{atom:summary/@xml:lang}"/>
</xsl:if>
<xsl:copy-of select="atom:summary/*"/>
</xsl:otherwise>
</xsl:choose>
</div>
<!-- entry footer -->
<xsl:text>&#10;</xsl:text>
<div class="permalink">
<a href="{atom:link[@rel='alternate']/@href}">
<xsl:choose>
<xsl:when test="atom:author/atom:name">
<xsl:text>by </xsl:text>
<xsl:value-of select="atom:author/atom:name"/>
<xsl:text> at </xsl:text>
</xsl:when>
<xsl:when test="atom:source/atom:author/atom:name">
<xsl:text>by </xsl:text>
<xsl:value-of select="atom:source/atom:author/atom:name"/>
<xsl:text> at </xsl:text>
</xsl:when>
</xsl:choose>
<span class="date" title="GMT">
<xsl:value-of select="atom:updated"/>
</span>
</a>
</div>
</div>
</xsl:template>
</xsl:stylesheet>

220
themes/asf/personalize.js Normal file
View File

@ -0,0 +1,220 @@
var entries = []; // list of news items
var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"];
var months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
// event complete: stop propagation of the event
function stopPropagation(event) {
if (event.preventDefault) {
event.preventDefault();
event.stopPropagation();
} else {
event.returnValue = false;
}
}
// scroll back to the previous article
function prevArticle(event) {
for (var i=entries.length; --i>=0;) {
if (entries[i].anchor.offsetTop < document.documentElement.scrollTop) {
window.location.hash=entries[i].anchor.id;
stopPropagation(event);
break;
}
}
}
// advance to the next article
function nextArticle(event) {
for (var i=1; i<entries.length; i++) {
if (entries[i].anchor.offsetTop-20 > document.documentElement.scrollTop) {
window.location.hash=entries[i].anchor.id;
stopPropagation(event);
break;
}
}
}
// process keypresses
function navkey(event) {
var checkbox = document.getElementById('navkeys');
if (!checkbox || !checkbox.checked) return;
if (!event) event=window.event;
key=event.keyCode;
if (!document.documentElement) return;
if (!entries[0].anchor || !entries[0].anchor.offsetTop) return;
if (key == 'J'.charCodeAt(0)) nextArticle(event);
if (key == 'K'.charCodeAt(0)) prevArticle(event);
}
// create (or reset) a cookie
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
// read a cookie
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
// each time the value of the option changes, update the cookie
function selectOption() {
var checkbox = document.getElementById('navkeys');
if (!checkbox) return;
createCookie("navkeys", checkbox.checked?'true':'false', 365);
}
// add navkeys option to sidebar
function addOption(event) {
if (entries.length > 1 && entries[entries.length-1].parent.offsetTop > 0) {
var sidebar = document.getElementById('sidebar');
if (!sidebar) return;
for (var i=entries.length; --i>=0;) {
var a = entries[i].anchor = document.createElement('a');
a.id = "news-" + i;
entries[i].parent.insertBefore(a, entries[i].parent.firstChild);
}
var h2 = document.createElement('h2');
h2.appendChild(document.createTextNode('Options'));
sidebar.appendChild(h2);
var form = document.createElement('form');
var p = document.createElement('p');
var input = document.createElement('input');
input.type = "checkbox";
input.id = "navkeys";
p.appendChild(input);
var a = document.createElement('a');
a.title = "Navigate entries";
a.appendChild(document.createTextNode('Enable '));
var code = document.createElement('code');
code.appendChild(document.createTextNode('J'));
a.appendChild(code);
a.appendChild(document.createTextNode(' and '));
code = document.createElement('code');
code.appendChild(document.createTextNode('K'));
a.appendChild(code);
a.appendChild(document.createTextNode(' keys'));
p.appendChild(a);
form.appendChild(p);
sidebar.appendChild(form);
var cookie = readCookie("navkeys");
if (cookie && cookie == 'true') input.checked = true;
input.onclick = selectOption;
document.onkeydown = navkey;
}
}
// convert date to local time
var localere = /^(\w+) (\d+) (\w+) \d+ 0?(\d\d?:\d\d):\d\d ([AP]M) (EST|EDT|CST|CDT|MST|MDT|PST|PDT)/;
function localizeDate(element) {
var date = new Date();
date.setTime(Date.parse(element.innerHTML + " GMT"));
var local = date.toLocaleString();
var match = local.match(localere);
if (match) {
element.innerHTML = match[4] + ' ' + match[5].toLowerCase();
element.title = match[6] + " \u2014 " +
match[1] + ', ' + match[3] + ' ' + match[2];
return days[date.getDay()] + ', ' + months[date.getMonth()] + ' ' +
date.getDate() + ', ' + date.getFullYear();
} else {
element.title = element.innerHTML + ' GMT';
element.innerHTML = local;
return days[date.getDay()] + ', ' + date.getDate() + ' ' +
months[date.getMonth()] + ' ' + date.getFullYear();
}
}
// find entries (and localizeDates)
function findEntries() {
var span = document.getElementsByTagName('span');
for (var i=0; i<span.length; i++) {
if (span[i].className == "date" && span[i].title == "GMT") {
var date = localizeDate(span[i]);
var parent = span[i];
while (parent && parent.className != 'news') {
parent = parent.parentNode;
}
if (parent) {
var info = entries[entries.length] = new Object();
info.parent = parent;
info.date = date;
}
}
}
}
// insert/remove date headers to indicate change of date in local time zone
function moveDateHeaders() {
lastdate = ''
for (var i=0; i<entries.length; i++) {
var parent = entries[i].parent;
var date = entries[i].date;
sibling = parent.previousSibling;
while (sibling && sibling.nodeType != 1) {
sibling = sibling.previousSibling;
}
if (sibling && sibling.nodeName.toLowerCase() == 'h2') {
if (lastdate == date) {
sibling.parentNode.removeChild(sibling);
} else {
sibling.innerHTML = date;
lastdate = date;
}
} else if (lastdate != date) {
var h2 = document.createElement('h2');
h2.className = 'date'
h2.appendChild(document.createTextNode(date));
parent.parentNode.insertBefore(h2, parent);
lastdate = date;
}
}
}
// adjust dates to local time zones, optionally provide navigation keys
function personalize() {
findEntries();
addOption();
moveDateHeaders();
}
// hook event
window.onload = personalize;
if (document.addEventListener) {
onDOMLoad = function() {
window.onload = undefined;
personalize();
};
document.addEventListener("DOMContentLoaded", onDOMLoad, false);
}

View File

@ -0,0 +1,25 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:indexing="urn:atom-extension:indexing"
xmlns:planet="http://planet.intertwingly.net/"
xmlns="http://www.w3.org/1999/xhtml">
<!-- strip planet elements and attributes -->
<xsl:template match="planet:*"/>
<!-- add Google/LiveJournal-esque noindex directive -->
<xsl:template match="atom:feed">
<xsl:copy>
<xsl:attribute name="indexing:index">no</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- pass through everything else -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,39 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:rss="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:planet="http://planet.intertwingly.net/"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="atom:feed">
<rdf:RDF>
<foaf:Group>
<foaf:name><xsl:value-of select="atom:author/atom:name"/></foaf:name>
<foaf:homepage><xsl:value-of select="atom:author/atom:uri"/></foaf:homepage>
<xsl:apply-templates select="planet:source"/>
</foaf:Group>
</rdf:RDF>
</xsl:template>
<xsl:template match="planet:source">
<foaf:member>
<foaf:Agent>
<foaf:name><xsl:value-of select="planet:name"/></foaf:name>
<foaf:weblog>
<foaf:Document rdf:about="{atom:link[@rel='alternate']/@href}">
<dc:title><xsl:value-of select="atom:title"/></dc:title>
<rdfs:seeAlso>
<rss:channel rdf:about="{atom:link[@rel='self']/@href}" />
</rdfs:seeAlso>
</foaf:Document>
</foaf:weblog>
</foaf:Agent>
</foaf:member>
</xsl:template>
</xsl:stylesheet>

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

View File

@ -0,0 +1,25 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:planet="http://planet.intertwingly.net/"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="atom:feed">
<opml version="1.1">
<head>
<title><xsl:value-of select="atom:title"/></title>
<dateModified><xsl:value-of select="atom:updated/@planet:format"/></dateModified>
<ownerName><xsl:value-of select="atom:author/atom:name"/></ownerName>
<ownerEmail><xsl:value-of select="atom:author/atom:email"/></ownerEmail>
</head>
<body>
<xsl:for-each select="planet:source">
<outline type="rss" text="{planet:name}" title="{atom:title}"
xmlUrl="{atom:link[@rel='self']/@href}"/>
</xsl:for-each>
</body>
</opml>
</xsl:template>
</xsl:stylesheet>