amr_google_calendar 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,109 @@
1
+ require "addressable/uri"
2
+ require 'google/net/https'
3
+
4
+ module Google
5
+
6
+ # This is a utility class that performs all of the
7
+ # communication with the google calendar api.
8
+ #
9
+ class Connection
10
+ # set the username, password, auth_url, app_name, and login.
11
+ #
12
+ def initialize(params)
13
+ @username = params[:username]
14
+ @password = params[:password]
15
+ @auth_url = params[:auth_url] || "https://www.google.com/accounts/ClientLogin"
16
+ @app_name = params[:app_name] || "northworld.com-googlecalendar-integration"
17
+
18
+ login()
19
+ end
20
+
21
+ # login to the google calendar and grab an auth token.
22
+ #
23
+ def login()
24
+ content = {
25
+ 'Email' => @username,
26
+ 'Passwd' => @password,
27
+ 'source' => @app_name,
28
+ 'accountType' => 'HOSTED_OR_GOOGLE',
29
+ 'service' => 'cl'}
30
+
31
+ response = send(Addressable::URI.parse(@auth_url), :post_form, content)
32
+
33
+ raise HTTPRequestFailed unless response.kind_of? Net::HTTPSuccess
34
+
35
+ @token = response.body.split('=').last
36
+ @headers = {
37
+ 'Authorization' => "GoogleLogin auth=#{@token}",
38
+ 'Content-Type' => 'application/atom+xml'
39
+ }
40
+ @update_header = @headers.clone
41
+ @update_header["If-Match"] = "*"
42
+ end
43
+
44
+ # send a request to google.
45
+ #
46
+ def send(uri, method, content = '', redirect_count = 10)
47
+ raise HTTPTooManyRedirections if redirect_count == 0
48
+
49
+ set_session_if_necessary(uri)
50
+
51
+ http = (uri.scheme == 'https' ? Net::HTTPS.new(uri.host, uri.inferred_port) : Net::HTTP.new(uri.host, uri.inferred_port))
52
+
53
+ case method
54
+ when :delete
55
+ # puts "in delete: #{uri.to_s}"
56
+ request = Net::HTTP::Delete.new(uri.to_s, @update_header)
57
+ when :get
58
+ # puts "in get: #{uri.to_s}"
59
+ request = Net::HTTP::Get.new(uri.to_s, @headers)
60
+ when :post_form
61
+ # puts "in post_form: #{uri.to_s}"
62
+ request = Net::HTTP::Post.new(uri.to_s, @headers)
63
+ request.set_form_data(content)
64
+ when :post
65
+ # puts "in post: #{uri.to_s}\n#{content}"
66
+ request = Net::HTTP::Post.new(uri.to_s, @headers)
67
+ request.body = content
68
+ when :put
69
+ # puts "in put: #{uri.to_s}\n#{content}"
70
+ request = Net::HTTP::Put.new(uri.to_s, @update_header)
71
+ request.body = content
72
+ end
73
+
74
+ response = http.request(request)
75
+
76
+ # recurse if necessary.
77
+ if response.kind_of? Net::HTTPRedirection
78
+ response = send(Addressable::URI.parse(response['location']), method, content, redirect_count - 1)
79
+ end
80
+
81
+ # Raise the appropiate error if necessary.
82
+ if response.kind_of? Net::HTTPForbidden
83
+ raise HTTPAuthorizationFailed, response.body
84
+
85
+ elsif response.kind_of? Net::HTTPBadRequest
86
+ raise HTTPRequestFailed, response.body
87
+
88
+ elsif response.kind_of? Net::HTTPNotFound
89
+ raise HTTPNotFound, response.body
90
+ end
91
+
92
+ return response
93
+ end
94
+
95
+ protected
96
+
97
+ def set_session_if_necessary(uri) #:nodoc:
98
+ # only extract the session if we don't already have one.
99
+ @session_id = uri.query_values['gsessionid'] if @session_id == nil && uri.query
100
+
101
+ if @session_id
102
+ uri.query ||= ''
103
+ uri.query_values = uri.query_values.merge({'gsessionid' => @session_id})
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,7 @@
1
+ module Google
2
+ class HTTPRequestFailed < StandardError; end
3
+ class HTTPAuthorizationFailed < StandardError; end
4
+ class HTTPNotFound < StandardError; end
5
+ class HTTPTooManyRedirections < StandardError; end
6
+ class InvalidCalendar < StandardError; end
7
+ end
@@ -0,0 +1,182 @@
1
+ require 'nokogiri'
2
+ require 'time'
3
+
4
+ module Google
5
+
6
+ # Represents a google Event.
7
+ #
8
+ # === Attributes
9
+ #
10
+ # * +id+ - The google assigned id of the event (nil until saved), read only.
11
+ # * +title+ - The title of the event, read/write.
12
+ # * +content+ - The content of the event, read/write.
13
+ # * +start_time+ - The start time of the event (Time object, defaults to now), read/write.
14
+ # * +end_time+ - The end time of the event (Time object, defaults to one hour from now), read/write.
15
+ # * +calendar+ - What calendar the event belongs to, read/write.
16
+ # * +raw_xml+ - The full google xml representation of the event.
17
+ #
18
+ class Event
19
+ attr_reader :id, :raw_xml
20
+ attr_accessor :title, :content, :where, :calendar, :quickadd
21
+
22
+ # Create a new event, and optionally set it's attributes.
23
+ #
24
+ # ==== Example
25
+ # Event.new(:title => 'Swimming',
26
+ # :content => 'Do not forget a towel this time',
27
+ # :where => 'The Ocean',
28
+ # :start_time => Time.now,
29
+ # :end_time => Time.now + (60 * 60),
30
+ # :calendar => calendar_object)
31
+ #
32
+ def initialize(params = {})
33
+ @id = params[:id]
34
+ @title = params[:title]
35
+ @content = params[:content]
36
+ @where = params[:where]
37
+ @start_time = params[:start_time]
38
+ @end_time = params[:end_time]
39
+ self.all_day= params[:all_day] if params[:all_day]
40
+ @calendar = params[:calendar]
41
+ @raw_xml = params[:raw_xml]
42
+ @quickadd = params[:quickadd]
43
+ end
44
+
45
+ # Sets the start time of the Event. Must be a Time object or a parsable string representation of a time.
46
+ #
47
+ def start_time=(time)
48
+ raise ArgumentError, "Start Time must be either Time or String" unless (time.is_a?(String) || time.is_a?(Time))
49
+ @start_time = (time.is_a? String) ? Time.parse(time) : time
50
+ end
51
+
52
+ # Get the start_time of the event.
53
+ #
54
+ # If no time is set (i.e. new event) it defaults to the current time.
55
+ #
56
+ def start_time
57
+ @start_time ||= Time.now
58
+ (@start_time.is_a? String) ? @start_time : @start_time.utc.xmlschema
59
+ end
60
+
61
+ # Get the end_time of the event.
62
+ #
63
+ # If no time is set (i.e. new event) it defaults to one hour in the future.
64
+ #
65
+ def end_time
66
+ @end_time ||= Time.now + (60 * 60) # seconds * min
67
+ (@end_time.is_a? String) ? @end_time : @end_time.utc.xmlschema
68
+ end
69
+
70
+ # Sets the end time of the Event. Must be a Time object or a parsable string representation of a time.
71
+ #
72
+ def end_time=(time)
73
+ raise ArgumentError, "End Time must be either Time or String" unless (time.is_a?(String) || time.is_a?(Time))
74
+ @end_time = ((time.is_a? String) ? Time.parse(time) : time)
75
+ end
76
+
77
+ # Returns whether the Event is an all-day event, based on whether the event starts at the beginning and ends at the end of the day.
78
+ #
79
+ def all_day?
80
+ duration == 24 * 60 * 60 # Exactly one day
81
+ end
82
+
83
+ def all_day=(time)
84
+ if time.class == String
85
+ time = Time.parse(time)
86
+ end
87
+ @start_time = time.strftime("%Y-%m-%d")
88
+ @end_time = (time + 24*60*60).strftime("%Y-%m-%d")
89
+ end
90
+
91
+ # Duration in seconds
92
+ def duration
93
+ Time.parse(end_time) - Time.parse(start_time)
94
+ end
95
+
96
+ #
97
+ def self.build_from_google_feed(xml, calendar)
98
+ Nokogiri::XML(xml).xpath("//xmlns:entry").collect {|e| new_from_xml(e, calendar)}
99
+ end
100
+
101
+ # Google XMl representation of an evetn object.
102
+ #
103
+ def to_xml
104
+ unless quickadd
105
+ "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
106
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'></category>
107
+ <title type='text'>#{title}</title>
108
+ <content type='text'>#{content}</content>
109
+ <gd:transparency value='http://schemas.google.com/g/2005#event.opaque'></gd:transparency>
110
+ <gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'></gd:eventStatus>
111
+ <gd:where valueString=\"#{where}\"></gd:where>
112
+ <gd:when startTime=\"#{start_time}\" endTime=\"#{end_time}\"></gd:when>
113
+ </entry>"
114
+ else
115
+ %Q{<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005'>
116
+ <content type="html">#{content}</content>
117
+ <gCal:quickadd value="true"/>
118
+ </entry>}
119
+ end
120
+ end
121
+
122
+ # String representation of an event object.
123
+ #
124
+ def to_s
125
+ s = "#{title} (#{self.id})\n\t#{start_time}\n\t#{end_time}\n\t#{where}\n\t#{content}"
126
+ s << "\n\t#{quickadd}" if quickadd
127
+ s
128
+ end
129
+
130
+ # Saves an event.
131
+ # Note: If using this on an event you created without using a calendar object,
132
+ # make sure to set the calendar before calling this method.
133
+ #
134
+ def save
135
+ update_after_save(@calendar.save_event(self))
136
+ end
137
+
138
+ # Deletes an event.
139
+ # Note: If using this on an event you created without using a calendar object,
140
+ # make sure to set the calendar before calling this method.
141
+ #
142
+ def delete
143
+ @calendar.delete_event(self)
144
+ @id = nil
145
+ end
146
+
147
+ protected
148
+
149
+ # Create a new event from a google 'entry' xml block.
150
+ #
151
+ def self.new_from_xml(xml, calendar) #:nodoc:
152
+ id = xml.at_xpath("gCal:uid")['value'].split('@').first
153
+ title = xml.at_xpath("xmlns:title").content
154
+ content = xml.at_xpath("xmlns:content").content
155
+ where = xml.at_xpath("gd:where")['valueString']
156
+ start_time = xml.at_xpath("gd:when")['startTime']
157
+ end_time = xml.at_xpath("gd:when")['endTime']
158
+ quickadd = xml.at_xpath("gCal:quickadd") ? xml.at_xpath("gCal:quickadd")['quickadd'] : nil
159
+
160
+ Event.new(:id => id,
161
+ :title => title,
162
+ :content => content,
163
+ :where => where,
164
+ :start_time => start_time,
165
+ :end_time => end_time,
166
+ :calendar => calendar,
167
+ :raw_xml => xml,
168
+ :quickadd => quickadd)
169
+ end
170
+
171
+ # Set the ID after google assigns it (only necessary when we are creating a new event)
172
+ #
173
+ def update_after_save(respose) #:nodoc:
174
+ return if @id && @id != ''
175
+
176
+ xml = Nokogiri::XML(respose.body).at_xpath("//xmlns:entry")
177
+ @id = xml.at_xpath("gCal:uid")['value'].split('@').first
178
+ @raw_xml = xml
179
+ end
180
+
181
+ end
182
+ end
@@ -0,0 +1,19 @@
1
+ require "net/http"
2
+ require "net/https"
3
+
4
+ module Net
5
+
6
+ # Helper Method for using SSL
7
+ #
8
+ class HTTPS < HTTP
9
+
10
+ # Setup a secure connection with defaults
11
+ #
12
+ def initialize(address, port = nil)
13
+ super(address, port)
14
+ self.use_ssl = true
15
+ self.verify_mode = OpenSSL::SSL::VERIFY_NONE
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,6 @@
1
+ module Google
2
+ require 'google/errors'
3
+ autoload :Calendar, 'google/calendar'
4
+ autoload :Connection, 'google/connection'
5
+ autoload :Event, 'google/event'
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+ require 'mocha'
13
+
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ require 'google_calendar'
17
+ require 'google/net/https'
18
+
19
+ class Test::Unit::TestCase
20
+ @@mock_path = File.expand_path(File.join(File.dirname(__FILE__), 'mocks'))
21
+ end
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:gCal="http://schemas.google.com/gCal/2005" xmlns:gd="http://schemas.google.com/g/2005">
3
+ <id>http://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0</id>
4
+ <published>2010-12-13T07:34:25.000Z</published>
5
+ <updated>2010-12-13T07:34:25.000Z</updated>
6
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
7
+ <title type="text">New Event</title>
8
+ <content type="text">A new event</content>
9
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=YjF2cTZyajRyNG1nODVrY2lja2M3aW9tYjAgc3RldmUuemljaEBt" title="alternate"/>
10
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0"/>
11
+ <link rel="edit" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0/63427908865"/>
12
+ <author>
13
+ <name>Some Guy</name>
14
+ <email>some.guy@gmail.com</email>
15
+ </author>
16
+ <gd:comments>
17
+ <gd:feedLink href="https://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0/comments"/>
18
+ </gd:comments>
19
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
20
+ <gd:where valueString="Joe's House"/>
21
+ <gd:who email="some.guy@gmail.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="some.guy@gmail.com"/>
22
+ <gd:when endTime="2010-12-13T01:34:20.000-08:00" startTime="2010-12-13T00:34:20.000-08:00"/>
23
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
24
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
25
+ <gCal:anyoneCanAddSelf value="false"/>
26
+ <gCal:guestsCanInviteOthers value="true"/>
27
+ <gCal:guestsCanModify value="false"/>
28
+ <gCal:guestsCanSeeGuests value="true"/>
29
+ <gCal:sequence value="0"/>
30
+ <gCal:uid value="b1vq6rj4r4mg85kcickc7iomb0@google.com"/>
31
+ </entry>
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:gCal="http://schemas.google.com/gCal/2005" xmlns:gd="http://schemas.google.com/g/2005">
3
+ <id>http://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0</id>
4
+ <published>2011-05-04T00:59:03.000Z</published>
5
+ <updated>2011-05-04T00:59:03.000Z</updated>
6
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
7
+ <title type="text">movie at AMC Van Ness</title>
8
+ <content type="text"/>
9
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=aW1vcnE4cHY5YWZlZ3R0ZDVlN3Fkc2ZidmcgYXluQGFuZHJld25nLmNvbQ" title="alternate"/>
10
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/imorq8pv9afegttd5e7qdsfbvg"/>
11
+ <link rel="edit" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/imorq8pv9afegttd5e7qdsfbvg/63440153943"/>
12
+ <author>
13
+ <name>Some Guy</name>
14
+ <email>some.guy@gmail.com</email>
15
+ </author>
16
+ <gd:comments>
17
+ <gd:feedLink href="https://www.google.com/calendar/feeds/default/private/full/b1vq6rj4r4mg85kcickc7iomb0/comments"/>
18
+ </gd:comments>
19
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
20
+ <gd:where valueString="AMC Van Ness"/>
21
+ <gd:who email="some.guy@gmail.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="some.guy@gmail.com"/>
22
+ <gd:when endTime="2011-05-05T00:00:00.000-07:00" startTime="2011-05-04T23:00:00.000-07:00"/>
23
+ <gd:transparency value="http://schemas.google.com/g/2005#event.opaque"/>
24
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
25
+ <gCal:anyoneCanAddSelf value="false"/>
26
+ <gCal:guestsCanInviteOthers value="true"/>
27
+ <gCal:guestsCanModify value="false"/>
28
+ <gCal:guestsCanSeeGuests value="true"/>
29
+ <gCal:sequence value="0"/>
30
+ <gCal:uid value="b1vq6rj4r4mg85kcickc7iomb0@google.com"/>
31
+ </entry>
@@ -0,0 +1,119 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:gCal="http://schemas.google.com/gCal/2005" xmlns:gd="http://schemas.google.com/g/2005">
3
+ <id>http://www.google.com/calendar/feeds/default/private/full</id>
4
+ <updated>2010-12-10T07:09:25.000Z</updated>
5
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
6
+ <title type="text">Some Guy</title>
7
+ <subtitle type="text">Some Guy</subtitle>
8
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/embed?src=some.guy@gmail.com"/>
9
+ <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full"/>
10
+ <link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full"/>
11
+ <link rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/batch"/>
12
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full?q=Test&amp;max-results=25"/>
13
+ <author>
14
+ <name>Some Guy</name>
15
+ <email>some.guy@gmail.com</email>
16
+ </author>
17
+ <generator version="1.0" uri="http://www.google.com/calendar">Google Calendar</generator>
18
+ <openSearch:totalResults>1</openSearch:totalResults>
19
+ <openSearch:startIndex>1</openSearch:startIndex>
20
+ <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
21
+ <gCal:timezone value="America/Los_Angeles"/>
22
+ <gCal:timesCleaned value="0"/>
23
+ <entry>
24
+ <id>http://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio</id>
25
+ <published>2010-04-22T16:05:29.000Z</published>
26
+ <updated>2010-05-01T05:14:18.000Z</updated>
27
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
28
+ <title type="text">Test</title>
29
+ <content type="text"/>
30
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=b2o2Zm1wYXVsYnZrOW91b2owbGo0djZoaW8gc3RldmUuemljaEBt" title="alternate"/>
31
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio"/>
32
+ <link rel="edit" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/63408374058"/>
33
+ <author>
34
+ <name>Some Guy</name>
35
+ <email>some.guy@gmail.com</email>
36
+ </author>
37
+ <gd:comments>
38
+ <gd:feedLink href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/comments"/>
39
+ </gd:comments>
40
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
41
+ <gd:where valueString=""/>
42
+ <gd:who email="some.guy@gmail.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="some.guy@gmail.com"/>
43
+ <gd:when endTime="2010-04-09" startTime="2010-04-08">
44
+ <gd:reminder method="alert" minutes="10"/>
45
+ </gd:when>
46
+ <gd:transparency value="http://schemas.google.com/g/2005#event.transparent"/>
47
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
48
+ <gCal:anyoneCanAddSelf value="false"/>
49
+ <gCal:guestsCanInviteOthers value="true"/>
50
+ <gCal:guestsCanModify value="false"/>
51
+ <gCal:guestsCanSeeGuests value="true"/>
52
+ <gCal:sequence value="0"/>
53
+ <gCal:uid value="oj6fmpaulbvk9ouoj0lj4v6hio@google.com"/>
54
+ </entry>
55
+ <entry>
56
+ <id>http://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio</id>
57
+ <published>2010-04-22T16:05:29.000Z</published>
58
+ <updated>2010-05-01T05:14:18.000Z</updated>
59
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
60
+ <title type="text">Test</title>
61
+ <content type="text"/>
62
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=b2o2Zm1wYXVsYnZrOW91b2owbGo0djZoaW8gc3RldmUuemljaEBt" title="alternate"/>
63
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio"/>
64
+ <link rel="edit" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/63408374058"/>
65
+ <author>
66
+ <name>Some Guy</name>
67
+ <email>some.guy@gmail.com</email>
68
+ </author>
69
+ <gd:comments>
70
+ <gd:feedLink href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/comments"/>
71
+ </gd:comments>
72
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
73
+ <gd:where valueString=""/>
74
+ <gd:who email="some.guy@gmail.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="some.guy@gmail.com"/>
75
+ <gd:when endTime="2010-04-09" startTime="2010-04-08">
76
+ <gd:reminder method="alert" minutes="10"/>
77
+ </gd:when>
78
+ <gd:transparency value="http://schemas.google.com/g/2005#event.transparent"/>
79
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
80
+ <gCal:anyoneCanAddSelf value="false"/>
81
+ <gCal:guestsCanInviteOthers value="true"/>
82
+ <gCal:guestsCanModify value="false"/>
83
+ <gCal:guestsCanSeeGuests value="true"/>
84
+ <gCal:sequence value="0"/>
85
+ <gCal:uid value="oj6fmpaulbvk9ouoj0lj4v6hio@google.com"/>
86
+ </entry>
87
+ <entry>
88
+ <id>http://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio</id>
89
+ <published>2010-04-22T16:05:29.000Z</published>
90
+ <updated>2010-05-01T05:14:18.000Z</updated>
91
+ <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/g/2005#event"/>
92
+ <title type="text">Test</title>
93
+ <content type="text"/>
94
+ <link rel="alternate" type="text/html" href="https://www.google.com/calendar/event?eid=b2o2Zm1wYXVsYnZrOW91b2owbGo0djZoaW8gc3RldmUuemljaEBt" title="alternate"/>
95
+ <link rel="self" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio"/>
96
+ <link rel="edit" type="application/atom+xml" href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/63408374058"/>
97
+ <author>
98
+ <name>Some Guy</name>
99
+ <email>some.guy@gmail.com</email>
100
+ </author>
101
+ <gd:comments>
102
+ <gd:feedLink href="https://www.google.com/calendar/feeds/default/private/full/oj6fmpaulbvk9ouoj0lj4v6hio/comments"/>
103
+ </gd:comments>
104
+ <gd:eventStatus value="http://schemas.google.com/g/2005#event.confirmed"/>
105
+ <gd:where valueString=""/>
106
+ <gd:who email="some.guy@gmail.com" rel="http://schemas.google.com/g/2005#event.organizer" valueString="some.guy@gmail.com"/>
107
+ <gd:when endTime="2010-04-09" startTime="2010-04-08">
108
+ <gd:reminder method="alert" minutes="10"/>
109
+ </gd:when>
110
+ <gd:transparency value="http://schemas.google.com/g/2005#event.transparent"/>
111
+ <gd:visibility value="http://schemas.google.com/g/2005#event.default"/>
112
+ <gCal:anyoneCanAddSelf value="false"/>
113
+ <gCal:guestsCanInviteOthers value="true"/>
114
+ <gCal:guestsCanModify value="false"/>
115
+ <gCal:guestsCanSeeGuests value="true"/>
116
+ <gCal:sequence value="0"/>
117
+ <gCal:uid value="oj6fmpaulbvk9ouoj0lj4v6hio@google.com"/>
118
+ </entry>
119
+ </feed>