Confluence4r

Ruby helper class for handling XML-RPC with Confluence (Sadly the name Rubicon was already taken )
Download the file: confluence4r.rb

New Extended Confluence4r

We at Encore have taken confluence4r and greatly expanded it (some might even argue that we've gone a little overboard). Our extended version now allows you to talk to Confluence via a vaguely ActiveRecord-like interface. For more info on how to install it (as a Rails plugin) and how to use it, see here --> Confluence4r Rails Plugin

require 'xmlrpc/client'

# A useful helper for running Confluence XML-RPC from Ruby. Takes care of
# adding the token to each method call (so you can call server.getSpaces()
# instead of server.getSpaces(token)). Also takes care of re-logging in
# if your login times out.
#
# Usage:
#
# server = Confluence::Server.new("http://confluence.atlassian.com")
# server.login("user", "password")
# puts server.getSpaces()
#
module Confluence
   class Server
		def initialize(server_url)
			server_url += "/rpc/xmlrpc" unless server_url[-11..-1] == "/rpc/xmlrpc"
			@server_url = server_url
			server = XMLRPC::Client.new2(server_url)
			@conf = server.proxy("confluence1")
			@token = "12345"
		end

		def login(username, password)
			@user = username
			@pass = password
			do_login()
		end

		def method_missing(method_name, *args)
			begin
				@conf.send(method_name, *([@token] + args))
			rescue XMLRPC::FaultException => e
				if (e.faultString.include?("InvalidSessionException"))
					do_login
					retry
				else
					raise e.faultString
				end
			end
		end

		private

		def do_login()
			begin
				@token = @conf.login(@user, @pass)
			rescue XMLRPC::FaultException => e
				raise e.faultString
			end
		end
	end
end

Labels

ruby ruby Delete
scripts scripts Delete
xml-rpc xml-rpc Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Feb 15, 2007

    Frithjof Eckhardt says:

    Hi, this Ruby helper is way cool. Though I can't figure out how to use the addAt...

    Hi, this Ruby helper is way cool. Though I can't figure out how to use the addAttachment method to upload some file to Confluence. The code

    att_name = "test.doc"
    attach = server.getAttachment("6921", att_name, "1")
    attach.each_pair do |key, value|
      puts "#{key} => #{value}"
    end
    
    att_data = []
    f = File.open(att_name, "rb")
    f.read.each_byte do |byte|
      att_data << byte
    end
    f.close
    
    #Attachment addAttachment(String token, long contentId, Attachment attachment, byte[] attachmentData)
    attach = server.addAttachment("6921", attach, att_data)

    results in the following error:

    ./confluence4r.rb:38:in `method_missing': java.lang.NoSuchMethodException: 
    $Proxy42.addAttachment(java.lang.String, java.lang.String, java.util.Hashtable, java.util.Vector) (RuntimeError)

    It seems the problem is how to map the Ruby classes to Java classes correctly.
    Any suggestions?

  2. Feb 15, 2007

    Matt Zukowski says:

    Looking at the XML-RPC API, the signature for the addAttachment procedure is: ...

    Looking at the XML-RPC API, the signature for the addAttachment procedure is:

    addAttachment(String token, long contentId, Attachment attachment, byte[] attachmentData)
    

    But you are trying to do:

    addAttachment(java.lang.String, java.lang.String, java.util.Hashtable, java.util.Vector)
    

    So just looking at your ruby code, the first argument should be an integer (6921, not "6921"). Also, I'm not sure how the ruby XMLRPC client does it's marshalling, but for the third argument rather than submitting a ruby array, you might have to submit some other data structure to get a byte[] array out of it rather than a Vector.

    1. Feb 15, 2007

      Matt Zukowski says:

      p.s. keep in mind that the very first argument (the String) is the authenticatio...

      p.s. keep in mind that the very first argument (the String) is the authentication token, which the confluence4r helper takes care of for you.

      Also, please do let me know if you figure out what to do about that byte[] argument. I might have to deal with attachments soon too, so I will have the same problem.

    2. Feb 16, 2007

      Frithjof Eckhardt says:

      I changed the contentId, but the method_missing error remains. The solution seem...

      I changed the contentId, but the method_missing error remains. The solution seems to be to get the marshalling work correctly as you pointed out. I think I need to switch to JRuby for this task.

      1. Feb 16, 2007

        Matt Zukowski says:

        I'm not sure if JRuby will make any difference. From what I understand it's just...

        I'm not sure if JRuby will make any difference. From what I understand it's just a ruby interpreter running in the Java virtual machine. It should be have exactly the same way as regular ruby.

        I think the problem is that when you try to use a ruby Array as an argument in your XMLRPC procedure call, the ruby XML-RPC client translates it to the wrong type. You want a byte[] but you get a Vector instead. Maybe try feeding it a base64-encoded string rather than an Array?

        Try this:

        att_data = ""
        f = File.open(att_name, "rb")
        f.read.each_byte do |byte|
          att_data << byte
        end
        f.close
        
        att_data_b64 = XMLRPC::Base64.new(att_data)
        
        #Attachment addAttachment(String token, long contentId, Attachment attachment, byte[] attachmentData)
        attach = server.addAttachment(6921, attach, att_data_b64)
        
        1. Feb 26, 2007

          Frithjof Eckhardt says:

          Thanks Matt, it works but... The transformation to Base64 helped if the byte[] ...

          Thanks Matt, it works but...

          The transformation to Base64 helped if the byte[] is joined before. Otherwise the error is:

          ruby/lib/ruby/1.8/xmlrpc/base64.rb:69:in `pack': can't convert Array into String (TypeError)
          

          Also I had to change the type of the contentId back to String again.

          So, the following code does the upload, ...

          ...
          attach = server.getAttachment("6921", att_name, "1")
          att_data = []
          f = File.open(att_name, "rb")
          f.read.each_byte do |byte|
            att_data << byte
          end
          f.close
          
          #Attachment addAttachment(String token, long contentId, Attachment attachment, byte[] attachmentData)
          att_data_64 = XMLRPC::Base64.new(att_data.join)
          attach = server.addAttachment(6921.to_s, attach, att_data_64)
          ...

          ...but the attachment ist corrupted (due to the join of the byte[]). For plain text the Base64 approach works as expected:

          ...
          attach['fileName'] = "Test.txt"
          attach['contentType'] = "text/plain"
          att_data_64 = XMLRPC::Base64.new("Trying to upload files to Confluence.")
          attach = server.addAttachment(6921.to_s, attach, att_data_64)
          ...

          The file Test.txt gets into Confluence without problems. The point now is how to prepare the byte[] before applying the Base64 transformation in order to get binary attachment upload working the same way?

          1. Feb 26, 2007

            Matt Zukowski says:

            Note that I initialize att_data as a string, not as an array, so doing << ...

            Note that I initialize att_data as a string, not as an array, so doing << on it should just append to the string. You shouldn't have to join it. Then again I haven't actually tried running any of this code.

            As to why the data is corrupted... hmm. Maybe try reading the file using sysread, like this:

            f = File.open(att_name, "rb")
            att_data = f.sysread(File.size(att_name))
            
            att_data_b64 = XMLRPC::Base64.new(att_data)
            

            Try it with and without the b flag...

            1. Feb 28, 2007

              Frithjof Eckhardt says:

              Thanks Matt, the initialization as string was the key. Now the following code wo...

              Thanks Matt, the initialization as string was the key. Now the following code works for me to upload binary content to Confluence:

              require 'confluence4r'
              require 'rubygems'
              require 'highline/import'
              
              user     = ask("User: ") {|q| q.echo = true}
              password = ask("Passwort: ") {|q| q.echo = false}
              
              server = Confluence::Server.new("http://wiki")
              server.login(user, password)
              
              # The attachment to be updated
              attach = {
                'fileName'    => "Test.doc",
                'pageId'      => "6921",
                'contentType' => "application/msword",
              }
              
              # Read existing attached document
              begin
                attach = server.getAttachment(attach['pageId'], attach['fileName'], "1")
              rescue RuntimeError => e
                puts "No existing version available for #{attach['fileName']}"
              end
              
              # Read updated local copy of the document
              att_data = ""   # data array does not work, init as string instead
              f = File.open(attach['fileName'], "rb")  # don't forget the 'b' for binary
              f.read.each_byte {|byte| att_data << byte }
              f.close
              
              # Upload updated local copy of the document
              #Attachment addAttachment(String token, long contentId, Attachment attachment, byte[] attachmentData)
              attach = server.addAttachment(attach['pageId'], attach, XMLRPC::Base64.new(att_data))
              1. Apr 16

                Andrew Hill says:

                So close, yet so far! I'm using the code above to try to upload a file to Conflu...

                So close, yet so far! I'm using the code above to try to upload a file to Confluence, but I get the following error:

                /usr/lib/ruby/1.8/xmlrpc/client.rb:554:in `do_rpc': Wrong content-type (RuntimeError)
                        from /usr/lib/ruby/1.8/xmlrpc/client.rb:420:in `call2'
                        from /usr/lib/ruby/1.8/xmlrpc/client.rb:410:in `call'
                        from /usr/lib/ruby/1.8/xmlrpc/client.rb:608:in `send'
                        from /usr/lib/ruby/1.8/xmlrpc/client.rb:608:in `method_missing'
                        from ./confluence4r.rb:32:in `send'
                        from ./confluence4r.rb:32:in `method_missing'
                        from ./test.rb:42
                        from ./test.rb:25:in `each'
                        from ./test.rb:25
                

                Any ideas? TIA!

                1. Apr 17

                  Andrew Hill says:

                  Further investigation today suggests that the Ruby XML-RPC library and Confluenc...

                  Further investigation today suggests that the Ruby XML-RPC library and Confluence are not talking to one another nicely. Looks like Confluence may be responding with Content-Type "text/plain" for the attachement methods. Does anyone know what this is all about? TIA!