provisioning-api 0.0.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/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ .DS_Store
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
data/.rvmrc ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.2-p290@provisioning-api"
8
+
9
+ #
10
+ # Uncomment following line if you want options to be set only for given project.
11
+ #
12
+ # PROJECT_JRUBY_OPTS=( --1.9 )
13
+
14
+ #
15
+ # First we attempt to load the desired environment directly from the environment
16
+ # file. This is very fast and efficient compared to running through the entire
17
+ # CLI and selector. If you want feedback on which environment was used then
18
+ # insert the word 'use' after --create as this triggers verbose mode.
19
+ #
20
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
21
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
22
+ then
23
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
24
+
25
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
26
+ then
27
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
28
+ fi
29
+ else
30
+ # If the environment file has not yet been created, use the RVM CLI to select.
31
+ if ! rvm --create "$environment_id"
32
+ then
33
+ echo "Failed to create RVM environment '${environment_id}'."
34
+ exit 1
35
+ fi
36
+ fi
37
+
38
+ #
39
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
40
+ # it be automatically loaded. Uncomment the following and adjust the filename if
41
+ # necessary.
42
+ #
43
+ # filename=".gems"
44
+ # if [[ -s "$filename" ]] ; then
45
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
46
+ # fi
47
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ group :development, :test do
4
+ gem 'vcr'
5
+ gem 'pry'
6
+ gem 'fakeweb'
7
+ end
8
+
data/Gemfile.lock ADDED
@@ -0,0 +1,25 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ coderay (0.9.8)
5
+ fakeweb (1.3.0)
6
+ method_source (0.6.7)
7
+ ruby_parser (>= 2.3.1)
8
+ pry (0.9.7.4)
9
+ coderay (~> 0.9.8)
10
+ method_source (~> 0.6.7)
11
+ ruby_parser (>= 2.3.1)
12
+ slop (~> 2.1.0)
13
+ ruby_parser (2.3.1)
14
+ sexp_processor (~> 3.0)
15
+ sexp_processor (3.0.8)
16
+ slop (2.1.0)
17
+ vcr (1.11.3)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ fakeweb
24
+ pry
25
+ vcr
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ Google apps provisioning API ruby library
2
+ ========================================
3
+
4
+ This is a copy of the Google Apps provisioning API taken from http://code.google.com/p/gdatav2rubyclientlib/ v 1.2 (latest)
5
+
6
+ BEFORE USE Enable the API on Google Admin Panel
7
+ =================================================
8
+
9
+ Domain Settings -> User Settings -> Enable Provisionin API
10
+
11
+ https://www.google.com/a/cpanel/[YOUR_DOMAIN.com]/DomainSettingsUserSettings
12
+
13
+ Example
14
+ =========
15
+
16
+ #!/usr/bin/ruby
17
+ require 'gappsprovisioning/provisioningapi'
18
+ include GAppsProvisioning
19
+ adminuser = "root@mydomain.com"
20
+ password = "PaSsWo4d!"
21
+ myapps = ProvisioningApi.new(adminuser,password)
22
+
23
+ new_user = myapps.create_user("jsmith", "john", "smith", "12345678", nil, "2048")
24
+ puts new_user.family_name
25
+ puts new_user.given_name
26
+
27
+ Want to update a user ?
28
+
29
+ user = myapps.retrieve_user('jsmith')
30
+ user_updated = myapps.update_user(user.username, user.given_name, user.family_name, nil, nil, "true")
31
+
32
+ Want to add an alias or nickname ?
33
+
34
+ new_nickname = myapps.create_nickname("jsmith", "john.smith")
35
+
36
+ Want to add an email forwarding (thanks to Scott Jungling) ?
37
+
38
+ new_forwarding = myapps.create_email_forwarding("jsmith", "brenda@yourdomain.com", "KEEP")
39
+
40
+ Want to manage groups ? (i.e. mailing lists)
41
+
42
+ new_group = myapps.create_group("sales-dep", ['Sales Departement'])
43
+ new_member = myapps.add_member_to_group("jsmith", "sales-dep")
44
+ new_owner = myapps.add_owner_to_group("jsmith", "sales-dep")
45
+ # (ATTENTION: a owner is added only if it's already member of the group!)
46
+
47
+ Want to handle errors ?
48
+
49
+ begin
50
+ user = myapps.retrieve_user('noone')
51
+ puts "givenName : "+user.given_name, "familyName : "+user.family_name, "username : "+user.username
52
+ puts "admin ? : "+user.admin
53
+ rescue GDataError => e
54
+ puts "errorcode = "+e.code, "input : "+e.input, "reason : "+e.reason
55
+ end
56
+
57
+
58
+ Character Usage
59
+ ===============
60
+
61
+ - Usernames may contain letters (a-z), numbers (0-9), dashes (-), underscores (_), and periods (.), and may not contain an equal sign (=) or brackets (<,>).
62
+ They can't contain more than one period in a row.
63
+
64
+ - Passwords may contain any combination of characters, but a minimum of 8 characters is required.
65
+
66
+ - First and last names support unicode/UTF-8 characters, and may contain spaces, letters (a-z), numbers (0-9), dashes (-), forward slashes (/), and periods (.), with a maximum of 40 characters.
67
+
68
+ -Characters that cannot be part of a name include: !, #, $, %, &, (, ), *, +, /, ?, \, ^, |, ~
69
+
70
+ Testing
71
+ =========
72
+
73
+ Google API testing is hard. There are multiple server side rules that
74
+ make testing hard, such as deleting a user and creating it back requires
75
+ you to wait for 5 days.
76
+
77
+ Instead we simply use VCR to verify that code interprets correctly and
78
+ that the codes are being made. If you want to "really" test the API you
79
+ need a username and password for a domain to run your tests.
80
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.pattern = "test/test_*.rb"
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
10
+
11
+ task :build do
12
+ system "gem build provisioning-api.gemspec"
13
+ end
14
+
@@ -0,0 +1,96 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: https://apps-apis.google.com:443/accounts/ClientLogin
6
+ body: !!null
7
+ headers:
8
+ content-type:
9
+ - application/x-www-form-urlencoded
10
+ content-length:
11
+ - '75'
12
+ response: !ruby/struct:VCR::Response
13
+ status: !ruby/struct:VCR::ResponseStatus
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ content-type:
18
+ - text/plain
19
+ cache-control:
20
+ - no-cache, no-store
21
+ pragma:
22
+ - no-cache
23
+ expires:
24
+ - Mon, 01-Jan-1990 00:00:00 GMT
25
+ date:
26
+ - Sat, 26 Nov 2011 20:35:13 GMT
27
+ x-content-type-options:
28
+ - nosniff
29
+ x-xss-protection:
30
+ - 1; mode=block
31
+ content-length:
32
+ - '1010'
33
+ server:
34
+ - GSE
35
+ body: ! 'SID=DQAAAOcAAABGChSuJCFvcdA-phg8w37dgl7m125nCpbJSMsqlGTD8nr-T5fqvL4VJJUPQfgOLwArzuJ_v5r3r0irFYlp75W7CuwVVIZcGVh3NinwQuK1C2b1LoDRXPYQAksj0FHf_-M4La-t3sx9Psz9KVZS0_sBRnaLACaX613iyTQQhih75XdF_pPJEAk2i-FODvFlywaVITn6aft6Q15ba2Ns3pXBxHC2xmNtXTD_A-6ItDo5h6ged6uE_tMUOI37hpmT56mqHGM2MKeKS1FWltKG04Q7a4IqAFtbOqIfm7NQng2OGTH8n35QamnvpNClsHXj-LU
36
+
37
+ LSID=DQAAAOgAAACmOK2Tz82DjgQCZsSyl3BBXyBrE_FRnJ1kX_b2--FxR41rFaDLrF7vZCVQRbYj6umr8m2k16XSHyRGC0OHAEN4ordG7pi3gVFePuPLGStA_0TXdR6kAD0E8XeuEiEo4jU2QOZUhNdUEsYJ2rmvyfCaieKPq28xy2jhxlni3HKe4ys6kCV9nslCcvpmgXGPtTe1MJJ_GhRgIwfh3Xp9JsP87tijHw7FK8n5rhZCuxtYWlMXZGXuTtWD_NsqcJ8rRLedk6KAsIxTFH_X9IC-3CRvRacRk29Wc6Ccv2YEMQWm5qq9WgrXkTcoQzdt-aY1JM0
38
+
39
+ Auth=DQAAAOkAAACmOK2Tz82DjgQCZsSyl3BBXyBrE_FRnJ1kX_b2--FxR41rFaDLrF7vZCVQRbYj6uluQDfTwKQVZT7NBybM4B5pq3iIBq-B7igOAeC3hfBQGPfE8Q5Lyu9qoAXLYOOkRjNBfydACgdsJxGbCZoggW3Amkh1Js4awD-DXSOVepBGOYiF8hCYiLu3LlwMt1e6EzTbVyOlCyB8aa1HPZDvHTh0VDNBSGSQZtl6dUjp4z1rOfutoNhpl0niywLX974lp0nXm4oqWH0a2ASv1vlZ2t_wbDMe3EVWI42N_DBMCP7t6jscweCZZhbREpjUEcEvgQQ
40
+
41
+ '
42
+ http_version: '1.1'
43
+ - !ruby/struct:VCR::HTTPInteraction
44
+ request: !ruby/struct:VCR::Request
45
+ method: :post
46
+ uri: https://apps-apis.google.com:443/a/feeds/authysec.com/user/2.0
47
+ body: !!null
48
+ headers:
49
+ content-type:
50
+ - application/atom+xml
51
+ authorization:
52
+ - GoogleLogin auth=DQAAAOkAAACmOK2Tz82DjgQCZsSyl3BBXyBrE_FRnJ1kX_b2--FxR41rFaDLrF7vZCVQRbYj6uluQDfTwKQVZT7NBybM4B5pq3iIBq-B7igOAeC3hfBQGPfE8Q5Lyu9qoAXLYOOkRjNBfydACgdsJxGbCZoggW3Amkh1Js4awD-DXSOVepBGOYiF8hCYiLu3LlwMt1e6EzTbVyOlCyB8aa1HPZDvHTh0VDNBSGSQZtl6dUjp4z1rOfutoNhpl0niywLX974lp0nXm4oqWH0a2ASv1vlZ2t_wbDMe3EVWI42N_DBMCP7t6jscweCZZhbREpjUEcEvgQQ
53
+ content-length:
54
+ - '473'
55
+ response: !ruby/struct:VCR::Response
56
+ status: !ruby/struct:VCR::ResponseStatus
57
+ code: 201
58
+ message: Created
59
+ headers:
60
+ content-type:
61
+ - application/atom+xml; charset=UTF-8
62
+ expires:
63
+ - Sat, 26 Nov 2011 20:35:17 GMT
64
+ date:
65
+ - Sat, 26 Nov 2011 20:35:17 GMT
66
+ cache-control:
67
+ - private, max-age=0, must-revalidate, no-transform
68
+ vary:
69
+ - Accept, X-GData-Authorization, GData-Version
70
+ gdata-version:
71
+ - '1.0'
72
+ location:
73
+ - https://apps-apis.google.com/a/feeds/authysec.com/user/2.0/josmith
74
+ content-location:
75
+ - https://apps-apis.google.com/a/feeds/authysec.com/user/2.0/josmith
76
+ x-content-type-options:
77
+ - nosniff
78
+ x-frame-options:
79
+ - SAMEORIGIN
80
+ x-xss-protection:
81
+ - 1; mode=block
82
+ server:
83
+ - GSE
84
+ transfer-encoding:
85
+ - chunked
86
+ body: <?xml version='1.0' encoding='UTF-8'?><entry xmlns='http://www.w3.org/2005/Atom'
87
+ xmlns:apps='http://schemas.google.com/apps/2006' xmlns:gd='http://schemas.google.com/g/2005'><id>https://apps-apis.google.com/a/feeds/authysec.com/user/2.0/josmith</id><updated>1970-01-01T00:00:00.000Z</updated><category
88
+ scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/apps/2006#user'/><title
89
+ type='text'>josmith</title><link rel='self' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/authysec.com/user/2.0/josmith'/><link
90
+ rel='edit' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/authysec.com/user/2.0/josmith'/><apps:login
91
+ userName='josmith' suspended='false' ipWhitelisted='false' admin='false' changePasswordAtNextLogin='true'
92
+ agreedToTerms='false'/><apps:quota limit='7168'/><apps:name familyName='smith'
93
+ givenName='john'/><gd:feedLink rel='http://schemas.google.com/apps/2006#user.nicknames'
94
+ href='https://apps-apis.google.com/a/feeds/authysec.com/nickname/2.0?username=josmith'/><gd:feedLink
95
+ rel='http://schemas.google.com/apps/2006#user.emailLists' href='https://apps-apis.google.com/a/feeds/authysec.com/emailList/2.0?recipient=josmith%40authysec.com'/></entry>
96
+ http_version: '1.1'
@@ -0,0 +1,882 @@
1
+ #!/usr/bin/ruby
2
+ # == Google Apps Provisioning API client library
3
+ #
4
+ # This library allows you to manage your domain (accounts, email lists, aliases) within your Ruby code.
5
+ # It's based on the GDATA provisioning API v2.0.
6
+ # Reference : http://code.google.com/apis/apps/gdata_provisioning_api_v2.0_reference.html.
7
+ #
8
+ # All the public methods with _ruby_style_ names are aliased with _javaStyle_ names. Ex : create_user and createUser.
9
+ #
10
+ # Notice : because it uses REXML, your script using this library MUST be encoded in unicode (UTF-8).
11
+ #
12
+ # == Examples
13
+ #
14
+ # #!/usr/bin/ruby
15
+ # require 'provisioning-api/provisioningapi'
16
+ # include provisioning-api
17
+ # adminuser = "root@mydomain.com"
18
+ # password = "PaSsWo4d!"
19
+ # myapps = ProvisioningApi.new(adminuser,password)
20
+ # (see examples in ProvisioningApi.new documentation for handling proxies)
21
+ #
22
+ # new_user = myapps.create_user("jsmith", "john", "smith", "secret", nil, "2048")
23
+ # puts new_user.family_name
24
+ # puts new_user.given_name
25
+ #
26
+ # Want to update a user ?
27
+ #
28
+ # user = myapps.retrieve_user('jsmith')
29
+ # user_updated = myapps.update_user(user.username, user.given_name, user.family_name, nil, nil, "true")
30
+ #
31
+ # Want to add an alias or nickname ?
32
+ #
33
+ # new_nickname = myapps.create_nickname("jsmith", "john.smith")
34
+ #
35
+ # Want to manage groups ? (i.e. mailing lists)
36
+ #
37
+ # new_group = myapps.create_group("sales-dep", ['Sales Departement'])
38
+ # new_member = myapps.add_member_to_group("jsmith", "sales-dep")
39
+ # new_owner = myapps.add_owner_to_group("jsmith", "sales-dep")
40
+ # (ATTENTION: an owner is added only if it's already member of the group!)
41
+ #
42
+ # Want to handle errors ?
43
+ #
44
+ # begin
45
+ # user = myapps.retrieve_user('noone')
46
+ # puts "givenName : "+user.given_name, "familyName : "+user.family_name, "username : "+user.username"
47
+ # puts "admin ? : "+user.admin
48
+ # rescue GDataError => e
49
+ # puts "errorcode = " +e.code, "input : "+e.input, "reason : "+e.reason
50
+ # end
51
+ #
52
+ # Email lists (deprecated) ?
53
+ #
54
+ # new_list = myapps.create_email_list("sales-dep")
55
+ # new_address = myapps.add_address_to_email_list("sales-dep", "bibi@ruby-forge.org")
56
+ #
57
+ # All methods described in the provisioning-api::ProvisioningApi class documentation.
58
+ #
59
+ # Authors :: Jérôme Bousquié, Roberto Cerigato
60
+ # Ruby version :: from 1.8.6
61
+ # Licence :: Apache Licence, version 2
62
+ #
63
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
64
+ # use this file except in compliance with the License. You may obtain a copy of
65
+ # the License at
66
+ #
67
+ # http://www.apache.org/licenses/LICENSE-2.0
68
+ #
69
+ # Unless required by applicable law or agreed to in writing, software
70
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
71
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
72
+ # License for the specific language governing permissions and limitations under
73
+ # the License.
74
+ #
75
+
76
+ require 'cgi'
77
+ require 'rexml/document'
78
+
79
+ require 'provisioning-api/connection'
80
+ require 'provisioning-api/exceptions'
81
+
82
+ include REXML
83
+
84
+
85
+
86
+ module GAppsProvisioning #:nodoc:
87
+
88
+ # =Administrative object for accessing your domain
89
+ # Examples
90
+ #
91
+ # adminuser = "root@mydomain.com"
92
+ # password = "PaSsWo4d!"
93
+ # myapps = ProvisioningApi.new(adminuser,password)
94
+ # (see examples in ProvisioningApi.new documentation for handling proxies)
95
+ #
96
+ # new_user = myapps.create_user("jsmith", "john", "smith", "secret", nil, "2048")
97
+ # puts new_user.family_name
98
+ # puts new_user.given_name
99
+ #
100
+ # Want to update a user ?
101
+ #
102
+ # user = myapps.retrieve_user('jsmith')
103
+ # user_updated = myapps.update_user(user.username, user.given_name, user.family_name, nil, nil, "true")
104
+ #
105
+ # Want to add an alias or nickname ?
106
+ #
107
+ # new_nickname = myapps.create_nickname("jsmith", "john.smith")
108
+ #
109
+ # Want to manage groups ? (i.e. mailing lists)
110
+ #
111
+ # new_group = myapps.create_group("sales-dep", ['Sales Departement'])
112
+ # new_member1 = myapps.add_member_to_group("john.doe@somedomain.com", "sales-dep")
113
+ # new_member2 = myapps.add_member_to_group("jsmith", "sales-dep")
114
+ # new_owner = myapps.add_owner_to_group("jsmith", "sales-dep")
115
+ # (ATTENTION: an owner is added only if it's already member of the group!)
116
+ #
117
+ #
118
+ # Want to handle errors ?
119
+ #
120
+ # begin
121
+ # user = myapps.retrieve_user('noone')
122
+ # puts "givenName : "+user.given_name, "familyName : "+user.family_name, "username : "+user.username"
123
+ # puts "admin ? : "+user.admin
124
+ # rescue GDataError => e
125
+ # puts "errorcode = " +e.code, "input : "+e.input, "reason : "+e.reason
126
+ # end
127
+ #
128
+ #
129
+
130
+
131
+
132
+ class ProvisioningApi
133
+ @@google_host = 'apps-apis.google.com'
134
+ @@google_port = 443
135
+ # authentication token, valid up to 24 hours after the last connection
136
+ attr_reader :token
137
+
138
+
139
+ # Creates a new ProvisioningApi object
140
+ #
141
+ # mail : Google Apps domain administrator e-mail (string)
142
+ # passwd : Google Apps domain administrator password (string)
143
+ # proxy : (optional) host name, or IP, of the proxy (string)
144
+ # proxy_port : (optional) proxy port number (numeric)
145
+ # proxy_user : (optional) login for authenticated proxy only (string)
146
+ # proxy_passwd : (optional) password for authenticated proxy only (string)
147
+ #
148
+ # The domain name is extracted from the mail param value.
149
+ #
150
+ # Examples :
151
+ # standard : no proxy
152
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
153
+ # proxy :
154
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd','domain.proxy.com',8080)
155
+ # authenticated proxy :
156
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd','domain.proxy.com',8080,'foo','bAr')
157
+ def initialize(mail, passwd, proxy=nil, proxy_port=nil, proxy_user=nil, proxy_passwd=nil)
158
+ domain = mail.split('@')[1]
159
+ @action = setup_actions(domain)
160
+ conn = Connection.new(@@google_host, @@google_port, proxy, proxy_port, proxy_user, proxy_passwd)
161
+ @connection = conn
162
+ @token = login(mail, passwd)
163
+ @headers = {'Content-Type'=>'application/atom+xml', 'Authorization'=> 'GoogleLogin auth='+token}
164
+ return @connection
165
+ end
166
+
167
+
168
+
169
+ # Returns a UserEntry instance from a username
170
+ # ex :
171
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
172
+ # user = myapps.retrieve_user('jsmith')
173
+ # puts "givenName : "+user.given_name
174
+ # puts "familyName : "+user.family_name
175
+ def retrieve_user(username)
176
+ xml_response = request(:user_retrieve, username, @headers)
177
+ user_entry = UserEntry.new(xml_response.elements["entry"])
178
+ end
179
+
180
+ # Returns a UserEntry array populated with all the users in the domain. May take a while depending on the number of users in your domain.
181
+ # ex :
182
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
183
+ # list= myapps.retrieve_all_users
184
+ # list.each{ |user| puts user.username}
185
+ # puts 'nb users : ',list.size
186
+ def retrieve_all_users
187
+ response = request(:user_retrieve_all,nil,@headers)
188
+ user_feed = Feed.new(response.elements["feed"], UserEntry)
189
+ user_feed = add_next_feeds(user_feed, response, UserEntry)
190
+ end
191
+
192
+ # Returns a UserEntry array populated with 100 users, starting from a username
193
+ # ex :
194
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
195
+ # list= myapps.retrieve_page_of_users("jsmtih")
196
+ # list.each{ |user| puts user.username}
197
+ def retrieve_page_of_users(start_username)
198
+ param='?startUsername='+start_username
199
+ response = request(:user_retrieve_all,param,@headers)
200
+ user_feed = Feed.new(response.elements["feed"], UserEntry)
201
+ end
202
+
203
+ # Creates an account in your domain, returns a UserEntry instance
204
+ # params :
205
+ # username, given_name, family_name and password are required
206
+ # passwd_hash_function (optional) : nil (default) or "SHA-1"
207
+ # quota (optional) : nil (default) or integer for limit in MB
208
+ # ex :
209
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
210
+ # user = myapps.create('jsmith', 'John', 'Smith', 'p455wD')
211
+ #
212
+ # By default, a new user must change his password at first login. Please use update_user if you want to change this just after the creation.
213
+ def create_user(username, given_name, family_name, password, passwd_hash_function=nil, quota=nil)
214
+ msg = RequestMessage.new
215
+ msg.about_login(username,password,passwd_hash_function,"false","false", "true")
216
+ msg.about_name(family_name, given_name)
217
+ msg.about_quota(quota.to_s) if quota
218
+ response = request(:user_create,nil,@headers, msg.to_s)
219
+ user_entry = UserEntry.new(response.elements["entry"])
220
+ end
221
+
222
+ # Updates an account in your domain, returns a UserEntry instance
223
+ # params :
224
+ # username is required and can't be updated.
225
+ # given_name and family_name are required, may be updated.
226
+ # if set to nil, every other parameter won't update the attribute.
227
+ # passwd_hash_function : string "SHA-1", "MD5" or nil (default)
228
+ # admin : string "true" or string "false" or nil (no boolean : true or false).
229
+ # suspended : string "true" or string "false" or nil (no boolean : true or false)
230
+ # change_passwd : string "true" or string "false" or nil (no boolean : true or false)
231
+ # quota : limit en MB, ex : string "2048"
232
+ # ex :
233
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
234
+ # user = myapps.update('jsmith', 'John', 'Smith', nil, nil, "true", nil, "true", nil)
235
+ # puts user.admin => "true"
236
+ def update_user(username, given_name, family_name, password=nil, passwd_hash_function=nil, admin=nil, suspended=nil, changepasswd=nil, quota=nil)
237
+ msg = RequestMessage.new
238
+ msg.about_login(username,password,passwd_hash_function,admin,suspended, changepasswd)
239
+ msg.about_name(family_name, given_name)
240
+ msg.about_quota(quota) if quota
241
+ msg.add_path('https://'+@@google_host+@action[:user_update][:path]+username)
242
+ response = request(:user_update,username,@headers, msg.to_s)
243
+ user_entry = UserEntry.new(response.elements["entry"])
244
+ end
245
+
246
+ # Renames a user, returns a UserEntry instance
247
+ # ex :
248
+ #
249
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
250
+ # user = myapps.rename_user('jsmith','jdoe')
251
+ #
252
+ # It is recommended to log out rhe user from all browser sessions and service before renaming.
253
+ # Once renamed, the old username becomes a nickname of the new username.
254
+ # Note from Google: Google Talk will lose all remembered chat invitations after renaming.
255
+ # The user must request permission to chat with friends again.
256
+ # Also, when a user is renamed, the old username is retained as a nickname to ensure continuous mail delivery in the case of email forwarding settings.
257
+ # To remove the nickname, you should issue an HTTP DELETE to the nicknames feed after renaming.
258
+ def rename_user(username, new_username)
259
+ msg = RequestMessage.new
260
+ msg.about_login(new_username)
261
+ msg.add_path('https://'+@@google_host+@action[:user_rename][:path]+username)
262
+ response = request(:user_update,username,@headers, msg.to_s)
263
+ end
264
+
265
+ # Suspends an account in your domain, returns a UserEntry instance
266
+ # ex :
267
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
268
+ # user = myapps.suspend('jsmith')
269
+ # puts user.suspended => "true"
270
+ def suspend_user(username)
271
+ msg = RequestMessage.new
272
+ msg.about_login(username,nil,nil,nil,"true")
273
+ msg.add_path('https://'+@@google_host+@action[:user_update][:path]+username)
274
+ response = request(:user_update,username,@headers, msg.to_s)
275
+ user_entry = UserEntry.new(response.elements["entry"])
276
+ end
277
+
278
+ # Restores a suspended account in your domain, returns a UserEntry instance
279
+ # ex :
280
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
281
+ # user = myapps.restore('jsmith')
282
+ # puts user.suspended => "false"
283
+ def restore_user(username)
284
+ msg = RequestMessage.new
285
+ msg.about_login(username,nil,nil,nil,"false")
286
+ msg.add_path('https://'+@@google_host+@action[:user_update][:path]+username)
287
+ response = request(:user_update,username,@headers, msg.to_s)
288
+ user_entry = UserEntry.new(response.elements["entry"])
289
+ end
290
+
291
+ # Deletes an account in your domain
292
+ # ex :
293
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
294
+ # myapps.delete('jsmith')
295
+ def delete_user(username)
296
+ response = request(:user_delete,username,@headers)
297
+ end
298
+
299
+ # Returns a NicknameEntry instance from a nickname
300
+ # ex :
301
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
302
+ # nickname = myapps.retrieve_nickname('jsmith')
303
+ # puts "login : "+nickname.login
304
+ def retrieve_nickname(nickname)
305
+ xml_response = request(:nickname_retrieve, nickname, @headers)
306
+ nickname_entry = NicknameEntry.new(xml_response.elements["entry"])
307
+ end
308
+
309
+ # Returns a NicknameEntry array from a username
310
+ # ex : lists jsmith's nicknames
311
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
312
+ # mynicks = myapps.retrieve('jsmith')
313
+ # mynicks.each {|nick| puts nick.nickname }
314
+ def retrieve_nicknames(username)
315
+ xml_response = request(:nickname_retrieve_all_for_user, username, @headers)
316
+ nicknames_feed = Feed.new(xml_response.elements["feed"], NicknameEntry)
317
+ nicknames_feed = add_next_feeds(nicknames_feed, xml_response, NicknameEntry)
318
+ end
319
+
320
+ # Returns a NicknameEntry array for the whole domain. May take a while depending on the number of users in your domain.
321
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
322
+ # allnicks = myapps.retrieve_all_nicknames
323
+ # allnicks.each {|nick| puts nick.nickname }
324
+ def retrieve_all_nicknames
325
+ xml_response = request(:nickname_retrieve_all_in_domain, nil, @headers)
326
+ nicknames_feed = Feed.new(xml_response.elements["feed"], NicknameEntry)
327
+ nicknames_feed = add_next_feeds(nicknames_feed, xml_response, NicknameEntry)
328
+ end
329
+
330
+ # Creates a nickname for the username in your domain and returns a NicknameEntry instance
331
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
332
+ # mynewnick = myapps.create_nickname('jsmith', 'john.smith')
333
+ def create_nickname(username,nickname)
334
+ msg = RequestMessage.new
335
+ msg.about_login(username)
336
+ msg.about_nickname(nickname)
337
+ response = request(:nickname_create,nil,@headers, msg.to_s)
338
+ nickname_entry = NicknameEntry.new(response.elements["entry"])
339
+ end
340
+
341
+ # Deletes the nickname in your domain
342
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
343
+ # myapps.delete_nickname('john.smith')
344
+ def delete_nickname(nickname)
345
+ response = request(:nickname_delete,nickname,@headers)
346
+ end
347
+
348
+ # Returns a NicknameEntry array populated with 100 nicknames, starting from a nickname
349
+ # ex :
350
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
351
+ # list= myapps.retrieve_page_of_nicknames("joe")
352
+ # list.each{ |nick| puts nick.login}
353
+ def retrieve_page_of_nicknames(start_nickname)
354
+ param='?startNickname='+start_nickname
355
+ xml_response = request(:nickname_retrieve_all_in_domain, param, @headers)
356
+ nicknames_feed = Feed.new(xml_response.elements["feed"], NicknameEntry)
357
+ end
358
+
359
+ # Deprecated. Please use Group management instead.
360
+ def retrieve_email_lists(email_adress)
361
+ puts("retrieve_email_lists : deprecated. Please use Group management instead.")
362
+ end
363
+
364
+ # Deprecated. Please use Group management instead.
365
+ def retrieve_all_email_lists
366
+ puts("retrieve_all_email_lists : deprecated. Please use Group management instead.")
367
+ end
368
+
369
+ # Deprecated. Please use Group management instead.
370
+ def retrieve_page_of_email_lists(start_listname)
371
+ puts("retrieve_page_of_email_lists : deprecated. Please use Group management instead.")
372
+ end
373
+
374
+ # Deprecated. Please use Group management instead.
375
+ def create_email_list(name)
376
+ puts("create_email_list : deprecated. Please use Group management instead.")
377
+ end
378
+
379
+ # Deprecated. Please use Group management instead.
380
+ def delete_email_list(name)
381
+ puts("delete_email_list : deprecated. Please use Group management instead.")
382
+ end
383
+
384
+ # Deprecated. Please use Group management instead.
385
+ def retrieve_all_recipients(email_list)
386
+ puts("retrieve_all_recipients : deprecated. Please use Group management instead.")
387
+ end
388
+
389
+ # Deprecated. Please use Group management instead.
390
+ def retrieve_page_of_recipients(email_list, start_recipient)
391
+ puts("Deprecated. Please use Group management instead.")
392
+ end
393
+
394
+ # Deprecated. Please use Group management instead.
395
+ def add_address_to_email_list(email_list,address)
396
+ puts("add_address_to_email_list : deprecated. Please use Group management instead.")
397
+ end
398
+
399
+ # Deprecated. Please use Group management instead.
400
+ def remove_address_from_email_list(address,email_list)
401
+ puts("remove_address_from_email_list : deprecated. Please use Group management instead.")
402
+ end
403
+
404
+ # Creates a group in your domain and returns a GroupEntry (ATTENTION: the group name is necessary!).
405
+ # ex :
406
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
407
+ # group= myapps.create_group("mygroup", ["My Group name", "My Group description", "<emailPermission>"])
408
+ def create_group(group_id, properties)
409
+ msg = RequestMessage.new
410
+ msg.about_group(group_id, properties)
411
+ response = request(:group_create, nil, @headers, msg.to_s)
412
+ group_entry = GroupEntry.new(response.elements["entry"])
413
+ end
414
+
415
+ # Updates a group in your domain and returns a GroupEntry (ATTENTION: the group name is necessary!).
416
+ # ex :
417
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
418
+ # group= myapps.update_group("mygroup", ["My Group name", "My Group description", "<emailPermission>"])
419
+ def update_group(group_id, properties)
420
+ msg = RequestMessage.new
421
+ msg.about_group(group_id, properties)
422
+ response = request(:group_update, group_id, @headers, msg.to_s)
423
+ group_entry = GroupEntry.new(response.elements["entry"])
424
+ end
425
+
426
+ # Deletes a group in your domain.
427
+ # ex :
428
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
429
+ # myapps.delete_group("mygroup")
430
+ def delete_group(group_id)
431
+ response = request(:group_delete,group_id,@headers)
432
+ end
433
+
434
+ # Returns a GroupEntry array for a particular member of the domain (ATTENTION: it doesn't work for members of other domains!).
435
+ # The user parameter can be a complete email address or can be written without "@mydomain.com".
436
+ # ex :
437
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
438
+ # mylists = myapps.retrieve_groups('jsmith') # you can search for 'jsmith@mydomain.com' too
439
+ # mylists.each {|list| puts list.group_id }
440
+ def retrieve_groups(user)
441
+ xml_response = request(:groups_retrieve, user, @headers)
442
+ list_feed = Feed.new(xml_response.elements["feed"], GroupEntry)
443
+ list_feed = add_next_feeds(list_feed, xml_response, GroupEntry)
444
+ end
445
+
446
+ # Returns a GroupEntry array for the whole domain.
447
+ # ex :
448
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
449
+ # all_lists = myapps.retrieve_all_groups
450
+ # all_lists.each {|list| puts list.group_id }
451
+ def retrieve_all_groups
452
+ xml_response = request(:all_groups_retrieve, nil, @headers)
453
+ list_feed = Feed.new(xml_response.elements["feed"], GroupEntry)
454
+ list_feed = add_next_feeds(list_feed, xml_response, GroupEntry)
455
+ end
456
+
457
+ # Adds an email address (user or group) to a mailing list in your domain and returns a MemberEntry instance.
458
+ # You can add addresses from other domains to your mailing list. Omit "@mydomain.com" in the group name.
459
+ # ex :
460
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
461
+ # new_member = myapps.add_member_to_group('example@otherdomain.com', 'mygroup')
462
+ def add_member_to_group(email_address, group_id)
463
+ msg = RequestMessage.new
464
+ msg.about_member(email_address)
465
+ response = request(:membership_add, group_id+'/member', @headers, msg.to_s)
466
+ member_entry = MemberEntry.new(response.elements["entry"])
467
+ end
468
+
469
+ # Removes an email address (user or group) from a mailing list. Omit "@mydomain.com" in the group name.
470
+ # ex :
471
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
472
+ # myapps.remove_member_from_group('example@otherdomain.com', 'mygroup')
473
+ def remove_member_from_group(email_address, group_id)
474
+ response = request(:membership_remove, group_id+'/member/'+email_address,@headers)
475
+ end
476
+
477
+ # Returns true if the email address (user or group) is member of the group, false otherwise.
478
+ # ex :
479
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
480
+ # boolean = myapps.is_member('example@otherdomain.com', 'mylist')
481
+ def is_member(email_address, group_id)
482
+ xml_response = request(:membership_confirm, group_id+'/member/'+email_address, @headers)
483
+ # if the email_address is not member of the group, an error is raised, otherwise true is returned
484
+ return true
485
+
486
+ rescue GDataError => e
487
+ return false if e.reason.eql?("EntityDoesNotExist")
488
+ end
489
+
490
+ # Returns a MemberEntry array with the members of a group.
491
+ # ex :
492
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
493
+ # list = myapps.retrieve_all_members('mygroup')
494
+ # lists.each {|list| puts list.member_id }
495
+ def retrieve_all_members(group_id)
496
+ xml_response = request(:all_members_retrieve, group_id+'/member', @headers)
497
+ list_feed = Feed.new(xml_response.elements["feed"], MemberEntry)
498
+ list_feed = add_next_feeds(list_feed, xml_response, MemberEntry)
499
+ end
500
+
501
+ # Adds a owner (user or group) to a mailing list in your domain and returns a OwnerEntry instance.
502
+ # You can add addresses from other domains to your mailing list. Omit "@mydomain.com" in the group name.
503
+ # ATTENTION: a owner is added only if it's already member of the group, otherwise no action is done!
504
+ # ex :
505
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
506
+ # new_member = myapps.add_owner_to_group('example@otherdomain.com', 'mygroup')
507
+ def add_owner_to_group(email_address, group_id)
508
+ msg = RequestMessage.new
509
+ msg.about_owner(email_address)
510
+ response = request(:ownership_add, group_id+'/owner', @headers, msg.to_s)
511
+ owner_entry = OwnerEntry.new(response.elements["entry"])
512
+ end
513
+
514
+ # Removes an owner from a mailing list. Omit "@mydomain.com" in the group name.
515
+ # ATTENTION: when a owner is removed, it loses the privileges but still remains member of the group!
516
+ # ex :
517
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
518
+ # myapps.remove_owner_from_group('example@otherdomain.com', 'mygroup')
519
+ def remove_owner_from_group(email_address, group_id)
520
+ response = request(:ownership_remove, group_id+'/owner/'+email_address,@headers)
521
+ end
522
+
523
+ # Returns true if the email address (user or group) is owner of the group, false otherwise.
524
+ # ex :
525
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
526
+ # boolean = myapps.is_owner('example@otherdomain.com', 'mylist')
527
+ def is_owner(email_address, group_id)
528
+ xml_response = request(:ownership_confirm, group_id+'/owner/'+email_address, @headers)
529
+ # if the email_address is not member of the group, an error is raised, otherwise true is returned
530
+ return true
531
+
532
+ rescue GDataError => e
533
+ return false if e.reason.eql?("EntityDoesNotExist")
534
+ end
535
+
536
+ # Returns a OwnerEntry array with the owners of a group.
537
+ # ex :
538
+ # myapps = ProvisioningApi.new('root@mydomain.com','PaSsWoRd')
539
+ # list = myapps.retrieve_all_owners('mygroup')
540
+ # lists.each {|list| puts list.owner_id }
541
+ def retrieve_all_owners(group_id)
542
+ xml_response = request(:all_owners_retrieve, group_id+'/owner', @headers)
543
+ list_feed = Feed.new(xml_response.elements["feed"], OwnerEntry)
544
+ list_feed = add_next_feeds(list_feed, xml_response, OwnerEntry)
545
+ end
546
+
547
+ # Aliases
548
+ alias createUser create_user
549
+ alias retrieveUser retrieve_user
550
+ alias retrieveAllUsers retrieve_all_users
551
+ alias retrievePageOfUsers retrieve_page_of_users
552
+ alias updateUser update_user
553
+ alias renameUser rename_user
554
+ alias suspendUser suspend_user
555
+ alias restoreUser restore_user
556
+ alias deleteUser delete_user
557
+ alias createNickname create_nickname
558
+ alias retrieveNickname retrieve_nickname
559
+ alias retrieveNicknames retrieve_nicknames
560
+ alias retrieveAllNicknames retrieve_all_nicknames
561
+ alias retrievePageOfNicknames retrieve_page_of_nicknames
562
+ alias deleteNickname delete_nickname
563
+ alias retrieveAllRecipients retrieve_all_recipients
564
+ alias retrievePageOfRecipients retrieve_page_of_recipients
565
+ alias removeRecipientFromEmailList remove_address_from_email_list
566
+ alias createGroup create_group
567
+ alias updateGroup update_group
568
+ alias deleteGroup delete_group
569
+ alias retrieveGroups retrieve_groups
570
+ alias retrieveAllGroups retrieve_all_groups
571
+ alias addMemberToGroup add_member_to_group
572
+ alias removeMemberFromGroup remove_member_from_group
573
+ alias isMember is_member
574
+ alias retrieveAllMembers retrieve_all_members
575
+ alias addOwnerToGroup add_owner_to_group
576
+ alias removeOwnerFromGroup remove_owner_from_group
577
+ alias isOwner is_owner
578
+ alias retrieveAllOwners retrieve_all_owners
579
+
580
+
581
+ # private methods
582
+ private #:nodoc:
583
+
584
+ # Associates methods, http verbs and URL for REST access
585
+ def setup_actions(domain)
586
+ path_user = '/a/feeds/'+domain+'/user/2.0'
587
+ path_nickname = '/a/feeds/'+domain+'/nickname/2.0'
588
+ path_group = '/a/feeds/group/2.0/'+domain # path for Google groups
589
+
590
+ action = Hash.new
591
+ action[:domain_login] = {:method => 'POST', :path => '/accounts/ClientLogin' }
592
+ action[:user_create] = { :method => 'POST', :path => path_user }
593
+ action[:user_retrieve] = { :method => 'GET', :path => path_user+'/' }
594
+ action[:user_retrieve_all] = { :method => 'GET', :path => path_user }
595
+ action[:user_update] = { :method => 'PUT', :path => path_user +'/' }
596
+ action[:user_rename] = { :method => 'PUT', :path => path_user +'/' }
597
+ action[:user_delete] = { :method => 'DELETE', :path => path_user +'/' }
598
+ action[:nickname_create] = { :method => 'POST', :path =>path_nickname }
599
+ action[:nickname_retrieve] = { :method => 'GET', :path =>path_nickname+'/' }
600
+ action[:nickname_retrieve_all_for_user] = { :method => 'GET', :path =>path_nickname+'?username=' }
601
+ action[:nickname_retrieve_all_in_domain] = { :method => 'GET', :path =>path_nickname }
602
+ action[:nickname_delete] = { :method => 'DELETE', :path =>path_nickname+'/' }
603
+ action[:group_create] = { :method => 'POST', :path =>path_group }
604
+ action[:group_update] = { :method => 'PUT', :path =>path_group+'/' }
605
+ action[:group_delete] = { :method => 'DELETE', :path =>path_group+'/' }
606
+ action[:groups_retrieve] = { :method => 'GET', :path =>path_group+'?member=' }
607
+ action[:all_groups_retrieve] = { :method => 'GET', :path =>path_group }
608
+ action[:membership_add] = { :method => 'POST', :path =>path_group+'/' }
609
+ action[:membership_remove] = { :method => 'DELETE', :path =>path_group+'/' }
610
+ action[:membership_confirm] = { :method => 'GET', :path =>path_group+'/' }
611
+ action[:all_members_retrieve] = { :method => 'GET', :path =>path_group+'/' }
612
+ action[:ownership_add] = { :method => 'POST', :path =>path_group+'/' }
613
+ action[:ownership_remove] = { :method => 'DELETE', :path =>path_group+'/' }
614
+ action[:ownership_confirm] = { :method => 'GET', :path =>path_group+'/' }
615
+ action[:all_owners_retrieve] = { :method => 'GET', :path =>path_group+'/' }
616
+
617
+ # special action "next" for linked feed results. :path will be affected with URL received in a link tag.
618
+ action[:next] = {:method => 'GET', :path =>nil }
619
+ return action
620
+ end
621
+
622
+ # Sends credentials and returns an authentication token
623
+ def login(mail, passwd)
624
+ request_body = '&Email='+CGI.escape(mail)+'&Passwd='+CGI.escape(passwd)+'&accountType=HOSTED&service=apps'
625
+ res = request(:domain_login, nil, {'Content-Type'=>'application/x-www-form-urlencoded'}, request_body)
626
+ return /^Auth=(.+)$/.match(res.to_s)[1]
627
+ # res.to_s needed, because res.class is REXML::Document
628
+ end
629
+
630
+
631
+ # Completes the feed by following et requesting the URL links
632
+ def add_next_feeds(current_feed, xml_content, element_class)
633
+ xml_content.elements.each("feed/link") {|link|
634
+ if link.attributes["rel"] == "next"
635
+ @action[:next] = {:method => 'GET', :path=> link.attributes["href"]}
636
+ next_response = request(:next,nil,@headers)
637
+ current_feed.concat(Feed.new(next_response.elements["feed"], element_class))
638
+ current_feed = add_next_feeds(current_feed, next_response, element_class)
639
+ end
640
+ }
641
+ return current_feed
642
+ end
643
+
644
+ # Perfoms a REST request based on the action hash (cf setup_actions)
645
+ # ex : request (:user_retrieve, 'jsmith') sends a http GET www.google.com/a/feeds/domain/user/2.0/jsmith
646
+ # returns REXML Document
647
+ def request(action, value=nil, header=nil, message=nil)
648
+ #param value : value to be concatenated to action path ex: GET host/path/value
649
+ method = @action[action][:method]
650
+ value = '' if !value
651
+ path = @action[action][:path]+value
652
+ response = @connection.perform(method, path, message, header)
653
+ response_xml = Document.new(response.body)
654
+ test_errors(response_xml)
655
+ return response_xml
656
+ end
657
+
658
+ # parses xml response for an API error tag. If an error, constructs and raises a GDataError.
659
+ def test_errors(xml)
660
+ error = xml.elements["AppsForYourDomainErrors/error"]
661
+ if error
662
+ gdata_error = GDataError.new
663
+ gdata_error.code = error.attributes["errorCode"]
664
+ gdata_error.input = error.attributes["invalidInput"]
665
+ gdata_error.reason = error.attributes["reason"]
666
+ msg = "error code : "+gdata_error.code+", invalid input : "+gdata_error.input+", reason : "+gdata_error.reason
667
+ raise gdata_error, msg
668
+ end
669
+ #in case the domain is not configured to use google apps
670
+ error = xml.elements['HTML']
671
+ if(error)
672
+ gdata_error = GDataError.new
673
+ msg = /.*<TITLE>(.*)<\/TITLE>.*/.match(error.to_s)
674
+ raise gdata_error, msg[1]
675
+ end
676
+
677
+ end
678
+ end
679
+
680
+
681
+ # UserEntry object.
682
+ #
683
+ # Handles API responses relative to a user
684
+ #
685
+ # Attributes :
686
+ # username : string
687
+ # given_name : string
688
+ # family_name : string
689
+ # suspended : string "true" or string "false"
690
+ # ip_whitelisted : string "true" or string "false"
691
+ # admin : string "true" or string "false"
692
+ # change_password_at_next_login : string "true" or string "false"
693
+ # agreed_to_terms : string "true" or string "false"
694
+ # quota_limit : string (value in MB)
695
+ class UserEntry
696
+ attr_reader :given_name, :family_name, :username, :suspended, :ip_whitelisted, :admin, :change_password_at_next_login, :agreed_to_terms, :quota_limit
697
+
698
+ # UserEntry constructor. Needs a REXML::Element <entry> as parameter
699
+ def initialize(entry) #:nodoc:
700
+ @family_name = entry.elements["apps:name"].attributes["familyName"]
701
+ @given_name = entry.elements["apps:name"].attributes["givenName"]
702
+ @username = entry.elements["apps:login"].attributes["userName"]
703
+ @suspended = entry.elements["apps:login"].attributes["suspended"]
704
+ @ip_whitelisted = entry.elements["apps:login"].attributes["ipWhitelisted"]
705
+ @admin = entry.elements["apps:login"].attributes["admin"]
706
+ @change_password_at_next_login = entry.elements["apps:login"].attributes["changePasswordAtNextLogin"]
707
+ @agreed_to_terms = entry.elements["apps:login"].attributes["agreedToTerms"]
708
+ @quota_limit = entry.elements["apps:quota"].attributes["limit"]
709
+ end
710
+ end
711
+
712
+
713
+ # NicknameEntry object.
714
+ #
715
+ # Handles API responses relative to a nickname
716
+ #
717
+ # Attributes :
718
+ # login : string
719
+ # nickname : string
720
+ class NicknameEntry
721
+ attr_reader :login, :nickname
722
+
723
+ # NicknameEntry constructor. Needs a REXML::Element <entry> as parameter
724
+ def initialize(entry) #:nodoc:
725
+ @login = entry.elements["apps:login"].attributes["userName"]
726
+ @nickname = entry.elements["apps:nickname"].attributes["name"]
727
+ end
728
+ end
729
+
730
+
731
+ # UserFeed object : Array populated with Element_class objects (UserEntry, NicknameEntry, EmailListEntry or EmailListRecipientEntry)
732
+ class Feed < Array #:nodoc:
733
+
734
+ # UserFeed constructor. Populates an array with Element_class objects. Each object is an xml <entry> parsed from the REXML::Element <feed>.
735
+ # Ex : user_feed = Feed.new(xml_feed, UserEntry)
736
+ # nickname_feed = Feed.new(xml_feed, NicknameEntry)
737
+ def initialize(xml_feed, element_class)
738
+ xml_feed.elements.each("entry"){ |entry| self << element_class.new(entry) }
739
+ end
740
+ end
741
+
742
+
743
+
744
+ # GroupEntry object.
745
+ #
746
+ # Handles API responses relative to a group.
747
+ #
748
+ # Attributes :
749
+ # group_id : string . The group_id is written without "@" and everything following.
750
+ class GroupEntry
751
+ attr_reader :group_id
752
+
753
+ # GroupEntry constructor. Needs a REXML::Element <entry> as parameter
754
+ def initialize(entry) #:nodoc:
755
+ entry.elements.each("apps:property"){ |e| @group_id = e.attributes["value"] if e.attributes["name"].eql?("groupId") }
756
+ end
757
+ end
758
+
759
+
760
+ # MemberEntry object.
761
+ #
762
+ # Handles API responses relative to a meber of a group.
763
+ #
764
+ # Attributes :
765
+ # member_id : string . The member_id is a complete email address.
766
+ class MemberEntry
767
+ attr_reader :member_id
768
+
769
+ # MemberEntry constructor. Needs a REXML::Element <entry> as parameter
770
+ def initialize(entry) #:nodoc:
771
+ entry.elements.each("apps:property"){ |e| @member_id = e.attributes["value"] if e.attributes["name"].eql?("memberId") }
772
+ end
773
+ end
774
+
775
+
776
+ # OwnerEntry object.
777
+ #
778
+ # Handles API responses relative to a owner of a group.
779
+ #
780
+ # Attributes :
781
+ # owner_id : string . The owner_id is a complete email address.
782
+ class OwnerEntry
783
+ attr_reader :owner_id
784
+
785
+ # OwnerEntry constructor. Needs a REXML::Element <entry> as parameter
786
+ def initialize(entry) #:nodoc:
787
+ entry.elements.each("apps:property"){ |e| @owner_id = e.attributes["value"] if e.attributes["name"].eql?("email") }
788
+ end
789
+ end
790
+
791
+
792
+ class RequestMessage < Document #:nodoc:
793
+ # Request message constructor.
794
+ # parameter type : "user", "nickname" or "emailList"
795
+
796
+ # creates the object and initiates the construction
797
+ def initialize
798
+ super '<?xml version="1.0" encoding="UTF-8"?>'
799
+ self.add_element "atom:entry", {"xmlns:apps" => "http://schemas.google.com/apps/2006",
800
+ "xmlns:gd" => "http://schemas.google.com/g/2005",
801
+ "xmlns:atom" => "http://www.w3.org/2005/Atom"}
802
+ self.elements["atom:entry"].add_element "atom:category", {"scheme" => "http://schemas.google.com/g/2005#kind"}
803
+ end
804
+
805
+ # adds <atom:id> element in the message body. Url is inserted as a text.
806
+ def add_path(url)
807
+ self.elements["atom:entry"].add_element "atom:id"
808
+ self.elements["atom:entry/atom:id"].text = url
809
+ end
810
+
811
+ # adds <apps:emailList> element in the message body.
812
+ def about_email_list(email_list)
813
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#emailList")
814
+ self.elements["atom:entry"].add_element "apps:emailList", {"name" => email_list }
815
+ end
816
+
817
+ # adds <apps:property> element in the message body for a group.
818
+ def about_group(group_id, properties)
819
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#emailList")
820
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "groupId", "value" => group_id }
821
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "groupName", "value" => properties[0] }
822
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "description", "value" => properties[1] }
823
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "emailPermission", "value" => properties[2] }
824
+ end
825
+
826
+ # adds <apps:property> element in the message body for a member.
827
+ def about_member(email_address)
828
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
829
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "memberId", "value" => email_address }
830
+ end
831
+
832
+ # adds <apps:property> element in the message body for an owner.
833
+ def about_owner(email_address)
834
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
835
+ self.elements["atom:entry"].add_element "apps:property", {"name" => "email", "value" => email_address }
836
+ end
837
+
838
+
839
+ # adds <apps:login> element in the message body.
840
+ # warning : if valued admin, suspended, or change_passwd_at_next_login must be the STRINGS "true" or "false", not the boolean true or false
841
+ # when needed to construct the message, should always been used before other "about_" methods so that the category tag can be overwritten
842
+ # only values permitted for hash_function_function_name : "SHA-1", "MD5" or nil
843
+ def about_login(user_name, passwd=nil, hash_function_name=nil, admin=nil, suspended=nil, change_passwd_at_next_login=nil)
844
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#user")
845
+ self.elements["atom:entry"].add_element "apps:login", {"userName" => user_name }
846
+ self.elements["atom:entry/apps:login"].add_attribute("password", passwd) if not passwd.nil?
847
+ self.elements["atom:entry/apps:login"].add_attribute("hashFunctionName", hash_function_name) if not hash_function_name.nil?
848
+ self.elements["atom:entry/apps:login"].add_attribute("admin", admin) if not admin.nil?
849
+ self.elements["atom:entry/apps:login"].add_attribute("suspended", suspended) if not suspended.nil?
850
+ self.elements["atom:entry/apps:login"].add_attribute("changePasswordAtNextLogin", change_passwd_at_next_login) if not change_passwd_at_next_login.nil?
851
+ return self
852
+ end
853
+
854
+ # adds <apps:quota> in the message body.
855
+ # limit in MB: integer
856
+ def about_quota(limit)
857
+ self.elements["atom:entry"].add_element "apps:quota", {"limit" => limit }
858
+ return self
859
+ end
860
+
861
+ # adds <apps:name> in the message body.
862
+ def about_name(family_name, given_name)
863
+ self.elements["atom:entry"].add_element "apps:name", {"familyName" => family_name, "givenName" => given_name }
864
+ return self
865
+ end
866
+
867
+ # adds <apps:nickname> in the message body.
868
+ def about_nickname(name)
869
+ self.elements["atom:entry/atom:category"].add_attribute("term", "http://schemas.google.com/apps/2006#nickname")
870
+ self.elements["atom:entry"].add_element "apps:nickname", {"name" => name}
871
+ return self
872
+ end
873
+
874
+ # adds <gd:who> in the message body.
875
+ def about_who(email)
876
+ self.elements["atom:entry"].add_element "gd:who", {"email" => email }
877
+ return self
878
+ end
879
+
880
+ end
881
+
882
+ end