clio-gcal4ruby 0.3.2

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/CHANGELOG ADDED
@@ -0,0 +1,55 @@
1
+ #=CHANGELOG
2
+ #==version 0.3.1_OAuth
3
+ #* Git repo by tvongaza to implement OAuth authentication into the GCal4Ruby gem
4
+ #==version 0.3.1_AuthSub
5
+ #* Git repo by mgornick to implement Authsub authentication into the GCal4Ruby gem
6
+ #==version 0.3.1
7
+ #* Fixed Event.find method to work with secondary calendars and google apps accounts. Fix provided by groesser3.
8
+ #* Added max results to Calendar.find.
9
+ #==version 0.3.0
10
+ #* Rewrote Event.find to improve performance significantly.
11
+ #* Added improvements to event recurrence handling, including loading existing recurrences, changing recurring events to non recurring and vice versa.
12
+ #* Added support for initialization attributes to Event, Calendar, Service and Recurrence.
13
+ #* Fixed query string typo in Event.find. Fix provided by nat.lownes.
14
+ #==version 0.2.11
15
+ #* Added support for GML elements in calendar events. Fix provided by nat.lownes.
16
+ #* Fixed event status bug where cancelled events were marked confirmed. Fix provided by rifraf.
17
+ #==version 0.2.10
18
+ #* Fixed library support for Google Hosted Apps accounts running in forced SSL mode.
19
+ #==version 0.2.9
20
+ #* Fixed small SSL redirect bug due to variable misnaming in base.rb. Fix provided by JohnMetta
21
+ #==version 0.2.8
22
+ #* Merged changes from edruder and h13ronim from the unofficial github repo - http://github.com/h13ronim/gcal4ruby/commits/master
23
+ #==version 0.2.7
24
+ #* Added fix for finding events in calendars that have more than 25 entries
25
+ #==version 0.2.6
26
+ #* Added fix for updated google calendar XML per http://cookingandcoding.wordpress.com/2009/06/08/new-ruby-google-calendar-api-gem-gcal4ruby/#comment-183
27
+ #==version 0.2.5
28
+ #* Added calendar color support to to_iframe methods in Calendar and Service.
29
+ #==version 0.2.4
30
+ #* Fixed bug with ACL check in Calendar#load
31
+ #==version 0.2.3
32
+ #* Implemented to_iframe method for calendars and services to output embeddable iframe text.
33
+ #* Added switch to turn off ACL check for public calendars. Can increase effeciency if turned off.
34
+ #==version 0.2.2
35
+ #* Fixed URL encoding problem in Event.find method.
36
+ #* cleaned up Event.find method to allow for finding events by id
37
+ #* updated Calendar.find method to add params hash
38
+ #* added 'published', 'updated', and 'edited' attributes
39
+ #==version 0.2.1
40
+ #* fixed Event.find calendar specification
41
+ #==version 0.2.0
42
+ #* Fixed redirect URI query params problem
43
+ #* Updated syntax for finding events to include most google api parameters, Non-backwards compatible.
44
+ #==version 0.1.4
45
+ #* Added additional search criteria for Event.find
46
+ #==version 0.1.3
47
+ #* Added support for authenticating with Google Hosted Apps accounts
48
+ #* Added flag to indicate whether a calendar is editable
49
+ #* Added handling to gracefully throw error when trying to create event on a non-editable (shared) calendar
50
+ #==version 0.1.2
51
+ #* Fixed to_xml dump problem with hidden and selected attributes
52
+ #==version 0.1.1
53
+ #* Added all_day indicator to event to indicate an all day event
54
+ #==version 0.1.0
55
+ #* Initial Version
data/README.markdown ADDED
@@ -0,0 +1,136 @@
1
+ NOTES
2
+ ----
3
+
4
+ In all of the example code here and in the API you need to namespace the object like `GCal4Ruby::Calendar` instead of just `Calendar`. This documentation needs a lot of reformatting.
5
+
6
+ GCal4Ruby
7
+ ===
8
+
9
+ Introduction
10
+ ------------
11
+
12
+ GCal4Ruby is a full featured wrapper for the google calendar API. GCal4Ruby implements
13
+ all of the functionality available through the Google Calnedar API, including permissions,
14
+ attendees, reminders and event recurrence.
15
+
16
+ Author and Contact Information
17
+ ------------------------------
18
+
19
+ This branch was made by Matt Gornick (http://github.com/mgornick) to implement Google AuthSub into the original
20
+ GCal4Ruby that was created and is maintained by {Mike Reich}[mailto:mike@seabourneconsulting.com]
21
+ and is licenses under the GPL v2. Feel free to use and update, but be sure to contribute your
22
+ code back to the project and attribute as required by the license.
23
+
24
+ Website
25
+ -------
26
+
27
+ http://rubyforge.org/projects/gcal4ruby/
28
+
29
+ Example Application
30
+ -------------------
31
+
32
+ http://github.com/mgornick/studentplanr
33
+
34
+ Description
35
+ -----------
36
+
37
+ GCal4Ruby has three major components: the service, calendar and event objects. Each service
38
+ has many calendars, which in turn have many events. Each service is the representation of a
39
+ google account, and thus must be successfully authenticated using valid Google Calendar
40
+ account credentials.
41
+
42
+ Examples
43
+ --------
44
+
45
+ Below are some common usage examples. For more examples, check the documentation.
46
+
47
+ Service
48
+ -------
49
+
50
+ 1. Authenticate
51
+ service = Service.new
52
+ service.authenticate("user@gmail.com", "password")
53
+
54
+ 2. Authenticate with Google AuthSub
55
+ # your authentication controller (example is index action in the home controller):
56
+ scope = 'http://www.google.com/calendar/feeds/'
57
+ next_url = 'http://127.0.0.1:3000/home/authenticated'
58
+ secure = false # set secure = true for signed AuthSub requests
59
+ session = true
60
+ @authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, session)
61
+
62
+ # in your next_url controller as defined by your AuthSub link (in example would be the authenticated action)
63
+ @token = params[:token]
64
+ client = GData::Client::Calendar.new
65
+ client.authsub_token = params[:token] # extract the single-use token from the URL query params
66
+ session[:token] = client.auth_handler.upgrade()
67
+ client.authsub_token = session[:token] if session[:token]
68
+
69
+ service= GCal4Ruby::Service.new
70
+ service.authsub_authenticate(session[:token], "user@gmail.com")
71
+
72
+ 3. Get Calendar List
73
+ calendars = service.calendars
74
+
75
+ Calendar
76
+ --------
77
+
78
+ All usages assume a successfully authenticated Service.
79
+
80
+ 1. Create a new Calendar
81
+ cal = Calendar.new(service)
82
+
83
+ 2. Find an existing Calendar
84
+ cal = Calendar.find(service, "New Calendar", :first)
85
+
86
+ 3. Find all calendars containing the search term
87
+ cal = Calendar.find(service, "Soccer Team")
88
+
89
+ 4. Find a calendar by ID
90
+ cal = Calendar.find(service, id, :first)
91
+
92
+ Event
93
+ -----
94
+
95
+ All usages assume a successfully authenticated Service and valid Calendar.
96
+
97
+ 1. Create a new Event
98
+ event = Event.new(calendar)
99
+ event.title = "Soccer Game"
100
+ event.start = Time.parse("12-06-2009 at 12:30 PM")
101
+ event.end = Time.parse("12-06-2009 at 1:30 PM")
102
+ event.where = "Merry Playfields"
103
+ event.save
104
+
105
+ 2. Find an existing Event
106
+ event = Event.find(cal, "Soccer Game", {:scope => :first})
107
+
108
+ 3. Find all events containing the search term
109
+ event = Event.find(cal, "Soccer Game")
110
+
111
+ 4. Create a recurring event for every saturday
112
+ event = Event.new(calendar)
113
+ event.title = "Baseball Game"
114
+ event.where = "Municipal Stadium"
115
+ event.recurrence = Recurrence.new
116
+ event.recurrence.start = Time.parse("13-06-2009 at 4:30 PM")
117
+ event.recurrence.end = Time.parse("13-06-2009 at 6:30 PM")
118
+ event.recurrence.frequency = {"weekly" => ["SA"]}
119
+ event.save
120
+
121
+ 5. Create an event with a 15 minute email reminder
122
+ event = Event.new(calendar)
123
+ event.title = "Dinner with Kate"
124
+ event.start = Time.parse("20-06-2009 at 5 pm")
125
+ event.end = Time.parse("20-06-209 at 8 pm")
126
+ event.where = "Luigi's"
127
+ event.reminder = {:minutes => 15, :method => 'email'}
128
+ event.save
129
+
130
+ 6. Create an event with attendees
131
+ event = Event.new(calendar)
132
+ event.title = "Dinner with Kate"
133
+ event.start = Time.parse("20-06-2009 at 5 pm")
134
+ event.end = Time.parse("20-06-209 at 8 pm")
135
+ event.attendees => {:name => "Kate", :email => "kate@gmail.com"}
136
+ event.save
data/lib/gcal4ruby.rb ADDED
@@ -0,0 +1 @@
1
+ require "gcal4ruby/service"
@@ -0,0 +1,339 @@
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
+ class CalendarNotEditable < StandardError; end
67
+
68
+ class QueryParameterError < StandardError; end
69
+
70
+ #The ProxyInfo class contains information for configuring a proxy connection
71
+
72
+ class ProxyInfo
73
+ attr_accessor :address, :port, :username, :password
74
+ @address = nil
75
+ @port = nil
76
+ @username = nil
77
+ @password = nil
78
+
79
+ #The initialize function accepts four variables for configuring the ProxyInfo object.
80
+ #The proxy connection is initiated using the builtin Net::HTTP proxy support.
81
+
82
+ def initialize(address, port, username=nil, password=nil)
83
+ @address = address
84
+ @port = port
85
+ @username = username
86
+ @password = password
87
+ end
88
+ end
89
+
90
+ #The Base class includes the basic HTTP methods for sending and receiving
91
+ #messages from the Google Calendar API. You shouldn't have to use this class
92
+ #directly, rather access the functionality through the Service subclass.
93
+
94
+ class Base
95
+ AUTH_URL = "https://www.google.com/accounts/ClientLogin"
96
+ CALENDAR_LIST_FEED = "http://www.google.com/calendar/feeds/default/allcalendars/full"
97
+ @proxy_info = nil
98
+ @auth_token = nil
99
+ @debug = false
100
+
101
+ #Contains the ProxyInfo object for using a proxy server
102
+ attr_accessor :proxy_info
103
+
104
+ #If set to true, debug will dump all raw HTTP requests and responses
105
+ attr_accessor :debug
106
+
107
+ # Sends an HTTP POST request. The header should be a hash of name/value pairs.
108
+ # Returns the Net::HTTPResponse object on succces, or raises the appropriate
109
+ # error if a non 20x response code is received.
110
+ def send_post(url, content, header=nil)
111
+ header = auth_header(header)
112
+ ret = nil
113
+ location = URI.parse(url)
114
+ puts "url = "+url if @debug
115
+ ret = do_post(location, header, content)
116
+ while ret.is_a?(Net::HTTPRedirection)
117
+ puts "Redirect received, resending post" if @debug
118
+ if oauth?
119
+ header ||= {}
120
+ header['Cookie'] = ret['set-cookie'] if ret['set-cookie']
121
+ end
122
+ ret = do_post(ret['location'], header, content)
123
+ end
124
+ if ret.is_a?(Net::HTTPSuccess)
125
+ puts "20x response received\nResponse: \n"+ret.read_body if @debug
126
+ return ret
127
+ else
128
+ puts "invalid response received: "+ret.code if @debug
129
+ raise HTTPPostFailed, ret.body
130
+ end
131
+ end
132
+
133
+ def do_post(url, header, content)
134
+ ret = nil
135
+ if url.is_a?(String)
136
+ location = URI.parse(url)
137
+ else
138
+ location = url
139
+ end
140
+
141
+ puts "Starting post\nHeader: #{header}\n" if @debug
142
+ if oauth?
143
+ header ||= {}
144
+ header.merge!({ 'Accept'=>'application/atom+xml', 'Content-Type' => 'application/atom+xml' })
145
+ ret = @auth_token.post(url.to_s, content, header)
146
+ else
147
+ http = get_http_object(location)
148
+ http.start do |ht|
149
+ ret = ht.post(location.to_s, content, header)
150
+ end
151
+ end
152
+ return ret
153
+ end
154
+
155
+ # Sends an HTTP PUT request. The header should be a hash of name/value pairs.
156
+ # Returns the Net::HTTPResponse object on succces, or raises the appropriate
157
+ # error if a non 20x response code is received.
158
+ def send_put(url, content, header=nil)
159
+ header = auth_header(header)
160
+ ret = nil
161
+ location = URI.parse(url)
162
+ puts "url = "+url if @debug
163
+ ret = do_put(location, header, content)
164
+ while ret.is_a?(Net::HTTPRedirection)
165
+ puts "Redirect received, resending post" if @debug
166
+ ret = do_put(ret['location'], header, content)
167
+ end
168
+ if ret.is_a?(Net::HTTPSuccess)
169
+ puts "20x response received\nResponse: \n"+ret.read_body if @debug
170
+ return ret
171
+ else
172
+ puts "invalid response received: "+ret.code if @debug
173
+ raise HTTPPutFailed, ret.body
174
+ end
175
+ end
176
+
177
+ def do_put(url, header, content)
178
+ ret = nil
179
+ if url.is_a?(String)
180
+ location = URI.parse(url)
181
+ else
182
+ location = url
183
+ end
184
+
185
+ puts "Starting put\nHeader: #{header}\n" if @debug
186
+ if oauth?
187
+ header ||= {}
188
+ header.merge!({ 'Accept'=>'application/atom+xml', 'Content-Type' => 'application/atom+xml' })
189
+ ret = @auth_token.put(url.to_s, content, header)
190
+ else
191
+ http = get_http_object(location)
192
+ http.start do |ht|
193
+ ret = ht.put(location.to_s, content, header)
194
+ end
195
+ end
196
+ return ret
197
+ end
198
+
199
+ # Sends an HTTP GET request. The header should be a hash of name/value pairs.
200
+ # Returns the Net::HTTPResponse object on succces, or raises the appropriate
201
+ # error if a non 20x response code is received.
202
+ def send_get(url, header = nil)
203
+ header = auth_header(header)
204
+ ret = nil
205
+ location = URI.parse(url)
206
+ puts "url = "+url if @debug
207
+ ret = do_get(location, header)
208
+
209
+ while ret.is_a?(Net::HTTPRedirection)
210
+ puts "Redirect received from #{location.to_s}, resending get to #{ret['location']}" if @debug
211
+ if oauth?
212
+ header ||= {}
213
+ header['Cookie'] = ret['set-cookie'] if ret['set-cookie']
214
+ end
215
+ ret = do_get(ret['location'], header)
216
+ end
217
+ if ret.is_a?(Net::HTTPSuccess)
218
+ puts "20x response received\nResponse: \n"+ret.read_body if @debug
219
+ return ret
220
+ else
221
+ puts "Error received, resending get" if @debug
222
+ raise HTTPGetFailed, ret.body
223
+ end
224
+ end
225
+
226
+ def do_get(url, header)
227
+ ret = nil
228
+ if url.is_a?(String)
229
+ location = URI.parse(url)
230
+ else
231
+ location = url
232
+ end
233
+
234
+ puts "Starting get\nHeader: #{header}\n" if @debug
235
+ if oauth?
236
+ ret = @auth_token.get(url.to_s, header)
237
+ else
238
+ http = get_http_object(location)
239
+ http.start do |ht|
240
+ ret = ht.get(location.to_s, header)
241
+ end
242
+ end
243
+ return ret
244
+ end
245
+
246
+ # Sends an HTTP DELETE request. The header should be a hash of name/value pairs.
247
+ # Returns the Net::HTTPResponse object on succces, or raises the appropriate
248
+ # error if a non 20x response code is received.
249
+ def send_delete(url, header = nil)
250
+ header = auth_header(header)
251
+ ret = nil
252
+ location = URI.parse(url)
253
+ puts "url = "+url if @debug
254
+ ret = do_delete(location, header)
255
+ while ret.is_a?(Net::HTTPRedirection)
256
+ puts "Redirect received, resending post" if @debug
257
+ ret = do_delete(ret['location'], header)
258
+ end
259
+ if ret.is_a?(Net::HTTPSuccess)
260
+ puts "20x response received\nResponse: \n"+ret.read_body if @debug
261
+ return true
262
+ else
263
+ puts "invalid response received: "+ret.code if @debug
264
+ raise HTTPDeleteFailed, ret.body
265
+ end
266
+ end
267
+
268
+ def do_delete(url, header)
269
+ ret = nil
270
+ if url.is_a?(String)
271
+ location = URI.parse(url)
272
+ else
273
+ location = url
274
+ end
275
+
276
+ puts "Starting get\nHeader: #{header}\n" if @debug
277
+ if oauth?
278
+ ret = @auth_token.delete(url.to_s, header)
279
+ else
280
+ http = get_http_object(location)
281
+ http.start do |ht|
282
+ ret = ht.delete(location.to_s, header)
283
+ end
284
+ end
285
+ return ret
286
+ end
287
+
288
+ private
289
+
290
+ def get_http_object(location)
291
+ if @proxy_info and @proxy_info.address
292
+ http = Net::HTTP.new(location.host, location.port, @proxy_info.address, @proxy_info.port, @proxy_info.username, @proxy_info.password)
293
+ else
294
+ http = Net::HTTP.new(location.host, location.port)
295
+ end
296
+ if location.scheme == 'https'
297
+ #fixed http/http misnaming via JohnMetta
298
+ puts "SSL True" if @debug
299
+ http.use_ssl = true
300
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
301
+ end
302
+ return http
303
+ end
304
+
305
+ def auth_header_client_login(header)
306
+ if @auth_token
307
+ if header
308
+ header.merge!({'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => "2.1"})
309
+ else
310
+ header = {'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => "2.1"}
311
+ end
312
+ end
313
+
314
+ return header
315
+ end
316
+
317
+ #auth header that uses google authsub
318
+ def auth_header_authsub(header)
319
+ if @auth_token
320
+ if header
321
+ header.merge!({'Authorization' => "AuthSub token=#{@auth_token}", "GData-Version" => "2.1"})
322
+ else
323
+ header = {'Authorization' => "AuthSub token=#{@auth_token}", "GData-Version" => "2.1"}
324
+ end
325
+ end
326
+ return header
327
+ end
328
+
329
+ # routes the authentication to the proper header based upon auth type
330
+ def auth_header(header)
331
+ if @auth_type == 'AuthSub'
332
+ return auth_header_authsub(header)
333
+ elsif @auth_type == 'ClientLogin'
334
+ return auth_header_client_login(header)
335
+ end
336
+ end
337
+
338
+ end
339
+ end