#!/opt/local/bin/ruby # # Created by Stuart Johnston. # For more information visit, http://blog.ifbydesign.com # require 'rubygems' require 'hpricot' require 'open-uri' require 'vpim/vcard' require 'net/http' # --- SETUP --- # # Enter your Highrise token, found under "My Info" HIGHRISE_TOKEN = 'YOUR_TOKEN' # Enter your Highrise site address, http://.highrisehq.com HIGHRISE_ADDRESS = 'YOUR_ADDRESS' # --- END SETUP --- # # Extend Vpim to include some useful methods module Vpim class Vcard class Maker # for adding a job title def add_title(value) @card << Vpim::DirectoryInfo::Field.create( 'TITLE', value.to_str ); end # for adding a company def add_org(value) @card << Vpim::DirectoryInfo::Field.create( 'ORG', value.to_str ); end # for marking a vCard as a company def set_as_company @card << Vpim::DirectoryInfo::Field.create( 'X-ABShowAs', 'COMPANY' ); end end end end def export puts 'Exporting...' @elements.each do |id,item| # Create the card card = Vpim::Vcard::Maker.make2 do |maker| # Add name maker.add_name do |name| if item.name == 'company' name.fullname = item.at("name").inner_html else name.given = item.at("first-name").inner_html name.family = item.at("last-name").inner_html end end if item.name == 'company' maker.add_org(item.at("name").inner_html) rescue nil maker.set_as_company else # Add job title and company maker.add_title(item.at("title").inner_html) rescue nil maker.add_org(@companies[item.at("company-id").inner_html].at("name").inner_html) rescue nil end # Add contact details maker = add_email_addresses(maker,item) maker = add_addresses(maker,item) maker = add_im_addresses(maker,item) maker = add_web_addresses(maker,item) maker = add_phone_numbers(maker,item) end puts ' ' + item.name + ': ' + card.name.fullname # Attempt to download photograph begin Net::HTTP.start('asset1.highrisehq.com') {|http| req = Net::HTTP::Get.new('/avatars/' + item.name + '/0' + id[0..2] + '/' + id[3..6] + '-large.png') response = http.request(req) @photo = response.body } # Tweak for compatibility with Mac OS X Address Book from: http://blogs.23.nu/c0re/stories/9671/ photo = [@photo].pack('m').to_s photo = photo.gsub(/[ \n]/, '').scan(/.{1,76}/).join("\n ") card.to_s.sub!('END:VCARD', "PHOTO;BASE64:\n " + photo + "\nEND:VCARD") rescue # No photo found end @vcards.push(card) end puts '... Finished exporting' end def save_vcards # Write cards to file puts 'Saving cards to highrise.vcf' open('highrise.vcf', 'w').write @vcards.join('') end def fetch(type) count = nil total = 0 puts 'Fetching list of ' + type + '...' # The lists are paged at 50, therefore loop until less than 50 elements are returned while (count.nil? or count >= 50) do # Fetch XML list of people using API xml = Hpricot(open("http://#{HIGHRISE_ADDRESS}.highrisehq.com/#{type}.xml?n=#{total}", :http_basic_authentication=>[HIGHRISE_TOKEN, 'X'])) items = xml.root.containers count = items.length total += items.length # Create a hash of people indexed by their id items.each do |item| @elements[item.at("id").inner_html] = item end end puts '... Found ' + total.to_s + ' ' + type end def add_email_addresses(maker,xml) email_addresses = (xml/"email-address") email_addresses.each do |email| maker.add_email(email.at("address").inner_html) do |e| e.location = email.at("location").inner_html e.preferred = true if email == email_addresses.first end end if email_addresses.any? return maker end def add_addresses(maker,xml) addresses = (xml.at("addresses")/"address") addresses.each do |address| maker.add_addr do |a| a.street = address.at("street").inner_html.gsub(/\r/,'') rescue '' a.locality = address.at("city").inner_html rescue '' a.region = address.at("state").inner_html rescue '' a.postalcode = address.at("zip").inner_html rescue '' a.country = address.at("country").inner_html rescue '' a.location = address.at("location").inner_html rescue '' a.preferred = true if address == addresses.first end end if addresses.any? return maker end def add_im_addresses(maker,xml) im_addresses = (xml/"instant-messenger") im_addresses.each do |im| maker.add_impp(im.at("address").inner_html) do |i| i.location = im.at("location").inner_html i.preferred = true if im == im_addresses.first end end if im_addresses.any? return maker end def add_web_addresses(maker,xml) web_addresses = (xml/"web-address") web_addresses.each do |web| maker.add_url(web.at("url").inner_html) do |w| w.location = web.at("location").inner_html w.preferred = true if web == web_addresses.first end end if web_addresses.any? return maker end def add_phone_numbers(maker,xml) phone_numbers = (xml/"phone-number") phone_numbers.each do |phone| maker.add_tel(phone.at("number").inner_html) do |t| t.location = phone.at("location").inner_html t.preferred = true if phone == phone_numbers.first end end if phone_numbers.any? return maker end puts ' --- STARTING EXPORT ---' @vcards = [] @elements = {} fetch('people') fetch('companies') export save_vcards puts ' --- FINISHED ---'