跳转到主要内容
yoyo198624 提交于 29 August 2014

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

Drupal 版本