I ran into this problem while trying to use sf_notifications to create new Drupal users. You should be able to create a new Contact in Salesforce and have a new user created in Drupal.
Look at this bit of code towards the end of sf_user_import():
// If the user exists, update the existing user.
if ($account->uid) {
$account = user_save($account, $changes);
$create = FALSE;
} else {
$account = user_save(null, $changes);
$create = TRUE;
}
if ((($map->automatic & SALESFORCE_AUTO_SYNC_CREATE) && $create)
|| (($map->automatic & SALESFORCE_AUTO_SYNC_UPDATE) && !$create)) {
// Store the Salesforce ID for the node and return TRUE.
salesforce_api_id_save('user', $account->uid, $sfid, $name);
}
unset($account->sf_user_skip_export);
return $account->uid;
There are a couple of problems here.
The first is that if ($account->uid) { always evaluates to true, because the value of $account->uid is 0.
So when we get here $account = user_save($account, $changes); we get SQL errors about duplicate entry because user_save is attempting to update account with uid 0.
Once we deal with the first if statement, we have another issue. The call to user_save(null, $changes); for creating a new account fails. A basic user object needs to be passed to user_save for it to work.
Finally, it seems that the call to salesforce_api_id_save should happen right after the user_save for the new account creation, not at the end of the function.
Attached is a patch, although it is incomplete as it makes assumption about your fieldmap – in this case I have mapped an e-mail address field to the Drupal username.
| Comment | File | Size | Author |
|---|---|---|---|
| #22 | 1058870-22-sf_user-user_save.patch | 944 bytes | aaronbauman |
| #7 | 1058870-6-sf_user-user_save.patch | 915 bytes | aaronbauman |
| #4 | 1058870-sf_user_import.patch | 1.21 KB | aaronbauman |
| user-save-error.patch | 1.34 KB | kostajh |
Comments
Comment #1
aaronbaumanI am not discounting this bug report entirely - I too have seen SQL duplicate value errors.
However, I have verified locally that sf_user_import() successfully creates a user object.
A few things though,
This statement not true.
If $account->uid is 0, it evaluates to FALSE.
This statement is also false.
If $account is NULL, user_save() proceeds to the user insert routine.
From user.module, line 215, the conditional that determines whether a user is updated or inserted is the following:
Just because we have a matching user account, doesn't mean a link exists between it and the salesforce record.
If you're still running to issues with this function, please provide some more info so we can try to get to the bottom of them.
Specifically I'd be interested to know
by what mechanism are you firing this function?
what is the value of $account immediately before user_save() is fired in this function?
what is the value of your fieldmap?
does the prematch routine find any matches?
Comment #2
aaronbaumanPS. i'm testing via drush, since there is not a straightforward way to trigger sf_user_import() off the shelf.
In this way, I isolated the behavior of sf_user_import(), and successfully created a user from the Contact record:
Comment #3
kostajh commentedHi Aaron,
I added this at the very beginning of sf_user_import():
Here is the sanitized output when sf_user_import is invoked by sf_notifications.
As you can see 'uid' is NULL.
Here is the fieldmap for Contact to User object:
I added another watchdog statement right before the attempt is made to insert a new user (when sf_user_import is invoked by creating a new Contact in Salesforce that is sent via outbound messaging to the Drupal site):
Looking at this I notice that the values for profile_first_name and profile_last_name are different in $account and $changes; I'm not sure why that is occurring.
If I modify the code in sf_user_import according to the patch provided at the beginning of this issue, a new Drupal user will be created successfully.
If I don't do that, the "else" is never evaluated and if I modify the code so that it is, calling user_save(NULL, $changes) results in duplicate key entry errors.
If you want, take the array for "changes" that is output above and try calling user_save(NULL, $changes) on a local test environment to see what happens.
One thing I can see about your testing via drush is that you are calling sf_user_import with the Salesforce ID rather than with an object of Salesforce data, so that is one potential reason why we are getting different results.
Finally, I am not using sf_prematch as it was causing some issues (mis-matching Drupal user IDs and SF IDs), and I did not need it for the project I'm working on at the moment.
Thanks for your help, let me know if there is any other information I can provide.
Comment #4
aaronbaumanOK, first thing:
Looks like sf_user mistakenly linked user 0 to an existing contact -- 003Q000000Hiqr1IAB
This explains the discrepancy between $account and $changes
The problem stems from sf_user_import():
which should probably be wrapped with a conditional like
so, try this:
* delete any entries in salesforce_object_map where oid == 0
* apply the following patch
* retest & report
Finally, I still don't get any duplicate key errors.
Are you sure that $changes['name'] is unique?
Also, SF.Email is mapped to Drupal.name - which may be intentional, but looks suspicious.
Comment #5
kostajh commentedI reverted back to the latest code from 6.x-2.x-dev and applied your patch Aaron.
Here is the result from updating a Contact in Salesforce:
Duplicate entry '' for key 'name' query: INSERT INTO users (login, created) VALUES (0, 1297975994) in modules/user/user.module on line 327.Followed by:
Here is the result from creating a new Contact in Salesforce, which should result in a new Drupal user being created.
Duplicate entry '' for key 'name' query: INSERT INTO users (login, created) VALUES (0, 1297976302) in modules/user/user.module on line 327.Followed by
SalesForce Notificaitions failed to update user from record.and the same array of data that I pasted above. The relevant bit is that oid is 0.Then I remembered you asked to delete any items from salesforce_object_map where oid is 0. There was one record that I removed. I tried to update a Contact and got this result:
Duplicate entry '' for key 'name' query: INSERT INTO users (login, created) VALUES (0, 1297976654) in modules/user/user.module on line 327.Followed by "SalesForce Notificaitions failed to update from record." and the array, the interesting bit is this:
So neither oid nor drupal_type are set.
Can you please take another look at the initial patch I provided at the beginning of this issue? Because in my testing that patch resolves the issue for both inserting and updating users from Salesforce Contacts and Leads.
Comment #6
rjacobs commentedSubscribing... I'd like to follow this. I've encountered problems working with sf_import when trying to import contacts to users. I've only begun to look-at/test import concepts, so I don't have too much new insight to contribute. I just wanted to note that we too have seen similar issues.
It seems that no user details are created though sf_import appears to be successfully finding the contact objects on the SF side. I've also noticed that every time I test an import I always end up with only one new entry in salesforce_object_map that has an oid of 0.
It would also appear that the $changes array is never populated, probably due to the check around line 560 of sf_user, where $account->$drupal_fieldname is never set properly:
elseif (isset($sf_data->$sf_fieldname) && isset($account->$drupal_fieldname)) {
I don't want to cloud the waters... just wanted to note our initial experiences with this.
Ryan
Comment #7
aaronbaumanRyan: i think you hit the nail on the head for importing new users. The bug you point out partially explains why new users are not imported properly, but it doesn't explain why existing users are not updated properly -- maybe existing users are update properly?
The attached patch will make sure that all mapped Salesforce data will get applied to the user object.
But, it will not resolve the problem completely.
The SQL error that Kosta is due to a missing username, which is not addressed by this concern.
Using a fixed value or a php value for username seems appealing, but such an approach is a bit of a rabbit hole. What if we need different values for import vs. export? What if we only want to assign the value under certain conditions?
When I have implemented this feature for clients, I have used content_profile (sf_node) and avoided sf_user mappings altogether.
Since having username in the fieldmap may not be particularly useful or feasible, we need to come up with a fallback.
Should sf_user create usernames when they are not set?
For that matter, should sf_user create all required data if not set?
Do we need a policy-based approach, where administrators can specify what should happen for unmapped, required data fields on import?
Or should we simply state that all required Drupal data must be mapped in order for imports to succeed.
In the interim, should we remove user fieldmaps from sf_import, since they don't really work?
Until these questions are addressed, implementing a pre-import hook (after applying the patch #1042272: Pre and post import hooks) to address these concerns for your projects will help you get over this hurdle.
Comment #8
dpearcefl commentedI'm going to jump in here since I've been chasing this same problem today. I started with another issue (#1079248: Outbound Message to create user fails) which Aaron rightfully flagged as a dup of this issue.
I'd like to suggest two fixes:
In my case, I am doing a SF outbound message which creates a Drupal user. I have this working with my patches. Will this resolve this issue?
I am very interested in resolving this issue and can easily roll a patch.
Comment #9
kostajh commented@dpearceMN: I ended up implementing hook_salesforce_api_pre_import and hook_salesforce_api_post_export to deal with these issues.
In my implementation of salesforce_api_pre_import, I set some values of the object being imported.
At this point none of the profile fields are saved, however, and a link between the newly created user and the Salesforce object hasn't been established in
{salesforce_object_map}. So in salesforce_api_post_import I have:As you can see this is fairly cumbersome. But, the code successfully creates a new user with the profile fields defined in the fieldmap when a new Salesforce Contact is created.
The only major issues I'm having at this point are that
Duplicate entry for key 'name'on users that already exist; instead of updating the existing Drupal user object from modifications to a Salesforce contact, the code is attempting to insert a new user and failing because user_save is getting duplicate key entry errors.Comment #10
aaronbaumanre: #8
the logging step makes sense - better debug messages would help people troubleshoot their installs.
the first point, however - setting user fields to the empty string - i don't see what this accomplishes.
i'm not too familiar with profile.module - will it choke if an account property is not set?
with regard to core fields - username, email - this approach won't address the issues outlined above.
edited, crosspost
Comment #11
dpearcefl commented"setting user fields to the empty string" - All this does it create the profile fields in the array. If you look at what I wrote in the duplicate issue, anonymous (user id 0) is unique in that it does not have profile fields defined in the user object. Therefore those fields never make it into $changes.
Comment #12
aaronbaumanOK, I think I understand the concern now.
But, I think patch #7 gets around the issue by ignoring the $account object altogether while building $changes: in the patch, $changes gets populated based solely on the contents of $sf_data
Comment #13
dpearcefl commentedAaron, I agree on the patch #7. Less code is always better. And it accomplishes the same thing as my patch.
Comment #14
kostajh commentedI also agree that patch in #7 is reasonable. I'm still getting the duplicate entry errors in sf_user_import though. I will try to track that down soon.
Comment #15
dpearcefl commentedConcerning Aaron's comment #10 on my idea of logging missing required fields in #8:
Turns out that for user_save(), the only truly required field is 'name'. Even if the profile field is marked as required in the profile, user_save() will still create the user if that field is missing.
Adding code to create a salesforce_api_log() call if the 'name' field is missing seems logical. If it is missing we should probably fail the import.
But do we do anything is a required profile field is missing? Do we:
Comment #16
kostajh commentedI would like to commit the patch in #7 to dev, are there any objections?
Comment #17
aaronbaumanunrelated: Cross posting #1092786: sf_user_export does not react to salesforce_api_pre_export hook changes to fieldmap name - change to pre_ import hook arguments.
Comment #18
kostajh commentedI tested the patch in #7. When I try to visit user/%uid/salesforce and run an "Import changes from Salesforce" I get:
In bootstrap.inc, we have
Any ideas?
Comment #19
aaronbaumanKosta,
sounds like your database schema doesn't match the schema in the install file.
do you have a {salesforce_field_map}.fields column in your database?
I don't know why patch #7 would have affect this error one way or another.
Comment #20
kostajh commentedYes, I have a fields column. Here is the schema:
I will try to look into this some more today if I have time.
Comment #21
kostajh commentedHere is some more info on the issue: https://drupal.org/node/767512#comment-2843034
Looks like the problem is with saving an empty key in the user data. Later versions of Drupal core should prevent the error from occurring, but since the site I'm working on is 6.14, I need to ensure that no attempts at saving empty keys are made.
Comment #22
aaronbaumanah - i see, so the changes in patch #7 are definitely related.
see if the attached patch fixes it
Comment #23
aaronbaumanComment #24
kostajh commentedI was just working on this and came up with the same thing. The patch fixes my error message, and addresses the field importing issues described earlier in the thread. I think it is ready to commit. Thanks!
Comment #25
kostajh commentedPatch in #22 is committed to dev. http://drupalcode.org/project/salesforce.git/commit/62aaeba
Do we want to leave this issue open to deal with questions raised in #15?
Comment #26
aaronbaumanI opened a new issue to keep things tidy:
#1105392: Implement and document consistent behavior when sf_user_import() fails
Thanks for everyone's work on this