## # Originally written by David Heinemeier Hansson, 37signals # Official original version available at # http://www.backpackit.com/api/ruby_wrapper.rb # # Extended by Aaron Suggs, aaron@ktheory.com # * Includes multiple list support as described at # http://danontopic.com/public/pages/backpackApiLists.html # # See included list-tester.rb for example usage # # Extended by Geoffrey Grosenbach # * Can read YAML config file automatically with Backpack.init_from_config # * Uses Hpricot for XML parsing # * http://topfunky.com # require 'rubygems' require 'yaml' require 'net/https' require 'open-uri' require 'cgi' require 'hpricot' class Backpack attr_accessor :username, :token, :current_page_id, :config def self.init_from_config config_file = File.expand_path('~/.backpackrc') return unless File.exist?(config_file) @config = YAML.load File.open(config_file).read self.new(@config['username'], @config['api_key'], @config['use_ssl']) end def initialize(username, token, use_ssl=true) @username, @token, @use_ssl = username, token, use_ssl connect(@use_ssl) end ## # Returns the base domain for this account. # # Examples: # # http://maggie.backpackit.com # https://homer.backpackit.com def domain (@use_ssl ? 'https' : 'http') + "://#{@username}.backpackit.com" end def connect(use_ssl = true) @connection = Net::HTTP.new("#{@username}.backpackit.com", use_ssl ? 443 : 80) @connection.use_ssl = use_ssl @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if use_ssl end def page_id=(id) self.current_page_id = id end alias :pi= :page_id= def request(path, parameters = {}, second_try = false) parameters = { "token" => @token }.merge(parameters) response = @connection.post("/ws/#{path}", parameters.to_yaml, "X-POST_DATA_FORMAT" => "yaml") if response.code == "200" result = Hpricot.XML(response.body) # result.delete "success" # result.empty? ? true : result result elsif response.code == "302" && !second_try connect(@use_ssl) request(path, parameters, true) else raise "Error occured (#{response.code}): #{response.body}" end end alias :r :request # Lists ---- def list_lists(page_id = current_page_id) request "page/#{page_id}/lists/list" end alias :ll :list_lists def create_list(name, page_id = current_page_id) request "page/#{page_id}/lists/add", "name" => name end alias :cl :create_list def update_list(list_id, name, page_id = current_page_id) request "page/#{page_id}/lists/update/#{list_id}", "list" => { "name" => name } end alias :ul :update_list def destroy_list(list_id, page_id = current_page_id) request "page/#{page_id}/lists/destroy/#{list_id}" end alias :dl :destroy_list # Not yet implemented # def move_list(list_id, direction, page_id = current_page_id) # request "page/#{page_id}/lists/move/#{list_id}", "direction" => "move_#{direction}" # end # alias :ml :move_list # Items ---- def list_items(page_id = current_page_id, list_id = '') request "page/#{page_id}/items/list?list_id=#{list_id}" end alias :li :list_items def create_item(content, page_id = current_page_id, list_id = '') request "page/#{page_id}/items/add?list_id=#{list_id}", "item" => { "content" => content } end alias :ci :create_item def update_item(item_id, content, page_id = current_page_id) request "page/#{page_id}/items/update/#{item_id}", "item" => { "content" => content } end alias :ui :update_item def destroy_item(item_id, page_id = current_page_id) request "page/#{page_id}/items/destroy/#{item_id}" end alias :di :destroy_item def toggle_item(item_id, page_id = current_page_id) request "page/#{page_id}/items/toggle/#{item_id}" end alias :ti :toggle_item def move_item(item_id, direction, page_id = current_page_id) request "page/#{page_id}/items/move/#{item_id}", "direction" => "move_#{direction}" end alias :mi :move_item # Notes ---- def list_notes(page_id = current_page_id) request "page/#{page_id}/notes/list" end alias :ln :list_notes def create_note(title, body, page_id = current_page_id) request "page/#{page_id}/notes/create", "note" => { "title" => title, "body" => body } end alias :cn :create_note def update_note(note_id, title, body, page_id = current_page_id) request "page/#{page_id}/notes/update/#{note_id}", "note" => { "title" => title, "body" => body } end alias :un :update_note def destroy_note(note_id, page_id = current_page_id) request "page/#{page_id}/notes/destroy/#{note_id}" end alias :dn :destroy_note # Emails ---- def list_emails(page_id = current_page_id) request "page/#{page_id}/emails/list" end alias :le :list_emails def show_email(email_id, page_id = current_page_id) request "page/#{page_id}/emails/show/#{email_id}" end alias :se :show_email def destroy_email(email_id, page_id = current_page_id) request "page/#{page_id}/emails/destroy/#{email_id}" end alias :de :destroy_email # Tags ---- def list_pages_with_tag(tag_id) request "tags/select/#{tag_id}" end alias :lpt :list_pages_with_tag def tag_page(tags, page_id = current_page_id) request "page/#{page_id}/tags/tag", "tags" => tags end alias :tp :tag_page # Pages ---- def list_pages request "pages/all" end alias :lp :list_pages def create_page(title, body) request "pages/new", "page" => { "title" => title, "description" => body } end alias :cp :create_page def show_page(page_id = current_page_id) request "page/#{page_id}" end alias :sp :show_page def destroy_page(page_id = current_page_id) request "page/#{page_id}/destroy" end alias :dp :destroy_page def update_title(title, page_id = current_page_id) request "page/#{page_id}/update_title", "page" => { "title" => title } end alias :ut :update_title def update_body(body, page_id = current_page_id) request "page/#{page_id}/update_body", "page" => { "description" => body } end alias :ub :update_body def link_page(linked_page_id, page_id = current_page_id) request "page/#{page_id}/link", "linked_page_id" => linked_page_id end alias :lip :link_page def unlink_page(linked_page_id, page_id = current_page_id) request "page/#{page_id}/unlink", "linked_page_id" => linked_page_id end alias :ulip :unlink_page def share_page(email_addresses, public_page = nil, page_id = current_page_id) parameters = { "email_addresses" => email_addresses } parameters = parameters.merge({ "page" => { "public" => public_page ? "1" : "0" }}) unless public_page.nil? request "page/#{page_id}/share", parameters end # Reminders --- def list_reminders request "reminders" end alias :lr :list_reminders def create_reminder(content, remind_at = "") request "reminders/create", "reminder" => { "content" => content, "remind_at" => remind_at } end alias :cr :create_reminder def update_reminder(reminder_id, content, remind_at) request "reminders/update/#{reminder_id}", "reminder" => { "content" => content, "remind_at" => remind_at } end alias :ur :update_reminder def destroy_reminder(reminder_id) request "reminders/destroy/#{reminder_id}" end alias :dr :destroy_reminder # Account Export -- def export request "account/export" end # Files --- def login @req = Net::HTTP::new("#{@config['username']}.backpackit.com", @config['use_ssl'] ? 443 : 80) @req.use_ssl = @config['use_ssl'] @req.verify_mode = OpenSSL::SSL::VERIFY_NONE if @config['use_ssl'] headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } params = "username=#{CGI.escape(@config['username'])}&password=#{CGI.escape(@config['password'])}" debug params res = @req.post("/account/authorize", params, headers) debug res.body @headers = { 'Cookie' => res.response['set-cookie'] } end ## # TODO Doesn't work yet.** def upload_file(file_path, page_id) debug "Uploading #{file_path}" if @req.nil? login end debug "Posting file" res = Net::HTTP.post_form( URI.parse("#{domain}/page/#{page_id}/assets/create"), { "asset[type]" => "Attachment", "asset[file]" => File.open(file_path).read }, @headers) debug res.body end protected def debug(message) return unless @config['debug'] case message when String puts message else y message end end end