#!/usr/bin/ruby
module GoogleAppsApi #:nodoc:
module Calendar
class Api < GoogleAppsApi::BaseApi
attr_reader :token
def initialize(*args)
super(:calendar, *args)
end
def set_calendar_for_user(calendar, user, *args)
options = args.extract_options!
existing_acl = "none"
need_to_create = false
cal = retrieve_calendar_for_user(calendar, user)
if cal
existing_acl = cal.details[:accesslevel]
else
need_to_create = true
end
acl = options.delete(:accesslevel) || existing_acl
if need_to_create
raise "Must set accesslevel for a newly added calendar" unless acl
owner_acl = CalendarAcl.new(:calendar => calendar, :scope => user, :role => "owner")
set_calendar_acl(owner_acl)
add_calendar_to_user(calendar, user)
end
if acl == "none"
remove_calendar_from_user(calendar, user) unless existing_acl == "none"
else
if acl != existing_acl
set_calendar_acl(CalendarAcl.new(:calendar => calendar, :scope => user, :role => acl))
end
update_calendar_for_user(calendar, user, options) unless options.empty?
end
end
def add_calendar_to_user(calendar, user, *args)
req = <<-DESCXML
#{calendar.full_id_escaped}
DESCXML
options = args.extract_options!.merge(:username => user.full_id_escaped, :body => req.strip)
request(:add_calendar_to_user, options)
end
def remove_calendar_from_user(calendar, user, *args)
options = args.extract_options!.merge(:username => user.full_id_escaped, :calendar => calendar.full_id_escaped)
request(:remove_calendar_from_user, options)
end
def update_calendar_for_user(calendar, user, *args)
username = user.full_id_escaped
cal = nil
cal = retrieve_calendar_for_user(calendar, user)
cal_id = cal.full_id_escaped
options = args.extract_options!.merge(:username => username, :calendar => cal_id)
details = cal.details.merge(options)
req = <<-DESCXML
http://www.google.com/calendar/feeds/#{username}/allcalendars/full/#{cal_id}
#{details[:title]}
#{details[:summary]}
DESCXML
options[:body] = req.strip
request(:update_calendar_for_user, options)
end
# lists all calendards for a user
def retrieve_calendars_for_user(user, *args)
options = args.extract_options!.merge(:username => user.full_id_escaped)
request(:retrieve_calendars_for_user, options)
end
# lists all calendards for a user
def retrieve_calendar_for_user(calendar, user, *args)
options = args.extract_options!.merge(:calendar => calendar.full_id_escaped, :username => user.full_id_escaped)
retries = options[:retries] || 5
while (retries > 0)
begin
return request(:retrieve_calendar_for_user, options)
rescue GDataError => g
retries -= 1
sleep 0.5
end
end
return nil
end
# returns array of acls for a given calendar
def retrieve_acls_for_calendar(calendar, *args)
options = args.extract_options!.merge(:calendar => calendar.full_id_escaped)
request(:retrieve_acls_for_calendar, options)
end
# generally, use set_calendar_acl, it works for both creates and updates
# this will throw an exception "Version Conflict" if it already exists
def create_calendar_acl(acl, *args)
req = <<-DESCXML
DESCXML
options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :body => req.strip)
request(:create_calendar_acl, options)
end
# you can substitute set_calendar_acl with role set to none
def remove_calendar_acl(acl, *args)
options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :scope => acl.scope.qualified_id_escaped)
request(:remove_calendar_acl, options)
end
# updates a given acl for a given scope
def set_calendar_acl(acl, *args)
req = <<-DESCXML
DESCXML
options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :scope => acl.scope.qualified_id_escaped, :body => req.strip)
request(:set_calendar_acl, options)
end
end
end
class CalendarAcl
attr_accessor :calendar, :scope, :role
def initialize(*args)
options = args.extract_options!
if (_xml = options[:xml])
_xml = _xml.kind_of?(Nokogiri::XML::Document) ? _xml.children.first : _xml
scope_kind = _xml.at_css('gAcl|scope').attribute("type").content
scope_id = _xml.at_css('gAcl|scope').attribute("value").content
@scope = Entity.new(scope_kind => scope_id)
@role = _xml.at_css('title').content
@calendar = CalendarEntity.new(_xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,""))
else
@scope = options[:scope]
@role = options[:role]
@calendar = options[:calendar]
end
end
def role_schema
if @role == "none"
"none"
else
"http://schemas.google.com/gCal/2005##{@role}"
end
end
end
# def role_url
# "http://schemas.google.com/gCal/2005#{@role}"
# end
# end
class CalendarEntity < Entity
attr_reader :details
def initialize(*args)
@details = {}
options = args.extract_options!
if options.has_key?(:xml)
parse_calendar_xml(options[:xml])
super(:calendar => @calendar_id)
else
@title = options.delete(:title)
if args.first.kind_of?(String)
super(:calendar => args.first)
else
super(options.merge(:kind => "calendar"))
end
end
end
def ==(other)
super(other)
end
#
# # updates with details.
# def refresh_details!(c_api, *args)
# c_api.retrieve_calendar_details(self, *args)
# end
#
def get_acls(c_api, *args)
c_api.retrieve_acls_for_calendar(self, *args)
end
private
def parse_calendar_xml(xml)
entry = xml.kind_of?(Nokogiri::XML::Document) ? xml.children.first : xml
@kind = "calendar"
case entry.attribute("kind").content
when "calendar#calendar"
@calendar_id = entry.at_css('id').content.gsub(/^.*calendars\//,"")
@details[:title] = entry.at_css('title').content
@details[:summary] = entry.at_css('summary')
@details[:summary] = @details[:summary] ? @details[:summary].content : ""
@details[:timezone] = entry.at_css('gCal|timezone').attribute("value").content
@details[:accesslevel] = entry.at_css('gCal|accesslevel').attribute("value").content
@details[:where] = entry.at_css('gd|where')
@details[:where] = @details[:where] ? @details[:where].attribute("valueString").content : ""
@details[:color] = entry.at_css('gCal|color').attribute("value").content
@details[:hidden] = entry.at_css('gCal|hidden').attribute("value").content == "true"
@details[:selected] = entry.at_css('gCal|selected').attribute("value").content == "true"
when "calendar#acl"
@calendar_id = entry.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,"")
end
end
end
# acl names: none, read, freebusy, editor, owner, root
# class CalendarEntry
# attr_accessor :calendar_id, :title, :entity, :role
# def initialize(*args)
# options = args.extract_options!
# if (_xml = options[:xml])
# kind = _xml.attribute("kind")
# raise "invalid xml passed" unless kind
#
# case kind.content
# when "calendar#calendar"
# user_id = _xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/.*$/,"")
#
# @entity = Entity.new("user", user_id)
# @role = _xml.at_css('gCal|accesslevel').attribute("value").content
#
# @calendar_id = _xml.at_css('id').content.gsub(/^.*calendars\//,"")
# @title = _xml.at_css('title').content
# when "calendar#acl"
# scope_type = _xml.at_css('gAcl|scope').attribute("type").content
# scope_id = _xml.at_css('gAcl|scope').attribute("value").content
# @entity = Entity.new(scope_type, scope_id)
# @role = _xml.at_css('title').content
# @calendar_id = _xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,"")
# end
# else
# @calendar_id = options[:calendar_id]
# @title = options[:title]
# @entity = options[:entity]
# @role = options[:role]
#
# end
#
# raise "Calendar ID required" unless @calendar_id
#
# end
#
# def to_s
# @calendar_id
# end
#
# def to_url
# CGI::escape(CGI::unescape(@calendar_id))
# end
#
# def add_message
# req = <<-DESCXML
#
# #{CGI::escape(calendar_id.to_s)}
#
# DESCXML
#
# req.strip
# end
# end
end