#-- encoding: UTF-8 #-- copyright # ChiliProject is a project management system. # # Copyright (C) 2010-2012 the ChiliProject Team # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # See doc/COPYRIGHT.rdoc for more details. #++ module ChiliProject module Liquid class Template < ::Liquid::Template # creates a new Template object from liquid source code def self.parse(source) template = self.new template.parse(source) template end def context_from_render_options(*args) # This method is pulled out straight from the original # Liquid::Template#render context = case args.first when ::Liquid::Context args.shift when Hash ::Liquid::Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors) when nil ::Liquid::Context.new(assigns, instance_assigns, registers, @rethrow_errors) else raise ArgumentError, "Expect Hash or Liquid::Context as parameter" end case args.last when Hash options = args.pop if options[:registers].is_a?(Hash) self.registers.merge!(options[:registers]) end if options[:filters] context.add_filters(options[:filters]) end when Module context.add_filters(args.pop) when Array context.add_filters(args.pop) end context end # Render takes a hash with local variables. # # if you use the same filters over and over again consider registering them globally # with Template.register_filter # # Following options can be passed: # # * filters : array with local filters # * registers : hash with register variables. Those can be accessed from # filters and tags and might be useful to integrate liquid more with its host application # def render(*args) return '' if @root.nil? context = context_from_render_options(*args) context.registers[:html_results] ||= {} obj = context.registers[:object] attribute = context.registers[:attribute] if Setting.cache_formatted_text? && cache_key = cache_key_for(Setting.text_formatting, obj, attribute) # Text retrieved from the cache store may be frozen # We need to dup it so we can do in-place substitutions with gsub! result = Rails.cache.fetch(cache_key) result ||= begin result = render_context(context) Rails.cache.write(cache_key, result) if context.cacheable? result end.dup else render_context(context) end end def render_context(context) return '' if @root.nil? # ENTER THE RENDERING STAGE # 1. Render the input as Liquid # Some tags might register final HTML output in this stage. begin # for performance reasons we get a array back here. join will make a string out of it result = @root.render(context) result.respond_to?(:join) ? result.join : result ensure @errors = context.errors end # 2. Perform the Wiki markup transformation (e.g. Textile) obj = context.registers[:object] attribute = context.registers[:attribute] result = Redmine::WikiFormatting.to_html(Setting.text_formatting, result, :object => obj, :attribute => attribute) # 3. Now finally, replace the captured raw HTML bits in the final content length = nil # replace HTML results until we can find no additional variables while length != context.registers[:html_results].length do length = context.registers[:html_results].length context.registers[:html_results].delete_if do |key, value| # We use the block variant to avoid the evaluation of escaped # characters in +value+ during substitution. result.sub!(key) { |match| value } end end result end private def cache_key_for(format, object, attribute) if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank? "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}" end end end end end