amr_google_calendar 0.2.3

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.
@@ -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>