Export XHTML Pages
When a Web uses one of the Markdown Text Filters, and you export all the pages as a zip file, you'd like the MathML and SVG to render when the pages are viewed locally. This means saving them with a .xhtml extension. Users of non-XHTML-capable browsers or Textile users should still get .html files.
This commit is contained in:
parent
13b7e1d766
commit
0b2a6935a2
6 changed files with 64 additions and 20 deletions
|
@ -172,6 +172,10 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
def xhtml_enabled?
|
||||
in_a_web? and (@web.markup == :markdownMML or @web.markup == :markdownPNG or @web.markup == :markdown)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_robots_metatag
|
||||
|
@ -200,10 +204,6 @@ class ApplicationController < ActionController::Base
|
|||
not @web_name.nil?
|
||||
end
|
||||
|
||||
def xhtml_enabled?
|
||||
in_a_web? and (@web.markup == :markdownMML or @web.markup == :markdownPNG or @web.markup == :markdown)
|
||||
end
|
||||
|
||||
def authorization_needed?
|
||||
not %w( login authenticate feeds published atom_with_headlines atom_with_content).include?(action_name)
|
||||
end
|
||||
|
|
|
@ -68,7 +68,7 @@ class WikiController < ApplicationController
|
|||
|
||||
def export_html
|
||||
stylesheet = File.read(File.join(RAILS_ROOT, 'public', 'stylesheets', 'instiki.css'))
|
||||
export_pages_as_zip('html') do |page|
|
||||
export_pages_as_zip(html_ext) do |page|
|
||||
|
||||
renderer = PageRenderer.new(page.revisions.last)
|
||||
rendered_page = <<-EOL
|
||||
|
@ -90,6 +90,10 @@ class WikiController < ApplicationController
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="pageName">
|
||||
<span class="webName">#{@web.name}</span><br />
|
||||
#{page.plain_name}
|
||||
</h1>
|
||||
#{renderer.display_content_for_export}
|
||||
<div class="byline">
|
||||
#{page.revisions? ? "Revised" : "Created" } on #{ page.revised_at.strftime('%B %d, %Y %H:%M:%S') }
|
||||
|
@ -329,6 +333,15 @@ class WikiController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def html_ext
|
||||
if xhtml_enabled? && request.env.include?('HTTP_ACCEPT') &&
|
||||
Mime::Type.parse(request.env["HTTP_ACCEPT"]).include?(Mime::XHTML)
|
||||
'xhtml'
|
||||
else
|
||||
'html'
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def do_caching?
|
||||
|
@ -364,7 +377,7 @@ class WikiController < ApplicationController
|
|||
end
|
||||
|
||||
def export_pages_as_zip(file_type, &block)
|
||||
|
||||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = File.join(@wiki.storage_path, file_prefix + timestamp + '.zip')
|
||||
|
@ -376,9 +389,9 @@ class WikiController < ApplicationController
|
|||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == 'html'
|
||||
zip_out.put_next_entry 'index.html'
|
||||
zip_out.puts "<html><head>" +
|
||||
if file_type.to_s.downcase == html_ext
|
||||
zip_out.put_next_entry "index.#{html_ext}"
|
||||
zip_out.puts "<html xmlns='http://www.w3.org/1999/xhtml'><head>" +
|
||||
"<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;URL=HomePage.#{file_type}\"></head></html>"
|
||||
end
|
||||
end
|
||||
|
@ -468,5 +481,4 @@ class WikiController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -61,7 +61,7 @@ module Chunk
|
|||
end
|
||||
|
||||
def unmask
|
||||
@content.sub!(/#{mask}/){|s| s.replace @unmask_text}
|
||||
@content.sub!(mask){|s| s.replace @unmask_text}
|
||||
end
|
||||
|
||||
def rendered?
|
||||
|
@ -73,7 +73,7 @@ module Chunk
|
|||
end
|
||||
|
||||
def revert
|
||||
@content.sub!(/#{mask}/){|s| s.replace @text}
|
||||
@content.sub!(mask){|s| s.replace @text}
|
||||
# unregister
|
||||
@content.delete_chunk(self)
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ module Literal
|
|||
|
||||
# A literal chunk that protects HTML tags from wiki rendering.
|
||||
class Tags < AbstractLiteral
|
||||
TAGS_PATTERN = Regexp.new('<[-a-zA-Z]+[^>]*?>', Regexp::MULTILINE)
|
||||
TAGS_PATTERN = Regexp.new('</?[-a-zA-Z:]+\b[^>]*?>', Regexp::MULTILINE)
|
||||
def self.pattern() TAGS_PATTERN end
|
||||
end
|
||||
|
||||
|
@ -32,7 +32,7 @@ module Literal
|
|||
class Math < AbstractLiteral
|
||||
MATH_START = '(\${1,2}|' + Regexp.escape('\[') + '|\\begin\{equation\})'
|
||||
MATH_END = '(\${1,2}|' + Regexp.escape('\]') + '|\\end\{equation\})'
|
||||
MATH_PATTERN = Regexp.new(MATH_START + '([^$]|\\\$)+?' + MATH_END, Regexp::MULTILINE)
|
||||
MATH_PATTERN = Regexp.new(MATH_START + '([^$]|\\\$)+' + MATH_END, Regexp::MULTILINE)
|
||||
def self.pattern() MATH_PATTERN end
|
||||
end
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
case mode
|
||||
when :export
|
||||
if known_file
|
||||
%{<a class="existingWikiWord" title="#{description}" href="#{CGI.escape(name)}.html">#{text}</a>}
|
||||
%{<a class="existingWikiWord" title="#{description}" href="#{CGI.escape(name)}.#{@controller.html_ext}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
|
@ -79,7 +79,7 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
case mode
|
||||
when :export
|
||||
if known_page
|
||||
%{<a class="existingWikiWord" href="#{CGI.escape(name)}.html">#{text}</a>}
|
||||
%{<a class="existingWikiWord" href="#{CGI.escape(name)}.#{@controller.html_ext}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
|
@ -142,5 +142,5 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
%{<span class="deleteWikiWord">[[#{name}:delete]]</span>}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -111,7 +111,39 @@ class WikiControllerTest < Test::Unit::TestCase
|
|||
assert_equal '/wiki1/save/With+%3A+Special+%2F%3E+symbols', form.attributes['action']
|
||||
end
|
||||
|
||||
def test_export_xhtml
|
||||
@request.accept = 'application/xhtml+xml'
|
||||
# rollback homepage to a version that is easier to match
|
||||
@home.rollback(0, Time.now, 'Rick', test_renderer)
|
||||
r = process 'export_html', 'web' => 'wiki1'
|
||||
|
||||
assert_response(:success, bypass_body_parsing = true)
|
||||
assert_equal 'application/zip', r.headers['type']
|
||||
assert_match /attachment; filename="wiki1-xhtml-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
|
||||
r.headers['Content-Disposition']
|
||||
assert_equal 'PK', r.body[0..1], 'Content is not a zip file'
|
||||
|
||||
# Tempfile doesn't know how to open files with binary flag, hence the two-step process
|
||||
Tempfile.open('instiki_export_file') { |f| @tempfile_path = f.path }
|
||||
begin
|
||||
File.open(@tempfile_path, 'wb') { |f| f.write(r.body); @exported_file = f.path }
|
||||
Zip::ZipFile.open(@exported_file) do |zip|
|
||||
assert_equal %w(Elephant.xhtml FirstPage.xhtml HomePage.xhtml MyWay.xhtml NoWikiWord.xhtml Oak.xhtml SmartEngine.xhtml ThatWay.xhtml index.xhtml), zip.dir.entries('.').sort
|
||||
assert_match /.*<html .*All about elephants.*<\/html>/,
|
||||
zip.file.read('Elephant.xhtml').gsub(/\s+/, ' ')
|
||||
assert_match /.*<html .*All about oak.*<\/html>/,
|
||||
zip.file.read('Oak.xhtml').gsub(/\s+/, ' ')
|
||||
assert_match /.*<html .*First revision of the.*HomePage.*end.*<\/html>/,
|
||||
zip.file.read('HomePage.xhtml').gsub(/\s+/, ' ')
|
||||
assert_equal '<html xmlns=\'http://www.w3.org/1999/xhtml\'><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.xhtml"></head></html> ', zip.file.read('index.xhtml').gsub(/\s+/, ' ')
|
||||
end
|
||||
ensure
|
||||
File.delete(@tempfile_path) if File.exist?(@tempfile_path)
|
||||
end
|
||||
end
|
||||
|
||||
def test_export_html
|
||||
@request.accept = 'tex/html'
|
||||
# rollback homepage to a version that is easier to match
|
||||
@home.rollback(0, Time.now, 'Rick', test_renderer)
|
||||
r = process 'export_html', 'web' => 'wiki1'
|
||||
|
@ -134,19 +166,19 @@ class WikiControllerTest < Test::Unit::TestCase
|
|||
zip.file.read('Oak.html').gsub(/\s+/, ' ')
|
||||
assert_match /.*<html .*First revision of the.*HomePage.*end.*<\/html>/,
|
||||
zip.file.read('HomePage.html').gsub(/\s+/, ' ')
|
||||
assert_equal '<html><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html> ', zip.file.read('index.html').gsub(/\s+/, ' ')
|
||||
assert_equal '<html xmlns=\'http://www.w3.org/1999/xhtml\'><head><META HTTP-EQUIV="Refresh" CONTENT="0;URL=HomePage.html"></head></html> ', zip.file.read('index.html').gsub(/\s+/, ' ')
|
||||
end
|
||||
ensure
|
||||
File.delete(@tempfile_path) if File.exist?(@tempfile_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_export_html_no_layout
|
||||
r = process 'export_html', 'web' => 'wiki1', 'layout' => 'no'
|
||||
|
||||
assert_response(:success, bypass_body_parsing = true)
|
||||
assert_equal 'application/zip', r.headers['type']
|
||||
assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
|
||||
assert_match /attachment; filename="wiki1-x?html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/,
|
||||
r.headers['Content-Disposition']
|
||||
assert_equal 'PK', r.body[0..1], 'Content is not a zip file'
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue