# ~*~ encoding: utf-8 ~*~ require 'net/http' require 'uri' require 'open-uri' require 'zlib' # PlantUML Diagrams # # This filter replaces PlantUML blocks with HTML img tags. These img tags # point to a PlantUML web service that converts the UML text blocks into nice # diagrams. # # For this to work you must have your own PlantUML server running somewhere. # Just follow the instructions on the github page to run your own server: # # https://github.com/plantuml/plantuml-server # # Once you start you own plantuml server you need to configure this filter to # point to it: # # Gollum::Filter::PlantUML.configure do |config| # config.url = "http://localhost:8080/plantuml/png" # end # # Then in your wiki pages simply add PlantUML blocks anywhere you want a # diagram: # # @startuml # Alice -> Bob: Authentication Request # Bob --> Alice: Authentication Response # Alice -> Bob: Another authentication Request # Alice <-- Bob: another authentication Response # @enduml # # To learn more about how to create cool PlantUML diagrams check the examples # at: http://plantuml.sourceforge.net/ # class Gollum::Filter::PlantUML < Gollum::Filter DEFAULT_URL = "http://localhost:8080/plantuml/png" # Configuration class used to change the behaviour of the PlatnUML filter. # # url: PlantUML server URL (e.g. http://localhost:8080) # test: Set to true when running tests to skip the server check. # class Configuration attr_accessor :url, :test def initialize @url = DEFAULT_URL @test = false end end class << self attr_writer :configuration end def self.configuration @configuration ||= Configuration.new end def self.configure yield(configuration) end # Extract all sequence diagram blocks into the map and replace with # placeholders. def extract(data) return data if @markup.format == :txt data.gsub(/(@startuml\r?\n.+?\r?\n@enduml\r?$)/m) do id = Digest::SHA1.hexdigest($1) @map[id] = { :code => $1 } id end end # Process all diagrams from the map and replace the placeholders with # the final HTML. def process(data) @map.each do |id, spec| data.gsub!(id) do render_plantuml(id, spec[:code]) end end data end private def server_url PlantUML::configuration.url end def test? PlantUML::configuration.test end def render_plantuml(id, code) if check_server plantuml_url = gen_url(code) "" else html_error("Sorry, unable to render PlantUML diagram at this time") end end # Compression code used to generate PlantUML URLs. Taken directly from the # Transcoder class in the PlantUML java code. def gen_url(text) result = "" compressedData = Zlib::Deflate.deflate(text) compressedData.chars.each_slice(3) do |bytes| #print bytes[0], ' ' , bytes[1] , ' ' , bytes[2] b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF) b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF) b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF) result += append3bytes(b1, b2, b3) end "#{server_url}/#{result}" end def encode6bit(b) if b < 10 return ('0'.ord + b).chr end b = b - 10 if b < 26 return ('A'.ord + b).chr end b = b - 26 if b < 26 return ('a'.ord + b).chr end b = b - 26 if b == 0 return '-' end if b == 1 return '_' end return '?' end def append3bytes(b1, b2, b3) c1 = b1 >> 2 c2 = ((b1 & 0x3) << 4) | (b2 >> 4) c3 = ((b2 & 0xF) << 2) | (b3 >> 6) c4 = b3 & 0x3F return encode6bit(c1 & 0x3F).chr + encode6bit(c2 & 0x3F).chr + encode6bit(c3 & 0x3F).chr + encode6bit(c4 & 0x3F).chr end # Make a call to the PlantUML server with the simplest diagram possible to # check if the server is available or not. def check_server return true if test? check_url = "#{server_url}/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000" response = Net::HTTP.get_response(URI(check_url)) return response.is_a?(Net::HTTPSuccess) rescue return false end end