usermerge.module in User Merge 7.2
Same filename and directory in other branches
Main file for the User Merge module.
File
usermerge.moduleView source
<?php
/**
* @file
* Main file for the User Merge module.
*
*/
/**
* Implements hook_permission().
*/
function usermerge_permission() {
return array(
'merge accounts' => array(
'title' => t('Merge accounts'),
),
'administer usermerge settings' => array(
'title' => t('Administer User Merge settings'),
),
);
}
/**
* Implements hook_menu().
*/
function usermerge_menu() {
$items['admin/people/merge'] = array(
'title' => 'Merge accounts',
'page callback' => 'usermerge_page',
'access callback' => 'user_access',
'access arguments' => array(
'merge accounts',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/people/usermerge'] = array(
'title' => 'User Merge settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'usermerge_settings',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer usermerge settings',
),
);
return $items;
}
/**
* Loads includes before calling drupal_get_form().
*
* @see usermerge_merge_form()
*/
function usermerge_page() {
return drupal_get_form('usermerge_merge_form');
}
/**
* Form to collect the two user IDs.
*/
function usermerge_merge_form($form, &$form_state) {
if (!empty($form_state['form_page']) && $form_state['form_page'] == 'review_table') {
return usermerge_data_review_form($form, $form_state);
}
// This is necessary, otherwise the title will show up as "People"
drupal_set_title(t('Merge accounts'));
$form['supported_actions'] = array(
'#type' => 'fieldset',
'#title' => t('Supported actions'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['supported_actions']['list'] = array(
'#theme' => 'item_list',
'#type' => 'ul',
);
$supported_actions = usermerge_invoke_all('usermerge_actions_supported', array());
ksort($supported_actions);
$form['supported_actions']['list']['#items'] = $supported_actions;
$form['general']['usermerge_user_delete'] = array(
'#type' => 'textfield',
'#title' => t('The name of the account you wish to remove'),
'#autocomplete_path' => 'user/autocomplete',
'#required' => TRUE,
);
$form['general']['usermerge_user_keep'] = array(
'#type' => 'textfield',
'#title' => t('The name of the account you wish to keep'),
'#autocomplete_path' => 'user/autocomplete',
'#required' => TRUE,
);
$form['general']['usermerge_user_delete_action'] = array(
'#type' => 'select',
'#title' => t('Action to perform on the account you wish to remove'),
'#options' => array(
'block' => t('Block'),
'delete' => t('Delete'),
),
'#default_value' => 'block',
);
$form['general']['submit'] = array(
'#type' => 'submit',
'#value' => t('Review account data'),
'#submit' => array(
'usermerge_merge_form_submit',
),
);
return $form;
}
/**
* Form that allows the selection of data to be merged.
*/
function usermerge_data_review_form($form, &$form_state) {
drupal_set_title(t('Merge accounts: review account data'));
$user_to_delete = $form_state['values']['user_to_delete'];
$user_to_keep = $form_state['values']['user_to_keep'];
$action = $form_state['page_values']['merge_form']['usermerge_user_delete_action'];
// Invoke hook_usermerge_account_properties
$account_properties = usermerge_invoke_all('usermerge_account_properties', $user_to_delete, $user_to_keep, $action);
// Allow modules to alter pre-existing account properties
drupal_alter('usermerge_account_properties', $account_properties, $user_to_delete, $user_to_keep, $action);
$form['review'] = usermerge_invoke_all('usermerge_build_review_form_elements', array(), $account_properties, $user_to_delete, $user_to_keep);
$form['review']['#type'] = 'container';
$form['review']['#tree'] = TRUE;
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Merge accounts'),
);
$form['cancel'] = array(
'#type' => 'markup',
'#markup' => l(t('Cancel'), 'admin/people/merge'),
);
return $form;
}
/**
* Implements hook_theme().
*/
function usermerge_theme() {
$theme['usermerge_data_review_form_table'] = array(
'render element' => 'element',
);
return $theme;
}
/**
* Returns HTML for each data-review table.
*/
function theme_usermerge_data_review_form_table($variables) {
$element = $variables['element'];
$table = array(
'header' => array(
'property_name' => isset($element['#attributes']['property_label']) ? $element['#attributes']['property_label'] : t('Property'),
'user_to_delete' => t('User to delete'),
'user_to_keep' => t('User to keep'),
),
);
if (!in_array('no_merge', $element['#attributes'])) {
$table['header']['merge'] = t('Merge');
}
foreach (element_children($element) as $id) {
$table['rows'][$id]['property_name'] = '<strong>' . drupal_render($element[$id]['property_name']) . '</strong>';
$table['rows'][$id]['user_to_delete'] = drupal_render($element[$id]['options']['user_to_delete']);
$table['rows'][$id]['user_to_keep'] = drupal_render($element[$id]['options']['user_to_keep']);
if (isset($table['header']['merge'])) {
if (isset($element[$id]['options']['merge'])) {
// Case 'force_select' or 'no_merge'
$table['rows'][$id]['merge'] = '';
// Case 'merge'
if ($element[$id]['options']['#options']['merge'] == 'merge') {
$element[$id]['options']['merge']['#title'] = t('Merge data');
$table['rows'][$id]['merge'] = drupal_render($element[$id]['options']['merge']);
}
}
}
}
return empty($table['rows']) ? '' : '<h3>' . $element['#title'] . "</h3>\n" . (isset($element['#description']) ? "<p>" . $element['#description'] . "</p>\n" : '') . theme('table', $table);
}
/**
* Validates contents of form for user validation.
*/
function usermerge_merge_form_validate($form, &$form_state) {
// Validate first step
if (!isset($form_state['form_page'])) {
// Can't be the same user.
if ($form_state['values']['usermerge_user_keep'] == $form_state['values']['usermerge_user_delete']) {
form_set_error('usermerge_user', 'You cannot have the same username in both fields.');
}
$user_to_delete = user_load_multiple(array(), array(
'name' => $form_state['values']['usermerge_user_delete'],
));
$user_to_keep = user_load_multiple(array(), array(
'name' => $form_state['values']['usermerge_user_keep'],
));
// Load up the users. We store these in the form_state['values'] so that we don't have to load again later.
$form_state['values']['user_to_delete'] = array_shift($user_to_delete);
$form_state['values']['user_to_keep'] = array_shift($user_to_keep);
// Use the validate helper function.
usermerge_validate_merge($form_state['values']['user_to_delete'], $form_state['values']['user_to_keep']);
}
}
/**
* Helper validation function used by the form submit and the API.
*
* @param $user_to_delete
* Object of user to be deleted
* @param $user_to_keep
* Object of user to be kept
*/
function usermerge_validate_merge($user_to_delete, $user_to_keep) {
$valid = TRUE;
if (empty($user_to_delete->uid)) {
form_set_error('usermerge_user_delete', t('This user name does not exist.'));
$valid = FALSE;
}
elseif ($user_to_delete->uid == 1) {
form_set_error('usermerge_user_delete', t('Blocking or deleting user 1 is not allowed.'));
$valid = FALSE;
}
if (empty($user_to_keep->uid)) {
form_set_error('usermerge_user_keep', t('This user name does not exist.'));
$valid = FALSE;
}
return $valid;
}
/**
* Displays the userdata-review form, or merges the selected accounts.
*/
function usermerge_merge_form_submit($form, &$form_state) {
if (!isset($form_state['form_page'])) {
$form_state['page_values']['merge_form'] = $form_state['values'];
if (!empty($form_state['page_values']['review_table'])) {
$form_state['values'] = $form_state['page_values']['review_table'];
}
$form_state['form_page'] = 'review_table';
$form_state['rebuild'] = TRUE;
}
else {
$form_state['values']['usermerge_user_delete_action'] = $form_state['page_values']['merge_form']['usermerge_user_delete_action'];
usermerge_merge_accounts($form_state['page_values']['merge_form']['user_to_delete'], $form_state['page_values']['merge_form']['user_to_keep'], $form_state['values']);
}
}
/**
* Merges the selected accounts.
*
* @param object $user_to_delete
* A fully loaded user object from user_load() that will be merged and blocked or deleted.
* @param object $user_to_keep
* A fully loaded user object from user_load() that will be merged and retained.
* @param $form_values
* The values from the submitted data-review form.
* @return boolean
* TRUE if they are merged, FALSE if the validation failed.
*/
function usermerge_merge_accounts($user_to_delete, $user_to_keep, $form_values) {
if (usermerge_validate_merge($user_to_delete, $user_to_keep)) {
$merged_account = usermerge_invoke_all('usermerge_merge_accounts', $user_to_delete, $user_to_keep, $form_values['review']);
// This allows modules to modify the array without merging values
drupal_alter('usermerge_merge_accounts', $merged_account, $user_to_delete, $user_to_keep);
$merged_account = (object) $merged_account;
// Save $merged_account
$merged_account->pass = $user_to_keep->pass;
$merged_account->status = 1;
// Block or delete old account. Do this before saving the new account in
// case the merged account is using the deleted account's mail or name.
switch ($form_values['usermerge_user_delete_action']) {
case 'block':
user_block_user_action($user_to_delete);
break;
case 'delete':
user_delete($user_to_delete->uid);
break;
}
// Save merged account
user_save($merged_account);
drupal_set_message(t('%user_to_delete was successfully merged into %user_to_keep', array(
'%user_to_delete' => $user_to_delete->name,
'%user_to_keep' => $user_to_keep->name,
)));
return TRUE;
}
else {
return FALSE;
}
}
/**
* Merges two accounts automatically, making decisions on which data should
* be preserved.
*
* Modules that need to implement a straightforward way to merge accounts should
* use this function.
*
* @param object $user_to_delete
* A fully loaded user object from user_load() that will be merged and blocked or deleted.
* @param object $user_to_keep
* A fully loaded user object from user_load() that will be merged and retained.
*/
function usermerge_do($user_to_delete, $user_to_keep) {
// Build (but not display) data-review form
usermerge_load_includes();
$review_form_state['values']['user_to_delete'] = $user_to_delete;
$review_form_state['values']['user_to_keep'] = $user_to_keep;
// This allows to call the data-review table instead of the account-selection form
$review_form_state['form_page'] = 'review_table';
$review_form = usermerge_merge_form(array(), $review_form_state);
// Build form values
// This makes a few assumptions on modules' behavior. It would be advisable to
// make the User Merge API documentation clearer on what modules should do.
$review_form_state['page_values']['merge_form'] = $review_form_state['values'];
$review = array_intersect_key($review_form['review'], array_flip(element_children($review_form['review'])));
foreach ($review as $component => $items) {
if (isset($items['#value'])) {
$review_form_state['values'][$component] = $items['#value'];
}
else {
$items = array_intersect_key($items, array_flip(element_children($items)));
foreach ($items as $name => $properties) {
if (isset($properties['options']['#options'])) {
// If the property has a merge options, merge data
if (isset($properties['options']['#options']['merge']) && $properties['options']['#options']['merge'] == 'merge') {
$review_form_state['values']['review'][$component][$name]['options'] = 'merge';
}
else {
$review_form_state['values']['review'][$component][$name]['options'] = 'user_to_keep';
}
}
else {
// If there are no set options, check if the module sets other form fields
unset($properties['property_name'], $properties['options']);
if (count($properties)) {
foreach ($properties as $property_name => $property) {
if (isset($property['#value'])) {
$review_form_state['values']['review'][$component][$name][$property_name] = $property['#value'];
}
}
}
}
}
}
}
// Merge accounts
// Force-delete old account
$review_form_state['page_values']['merge_form']['usermerge_user_delete_action'] = 'delete';
drupal_form_submit('usermerge_merge_form', $review_form_state);
}
/**
* Implements hook_hook_info().
*/
function usermerge_hook_info() {
$hooks = array(
'usermerge_actions_supported' => array(
'group' => 'usermerge',
),
'usermerge_account_properties' => array(
'group' => 'usermerge',
),
'usermerge_account_properties_alter' => array(
'group' => 'usermerge',
),
'usermerge_build_review_form_elements' => array(
'group' => 'usermerge',
),
'usermerge_merge_accounts' => array(
'group' => 'usermerge',
),
'usermerge_merge_accounts_alter' => array(
'group' => 'usermerge',
),
);
return $hooks;
}
/**
* Calls all includes.
*
* Ideally, this function should call only supplemental includes provided by
* the module itself, but supplemental includes conflict with includes provided
* by other modules, making the whole module unreliable quite randomly.
*/
function usermerge_load_includes() {
foreach (module_list() as $module) {
// Load MODULE.usermerge.inc files
if (!($file = module_load_include('inc', $module, $module . '.usermerge'))) {
// Load supplemental includes only if the module doesn't provide its own implementation
$file = module_load_include('inc', 'usermerge', 'includes/' . $module . '.usermerge');
}
}
}
/**
* Invokes a hook in all enabled modules that implement it.
*
* Replaces module_invoke_all() and bypasses the cache.
*/
function usermerge_invoke_all($hook) {
usermerge_load_includes();
$args = func_get_args();
// Remove $hook from the arguments.
unset($args[0]);
$return = array();
foreach (usermerge_module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$result = call_user_func_array($function, $args);
if (isset($result) && is_array($result)) {
$return = array_merge_recursive($return, $result);
}
elseif (isset($result)) {
$return[] = $result;
}
}
}
return $return;
}
/**
* Determines which modules are implementing a hook.
*
* Replaces module_implements().
* Does not need to load includes, because they should all have already
* been loaded in usermerge_invoke_all().
*/
function usermerge_module_implements($hook) {
$modules = array();
foreach (module_list() as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$modules[] = $module;
}
}
return $modules;
}
/**
* Form builder for User Merge settings.
*/
function usermerge_settings() {
$usermerge_settings = variable_get('usermerge_settings', usermerge_settings_default());
$core_properties = usermerge_get_user_core_properties();
$form['core'] = array(
'#type' => 'fieldset',
'#title' => t('Core properties'),
'#description' => t('By default, the module handles core properties automatically, by keeping the value from the user to keep. This is why most of them do not appear in the review table. If you want to display some core properties in the review table, enable them here by checking the relevant boxes.'),
'#tree' => TRUE,
);
foreach ($core_properties as $property) {
// For some properties there is never a choice
if (!in_array($property, array(
'uid',
'created',
'access',
'login',
'pass',
'init',
'data',
'roles',
))) {
$form['core'][$property] = array(
'#type' => 'checkbox',
'#title' => $property,
// No need to localize this
'#default_value' => !empty($usermerge_settings['core'][$property]) ? $usermerge_settings['core'][$property] : 0,
);
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Submit handler for the User Merge settings form.
*/
function usermerge_settings_submit($form, &$form_state) {
form_state_values_clean($form_state);
foreach ($form_state['values'] as $type => $values) {
$usermerge_settings[$type] = $values;
}
variable_set('usermerge_settings', $usermerge_settings);
drupal_set_message('The User Merge settings have been saved.');
}
/**
* Default value for the User Merge settings variable.
*/
function usermerge_settings_default() {
static $default;
if (empty($default)) {
$core_properties = usermerge_get_user_core_properties();
foreach ($core_properties as $property) {
if (!in_array($property, array(
'uid',
'created',
'access',
'login',
'pass',
'name',
'mail',
'init',
'data',
'roles',
))) {
$default['core'][$property] = 0;
}
}
}
return $default;
}
/**
* Returns an array of the core properties for the user entity.
*/
function usermerge_get_user_core_properties() {
static $properties;
if (empty($properties)) {
// Define list of fields and other user data
// Using the columns in the user table, so non-field data added from other modules doesn't get mixed in
$user_entity_info = entity_get_info('user');
$properties = $user_entity_info['schema_fields_sql']['base table'];
// Adding roles
$properties[] = 'roles';
}
return $properties;
}
Functions
Name![]() |
Description |
---|---|
theme_usermerge_data_review_form_table | Returns HTML for each data-review table. |
usermerge_data_review_form | Form that allows the selection of data to be merged. |
usermerge_do | Merges two accounts automatically, making decisions on which data should be preserved. |
usermerge_get_user_core_properties | Returns an array of the core properties for the user entity. |
usermerge_hook_info | Implements hook_hook_info(). |
usermerge_invoke_all | Invokes a hook in all enabled modules that implement it. |
usermerge_load_includes | Calls all includes. |
usermerge_menu | Implements hook_menu(). |
usermerge_merge_accounts | Merges the selected accounts. |
usermerge_merge_form | Form to collect the two user IDs. |
usermerge_merge_form_submit | Displays the userdata-review form, or merges the selected accounts. |
usermerge_merge_form_validate | Validates contents of form for user validation. |
usermerge_module_implements | Determines which modules are implementing a hook. |
usermerge_page | Loads includes before calling drupal_get_form(). |
usermerge_permission | Implements hook_permission(). |
usermerge_settings | Form builder for User Merge settings. |
usermerge_settings_default | Default value for the User Merge settings variable. |
usermerge_settings_submit | Submit handler for the User Merge settings form. |
usermerge_theme | Implements hook_theme(). |
usermerge_validate_merge | Helper validation function used by the form submit and the API. |