Issue

The Drupal 7 branch of Connector does not sync most user fields, neither on account creation or login. The administrative user interface leads one to believe this possible, but it simply isn't.

Cause

The _connector_create_account() or _connector_log_in() functions didn't assign values other than name and mail.

Solution

Provided patch does the following:

  1. Creates a new _connector_store_account_values() internal function and implements it in _connector_create_account() and _connector_log_in()
  2. Creates hook_connector_user_info_alter() which let's you alter the incoming info. I then used this to handle the avatar situation.
  3. Creates hook_connector_field_value_handler_FIELDNAME() can save each field.
  4. Saves a temporary picture file for the avatar field, which will be permanently saved by user.module

Original Issue

After successfully creating and/or connecting an account with Google or Facebook there's no data imported to the user profile. Here is the exported code of my Google connector:

$provider = new stdClass();
$provider->disabled = FALSE; /* Edit this to true to make a default provider disabled initially */
$provider->name = 'google';
$provider->title = 'Google';
$provider->url = 'https://accounts.google.com';
$provider->consumer_advanced = array(
  'oauth2' => 1,
  'signature method' => 'HMAC-SHA1',
  'authentication realm' => 'authorization_code',
  'request token endpoint' => 'none',
  'authorization scope' => 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
  'authorization endpoint' => '/o/oauth2/auth',
  'access token endpoint' => '/o/oauth2/token',
);
$provider->mapping = array(
  'fields' => array(
    'uid' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'id',
      'querypath' => FALSE,
      'sync_with_field' => '',
    ),
    'email' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'email',
      'querypath' => FALSE,
      'sync_with_field' => 'mail',
    ),
    'name' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'name',
      'querypath' => FALSE,
      'sync_with_field' => 'name',
    ),
    'avatar' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'picture',
      'querypath' => FALSE,
      'sync_with_field' => 'picture',
    ),
  ),
  'format' => 'json',
);

Additionally I get these errors when saving a new connector:

Notice: Undefined property: stdClass::$csid in oauthconnector_provider_save() (line 461 of /home/s080feb3/public_html/d7/sites/default/modules/oauthconnector/oauthconnector.module).
Notice: Undefined property: stdClass::$csid in oauthconnector_provider_save() (line 462 of /home/s080feb3/public_html/d7/sites/default/modules/oauthconnector/oauthconnector.module).

Great module by the way, just need it to work!

Comments

nonozz’s picture

Assigned:nonozz» Unassigned
nonozz’s picture

Priority:Critical» Normal
Frans’s picture

Status:Active» Postponed (maintainer needs more info)

What data do you mean?

The mapping of the fields is a pain in the ...
Basic fields as email and username should work.

nonozz’s picture

I'm afraid I'm talking about that pain in that place.. none of the mapped fields gets filled after connecting/creating an account, guess it's related to that 'Notice: Undefined property'

Frans’s picture

Does 'sync local profile with PROVIDERNAME' do something on:
user/1/connections ?

If not, did you map fields on the aouthconnector provider config?
'Field to match on user profile'

nonozz’s picture

Yes and yes, connected accounts does appear on the user profile tab and I have mapped the fields in the config.. even tried different combinations of default and custom fields but didn't get any data.

I'm running Drupal 7.12 minimal profile install with the necessary modules only.

Frans’s picture

can you export the provider and dump it here?
admin/structure/oauthconnector

Also like to know what kind of fields (text, username...) you are trying to fill.

nonozz’s picture

I've tried mapping with the preset's default fields and also with just a custom text field mapped to the username, no succeed. When creating a new account the resulting profile is empty, no email, no picture, nothing in the custom field, nothing at all but the username which is something like 'oauthconnector_google__10533...'

Here is the exported provider:

$provider = new stdClass();
$provider->disabled = FALSE; /* Edit this to true to make a default provider disabled initially */
$provider->name = 'google';
$provider->title = 'Google';
$provider->url = 'https://accounts.google.com';
$provider->consumer_advanced = array(
  'oauth2' => 1,
  'signature method' => 'HMAC-SHA1',
  'authentication realm' => 'authorization_code',
  'request token endpoint' => 'none',
  'authorization scope' => 'https://www.googleapis.com/auth/userinfo.profile',
  'authorization endpoint' => '/o/oauth2/auth',
  'access token endpoint' => '/o/oauth2/token',
);
$provider->mapping = array(
  'fields' => array(
    'uid' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'id',
      'querypath' => FALSE,
      'sync_with_field' => '',
    ),
    'name' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'name',
      'querypath' => FALSE,
      'sync_with_field' => 'field_nombre',
    ),
    'avatar' => array(
      'resource' => 'https://www.googleapis.com/oauth2/v1/userinfo',
      'method post' => 0,
      'field' => 'picture',
      'querypath' => FALSE,
      'sync_with_field' => 'picture',
    ),
  ),
  'format' => 'json',
);
Frans’s picture

Project:OAuth Connector» Connector
Status:Postponed (maintainer needs more info)» Needs review

I did some work on the mapping of the fields.

This is a connector thingie and not a oauth connector thingie.

The default action now maps email and name to the right fields on first login. Please read carefully... it only maps mail and name and only on first login .
So... it does not do anything with avatar or other fields (yet).

The functionality 'sync local profile with PROVIDERNAME' does also a better job now. Normal text fields are all mapped now, images and other fields not.

What now... or how to solve...

  • You can take a look at the sub module (of connector) connector_action_default_register_form.module to write your own custom behavior after a connection is made.
  • Or write a patch for connector so that the default action does its job.

When writing patches, please also see and try to team up:
#1450664: Prefill user form patch
#1481574: Integration with feeds

remydenton’s picture

This patch adds additional fields on account creation (it uses entity metadata wrappers, which means it should work for most user profile entity fields but also requires the entity api module).

It definitely needs some more work and this may not even be the right approach, but I needed a solution for this and thought I'd share what I had in case it helps with progress.

twist3r’s picture

Thanks remydenton for your patch, following yout work I've added support for importing link fields and address fields.

Please note that this requires patch #10 to work:

--- connector.module.orig 2014-01-25 11:49:36.000000000 +0100
+++ connector.module 2014-01-25 19:43:23.000000000 +0100
@@ -490,12 +490,45 @@
       if (module_exists('entity')) {
         $user_w = entity_metadata_wrapper('user', $new_account);

-        foreach ($info as $field) {
+        foreach ($info as $key => $field) {
           if (isset($field['sync']) && $field['sync'] && !in_array($field['sync'], $allowed_fields)) {
             if (!empty($field['value'])) {
               try {
+ if ($user_w->{$field['sync']}->type() == 'field_item_link') {
+   // Field type link
+   $user_w->{$field['sync']}->url->set($field['value']);
+ }
+ if ($user_w->{$field['sync']}->type() == 'addressfield') {
+   // Address field
+   if ($key == 'first_name') {
+ $user_w->{$field['sync']}->first_name->set($field['value']);
+   }
+   elseif ($key == 'last_name') {
+ $user_w->{$field['sync']}->last_name->set($field['value']);
+   }
+   elseif ($key == 'location') {
+     $point = geocoder('google', $field['value']['name']);
+ foreach ($point->data['geocoder_address_components'] as $cmp) {
+   if (in_array('locality', $cmp->types)) { // Bologna
+ $user_w->{$field['sync']}->locality->set($cmp->short_name);
+   }
+   elseif (in_array('administrative_area_level_2', $cmp->types)) { // BO
+ $user_w->{$field['sync']}->administrative_area->set($cmp->short_name);
+   }
+   elseif (in_array('country', $cmp->types)) { // IT
+ $user_w->{$field['sync']}->country->set($cmp->short_name);
+   }
+   elseif (in_array('postal_code', $cmp->types)) { // IT
+ $user_w->{$field['sync']}->postal_code->set($cmp->short_name);
+   }
+ } // rof
+   }
+ }
+ else {
+   // Other field types
                 $user_w->{$field['sync']}->set($field['value']);
               }
+              }
               catch (EntityMetadataWrapperException $e) {
                 watchdog('connector', 'Unable to set field %field_name on new user profile: %message', array('%field_name' => $field['sync'], '%message' => $e->getMessage()), WATCHDOG_NOTICE);
               }

Deciphered’s picture

StatusFileSize
new2.16 KB

Another patch along the same lines, except this one actually makes Entity a dependency.

I've turned the $allowed_fields into $unique_fields so it will still check if the username or email address is already in use, but otherwise all other fields will be inserted as per the EntityMetaDataWrapper.

Might need some additional checks to make sure every type of field is being attached correctly, but definitely not with such specific checking as per #11.

Deciphered’s picture

StatusFileSize
new2.13 KB
new434 bytes

Minor fix, last minute optimisation accidentally broke the unique_fields stuff.

j_nunes’s picture

Why are you guys looking at 7.x-1.0-beta1?
I can't even find that version to test the patch.
Just thought that new patches would apply on the latest versions.

Deciphered’s picture

Version:7.x-1.0-beta1» 7.x-1.x-dev

We're no (well, I'm not), I just didn't update the issue version.

johnnyfofo’s picture

#13 works fine, would love to see this committed!

d0tcom’s picture

#13 works great when using the Google+ scopes! (It took me a while to finally get the correct combination that will fetch information from Google)

Unfortunately I am still unable to map the avatar field..

I'm using https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.me for the scope.
I'm using https://www.googleapis.com/plus/v1/people/me as the resource URL and image.url for the Avatar field.

I am able to fetch the URL if I map the image.url field to a regular text field in Drupal but it doesn't work for the Drupal Picture field.

ram4nd’s picture

#13 seems to work

What about auto-sync?

ram4nd’s picture

StatusFileSize
new1003 bytes

This patch syncs data when logging in.

heshanlk’s picture

(Only for beta2)
This patch fixed the issue with no image import when login and sync. There are two ways to map the avatar.

  1. Use built in Picture property.
  2. Use a custom image or file field.

The both scenarios worked fine in this path.

Thanks

heshanlk’s picture

Same patch for as in #20. Re-rolled against dev branch.

pianomansam’s picture

Issue summary:View changes
StatusFileSize
new5.42 KB

I'm providing a new patch and updating the issue summary to explain exactly what the issue is. This new patch takes many of the solutions strung across all the previous patches, improves upon them, sometimes fixing them, and putting them in a single patch. Since the work here introduced the entity module as a requirement, I continued to use it even expanded upon its use. Using entity_metadata_wrapper() should work for many fields on users, but there are several properties that it cannot be used for yet #2224645: EntityMetadataWrapper is missing user properties for pictures, passwords, timezones and signatures. So this patch allows values to be stored in an $edit variable as well as the entity wrapper.

This patch also introduces the hook_connector_field_value() hook as a DRY method of validating and setting values. This is used for both user creation and login. There is a default implementation included that provides handling of the avatar field since so many comments and patches were regarding that. This default hook can be expanded to handle other field types like the code suggested by twist3r in #11, or other implementation of it in another module.

Regarding handling the avatar field, I greatly simplified and improved the code given heshan.lk in #20 and #21. Those patches were trying to do too much that was already handled in user.module, assumed the incoming image was JPG, and in the case of #21, gave show-stopping errors because they were trying to create a managed file using the uid when the user hadn't been saved yet.

tr33m4n’s picture

Hello there,

Applied your patch to the latest dev however hunk #1 failed (adding "dependencies[] = entity" to the info file). Easy fix though

Cheers

pianomansam’s picture

Issue summary:View changes
StatusFileSize
new6.76 KB

tr33m4n, I'm not sure why the patch gave you an error. I tried both patch -p1 < 1503258-22-connector-no-user-data-imported.patch and git apply 1503258-22-connector-no-user-data-imported.patch and they both worked fine. Anyways, I'm updating the patch because I wasn't satisfied with how values were being stored to the user account. Since this is using a hook, there is no way for each hook implementation to know if the value had been saved already or not. And I also didn't like how the hook combined both modifying the value and saving the value. So I've split the hook up into two hooks now:

  1. hook_connector_user_info_alter() which let's you alter the incoming info. I then used this to handle the avatar situation.
  2. hook_connector_field_value_handler_FIELDNAME() can save each field.

A few words on hook_connector_field_value_handler_FIELDNAME(). When called, this hook doesn't have to save something to the user. It can do additional validation or anything else. However, if it is implemented and doesn't save something to the user, it must set $field['stored'] = FALSE otherwise the default mapping handler will assume it really did save something.

Also, I did consider removing the new entity module requirement and trying to do everything without it. This would have been possible except the entity module really makes working with fields on user objects a lot easier. So I left it in.

tr33m4n’s picture

@pianomansam Just tried with your latest patch and still get the same hunk issue. I did the following:

drush dl connector-7.x-1.x-dev
curl -O https://www.drupal.org/files/issues/1503258-23-connector-no-user-data-imported.patch
patch -p1 < 1503258-23-connector-no-user-data-imported.patch

Which resulted in:

patching file connector.api.php
patching file connector.info
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file connector.info.rej
patching file connector.module
pianomansam’s picture

@tr33m4n I think I see the issue. I created my patch against the git checkout, which differs slightly since the packaged version adds a few lines of to info file.

tr33m4n’s picture

Fair enough, I was thinking that was the case

tr33m4n’s picture

I'd imagine for such a major patch maybe build against dev to just avoid future people having similar problems

loool’s picture

Hi Guys,

any news about this patch? I applied it, all fields now are populated except for the avatar picture .. is the same for you?

pianomansam’s picture

@lool, what kind of field is your avatar picture mapped to? The builtin profile field or a true image field?

arthur_drupal’s picture

Hi pianomansam,

I am trying your patch,
The $picture_file variable is not initialised (function connector_connector_field_value_handler_avatar)

pianomansam’s picture

StatusFileSize
new6.76 KB

@arthur_drupal, thank you for that find. It was an artifact of my prior code refactoring. Here's an updated patch that should fix that.