diff --git a/README b/README index 3806508..8a222e0 100644 --- a/README +++ b/README @@ -1,3 +1,22 @@ +HAML FOR MARS release 0.4 + +To run the haml example, execute: + + rake setup + rake planet + +and point your browser to yourdir/index.html + +To play with templates once the cache has been built: + + rake splice + +Setup copies in config and .haml templates for the intertwingly theme. +The various clean tasks remove the extra files. See rake -T + + +Previously on Planet Mars... + To verify that you have the necessary prereqs installed, execute: rake prereqs diff --git a/Rakefile.rb b/Rakefile.rb index d4a8010..0d2fbd7 100644 --- a/Rakefile.rb +++ b/Rakefile.rb @@ -1,9 +1,60 @@ task :default => [:test] +desc "Run the unit tests" task :test do ruby "-rubygems", "test.rb" end +desc "Test for Mars prerequisites" task :prereqs do ruby "-rubygems", "prereqs.rb" end + +desc "Clean up published files" +task :clean do + CLEAN_FILES = FileList['./*.xml', './*.html'] + CLEAN_FILES.each do |fn| + rm fn + end + rmdir "images" +end + +desc "Clean up all files" +task :clean_all do + CLEAN_ALL_FILES = FileList['./*.xml', './*.html', './*.haml', './*.xslt', './*.ini', './*.css', './*.ico', './*.js', './images/*', "source/*", "http/*", "entry/*"] + CLEAN_ALL_FILES.each do |fn| + rm fn + end + rmdir "images" + rmdir "source" + rmdir "http" + rmdir "entry" +end + +desc "Clean up cache files" +task :clean_cache do + CLEAN_CACHE_FILES = FileList[ "source/*", "http/*", "entry/*"] + CLEAN_CACHE_FILES.each do |fn| + rm fn + end + rmdir "source" + rmdir "http" + rmdir "entry" +end + +desc "Install config files for haml/intertwingly example" +task :setup do + FileList['./themes/intertwingly/*'].each do |f| cp "#{f}", "." end + mkdir "images" + mv "feed-icon-10x10.png", "images" +end + +desc "Run planet for haml example" +task :planet do + ruby "planet.rb", "basic.ini" +end + +desc "Splice planet for haml example" +task :splice do + ruby "splice.rb", "basic.ini" +end diff --git a/planet/config.rb b/planet/config.rb index 692dbb4..4868dcd 100644 --- a/planet/config.rb +++ b/planet/config.rb @@ -7,6 +7,13 @@ def Planet.config @@config end + def Planet.predefine_options(planet_hash) + planet_hash['name'] = "Unconfigured Planet" unless planet_hash['name'] + planet_hash['link'] = '' unless planet_hash['link'] + planet_hash['date_format'] = "%B %d, %Y %I:%M %p" unless planet_hash['date_format'] + planet_hash['new_date_format'] = "%B %d, %Y" unless planet_hash['new_date_format'] + end + # Configuration parser compatible with the data format supported by Python: # http://docs.python.org/lib/module-ConfigParser.html class PythonConfigParser < DelegateClass(Hash) @@ -85,6 +92,8 @@ def read filename planet = self['Planet'] + Planet.predefine_options(planet) + Planet.log_format planet['log_format'] if planet['log_format'] Planet.log_level planet['log_level'] if planet['log_level'] end diff --git a/planet/formatter.rb b/planet/formatter.rb new file mode 100644 index 0000000..e101af2 --- /dev/null +++ b/planet/formatter.rb @@ -0,0 +1,39 @@ +require 'planet/config' + +class PlanetFormatter + + def process( stylesheet, feed ) + raise 'Abstract method called' + end + + def plain(value) + #TODO add HTML stripper + return value + end + + def string(value) + #TODO deal with encoding issues + return value + end + + def planet_date(value) + df = Planet.config['Planet']['date_format'] + df = "%B %d, %Y %I:%M %p" unless df + return value ? Time.parse(value).gmtime.strftime(df): nil + end + + def new_date(value) + ndf = Planet.config['Planet']['new_date_format'] + ndf = "%B %d, %Y %I:%M %p" unless ndf + return value ? Time.parse(value).gmtime.strftime(ndf): nil + end + + def rfc822(value) + return value ? Time.parse(value).gmtime.strftime("%a, %d %b %Y %H:%M:%S +0000"): nil + end + + def rfc3399(value) + return value ? Time.parse(value).gmtime.strftime("%Y-%m-%dT%H:%M:%S+00:00"): nil + end + +end diff --git a/planet/hamlformatter.rb b/planet/hamlformatter.rb new file mode 100755 index 0000000..c33de62 --- /dev/null +++ b/planet/hamlformatter.rb @@ -0,0 +1,261 @@ +require 'planet/formatter' +require 'planet/harvest' +require 'planet/config' + +class HamlFormatter < PlanetFormatter + begin + # http://haml.hamptoncatlin.com/ + require 'haml' + rescue LoadError + puts "Haml_interp: haml library not found. Try gem install haml" + end + + def map_feed(f) + # map a harvest feed to a hash + out = {} + out['author'] = string(f.author) unless f.author.size == 0 + out['author_name'] = string(f.author_detail.name) if f.author_detail.name + out['icon'] = string(f.icon) if f.icon + out['id'] = string(f.id) if f.id + out['last_updated'] = planet_date(f.updated) if f.updated + out['last_updated_822'] = rfc822(f.updated) if f.updated + out['last_updated_iso'] = rfc3399(f.updated) if f.updated + out['link'] = string(f.link) if f.link + out['logo'] = string(f.logo) if f.logo + out['message'] = string(f.message) if f.message + out['name'] = string(f.name) if f.name + out['rights'] = string(f.rights) if f.rights + out['subtitle'] = string(f.subtitle) if f.subtitle + out['title'] = string(f.title) if f.title + out['title_plain'] = plain(f.title) if f.title + out['url'] = string(f.url) if f.url + return out + end + + def map_entry(e) + # map a harvest entry to a hash + out = {} + out['author'] = string(e.author) unless e.author.size == 0 + out['author_name'] = string(e.author_detail.name) if e.author_detail.name + out['author_email'] = string(e.author_detail.email) if e.author_detail.email + out['author_uri'] = string(e.author_detail.uri) if e.author_detail.uri + out['channel_author'] = string(e.source.author) unless e.source.author.size == 0 + out['channel_author_name'] = string(e.source.author_detail.name) if e.source.author_detail.name + out['channel_icon'] = string(e.source.icon) if e.source.icon + out['channel_id'] = string(e.source.id) if e.source.id + out['channel_last_updated'] = planet_date(e.updated) if e.updated + out['channel_last_updated_822'] = rfc822(e.updated) if e.updated + out['channel_last_updated_iso'] = rfc3399(e.updated) if e.updated + out['channel_link'] = string(e.source.link) if e.source.link + out['channel_logo'] = string(e.source.logo) if e.source.logo + out['channel_message'] = string(e.source.message) if e.source.message + out['channel_name'] = string(e.source.name) if e.source.name + out['channel_rights'] = string(e.source.rights) unless e.source.rights.size == 0 + out['channel_subtitle'] = string(e.source.subtitle) unless e.source.subtitle.size == 0 + out['channel_title'] = string(e.source.title) unless e.source.title.size == 0 + out['channel_title_plain'] = plain(e.source.title) unless e.source.title.size == 0 + out['content'] = string(e.description) if e.description.size != 0 + out['content'] = string(e.summary[0].value) if e.summary[0].value rescue nil + out['content'] = string(e.content[0].value) if e.content[0].value rescue nil + out['content_language'] = string(e.content[0].language) if e.content[0].language rescue nil + out['date'] = planet_date(e.published) if e.published + out['date'] = planet_date(e.updated) if e.updated + out['date_822'] = rfc822(e.published) if e.published + out['date_822'] = rfc822(e.updated) if e.updated + out['date_iso'] = rfc3399(e.published) if e.published + out['date_iso'] = rfc3399(e.updated) if e.updated + out['description'] = string(e.description) if e.description.size != 0 + out['enclosure_href'] = string(e.enclosure_href) if e.enclosure_href + out['enclosure_length'] = string(e.enclosure_length) if e.enclosure_length + out['enclosure_type'] = string(e.enclosure_type) if e.enclosure_type + out['id'] = string(e.id) if e.id + out['link'] = string(e.link) if e.link + out['new_channel'] = string(e.source.id) if e.source.id + out['new_date'] = new_date(e.published) if e.published + out['new_date'] = new_date(e.updated) if e.updated + out['published'] = planet_date(e.published) if e.published + out['published_822'] = rfc822(e.published) if e.published + out['published_iso'] = rfc3399(e.published) if e.published + out['rights'] = string(e.rights) unless e.rights.size == 0 + out['source'] = string(e.source) if e.source + out['summary_language'] = string(e.summary_detail.language) if e.summary_detail.language rescue nil + out['title'] = string(e.title) unless e.title.size == 0 + out['title_language'] = string(e.title_detail.language) if e.title_detail.language rescue nil + out['title_plain'] = plain(e.title) unless e.title.size == 0 + out['updated'] = planet_date(e.updated) if e.updated + out['updated_822'] = rfc822(e.updated) if e.updated + out['updated_iso'] = rfc3399(e.updated) if e.updated + return out + end + + def haml_info(source) + # Get template information from harvest output + + config = Planet.config['Planet'] + + # Add feed and items attributes using harvest + doc = Planet.add_attrs(source) + doc.attributes['xml:base'] = 'http://127.0.0.1:8097/' + + # apply mapping rules to convert harvest UserDict to haml input + output = {'channels' => [], 'items' => []} + + doc.feed.sources.each { |f| output['channels'] << map_feed(f) } + + output['channels'].sort! { |a, b| a['name'] <=> b['name'] rescue 0 } + + doc.entries.each { |e| output['items'] << map_entry(e) } + + # synthesize isPermaLink attribute + output['items'].each { |item| + if item['id'] == item['link'] + item['guid_isPermaLink']='true' + else + item['guid_isPermaLink']='false' + end + } + + # feed level information + output['generator'] = config['generator_uri'] + output['name'] = config['name'] + output['link'] = config['link'] + output['owner_name'] = config['owner_name'] + output['owner_email'] = config['owner_email'] + if config['feed'] + output['feed'] = config['feed'] + output['feedtype'] = config['feed'].include?('rss') ? 'rss' : 'atom' + end + + # date/time information + date = Time.now.getgm.to_s + output['date'] = planet_date(date) + output['date_iso'] = rfc3399(date) + output['date_822'] = rfc822(date) + + # remove new_dates and new_channels that aren't "new" + date = channel = nil + output['items'].each { |item| + if item.has_key? 'new_date' + if item['new_date'] == date + item.delete('new_date') + else + date = item['new_date'] + end + end + + if item.has_key? 'new_channel' + if item['new_channel'] == channel and not item.has_key? 'new_date' + item.delete('new_channel') + else + channel = item['new_channel'] + end + end + } + + return output + end + + def process stylesheet, feed + + # convert a string stylesheet to a file + if stylesheet.index('%') + require 'tempfile' + file = Tempfile.open("style") + begin + file.write(stylesheet) + file.close + return process(file.path, feed) + ensure + file.unlink + end + end + + # process a feed using a haml template + template = open(stylesheet).read + haml_engine = Haml::Engine.new(template) + context = haml_info(feed) + return haml_engine.to_html(Object.new, context) + end + +# Methods for debugging +# + def print_doc(doc) + formatter = REXML::Formatters::Pretty.new( 2 ) + xml_file = File.open( "dump.xml", "a" ) + formatter.write( doc, xml_file ) + end + + def dump_feed f + result = "------------------------------\n" + result << "Investigating feed, class #{f.class}\n" + result << "author: #{f['author']}\n" if f['author'] + result << "author_name: #{f['author_name']}\n" if f['author_name'] + result << "icon: #{f['icon']}\n" if f['icon'] + result << "id: #{f['id']}\n" if f['id'] + result << "last_updated: #{f['last_updated']}\n" if f['last_updated'] + result << "last_updated_822: #{f['last_updated_822']}\n" if f['last_updated_822'] + result << "last_updated_iso: #{f['last_updated_iso']}\n" if f['last_updated_iso'] + result << "link: #{f['link']}\n" if f['link'] + result << "logo: #{f['logo']}\n" if f['logo'] + result << "message: #{f['message']}\n" if f['message'] + result << "name: #{f['name']}\n" if f['name'] + result << "rights: #{f['rights']}\n" if f['rights'].length > 0 + result << "subtitle: #{f['subtitle']}\n" if f['subtitle'].length > 0 + result << "title: #{f['title']}\n" if f['title'].length > 0 + result << "title_plain: #{f['title']}\n" if f['title'].length > 0 + result << "................................\n\n" + return result + end + + def dump_entry e + result = "------------------------------\n" + result << "Investigating entry, class #{e.class}\n" + result << "author: #{e['author']}\n" if e['author'] + result << "author_name: #{e['author_name']}\n" if e['author_name'] + result << "author_email: #{e['author_email']}\n" if e['author_email'] + result << "author_uri: #{e['author_uri']}\n" if e['author_uri'] + result << "channel_author: #{e['channel_author']}\n" if e['channel_author'] + result << "channel_author_name: #{e['channel_author_name']}\n" if e['channel_author_name'] + result << "channel_icon: #{e['channel_icon']}\n" if e['channel_icon'] + result << "channel_id: #{e['channel_id']}\n" if e['channel_id'] + result << "channel_last_updated: #{e['channel_last_updated']}\n" if e['channel_last_updated'] + result << "channel_last_updated_822: #{e['channel_last_updated_822']}\n" if e['channel_last_updated_822'] + result << "channel_last_updated_iso: #{e['channel_last_updated_iso']}\n" if e['channel_last_updated_iso'] + result << "channel_link: #{e['channel_link']}\n" if e['channel_link'] + result << "channel_logo: #{e['channel_logo']}\n" if e['channel_logo'] + result << "channel_message: #{e['channel_message']}\n" if e['channel_message'] + result << "channel_name: #{e['channel_name']}\n" if e['channel_name'] + result << "channel_rights: #{e['channel_rights']}\n" if e['channel_rights'] + result << "channel_subtitle: #{e['channel_subtitle']}\n" if e['channel_subtitle'] + result << "channel_title: #{e['channel_title']}\n" if e['channel_title'] + result << "channel_title_plain: #{e['channel_title']}\n" if e['channel_title'] + result << "content: #{e['content']}\n" if e['content'] + result << "content_language: #{e['content_language']}\n" if e['content_language'] + result << "date: #{e['published']}\n" if e['published'] + result << "date: #{e['updated']}\n" if e['updated'] + result << "date_822: #{e['date_822']}\n" if e['date_822'] + result << "date_iso: #{e['date_iso']}\n" if e['date_iso'] + result << "description: #{e['description']}\n" if e['description'] + result << "enclosure_href: #{e['enclosure_href']}\n" if e['enclosure_href'] + result << "enclosure_length: #{e['enclosure_length']}\n" if e['enclosure_length'] + result << "enclosure_type: #{e['enclosure_type']}\n" if e['enclosure_type'] + result << "id: #{e['id']}\n" if e['id'] + result << "link: #{e['link']}\n" if e['link'] + result << "new_channel: #{e['new_channel']}\n" if e['new_channel'] + result << "new_date: #{e['new_date']}\n" if e['new_date'] + result << "published: #{e['published']}\n" if e['published'] + result << "published_822: #{e['published_822']}\n" if e['published_822'] + result << "published_iso: #{e['published_iso']}\n" if e['published_iso'] + result << "rights: #{e['rights']}\n" if e['rights'] + result << "source: #{e['source']}\n" if e['source'] + result << "summary_language: #{e['summary_language']}\n" if e['summary_language'] + result << "title: #{e['title']}\n" if e['title'] + result << "title_language: #{e['title_language']}\n" if e['title_language'] + result << "title_plain: #{e['title']}\n" if e['title'] + result << "updated: #{e['updated']}\n" if e['updated'] + result << "updated_822: #{e['updated_822']}\n" if e['updated_822'] + result << "updated_iso: #{e['updated_iso']}\n" if e['updated_iso'] + result << "................................\n\n" + return result + end +end \ No newline at end of file diff --git a/planet/harvest.rb b/planet/harvest.rb old mode 100644 new mode 100755 index 20c24bf..dcfcc98 --- a/planet/harvest.rb +++ b/planet/harvest.rb @@ -6,7 +6,12 @@ def Planet.harvest source doc = Planet::Transmogrify.parse(open(source)) doc.attributes['xml:base'] = source - # augment the document with feed parser attributes + Planet.add_attrs(doc) + end + + # Augment a document with feed parser attributes + def Planet.add_attrs doc + class << doc attr_accessor :feed, :entries end @@ -75,7 +80,7 @@ def UserDict.text_construct *names end class CommonElements < UserDict - text_element :id + text_element :id, :updated, :published alias :guid :id text_construct :rights @@ -111,14 +116,6 @@ def contributors @node.elements.to_a('contributor').map {|node| Author.new(node)} end - def categories - tags.map {|tag| [tag.scheme, tag.term]} - end - - def category - tags.first.term rescue nil - end - def author author_detail.to_s end @@ -145,6 +142,24 @@ def generator def generator_detail Generator.new(@node.elements['generator']) end + + def message + element = @node.elements['planet:message'] + element ? element.texts.map {|t| t.value}.join : nil + end + + def name + element = @node.elements['planet:name'] + element ? element.texts.map {|t| t.value}.join : nil + end + + def sources + @node.elements.to_a('planet:source').map {|node| Feed.new(node)} + end + + def url + links.select {|link| link.rel=='self'}.first.href rescue nil + end end class Entry < CommonElements @@ -156,6 +171,22 @@ def content @node.elements.to_a('content').map {|node| TextConstruct.new(node)} end + def enclosure_href + enclosures.first.href rescue nil + end + + def enclosure_length + enclosures.first.length rescue nil + end + + def enclosure_type + if enclosures.first.is_a?(Planet::Link) + return enclosures.first.type + else + return nil + end + end + def enclosures links.select {|link| link.rel == 'enclosure'} end @@ -214,6 +245,10 @@ def base url_norm(@node.xmlbase) end + def language + @node.attributes['xml:lang'] + end + private # DOM to string @@ -255,7 +290,11 @@ def uri end def to_s - email ? "#{name} (#{email})" : "#{name}" + if name + email ? "#{name} (#{email})" : "#{name}" + else + "#{email}" + end end alias :url :uri diff --git a/planet/publisher.rb b/planet/publisher.rb new file mode 100644 index 0000000..fa933f5 --- /dev/null +++ b/planet/publisher.rb @@ -0,0 +1,40 @@ +require 'planet/config' +require 'planet/hamlformatter' +require 'planet/xsltformatter' + +# a TemplatePublisher coordinates processing of the feed with provided templates +class TemplatePublisher + + ALLOWED_TEMPLATES = %w[xslt haml] + + def initialize + @formatters = { 'haml' => HamlFormatter.new, + 'xslt' => XsltFormatter.new } + end + + def publish_feed(template_files, feed) + config = Planet.config['Planet'] + output_dir = config['output_dir'] || '.' + + # loop through the listed templates + template_files.split.each do |template| + next unless template =~ /^(.* \. .*) \. (\w+)/x + + # skip templates that aren't supported + unless ALLOWED_TEMPLATES.include?($2) then + Planet.log.warn "#{$2}: not yet supported" + next + end + + # pick the formatter + @formatter = @formatters[$2] + + # process the template + File.open(File.join(output_dir,$1),'w') do |file| + Planet.log.info "Processing template #{template}" + file.write @formatter.process(template, feed) + end + end + end + + end \ No newline at end of file diff --git a/planet/splice.rb b/planet/splice.rb index da97b7b..28ed854 100644 --- a/planet/splice.rb +++ b/planet/splice.rb @@ -1,8 +1,8 @@ require 'planet/config' -require 'planet/style' require 'planet/xmlparser' require 'fileutils' require 'time' +require 'planet/publisher' module Planet @@ -90,17 +90,6 @@ def Planet.splice end # apply templates - config['template_files'].split.each do |template| - next unless template =~ /^ .* \/ (.*) \. (\w+)/x - - if $2 != 'xslt' - Planet.log.warn "#{$2}: not yet supported" - else - File.open(File.join(output_dir,$1),'w') do |file| - Planet.log.info "Processing template #{template}" - file.write Planet::Xslt.process(template, feed) - end - end - end + TemplatePublisher.new.publish_feed(config['template_files'], feed) end end diff --git a/planet/style.rb b/planet/xsltformatter.rb old mode 100644 new mode 100755 similarity index 90% rename from planet/style.rb rename to planet/xsltformatter.rb index be672e2..392e54c --- a/planet/style.rb +++ b/planet/xsltformatter.rb @@ -1,5 +1,6 @@ -module Planet - module Xslt +require 'planet/formatter' + + class XsltFormatter < PlanetFormatter begin # http://greg.rubyfr.net/pub/packages/ruby-xslt/files/README.html require 'xml/libxslt' @@ -8,7 +9,7 @@ module Xslt @@processor = :xsltproc end - def Xslt.process stylesheet, doc + def process stylesheet, doc if @@processor == :libxslt translate = XML::XSLT.new @@ -44,4 +45,3 @@ def Xslt.process stylesheet, doc end end -end diff --git a/splice.rb b/splice.rb new file mode 100644 index 0000000..a6184b9 --- /dev/null +++ b/splice.rb @@ -0,0 +1,10 @@ +#!/usr/bin/ruby + +require 'planet/config' +require 'planet/spider' +require 'planet/splice' + +ARGV.each {|arg| Planet.config.read arg} + +#Planet.spider +Planet.splice diff --git a/test/data/filter/haml/author_email.xml b/test/data/filter/haml/author_email.xml new file mode 100755 index 0000000..bdbda8e --- /dev/null +++ b/test/data/filter/haml/author_email.xml @@ -0,0 +1,13 @@ + + + + + + john@example.com + + + + diff --git a/test/data/filter/haml/author_name.xml b/test/data/filter/haml/author_name.xml new file mode 100755 index 0000000..cb1ddb4 --- /dev/null +++ b/test/data/filter/haml/author_name.xml @@ -0,0 +1,13 @@ + + + + + + John Doe + + + + diff --git a/test/data/filter/haml/author_uri.xml b/test/data/filter/haml/author_uri.xml new file mode 100755 index 0000000..d88d30d --- /dev/null +++ b/test/data/filter/haml/author_uri.xml @@ -0,0 +1,13 @@ + + + + + + http://example.com/~john/ + + + + diff --git a/test/data/filter/haml/content_html.xml b/test/data/filter/haml/content_html.xml new file mode 100755 index 0000000..0ba4ac2 --- /dev/null +++ b/test/data/filter/haml/content_html.xml @@ -0,0 +1,10 @@ + + + + + D&eacute;tente + + diff --git a/test/data/filter/haml/content_lang.xml b/test/data/filter/haml/content_lang.xml new file mode 100755 index 0000000..4b41832 --- /dev/null +++ b/test/data/filter/haml/content_lang.xml @@ -0,0 +1,10 @@ + + + + + foo + + diff --git a/test/data/filter/haml/content_text.xml b/test/data/filter/haml/content_text.xml new file mode 100755 index 0000000..ad2cbc9 --- /dev/null +++ b/test/data/filter/haml/content_text.xml @@ -0,0 +1,10 @@ + + + + + AT&T + + diff --git a/test/data/filter/haml/content_xhtml.xml b/test/data/filter/haml/content_xhtml.xml new file mode 100755 index 0000000..b20521b --- /dev/null +++ b/test/data/filter/haml/content_xhtml.xml @@ -0,0 +1,13 @@ + + + + + +
A very bad day
+
+
+
+ diff --git a/test/data/filter/haml/content_xhtml2.xml b/test/data/filter/haml/content_xhtml2.xml new file mode 100755 index 0000000..d43256d --- /dev/null +++ b/test/data/filter/haml/content_xhtml2.xml @@ -0,0 +1,13 @@ + + + + + +
+
+
+
+ diff --git a/test/data/filter/haml/enclosure_href.xml b/test/data/filter/haml/enclosure_href.xml new file mode 100755 index 0000000..98b77f7 --- /dev/null +++ b/test/data/filter/haml/enclosure_href.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/enclosure_length.xml b/test/data/filter/haml/enclosure_length.xml new file mode 100755 index 0000000..e4a4191 --- /dev/null +++ b/test/data/filter/haml/enclosure_length.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/enclosure_type.xml b/test/data/filter/haml/enclosure_type.xml new file mode 100755 index 0000000..32f35ef --- /dev/null +++ b/test/data/filter/haml/enclosure_type.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/feed_feed.ini b/test/data/filter/haml/feed_feed.ini new file mode 100755 index 0000000..ec81c0b --- /dev/null +++ b/test/data/filter/haml/feed_feed.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: feed == 'http://example.com/atom.xml' and feedtype == 'atom' +; + +[Planet] +feed: http://example.com/atom.xml diff --git a/test/data/filter/haml/feed_generator.ini b/test/data/filter/haml/feed_generator.ini new file mode 100755 index 0000000..2992793 --- /dev/null +++ b/test/data/filter/haml/feed_generator.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: generator == 'http://example.com/planet' +; + +[Planet] +generator_uri: http://example.com/planet diff --git a/test/data/filter/haml/feed_link.ini b/test/data/filter/haml/feed_link.ini new file mode 100755 index 0000000..49b509a --- /dev/null +++ b/test/data/filter/haml/feed_link.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: link == 'http://example.com/planet' +; + +[Planet] +link: http://example.com/planet diff --git a/test/data/filter/haml/feed_name.ini b/test/data/filter/haml/feed_name.ini new file mode 100755 index 0000000..f8e5b06 --- /dev/null +++ b/test/data/filter/haml/feed_name.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: name == 'My Planet' +; + +[Planet] +name: My Planet diff --git a/test/data/filter/haml/feed_owner_email.ini b/test/data/filter/haml/feed_owner_email.ini new file mode 100755 index 0000000..5b90a8c --- /dev/null +++ b/test/data/filter/haml/feed_owner_email.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: owner_email == 'me@example.com' +; + +[Planet] +owner_email: me@example.com diff --git a/test/data/filter/haml/feed_owner_name.ini b/test/data/filter/haml/feed_owner_name.ini new file mode 100755 index 0000000..3fec36e --- /dev/null +++ b/test/data/filter/haml/feed_owner_name.ini @@ -0,0 +1,7 @@ +; +; Description: id +; Expect: owner_name == 'me' +; + +[Planet] +owner_name: me diff --git a/test/data/filter/haml/id.xml b/test/data/filter/haml/id.xml new file mode 100755 index 0000000..55da7af --- /dev/null +++ b/test/data/filter/haml/id.xml @@ -0,0 +1,11 @@ + + + + + http://example.com/1 + + + diff --git a/test/data/filter/haml/id_only_content.xml b/test/data/filter/haml/id_only_content.xml new file mode 100755 index 0000000..41f78af --- /dev/null +++ b/test/data/filter/haml/id_only_content.xml @@ -0,0 +1,13 @@ + + + + + + content + + + + diff --git a/test/data/filter/haml/id_only_description.xml b/test/data/filter/haml/id_only_description.xml new file mode 100755 index 0000000..204f992 --- /dev/null +++ b/test/data/filter/haml/id_only_description.xml @@ -0,0 +1,13 @@ + + + + + + description + + + + diff --git a/test/data/filter/haml/id_only_link.xml b/test/data/filter/haml/id_only_link.xml new file mode 100755 index 0000000..ac5bf23 --- /dev/null +++ b/test/data/filter/haml/id_only_link.xml @@ -0,0 +1,13 @@ + + + + + + http://example.com/1 + + + + diff --git a/test/data/filter/haml/id_only_title.xml b/test/data/filter/haml/id_only_title.xml new file mode 100755 index 0000000..4dab3e4 --- /dev/null +++ b/test/data/filter/haml/id_only_title.xml @@ -0,0 +1,13 @@ + + + + + + title + + + + diff --git a/test/data/filter/haml/link_href.xml b/test/data/filter/haml/link_href.xml new file mode 100755 index 0000000..77a8964 --- /dev/null +++ b/test/data/filter/haml/link_href.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/link_rel.xml b/test/data/filter/haml/link_rel.xml new file mode 100755 index 0000000..77a8964 --- /dev/null +++ b/test/data/filter/haml/link_rel.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/link_type.xml b/test/data/filter/haml/link_type.xml new file mode 100755 index 0000000..77a8964 --- /dev/null +++ b/test/data/filter/haml/link_type.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/test/data/filter/haml/new_channel.xml b/test/data/filter/haml/new_channel.xml new file mode 100755 index 0000000..b3282a2 --- /dev/null +++ b/test/data/filter/haml/new_channel.xml @@ -0,0 +1,34 @@ + + + + + + http://example.com/ + + + + + http://example.com/ + + + + + http://example.org/ + + + + + http://example.com/ + + + + http://example.com/ + + + http://example.org/ + + + diff --git a/test/data/filter/haml/new_channel_date.xml b/test/data/filter/haml/new_channel_date.xml new file mode 100755 index 0000000..e44534b --- /dev/null +++ b/test/data/filter/haml/new_channel_date.xml @@ -0,0 +1,35 @@ + + + + + 2004-02-28T18:14:55Z + + http://example.com/ + + + + 2004-02-28T14:14:55Z + + http://example.com/ + + + + 2004-02-27T14:14:55Z + + http://example.org/ + + + + 2004-02-26T14:14:55Z + + http://example.org/ + + + + http://example.com/ + + + diff --git a/test/data/filter/haml/new_date.xml b/test/data/filter/haml/new_date.xml new file mode 100755 index 0000000..40cc80e --- /dev/null +++ b/test/data/filter/haml/new_date.xml @@ -0,0 +1,23 @@ + + + + + 2004-02-28T18:14:55Z + + + 2004-02-28T14:14:55Z + + + 2004-02-27T14:14:55Z + + + 2004-02-26T14:14:55Z + + + http://example.com/ + + + diff --git a/test/data/filter/haml/planet_name.xml b/test/data/filter/haml/planet_name.xml new file mode 100755 index 0000000..3c5b070 --- /dev/null +++ b/test/data/filter/haml/planet_name.xml @@ -0,0 +1,16 @@ + + + + + + foo + + + + foo + + + diff --git a/test/data/filter/haml/published.xml b/test/data/filter/haml/published.xml new file mode 100755 index 0000000..b7efb67 --- /dev/null +++ b/test/data/filter/haml/published.xml @@ -0,0 +1,11 @@ + + + + + 2004-02-28T18:14:55-08:00 + + + diff --git a/test/data/filter/haml/rights.xml b/test/data/filter/haml/rights.xml new file mode 100755 index 0000000..8dd99bc --- /dev/null +++ b/test/data/filter/haml/rights.xml @@ -0,0 +1,11 @@ + + + + + &copy; 2006 + + + diff --git a/test/data/filter/haml/source_author.xml b/test/data/filter/haml/source_author.xml new file mode 100755 index 0000000..93c2195 --- /dev/null +++ b/test/data/filter/haml/source_author.xml @@ -0,0 +1,20 @@ + + + + + + + John Doe + + + + + + John Doe + + + + diff --git a/test/data/filter/haml/source_icon.xml b/test/data/filter/haml/source_icon.xml new file mode 100755 index 0000000..a25d133 --- /dev/null +++ b/test/data/filter/haml/source_icon.xml @@ -0,0 +1,16 @@ + + + + + + http://www.example.com/favicon.ico + + + + http://www.example.com/favicon.ico + + + diff --git a/test/data/filter/haml/source_id.xml b/test/data/filter/haml/source_id.xml new file mode 100755 index 0000000..612819f --- /dev/null +++ b/test/data/filter/haml/source_id.xml @@ -0,0 +1,16 @@ + + + + + + http://example.com/ + + + + http://example.com/ + + + diff --git a/test/data/filter/haml/source_link.xml b/test/data/filter/haml/source_link.xml new file mode 100755 index 0000000..3a1bc55 --- /dev/null +++ b/test/data/filter/haml/source_link.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/test/data/filter/haml/source_logo.xml b/test/data/filter/haml/source_logo.xml new file mode 100755 index 0000000..9e43049 --- /dev/null +++ b/test/data/filter/haml/source_logo.xml @@ -0,0 +1,16 @@ + + + + + + http://www.example.com/logo.jpg + + + + http://www.example.com/logo.jpg + + + diff --git a/test/data/filter/haml/source_planet_message.xml b/test/data/filter/haml/source_planet_message.xml new file mode 100755 index 0000000..67ae9cb --- /dev/null +++ b/test/data/filter/haml/source_planet_message.xml @@ -0,0 +1,16 @@ + + + + + + foo + + + + foo + + + diff --git a/test/data/filter/haml/source_planet_name.xml b/test/data/filter/haml/source_planet_name.xml new file mode 100755 index 0000000..3c5b070 --- /dev/null +++ b/test/data/filter/haml/source_planet_name.xml @@ -0,0 +1,16 @@ + + + + + + foo + + + + foo + + + diff --git a/test/data/filter/haml/source_rights.xml b/test/data/filter/haml/source_rights.xml new file mode 100755 index 0000000..fb96c14 --- /dev/null +++ b/test/data/filter/haml/source_rights.xml @@ -0,0 +1,16 @@ + + + + + + &copy; 2006 + + + + &copy; 2006 + + + diff --git a/test/data/filter/haml/source_subtitle.xml b/test/data/filter/haml/source_subtitle.xml new file mode 100755 index 0000000..7472e91 --- /dev/null +++ b/test/data/filter/haml/source_subtitle.xml @@ -0,0 +1,16 @@ + + + + + + snarky phrase + + + + snarky phrase + + + diff --git a/test/data/filter/haml/source_title.xml b/test/data/filter/haml/source_title.xml new file mode 100755 index 0000000..be9afcd --- /dev/null +++ b/test/data/filter/haml/source_title.xml @@ -0,0 +1,16 @@ + + + + + + visible name + + + + visible name + + + diff --git a/test/data/filter/haml/source_updated.xml b/test/data/filter/haml/source_updated.xml new file mode 100755 index 0000000..a2ca53f --- /dev/null +++ b/test/data/filter/haml/source_updated.xml @@ -0,0 +1,16 @@ + + + + + + 2004-02-28T18:14:55-08:00 + + + + 2004-02-28T18:14:55-08:00 + + + diff --git a/test/data/filter/haml/summary_html.xml b/test/data/filter/haml/summary_html.xml new file mode 100755 index 0000000..f335c17 --- /dev/null +++ b/test/data/filter/haml/summary_html.xml @@ -0,0 +1,10 @@ + + + + + D&eacute;tente + + diff --git a/test/data/filter/haml/summary_lang.xml b/test/data/filter/haml/summary_lang.xml new file mode 100755 index 0000000..eb30b5c --- /dev/null +++ b/test/data/filter/haml/summary_lang.xml @@ -0,0 +1,10 @@ + + + + + foo + + diff --git a/test/data/filter/haml/summary_text.xml b/test/data/filter/haml/summary_text.xml new file mode 100755 index 0000000..0984402 --- /dev/null +++ b/test/data/filter/haml/summary_text.xml @@ -0,0 +1,10 @@ + + + + + AT&T + + diff --git a/test/data/filter/haml/summary_xhtml.xml b/test/data/filter/haml/summary_xhtml.xml new file mode 100755 index 0000000..02c9db3 --- /dev/null +++ b/test/data/filter/haml/summary_xhtml.xml @@ -0,0 +1,13 @@ + + + + + +
A very bad day
+
+
+
+ diff --git a/test/data/filter/haml/title_html.xml b/test/data/filter/haml/title_html.xml new file mode 100755 index 0000000..544ed38 --- /dev/null +++ b/test/data/filter/haml/title_html.xml @@ -0,0 +1,10 @@ + + + + + D&eacute;tente + + diff --git a/test/data/filter/haml/title_lang.xml b/test/data/filter/haml/title_lang.xml new file mode 100755 index 0000000..325b913 --- /dev/null +++ b/test/data/filter/haml/title_lang.xml @@ -0,0 +1,10 @@ + + + + + foo + + diff --git a/test/data/filter/haml/title_text.xml b/test/data/filter/haml/title_text.xml new file mode 100755 index 0000000..cec48b2 --- /dev/null +++ b/test/data/filter/haml/title_text.xml @@ -0,0 +1,10 @@ + + + + + AT&T + + diff --git a/test/data/filter/haml/title_xhtml.xml b/test/data/filter/haml/title_xhtml.xml new file mode 100755 index 0000000..dae2191 --- /dev/null +++ b/test/data/filter/haml/title_xhtml.xml @@ -0,0 +1,13 @@ + + + + + + <div xmlns="http://www.w3.org/1999/xhtml">A <b>very</b> bad day</div> + + + + diff --git a/test/data/filter/haml/updated.xml b/test/data/filter/haml/updated.xml new file mode 100755 index 0000000..1230e7c --- /dev/null +++ b/test/data/filter/haml/updated.xml @@ -0,0 +1,11 @@ + + + + + 2004-02-28T18:14:55-08:00 + + + diff --git a/test/haml.rb b/test/haml.rb new file mode 100755 index 0000000..81c94a8 --- /dev/null +++ b/test/haml.rb @@ -0,0 +1,91 @@ +require 'test/unit' +require 'planet/harvest' +require 'planet/config' +require 'planet/hamlformatter' + + +H_FILES = [ 'haml/*', 'haml/*.ini'] +H_FILES.map! {|name| name.index('.') ? name : name+".xml"} +H_TESTS = H_FILES.map {|arg| Dir[File.join('test/data/filter',arg)]} + +# test cases are in Python syntax, convert to something that eval will accept +def python2ruby(expr, source) + # unicode strings + expr.gsub! " u'", " '" + expr.gsub! ' u"', ' "' + + # triple strings + expr.gsub! /"""(.*?)"""/, '%q{\1}' + + # dict to hash + expr.gsub! "': '", "' => '" + + # const to variable + expr.gsub! "Items[", "items[" + expr.gsub! "Channels[", "channels[" + + # true + expr = "true" if expr == "1" + + # differences in XML/URI serializations + name = source.split('/').last.split('.').first + expr.sub!('"','%22') if name == 'missing_quote_in_attr' + expr.sub! '>', '>' if name == 'tag_in_attr' + expr.gsub!('"', "\\\\'").gsub!('"','"') if name == 'quote_in_attr' + + expr +end + +require 'planet/formatter' + +# Add a method for comparing a UserDict with a Python +module Planet + class UserDict + def == dict + dict == Hash[*dict.keys.map {|key| [key,self[key]]}.flatten] + end + def has_key key + send key rescue false + end + end +end + +class HamlTestCase < Test::Unit::TestCase + H_TESTS.flatten.each do |file| + name = file.split('.').first.split('/')[-3..-1].join('_') + define_method "test_#{name}" do + testdata = open(file).read + + case testdata + # for .xml files + when /Description:\s*(.*?)\s*Expect:\s*(.*)\s*/ + desc = python2ruby($2, file) + doc = Planet.harvest(file) + output = HamlFormatter.new.haml_info(doc) + channels = output['channels'] + items = output['items'] if output['items'] + + # for .ini files... any xml will do + when /Description:\s*(.*?)\s*; Expect:\s*(.*)\s*/ + desc = python2ruby($2, file) + Planet.config.read file + doc = Planet.harvest('test/data/filter/haml/new_channel.xml') + output = HamlFormatter.new.haml_info(doc) + + # copy haml hash to environment + feed = output['feed'] + feedtype = output['feedtype'] + generator = output['generator_uri'] + link = output['link'] + name = output['name'] + owner_email = output['owner_email'] + owner_name = output['owner_name'] + else + raise Exception.new('testcase parse error') + end + + test_result = eval desc + assert_equal true, test_result, message=desc + end + end +end \ No newline at end of file diff --git a/themes/intertwingly/atom.xml.xslt b/themes/intertwingly/atom.xml.xslt new file mode 100644 index 0000000..c57a938 --- /dev/null +++ b/themes/intertwingly/atom.xml.xslt @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + no + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/themes/intertwingly/basic.ini b/themes/intertwingly/basic.ini new file mode 100644 index 0000000..64c2bde --- /dev/null +++ b/themes/intertwingly/basic.ini @@ -0,0 +1,80 @@ +# Every planet needs a [Planet] section +[Planet] +# name: Your planet's name +# link: Link to the main page +# owner_name: Your name +# owner_email: Your e-mail address +name = Planet Test +link = http://planettest.org/ +owner_name = JR +owner_email = jr@planettest.com +feed = http://planettest.org/planet/atom.xml + +# cache_directory: Where cached feeds are stored +# log_level: One of DEBUG, INFO, WARNING, ERROR or CRITICAL +# feed_timeout: number of seconds to wait for any given feed +cache_directory = . +log_level = INFO +feed_timeout = 30 + +# items_per_page: How many items to put on each page +template_files = index.html.haml validate.html.xslt atom.xml.xslt rss10.xml.haml +items_per_page = 50 +date_format = "%a %b $d $H:%M:%S %Y" +new_date_format = "%A, %d %B %Y" +filters = foo + +[index.html.haml] +days_per_page = 7 + +# Any other section defines a feed to subscribe to. The section title +# (in the []s) is the URI of the feed itself. A section can also be +# have any of the following options: +# +# name: Name of the feed (defaults to the title found in the feed) +# + +[http://intertwingly.net/blog/index.atom] +name = Sam Ruby + +[http://www.tbray.org/ongoing/ongoing.atom] +name = Tim Bray + +[http://16cards.com/feed/atom/] +name = Brandon Smith + +[http://1raindrop.typepad.com/1_raindrop/atom.xml] +name = Gunnar Peterson + +[http://adambosworth.net/feed/atom/] +name = Adam Bosworth + +[http://agylen.com/feed/] +name = Ugo Cei + +[http://allinthehead.com/atom/] +name = Drew McLellan + +[http://anarchogeek.com/feed/atom.xml] +name = Evan Henshaw-Plath + +[http://annevankesteren.nl/feeds/weblog] +name = Anne van Kesteren + +[http://arch.jabber.com/feed/atom/] +name = Joe Hildebrand + +[http://armstrongonsoftware.blogspot.com/feeds/posts/default] +name = Joe Armstrong + +[http://ascher.ca/blog/feed/atom/] +name = David Ascher + +[http://atmanes.blogspot.com/feeds/posts/default] +name = Anne Thomas Manes + +[http://atomenabled.org/atom.xml] +name = AtomEnabled.org + +[http://avantgame.blogspot.com/feeds/posts/default] +name = Jane McGonigal \ No newline at end of file diff --git a/themes/intertwingly/default.css b/themes/intertwingly/default.css new file mode 100755 index 0000000..85c42b3 --- /dev/null +++ b/themes/intertwingly/default.css @@ -0,0 +1,511 @@ +/* + * Written by Stefano Mazzocchi + */ + +/* ----------------------------- 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 { +} + +a.inactive { + color: #558; +} + +a.rising { + font-weight: bold; +} + +a[rel~='license'] { + text-decoration: none; +} + +body > 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 h2 a img { + margin-bottom: 2px; + vertical-align: middle; +} + +#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 ul li a:visited { + color: #000; +} + +#sidebar ul li ul { + display: none; +} + +#sidebar ul li { + position: relative; +} + +#sidebar ul li:hover ul { + background-color: #EEE; + border: 2px solid #BBB; + color:#000; + display: block; + margin-left: -300px; + margin-right: 115px; + padding: 10px; + padding-left: 25px; + position: absolute; + right: 80px; + top: -12px; + z-index: 1; +} + +#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; +} + +#sidebar input[name=q] { + margin: 4px 0 0 24px; +} + +/* ---------------------------- Footer --------------------------- */ + +#footer ul { + margin: 0 20px 0 -25px; + padding: 0; +} + +#footer li { + margin: 0; + padding: 0; + list-style: none; + display: inline; +} + +#footer ul li ul { + display: none; +} + +#footer img { + display: none; +} + +/* ----------------------------- Body ---------------------------- */ + +#body { + margin-top: 10px; +} + +.admin { + text-align: right; +} + +#body > h2 { + 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 */ +p { + clear: none !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; +} + +.numberedEq span, .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; +} + +/* Tantek */ +ul.tags,ul.tags li,h4.tags { + display:inline; + font-size: x-small +} + +ul.tags a:link, ul.tags a:visited { + color:green +} + +a[rel='tag'] img { + border: 0; +} + +/* DiveIntoMark */ +.framed { + float: none; +} + +/* BurningBird */ +.update:before { + content: 'Update'; + font-weight: bold; +} + +.update { + margin: 2em; + padding: 0 1em 0 1em; + background: #eee; + border: 1px solid #aaa; +} + +/* ----------------------------- 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; +} \ No newline at end of file diff --git a/themes/intertwingly/favicon.ico b/themes/intertwingly/favicon.ico new file mode 100644 index 0000000..490e613 Binary files /dev/null and b/themes/intertwingly/favicon.ico differ diff --git a/themes/intertwingly/feed-icon-10x10.png b/themes/intertwingly/feed-icon-10x10.png new file mode 100644 index 0000000..cc869bc Binary files /dev/null and b/themes/intertwingly/feed-icon-10x10.png differ diff --git a/themes/intertwingly/index.html.haml b/themes/intertwingly/index.html.haml new file mode 100755 index 0000000..083c4d0 --- /dev/null +++ b/themes/intertwingly/index.html.haml @@ -0,0 +1,60 @@ + +%html{:xmlns => "http://www.w3.org/1999/xhtml"} + %head + %meta{"http-equiv" => "Content-Type", :content => "text/html;charset=utf-8"} + %link{:rel => "stylesheet", :href => "default.css", :type => "text/css"} + %title= name + %meta{:name => "robots", :content => "noindex,nofollow"} + %meta{:name => "generator", :content => "#{generator}"} + - if feedtype + %link{:rel => "alternate", :href => "#{feed}", :title => "#{name}", :type => "application/#{feedtype}+xml"} + %script{ :type => "text/javascript", :src => "personalize.js"} + + %body + %h1= name + + #body + - for item in items + - if item['new_date'] + %h2 + %time{ :datetime => "#{item['date_iso']}" }= item['new_date'] + + -# TODO add content language to class + .news + %h3 + - if item['channel_icon'] + %img{ :src => "#{item['channel_icon']}"} + %a{ :href => "#{item['channel_link']}", :title => "#{item['channel_name']}" }= item['channel_name'] + = "-" + %a{ :href => "#{item['link']}", :title => "#{item['title']}" }= item['title'] + .content= item['content'] + .permalink + -if item['channel_rights'] + %a{ :title => "#{item['channel_rights']}" }= "©" + %a{ :href => "#{item['link']}" } + = "#{item['channel_name']} at " + %time{:datetime =>"#{item['updated_iso']}", :title => "GMT"}= item['date'] + -else + %a{ :href => "#{item['link']}" } + = "by #{item['channel_name']} at " + %time{:datetime =>"#{item['updated_iso']}", :title => "GMT"}= item['date'] + + #sidebar + %h2 Info + %dl + %dt Last updated: + %dd + %time{:datetime =>"#{date_iso}", :title => "GMT"}= date + %dt Powered by: + %dd + %a{:href =>"http://intertwingly.net/code/venus/"} + %img{ :src =>"images/venus.png", :alt => "Venus", :width => "80", :height => "15", :border => "0" } + + #footer + %h2 Subscriptions + %ul + - for source in channels + %li + %a{ :title => "subscribe", :href => "#{source['url']}" } + %img{ :src =>"images/feed-icon-10x10.png", :alt => "(feed)" } + %a{ :href => "#{source['link']}", :title => "#{source['title_plain']}" }= source['name'] diff --git a/themes/intertwingly/personalize.js b/themes/intertwingly/personalize.js new file mode 100644 index 0000000..f89b714 --- /dev/null +++ b/themes/intertwingly/personalize.js @@ -0,0 +1,299 @@ +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) continue; + 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 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; + if (event.originalTarget && + event.originalTarget.nodeName.toLowerCase() == 'input' && + event.originalTarget.id != 'navkeys') return; + + if (!document.documentElement) return; + if (!entries[0].anchor || !entries[0].anchor.offsetTop) return; + + key=event.keyCode; + 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 + "="; + if (!document.cookie) return; + 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) { + var sidebar = document.getElementById('sidebar'); + if (!sidebar) return; + + var h2 = null; + for (var i=entries.length; --i>=0;) { + if (document.getElementById("news-" + i)) break; + if (entries[i].parent.offsetTop > 0) { + var a = entries[i].anchor = document.createElement('a'); + a.id = "news-" + i; + entries[i].parent.insertBefore(a, entries[i].parent.firstChild); + if (h2 == null) h2 = document.createElement('h2'); + } + } + + if (h2 != null && !document.getElementById("navkeys")) { + 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; + } +} + +// Parse an HTML5-liberalized version of RFC 3339 datetime values +Date.parseRFC3339 = function (string) { + var date=new Date(); + date.setTime(0); + var match = string.match(/(\d{4})-(\d\d)-(\d\d)\s*(?:[\sT]\s*(\d\d):(\d\d)(?::(\d\d))?(\.\d*)?\s*(Z|([-+])(\d\d):(\d\d))?)?/); + if (!match) return; + if (match[2]) match[2]--; + if (match[7]) match[7] = (match[7]+'000').substring(1,4); + var field = [null,'FullYear','Month','Date','Hours','Minutes','Seconds','Milliseconds']; + for (var i=1; i<=7; i++) if (match[i]) date['setUTC'+field[i]](match[i]); + if (match[9]) date.setTime(date.getTime()+ + (match[9]=='-'?1:-1)*(match[10]*3600000+match[11]*60000) ); + return date.getTime(); +} + +// convert datetime to local date +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.parseRFC3339(element.getAttribute('datetime'))); + if (!date.getTime()) return; + + 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 times = document.getElementsByTagName('time'); + + for (var i=0; i "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 'xmlns:dc' => "http://purl.org/dc/elements/1.1/", 'xmlns:foaf' => "http://xmlns.com/foaf/0.1/", 'xmlns:content' => "http://purl.org/rss/1.0/modules/content/", 'xmlns' => "http://purl.org/rss/1.0/" } + %channel{ 'rdf:about' => "#{link}" } + %title + = name + %link + = link + %description + = name + = "- #{link}" + %items + %rdf:seq + - for item in items + %rdf:li{ 'rdf:resource' => "#{item["id"]}" } + - for item in items + %item{ 'rdf:about' => "#{item["id"]}" } + %title + = item['channel_name'] + - if item['title'] + = ": #{item['title_plain']}" + %link + = link + - if item['content'] + %content:encoded + = item['content'] + %dc:date + = item['date_iso'] + - if item['author_name'] + %dc:creator + = item['author_name'] diff --git a/themes/intertwingly/validate.html.xslt b/themes/intertwingly/validate.html.xslt new file mode 100644 index 0000000..0cabdcc --- /dev/null +++ b/themes/intertwingly/validate.html.xslt @@ -0,0 +1,146 @@ + + + + + + + + + <xsl:value-of select="atom:title"/> + + + + + + + + + + + + + + + + + + + + + + + + +
NameFormatNotes
+ + +
+ + + + + rss_0_90 + rss_0_91 + rss_0_91 + rss_1_0 + rss_0_90 + + rss_0_90 + rss_2_0 + rss_2_0 + + + atom_0_3 + atom_1_0 + atom_1_0 + + + + + + + + + background-color:#FCC + + + + + http://feedvalidator.org/check?url= + + + + + + + + + + + + + + http://www.validome.org/rss-atom/validate? + viewSourceCode=1&version= + + &url= + + + + + + + + + + + + + + + + + message + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
+ +
+