google_apps_api 0.1.0 → 0.2.1

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.
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/ruby
2
- include REXML
3
-
4
-
5
2
 
6
3
  module GoogleAppsApi #:nodoc:
7
4
 
@@ -12,54 +9,332 @@ module GoogleAppsApi #:nodoc:
12
9
  def initialize(*args)
13
10
  super(:calendar, *args)
14
11
  end
12
+
13
+ def set_calendar_for_user(calendar, user, *args)
14
+ options = args.extract_options!
15
+
16
+ existing_acl = "none"
17
+ need_to_create = false
18
+
19
+ cal = retrieve_calendar_for_user(calendar, user)
20
+ if cal
21
+ existing_acl = cal.details[:accesslevel]
22
+ else
23
+ need_to_create = true
24
+ end
25
+
26
+ acl = options.delete(:accesslevel) || existing_acl
27
+
28
+ if need_to_create
29
+ raise "Must set accesslevel for a newly added calendar" unless acl
30
+ owner_acl = CalendarAcl.new(:calendar => calendar, :scope => user, :role => "owner")
31
+ set_calendar_acl(owner_acl)
32
+ add_calendar_to_user(calendar, user)
33
+
34
+ end
35
+
36
+ if acl == "none"
37
+ remove_calendar_from_user(calendar, user) unless existing_acl == "none"
38
+ else
15
39
 
16
- def retrieve_user_settings(username)
17
- xml_response = request(:retrieve_user_settings, :username => username)
40
+ if acl != existing_acl
41
+ set_calendar_acl(CalendarAcl.new(:calendar => calendar, :scope => user, :role => acl))
42
+ end
43
+ update_calendar_for_user(calendar, user, options) unless options.empty?
44
+
45
+ end
18
46
  end
19
47
 
20
- def add_calendar_to_user(calendar, user)
21
- CalendarEntry.new(request(:add_calendar_to_user, :username => user, :body => calendar.add_message))
48
+ def add_calendar_to_user(calendar, user, *args)
49
+ req = <<-DESCXML
50
+ <entry xmlns='http://www.w3.org/2005/Atom'>
51
+ <id>#{calendar.full_id_escaped}</id>
52
+ </entry>
53
+ DESCXML
54
+
55
+ options = args.extract_options!.merge(:username => user.full_id_escaped, :body => req.strip)
56
+ request(:add_calendar_to_user, options)
22
57
  end
23
58
 
24
- def delete_calendar_from_user(calendar, user)
25
- request(:delete_calendar_from_user, :username => user, :id => calendar.id)
59
+
60
+ def remove_calendar_from_user(calendar, user, *args)
61
+ options = args.extract_options!.merge(:username => user.full_id_escaped, :calendar => calendar.full_id_escaped)
62
+ request(:remove_calendar_from_user, options)
26
63
  end
27
- end
28
64
 
29
65
 
66
+ def update_calendar_for_user(calendar, user, *args)
30
67
 
68
+ username = user.full_id_escaped
31
69
 
70
+ cal = nil
71
+ cal = retrieve_calendar_for_user(calendar, user)
72
+
73
+ cal_id = cal.full_id_escaped
32
74
 
33
- end
75
+ options = args.extract_options!.merge(:username => username, :calendar => cal_id)
76
+
77
+ details = cal.details.merge(options)
78
+
79
+
80
+ req = <<-DESCXML
81
+ <entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'>
82
+ <id>http://www.google.com/calendar/feeds/#{username}/allcalendars/full/#{cal_id}</id>
83
+
84
+ <title type='text'>#{details[:title]}</title>
85
+ <summary type='text'>#{details[:summary]}</summary>
86
+ <gCal:timezone value='#{details[:timezone]}'/>
87
+ <gCal:hidden value='#{details[:hidden].to_s}'/>
88
+ <gCal:color value='#{details[:color]}'/>
89
+ <gCal:selected value='#{details[:selected].to_s}'/>
90
+ <gd:where valueString='#{details[:where]}'/>
91
+ </entry>
92
+
93
+ DESCXML
94
+
95
+ options[:body] = req.strip
96
+ request(:update_calendar_for_user, options)
97
+ end
98
+
99
+ # lists all calendards for a user
100
+ def retrieve_calendars_for_user(user, *args)
101
+ options = args.extract_options!.merge(:username => user.full_id_escaped)
102
+ request(:retrieve_calendars_for_user, options)
103
+ end
34
104
 
35
- class CalendarEntry
36
- attr_accessor :id, :title, :edit_link
37
- def initialize(xml = nil)
38
- if xml
39
- @id = xml.at_css('id').content.gsub(/^.*calendars\//,"")
40
- @title = xml.at_css('title').content
41
- @edit_link = xml.at_css('link[rel=edit]').attribute('href').value
105
+ # lists all calendards for a user
106
+ def retrieve_calendar_for_user(calendar, user, *args)
107
+ options = args.extract_options!.merge(:calendar => calendar.full_id_escaped, :username => user.full_id_escaped)
108
+ retries = options[:retries] || 5
109
+
110
+ while (retries > 0)
111
+ begin
112
+ return request(:retrieve_calendar_for_user, options)
113
+ rescue GDataError => g
114
+ retries -= 1
115
+ sleep 0.5
116
+ end
117
+ end
118
+
119
+ return nil
120
+ end
121
+
122
+
123
+ # returns array of acls for a given calendar
124
+ def retrieve_acls_for_calendar(calendar, *args)
125
+ options = args.extract_options!.merge(:calendar => calendar.full_id_escaped)
126
+ request(:retrieve_acls_for_calendar, options)
127
+ end
128
+
129
+
130
+ # generally, use set_calendar_acl, it works for both creates and updates
131
+ # this will throw an exception "Version Conflict" if it already exists
132
+ def create_calendar_acl(acl, *args)
133
+ req = <<-DESCXML
134
+ <?xml version="1.0" encoding="UTF-8"?>
135
+ <entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>
136
+ <category scheme='http://schemas.google.com/g/2005#kind'
137
+ term='http://schemas.google.com/acl/2007#accessRule'/>
138
+ <gAcl:scope type='#{acl.scope.kind}' value='#{acl.scope.full_id}'></gAcl:scope>
139
+ <gAcl:role
140
+ value='http://schemas.google.com/gCal/2005##{acl.role}'>
141
+ </gAcl:role>
142
+ </entry>
143
+ DESCXML
144
+ options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :body => req.strip)
145
+ request(:create_calendar_acl, options)
42
146
  end
147
+
148
+ # you can substitute set_calendar_acl with role set to none
149
+ def remove_calendar_acl(acl, *args)
150
+ options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :scope => acl.scope.qualified_id_escaped)
151
+ request(:remove_calendar_acl, options)
152
+ end
153
+
154
+
155
+ # updates a given acl for a given scope
156
+ def set_calendar_acl(acl, *args)
157
+ req = <<-DESCXML
158
+ <?xml version="1.0" encoding="UTF-8"?>
159
+ <entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'
160
+ xmlns:gd='http://schemas.google.com/g/2005'
161
+ gd:etag='W/"DU4ERH47eCp7ImA9WxRVEkQ."'>
162
+ <category scheme='http://schemas.google.com/g/2005#kind'
163
+ term='http://schemas.google.com/acl/2007#accessRule'/>
164
+ <gAcl:scope type='#{acl.scope.kind}' value='#{acl.scope.full_id}'></gAcl:scope>
165
+ <gAcl:role
166
+ value='#{acl.role_schema}'>
167
+ </gAcl:role>
168
+ </entry>
169
+ DESCXML
170
+
171
+ options = args.extract_options!.merge(:calendar => acl.calendar.full_id_escaped, :scope => acl.scope.qualified_id_escaped, :body => req.strip)
172
+ request(:set_calendar_acl, options)
173
+ end
174
+
175
+
176
+
43
177
  end
44
178
 
45
- def to_s
46
- title
179
+ end
180
+
181
+ class CalendarAcl
182
+ attr_accessor :calendar, :scope, :role
183
+
184
+ def initialize(*args)
185
+ options = args.extract_options!
186
+ if (_xml = options[:xml])
187
+ _xml = _xml.kind_of?(Nokogiri::XML::Document) ? _xml.children.first : _xml
188
+ scope_kind = _xml.at_css('gAcl|scope').attribute("type").content
189
+ scope_id = _xml.at_css('gAcl|scope').attribute("value").content
190
+
191
+ @scope = Entity.new(scope_kind => scope_id)
192
+ @role = _xml.at_css('title').content
193
+ @calendar = CalendarEntity.new(_xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,""))
194
+
195
+ else
196
+
197
+ @scope = options[:scope]
198
+ @role = options[:role]
199
+ @calendar = options[:calendar]
200
+ end
201
+ end
202
+
203
+ def role_schema
204
+ if @role == "none"
205
+ "none"
206
+ else
207
+ "http://schemas.google.com/gCal/2005##{@role}"
208
+ end
47
209
  end
210
+ end
211
+
48
212
 
49
- def inspect
50
- "<CalendarEntry: #{title} : #{id}, #{edit_link}>"
213
+ # def role_url
214
+ # "http://schemas.google.com/gCal/2005#{@role}"
215
+ # end
216
+ # end
217
+
218
+
219
+ class CalendarEntity < Entity
220
+ attr_reader :details
221
+
222
+ def initialize(*args)
223
+ @details = {}
224
+
225
+ options = args.extract_options!
226
+ if options.has_key?(:xml)
227
+ parse_calendar_xml(options[:xml])
228
+ super(:calendar => @calendar_id)
229
+ else
230
+ @title = options.delete(:title)
231
+
232
+ if args.first.kind_of?(String)
233
+ super(:calendar => args.first)
234
+ else
235
+ super(options.merge(:kind => "calendar"))
236
+ end
237
+ end
51
238
  end
239
+
240
+ def ==(other)
241
+ super(other)
242
+ end
243
+
244
+ #
245
+ # # updates with details.
246
+ # def refresh_details!(c_api, *args)
247
+ # c_api.retrieve_calendar_details(self, *args)
248
+ # end
249
+ #
250
+ def get_acls(c_api, *args)
251
+ c_api.retrieve_acls_for_calendar(self, *args)
252
+ end
253
+
254
+ private
255
+ def parse_calendar_xml(xml)
256
+ entry = xml.kind_of?(Nokogiri::XML::Document) ? xml.children.first : xml
257
+
258
+ @kind = "calendar"
259
+
260
+ case entry.attribute("kind").content
261
+ when "calendar#calendar"
262
+ @calendar_id = entry.at_css('id').content.gsub(/^.*calendars\//,"")
263
+ @details[:title] = entry.at_css('title').content
264
+ @details[:summary] = entry.at_css('summary')
265
+ @details[:summary] = @details[:summary] ? @details[:summary].content : ""
266
+ @details[:timezone] = entry.at_css('gCal|timezone').attribute("value").content
267
+ @details[:accesslevel] = entry.at_css('gCal|accesslevel').attribute("value").content
268
+ @details[:where] = entry.at_css('gd|where')
269
+ @details[:where] = @details[:where] ? @details[:where].attribute("valueString").content : ""
270
+ @details[:color] = entry.at_css('gCal|color').attribute("value").content
271
+
272
+ @details[:hidden] = entry.at_css('gCal|hidden').attribute("value").content == "true"
273
+ @details[:selected] = entry.at_css('gCal|selected').attribute("value").content == "true"
274
+ when "calendar#acl"
275
+ @calendar_id = entry.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,"")
276
+ end
52
277
 
53
- def add_message
54
- Nokogiri::XML::Builder.new { |xml|
55
- xml.entry(:xmlns => "http://www.w3.org/2005/Atom") {
56
- xml.id_ {
57
- xml.text id.to_s
58
- }
59
- }
60
- }.to_xml
61
278
  end
279
+
62
280
  end
63
281
 
64
282
 
283
+ # acl names: none, read, freebusy, editor, owner, root
284
+ # class CalendarEntry
285
+ # attr_accessor :calendar_id, :title, :entity, :role
286
+ # def initialize(*args)
287
+ # options = args.extract_options!
288
+ # if (_xml = options[:xml])
289
+ # kind = _xml.attribute("kind")
290
+ # raise "invalid xml passed" unless kind
291
+ #
292
+ # case kind.content
293
+ # when "calendar#calendar"
294
+ # user_id = _xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/.*$/,"")
295
+ #
296
+ # @entity = Entity.new("user", user_id)
297
+ # @role = _xml.at_css('gCal|accesslevel').attribute("value").content
298
+ #
299
+ # @calendar_id = _xml.at_css('id').content.gsub(/^.*calendars\//,"")
300
+ # @title = _xml.at_css('title').content
301
+ # when "calendar#acl"
302
+ # scope_type = _xml.at_css('gAcl|scope').attribute("type").content
303
+ # scope_id = _xml.at_css('gAcl|scope').attribute("value").content
304
+ # @entity = Entity.new(scope_type, scope_id)
305
+ # @role = _xml.at_css('title').content
306
+ # @calendar_id = _xml.at_css('id').content.gsub(/^.*feeds\//,"").gsub(/\/acl.*$/,"")
307
+ # end
308
+ # else
309
+ # @calendar_id = options[:calendar_id]
310
+ # @title = options[:title]
311
+ # @entity = options[:entity]
312
+ # @role = options[:role]
313
+ #
314
+ # end
315
+ #
316
+ # raise "Calendar ID required" unless @calendar_id
317
+ #
318
+ # end
319
+ #
320
+ # def to_s
321
+ # @calendar_id
322
+ # end
323
+ #
324
+ # def to_url
325
+ # CGI::escape(CGI::unescape(@calendar_id))
326
+ # end
327
+ #
328
+ # def add_message
329
+ # req = <<-DESCXML
330
+ # <entry xmlns='http://www.w3.org/2005/Atom'>
331
+ # <id>#{CGI::escape(calendar_id.to_s)}</id>
332
+ # </entry>
333
+ # DESCXML
334
+ #
335
+ # req.strip
336
+ # end
337
+ # end
338
+
339
+
65
340
  end
@@ -1,53 +1,83 @@
1
- # #!/usr/bin/ruby
2
- #
3
- # # require 'cgi'
4
- # # require 'rexml/document'
5
- # #
6
- #
7
- # include REXML
8
- #
9
- #
10
- #
11
- # module GoogleAppsApi #:nodoc:
12
- # module SharedContacts
13
- # class Api < GoogleAppsApi::BaseApi
14
- # attr_reader :token
15
- #
16
- # def initialize(*args)
17
- # action_list = {
18
- # :domain_login => [:post, ":auth:/accounts/ClientLogin"],
19
- # :retrieve_all => [:get, ":feed_basic:/full"],
20
- # :create_contact => [:post, ":feed_basic:/full"]
21
- # }
22
- #
23
- # options = args.extract_options!
24
- # options.merge!(:action_hash => action_list, :auth => "https://www.google.com", :feed => "https://www.google.com", :service => "cp")
25
- # options[:feed_basic] = options[:feed]+ "/m8/feeds/contacts/#{options[:domain]}"
26
- #
27
- # super(options)
28
- # end
29
- #
30
- # def create_contact(*args)
31
- # options = args.extract_options!
32
- # msg = RequestMessage.new
33
- # msg.add_contact(options)
34
- # response = request(:create_contact, :body => msg.to_s)
35
- # return response.to_s
36
- # end
37
- #
38
- # end
39
- #
40
- # class RequestMessage < GoogleAppsApi::RequestMessage #:nodoc:
41
- #
42
- # def add_contact(*args)
43
- # options = args.extract_options!
44
- # base = self.elements["atom:entry"]
45
- # base.add_element("atom:title", {"type" => "text"}).text = options[:name]
46
- # base.add_element("atom:content", {"type" => "text"}).text = "Notes"
47
- # (options[:email] || {}).each_pair do |email_type, address|
48
- # base.add_element("gd:email", {"rel" => "http://schemas.google.com/g/2005##{email_type.to_s}", "address" => address})
49
- # end
50
- # end
51
- # end
52
- # end
53
- # end
1
+ #!/usr/bin/ruby
2
+
3
+ module GoogleAppsApi #:nodoc:
4
+ module Contacts
5
+ class Api < GoogleAppsApi::BaseApi
6
+ attr_reader :token
7
+
8
+ def initialize(*args)
9
+ super(:contacts, *args)
10
+ end
11
+
12
+ def retrieve_all_contacts(*args)
13
+ options = args.extract_options!
14
+ request(:retrieve_all_contacts, options)
15
+ end
16
+
17
+ def remove_contact(contact, *args)
18
+ options = args.extract_options!.merge(:contact => contact.id_escaped, :merge_headers => {"If-Match" => "*"})
19
+
20
+ request(:remove_contact, options)
21
+ end
22
+
23
+ def create_contact(contact, *args)
24
+
25
+ req = <<-DESCXML
26
+ <atom:entry xmlns:atom='http://www.w3.org/2005/Atom'
27
+ xmlns:gd='http://schemas.google.com/g/2005'>
28
+ <atom:category scheme='http://schemas.google.com/g/2005#kind'
29
+ term='http://schemas.google.com/contact/2008#contact' />
30
+ <atom:title type='text'>#{contact.name}</atom:title>
31
+ DESCXML
32
+
33
+ contact.emails.each_pair do |loc, email|
34
+ req += <<-DESCXML
35
+ <gd:email rel='http://schemas.google.com/g/2005##{loc}'
36
+ primary='#{contact.primary_email == loc ? 'true' : 'false'}'
37
+ address='#{email}' displayName='#{contact.name}' />
38
+ DESCXML
39
+ end
40
+
41
+ req += "</atom:entry>"
42
+
43
+ options = args.extract_options!.merge(:body => req.strip)
44
+
45
+ request(:create_contact, options)
46
+
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ class ContactEntity < Entity
53
+ attr_reader :name, :emails, :primary_email
54
+
55
+ def initialize(*args)
56
+ @emails = {}
57
+
58
+ options = args.extract_options!
59
+ if (_xml = options[:xml])
60
+ xml = _xml.at_css("entry") || _xml
61
+ @kind = "contact"
62
+ @id = xml.at_css("id").content.gsub(/^.+\/base\//,"")
63
+ @domain = xml.at_css("id").content.gsub(/^.+\/contacts\/([^\/]+)\/.+$/,"\\1")
64
+ @name = xml.at_css("title").content
65
+ else
66
+ if args.first.kind_of?(String)
67
+ super(:contact => args.first)
68
+ else
69
+ @name = options.delete(:name)
70
+ @emails = options.delete(:emails) || {}
71
+
72
+ super(options.merge(:kind => "contact"))
73
+ end
74
+ end
75
+ end
76
+
77
+ def ==(other)
78
+ super(other)
79
+ end
80
+
81
+ end
82
+
83
+ end