- Unified block/inline rendering.

- Split views to separate files.
- Added PNG fallback for SVG displaying.
- Improved SVG compliance.
This commit is contained in:
Alexander Tsvyashchenko 2010-03-27 00:33:50 +02:00
parent 8cfa874772
commit 5a6f81888f
15 changed files with 151 additions and 158 deletions

View File

@ -2,8 +2,13 @@ Redmine Wiki External Filter plugin that allows to filter macro blocks
arguments using external program and display results on wiki.
Copyright (C) 2010 Alexander Tsvyashchenko, http://www.ndl.kiev.ua
Based on wiki_latex_plugin by Nils Israel <info@nils-israel.net>
Based on wiki_graphviz_plugin by tckz <at.tckz@gmail.com>
* Based on wiki_latex_plugin by Nils Israel <info@nils-israel.net>
* Based on wiki_graphviz_plugin by tckz <at.tckz@gmail.com>
* Includes FlowPlayer flash player, see http://www.flowplayer.org
* Includes CSS Browser Selector, see http://rafael.adm.br/css_browser_selector/
* Thanks to Eike for his "Cross-Browser SVG Issues" article,
http://e.metaclarity.org/52/cross-browser-svg-issues/
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License

View File

@ -13,7 +13,7 @@ class WikiExternalFilterController < ApplicationController
content = read_fragment cache_key
if (content)
send_data content[index], :type => config[macro]['content_type'], :disposition => 'inline', :filename => filename
send_data content[index], :type => config[macro]['outputs'][index]['content_type'], :disposition => 'inline', :filename => filename
else
render_404
end

View File

@ -47,7 +47,6 @@ module WikiExternalFilterHelper
if content
result[:source] = text
result[:content] = content
result[:content_type] = info['content_type']
RAILS_DEFAULT_LOGGER.debug "from cache: #{name}"
else
result = self.build_forced(text, attachments, info)
@ -63,6 +62,8 @@ module WikiExternalFilterHelper
result[:name] = name
result[:macro] = macro
result[:content_types] = info['outputs'].map { |out| out['content_type'] }
result[:template] = info['template']
return result
end
@ -79,18 +80,16 @@ module WikiExternalFilterHelper
content = []
errors = ""
commands = info['commands']? info['commands'] : [info['command']]
commands.each do |command|
RAILS_DEFAULT_LOGGER.info "executing command: #{command}"
info['outputs'].each do |out|
RAILS_DEFAULT_LOGGER.info "executing command: #{out['command']}"
c = nil
e = nil
Open4::popen4(command) { |pid, fin, fout, ferr|
fin.write info[:prolog] if info.key?(:prolog)
Open4::popen4(out['command']) { |pid, fin, fout, ferr|
fin.write out['prolog'] if out.key?('prolog')
fin.write CGI.unescapeHTML(text)
fin.write info[:epilog] if info.key?(:epilog)
fin.write out['epilog'] if out.key?('epilog')
fin.close
c, e = [fout.read, ferr.read]
}
@ -103,7 +102,6 @@ module WikiExternalFilterHelper
result[:content] = content
result[:errors] = errors
result[:content_type] = info['content_type']
result[:source] = text
result[:status] = $?.exitstatus == 0
@ -111,21 +109,35 @@ module WikiExternalFilterHelper
end
def render_tag(result)
render_to_string :template => 'wiki_external_filter/macro_inline', :layout => false, :locals => result
result = result.dup
result[:render_type] = 'inline'
html = render_common(result).chop
html << headers_common(result).chop
html
end
def render_block(result, wiki_name)
result = result.dup
result[:render_type] = 'block'
result[:wiki_name] = wiki_name
render_to_string :template => 'wiki_external_filter/macro_block', :layout => false, :locals => result
result[:inside] = render_common(result)
html = render_to_string(:template => 'wiki_external_filter/block', :layout => false, :locals => result).chop
html << headers_common(result).chop
html
end
def render_common(result)
render_to_string :template => "wiki_external_filter/macro_#{result[:template]}", :layout => false, :locals => result
end
def headers_common(result)
render_to_string :template => 'wiki_external_filter/headers', :layout => false, :locals => result
end
class Macro
def initialize(view, source, attachments, macro, info)
@view = view
@view.controller.extend(WikiExternalFilterHelper)
source.gsub!(/<br \/>/, "")
source.gsub!(/<\/?p>/, "")
@result = @view.controller.build(source, attachments, macro, info)
end

View File

@ -0,0 +1,5 @@
<div class='externalfilterblock'>
<%= inside %>
<br/>
<span class='wiki_page'>Goto source: [[<%= wiki_name %>]]</span>
</div>

View File

@ -0,0 +1,10 @@
<%
content_for :header_tags do
if not @stylesheets_included
@stylesheets_included = true
%>
<%= stylesheet_link_tag "wiki_external_filter.css", :plugin => "wiki_external_filter", :media => :all %>
<%
end
end
%>

View File

@ -1,64 +0,0 @@
<div class='externalfilterblock'>
<%
case content_type
when /image\// then
%>
<img src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name) %>' alt="<%= h source %>" />
<%
when /text\/plain/ then
%>
<pre><%= h content %></pre>
<%
when /\/.*(x|ht)ml/ then
%>
<div><%= content %></div>
<%
when /video\/x\-flv/ then
require 'RMagick'
image = Magick::Image::from_blob(content[0]).first
%>
<a class='flowplayer-video' href='<%= ActionController::Base.relative_url_root + "/wiki_external_filter/video.flv?name=#{name}" %>' alt='<%= h source %>' style='display:block;width:<%= image.columns %>px;height:<%= image.rows + 24 %>px;background-image:url(<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>);background-repeat:no-repeat'><%= image_tag 'play_large.png', :plugin => 'wiki_external_filter', :style => "display:block;position:relative;left:#{image.columns / 2 - 83 / 2}px;top:#{image.rows / 2 - 83 / 2}px" %></a>
<%
@flowplayer_used = true
%>
<%
else
%>
<object class='externalfilterinline' name='<%= name %>' data='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name) %>' type='<%= content_type %>' title="<%= h source %>">
<embed name='<%= name %>-2' src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name) %>' type='<%= content_type %>' title="<%= h source %>" />
</object>
<%
end
%>
<br/>
<span class='wiki_page'>Goto source: [[<%= wiki_name %>]]</span>
</div>
<%
content_for :header_tags do
%>
<%
if not @stylesheets_included
@stylesheets_included = true
%>
<%= stylesheet_link_tag "wiki_external_filter.css", :plugin => "wiki_external_filter", :media => :all %>
<%
end
%>
<%
if @flowplayer_used
if not @flowplayer_scripts_included
@flowplayer_scripts_included = true
%>
<%= javascript_include_tag 'flowplayer.min.js', :plugin => 'wiki_external_filter' %>
<script language="JavaScript">
//<![CDATA[
window.onload = function () {
flowplayer("a.flowplayer-video", "<%= ActionController::Base.relative_url_root + Engines::RailsExtensions::AssetHelpers.plugin_asset_path('wiki_external_filter', 'javascripts', 'flowplayer.swf') %>", { clip: { bufferLength: 1 } });
};
//]]>
</script>
<%
end
end
end
%>

View File

@ -0,0 +1,22 @@
<%
require 'RMagick'
image = Magick::Image::from_blob(content[0]).first
%>
<a class='flowplayer-video<%= render_type == 'inline' ? " externalfilterinline" : "" %>' href='<%= ActionController::Base.relative_url_root + "/wiki_external_filter/#{macro}.flv?name=#{name}" %>' title='<%= h source %>' style='display:block;width:<%= image.columns %>px;height:<%= image.rows + 24 %>px;background-image:url(<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>);background-repeat:no-repeat'><%= image_tag 'play_large.png', :plugin => 'wiki_external_filter', :style => "display:block;position:relative;left:#{image.columns / 2 - 83 / 2}px;top:#{image.rows / 2 - 83 / 2}px" %></a>
<%
content_for :header_tags do
if not @flowplayer_scripts_included
@flowplayer_scripts_included = true
%>
<%= javascript_include_tag 'flowplayer.min.js', :plugin => 'wiki_external_filter' %>
<script language="JavaScript" type="text/javascript">
//<![CDATA[
window.onload = function () {
flowplayer("a.flowplayer-video", "<%= ActionController::Base.relative_url_root + Engines::RailsExtensions::AssetHelpers.plugin_asset_path('wiki_external_filter', 'javascripts', 'flowplayer.swf') %>", { clip: { bufferLength: 1 } });
};
//]]>
</script>
<%
end
end
%>

View File

@ -0,0 +1 @@
<img <%= render_type == 'inline' ? "class='externalfilterinline'" : "" %> src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' alt="<%= h source %>" />

View File

@ -1,61 +0,0 @@
<%
case content_type
when /image\// then
%>
<img class='externalfilterinline' src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' alt="<%= h source %>" />
<%
when /text\/plain/ then
%>
<pre class='externalfilterinline'><%= h content[0] %></pre>
<%
when /\/.*(x|ht)ml/ then
%>
<div class='externalfilterinline'><%= content[0] %></div>
<%
when /video\/x\-flv/ then
require 'RMagick'
image = Magick::Image::from_blob(content[0]).first
%>
<a class='flowplayer-video externalfilterinline' href='<%= ActionController::Base.relative_url_root + "/wiki_external_filter/video.flv?name=#{name}" %>' alt='<%= h source %>' style='display:block;width:<%= image.columns %>px;height:<%= image.rows + 24 %>px;background-image:url(<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>);background-repeat:no-repeat'><%= image_tag 'play_large.png', :plugin => 'wiki_external_filter', :style => "display:block;position:relative;left:#{image.columns / 2 - 83 / 2}px;top:#{image.rows / 2 - 83 / 2}px" %></a>
<%
@flowplayer_used = true
%>
<%
else
%>
<object class='externalfilterinline' name='<%= name %>' data='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' type='<%= content_type %>' title="<%= h source %>">
<embed name='<%= name %>-2' src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' type='<%= content_type %>' title="<%= h source %>" />
</object>
<%
end
%>
<%
content_for :header_tags do
%>
<%
if not @stylesheets_included
@stylesheets_included = true
%>
<%= stylesheet_link_tag "wiki_external_filter.css", :plugin => "wiki_external_filter", :media => :all %>
<%
end
%>
<%
if @flowplayer_used
if not @flowplayer_scripts_included
@flowplayer_scripts_included = true
%>
<%= javascript_include_tag 'flowplayer.min.js', :plugin => 'wiki_external_filter' %>
<script language="JavaScript">
//<![CDATA[
window.onload = function () {
flowplayer("a.flowplayer-video", "<%= ActionController::Base.relative_url_root + Engines::RailsExtensions::AssetHelpers.plugin_asset_path('wiki_external_filter', 'javascripts', 'flowplayer.swf') %>", { clip: { bufferLength: 1 } });
};
//]]>
</script>
<%
end
end
end
%>

View File

@ -0,0 +1,27 @@
<img class='svg <%= render_type == 'inline' ? "externalfilterinline" : "" %>' src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' alt='<%= h source %>' />
<object class='svgobject <%= render_type == 'inline' ? "externalfilterinline" : "" %>' name='<%= name %>' data='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 0) %>' type='<%= content_types[0] %>' title="<%= h source %>">
<%
if content.size > 1
%>
<img<%= render_type == 'inline' ? " class='externalfilterinline'" : "" %> src='<%= url_for(:controller => 'wiki_external_filter', :action => 'filter', :macro => macro, :name => name, :index => 1) %>' alt='<%= h source %>' />
<%
end
%>
</object>
<%
content_for :header_tags do
if not @svg_scripts_included
@svg_scripts_included = true
%>
<%= javascript_include_tag 'css_browser_selector.js', :plugin => 'wiki_external_filter' %>
<style type="text/css">
.svgobject { display: none }
.ie .svgobject { display: inline }
.ie img.svg { display: none }
.gecko .svgobject { display: inline }
.gecko img.svg { display: none }
</style>
<%
end
end
%>

View File

@ -0,0 +1 @@
<span<%= render_type == 'inline' ? " class='externalfilterinline'" : "" %>><%= h content[0] %></span>

View File

@ -0,0 +1,8 @@
/*
CSS Browser Selector v0.3.5 (Feb 05, 2010)
Rafael Lima (http://rafael.adm.br)
http://rafael.adm.br/css_browser_selector
License: http://creativecommons.org/licenses/by/2.5/
Contributors: http://rafael.adm.br/css_browser_selector#contributors
*/
function css_browser_selector(u){var ua = u.toLowerCase(),is=function(t){return ua.indexOf(t)>-1;},g='gecko',w='webkit',s='safari',o='opera',h=document.documentElement,b=[(!(/opera|webtv/i.test(ua))&&/msie\s(\d)/.test(ua))?('ie ie'+RegExp.$1):is('firefox/2')?g+' ff2':is('firefox/3.5')?g+' ff3 ff3_5':is('firefox/3')?g+' ff3':is('gecko/')?g:is('opera')?o+(/version\/(\d+)/.test(ua)?' '+o+RegExp.$1:(/opera(\s|\/)(\d+)/.test(ua)?' '+o+RegExp.$2:'')):is('konqueror')?'konqueror':is('chrome')?w+' chrome':is('iron')?w+' iron':is('applewebkit/')?w+' '+s+(/version\/(\d+)/.test(ua)?' '+s+RegExp.$1:''):is('mozilla/')?g:'',is('j2me')?'mobile':is('iphone')?'iphone':is('ipod')?'ipod':is('mac')?'mac':is('darwin')?'mac':is('webtv')?'webtv':is('win')?'win':is('freebsd')?'freebsd':(is('x11')||is('linux'))?'linux':'','js']; c = b.join(' '); h.className += ' '+c; return c;}; css_browser_selector(navigator.userAgent);

View File

@ -1,3 +1,4 @@
ActionController::Routing::Routes.draw do |map|
map.connect 'wiki_external_filter/:filename', :controller => 'wiki_external_filter', :action => 'filter', :macro => 'flowplayer', :index => '1', :requirements => { :filename => /\S+\.flv/ }
map.connect 'wiki_external_filter/:filename', :controller => 'wiki_external_filter', :action => 'filter', :macro => 'video', :index => '1', :requirements => { :filename => /video\.flv/ }
map.connect 'wiki_external_filter/:filename', :controller => 'wiki_external_filter', :action => 'filter', :macro => 'video_url', :index => '1', :requirements => { :filename => /video_url\.flv/ }
end

View File

@ -1,28 +1,54 @@
development: &development
plantuml:
description: "Constructs UML diagram image from its textual description in PlantUML language, see http://plantuml.sourceforge.net"
command: "/usr/bin/plantuml -pipe"
content_type: "image/png"
prolog: "@startuml"
epilog: "@enduml"
template: image
outputs:
- command: "/usr/bin/plantuml -pipe"
content_type: "image/png"
prolog: "@startuml"
epilog: "@enduml"
graphviz:
description: "Constructs graph image from its textual description in DOT language, see http://www.graphviz.org"
command: "/usr/bin/dot -Tsvg"
content_type: "image/svg+xml"
template: svg
outputs:
- command: "/usr/bin/dot -Tsvg"
content_type: "image/svg+xml"
- command: "/usr/bin/dot -Tpng"
content_type: "image/png"
ritex:
description: "Converts WebTeX expression to MathML, see http://ritex.rubyforge.org/"
command: "(echo '<!DOCTYPE math PUBLIC \"-//W3C//DTD MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/mathml2.dtd\">'; /usr/bin/ritex) | xmllint --noent --nonet --catalogs --loaddtd - | sed 's/^<!DOCTYPE.\\+$//' | /usr/bin/math2svg"
content_type: "image/svg+xml"
# For MathML-compliant browsers and when Redmine is fully XML-compliant.
# ritex:
# description: "Converts WebTeX expression to MathML, see http://ritex.rubyforge.org/"
# command: "/usr/bin/ritex"
# content_type: "application/xhtml+xml"
template: svg
outputs:
- command: "(echo '<!DOCTYPE math PUBLIC \"-//W3C//DTD MathML 2.0//EN\" \"http://www.w3.org/Math/DTD/mathml2/mathml2.dtd\">'; /usr/bin/ritex) | xmllint --noent --nonet --catalogs --loaddtd - | sed 's/^<!DOCTYPE.\\+$//' | /usr/bin/math2svg"
content_type: "image/svg+xml"
video:
description: "Converts video file given by its filename to FLV and displays it via Flowplayer"
template: flash-video
replace_attachments: true
outputs:
- command: "xargs -I{} ffmpeg -i {} -vframes 1 -f mjpeg -"
content_type: "image/jpeg"
# We have to use temporary file as otherwise ffmpeg is unable to inject
# metadata.
- command: "FN=`cat`; if [ `echo ${FN##*.} | tr [:upper:] [:lower:]` != 'flv' ]; then TMPVIDEO=`tempfile --directory=/var/tmp`; ffmpeg -y -i $FN -sameq -ar 44100 -f flv $TMPVIDEO; STATUS=$?; cat $TMPVIDEO; rm $TMPVIDEO; exit $STATUS; else cat $FN; fi"
content_type: "video/x-flv"
video_url:
description: "Converts video file given by its URL to FLV and displays it via Flowplayer"
template: flash-video
outputs:
- command: "wget -i - -O - | ffmpeg -i - -vframes 1 -f mjpeg -"
content_type: "image/jpeg"
# We have to use temporary file as otherwise ffmpeg is unable to inject
# metadata.
- command: "FN=`cat`; wget $FN -O - | (if [ `echo ${FN##*.} | tr [:upper:] [:lower:]` != 'flv' ]; then TMPVIDEO=`tempfile --directory=/var/tmp`; ffmpeg -y -i - -sameq -ar 44100 -f flv $TMPVIDEO; STATUS=$?; cat $TMPVIDEO; rm $TMPVIDEO; exit $STATUS; else cat; fi)"
content_type: "video/x-flv"
fortune:
description: "Prints a random, hopefully interesting, adage, see http://en.wikipedia.org/wiki/Fortune_(Unix)"
command: "/usr/bin/fortune"
template: text
cache_seconds: 0
content_type: "text/plain"
outputs:
- command: "/usr/bin/fortune"
content_type: "text/plain"
test:
type: mock

View File

@ -19,7 +19,7 @@ Redmine::Plugin.register :wiki_external_filter do
info = config[name]
desc info['description']
macro name do |obj, args|
m = WikiExternalFilterHelper::Macro.new(self, args.to_s, obj.page.attachments, name, info)
m = WikiExternalFilterHelper::Macro.new(self, args.to_s, obj ? obj.page.attachments : nil, name, info)
m.render
end