connector.module in Connector 7
Same filename and directory in other branches
Connector module
File
connector.moduleView source
<?php
/**
* @file
* Connector module
*/
//TODO: Make the user edit page more themable by other modules/themes - they should be able to rearrange or hide parts of it
//TODO: Check whether 'invalidate old info' is set or not in hook_requirements()?
//TODO: Enable user to remove itself/it's connection from the site?
//TODO: React on disabling and uninstalling of another connector module
//TODO: Show indication of the connection when logged in?
//TODO: Is there really a need for a separate avatar callback?
//TODO: Make it possible to upload an icon for a button?
//TODO: Make it possible to disable default login, create account etc?
//TODO: Always refresh info on log in? Because we will most certainly have access to the info then. Need to make sure not to refresh too often though
//TODO: Show status of connections to at least the admin - do that on eg. the user's profile page
//TODO: Should the backing of really be in this module?
//TODO: Move syncing of info to a hook-system that everyone can hook into?
//TODO: Provide more hooks!
//TODO: Remove $uid from info callbacks?
/**
* Implements hook_theme().
*/
function connector_theme() {
return array(
'connector_buttons' => array(
'render element' => 'form',
),
'connector_connections_list_tableselect' => array(
'render element' => 'form',
'file' => 'connector.pages.inc',
),
);
}
/**
* Implements hook_menu().
*/
function connector_menu() {
$items = array();
$items['user/%user/connections'] = array(
'title' => 'Connections',
'page callback' => 'connector_user_settings',
'page arguments' => array(
1,
),
'access callback' => 'connector_user_settings_access_callback',
'access arguments' => array(
1,
),
'type' => MENU_LOCAL_TASK,
'file' => 'connector.pages.inc',
'weight' => 2,
);
$items['user/%user/connections/%/delete'] = array(
'title' => 'Remove connection',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'connector_user_delete_form',
1,
3,
),
'access callback' => 'connector_user_settings_access_callback',
'access arguments' => array(
1,
),
'file' => 'connector.pages.inc',
);
$items['user/%user/connections/%/sync'] = array(
'title' => 'Sync profile with connection',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'connector_user_sync_form',
1,
3,
),
'access callback' => 'connector_user_sync_access_callback',
'access arguments' => array(
1,
),
'file' => 'connector.pages.inc',
);
$items['connect/%'] = array(
'page callback' => 'connector_redirect_callback',
'page arguments' => array(
1,
),
'access callback' => 'connector_redirect_access',
'access arguments' => array(
1,
),
);
return $items;
}
/**
* Menu callback for the direct url.
*/
function connector_redirect_callback($connector_name) {
global $user;
if (!($connector = _connector_get_connectors($connector_name))) {
return FALSE;
}
$callback = 'button callback';
if ($user && $user->uid != 0) {
$callback = 'connect button callback';
}
if (!is_callable($connector[$callback])) {
return FALSE;
}
// We make sure there is a default destination to avoid redirect loops.
if (!isset($_GET['destination'])) {
$_GET['destination'] = '/';
}
$action = 'default';
if (arg(2) && connector_actions(arg(2))) {
$action = arg(2);
}
// this is normally a submit handler... we mimic that.
$form = array();
$form_state = array();
$form_state['clicked_button']['connector']['#value'] = $connector;
$form_state['values']['action'] = $action;
$connector[$callback]($form, $form_state);
}
/**
* Access callback for the direct url
*/
function connector_redirect_access($connector_name) {
if (!($connector = _connector_get_connectors($connector_name))) {
return FALSE;
}
return user_access('connect with ' . $connector['name']);
}
function connector_user_settings_access_callback($account) {
return (bool) (user_edit_access($account) && user_access('access connections tab'));
}
function connector_user_sync_access_callback($account) {
return (bool) (user_edit_access($account) && user_access('sync local profile with connections'));
}
/**
* Implements hook_permission().
*/
function connector_permission() {
$perms = array(
'access connections tab' => array(
'title' => t('Access connections tab'),
'description' => t('Access the tab with connections to administer connections to external sites for a user.'),
),
'sync local profile with connections' => array(
'title' => t('Sync local profile with connections'),
'description' => t('Sync a local profile with a connection.'),
),
);
$connectors = _connector_get_connectors();
foreach ($connectors as $connector) {
$perms['connect with ' . $connector['name']] = array(
'title' => t('Connect with @title', array(
'@title' => $connector['title'],
)),
'description' => t('Allow user to make a connection with #title to login, register on this site or do stuff on #title in name of the user.', array(
'#title' => $connector['title'],
)),
);
}
return $perms;
}
/**
* Implements hook_block_info().
*/
function connector_block_info() {
$block['one_click_block']['info'] = t('Connector');
return $block;
}
/**
* Implements hook_block_view().
*/
function connector_block_view($delta) {
global $user;
switch ($delta) {
case 'one_click_block':
if (!$user->uid) {
$state = array(
'build_info' => array(
'args' => array(),
),
);
$form = drupal_retrieve_form('connector_button_form', $state);
if ($form['#has_buttons']) {
return array(
'content' => drupal_get_form('connector_button_form'),
);
}
}
break;
}
}
/**
* Implements hook_user_cancel().
*/
function connector_user_cancel($edit, $account, $method) {
$connectors = _connector_get_connectors();
$connections = _connector_get_user_connections($account);
foreach ($connections as $connection) {
if (array_key_exists($connection->connector, $connectors)) {
$connector = $connectors[$connection->connector];
if (isset($connector['delete callback']) && is_callable($connector['delete callback'])) {
call_user_func($connector['delete callback'], $connector, $connection->cid);
}
}
}
db_delete('connector_user')
->condition('uid', $account->uid)
->execute();
db_delete('connector_info')
->condition('uid', $account->uid)
->execute();
}
/**
* Implements hook_user_logout().
*/
function connector_user_logout($account) {
$connectors = _connector_get_connectors();
$connections = _connector_get_user_connections($account);
foreach ($connections as $connection) {
if (array_key_exists($connection->connector, $connectors)) {
$connector = $connectors[$connection->connector];
if (isset($connector['logout callback']) && is_callable($connector['logout callback'])) {
call_user_func($connector['logout callback'], $connector, $connection->cid);
}
}
}
}
/**
* Form for the connector buttons.
*
* @param object $account
* Account for which to display the button form.
* @param string $label
* String to display on the button.
* @param string $action
* Action to take when button is clicked.
*
* @return array $form
* Form with connector buttons.
*/
function connector_button_form($form, &$form_state, $account = FALSE, $label = 'Connect with !title', $action = 'default') {
$form = array(
'#theme' => 'connector_buttons',
'#has_buttons' => FALSE,
);
$form['action'] = array(
'#value' => $action,
'#type' => 'value',
);
$i = 0;
$connectors = _connector_get_connectors();
if ($account && $account->uid != 0) {
$callback = 'connect button callback';
}
else {
$callback = 'button callback';
}
foreach ($connectors as $key => $connector) {
if (user_access('connect with ' . $connector['name']) && isset($connector[$callback]) && is_callable($connector[$callback])) {
$form[$key] = array(
'#type' => 'submit',
'#value' => t($label, array(
'!title' => $connector['title'],
)),
'#submit' => array(
$connector[$callback],
),
'connector' => array(
'#type' => 'value',
'#value' => $connector,
),
);
$form['#has_buttons'] = TRUE;
}
}
return $form;
}
function connector_actions($action_name) {
static $connector_actions;
if (!isset($connector_actions)) {
$connector_actions = (array) module_invoke_all('connector_action');
foreach (array_keys($connector_actions) as $key) {
if (!isset($connector_actions[$key]['name'])) {
$connector_actions[$key]['name'] = $key;
}
}
drupal_alter('connector_action', $connector_actions);
}
if ($action_name) {
if (array_key_exists($action_name, $connector_actions)) {
return $connector_actions[$action_name];
}
else {
return FALSE;
}
}
return $connector_actions;
}
function connector_connector_action() {
return array(
'default' => array(
'login callback' => '_connector_log_in',
'create account callback' => '_connector_create_account',
'add connection callback' => '_connector_add_connection',
'no external uid' => NULL,
),
);
}
function _connector_get_connectors($connector = NULL) {
static $connectors;
if (!isset($connectors)) {
$connectors = (array) module_invoke_all('connector');
// Make sure all connectors has a reference to their own name
foreach (array_keys($connectors) as $key) {
if (!isset($connectors[$key]['name'])) {
$connectors[$key]['name'] = $key;
}
}
drupal_alter('connector', $connectors);
}
if ($connector) {
if (array_key_exists($connector, $connectors)) {
return $connectors[$connector];
}
else {
return FALSE;
}
}
return $connectors;
}
function _connector_get_user_connections($uid) {
$connectors = array();
if (is_object($uid)) {
$uid = $uid->uid;
}
$result = db_query("SELECT authname FROM {authmap} WHERE module = :module AND uid = :uid", array(
':module' => 'connector',
':uid' => $uid,
));
foreach ($result as $row) {
$row = explode('__', $row->authname, 2);
if (count($row) === 2) {
$connectors[] = (object) array(
'connector' => $row[0],
'cid' => $row[1],
);
}
}
return $connectors;
}
function _connector_get_primary_connection($uid) {
$result = FALSE;
if (is_object($uid)) {
$uid = $uid->uid;
}
$primary_connection = db_query("SELECT primary_connection FROM {connector_user} WHERE uid = :uid", array(
':uid' => $uid,
))
->fetchField();
if ($primary_connection) {
$row = explode('__', $primary_connection, 2);
if (count($row) === 2) {
$result = (object) array(
'connector' => $row[0],
'cid' => $row[1],
);
}
}
return $result;
}
function _connector_set_primary_connection($uid, $connection) {
if (is_object($uid)) {
$uid = $uid->uid;
}
if (is_object($connection)) {
$connection = $connection->connector . '__' . $connection->cid;
}
db_delete('connector_user')
->condition('uid', $uid)
->execute();
db_insert('connector_user')
->fields(array(
'uid' => $uid,
'primary_connection' => $connection,
))
->execute();
}
function _connector_log_in($connector_name, $cid = NULL, $consumer = NULL, $access_token = NULL, $request_token = NULL) {
global $user;
if (user_is_logged_in()) {
return TRUE;
}
$connector = _connector_get_connectors($connector_name);
if (!$connector) {
return FALSE;
}
//Fetch connector ID
if ($cid === NULL && isset($connector['id callback']) && is_callable($connector['id callback'])) {
$cid = call_user_func($connector['id callback'], $connector);
}
if ($cid !== NULL) {
$authname = $connector_name . '__' . $cid;
$account = user_external_load($authname);
if (!$account) {
// Return NULL and not FALSE so that we know we didn't find a user.
return NULL;
}
if ($account) {
if ($account->status) {
if (variable_get('user_email_verification', FALSE) && !$account->login) {
// User still needs to verify the emailaddress.
drupal_set_message(t('Your account is currently pending e-mail verification. You have receveid a email with further instructions. !link to start a new e-mail verification.', array(
'!link' => l('Request a new password', 'user/password'),
)), 'warning');
if (isset($connector['logout callback']) && is_callable($connector['logout callback'])) {
call_user_func($connector['logout callback'], $connector, $connection->cid);
}
}
else {
//Log in user
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
return TRUE;
}
}
else {
drupal_set_message(t('Your account is currently pending approval by the site administrator.'), 'warning');
// why logout when account is pending? may be it is intentional.
if (isset($connector['logout callback']) && is_callable($connector['logout callback'])) {
call_user_func($connector['logout callback'], $connector, $connection->cid);
}
}
}
}
return FALSE;
}
function _connector_create_account($connector_name, $cid = NULL, $consumer = NULL, $access_token = NULL, $request_token = NULL) {
$connector = _connector_get_connectors($connector_name);
if (!$connector) {
return FALSE;
}
$authname = $connector_name . '__' . $cid;
if (variable_get('user_register', 1)) {
$userinfo = array(
'name' => $authname,
'pass' => user_password(),
'init' => $authname,
'status' => variable_get('user_register', 1) == 1,
'access' => REQUEST_TIME,
);
// Try to assign values from connection.
$info = array();
if (!empty($connector['information callback']) && is_callable($connector['information callback'])) {
$info = $connector['information callback']($connector, $cid, array(), $access_token);
}
$allowed_fields = array(
'name',
'mail',
);
foreach ($info as $field) {
if (isset($field['sync']) && $field['sync'] && in_array($field['sync'], $allowed_fields)) {
$exists = db_select('users', 'u')
->fields('u')
->condition($field['sync'], $field['value'])
->execute()
->rowCount();
if ($exists < 1) {
$userinfo[$field['sync']] = $field['value'];
}
}
}
$new_account = user_save('', $userinfo);
// Terminate if an error occured during user_save().
if (!$new_account) {
drupal_set_message(t("Error saving user account."), 'error');
}
else {
watchdog('user', 'New external user: %name using module %module.', array(
'%name' => $authname,
'%module' => 'connector',
), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $new_account->uid . '/edit'));
return $new_account;
}
}
else {
drupal_set_message(t('Only site administrators can create new user accounts.'), 'error');
if (isset($connector['logout callback']) && is_callable($connector['logout callback'])) {
call_user_func($connector['logout callback'], $connector, $connection->cid);
}
}
}
function _connector_add_connection($connector_name, $cid = NULL, $uid = NULL) {
global $user;
$connector = _connector_get_connectors($connector_name);
if (!$connector) {
return FALSE;
}
$result = FALSE;
if (empty($uid)) {
$uid = $user->uid;
}
elseif (is_object($uid)) {
$uid = $uid->uid;
}
//Fetch connector ID
if ($cid === NULL && isset($connector['id callback']) && is_callable($connector['id callback'])) {
$cid = call_user_func($connector['id callback'], $connector);
}
// Check that we have an external id to connect with
if ($cid !== NULL) {
$authname = $connector_name . '__' . $cid;
$account = user_external_load($authname);
// Check if the external id already connected to someone
if ($account) {
db_delete('authmap')
->condition('uid', $account->uid)
->condition('authname', $authname)
->execute();
}
$result = (bool) db_insert('authmap')
->fields(array(
'uid' => $uid,
'authname' => $authname,
'module' => 'connector',
))
->execute();
if (!_connector_get_primary_connection($uid)) {
_connector_set_primary_connection($uid, $authname);
}
}
return $result;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function theme_connector_buttons($variables) {
$form = $variables['form'];
if (!$form['#has_buttons']) {
return NULL;
}
$output = '';
$buttons = array();
foreach (element_children($form) as $key) {
if ($form[$key]['#type'] == 'submit') {
$class = str_replace('_', '-', $key);
$form[$key]['#attributes']['class'][] = 'connector-button';
$form[$key]['#attributes']['class'][] = $class;
$form[$key]['#prefix'] = '<span class="connector-button-wrapper ' . $class . '">';
$form[$key]['#suffix'] = '</span>';
}
}
return drupal_render_children($form);
}
Functions
Name | Description |
---|---|
connector_actions | |
connector_block_info | Implements hook_block_info(). |
connector_block_view | Implements hook_block_view(). |
connector_button_form | Form for the connector buttons. |
connector_connector_action | |
connector_menu | Implements hook_menu(). |
connector_permission | Implements hook_permission(). |
connector_redirect_access | Access callback for the direct url |
connector_redirect_callback | Menu callback for the direct url. |
connector_theme | Implements hook_theme(). |
connector_user_cancel | Implements hook_user_cancel(). |
connector_user_logout | Implements hook_user_logout(). |
connector_user_settings_access_callback | |
connector_user_sync_access_callback | |
theme_connector_buttons | @todo Please document this function. |
_connector_add_connection | |
_connector_create_account | |
_connector_get_connectors | |
_connector_get_primary_connection | |
_connector_get_user_connections | |
_connector_log_in | |
_connector_set_primary_connection |