require 'cgi' require 'uri' # :main: TracWiki # The TracWiki parses and translates Trac formatted text into # XHTML. Creole is a lightweight markup syntax similar to what many # WikiWikiWebs use. Example syntax: # # = Heading 1 = # == Heading 2 == # === Heading 3 === # **Bold text** # ''Italic text'' # [[Links]] # ||=Table=||=Heading=|| # || Table || Cells || # [[Image(image.png)]] # [[Image(image.png, options)]] # # The simplest interface is TracWiki.render. The default handling of # links allow explicit local links using the [[link]] syntax. External # links will only be allowed if specified using http(s) and ftp(s) # schemes. If special link handling is needed, such as inter-wiki or # hierachical local links, you must inherit Creole::CreoleParser and # override make_local_link. # # You can customize the created image markup by overriding # make_image. # Main TracWiki parser class. Call TracWikiParser#parse to parse # TracWiki formatted text. # # This class is not reentrant. A separate instance is needed for # each thread that needs to convert Creole to HTML. # # Inherit this to provide custom handling of links. The overrideable # methods are: make_local_link module TracWiki class Parser # Allowed url schemes # Examples: http https ftp ftps attr_accessor :allowed_schemes # Non-standard wiki text extensions enabled? # E.g. underlined, deleted text etc attr_writer :extensions def extensions?; @extensions; end # Disable url escaping for local links # Escaping: [[/Test]] --> %2FTest # No escaping: [[/Test]] --> Test attr_writer :no_escape def no_escape?; @no_escape; end # Disable url escaping for local links # [[whatwerver]] stays [[whatwerver]] attr_writer :no_link def no_link?; @no_link; end # Create a new Parser instance. def initialize(text, options = {}) @allowed_schemes = %w(http https ftp ftps) @text = text @extensions = @no_escape = nil options.each_pair {|k,v| send("#{k}=", v) } end # Convert CCreole text to HTML and return # the result. The resulting HTML does not contain and #
tags. # # Example: # # parser = CreoleParser.new("**Hello //World//**", :extensions => true) # parser.to_html # #=> "Hello World
" def to_html @out = '' @p = false @stack = [] @stacki = [] parse_block(@text) @out end protected # Escape any characters with special meaning in HTML using HTML # entities. def escape_html(string) CGI::escapeHTML(string) end # Escape any characters with special meaning in URLs using URL # encoding. def escape_url(string) CGI::escape(string) end def start_tag(tag, args = '', lindent = nil) lindent = @stacki.last || -1 if lindent.nil? @stack.push(tag) @stacki.push(lindent) if tag == 'strongem' @out << '' else @out << '<' << tag << args << '>' end end def end_tag tag = @stack.pop tagi = @stacki.pop if tag == 'strongem' @out << '' elsif tag == 'p' @out << "\n" else @out << "#{tag}>" end end def toggle_tag(tag, match) if @stack.include?(tag) if @stack.last == tag end_tag else @out << escape_html(match) end else start_tag(tag) end end def end_paragraph end_tag while !@stack.empty? @p = false end def start_paragraph if @p @out << ' ' if @out[-1] != ?\s else end_paragraph start_tag('p') @p = true end end # Translate an explicit local link to a desired URL that is # properly URL-escaped. The default behaviour is to convert local # links directly, escaping any characters that have special # meaning in URLs. Relative URLs in local links are not handled. # # Examples: # # make_local_link("LocalLink") #=> "LocalLink" # make_local_link("/Foo/Bar") #=> "%2FFoo%2FBar" # # Must ensure that the result is properly URL-escaped. The caller # will handle HTML escaping as necessary. HTML links will not be # inserted if the function returns nil. # # Example custom behaviour: # # make_local_link("LocalLink") #=> "/LocalLink" # make_local_link("Wikipedia:Bread") #=> "http://en.wikipedia.org/wiki/Bread" def make_local_link(link) #:doc: no_escape? ? link : escape_url(link) end # Sanatize a direct url (e.g. http://wikipedia.org/). The default # behaviour returns the original link as-is. # # Must ensure that the result is properly URL-escaped. The caller # will handle HTML escaping as necessary. Links will not be # converted to HTML links if the function returns link. # # Custom versions of this function in inherited classes can # implement specific link handling behaviour, such as redirection # to intermediate pages (for example, for notifing the user that # he is leaving the site). def make_direct_link(url) #:doc: url end # Sanatize and prefix image URLs. When images are encountered in # Creole text, this function is called to obtain the actual URL of # the image. The default behaviour is to return the image link # as-is. No image tags are inserted if the function returns nil. # # Custom version of the method can be used to sanatize URLs # (e.g. remove query-parts), inhibit off-site images, or add a # base URL, for example: # # def make_image_link(url) # URI.join("http://mywiki.org/images/", url) # end def make_image_link(url) #:doc: url end # Create image markup. This # method can be overridden to generate custom # markup, for example to add html additional attributes or # to put divs around the imgs. def make_image(uri, attrs='') "" end def make_image_attrs(attrs) return '' if ! attrs a = {} style = [] attrs.strip.split(/\s*,\s*/).each do |opt| case opt when /^\d+[^\d]*$/ a['width'] = escape_url(opt) when /^(right|left|center)/i a['align'] = escape_url(opt) when /^(top|bottom|middle)$/i a['valign'] = escape_url(opt) when /^link=(.*)$/i # pass when /^nolink$/i # pass when /^(align|valign|border|width|height|alt|title|longdesc|class|id|usemap)=(.*)$/i a[$1]= escape_url($2) when /^(margin|margin-(left|right|top|bottom))=(\d+)$/ style.push($1 + ':' + escape_url($3)) end end a['style'] = style.join(';') if ! style.empty? return '' if a.empty? return ' ' + a.map{|k,v| "#{k}=\"#{v}\"" }.sort.join(' ') end def make_headline(level, text) "' << escape_html(nowikiblock) << '' # horizontal rule when /\A\s*-{4,}\s*$/ end_paragraph @out << '