omnicontacts 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 153f2fff78dd7440fe1c653a7458f47db2b8717a
4
+ data.tar.gz: f115709601214db1e8826893355146514c5c8b82
5
+ SHA512:
6
+ metadata.gz: 29f390858859e150129a1a01d3fbbe6d523b997ece36e113e6c8635d5c0ecb2cc47f77e2686c07503703a4a0148dd543842cebfa5d285f0923be01e5f5d4454c
7
+ data.tar.gz: 7850d3c9bd1f6309f0dd1065262c6b16f7aa947ba3f33deb186ba8bff3a9809e5baeb1e588b6cc817c7b159650f3692c44c522df1d062f62a37fffbd6a97b90a
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  coverage
2
2
  *.iml
3
3
  .idea
4
+ .rvmrc
4
5
  .DS_Store
5
6
  nbproject
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
@@ -1,32 +1,38 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- omnicontacts (0.3.4)
4
+ omnicontacts (0.3.5)
5
5
  json
6
6
  rack
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- diff-lcs (1.1.3)
12
- json (1.7.7)
13
- multi_json (1.1.0)
14
- rack (1.4.1)
15
- rack-test (0.6.1)
11
+ diff-lcs (1.2.5)
12
+ docile (1.1.5)
13
+ json (1.8.1)
14
+ multi_json (1.10.1)
15
+ rack (1.5.2)
16
+ rack-test (0.6.2)
16
17
  rack (>= 1.0)
17
- rake (0.9.2.2)
18
- rspec (2.8.0)
19
- rspec-core (~> 2.8.0)
20
- rspec-expectations (~> 2.8.0)
21
- rspec-mocks (~> 2.8.0)
22
- rspec-core (2.8.0)
23
- rspec-expectations (2.8.0)
24
- diff-lcs (~> 1.1.2)
25
- rspec-mocks (2.8.0)
26
- simplecov (0.6.1)
18
+ rake (10.3.2)
19
+ rspec (3.1.0)
20
+ rspec-core (~> 3.1.0)
21
+ rspec-expectations (~> 3.1.0)
22
+ rspec-mocks (~> 3.1.0)
23
+ rspec-core (3.1.7)
24
+ rspec-support (~> 3.1.0)
25
+ rspec-expectations (3.1.2)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.1.0)
28
+ rspec-mocks (3.1.3)
29
+ rspec-support (~> 3.1.0)
30
+ rspec-support (3.1.2)
31
+ simplecov (0.9.1)
32
+ docile (~> 1.1.0)
27
33
  multi_json (~> 1.0)
28
- simplecov-html (~> 0.5.3)
29
- simplecov-html (0.5.3)
34
+ simplecov-html (~> 0.8.0)
35
+ simplecov-html (0.8.0)
30
36
 
31
37
  PLATFORMS
32
38
  ruby
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # OmniContacts
2
2
 
3
- Inspired by the popular OmniAuth, OmniContacts is a library that enables users of an application to import contacts
4
- from their email or Facebook accounts. The email providers currently supported are Gmail, Yahoo and Hotmail.
3
+ Inspired by the popular OmniAuth, OmniContacts is a library that enables users of an application to import contacts
4
+ from their email or Facebook accounts. The email providers currently supported are Gmail, Yahoo and Hotmail.
5
5
  OmniContacts is a Rack middleware, therefore you can use it with Rails, Sinatra and any other Rack-based framework.
6
6
 
7
7
  OmniContacts uses the OAuth protocol to communicate with the contacts provider. Yahoo still uses OAuth 1.0, while
8
- Facebook, Gmail and Hotmail support OAuth 2.0.
8
+ Facebook, Gmail and Hotmail support OAuth 2.0.
9
9
  In order to use OmniContacts, it is therefore necessary to first register your application with the provider and to obtain client_id and client_secret.
10
10
 
11
11
  ## Usage
@@ -31,14 +31,27 @@ end
31
31
 
32
32
  ```
33
33
 
34
- Every importer expects `client_id` and `client_secret` as mandatory, while `:redirect_path` and `:ssl_ca_file` are optional.
35
- Since Yahoo implements the version 1.0 of the OAuth protocol, naming is slightly different. Instead of `:redirect_path` you should use `:callback_path` as key in the hash providing the optional parameters.
34
+ Every importer expects `client_id` and `client_secret` as mandatory, while `:redirect_path` and `:ssl_ca_file` are optional.
35
+ Since Yahoo implements the version 1.0 of the OAuth protocol, naming is slightly different. Instead of `:redirect_path` you should use `:callback_path` as key in the hash providing the optional parameters.
36
36
  While `:ssl_ca_file` is optional, it is highly recommended to set it on production environments for obvious security reasons.
37
37
  On the other hand it makes things much easier to leave the default value for `:redirect_path` and `:callback path`, the reason of which will be clear after reading the following section.
38
38
 
39
+ ## Register your application
40
+
41
+ * For Gmail : [Google API Console](https://code.google.com/apis/console/)
42
+
43
+ * For Yahoo : [Yahoo Developer Network](https://developer.apps.yahoo.com/projects)
44
+
45
+ * For Hotmail : [Microsoft Developer Network](https://account.live.com/developers/applications/index)
46
+
47
+ * For Facebook : [Facebook Developers](https://developers.facebook.com/apps)
48
+
49
+ ##### Note:
50
+ Please go through [MSDN](http://msdn.microsoft.com/en-us/library/cc287659.aspx) if above Hotmail link will not work.
51
+
39
52
  ## Integrating with your Application
40
53
 
41
- To use the Gem you first need to redirect your users to `/contacts/:importer`, where `:importer` can be facebook, gmail, yahoo or hotmail.
54
+ To use the Gem you first need to redirect your users to `/contacts/:importer`, where `:importer` can be facebook, gmail, yahoo or hotmail.
42
55
  No changes to `config/routes.rb` are needed for this step since OmniContacts will be listening on that path and redirect the user to the email provider's website in order to authorize your app to access his contact list.
43
56
  Once that is done the user will be redirected back to your application, to the path specified in `:redirect_path` (or `:callback_path` for yahoo).
44
57
  If nothing is specified the default value is `/contacts/:importer/callback` (e.g. `/contacts/yahoo/callback`). This makes things simpler and you can just add the following line to `config/routes.rb`:
@@ -47,7 +60,7 @@ If nothing is specified the default value is `/contacts/:importer/callback` (e.g
47
60
  match "/contacts/:importer/callback" => "your_controller#callback"
48
61
  ```
49
62
 
50
- The list of contacts can be accessed via the `omnicontacts.contacts` key in the environment hash and it consists of a simple array of hashes.
63
+ The list of contacts can be accessed via the `omnicontacts.contacts` key in the environment hash and it consists of a simple array of hashes.
51
64
  The following table shows which fields are supported by which provider:
52
65
 
53
66
  <table>
@@ -59,6 +72,13 @@ The following table shows which fields are supported by which provider:
59
72
  <th>:name</th>
60
73
  <th>:first_name</th>
61
74
  <th>:last_name</th>
75
+ <th>:address_1</th>
76
+ <th>:address_2</th>
77
+ <th>:city</th>
78
+ <th>:region</th>
79
+ <th>:postcode</th>
80
+ <th>:country</th>
81
+ <th>:phone_number</th>
62
82
  <th>:birthday</th>
63
83
  <th>:gender</th>
64
84
  <th>:relation</th>
@@ -74,15 +94,29 @@ The following table shows which fields are supported by which provider:
74
94
  <td>X</td>
75
95
  <td>X</td>
76
96
  <td>X</td>
97
+ <td>X</td>
98
+ <td>X</td>
99
+ <td>X</td>
100
+ <td>X</td>
101
+ <td>X</td>
102
+ <td>X</td>
103
+ <td>X</td>
77
104
  </tr>
78
105
  <tr>
79
106
  <td>Facebook</td>
107
+ <td></td>
80
108
  <td>X</td>
81
109
  <td>X</td>
82
110
  <td>X</td>
83
111
  <td>X</td>
84
112
  <td>X</td>
85
- <td>X</td>
113
+ <td></td>
114
+ <td></td>
115
+ <td></td>
116
+ <td></td>
117
+ <td></td>
118
+ <td></td>
119
+ <td></td>
86
120
  <td>X</td>
87
121
  <td>X</td>
88
122
  <td>X</td>
@@ -96,6 +130,13 @@ The following table shows which fields are supported by which provider:
96
130
  <td>X</td>
97
131
  <td>X</td>
98
132
  <td>X</td>
133
+ <td>X</td>
134
+ <td>X</td>
135
+ <td>X</td>
136
+ <td>X</td>
137
+ <td></td>
138
+ <td></td>
139
+ <td>X</td>
99
140
  <td></td>
100
141
  <td></td>
101
142
  </tr>
@@ -107,6 +148,13 @@ The following table shows which fields are supported by which provider:
107
148
  <td>X</td>
108
149
  <td>X</td>
109
150
  <td>X</td>
151
+ <td></td>
152
+ <td></td>
153
+ <td></td>
154
+ <td></td>
155
+ <td></td>
156
+ <td></td>
157
+ <td></td>
110
158
  <td>X</td>
111
159
  <td>X</td>
112
160
  <td></td>
@@ -115,11 +163,14 @@ The following table shows which fields are supported by which provider:
115
163
 
116
164
  Obviously it may happen that some fields are blank even if supported by the provider in the case that the contact did not provide any information about them.
117
165
 
118
- The following snippet shows how to simply print name and email of each contact:
166
+ The information for the logged in user can also be accessed via 'omnicontacts.user' key in the environment hash. It consists of a simple hash which includes the same fields as above.
167
+
168
+ The following snippet shows how to simply print name and email of each contact, and also the the name of logged in user:
119
169
  ```ruby
120
170
  def contacts_callback
121
171
  @contacts = request.env['omnicontacts.contacts']
122
- puts "List of contacts obtained from #{params[:importer]}:"
172
+ @user = request.env['omnicontacts.user']
173
+ puts "List of contacts of #{@user[:name]} obtained from #{params[:importer]}:"
123
174
  @contacts.each do |contact|
124
175
  puts "Contact found: name => #{contact[:name]}, email => #{contact[:email]}"
125
176
  end
@@ -142,8 +193,8 @@ importer :gmail, "xxx", "yyy", :max_results => 1000
142
193
 
143
194
  Yahoo requires you to configure the Permissions your application requires. Make sure to go the Yahoo website and to select Read permission for Contacts.
144
195
 
145
- Hotmail presents a "peculiar" feature. Their API returns a Contact object which does not contain an e-mail field!
146
- However, if the contact has either name, family name or both set to null, than there is a field called name which does contain the e-mail address.
196
+ Hotmail presents a "peculiar" feature. Their API returns a Contact object which does not contain an e-mail field!
197
+ However, if the contact has either name, family name or both set to null, than there is a field called name which does contain the e-mail address.
147
198
  This means that it may happen that an Hotmail contact does not contain the email field.
148
199
 
149
200
  ## Integration Testing
@@ -175,25 +226,51 @@ Follows a full example of an integration test:
175
226
 
176
227
  ## Working on localhost
177
228
 
178
- Since Hotmail and Facebook do not allow the usage of `localhost` as redirect path for the authorization step, a workaround is to use the `localtunnel` gem.
179
- This gem is really useful when you need someone, the contacts provider in this case, to access your locally running application using a unique url.
229
+ Since Hotmail and Facebook do not allow the usage of `localhost` as redirect path for the authorization step, a workaround is to use `ngrok`.
230
+ This is really useful when you need someone, the contacts provider in this case, to access your locally running application using a unique url.
231
+
232
+ Install ngrok, download from:
180
233
 
181
- Install the Gem using RubyGems:
234
+ https://ngrok.com/
235
+
236
+ https://github.com/inconshreveable/ngrok
237
+
238
+ Unzip the file
182
239
  ```bash
183
- sudo gem install localtunnel
240
+ unzip /place/this/is/ngrok.zip
184
241
  ```
242
+ Start your application
243
+ ```bash
244
+ $ rails server
185
245
 
186
- Start `localtunnel` passing your public SSH key and the port where your application is running:
246
+ => Booting WEBrick
247
+ => Rails 4.0.4 application starting in development on http://0.0.0.0:3000
248
+ ```
249
+
250
+ In a new terminal window, start the tunnel and pass the port where your application is running:
187
251
  ```bash
188
- localtunnel -k ~/.ssh/id_rsa.pub 3000
252
+ ./ngrok 3000
189
253
  ```
190
254
 
191
- Check the output to see something like
255
+ Check the output to see something like
192
256
  ```bash
193
- Port 3000 is now publicly accessible from http://8bv2.localtunnel.com ...
257
+ ngrok (Ctrl+C to quit)
258
+
259
+ Tunnel Status online
260
+ Version 1.6/1.5
261
+ Forwarding http://274101c1e.ngrok.com -> 127.0.0.1:3000
262
+ Forwarding https://274101c1e.ngrok.com -> 127.0.0.1:3000
263
+ Web Interface 127.0.0.1:4040
264
+ # Conn 0
265
+ Avg Conn Time 0.00ms
194
266
  ```
195
267
 
196
- The printed Url is the one you can now use to access your application.
268
+ This window will show all network transaction that your locally hosted application is processing.
269
+ Ngrok will process all of the requests and responses on your localhost. Visit:
270
+
271
+ ```bash
272
+ http://123456789.ngrok.com # replace 123456789 with your instance
273
+ ```
197
274
 
198
275
  ## Example application
199
276
 
@@ -201,8 +278,8 @@ Thanks to @sonianand11, you can find a full example of a Rails application using
201
278
 
202
279
  ## Thanks
203
280
 
204
- As already mentioned above, a special thanks goes to @sonianand11 for implementing an example app.
205
- Thanks also to @asmatameem for its huge contribution. He indeed added support for Facebook and for many fields which were missing before.
281
+ As already mentioned above, a special thanks goes to @sonianand11 for implementing an example app.
282
+ Thanks also to @asmatameem for her huge contribution. She indeed added support for Facebook and for many fields which were missing before.
206
283
 
207
284
  ## License
208
285
 
@@ -1,6 +1,8 @@
1
1
  module OmniContacts
2
2
 
3
- VERSION = "0.3.4"
3
+ VERSION = "0.3.5"
4
+
5
+ MOUNT_PATH = "/contacts/"
4
6
 
5
7
  autoload :Builder, "omnicontacts/builder"
6
8
  autoload :Importer, "omnicontacts/importer"
@@ -8,7 +10,8 @@ module OmniContacts
8
10
 
9
11
  class AuthorizationError < RuntimeError
10
12
  end
11
-
13
+
14
+
12
15
  def self.integration_test
13
16
  IntegrationTest.instance
14
17
  end
@@ -21,7 +21,7 @@ module OmniContacts
21
21
 
22
22
  def to_query_string map
23
23
  map.collect do |key, value|
24
- key.to_s + "=" + value
24
+ key.to_s + "=" + value.to_s
25
25
  end.join("&")
26
26
  end
27
27
 
@@ -22,22 +22,30 @@ module OmniContacts
22
22
  end
23
23
 
24
24
  def fetch_contacts_using_access_token access_token, access_token_secret
25
- self_response = https_get(@contacts_host, @self_path, :access_token => access_token)
26
- spouse_id = extract_spouse_id(self_response)
25
+ self_response = fetch_current_user access_token
26
+ user = current_user self_response
27
+ set_current_user user
28
+ spouse_id = extract_spouse_id self_response
27
29
  spouse_response = nil
28
30
  if spouse_id
29
31
  spouse_path = "/#{spouse_id}"
30
32
  spouse_response = https_get(@contacts_host, spouse_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture'})
31
33
  end
32
- family_response = https_get(@contacts_host, @family_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture'})
34
+ family_response = https_get(@contacts_host, @family_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture,relationship'})
33
35
  friends_response = https_get(@contacts_host, @friends_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture'})
34
36
  contacts_from_response(spouse_response, family_response, friends_response)
35
37
  end
36
38
 
39
+ def fetch_current_user access_token
40
+ self_response = https_get(@contacts_host, @self_path, {:access_token => access_token, :fields => 'first_name,last_name,name,id,gender,birthday,picture,relationship_status,significant_other,email'})
41
+ self_response = JSON.parse(self_response) if self_response
42
+ self_response
43
+ end
44
+
37
45
  private
38
46
 
39
- def extract_spouse_id self_response
40
- response = JSON.parse(self_response)
47
+ def extract_spouse_id response
48
+ return nil if response.nil?
41
49
  id = nil
42
50
  if response['significant_other'] && response['relationship_status'] == 'Married'
43
51
  id = response['significant_other']['id']
@@ -79,17 +87,36 @@ module OmniContacts
79
87
  contact[:name] = contact_info['name']
80
88
  contact[:email] = contact_info['email']
81
89
  contact[:gender] = contact_info['gender']
82
- birthday = contact_info['birthday'].split('/') if contact_info['birthday']
83
- contact[:birthday] = birthday_format(birthday[0],birthday[1],birthday[2]) if birthday
84
- contact[:profile_picture] = contact_info['picture']['data']['url'] if contact_info['picture']
90
+ contact[:birthday] = birthday(contact_info['birthday'])
91
+ contact[:profile_picture] = image_url(contact_info['id'])
85
92
  contact[:relation] = contact_info['relationship']
86
93
  contact
87
94
  end
88
95
 
96
+ def image_url fb_id
97
+ return "https://graph.facebook.com/" + fb_id + "/picture" if fb_id
98
+ end
99
+
89
100
  def escape_windows_format value
90
101
  value.gsub(/[\r\s]/, '')
91
102
  end
92
103
 
104
+ def birthday dob
105
+ return nil if dob.nil?
106
+ birthday = dob.split('/')
107
+ return birthday_format(birthday[0],birthday[1],birthday[2])
108
+ end
109
+
110
+ def current_user me
111
+ return nil if me.nil?
112
+ user = {:id => me['id'], :email => me['email'],
113
+ :name => me['name'], :first_name => normalize_name(me['first_name']),
114
+ :last_name => normalize_name(me['last_name']), :birthday => birthday(me['birthday']),
115
+ :gender => me['gender'], :profile_picture => image_url(me['id'])
116
+ }
117
+ user
118
+ end
119
+
93
120
  end
94
121
  end
95
122
  end
@@ -13,17 +13,26 @@ module OmniContacts
13
13
  @auth_host = "accounts.google.com"
14
14
  @authorize_path = "/o/oauth2/auth"
15
15
  @auth_token_path = "/o/oauth2/token"
16
- @scope = "https://www.google.com/m8/feeds"
16
+ @scope = "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
17
17
  @contacts_host = "www.google.com"
18
18
  @contacts_path = "/m8/feeds/contacts/default/full"
19
19
  @max_results = (args[3] && args[3][:max_results]) || 100
20
+ @self_host = "www.googleapis.com"
21
+ @profile_path = "/oauth2/v1/userinfo"
20
22
  end
21
23
 
22
24
  def fetch_contacts_using_access_token access_token, token_type
25
+ fetch_current_user(access_token, token_type)
23
26
  contacts_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))
24
27
  contacts_from_response contacts_response
25
28
  end
26
29
 
30
+ def fetch_current_user access_token, token_type
31
+ self_response = https_get(@self_host, @profile_path, contacts_req_params, contacts_req_headers(access_token, token_type))
32
+ user = current_user(self_response, access_token, token_type)
33
+ set_current_user user
34
+ end
35
+
27
36
  private
28
37
 
29
38
  def contacts_req_params
@@ -38,9 +47,25 @@ module OmniContacts
38
47
  response = JSON.parse(response_as_json)
39
48
  return [] if response['feed'].nil? || response['feed']['entry'].nil?
40
49
  contacts = []
50
+ return contacts if response.nil?
41
51
  response['feed']['entry'].each do |entry|
42
52
  # creating nil fields to keep the fields consistent across other networks
43
- contact = {:id => nil, :first_name => nil, :last_name => nil, :name => nil, :email => nil, :gender => nil, :birthday => nil, :profile_picture=> nil, :relation => nil}
53
+
54
+ contact = { :id => nil,
55
+ :first_name => nil,
56
+ :last_name => nil,
57
+ :name => nil,
58
+ :emails => nil,
59
+ :gender => nil,
60
+ :birthday => nil,
61
+ :profile_picture=> nil,
62
+ :relation => nil,
63
+ :addresses => nil,
64
+ :phone_numbers => nil,
65
+ :dates => nil,
66
+ :company => nil,
67
+ :position => nil
68
+ }
44
69
  contact[:id] = entry['id']['$t'] if entry['id']
45
70
  if entry['gd$name']
46
71
  gd_name = entry['gd$name']
@@ -50,25 +75,136 @@ module OmniContacts
50
75
  contact[:name] = full_name(contact[:first_name],contact[:last_name]) if contact[:name].nil?
51
76
  end
52
77
 
53
- contact[:email] = entry['gd$email'][0]['address'] if entry['gd$email']
78
+ contact[:emails] = []
79
+ entry['gd$email'].each do |email|
80
+ if email['rel']
81
+ split_index = email['rel'].index('#')
82
+ contact[:emails] << {:name => email['rel'][split_index + 1, email['rel'].length - 1], :email => email['address']}
83
+ elsif email['label']
84
+ contact[:emails] << {:name => email['label'], :email => email['address']}
85
+ end
86
+ end if entry['gd$email']
87
+
88
+ # Support older versions of the gem by keeping singular entries around
89
+ contact[:email] = contact[:emails][0][:email] if contact[:emails][0]
54
90
  contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:name]) if !contact[:name].nil? && contact[:name].include?('@')
55
- contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:email]) if contact[:name].nil? && contact[:email]
91
+ contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:emails][0][:email]) if contact[:name].nil? && contact[:emails][0][:email]
56
92
  #format - year-month-date
57
- if entry['gContact$birthday']
58
- birthday = entry['gContact$birthday']['when'].split('-')
59
- contact[:birthday] = birthday_format(birthday[2], birthday[3], nil) if birthday.size == 4
60
- contact[:birthday] = birthday_format(birthday[1], birthday[2], birthday[0]) if birthday.size == 3
61
- end
93
+ contact[:birthday] = birthday(entry['gContact$birthday']['when']) if entry['gContact$birthday']
94
+
62
95
  # value is either "male" or "female"
63
96
  contact[:gender] = entry['gContact$gender']['value'] if entry['gContact$gender']
64
- contact[:relation] = entry['gContact$relation']['rel'] if entry['gContact$relation']
97
+
98
+ if entry['gContact$relation']
99
+ if entry['gContact$relation'].is_a?(Hash)
100
+ contact[:relation] = entry['gContact$relation']['rel']
101
+ elsif entry['gContact$relation'].is_a?(Array)
102
+ contact[:relation] = entry['gContact$relation'].first['rel']
103
+ end
104
+ end
105
+
106
+ contact[:addresses] = []
107
+ entry['gd$structuredPostalAddress'].each do |address|
108
+ if address['rel']
109
+ split_index = address['rel'].index('#')
110
+ new_address = {:name => address['rel'][split_index + 1, address['rel'].length - 1]}
111
+ elsif address['label']
112
+ new_address = {:name => address['label']}
113
+ end
114
+
115
+ new_address[:address_1] = address['gd$street']['$t'] if address['gd$street']
116
+ new_address[:address_1] = address['gd$formattedAddress']['$t'] if new_address[:address_1].nil? && address['gd$formattedAddress']
117
+ if new_address[:address_1].index("\n")
118
+ parts = new_address[:address_1].split("\n")
119
+ new_address[:address_1] = parts.first
120
+ # this may contain city/state/zip if user jammed it all into one string.... :-(
121
+ new_address[:address_2] = parts[1..-1].join(', ')
122
+ end
123
+ new_address[:city] = address['gd$city']['$t'] if address['gd$city']
124
+ new_address[:region] = address['gd$region']['$t'] if address['gd$region'] # like state or province
125
+ new_address[:country] = address['gd$country']['code'] if address['gd$country']
126
+ new_address[:postcode] = address['gd$postcode']['$t'] if address['gd$postcode']
127
+ contact[:addresses] << new_address
128
+ end if entry['gd$structuredPostalAddress']
129
+
130
+ # Support older versions of the gem by keeping singular entries around
131
+ if contact[:addresses][0]
132
+ contact[:address_1] = contact[:addresses][0][:address_1]
133
+ contact[:address_2] = contact[:addresses][0][:address_2]
134
+ contact[:city] = contact[:addresses][0][:city]
135
+ contact[:region] = contact[:addresses][0][:region]
136
+ contact[:country] = contact[:addresses][0][:country]
137
+ contact[:postcode] = contact[:addresses][0][:postcode]
138
+ end
139
+
140
+ contact[:phone_numbers] = []
141
+ entry['gd$phoneNumber'].each do |phone_number|
142
+ if phone_number['rel']
143
+ split_index = phone_number['rel'].index('#')
144
+ contact[:phone_numbers] << {:name => phone_number['rel'][split_index + 1, phone_number['rel'].length - 1], :number => phone_number['$t']}
145
+ elsif phone_number['label']
146
+ contact[:phone_numbers] << {:name => phone_number['label'], :number => phone_number['$t']}
147
+ end
148
+ end if entry['gd$phoneNumber']
149
+
150
+ # Support older versions of the gem by keeping singular entries around
151
+ contact[:phone_number] = contact[:phone_numbers][0][:number] if contact[:phone_numbers][0]
152
+
153
+ if entry['gContact$website'] && entry['gContact$website'][0]["rel"] == "profile"
154
+ contact[:id] = contact_id(entry['gContact$website'][0]["href"])
155
+ contact[:profile_picture] = image_url(contact[:id])
156
+ else
157
+ contact[:profile_picture] = image_url_from_email(contact[:email])
158
+ end
159
+
160
+ if entry['gContact$event']
161
+ contact[:dates] = []
162
+ entry['gContact$event'].each do |event|
163
+ if event['rel']
164
+ contact[:dates] << {:name => event['rel'], :date => birthday(event['gd$when']['startTime'])}
165
+ elsif event['label']
166
+ contact[:dates] << {:name => event['label'], :date => birthday(event['gd$when']['startTime'])}
167
+ end
168
+ end
169
+ end
170
+
171
+ if entry['gd$organization']
172
+ contact[:company] = entry['gd$organization'][0]['gd$orgName']['$t'] if entry['gd$organization'][0]['gd$orgName']
173
+ contact[:position] = entry['gd$organization'][0]['gd$orgTitle']['$t'] if entry['gd$organization'][0]['gd$orgTitle']
174
+ end
65
175
 
66
176
  contacts << contact if contact[:name]
67
177
  end
68
- contacts.uniq! {|c| c[:email] || c[:name]}
178
+ contacts.uniq! {|c| c[:email] || c[:profile_picture] || c[:name]}
69
179
  contacts
70
180
  end
71
181
 
182
+ def image_url gmail_id
183
+ return "https://profiles.google.com/s2/photos/profile/" + gmail_id if gmail_id
184
+ end
185
+
186
+ def current_user me, access_token, token_type
187
+ return nil if me.nil?
188
+ me = JSON.parse(me)
189
+ user = {:id => me['id'], :email => me['email'], :name => me['name'], :first_name => me['given_name'],
190
+ :last_name => me['family_name'], :gender => me['gender'], :birthday => birthday(me['birthday']), :profile_picture => image_url(me['id']),
191
+ :access_token => access_token, :token_type => token_type
192
+ }
193
+ user
194
+ end
195
+
196
+ def birthday dob
197
+ return nil if dob.nil?
198
+ birthday = dob.split('-')
199
+ return birthday_format(birthday[2], birthday[3], nil) if birthday.size == 4
200
+ return birthday_format(birthday[1], birthday[2], birthday[0]) if birthday.size == 3
201
+ end
202
+
203
+ def contact_id(profile_url)
204
+ id = (profile_url.present?) ? File.basename(profile_url) : nil
205
+ id
206
+ end
207
+
72
208
  end
73
209
  end
74
210
  end