首先要说,有很多模块能做到facebook, linkedin之类的登陆,但是有些时候,比如客户不希望有第三方站点干预,比如客户不希望再额外付一笔钱,再比如客户希望再登陆时做一定的客制化。那么这个情况下,这类模块的选择会瞬间锐减。
connector + oauthconnector如何配置不做叙述,需要的问谷哥(度娘就免了),有大量教程。
我的目前版本, oauthconnector 7.x-1.0-beta2, connecor 7.x-1.0-beta2。
先说一下点击Connector block按钮,创建用户时的代码流程
1. oauthconnector.module L284 -> function oauthconnector_oauth_common_authorized
在经过一系列if else判断后,进入 $account = $action['create account callback']('oauthc 。。。 L327
这个$action['create account callback'] 是调用connector.module下的 function _connector_create_account L447
2. 读function _connector_create_account后发现,此处就是create account的地方。但是因为L470 中 in_array($field['sync'], $allowed_fields) 的存在,导致保存的$userinfo里会不保存除了name和email以外的所有资讯
3. 回到oauthconnector.module,继续阅读,发现没有module_invoke_all或者alter之类的勾子创建
于是我考虑直接从源头入手。源头是哪里,就是刚才#1中提到的$action['create account callback'],$action从哪里来?L299 $action = connector_actions($_SESSION['oauthconnector_action']);
4. 看function connector_actions,看到了 drupal_alter('connector_action', $connector_actions); 以及return的也是$connector_actions ok,说明此处可以修改这个源头了。
现在开始正式添加额外字段,比如field_first_name, last name和avatar。
首先需要参照oauthconnector里的oauthconnector_oauthconnector_fields_alter 方法,添加first name, last name
/**
* We use our own alter to add the 'extra' fields.
*/
function your_modulename_oauthconnector_fields_alter(&$fields) {
$fields['first_name'] = array(
'title' => t('First Name'),
'description' => t('Name for this connection (usally a real name).'),
'required' => FALSE,
);
$fields['last_name'] = array(
'title' => t('Last Name'),
'description' => t('Name for this connection (usally a real name).'),
'required' => FALSE,
);
return $fields;
}
下一步,在自己的module中加入如下的代码
/**
* Implements of hook_connector_action_alert
*/
function your_modulename_connector_action_alter(&$connector_actions){
$connector_actions['default']['create account callback'] = 'test_connector_create_account';
}
/**
* @override_function(_connector_create_account)
* This function should update the new fields. connector module didn't save the fields except name and email
*/
function test_connector_create_account($connector_name, $cid = NULL, $consumer = NULL, $access_token = NULL, $request_token = NULL){
$new_account = _connector_create_account($connector_name, $cid, $consumer, $access_token, $request_token);
if($new_account){
_test_connector_update_account($new_account,$connector_name, $cid, $consumer, $access_token, $request_token);
}
return $new_account;
}
/**
* update account function
*/
function _test_connector_update_account(&$new_account, $connector_name, $cid = NULL, $consumer = NULL, $access_token = NULL, $request_token = NULL) {
$connector = _connector_get_connectors($connector_name);
if (!$connector) {
return FALSE;
}
$info = array();
if (!empty($connector['information callback']) && is_callable($connector['information callback'])) {
$info = $connector['information callback']($connector, $cid, array(), $access_token);
module_invoke_all('test_connector_update', $new_account, $info);
}
}
在_test_connector_update_account中,我没有直接去update这个account,而是加了一个module_invoke_all,如果你觉得没有这个必要,可以直接把这里换成test_connector_update里的内容
/**
* Implements of hook_test_connector_update
*/
function your_modulename_test_connector_update($account, $info){
$not_allowed_fields = array('name', 'mail');
$edits = array();
foreach ($info as $field) {
if (isset($field['sync']) && $field['sync'] && !in_array($field['sync'], $not_allowed_fields)) {
$edits = array_merge($edits, _test_connector_update($account, $field));
}
}
if(!empty($edits)){
//waiting this issue fixed https://www.drupal.org/node/2224645, then we can use entity_metadata_wrapper to save
user_save($account, $edits);
}
}
function _test_connector_update($account, $field){
switch ($field['sync']) {
case 'picture':
$edit = test_connector_update_picture($account, $field);
break;
case 'field_first_name':
case 'field_last_name':
$edit = array ($field['sync'] => array(LANGUAGE_NONE=>array(array('value'=>$field['value']))));
break;
}
return $edit;
}
function test_connector_update_picture($account, $field){
if(!empty($field['value'])){
$img_info = getimagesize($field['value']);
if($img_info){
$file_name = 'picture-' . $account->uid . '-' . time();
if($file = system_retrieve_file($field['value'], 'public://pictures/'.$file_name, TRUE, FILE_EXISTS_REPLACE)){
$edit = array ('picture' => $file);
return $edit;
}
}
}
}
在your_modulename_test_connector_update中用的是user_save,其实用entity的entity_metadata_wrapper去set value更好,我这里如果field_first_name和last name有翻译问题,就不能用LANGUAGE_NONE
至此,需要添加的代码就结束了。然后就是回到oauthconnector中,修改一下linkedin和facebook的默认配置,给first name, last name和picture也加入就OK
PS. 需要额外提两点。
1. facebook的picture比较特殊,他的picture地址 https://graph.facebook.com/me/picture 如果使用facebook的Graph Explorer工具,他给出的url里会类似https://graph.facebook.com/me/picture?width=1024&redirect=false,而实际上,只需要https://graph.facebook.com/me/picture?width=1024就可以。
2. 在oauthconnector中,oauthconnector_oauthconnector_fetch_field_endpoint_call_alter这个alter里,对facebook的picture的url获得有额外的代码。这是因为这里的data很奇怪的并没有正确的返回过来,而是把url放在了header中,并且header的返回code是304。可是如果看HttpClient.inc的L163行, if ($response->responseCode >= 200 && $response->responseCode <= 299) { 可以发现,code超过299的是被认为error的。