#!/usr/bin/ruby -w # Path to custom-installed Ruby Gems require '/home/imsop/gem-paths.rb' require 'date' require 'cgi' require 'iconv' require 'icalendar' require 'net/http' require 'net/https' require 'rexml/document' # Adapted from http://blog.patrick-morgan.net/2007/02/ruby-https-with-http-basic-auth.html def get_intervals_response(path, auth_token) http = Net::HTTP.new('api.myintervals.com',443) req = Net::HTTP::Get.new(path) http.use_ssl = true req.basic_auth auth_token, 'X' req['Accept'] = 'text/xml' response = http.request(req) return response.body end ## MAIN ## begin cgi = CGI.new # Find out the user's "person ID" me_xml = get_intervals_response('/me/', cgi['token']); me_rexml = REXML::Document.new me_xml person_id = me_rexml.elements['//id'].text # Do we want information on closed tasks? if cgi['tasks'] == 'all' then excludeclosed = 'f'; else excludeclosed = 't'; end # Find tasks assigned to that user task_xml = get_intervals_response("/task/?assigneeid=#{person_id}&excludeclosed=#{excludeclosed}&limit=100", cgi['token']); task_rexml = REXML::Document.new task_xml # Re-encode broken chars, eliminating anything that's not in ISO 8859-15 # (this is extremely hacky, but just using UTF-8//IGNORE left characters that choked Lightning) def fix_encoding(s) s = Iconv.conv('ISO-8859-15//IGNORE', 'UTF-8', s) Iconv.conv('UTF-8', 'ISO-8859-15', s) end # Create the calendar cal = Icalendar::Calendar.new task_rexml.elements.each('//item') { |xml_node| if cgi['due_events'] == 'all' || cgi['due_events'] == 'smart' then if xml_node.elements['datedue'].text != nil && xml_node.elements['status'].text != 'Closed' then event_due_date = Date.parse xml_node.elements['datedue'].text; # "smart" events mode over-rides overdue events with today's date if cgi['due_events'] == 'smart' && event_due_date < Date.today then event_due_date = Date.today; end cal.event do dtstart event_due_date dtend dtstart + 1 summary "##{xml_node.elements['localid'].text}: #{xml_node.elements['title'].text}" # Strip HTML tags from summary, and hope the result is sane... if xml_node.elements['summary'].text != nil then description fix_encoding( xml_node.elements['summary'].text.gsub(%r{]+?>}, '') ) end end end end if cgi['tasks'] == 'all' || (cgi['tasks'] == 'open' && xml_node.elements['status'].text != 'Closed') then cal.todo do sequence xml_node.elements['localid'].text summary "##{xml_node.elements['localid'].text}: #{xml_node.elements['title'].text}" dtstart Date.parse xml_node.elements['dateopen'].text if xml_node.elements['datedue'].text != nil then due Date.parse xml_node.elements['datedue'].text end if xml_node.elements['status'].text == 'Closed' then completed Date.parse xml_node.elements['dateclosed'].text percent 100 status "COMPLETED" # Lightning appears to require this in order to display the closed tasks correctly custom_property('PERCENT-COMPLETE', '100') end # Strip HTML tags from summary, and hope the result is sane... if xml_node.elements['summary'].text != nil then description fix_encoding( xml_node.elements['summary'].text.gsub(%r{]+?>}, '') ) end end end } cgi.out ( cgi['send_plain'] == 'true' ? 'text/plain' : 'text/calendar' ) { cal.to_ical } rescue Exception => err cgi.out ( 'text/plain' ) { "Exception: #{err}" } end