首先要说,有很多模块能做到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的。