usermerge.usermerge.inc in User Merge 7.2
Implements User merge hooks for core-related user properties.
File
usermerge.usermerge.incView source
<?php
/**
* @file
* Implements User merge hooks for core-related user properties.
*
*/
/**
* Implement hook_usermerge_actions_supported().
*/
function usermerge_usermerge_actions_supported() {
return array(
'a-core' => t('Choosing which user information (default properties and custom fields, if available) should be kept, discarded, or merged (this choice is not available for all properties).'),
'a-entities' => t('Assigning to the new user any entities (such as nodes and comments) previously associated with the old user.'),
);
}
/**
* Implement hook_usermerge_account_properties().
*/
function usermerge_usermerge_account_properties($user_to_delete, $user_to_keep, $action) {
$user_entity_properties['core'] = usermerge_get_user_core_properties();
// Define an array of properties that can be used to display the data-review table(s)
$account_properties = array(
'core' => array(
'title' => t('Core properties'),
'items' => array(),
),
);
// Define list of all user properties (including fields)
// Using $user_to_delete insures that all properties are accounted for
$user_all_properties = array_keys((array) $user_to_delete);
// Find custom fields
$user_entity_properties['fields'] = preg_grep("/^field_/", $user_all_properties);
if (count($user_entity_properties['fields'])) {
$account_properties['fields'] = array(
'title' => t('Fields'),
'description' => t('Please note that single-value fields cannot be merged.'),
'items' => array(),
);
}
// Find other user properties
$user_noncore_properties = array_diff($user_all_properties, $user_entity_properties['core']);
$user_entity_properties['other'] = array_diff($user_noncore_properties, $user_entity_properties['fields']);
if (count($user_entity_properties['other'])) {
$account_properties['other'] = array(
'title' => t('Other properties'),
'items' => array(),
);
}
foreach ($user_entity_properties as $type => $properties) {
foreach (array_flip($properties) as $property_name => $delta) {
// Exclude properties that shouldn't be merged, like password
// These could be defined via settings page
if (!in_array($property_name, array(
'pass',
))) {
$account_properties[$type]['items'][$property_name] = array(
'name' => $property_name,
'criterion' => 'merge',
);
}
}
}
// Set default choices for core properties
// This could be defined via settings page
$usermerge_settings = variable_get('usermerge_settings', usermerge_settings_default());
foreach ($usermerge_settings['core'] as $property_name => $setting_value) {
if (empty($setting_value)) {
// If the property is not checked in settings, set a default
$account_properties['core']['items'][$property_name]['default'] = $user_to_keep->{$property_name};
}
}
// Always show uid, mail, and name, regarless of whether they can be editted.
foreach (array(
'uid',
'mail',
'name',
) as $property_name) {
if (isset($account_properties['core']['items'][$property_name]['default']) || $action != 'delete') {
$account_properties['core']['items'][$property_name]['disabled'] = TRUE;
}
unset($account_properties['core']['items'][$property_name]['default']);
}
// Automatically handle properties that can't be exposed via settings:
// Choose older created date
if ($user_to_delete->created < $user_to_keep->created) {
$account_properties['core']['items']['created']['default'] = $user_to_delete->created;
}
else {
$account_properties['core']['items']['created']['default'] = $user_to_keep->created;
}
// Choose newer access date
if ($user_to_delete->access > $user_to_keep->access) {
$account_properties['core']['items']['access']['default'] = $user_to_delete->access;
}
else {
$account_properties['core']['items']['access']['default'] = $user_to_keep->access;
}
// Choose newer login date
if ($user_to_delete->login > $user_to_keep->login) {
$account_properties['core']['items']['login']['default'] = $user_to_delete->login;
}
else {
$account_properties['core']['items']['login']['default'] = $user_to_keep->login;
}
$account_properties['core']['items']['init']['default'] = $user_to_keep->init;
$account_properties['core']['items']['data']['default'] = $user_to_keep->data;
foreach ($account_properties['core']['items'] as $property_name => $settings) {
if ($property_name == 'roles') {
// Keep the ability to choose between roles of the two users, but set the default option to merge
$account_properties['core']['items'][$property_name]['default_option'] = 'merge';
}
else {
// All other properties should not have a "both" option
$account_properties['core']['items'][$property_name]['criterion'] = 'no_merge';
}
}
// Special settings for fields
if (!empty($account_properties['fields']['items'])) {
foreach ($account_properties['fields']['items'] as $field_name => $properties) {
$field_settings = field_info_field($field_name);
// If the field's cardinality is not 1, do not allow merging
// This could pose problems for fields whose cardinality is greater than one, but not unlimited
if ($field_settings['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
$account_properties['fields']['items'][$field_name]['criterion'] = 'force_select';
}
}
}
// Authored entitites
$account_properties['entities']['title'] = t('Authored entities');
foreach (usermerge_get_authorable_entities() as $entity_name => $entity) {
$account_properties['entities']['items'][$entity_name] = array(
'name' => $entity_name,
'criterion' => 'no_merge',
);
}
return $account_properties;
}
/**
* Implement hook_usermerge_build_review_form_elements().
*/
function usermerge_usermerge_build_review_form_elements($review, $account_properties, $user_to_delete, $user_to_keep) {
// Build form elements
// Set very low weight to keep properties controlled by this file at the top of the table
$i = -10000;
foreach ($account_properties as $property_type => $properties) {
// User properties
if (in_array($property_type, array(
'core',
'fields',
'other',
))) {
$review[$property_type] = array(
'#tree' => TRUE,
'#theme' => 'usermerge_data_review_form_table',
'#title' => $properties['title'],
'#weight' => $i,
);
if (isset($properties['description'])) {
$review[$property_type]['#description'] = $properties['description'];
}
if ($property_type == 'fields') {
$review[$property_type]['#attributes']['property_label'] = t('Field');
}
foreach ($properties['items'] as $property) {
$property_name = $property['name'];
// Properties with default behaviors will not be displayed
if (!array_key_exists('default', $property)) {
$review[$property_type][$property_name]['property_name'] = array(
'#type' => 'markup',
'#markup' => $property_name,
);
// Display property values
$field_user_to_delete = $user_to_delete->{$property_name};
$field_user_to_keep = $user_to_keep->{$property_name};
switch ($property_name) {
// Roles must be dealt with separately
case 'roles':
$field_user_to_delete = implode(', ', $user_to_delete->{$property_name});
$field_user_to_keep = implode(', ', $user_to_keep->{$property_name});
break;
case 'picture':
if ($user_to_delete->{$property_name}) {
$filepath_user_to_delete = image_style_url('thumbnail', $user_to_delete->{$property_name}->uri);
$field_user_to_delete = theme('image', array(
'path' => $filepath_user_to_delete,
));
}
if ($user_to_keep->{$property_name}) {
$filepath_user_to_keep = image_style_url('thumbnail', $user_to_keep->{$property_name}->uri);
$field_user_to_keep = theme('image', array(
'path' => $filepath_user_to_keep,
));
}
break;
}
// Process custom fields for display
if ($property_type == 'fields') {
if (is_array($field_user_to_delete)) {
$field_user_to_delete = field_view_field('user', $user_to_delete, $property_name, array(
'label' => 'hidden',
'settings' => array(),
));
$field_user_to_delete = strip_tags(drupal_render($field_user_to_delete), '<em> <strong> <ul> <li>');
}
if (is_array($field_user_to_keep)) {
$field_user_to_keep = field_view_field('user', $user_to_keep, $property_name, array(
'label' => 'hidden',
'settings' => array(),
));
$field_user_to_keep = strip_tags(drupal_render($field_user_to_keep), '<em> <strong> <ul> <li>');
}
}
$review[$property_type][$property_name]['options'] = array(
'#type' => 'radios',
'#options' => array(
'user_to_delete' => is_string($field_user_to_delete) ? $field_user_to_delete : '',
'user_to_keep' => is_string($field_user_to_keep) ? $field_user_to_keep : '',
),
);
if (isset($property['default_option'])) {
$review[$property_type][$property_name]['options']['#default_value'] = $property['default_option'];
}
else {
$review[$property_type][$property_name]['options']['#default_value'] = 'user_to_keep';
}
switch ($property['criterion']) {
case 'merge':
$review[$property_type][$property_name]['options']['#options']['merge'] = 'merge';
break;
case 'force_select':
// Nothing happens here
$review[$property_type][$property_name]['options']['#options']['merge'] = 'force_select';
break;
case 'no_merge':
$review[$property_type][$property_name]['options']['#options']['merge'] = 'no_merge';
// uid and (if not editable, mail and name) should be visible but disabled.
if (!empty($property['disabled'])) {
$review[$property_type][$property_name]['options']['#disabled'] = TRUE;
}
break;
}
if (isset($property['default_option'])) {
$review[$property_type][$property_name]['options']['#default_value'] = $property['default_option'];
}
}
else {
$account[$property_name] = $property['default'];
}
}
}
elseif ($property_type == 'entities') {
$content_user_to_delete = usermerge_query_authored_entities($properties['items'], $user_to_delete->uid);
$content_user_to_keep = usermerge_query_authored_entities($properties['items'], $user_to_keep->uid);
if (count($content_user_to_delete) || count($content_user_to_keep)) {
$review[$property_type] = array(
'#tree' => TRUE,
'#theme' => 'usermerge_data_review_form_table',
'#title' => $properties['title'],
'#attributes' => array(
'no_merge',
'property_label' => t('Entity'),
),
'#weight' => $i,
);
foreach ($content_user_to_delete as $entity_type => $entities) {
$review[$property_type][$entity_type]['property_name'] = array(
'#type' => 'markup',
'#markup' => $entity_type,
);
$review[$property_type][$entity_type]['options']['user_to_delete'] = array(
'#type' => 'markup',
'#markup' => format_plural(count($entities), '1 %entity entity to be reassigned', '@count %entity entities to be reassigned', array(
'%entity' => $entity_type,
)),
);
}
foreach ($content_user_to_keep as $entity_type => $entities) {
$review[$property_type][$entity_type]['property_name'] = array(
'#type' => 'markup',
'#markup' => $entity_type,
);
$review[$property_type][$entity_type]['options']['user_to_keep'] = array(
'#type' => 'markup',
'#markup' => format_plural(count($entities), '1 %entity entity to be maintained', '@count %entity entities to be maintained', array(
'%entity' => $entity_type,
)),
);
}
ksort($review[$property_type]);
}
}
// Increments weight
$i++;
}
if (isset($account)) {
$review['account'] = array(
'#type' => 'hidden',
'#value' => serialize($account),
);
}
return $review;
}
/**
* Selects entities with a uid column.
*/
function usermerge_get_authorable_entities() {
$entities = entity_get_info();
foreach ($entities as $entity_type => $entity) {
if (!in_array('uid', $entity['schema_fields_sql']['base table'])) {
unset($entities[$entity_type]);
}
}
// Exclude user entity
unset($entities['user']);
return $entities;
}
/**
* Selects entities whose uid matches the selected user.
*/
function usermerge_query_authored_entities($entities, $user_id) {
$found_entities = array();
foreach ($entities as $entity_type => $entity) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $entity_type)
->propertyCondition('uid', $user_id);
$result = $query
->execute();
if (count($result)) {
$found_entities[$entity_type] = $result[$entity_type];
}
}
drupal_alter('usermerge_query_authored_entities', $found_entities, $entities, $user_id);
return $found_entities;
}
/**
* Implements hook_usermerge_merge_accounts().
*/
function usermerge_usermerge_merge_accounts($user_to_delete, $user_to_keep, $review) {
$merged_account = unserialize($review['account']);
unset($review['account']);
// Process core, fields, other properties
foreach ($review as $property_type => $properties) {
if (in_array($property_type, array(
'core',
'fields',
'other',
))) {
foreach ($properties as $property_name => $options) {
if ($options['options'] != NULL) {
if ($options['options'] != 'merge') {
$chosen_account = $options['options'];
$merged_account[$property_name] = ${$chosen_account}->{$property_name};
}
else {
switch ($property_type) {
case 'core':
// Other modules can enforce merging of properties this module won't merge, so we won't object to that here
// This module deals natively only with roles
if ($property_name == 'roles') {
$merged_account[$property_name] = $user_to_delete->{$property_name} + $user_to_keep->{$property_name};
ksort($merged_account[$property_name]);
}
break;
case 'fields':
// $user_to_delete has data in the field
if (count($user_to_delete->{$property_name})) {
foreach ($user_to_delete->{$property_name} as $language => $items) {
// $user_to_keep has data in the field
if (isset($user_to_keep->{$property_name}[$language])) {
$merged_account[$property_name][$language] = array_merge($user_to_delete->{$property_name}[$language], $user_to_keep->{$property_name}[$language]);
}
else {
$merged_account[$property_name][$language] = $user_to_delete->{$property_name}[$language];
}
}
}
elseif (count($user_to_keep->{$property_name})) {
$merged_account[$property_name] = $user_to_keep->{$property_name};
}
break;
}
}
}
}
}
}
// Operate on entities
// Rebuild the list of entities here instead of passing as form values
$authorable_entities = usermerge_get_authorable_entities();
$entities_user_to_delete = usermerge_query_authored_entities($authorable_entities, $user_to_delete->uid);
// No need to find entities for $user_to_keep
foreach ($entities_user_to_delete as $entity_type => $entities) {
// We take for granted that the base table has a uid column, because of usermerge_get_authorable_entities()
// We also don't need to select specific entities, because we're replacing all the entities belonging to $user_to_delete
db_update($authorable_entities[$entity_type]['base table'])
->fields(array(
'uid' => $user_to_keep->uid,
))
->condition('uid', $user_to_delete->uid)
->execute();
// Check if the entity has a revision table
if (isset($authorable_entities[$entity_type]['revision table'])) {
// Find the correct column name
// Not all revision tables have the uid stored in uid (like node_revision)
// Some store it in revision_uid
// Could be taking a chance here
$uid_column = preg_grep("/uid/", $authorable_entities[$entity_type]['schema_fields_sql']['revision table']);
$uid_column = reset($uid_column);
db_update($authorable_entities[$entity_type]['revision table'])
->fields(array(
$uid_column => $user_to_keep->uid,
))
->condition($uid_column, $user_to_delete->uid)
->execute();
}
}
return $merged_account;
}
Functions
Name | Description |
---|---|
usermerge_get_authorable_entities | Selects entities with a uid column. |
usermerge_query_authored_entities | Selects entities whose uid matches the selected user. |
usermerge_usermerge_account_properties | Implement hook_usermerge_account_properties(). |
usermerge_usermerge_actions_supported | Implement hook_usermerge_actions_supported(). |
usermerge_usermerge_build_review_form_elements | Implement hook_usermerge_build_review_form_elements(). |
usermerge_usermerge_merge_accounts | Implements hook_usermerge_merge_accounts(). |