#/usr/bin/ruby # # tmpl2xslt is a migration aide that will convert a Planet 2.0 or Venus # htmltmpl file into an XSLT file suitable for use with either Mars or Venus. # require 'html5' def tmpl2xslt tmpl names = { 'author' => 'atom:author/atom:name', 'author_email' => 'atom:author/atom:email', 'author_name' => 'atom:author/atom:name', 'author_uri' => 'atom:author/atom:uri' , 'content_language' => 'atom:content/@xml:lang', 'date' => 'atom:updated/@planet:format', 'date_iso' => 'atom:updated', 'enclosure_href' => "atom:link[@rel='enclosure']/@href", 'enclosure_length' => "atom:link[@rel='enclosure']/@length", 'enclosure_type' => "atom:link[@rel='enclosure']/@type", 'feed' => "atom:link[@rel='self']/@href", 'feedtype' => "atom:link[@rel='self']/@type", 'generator' => 'atom:generator', 'id' => "atom:id", 'last_updated' => 'atom:updated/@planet:format', 'last_updated_iso' => 'atom:updated', 'link' => "atom:link[@rel='alternate']/@href", 'logo' => "atom:logo", 'name' => 'atom:title', 'owner_name' => 'atom:author/atom:name', 'published' => 'atom:published/@planet:format', 'published_iso' => 'atom:published', 'rights' => 'atom:rights', 'subtitle' => 'atom:subtitle', 'title' => 'atom:title', 'title_language' => 'atom:title/@xml:lang', 'title_plain' => 'atom:title/text()', 'summary_language' => 'atom:summary/@xml:lang', 'updated' => 'atom:updated/@planet:format', 'updated_iso' => 'atom:updated', 'url' => "atom:link[@rel='alternate']/@href", } # convert variable names to xpath expressions xpath = proc do |name| case name when *names.keys then names[name] when 'Channels' then 'planet:source' when 'Items' then 'atom:entry' when /^channel_(.*)$/ then if names.has_key?($1) "atom:source/#{names[$1]}" else "atom:source/planet:#{$1}" end when 'new_date' then "substring-before(atom:updated/@planet:format,', ')}, {" + "substring-before(substring-after(atom:updated/@planet:format,', '), ' ')" else STDERR.puts "Unknown variable encountered: #{name}" name end end # kill the DOCTYPE tmpl.sub! /\s*/, '' # enclose line comments in XML/HTML comments tmpl.gsub! /^((#.*\n)+)/, "\n" # special case: feed type already is formatted as a mime type tmpl.gsub! 'application/+xml', '' # template variables tmpl.gsub! //, '' tmpl.gsub! // do if $1 == 'content' '' else "{#{xpath.call($1)}}" end end # template if statements temporarily become pseudo-span elements tmpl.gsub! /<\/TMPL_IF>/, '' tmpl.gsub! // do if $1 == 'new_date' '' elsif $1 == 'new_channel' '' else "" end end # template loop statements temporarily become pseudo-span elements tmpl.gsub! /<\/TMPL_LOOP>/, '' tmpl.gsub! // do "" end # convert template to DOM doc = HTML5.parse(tmpl) # reparent conditional head elements doc.elements.each('html/body/span[meta or link]') {|span| doc.elements['html/head'] << span } doc.elements.each('//*') do |node| # pseudo span elements become xsl control elements if node.name == 'span' if node.attributes['select'] node.name = 'xsl:for-each' elsif node.attributes['test'] node.name = 'xsl:if' end end # choose between content and summary if node.name == 'choose' node.name = 'xsl:choose' child = node.add_element('xsl:when', 'test' => 'atom:content') child.add_element('xsl:apply-templates', 'select' => 'atom:content') child = node.add_element('xsl:when', 'test' => 'atom:summary') child.add_element('xsl:apply-templates', 'select' => 'atom:summary') end # convert scripts to CDATA if node.name == 'script' node.texts.each do |text| if text.value =~ /[&<]/ text.previous_sibling = REXML::CData.new(text.value) text.remove end end end # add namespace to html element if node.name == 'html' node.add_namespace 'http://www.w3.org/1999/xhtml' end # replace string interpretation in text nodes with xsl:value of elements node.texts.each do |text| if text.value =~ /\{.*?\}/ text.value.split(/\{(.*?)\}/).each_with_index do |value, index| if index % 2 == 1 text.previous_sibling = REXML::Element.new('xsl:value-of') text.previous_sibling.attributes['select'] = value elsif !value.empty? text.previous_sibling = REXML::Text.new(value) end end text.remove end end end # Egregious hack: convert all escaped single quotes in XPath # expressions to double quotes. tmpl = doc.to_s.gsub /\{.*?\}| (test|select)='.*?'/ do |xpath| xpath.gsub(''','"') end # wrapt the template in a stylesheet REXML::Document.new < #{tmpl.gsub(/\n\n\n+/,"\n\n")} content
EOF end if $0 == __FILE__ if ARGV.first =~ /.tmpl$/ and ARGV.length == 1 # convert tmpl to stylesheet open(ARGV.first.sub(/.tmpl$/,'.xslt'),'w') do |file| file.write tmpl2xslt(File.open(ARGV.first).read).to_s.gsub('"','"') end else STDERR.puts "Usage: ruby #{__FILE__} index.html.tmpl" exit 86 end end