2010-01-11 13:38:44 +01:00
|
|
|
require 'digest/sha2'
|
2012-08-02 12:09:00 +02:00
|
|
|
include Redmine::WikiFormatting::Macros::Definitions
|
2010-01-11 13:38:44 +01:00
|
|
|
|
|
|
|
module WikiExternalFilterHelper
|
|
|
|
|
|
|
|
def load_config
|
|
|
|
unless @config
|
2012-08-02 12:09:00 +02:00
|
|
|
config_file = "#{Rails.root}/plugins/wiki_external_filter/config/wiki_external_filter.yml"
|
2010-01-11 13:38:44 +01:00
|
|
|
unless File.exists?(config_file)
|
|
|
|
raise "Config not found: #{config_file}"
|
|
|
|
end
|
2012-08-02 12:09:00 +02:00
|
|
|
@config = YAML.load_file(config_file)[Rails.env]
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
@config
|
|
|
|
end
|
|
|
|
|
|
|
|
def has_macro(macro)
|
|
|
|
config = load_config
|
|
|
|
config.key?(macro)
|
|
|
|
end
|
|
|
|
|
|
|
|
module_function :load_config, :has_macro
|
|
|
|
|
|
|
|
def construct_cache_key(macro, name)
|
|
|
|
['wiki_external_filter', macro, name].join("/")
|
|
|
|
end
|
|
|
|
|
2010-03-26 12:51:13 +01:00
|
|
|
def build(text, attachments, macro, info)
|
2010-01-11 13:38:44 +01:00
|
|
|
|
2012-08-02 12:09:00 +02:00
|
|
|
name = Digest::SHA256.hexdigest(text.to_s)
|
2010-01-11 13:38:44 +01:00
|
|
|
result = {}
|
|
|
|
content = nil
|
|
|
|
cache_key = nil
|
|
|
|
expires = 0
|
|
|
|
|
|
|
|
if info.key?('cache_seconds')
|
|
|
|
expires = info['cache_seconds']
|
|
|
|
else
|
|
|
|
expires = Setting.plugin_wiki_external_filter['cache_seconds'].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
if expires > 0
|
|
|
|
cache_key = self.construct_cache_key(macro, name)
|
2010-10-12 21:16:17 +02:00
|
|
|
begin
|
2012-08-02 12:09:00 +02:00
|
|
|
content = Rails.cache.read cache_key, :expires_in => expires.seconds
|
2010-10-12 21:16:17 +02:00
|
|
|
rescue
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.logger.error "Failed to load cache: #{cache_key}, error: $! #{error} #{$@}"
|
2010-10-12 21:16:17 +02:00
|
|
|
end
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
if content
|
2012-08-02 12:09:00 +02:00
|
|
|
result[:source] = text.to_s
|
2010-01-11 13:38:44 +01:00
|
|
|
result[:content] = content
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.logger.debug "from cache: #{cache_key}"
|
2010-01-11 13:38:44 +01:00
|
|
|
else
|
2010-03-26 12:51:13 +01:00
|
|
|
result = self.build_forced(text, attachments, info)
|
2010-01-11 13:38:44 +01:00
|
|
|
if result[:status]
|
|
|
|
if expires > 0
|
2010-10-12 21:16:17 +02:00
|
|
|
begin
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.cache.write cache_key, result[:content], :expires_in => expires.seconds
|
|
|
|
Rails.logger.debug "cache saved: #{cache_key} expires #{expires.seconds}"
|
2010-10-12 21:16:17 +02:00
|
|
|
rescue
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.logger.error "Failed to save cache: #{cache_key}, result content #{result[:content]}, error: $!"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
raise "please set expires time under plugins settings"
|
|
|
|
end
|
2010-01-11 13:38:44 +01:00
|
|
|
else
|
2010-03-26 12:51:13 +01:00
|
|
|
raise "Error applying external filter: stdout is #{result[:content]}, stderr is #{result[:errors]}"
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
result[:name] = name
|
|
|
|
result[:macro] = macro
|
2010-03-26 23:33:50 +01:00
|
|
|
result[:content_types] = info['outputs'].map { |out| out['content_type'] }
|
|
|
|
result[:template] = info['template']
|
2010-01-11 13:38:44 +01:00
|
|
|
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2010-03-26 12:51:13 +01:00
|
|
|
def build_forced(text, attachments, info)
|
|
|
|
|
|
|
|
if info['replace_attachments'] and attachments
|
|
|
|
attachments.each do |att|
|
2012-08-02 12:09:00 +02:00
|
|
|
text.to_s.gsub!(/#{att.filename.downcase}/i, att.diskfile)
|
2010-03-26 12:51:13 +01:00
|
|
|
end
|
|
|
|
end
|
2010-01-11 13:38:44 +01:00
|
|
|
|
|
|
|
result = {}
|
2010-03-26 12:51:13 +01:00
|
|
|
content = []
|
|
|
|
errors = ""
|
|
|
|
|
2012-08-02 12:09:00 +02:00
|
|
|
text = text.first.to_s.gsub("<br />", "\n")
|
|
|
|
Rails.logger.debug "\n Text #{text} \n"
|
|
|
|
|
2010-03-26 23:33:50 +01:00
|
|
|
info['outputs'].each do |out|
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.logger.info "executing command: #{out['command']}"
|
2010-01-11 13:38:44 +01:00
|
|
|
|
2010-03-26 12:51:13 +01:00
|
|
|
c = nil
|
|
|
|
e = nil
|
2010-01-11 13:38:44 +01:00
|
|
|
|
2010-03-27 00:22:42 +01:00
|
|
|
# If popen4 is available - use it as it provides stderr
|
|
|
|
# redirection so we can get more info in the case of error.
|
|
|
|
begin
|
|
|
|
require 'open4'
|
|
|
|
|
|
|
|
Open4::popen4(out['command']) { |pid, fin, fout, ferr|
|
|
|
|
fin.write out['prolog'] if out.key?('prolog')
|
2012-08-02 12:09:00 +02:00
|
|
|
fin.write text
|
2010-03-27 00:22:42 +01:00
|
|
|
fin.write out['epilog'] if out.key?('epilog')
|
|
|
|
fin.close
|
|
|
|
c, e = [fout.read, ferr.read]
|
|
|
|
}
|
|
|
|
rescue LoadError
|
|
|
|
IO.popen(out['command'], 'r+b') { |f|
|
|
|
|
f.write out['prolog'] if out.key?('prolog')
|
2012-08-02 12:09:00 +02:00
|
|
|
f.write text
|
2010-03-27 00:22:42 +01:00
|
|
|
f.write out['epilog'] if out.key?('epilog')
|
|
|
|
f.close_write
|
|
|
|
c = f.read
|
2012-08-02 12:09:00 +02:00
|
|
|
}
|
2010-03-27 00:22:42 +01:00
|
|
|
end
|
2010-03-26 12:51:13 +01:00
|
|
|
|
2012-08-02 12:09:00 +02:00
|
|
|
Rails.logger.debug("child status: sig=#{$?.termsig}, exit=#{$?.exitstatus}")
|
2010-03-26 12:51:13 +01:00
|
|
|
|
|
|
|
content << c
|
|
|
|
errors += e if e
|
|
|
|
end
|
2010-01-11 13:38:44 +01:00
|
|
|
|
|
|
|
result[:content] = content
|
2010-03-26 12:51:13 +01:00
|
|
|
result[:errors] = errors
|
2010-01-11 13:38:44 +01:00
|
|
|
result[:source] = text
|
|
|
|
result[:status] = $?.exitstatus == 0
|
|
|
|
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_tag(result)
|
2010-03-26 23:33:50 +01:00
|
|
|
result = result.dup
|
|
|
|
result[:render_type] = 'inline'
|
|
|
|
html = render_common(result).chop
|
|
|
|
html << headers_common(result).chop
|
|
|
|
html
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def render_block(result, wiki_name)
|
|
|
|
result = result.dup
|
2010-03-26 23:33:50 +01:00
|
|
|
result[:render_type] = 'block'
|
2010-01-11 13:38:44 +01:00
|
|
|
result[:wiki_name] = wiki_name
|
2010-03-26 23:33:50 +01:00
|
|
|
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
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
class Macro
|
2010-03-26 12:51:13 +01:00
|
|
|
def initialize(view, source, attachments, macro, info)
|
2010-01-11 13:38:44 +01:00
|
|
|
@view = view
|
|
|
|
@view.controller.extend(WikiExternalFilterHelper)
|
2010-03-26 12:51:13 +01:00
|
|
|
@result = @view.controller.build(source, attachments, macro, info)
|
2010-01-11 13:38:44 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def render()
|
|
|
|
@view.controller.render_tag(@result)
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_block(wiki_name)
|
|
|
|
@view.controller.render_block(@result, wiki_name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|