| 19 | 19 |  | 
  | 20 | 20 | require 'forwardable' | 
  | 21 | 21 | require 'cgi' | 
  |  | 22 | require 'digest/md5' | 
  | 22 | 23 |  | 
  | 23 | 24 | module ApplicationHelper | 
  | 24 | 25 |   include Redmine::WikiFormatting::Macros::Definitions | 
  | ... | ... |  | 
  | 487 | 488 |     project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) | 
  | 488 | 489 |     only_path = options.delete(:only_path) == false ? false : true | 
  | 489 | 490 |  | 
  |  | 491 |     text, macros_grabbed = preprocess_macros(text) | 
  |  | 492 |     options[:macros_grabbed] = macros_grabbed | 
  | 490 | 493 |     text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) | 
  | 491 | 494 |  | 
  | 492 | 495 |     @parsed_headings = [] | 
  | ... | ... |  | 
  | 497 | 500 |       end | 
  | 498 | 501 |     end | 
  | 499 | 502 |  | 
  |  | 503 |     # Any macros not parsed by this point must be in <pre> blocks - and, hence, should be | 
  |  | 504 |     # restored to their original state. | 
  |  | 505 |     restore_macros(text, macros_grabbed) unless text.nil? | 
  |  | 506 |  | 
  | 500 | 507 |     if @parsed_headings.any? | 
  | 501 | 508 |       replace_toc(text, @parsed_headings) | 
  | 502 | 509 |     end | 
  | ... | ... |  | 
  | 788 | 795 |                 ( | 
  | 789 | 796 |                 \{\{                        # opening tag | 
  | 790 | 797 |                 ([\w]+)                     # macro name | 
  | 791 |  |                 (\(([^\}]*)\))?             # optional arguments | 
  |  | 798 | 		(\((.*?)\))?                # optional arguments | 
  | 792 | 799 |                 \}\}                        # closing tag | 
  | 793 |  |                 ) | 
  | 794 |  |               /x unless const_defined?(:MACROS_RE) | 
  |  | 800 | 		) | 
  |  | 801 |               /xm unless const_defined?(:MACROS_RE) | 
  |  | 802 |  | 
  |  | 803 |   def preprocess_macros(text) | 
  |  | 804 |     macros_grabbed = {} | 
  |  | 805 |     text = text.gsub(MACROS_RE) do |s| | 
  |  | 806 |      esc, all, macro = $1, $2, $3.downcase | 
  |  | 807 |      if esc.nil? and (WikiExternalFilterHelper.has_macro macro rescue false) | 
  |  | 808 |        args = $5 | 
  |  | 809 |        key = Digest::MD5.hexdigest("#{macro}:#{args}") | 
  |  | 810 |        macros_grabbed[key] = {:macro => macro, :args => args, :raw => s} | 
  |  | 811 |        "{{_macros_grabbed(#{key})}}" | 
  |  | 812 |      else | 
  |  | 813 |        s | 
  |  | 814 |      end | 
  |  | 815 |    end | 
  |  | 816 |    [text, macros_grabbed] | 
  |  | 817 |   end | 
  | 795 | 818 |  | 
  | 796 | 819 |   # Macros substitution | 
  | 797 | 820 |   def parse_macros(text, project, obj, attr, only_path, options) | 
  | 798 | 821 |     text.gsub!(MACROS_RE) do | 
  | 799 | 822 |       esc, all, macro = $1, $2, $3.downcase | 
  | 800 | 823 |       args = ($5 || '').split(',').each(&:strip) | 
  |  | 824 |       if macro == '_macros_grabbed' and options[:macros_grabbed].member? args.first | 
  |  | 825 |         macro, args = options[:macros_grabbed][args.first].values_at(:macro, :args) | 
  |  | 826 |       end | 
  | 801 | 827 |       if esc.nil? | 
  | 802 | 828 |         begin | 
  | 803 | 829 |           exec_macro(macro, obj, args) | 
  | ... | ... |  | 
  | 807 | 833 |       else | 
  | 808 | 834 |         all | 
  | 809 | 835 |       end | 
  |  | 836 |     end | 
  |  | 837 |   end | 
  |  | 838 |  | 
  |  | 839 |   # Macros restoration to their original state - used for <pre> blocks. | 
  |  | 840 |   def restore_macros(text, macros_grabbed) | 
  |  | 841 |     text.gsub!(MACROS_RE) do | 
  |  | 842 |       all, macro, args = $&, $3.downcase, $5 | 
  |  | 843 |       if macro == '_macros_grabbed' and macros_grabbed.member? args | 
  |  | 844 |         "#{macros_grabbed[args].values_at(:raw)}" | 
  |  | 845 |       else | 
  |  | 846 |         all | 
  |  | 847 |       end | 
  | 810 | 848 |     end | 
  | 811 | 849 |   end | 
  | 812 | 850 |  |