gcal4ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +83 -0
- data/lib/gcal4ruby/base.rb +256 -0
- data/lib/gcal4ruby/calendar.rb +297 -0
- data/lib/gcal4ruby/event.rb +403 -0
- data/lib/gcal4ruby/recurrence.rb +164 -0
- data/lib/gcal4ruby/service.rb +61 -0
- data/lib/gcal4ruby.rb +1 -0
- data/test/unit.rb +120 -0
- metadata +59 -0
data/README
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#=GCal4Ruby
|
2
|
+
#
|
3
|
+
#==Introduction
|
4
|
+
#GCal4Ruby is a full featured wrapper for the google calendar API. GCal4Ruby implements
|
5
|
+
#all of the functionality available through the Google Calnedar API, including permissions,
|
6
|
+
#attendees, reminders and event recurrence.
|
7
|
+
#
|
8
|
+
#==Author and Contact Information
|
9
|
+
#GCal4Ruby was created and is maintained by {Mike Reich}[mailto:mike@seabourneconsulting.com]
|
10
|
+
#and is licenses under the GPL v2. Feel free to use and update, but be sure to contribute your
|
11
|
+
#code back to the project and attribute as required by the license.
|
12
|
+
#
|
13
|
+
#==Description
|
14
|
+
#GCal4Ruby has three major components: the service, calendar and event objects. Each service
|
15
|
+
#has many calendars, which in turn have many events. Each service is the representation of a
|
16
|
+
#google account, and thus must be successfully authenticated using valid Google Calendar
|
17
|
+
#account credentials.
|
18
|
+
#
|
19
|
+
#==Examples
|
20
|
+
#Below are some common usage examples. For more examples, check the documentation.
|
21
|
+
#===Service
|
22
|
+
#1. Authenticate
|
23
|
+
# service = Service.new
|
24
|
+
# service.authenticate("user@gmail.com", "password")
|
25
|
+
#
|
26
|
+
#2. Get Calendar List
|
27
|
+
# calendars = service.calendars
|
28
|
+
#
|
29
|
+
#===Calendar
|
30
|
+
#All usages assume a successfully authenticated Service.
|
31
|
+
#1. Create a new Calendar
|
32
|
+
# cal = Calendar.new(service)
|
33
|
+
#
|
34
|
+
#2. Find an existing Calendar
|
35
|
+
# cal = Calendar.find(service, "New Calendar", :first)
|
36
|
+
#
|
37
|
+
#3. Find all calendars containing the search term
|
38
|
+
# cal = Calendar.find(service, "Soccer Team")
|
39
|
+
#
|
40
|
+
#4. Find a calendar by ID
|
41
|
+
# cal = Calendar.find(service, id, :first)
|
42
|
+
#===Event
|
43
|
+
#All usages assume a successfully authenticated Service and valid Calendar.
|
44
|
+
#1. Create a new Event
|
45
|
+
# event = Event.new(calendar)
|
46
|
+
# event.title = "Soccer Game"
|
47
|
+
# event.start = Time.parse("12-06-2009 at 12:30 PM")
|
48
|
+
# event.end = Time.parse("12-06-2009 at 1:30 PM")
|
49
|
+
# event.where = "Merry Playfields"
|
50
|
+
# event.save
|
51
|
+
#
|
52
|
+
#2. Find an existing Event
|
53
|
+
# event = Event.find(cal, "Soccer Game", :first)
|
54
|
+
#
|
55
|
+
#3. Find all events containing the search term
|
56
|
+
# event = Event.find(cal, "Soccer Game")
|
57
|
+
#
|
58
|
+
#4. Create a recurring event for every saturday
|
59
|
+
# event = Event.new(calendar)
|
60
|
+
# event.title = "Baseball Game"
|
61
|
+
# event.where = "Municipal Stadium"
|
62
|
+
# event.recurrence = Recurrence.new
|
63
|
+
# event.recurrence.start = Time.parse("13-06-2009 at 4:30 PM")
|
64
|
+
# event.recurrence.end = Time.parse("13-06-2009 at 6:30 PM")
|
65
|
+
# event.recurrence.frequency = {"weekly" => ["SA"]}
|
66
|
+
# event.save
|
67
|
+
#
|
68
|
+
#5. Create an event with a 15 minute email reminder
|
69
|
+
# event = Event.new(calendar)
|
70
|
+
# event.title = "Dinner with Kate"
|
71
|
+
# event.start = Time.parse("20-06-2009 at 5 pm")
|
72
|
+
# event.end = Time.parse("20-06-209 at 8 pm")
|
73
|
+
# event.where = "Luigi's"
|
74
|
+
# event.reminder = {:minutes => 15, :method => 'email'}
|
75
|
+
# event.save
|
76
|
+
#
|
77
|
+
#6. Create an event with attendees
|
78
|
+
# event = Event.new(calendar)
|
79
|
+
# event.title = "Dinner with Kate"
|
80
|
+
# event.start = Time.parse("20-06-2009 at 5 pm")
|
81
|
+
# event.end = Time.parse("20-06-209 at 8 pm")
|
82
|
+
# event.attendees => {:name => "Kate", :email => "kate@gmail.com"}
|
83
|
+
# event.save
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
require "cgi"
|
3
|
+
require "uri"
|
4
|
+
require "net/http"
|
5
|
+
require "net/https"
|
6
|
+
require "open-uri"
|
7
|
+
require "nkf"
|
8
|
+
require "time"
|
9
|
+
|
10
|
+
Net::HTTP.version_1_2
|
11
|
+
|
12
|
+
# GCal4Ruby is a full featured wrapper for the google calendar API
|
13
|
+
|
14
|
+
# =Usage:
|
15
|
+
|
16
|
+
module GCal4Ruby
|
17
|
+
|
18
|
+
CALENDAR_XML = "<entry xmlns='http://www.w3.org/2005/Atom'
|
19
|
+
xmlns:gd='http://schemas.google.com/g/2005'
|
20
|
+
xmlns:gCal='http://schemas.google.com/gCal/2005'>
|
21
|
+
<title type='text'></title>
|
22
|
+
<summary type='text'></summary>
|
23
|
+
<gCal:timezone value=''></gCal:timezone>
|
24
|
+
<gCal:hidden value=''></gCal:hidden>
|
25
|
+
<gCal:color value=''></gCal:color>
|
26
|
+
<gd:where rel='' label='' valueString=''></gd:where>
|
27
|
+
</entry>"
|
28
|
+
|
29
|
+
ACL_XML = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>
|
30
|
+
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/acl/2007#accessRule'/>
|
31
|
+
<gAcl:scope type='default'></gAcl:scope>
|
32
|
+
<gAcl:role value=''></gAcl:role>
|
33
|
+
</entry>"
|
34
|
+
|
35
|
+
EVENT_XML = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
|
36
|
+
<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'></category>
|
37
|
+
<title type='text'></title>
|
38
|
+
<content type='text'></content>
|
39
|
+
<gd:transparency value=''></gd:transparency>
|
40
|
+
<gd:eventStatus value=''></gd:eventStatus>
|
41
|
+
<gd:where valueString=''></gd:where>
|
42
|
+
<gd:when startTime='' endTime=''></gd:when>
|
43
|
+
</entry>
|
44
|
+
"
|
45
|
+
|
46
|
+
class AuthenticationFailed < StandardError; end #:nodoc: all
|
47
|
+
|
48
|
+
class NotAuthenticated < StandardError; end
|
49
|
+
|
50
|
+
class InvalidService < StandardError; end
|
51
|
+
|
52
|
+
class HTTPPostFailed < StandardError; end
|
53
|
+
|
54
|
+
class HTTPPutFailed < StandardError; end
|
55
|
+
|
56
|
+
class HTTPGetFailed < StandardError; end
|
57
|
+
|
58
|
+
class HTTPDeleteFailed < StandardError; end
|
59
|
+
|
60
|
+
class CalendarSaveFailed < StandardError; end
|
61
|
+
|
62
|
+
class EventSaveFailed < StandardError; end
|
63
|
+
|
64
|
+
class RecurrenceValueError < StandardError; end
|
65
|
+
|
66
|
+
#The ProxyInfo class contains information for configuring a proxy connection
|
67
|
+
|
68
|
+
class ProxyInfo
|
69
|
+
attr_accessor :address, :port, :username, :password
|
70
|
+
@address = nil
|
71
|
+
@port = nil
|
72
|
+
@username = nil
|
73
|
+
@password = nil
|
74
|
+
|
75
|
+
#The initialize function accepts four variables for configuring the ProxyInfo object.
|
76
|
+
#The proxy connection is initiated using the builtin Net::HTTP proxy support.
|
77
|
+
|
78
|
+
def initialize(address, port, username=nil, password=nil)
|
79
|
+
@address = address
|
80
|
+
@port = port
|
81
|
+
@username = username
|
82
|
+
@password = password
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#The Base class includes the basic HTTP methods for sending and receiving
|
87
|
+
#messages from the Google Calendar API. You shouldn't have to use this class
|
88
|
+
#directly, rather access the functionality through the Service subclass.
|
89
|
+
|
90
|
+
class Base
|
91
|
+
AUTH_URL = "https://www.google.com/accounts/ClientLogin"
|
92
|
+
CALENDAR_LIST_FEED = "http://www.google.com/calendar/feeds/default/allcalendars/full"
|
93
|
+
@proxy_info = nil
|
94
|
+
@auth_token = nil
|
95
|
+
@debug = false
|
96
|
+
|
97
|
+
#Contains the ProxyInfo object for using a proxy server
|
98
|
+
attr_accessor :proxy_info
|
99
|
+
|
100
|
+
#If set to true, debug will dump all raw HTTP requests and responses
|
101
|
+
attr_accessor :debug
|
102
|
+
|
103
|
+
# Sends an HTTP POST request. The header should be a hash of name/value pairs.
|
104
|
+
# Returns the Net::HTTPResponse object on succces, or raises the appropriate
|
105
|
+
# error if a non 20x response code is received.
|
106
|
+
def send_post(url, content, header=nil)
|
107
|
+
header = auth_header(header)
|
108
|
+
ret = nil
|
109
|
+
location = URI.parse(url)
|
110
|
+
https = get_http_object(location)
|
111
|
+
puts "url = "+url if @debug
|
112
|
+
if location.scheme == 'https'
|
113
|
+
puts "SSL True" if @debug
|
114
|
+
https.use_ssl = true
|
115
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
116
|
+
end
|
117
|
+
puts "Starting post\nHeader: #{header}\nContent: #{content}" if @debug
|
118
|
+
https.start do |http|
|
119
|
+
ret = http.post(location.path, content, header)
|
120
|
+
end
|
121
|
+
while ret.is_a?(Net::HTTPRedirection)
|
122
|
+
puts "Redirect recieved, resending post" if @debug
|
123
|
+
https.start do |http|
|
124
|
+
ret = http.post(ret['location'], content, header)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
if ret.is_a?(Net::HTTPSuccess)
|
128
|
+
puts "20x response recieved\nResponse: \n"+ret.read_body if @debug
|
129
|
+
return ret
|
130
|
+
else
|
131
|
+
puts "invalid response received: "+ret.code if @debug
|
132
|
+
raise HTTPPostFailed, ret.body
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Sends an HTTP PUT request. The header should be a hash of name/value pairs.
|
137
|
+
# Returns the Net::HTTPResponse object on succces, or raises the appropriate
|
138
|
+
# error if a non 20x response code is received.
|
139
|
+
def send_put(url, content, header=nil)
|
140
|
+
header = auth_header(header)
|
141
|
+
ret = nil
|
142
|
+
location = URI.parse(url)
|
143
|
+
https = get_http_object(location)
|
144
|
+
puts "url = "+url if @debug
|
145
|
+
if location.scheme == 'https'
|
146
|
+
puts "SSL True" if @debug
|
147
|
+
https.use_ssl = true
|
148
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
149
|
+
end
|
150
|
+
puts "Starting post\nHeader: #{header}\nContent: #{content}" if @debug
|
151
|
+
https.start do |http|
|
152
|
+
ret = http.put(location.path, content, header)
|
153
|
+
end
|
154
|
+
while ret.is_a?(Net::HTTPRedirection)
|
155
|
+
puts "Redirect recieved, resending post" if @debug
|
156
|
+
https.start do |http|
|
157
|
+
ret = http.put(ret['location'], content, header)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
if ret.is_a?(Net::HTTPSuccess)
|
161
|
+
puts "20x response recieved\nResponse: \n"+ret.read_body if @debug
|
162
|
+
return ret
|
163
|
+
else
|
164
|
+
puts "invalid response received: "+ret.code if @debug
|
165
|
+
raise HTTPPutFailed, ret.body
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Sends an HTTP GET request. The header should be a hash of name/value pairs.
|
170
|
+
# Returns the Net::HTTPResponse object on succces, or raises the appropriate
|
171
|
+
# error if a non 20x response code is received.
|
172
|
+
def send_get(url, header = nil)
|
173
|
+
header = auth_header(header)
|
174
|
+
ret = nil
|
175
|
+
location = URI.parse(url)
|
176
|
+
http = get_http_object(location)
|
177
|
+
puts "url = "+url if @debug
|
178
|
+
if location.scheme == 'https'
|
179
|
+
puts "SSL True" if @debug
|
180
|
+
https.use_ssl = true
|
181
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
182
|
+
end
|
183
|
+
puts "Starting post\nHeader: #{header}\n" if @debug
|
184
|
+
http.start do |http|
|
185
|
+
ret = http.get(location.path, header)
|
186
|
+
end
|
187
|
+
while ret.is_a?(Net::HTTPRedirection)
|
188
|
+
puts "Redirect recieved, resending post" if @debug
|
189
|
+
http.start do |http|
|
190
|
+
ret = http.get(ret['location'], header)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
if ret.is_a?(Net::HTTPSuccess)
|
194
|
+
puts "20x response recieved\nResponse: \n"+ret.read_body if @debug
|
195
|
+
return ret
|
196
|
+
else
|
197
|
+
puts "Redirect recieved, resending post" if @debug
|
198
|
+
raise HTTPGetFailed, ret.body
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Sends an HTTP DELETE request. The header should be a hash of name/value pairs.
|
203
|
+
# Returns the Net::HTTPResponse object on succces, or raises the appropriate
|
204
|
+
# error if a non 20x response code is received.
|
205
|
+
def send_delete(url, header = nil)
|
206
|
+
header = auth_header(header)
|
207
|
+
ret = nil
|
208
|
+
location = URI.parse(url)
|
209
|
+
https = get_http_object(location)
|
210
|
+
puts "url = "+url if @debug
|
211
|
+
if location.scheme == 'https'
|
212
|
+
puts "SSL True" if @debug
|
213
|
+
https.use_ssl = true
|
214
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
215
|
+
end
|
216
|
+
puts "Starting post\nHeader: #{header}\n" if @debug
|
217
|
+
https.start do |http|
|
218
|
+
ret = http.delete(location.path, header)
|
219
|
+
end
|
220
|
+
while ret.is_a?(Net::HTTPRedirection)
|
221
|
+
puts "Redirect recieved, resending post" if @debug
|
222
|
+
https.start do |http|
|
223
|
+
ret = http.delete(ret['location'], header)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
if ret.is_a?(Net::HTTPSuccess)
|
227
|
+
puts "20x response recieved\nResponse: \n"+ret.read_body if @debug
|
228
|
+
return true
|
229
|
+
else
|
230
|
+
puts "invalid response received: "+ret.code if @debug
|
231
|
+
raise HTTPDeleteFailed, ret.body
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def get_http_object(location)
|
238
|
+
if @proxy_info and @proxy_info.address
|
239
|
+
return Net::HTTP.new(location.host, location.port, @proxy_info.address, @proxy_info.port, @proxy_info.username, @proxy_info.password)
|
240
|
+
else
|
241
|
+
return Net::HTTP.new(location.host, location.port)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def auth_header(header)
|
246
|
+
if @auth_token
|
247
|
+
if header
|
248
|
+
header.merge!({'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => "2.1"})
|
249
|
+
else
|
250
|
+
header = {'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => "2.1"}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
return header
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'gcal4ruby/event'
|
2
|
+
|
3
|
+
module GCal4Ruby
|
4
|
+
#The Calendar Class is the representation of a Google Calendar. Each user account
|
5
|
+
#can have multiple calendars. You must have an authenticated Service object before
|
6
|
+
#using the Calendar object.
|
7
|
+
#=Usage
|
8
|
+
#All usages assume a successfully authenticated Service.
|
9
|
+
#1. Create a new Calendar
|
10
|
+
# cal = Calendar.new(service)
|
11
|
+
#
|
12
|
+
#2. Find an existing Calendar
|
13
|
+
# cal = Calendar.find(service, "New Calendar", :first)
|
14
|
+
#
|
15
|
+
#3. Find all calendars containing the search term
|
16
|
+
# cal = Calendar.find(service, "Soccer Team")
|
17
|
+
#
|
18
|
+
#4. Find a calendar by ID
|
19
|
+
# cal = Calendar.find(service, id, :first)
|
20
|
+
#
|
21
|
+
#After a calendar object has been created or loaded, you can change any of the
|
22
|
+
#attributes like you would any other object. Be sure to save the calendar to write changes
|
23
|
+
#to the Google Calendar service.
|
24
|
+
|
25
|
+
class Calendar
|
26
|
+
CALENDAR_FEED = "http://www.google.com/calendar/feeds/default/owncalendars/full"
|
27
|
+
|
28
|
+
#The calendar title
|
29
|
+
attr_accessor :title
|
30
|
+
|
31
|
+
#A short description of the calendar
|
32
|
+
attr_accessor :summary
|
33
|
+
|
34
|
+
#The parent Service object passed on initialization
|
35
|
+
attr_reader :service
|
36
|
+
|
37
|
+
#The unique calendar id
|
38
|
+
attr_reader :id
|
39
|
+
|
40
|
+
#Boolean value indicating the calendar visibility
|
41
|
+
attr_accessor :hidden
|
42
|
+
|
43
|
+
#The calendar timezone[http://code.google.com/apis/calendar/docs/2.0/reference.html#gCaltimezone]
|
44
|
+
attr_accessor :timezone
|
45
|
+
|
46
|
+
#The calendar color. Must be one of these[http://code.google.com/apis/calendar/docs/2.0/reference.html#gCalcolor] values.
|
47
|
+
attr_accessor :color
|
48
|
+
|
49
|
+
#The calendar geo location, if any
|
50
|
+
attr_accessor :where
|
51
|
+
|
52
|
+
#A boolean value indicating whether the calendar appears by default when viewed online
|
53
|
+
attr_accessor :selected
|
54
|
+
|
55
|
+
#The event feed for the calendar
|
56
|
+
attr_reader :event_feed
|
57
|
+
|
58
|
+
#Returns true if the calendar exists on the Google Calendar system (i.e. was
|
59
|
+
#loaded or has been saved). Otherwise returns false.
|
60
|
+
def exists?
|
61
|
+
return @exists
|
62
|
+
end
|
63
|
+
|
64
|
+
#Returns true if the calendar is publically accessable, otherwise returns false.
|
65
|
+
def public?
|
66
|
+
return @public
|
67
|
+
end
|
68
|
+
|
69
|
+
#Returns an array of Event objects corresponding to each event in the calendar.
|
70
|
+
def events
|
71
|
+
events = []
|
72
|
+
ret = @service.send_get(@event_feed)
|
73
|
+
REXML::Document.new(ret.body).root.elements.each("entry"){}.map do |entry|
|
74
|
+
entry.attributes["xmlns:gCal"] = "http://schemas.google.com/gCal/2005"
|
75
|
+
entry.attributes["xmlns:gd"] = "http://schemas.google.com/g/2005"
|
76
|
+
entry.attributes["xmlns"] = "http://www.w3.org/2005/Atom"
|
77
|
+
e = Event.new(self)
|
78
|
+
if e.load(entry.to_s)
|
79
|
+
events << e
|
80
|
+
end
|
81
|
+
end
|
82
|
+
return events
|
83
|
+
end
|
84
|
+
|
85
|
+
#Set the calendar to public (p = true) or private (p = false). Publically viewable
|
86
|
+
#calendars can be accessed by anyone without having to log in to google calendar. See
|
87
|
+
#Calendar#to_iframe for options to display a public calendar in a webpage.
|
88
|
+
def public=(p)
|
89
|
+
if p
|
90
|
+
permissions = 'http://schemas.google.com/gCal/2005#read'
|
91
|
+
else
|
92
|
+
permissions = 'none'
|
93
|
+
end
|
94
|
+
|
95
|
+
#if p != @public
|
96
|
+
path = "http://www.google.com/calendar/feeds/#{@id}/acl/full/default"
|
97
|
+
request = REXML::Document.new(ACL_XML)
|
98
|
+
request.root.elements.each() do |ele|
|
99
|
+
if ele.name == 'role'
|
100
|
+
ele.attributes['value'] = permissions
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
if @service.send_put(path, request.to_s, {"Content-Type" => "application/atom+xml", "Content-Length" => request.length.to_s})
|
105
|
+
@public = p
|
106
|
+
return true
|
107
|
+
else
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
#end
|
111
|
+
end
|
112
|
+
|
113
|
+
#Accepts a Service object. Returns the new Calendar if successful, otherwise raises the InvalidService
|
114
|
+
#error.
|
115
|
+
def initialize(service)
|
116
|
+
super()
|
117
|
+
if !service.is_a?(Service)
|
118
|
+
raise InvalidService
|
119
|
+
end
|
120
|
+
@xml = CALENDAR_XML
|
121
|
+
@service = service
|
122
|
+
@exists = false
|
123
|
+
@title = ""
|
124
|
+
@summary = ""
|
125
|
+
@public = false
|
126
|
+
@id = nil
|
127
|
+
@hidden = false
|
128
|
+
@timezone = "America/Los_Angeles"
|
129
|
+
@color = "#2952A3"
|
130
|
+
@where = ""
|
131
|
+
return true
|
132
|
+
end
|
133
|
+
|
134
|
+
#Deletes a calendar. If successful, returns true, otherwise false. If successful, the
|
135
|
+
#calendar object is cleared.
|
136
|
+
def delete
|
137
|
+
if @exists
|
138
|
+
if @service.send_delete(CALENDAR_FEED+"/"+@id)
|
139
|
+
@exists = false
|
140
|
+
@title = nil
|
141
|
+
@summary = nil
|
142
|
+
@public = false
|
143
|
+
@id = nil
|
144
|
+
@hidden = false
|
145
|
+
@timezone = nil
|
146
|
+
@color = nil
|
147
|
+
@where = nil
|
148
|
+
return true
|
149
|
+
else
|
150
|
+
return false
|
151
|
+
end
|
152
|
+
else
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
#If the calendar does not exist, creates it, otherwise updates the calendar info. Returns
|
158
|
+
#true if the save is successful, otherwise false.
|
159
|
+
def save
|
160
|
+
if @exists
|
161
|
+
ret = service.send_put(@edit_feed, to_xml(), {'Content-Type' => 'application/atom+xml'})
|
162
|
+
else
|
163
|
+
ret = service.send_post(CALENDAR_FEED, to_xml(), {'Content-Type' => 'application/atom+xml'})
|
164
|
+
end
|
165
|
+
if !@exists
|
166
|
+
if load(ret.read_body)
|
167
|
+
return true
|
168
|
+
else
|
169
|
+
raise CalendarSaveFailed
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
175
|
+
#Class method for querying the google service for specific calendars. The service parameter
|
176
|
+
#should be an appropriately authenticated Service. The term parameter can be any string. The
|
177
|
+
#scope parameter may be either :all to return an array of matches, or :first to return
|
178
|
+
#the first match as a Calendar object.
|
179
|
+
def self.find(service, query_term, scope = :all)
|
180
|
+
t = query_term.downcase
|
181
|
+
cals = service.calendars
|
182
|
+
ret = []
|
183
|
+
cals.each do |cal|
|
184
|
+
title = cal.title || ""
|
185
|
+
summary = cal.summary || ""
|
186
|
+
id = cal.id || ""
|
187
|
+
if title.downcase.match(t) or summary.downcase.match(t) or id.downcase.match(t)
|
188
|
+
if scope == :first
|
189
|
+
return cal
|
190
|
+
elsif scope == :all
|
191
|
+
ret << cal
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
ret
|
196
|
+
end
|
197
|
+
|
198
|
+
#Reloads the calendar objects information from the stored server version. Returns true
|
199
|
+
#if successful, otherwise returns false. Any information not saved will be overwritten.
|
200
|
+
def reload
|
201
|
+
if not @exists
|
202
|
+
return false
|
203
|
+
end
|
204
|
+
t = Calendar.find(service, @id, :first)
|
205
|
+
if t
|
206
|
+
load(t.xml)
|
207
|
+
else
|
208
|
+
return false
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
#Returns the xml representation of the Calenar.
|
213
|
+
def to_xml
|
214
|
+
xml = REXML::Document.new(@xml)
|
215
|
+
xml.root.elements.each(){}.map do |ele|
|
216
|
+
case ele.name
|
217
|
+
when "title"
|
218
|
+
ele.text = @title
|
219
|
+
when "summary"
|
220
|
+
ele.text = @summary
|
221
|
+
when "timezone"
|
222
|
+
ele.attributes["value"] = @timezone
|
223
|
+
when "hidden"
|
224
|
+
ele.attributes["value"] = @hidden
|
225
|
+
when "color"
|
226
|
+
ele.attributes["value"] = @color
|
227
|
+
when "selected"
|
228
|
+
ele.attributes["value"] = @selected
|
229
|
+
end
|
230
|
+
end
|
231
|
+
xml.to_s
|
232
|
+
end
|
233
|
+
|
234
|
+
#Loads the Calendar with returned data from Google Calendar feed. Returns true if successful.
|
235
|
+
def load(string)
|
236
|
+
@exists = true
|
237
|
+
@xml = string
|
238
|
+
xml = REXML::Document.new(string)
|
239
|
+
xml.root.elements.each(){}.map do |ele|
|
240
|
+
case ele.name
|
241
|
+
when "id"
|
242
|
+
@id = ele.text.gsub("http://www.google.com/calendar/feeds/default/calendars/", "")
|
243
|
+
when 'title'
|
244
|
+
@title = ele.text
|
245
|
+
when 'summary'
|
246
|
+
@summary = ele.text
|
247
|
+
when "color"
|
248
|
+
@color = ele.attributes['value']
|
249
|
+
when 'hidden'
|
250
|
+
@hidden = ele.attributes["value"] == "true" ? true : false
|
251
|
+
when 'timezone'
|
252
|
+
@timezone = ele.attributes["value"]
|
253
|
+
when "selected"
|
254
|
+
@selected = ele.attributes["value"] == "true" ? true : false
|
255
|
+
when "link"
|
256
|
+
if ele.attributes['rel'] == 'edit'
|
257
|
+
@edit_feed = ele.attributes['href']
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
@event_feed = "http://www.google.com/calendar/feeds/#{@id}/private/full"
|
263
|
+
|
264
|
+
puts "Getting ACL Feed" if @service.debug
|
265
|
+
ret = @service.send_get("http://www.google.com/calendar/feeds/#{@id}/acl/full/")
|
266
|
+
r = REXML::Document.new(ret.read_body)
|
267
|
+
r.root.elements.each("entry") do |ele|
|
268
|
+
ele.elements.each do |e|
|
269
|
+
#puts "e = "+e.to_s if @service.debug
|
270
|
+
#puts "previous element = "+e.previous_element.to_s if @service.debug
|
271
|
+
if e.name == 'role' and e.previous_element.name == 'scope' and e.previous_element.attributes['type'] == 'default'
|
272
|
+
if e.attributes['value'].match('#read')
|
273
|
+
@public = true
|
274
|
+
else
|
275
|
+
@public = false
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
return true
|
281
|
+
end
|
282
|
+
|
283
|
+
#Returns a HTML <iframe> tag displaying the calendar.
|
284
|
+
def to_iframe(height=300, width=400)
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
@xml
|
290
|
+
@exists = false
|
291
|
+
@public = false
|
292
|
+
@event_feed = ''
|
293
|
+
@edit_feed = ''
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|