From 5849cfa230275722e9f0491c7c913105b3d9df8f Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Thu, 10 Mar 2011 04:22:42 +0100
Subject: [PATCH 01/13] Issue #924968 by plach, das-peter, sun, fago, klonos: Introduced field replacement API and UI

---
 title.core.inc |  149 ++++++++++++++++++++
 title.info     |    5 +-
 title.install  |   12 ++-
 title.module   |  426 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 587 insertions(+), 5 deletions(-)
 create mode 100644 title.core.inc

diff --git a/title.core.inc b/title.core.inc
new file mode 100644
index 0000000..bee572b
--- /dev/null
+++ b/title.core.inc
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @file
+ * TODO
+ */
+
+/**
+ * Provide core legacy fields replacement info.
+ */
+function _title_field_replacement_core_info() {
+  $info = array();
+
+  $field = array(
+    'type' => 'text',
+    'cardinality' => 1,
+    'translatable' => TRUE,
+  );
+
+  $instance = array(
+    'required' => TRUE,
+    'settings' => array(
+      'text_processing' => 0,
+    ),
+    'widget' => array(
+      'weight' => -5,
+    ),
+  );
+
+  $info['node'] = array(
+    'field replacement' => array(
+      'title' => array(
+        'field' => $field,
+        'instance' => array(
+          'label' => t('Title'),
+          'description' => t('A field replacing node title.'),
+        ) + $instance,
+      ),
+    ),
+  );
+
+  $info['taxonomy_term'] = array(
+    'field replacement' => array(
+      'name' => array(
+        'field' => $field,
+        'instance' => array(
+          'label' => t('Name'),
+          'description' => t('A field replacing taxonomy term name.'),
+        ) + $instance,
+      ),
+      'description' => array(
+        'field' => array(
+          'type' => 'text_with_summary',
+        ) + $field,
+        'instance' => array(
+          'required' => FALSE,
+          'label' => t('Description'),
+          'description' => t('A field replacing taxonomy term description.'),
+          'settings' => array(
+            'text_processing' => 1,
+          ),
+        ) + $instance,
+        'callbacks' => array(
+          'submit' => 'title_field_term_description_submit',
+        ),
+        'additional keys' => array(
+          'format' => 'format',
+        ),
+      ),
+    ),
+  );
+
+  $info['comment'] = array(
+    'field replacement' => array(
+      'subject' => array(
+        'field' => $field,
+        'instance' => array(
+          'label' => t('Subject'),
+          'description' => t('A field replacing comment subject.'),
+        ) + $instance,
+      ),
+    ),
+  );
+
+  $info['user'] = array(
+    'field replacement' => array(
+      'name' => array(
+        'field' => $field,
+        'instance' => array(
+          'label' => t('Name'),
+          'description' => t('A field replacing user name.'),
+        ) + $instance,
+      ),
+    ),
+  );
+
+  return $info;
+}
+
+/**
+ * TODO
+ */
+function title_field_term_description_submit(&$values, $legacy_field, $info, $langcode) {
+  $values['description'] = array();
+  foreach (array('value', 'format') as $key) {
+    $values['description'][$key] = $values[$info['field']['field_name']][$langcode][0][$key];
+  }
+}
+
+/**
+ * TODO
+ */
+function title_field_text_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
+  $wrapper = entity_metadata_wrapper($entity_type, $entity);
+  $wrapper->language($langcode);
+  $entity->{$legacy_field} = $wrapper->{$info['field']['field_name']}->raw();
+}
+
+/**
+ * TODO
+ */
+function title_field_text_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
+  $wrapper = entity_metadata_wrapper($entity_type, $entity);
+  $wrapper->language($langcode);
+  $wrapper->{$info['field']['field_name']}->set($entity->{$legacy_field});
+}
+
+/**
+ * TODO
+ */
+function title_field_text_with_summary_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
+  $format_key = $info['additional keys']['format'];
+  $wrapper = entity_metadata_wrapper($entity_type, $entity);
+  $wrapper->language($langcode);
+  $field_name = $info['field']['field_name'];
+  $entity->{$legacy_field} = $wrapper->{$field_name}->value->raw();
+  $entity->{$format_key} = $wrapper->{$field_name}->format->raw();
+}
+
+/**
+ * TODO
+ */
+function title_field_text_with_summary_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
+  $format_key = $info['additional keys']['format'];
+  $value = array('value' => $entity->{$legacy_field}, 'format' => $entity->{$format_key});
+  $wrapper = entity_metadata_wrapper($entity_type, $entity);
+  $wrapper->language($langcode);
+  $wrapper->{$info['field']['field_name']}->set($value);
+}
diff --git a/title.info b/title.info
index 8f642a7..e7f43e1 100644
--- a/title.info
+++ b/title.info
@@ -1,7 +1,6 @@
 name = Title
-description = Allows entity titles/labels to be translated.
-package = Multilingual
+description = Replaces entity legacy fields with regular fields.
 core = 7.x
-dependencies[] = locale
 files[] = title.module
 files[] = tests/title.test
+dependencies[] = entity
diff --git a/title.install b/title.install
index 35cabb8..b60b8c5 100644
--- a/title.install
+++ b/title.install
@@ -2,6 +2,16 @@
 
 /**
  * @file
- * Installation functions for Title module.
+ * Installation functions for the Title module.
  */
 
+/**
+ * Implements hook_install().
+ */
+function title_install() {
+  // Make sure fields are properly handled before any other module access them.
+  db_update('system')
+    ->fields(array('weight' => -100))
+    ->condition('name', 'title')
+    ->execute();
+}
diff --git a/title.module b/title.module
index 2c20b56..2e74b69 100644
--- a/title.module
+++ b/title.module
@@ -2,6 +2,430 @@
 
 /**
  * @file
- * Translatable entity title/label functionality.
+ * Replaces entity legacy fields with regular fields.
+ *
+ * Provides an API and a basic UI to replace legacy pseudo-fields with regular
+ * fields. The API only offers synchronization between the two data storage
+ * systems and data replacement on entity load/save. Field definitions have to
+ * be provided by the modules exploiting the API.
+ *
+ * Title implements its own entity description API to describe core legacy
+ * pseudo-fields:
+ * - Node: title
+ * - Taxonomy Term: name, description
+ * - Comment: subject
+ * - User: name
+ *
+ * @todo: tests
+ * @todo: entity insert/update sync
  */
 
+module_load_include('inc', 'title', 'title.core');
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function title_entity_info_alter(&$info) {
+  $info = array_merge_recursive($info, _title_field_replacement_core_info());
+
+  foreach ($info as $entity_type => $entity_info) {
+    if ($entity_info['fieldable'] && !empty($info[$entity_type]['field replacement'])) {
+      foreach ($info[$entity_type]['field replacement'] as $legacy_field => $data) {
+        // Provide defaults for the replacing field name.
+        $fr_info = &$info[$entity_type]['field replacement'][$legacy_field];
+        if (empty($fr_info['field']['field_name'])) {
+          $fr_info['field']['field_name'] = $legacy_field . '_field';
+        }
+        $fr_info['instance']['field_name'] = $fr_info['field']['field_name'];
+
+        // Provide defaults for the sync callbacks.
+        $type = $fr_info['field']['type'];
+        if (empty($fr_info['callbacks'])) {
+          $fr_info['callbacks'] = array();
+        }
+        $fr_info['callbacks'] += array(
+          'sync' => "title_field_{$type}_sync",
+          'sync_back' => "title_field_{$type}_sync_back",
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Return field replacement specific information.
+ */
+function title_field_replacement_info($entity_type, $legacy_field = NULL) {
+  $info = entity_get_info($entity_type);
+  if (empty($info['field replacement'])) {
+    return FALSE;
+  }
+  return isset($legacy_field) ? $info['field replacement'][$legacy_field] : $info['field replacement'];
+}
+
+/**
+ * Implements hook_entity_presave().
+ */
+function title_entity_presave($entity, $type) {
+  title_entity_sync($type, $entity, NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_attach_load().
+ *
+ * Synchronization must be performed as early as possible to prevent other code
+ * from accessing replaced fields before they get their actual value.
+ *
+ * @see title_entity_load()
+ */
+function title_field_attach_load($entity_type, $entities, $age, $options) {
+  // @todo: Do we need to handle revisions here?
+  title_entity_load($entities, $entity_type);
+}
+
+/**
+ * Implements hook_entity_load().
+ *
+ * Since the result of field_attach_load() is cached, synchronization must be
+ * performed also here to ensure that there is always the correct value in the
+ * replaced fields.
+ */
+function title_entity_load($entities, $type) {
+  foreach ($entities as &$entity) {
+    title_entity_sync($type, $entity);
+  }
+}
+
+/**
+ * Implements hook_entitycache_load().
+ *
+ * Entity cache might cache the entire $entity object, in which case
+ * synchronization will not be performed on entity load.
+ */
+function title_entitycache_load($entities, $type) {
+  foreach ($entities as &$entity) {
+    title_entity_sync($type, $entity);
+  }
+}
+
+/**
+ * Implements hook_entity_prepare_view().
+ *
+ * On load synchronization is performed using the current display language. A
+ * different language might be specified while viewing the entity in which case
+ * synchronization must be performed again.
+ *
+ * FIXME: $langcode is not passed along by entity_prepare_view() currently. We
+ * will need to remove the default NULL value once this is fixed.
+ * @see http://drupal.org/node/1089174
+ */
+function title_entity_prepare_view($entities, $type, $langcode = NULL) {
+  foreach ($entities as &$entity) {
+    title_entity_sync($type, $entity, $langcode);
+  }
+}
+
+/**
+ * Check whether field replacement is enabled for the given field.
+ *
+ * @param $entity_type
+ *   The type of $entity.
+ * @param $bundle
+ *   The bundle the legacy field belongs to.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ *
+ * @return
+ *   TRUE if field replacement is enabled for the given field, FALSE otherwise.
+ */
+function title_field_replacement_enabled($entity_type, $bundle, $legacy_field) {
+  $info = title_field_replacement_info($entity_type, $legacy_field);
+  $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle);
+  return !empty($instance);
+}
+
+/**
+ * Synchronize replaced fields with the regular field values.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $entity
+ *   The entity to work with.
+ * @param $back
+ *   Specifies the direction synchronization must be performed.
+ */
+function title_entity_sync($entity_type, &$entity, $langcode = NULL, $back = FALSE) {
+  $sync = &drupal_static(__FUNCTION__, array());
+  list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
+  $langcode = field_valid_language($langcode, FALSE);
+
+  // We do not need to perform this more than once.
+  if (!empty($sync[$entity_type][$id][$langcode][$back])) {
+    return;
+  }
+
+  $sync[$entity_type][$id][$langcode][$back] = TRUE;
+  $fr_info = title_field_replacement_info($entity_type);
+
+  if ($fr_info) {
+    foreach ($fr_info as $legacy_field => $info) {
+      if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
+        $function = $back ? 'title_field_sync_back' : 'title_field_sync';
+        $function($entity_type, $entity, $legacy_field, $info, $langcode);
+      }
+    }
+  }
+}
+
+/**
+ * Synchronize a single legacy field with its regular field value.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $entity
+ *   The entity to work with.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ * @param $field_name
+ *   The regular field to use as source value.
+ * @param $display
+ *   Specifies if synchronization is being performed on display or on save.
+ * @param $langcode
+ *   The field language to use for the source value.
+ */
+function title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode = NULL) {
+  if (isset($entity->{$legacy_field})) {
+    // Find out the actual language to use (field might be untranslatable).
+    $langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode);
+    $info['callbacks']['sync']($entity_type, $entity, $legacy_field, $info, $langcode);
+  }
+}
+
+/**
+ * TODO
+ */
+function title_field_sync_back($entity_type, $entity, $legacy_field, $info) {
+  if (isset($entity->{$legacy_field})) {
+    $langcode = title_entity_language($entity_type, $entity);
+    $info['callbacks']['sync_back']($entity_type, $entity, $legacy_field, $info, $langcode);
+  }
+}
+
+/**
+ * TODO
+ */
+function title_entity_language($entity_type, $entity) {
+  // If a language property is defined for the current entity we synchronize
+  // the field value using the entity language, otherwise we fall back to
+  // LANGUAGE_NONE.
+  try {
+    return entity_metadata_wrapper($entity_type, $entity)->language->value();
+  }
+  catch (EntityMetadataWrapperException $e) {
+    return LANGUAGE_NONE;
+  }
+}
+
+/**
+ * Implements hook_field_attach_form().
+ *
+ * Hide legacy field widgets on the assumption that this is always called on
+ * fieldable entity forms.
+ */
+function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+  $fr_info = title_field_replacement_info($entity_type);
+
+  if ($fr_info) {
+    foreach ($fr_info as $legacy_field => $info)  {
+      if (isset($form[$legacy_field]) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
+        $form[$legacy_field]['#access'] = FALSE;
+        $form_state['field_replacement'][] = $legacy_field;
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_attach_submit().
+ *
+ * Synchronize submitted field values into the corresponding legacy fields.
+ */
+function title_field_attach_submit($entity_type, $entity, $form, &$form_state) {
+  if (!empty($form_state['field_replacement'])) {
+    $values = &$form_state['values'];
+    $values = &drupal_array_get_nested_value($values, $form['#parents']);
+    $form = drupal_array_get_nested_value($form, $form['#parents']);
+    $fr_info = title_field_replacement_info($entity_type);
+
+    foreach ($form_state['field_replacement'] as $legacy_field) {
+      $info = $fr_info[$legacy_field];
+      $field_name = $info['field']['field_name'];
+      $langcode = $form[$field_name]['#language'];
+
+      // Give a chance to operate on submitted values either.
+      if (!empty($info['callbacks']['submit'])) {
+        $info['callbacks']['submit']($values, $legacy_field, $info, $langcode);
+      }
+
+      title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode);
+    }
+  }
+}
+
+/**
+ * Implements of hook_menu().
+ */
+function title_menu() {
+  $items = array();
+
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if (!empty($entity_info['field replacement'])) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        // Blindly taken from field_ui_menu().
+        if (isset($bundle_info['admin'])) {
+          $path = $bundle_info['admin']['path'];
+
+          if (isset($bundle_info['admin']['bundle argument'])) {
+            $bundle_arg = $bundle_info['admin']['bundle argument'];
+          }
+          else {
+            $bundle_arg = $bundle_name;
+          }
+
+          $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
+          $access += array(
+            'access callback' => 'user_access',
+            'access arguments' => array('administer site configuration'),
+          );
+
+          $items["$path/fields/replace"] = array(
+            'load arguments' => array(),
+            'title' => 'Replace fields',
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('title_field_replacement_form', $entity_type, $bundle_arg),
+          ) + $access;
+        }
+      }
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * Implements hook_field_extra_fields_alter().
+ */
+function title_field_extra_fields_alter(&$info) {
+  $entity_info = entity_get_info();
+  foreach ($info as $entity_type => $bundles) {
+    foreach ($bundles as $bundle_name => $bundle) {
+      if (!empty($entity_info[$entity_type]['field replacement'])) {
+        foreach ($entity_info[$entity_type]['field replacement'] as $field_name => $field_replacement_info) {
+          if (title_field_replacement_enabled($entity_type, $bundle_name, $field_name)) {
+            unset($info[$entity_type][$bundle_name]['form'][$field_name]);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Provide settings to enable title field.
+ */
+function title_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
+  $entity_info = entity_get_info($form['#entity_type']);
+
+  if (!empty($entity_info['field replacement'])) {
+    $field_replacement_info = $entity_info['field replacement'];
+    $admin_path = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']);
+    $form['fields']['#header'][6]['colspan'] += 1;
+
+    foreach (element_children($form['fields']) as $field_name) {
+      if (isset($field_replacement_info[$field_name])) {
+        $form['fields'][$field_name]['field_replacement'] = array(
+          '#type' => 'link',
+          '#title' => t('replace'),
+          '#href' => $admin_path . '/fields/replace/' . $field_name,
+          '#options' => array('attributes' => array('title' => t('Replace %field with a field instance.', array('%field' => $field_name)))),
+        );
+      }
+      else {
+        $form['fields'][$field_name]['field_replacement'] = array();
+      }
+    }
+  }
+}
+
+/**
+ * Generate a field replacement form.
+ */
+function title_field_replacement_form($form, $form_state, $entity_type, $bundle, $field_name) {
+  $bundle_name = field_extract_bundle($entity_type, $bundle);
+  $entity_info = entity_get_info($entity_type);
+  $info = $entity_info['field replacement'][$field_name];
+  $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle_name);
+
+  $form['#entity_type'] = $entity_type;
+  $form['#bundle'] = $bundle_name;
+  $form['#field_name'] = $field_name;
+
+  $form['enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Replace %field with a field instance.', array('%field' => $field_name)),
+    '#description' => t('If this is enabled the %field legacy field will be replaced with a regular field and will disappear from the <em>Manage fields</em> page. It will get back if the replacing field instance is deleted.', array('%field' => $field_name)),
+    '#default_value' => !empty($instance),
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
+  return $form;
+}
+
+/**
+ * Process field replacement form subissions.
+ */
+function title_field_replacement_form_submit($form, &$form_state) {
+  if (title_toggle_field_replacement($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
+    drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
+  }
+  else {
+    drupal_set_message(t('Field replacement removed.'));
+  }
+  $form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
+}
+
+/**
+ * Toggle field replacement for the given field.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $bundle
+ *   The bundle the legacy field belongs to.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ */
+function title_toggle_field_replacement($entity_type, $bundle, $legacy_field) {
+  $entity_info = entity_get_info($entity_type);
+  $info = $entity_info['field replacement'][$legacy_field];
+  $field_name = $info['field']['field_name'];
+  $instance = field_info_instance($entity_type, $field_name, $bundle);
+
+  if (empty($instance)) {
+    $field = field_info_field($field_name);
+    if (empty($field)) {
+      field_create_field($info['field']);
+    }
+    $info['instance']['entity_type'] = $entity_type;
+    $info['instance']['bundle'] = $bundle;
+    field_create_instance($info['instance']);
+    return TRUE;
+  }
+  else {
+    field_delete_instance($instance);
+    return FALSE;
+  }
+}
-- 
1.7.4.msysgit.0


From dbc4f03dc0bcef4a788bc3aa22d86db863bca0a3 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Fri, 11 Mar 2011 16:55:58 +0100
Subject: [PATCH 02/13] Issue #924968 by plach: Added PHP docs

---
 title.core.inc |   14 ++++++++------
 title.module   |   23 +++++++++++++++++++++--
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/title.core.inc b/title.core.inc
index bee572b..aa017f7 100644
--- a/title.core.inc
+++ b/title.core.inc
@@ -2,7 +2,9 @@
 
 /**
  * @file
- * TODO
+ *
+ * Provide field replacement information for core entities and type specific
+ * callbacks.
  */
 
 /**
@@ -98,7 +100,7 @@ function _title_field_replacement_core_info() {
 }
 
 /**
- * TODO
+ * Submit callback for the taxonomy term description.
  */
 function title_field_term_description_submit(&$values, $legacy_field, $info, $langcode) {
   $values['description'] = array();
@@ -108,7 +110,7 @@ function title_field_term_description_submit(&$values, $legacy_field, $info, $la
 }
 
 /**
- * TODO
+ * Sync callback for the text field type.
  */
 function title_field_text_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
@@ -117,7 +119,7 @@ function title_field_text_sync($entity_type, $entity, $legacy_field, $info, $lan
 }
 
 /**
- * TODO
+ * Sync back callback for the text field type.
  */
 function title_field_text_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
@@ -126,7 +128,7 @@ function title_field_text_sync_back($entity_type, $entity, $legacy_field, $info,
 }
 
 /**
- * TODO
+ * Sync callback for the text with summary field type.
  */
 function title_field_text_with_summary_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
   $format_key = $info['additional keys']['format'];
@@ -138,7 +140,7 @@ function title_field_text_with_summary_sync($entity_type, $entity, $legacy_field
 }
 
 /**
- * TODO
+ * Sync back callback for the text with summary field type.
  */
 function title_field_text_with_summary_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
   $format_key = $info['additional keys']['format'];
diff --git a/title.module b/title.module
index 2e74b69..94c49ca 100644
--- a/title.module
+++ b/title.module
@@ -202,7 +202,20 @@ function title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode
 }
 
 /**
- * TODO
+ * Synchronize a single regular field from its legacy field value.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $entity
+ *   The entity to work with.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ * @param $field_name
+ *   The regular field to use as source value.
+ * @param $display
+ *   Specifies if synchronization is being performed on display or on save.
+ * @param $langcode
+ *   The field language to use for the source value.
  */
 function title_field_sync_back($entity_type, $entity, $legacy_field, $info) {
   if (isset($entity->{$legacy_field})) {
@@ -212,7 +225,13 @@ function title_field_sync_back($entity_type, $entity, $legacy_field, $info) {
 }
 
 /**
- * TODO
+ * Provide the original entity language.
+ *
+ * @param $entity_type
+ * @param $entity
+ *
+ * @return
+ *   A language code
  */
 function title_entity_language($entity_type, $entity) {
   // If a language property is defined for the current entity we synchronize
-- 
1.7.4.msysgit.0


From b41cb120e4aeb7c55e97ddd167577711d70d326d Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Fri, 11 Mar 2011 19:49:54 +0100
Subject: [PATCH 03/13] Issue #924968 by plach: Fixed entity forms to support nested subforms

---
 title.module |   30 ++++++++++++++++--------------
 1 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/title.module b/title.module
index 94c49ca..d9e84b2 100644
--- a/title.module
+++ b/title.module
@@ -255,11 +255,11 @@ function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $l
   list(, , $bundle) = entity_extract_ids($entity_type, $entity);
   $fr_info = title_field_replacement_info($entity_type);
 
-  if ($fr_info) {
+  if (!empty($fr_info)) {
     foreach ($fr_info as $legacy_field => $info)  {
       if (isset($form[$legacy_field]) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
         $form[$legacy_field]['#access'] = FALSE;
-        $form_state['field_replacement'][] = $legacy_field;
+        $form[$legacy_field]['#field_replacement'] = TRUE;
       }
     }
   }
@@ -271,23 +271,25 @@ function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $l
  * Synchronize submitted field values into the corresponding legacy fields.
  */
 function title_field_attach_submit($entity_type, $entity, $form, &$form_state) {
-  if (!empty($form_state['field_replacement'])) {
+  $fr_info = title_field_replacement_info($entity_type);
+
+  if (!empty($fr_info)) {
     $values = &$form_state['values'];
     $values = &drupal_array_get_nested_value($values, $form['#parents']);
-    $form = drupal_array_get_nested_value($form, $form['#parents']);
     $fr_info = title_field_replacement_info($entity_type);
 
-    foreach ($form_state['field_replacement'] as $legacy_field) {
-      $info = $fr_info[$legacy_field];
-      $field_name = $info['field']['field_name'];
-      $langcode = $form[$field_name]['#language'];
-
-      // Give a chance to operate on submitted values either.
-      if (!empty($info['callbacks']['submit'])) {
-        $info['callbacks']['submit']($values, $legacy_field, $info, $langcode);
+    foreach ($fr_info as $legacy_field => $info) {
+      if (!empty($form[$legacy_field]['#field_replacement'])) {
+        $field_name = $info['field']['field_name'];
+        $langcode = $form[$field_name]['#language'];
+  
+        // Give a chance to operate on submitted values either.
+        if (!empty($info['callbacks']['submit'])) {
+          $info['callbacks']['submit']($values, $legacy_field, $info, $langcode);
+        }
+  
+        title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode);
       }
-
-      title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode);
     }
   }
 }
-- 
1.7.4.msysgit.0


From 93c1924ff5403efc098c7041469c4d10f66f9d59 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Sat, 12 Mar 2011 10:48:50 +0100
Subject: [PATCH 04/13] Issue #924968 by plach: Fixed resave needed if field values altered before save

---
 title.core.inc |   12 ------------
 title.module   |   42 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/title.core.inc b/title.core.inc
index aa017f7..498f070 100644
--- a/title.core.inc
+++ b/title.core.inc
@@ -84,18 +84,6 @@ function _title_field_replacement_core_info() {
     ),
   );
 
-  $info['user'] = array(
-    'field replacement' => array(
-      'name' => array(
-        'field' => $field,
-        'instance' => array(
-          'label' => t('Name'),
-          'description' => t('A field replacing user name.'),
-        ) + $instance,
-      ),
-    ),
-  );
-
   return $info;
 }
 
diff --git a/title.module b/title.module
index d9e84b2..f54078f 100644
--- a/title.module
+++ b/title.module
@@ -14,10 +14,9 @@
  * - Node: title
  * - Taxonomy Term: name, description
  * - Comment: subject
- * - User: name
  *
+ * @todo: entity label callback
  * @todo: tests
- * @todo: entity insert/update sync
  */
 
 module_load_include('inc', 'title', 'title.core');
@@ -68,6 +67,45 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) {
  */
 function title_entity_presave($entity, $type) {
   title_entity_sync($type, $entity, NULL, TRUE);
+  // Store a copy of the synchronized values to check if they have been altered
+  // before saving.
+  $entity->field_replacement = clone($entity);
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function title_entity_insert($entity, $type) {
+  title_entity_update($entity, $type);
+}
+
+/**
+ * Implements hook_entity_update().
+ *
+ * Since Title is supposed to act as the first module on hook invocation, legacy
+ * field values might be altered after reverse synchronization by subsequent
+ * hook implementations. If this happens the field values must be synchronized
+ * and the updated versions must be saved. 
+ */
+function title_entity_update($entity, $type) {
+  $fr_info = title_field_replacement_info($type);
+
+  if ($fr_info) {
+    $update = FALSE;
+    list(, , $bundle) = entity_extract_ids($type, $entity);     
+
+    foreach ($fr_info as $legacy_field => $info) {
+      if ($entity->{$legacy_field} !== $entity->field_replacement->{$legacy_field} && title_field_replacement_enabled($type, $bundle, $legacy_field)) {
+        title_field_sync_back($type, $entity, $legacy_field, $info);
+        $update = TRUE;
+      }
+    }
+
+    if ($update) {
+      // Save updated field values.
+      field_attach_update($type, $entity);
+    }
+  }
 }
 
 /**
-- 
1.7.4.msysgit.0


From 38a76f20d5b3248c522ec11085c0b4db416b5eb9 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Sat, 12 Mar 2011 11:17:44 +0100
Subject: [PATCH 05/13] Issue #924968 by plach: Added entity_label() support

---
 title.core.inc |   12 +++++-----
 title.module   |   61 +++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/title.core.inc b/title.core.inc
index 498f070..9ea57d7 100644
--- a/title.core.inc
+++ b/title.core.inc
@@ -100,16 +100,16 @@ function title_field_term_description_submit(&$values, $legacy_field, $info, $la
 /**
  * Sync callback for the text field type.
  */
-function title_field_text_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
+function title_field_text_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
   $wrapper->language($langcode);
-  $entity->{$legacy_field} = $wrapper->{$info['field']['field_name']}->raw();
+  return $wrapper->{$info['field']['field_name']}->raw();
 }
 
 /**
  * Sync back callback for the text field type.
  */
-function title_field_text_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
+function title_field_text_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) {
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
   $wrapper->language($langcode);
   $wrapper->{$info['field']['field_name']}->set($entity->{$legacy_field});
@@ -118,19 +118,19 @@ function title_field_text_sync_back($entity_type, $entity, $legacy_field, $info,
 /**
  * Sync callback for the text with summary field type.
  */
-function title_field_text_with_summary_sync($entity_type, $entity, $legacy_field, $info, $langcode) {
+function title_field_text_with_summary_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
   $format_key = $info['additional keys']['format'];
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
   $wrapper->language($langcode);
   $field_name = $info['field']['field_name'];
-  $entity->{$legacy_field} = $wrapper->{$field_name}->value->raw();
   $entity->{$format_key} = $wrapper->{$field_name}->format->raw();
+  return $wrapper->{$field_name}->value->raw();
 }
 
 /**
  * Sync back callback for the text with summary field type.
  */
-function title_field_text_with_summary_sync_back($entity_type, $entity, $legacy_field, $info, $langcode) {
+function title_field_text_with_summary_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) {
   $format_key = $info['additional keys']['format'];
   $value = array('value' => $entity->{$legacy_field}, 'format' => $entity->{$format_key});
   $wrapper = entity_metadata_wrapper($entity_type, $entity);
diff --git a/title.module b/title.module
index f54078f..635f5ce 100644
--- a/title.module
+++ b/title.module
@@ -15,7 +15,6 @@
  * - Taxonomy Term: name, description
  * - Comment: subject
  *
- * @todo: entity label callback
  * @todo: tests
  */
 
@@ -43,9 +42,14 @@ function title_entity_info_alter(&$info) {
           $fr_info['callbacks'] = array();
         }
         $fr_info['callbacks'] += array(
-          'sync' => "title_field_{$type}_sync",
-          'sync_back' => "title_field_{$type}_sync_back",
+          'sync_get' => "title_field_{$type}_sync_get",
+          'sync_set' => "title_field_{$type}_sync_set",
         );
+
+        // Support add explicit support for entity_label().
+        if ($entity_info['entity keys']['label'] == $legacy_field) {
+          $entity_info['entity keys']['label callback'] = 'title_entity_label';
+        }
       }
     }
   }
@@ -63,6 +67,27 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) {
 }
 
 /**
+ * Return an entity label value.
+ *
+ * @param $entity
+ *   The entity whose label has to be displayed.
+ * @param $type
+ *   The entity type.
+ * @param $langcode
+ *   (Optional) The language the entity label has to be displayed in.
+ *
+ * @return
+ *   The entity label as a string value.
+ */
+function title_entity_label($entity, $type, $langcode = NULL) {
+  $entity_info = entity_get_info($entity_type);
+  $legacy_field = $entity_info['entity keys']['label'];
+  $info = $entity_info['field_replacement'][$legacy_field];
+  $langcode = field_language($type, $entity, $info['field']['field_name'], $langcode);
+  return $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
+}
+
+/**
  * Implements hook_entity_presave().
  */
 function title_entity_presave($entity, $type) {
@@ -85,18 +110,18 @@ function title_entity_insert($entity, $type) {
  * Since Title is supposed to act as the first module on hook invocation, legacy
  * field values might be altered after reverse synchronization by subsequent
  * hook implementations. If this happens the field values must be synchronized
- * and the updated versions must be saved. 
+ * and the updated versions must be saved.
  */
 function title_entity_update($entity, $type) {
   $fr_info = title_field_replacement_info($type);
 
   if ($fr_info) {
     $update = FALSE;
-    list(, , $bundle) = entity_extract_ids($type, $entity);     
+    list(, , $bundle) = entity_extract_ids($type, $entity);
 
     foreach ($fr_info as $legacy_field => $info) {
       if ($entity->{$legacy_field} !== $entity->field_replacement->{$legacy_field} && title_field_replacement_enabled($type, $bundle, $legacy_field)) {
-        title_field_sync_back($type, $entity, $legacy_field, $info);
+        title_field_sync_set($type, $entity, $legacy_field, $info);
         $update = TRUE;
       }
     }
@@ -189,26 +214,26 @@ function title_field_replacement_enabled($entity_type, $bundle, $legacy_field) {
  *   The name of the entity type.
  * @param $entity
  *   The entity to work with.
- * @param $back
+ * @param $set
  *   Specifies the direction synchronization must be performed.
  */
-function title_entity_sync($entity_type, &$entity, $langcode = NULL, $back = FALSE) {
+function title_entity_sync($entity_type, &$entity, $langcode = NULL, $set = FALSE) {
   $sync = &drupal_static(__FUNCTION__, array());
   list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
   $langcode = field_valid_language($langcode, FALSE);
 
   // We do not need to perform this more than once.
-  if (!empty($sync[$entity_type][$id][$langcode][$back])) {
+  if (!empty($sync[$entity_type][$id][$langcode][$set])) {
     return;
   }
 
-  $sync[$entity_type][$id][$langcode][$back] = TRUE;
+  $sync[$entity_type][$id][$langcode][$set] = TRUE;
   $fr_info = title_field_replacement_info($entity_type);
 
   if ($fr_info) {
     foreach ($fr_info as $legacy_field => $info) {
       if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
-        $function = $back ? 'title_field_sync_back' : 'title_field_sync';
+        $function = 'title_field_sync_' . ($set ? 'set' : 'get');
         $function($entity_type, $entity, $legacy_field, $info, $langcode);
       }
     }
@@ -231,11 +256,11 @@ function title_entity_sync($entity_type, &$entity, $langcode = NULL, $back = FAL
  * @param $langcode
  *   The field language to use for the source value.
  */
-function title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode = NULL) {
+function title_field_sync_get($entity_type, $entity, $legacy_field, $info, $langcode = NULL) {
   if (isset($entity->{$legacy_field})) {
     // Find out the actual language to use (field might be untranslatable).
     $langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode);
-    $info['callbacks']['sync']($entity_type, $entity, $legacy_field, $info, $langcode);
+    $entity->{$legacy_field} = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
   }
 }
 
@@ -255,10 +280,10 @@ function title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode
  * @param $langcode
  *   The field language to use for the source value.
  */
-function title_field_sync_back($entity_type, $entity, $legacy_field, $info) {
+function title_field_sync_set($entity_type, $entity, $legacy_field, $info) {
   if (isset($entity->{$legacy_field})) {
     $langcode = title_entity_language($entity_type, $entity);
-    $info['callbacks']['sync_back']($entity_type, $entity, $legacy_field, $info, $langcode);
+    $info['callbacks']['sync_set']($entity_type, $entity, $legacy_field, $info, $langcode);
   }
 }
 
@@ -320,13 +345,13 @@ function title_field_attach_submit($entity_type, $entity, $form, &$form_state) {
       if (!empty($form[$legacy_field]['#field_replacement'])) {
         $field_name = $info['field']['field_name'];
         $langcode = $form[$field_name]['#language'];
-  
+
         // Give a chance to operate on submitted values either.
         if (!empty($info['callbacks']['submit'])) {
           $info['callbacks']['submit']($values, $legacy_field, $info, $langcode);
         }
-  
-        title_field_sync($entity_type, $entity, $legacy_field, $info, $langcode);
+
+        title_field_sync_get($entity_type, $entity, $legacy_field, $info, $langcode);
       }
     }
   }
-- 
1.7.4.msysgit.0


From af8bdacad3dc7cd8c8bc0ba1754be5c7dfc07523 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Sat, 12 Mar 2011 11:29:15 +0100
Subject: [PATCH 06/13] Issue #924968 by plach: Moved administration code into title.admin.inc

---
 title.admin.inc |  103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 title.core.inc  |    1 -
 title.module    |   96 ++-------------------------------------------------
 3 files changed, 106 insertions(+), 94 deletions(-)
 create mode 100644 title.admin.inc

diff --git a/title.admin.inc b/title.admin.inc
new file mode 100644
index 0000000..cf189e4
--- /dev/null
+++ b/title.admin.inc
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Admin page callbacks for the Title module.
+ */
+
+/**
+ * Provide settings to enable title field.
+ */
+function title_form_field_ui_overview(&$form, &$form_state) {
+  $entity_info = entity_get_info($form['#entity_type']);
+
+  if (!empty($entity_info['field replacement'])) {
+    $field_replacement_info = $entity_info['field replacement'];
+    $admin_path = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']);
+    $form['fields']['#header'][6]['colspan'] += 1;
+
+    foreach (element_children($form['fields']) as $field_name) {
+      if (isset($field_replacement_info[$field_name])) {
+        $form['fields'][$field_name]['field_replacement'] = array(
+          '#type' => 'link',
+          '#title' => t('replace'),
+          '#href' => $admin_path . '/fields/replace/' . $field_name,
+          '#options' => array('attributes' => array('title' => t('Replace %field with a field instance.', array('%field' => $field_name)))),
+        );
+      }
+      else {
+        $form['fields'][$field_name]['field_replacement'] = array();
+      }
+    }
+  }
+}
+
+/**
+ * Generate a field replacement form.
+ */
+function title_field_replacement_form($form, $form_state, $entity_type, $bundle, $field_name) {
+  $bundle_name = field_extract_bundle($entity_type, $bundle);
+  $entity_info = entity_get_info($entity_type);
+  $info = $entity_info['field replacement'][$field_name];
+  $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle_name);
+
+  $form['#entity_type'] = $entity_type;
+  $form['#bundle'] = $bundle_name;
+  $form['#field_name'] = $field_name;
+
+  $form['enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Replace %field with a field instance.', array('%field' => $field_name)),
+    '#description' => t('If this is enabled the %field legacy field will be replaced with a regular field and will disappear from the <em>Manage fields</em> page. It will get back if the replacing field instance is deleted.', array('%field' => $field_name)),
+    '#default_value' => !empty($instance),
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
+  return $form;
+}
+
+/**
+ * Process field replacement form subissions.
+ */
+function title_field_replacement_form_submit($form, &$form_state) {
+  if (title_toggle_field_replacement($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
+    drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
+  }
+  else {
+    drupal_set_message(t('Field replacement removed.'));
+  }
+  $form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
+}
+
+/**
+ * Toggle field replacement for the given field.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $bundle
+ *   The bundle the legacy field belongs to.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ */
+function title_toggle_field_replacement($entity_type, $bundle, $legacy_field) {
+  $entity_info = entity_get_info($entity_type);
+  $info = $entity_info['field replacement'][$legacy_field];
+  $field_name = $info['field']['field_name'];
+  $instance = field_info_instance($entity_type, $field_name, $bundle);
+
+  if (empty($instance)) {
+    $field = field_info_field($field_name);
+    if (empty($field)) {
+      field_create_field($info['field']);
+    }
+    $info['instance']['entity_type'] = $entity_type;
+    $info['instance']['bundle'] = $bundle;
+    field_create_instance($info['instance']);
+    return TRUE;
+  }
+  else {
+    field_delete_instance($instance);
+    return FALSE;
+  }
+}
diff --git a/title.core.inc b/title.core.inc
index 9ea57d7..1822c12 100644
--- a/title.core.inc
+++ b/title.core.inc
@@ -2,7 +2,6 @@
 
 /**
  * @file
- *
  * Provide field replacement information for core entities and type specific
  * callbacks.
  */
diff --git a/title.module b/title.module
index 635f5ce..384de15 100644
--- a/title.module
+++ b/title.module
@@ -388,6 +388,7 @@ function title_menu() {
             'title' => 'Replace fields',
             'page callback' => 'drupal_get_form',
             'page arguments' => array('title_field_replacement_form', $entity_type, $bundle_arg),
+            'file' => 'title.admin.inc',
           ) + $access;
         }
       }
@@ -417,99 +418,8 @@ function title_field_extra_fields_alter(&$info) {
 
 /**
  * Implements hook_form_FORM_ID_alter().
- *
- * Provide settings to enable title field.
  */
 function title_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
-  $entity_info = entity_get_info($form['#entity_type']);
-
-  if (!empty($entity_info['field replacement'])) {
-    $field_replacement_info = $entity_info['field replacement'];
-    $admin_path = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']);
-    $form['fields']['#header'][6]['colspan'] += 1;
-
-    foreach (element_children($form['fields']) as $field_name) {
-      if (isset($field_replacement_info[$field_name])) {
-        $form['fields'][$field_name]['field_replacement'] = array(
-          '#type' => 'link',
-          '#title' => t('replace'),
-          '#href' => $admin_path . '/fields/replace/' . $field_name,
-          '#options' => array('attributes' => array('title' => t('Replace %field with a field instance.', array('%field' => $field_name)))),
-        );
-      }
-      else {
-        $form['fields'][$field_name]['field_replacement'] = array();
-      }
-    }
-  }
-}
-
-/**
- * Generate a field replacement form.
- */
-function title_field_replacement_form($form, $form_state, $entity_type, $bundle, $field_name) {
-  $bundle_name = field_extract_bundle($entity_type, $bundle);
-  $entity_info = entity_get_info($entity_type);
-  $info = $entity_info['field replacement'][$field_name];
-  $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle_name);
-
-  $form['#entity_type'] = $entity_type;
-  $form['#bundle'] = $bundle_name;
-  $form['#field_name'] = $field_name;
-
-  $form['enabled'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Replace %field with a field instance.', array('%field' => $field_name)),
-    '#description' => t('If this is enabled the %field legacy field will be replaced with a regular field and will disappear from the <em>Manage fields</em> page. It will get back if the replacing field instance is deleted.', array('%field' => $field_name)),
-    '#default_value' => !empty($instance),
-  );
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
-  return $form;
-}
-
-/**
- * Process field replacement form subissions.
- */
-function title_field_replacement_form_submit($form, &$form_state) {
-  if (title_toggle_field_replacement($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
-    drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
-  }
-  else {
-    drupal_set_message(t('Field replacement removed.'));
-  }
-  $form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
-}
-
-/**
- * Toggle field replacement for the given field.
- *
- * @param $entity_type
- *   The name of the entity type.
- * @param $bundle
- *   The bundle the legacy field belongs to.
- * @param $legacy_field
- *   The name of the legacy field to be replaced.
- */
-function title_toggle_field_replacement($entity_type, $bundle, $legacy_field) {
-  $entity_info = entity_get_info($entity_type);
-  $info = $entity_info['field replacement'][$legacy_field];
-  $field_name = $info['field']['field_name'];
-  $instance = field_info_instance($entity_type, $field_name, $bundle);
-
-  if (empty($instance)) {
-    $field = field_info_field($field_name);
-    if (empty($field)) {
-      field_create_field($info['field']);
-    }
-    $info['instance']['entity_type'] = $entity_type;
-    $info['instance']['bundle'] = $bundle;
-    field_create_instance($info['instance']);
-    return TRUE;
-  }
-  else {
-    field_delete_instance($instance);
-    return FALSE;
-  }
+  module_load_include('inc', 'title', 'title.admin');
+  title_form_field_ui_overview($form, $form_state);
 }
-- 
1.7.4.msysgit.0


From 0f971ad8589300ca5b461c25e0b0a15680321ec6 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Sat, 12 Mar 2011 19:44:17 +0100
Subject: [PATCH 07/13] Issue #924968 by plach: Introduced tests for field replacement workflow

---
 tests/title.test        |   84 ++++++++++++++++++++++++++++++++
 tests/title_test.module |  124 +++++++++++++++++++++++++++++++++++++++++++++++
 title.admin.inc         |    8 ++-
 title.core.inc          |    4 +-
 title.module            |   13 ++---
 5 files changed, 222 insertions(+), 11 deletions(-)

diff --git a/tests/title.test b/tests/title.test
index a3addd2..8e9d50c 100644
--- a/tests/title.test
+++ b/tests/title.test
@@ -5,3 +5,87 @@
  * Tests for the Title module.
  */
 
+/**
+ * Tests for legacy field replacement.
+ */
+class TitleFieldReplacementTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Field replacement',
+      'description' => 'Test field replacement.',
+      'group' => 'Title',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('entity', 'field_test', 'title_test');
+  }
+
+  /**
+   * Test field replacement API.
+   */
+  function testFieldReplacementAPI() {
+    module_load_include('inc', 'title', 'title.admin');
+
+    $info = entity_get_info('test_entity');
+    $label_key = $info['entity keys']['label'];
+    $field_name = $label_key . '_field';
+    title_toggle_field_replacement('test_entity', 'test_bundle', $label_key);
+
+    $i = 0;
+    $entity = field_test_create_stub_entity(FALSE, FALSE);
+
+    while ($i++ <= 1) {
+      // The first time the entity gets greated the second time gets updated.
+      title_test_entity_save($entity);
+
+      // Check that the replacing field value has been synchronized on save.
+      $query = db_select('test_entity', 'te');
+      $query->addJoin('INNER', 'field_data_' . $field_name, 'f', 'te.ftid = f.entity_id');
+      $record = $query
+        ->fields('te')
+        ->fields('f')
+        ->condition('ftid', $entity->ftid)
+        ->execute()
+        ->fetch();
+
+      $phase = $entity->is_new ? 'insert' : 'update';
+      $this->assertIdentical($record->{$label_key}, $record->{$field_name . '_value'}, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
+      unset($entity->is_new);
+    }
+
+    // Store a dummy value in the legacy field.
+    while (($label = $this->randomName()) == $entity->{$label_key});
+
+    db_update('test_entity')
+      ->fields(array($label_key => $label))
+      ->execute();
+
+    $record = db_select('test_entity', 'te')
+      ->fields('te')
+      ->condition('ftid', $entity->ftid)
+      ->execute()
+      ->fetch();
+
+    $this->assertNotIdentical($record->{$label_key}, $entity->{$label_key}, t('Entity label has been changed.'));
+
+    // Clear field cache so we synchrboization can be performed on field attach
+    // load.
+    cache_clear_all('*', 'cache_field');
+
+    // Check that the replacing field value is correctly synchronized on load
+    // and view.
+    $entity = title_test_entity_test_load($entity);
+    title_test_phase_check('after_load', $entity);
+    $build = entity_view('test_entity', array($entity->ftid => $entity));
+
+    foreach (title_test_phase_store() as $phase => $value) {
+      $this->assertTrue($value, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
+    }
+
+    // Change the value stored into the label field to check entity_label().
+    $label = $this->randomName();
+    $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $label;
+    $this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
+  }
+}
diff --git a/tests/title_test.module b/tests/title_test.module
index 3268077..92032cf 100644
--- a/tests/title_test.module
+++ b/tests/title_test.module
@@ -5,3 +5,127 @@
  * Testing functionality for Title module.
  */
 
+/**
+ * Implements hook_entity_info().
+ */
+function title_test_entity_info() {
+  $info = array();
+
+  $field = array(
+    'type' => 'text',
+    'cardinality' => 1,
+    'translatable' => TRUE,
+  );
+
+  $instance = array(
+    'required' => TRUE,
+    'settings' => array(
+      'text_processing' => 0,
+    ),
+    'widget' => array(
+      'weight' => -5,
+    ),
+  );
+
+  $info['test_entity'] = array(
+    'label' => t('Test entity'),
+    'entity keys' => array(
+      'label' => 'ftlabel',
+    ),
+    'field replacement' => array(
+      'ftlabel' => array(
+        'field' => $field,
+        'instance' => array(
+          'label' => t('Title'),
+          'description' => t('A field replacing node title.'),
+        ) + $instance,
+      ),
+    ),
+    'controller class' => 'EntityAPIController',
+  );
+
+  return $info;
+}
+
+/**
+ * Save the given test entity.
+ */
+function title_test_entity_save($entity) {
+  // field_test_entity_save does not invoke hook_entity_presave().
+  module_invoke_all('entity_presave', $entity, 'test_entity');
+  field_test_entity_save($entity);
+  // field_test_entity_save does not invoke hook_entity_insert().
+  $hook = $entity->is_new ? 'entity_insert' : 'entity_update';
+  module_invoke_all($hook, $entity, 'test_entity');
+}
+
+/**
+ * Load the given test entity.
+ */
+function title_test_entity_test_load($entity) {
+  $entity = field_test_entity_test_load($entity->ftid);
+  // field_test_entity_load does not invoke hook_entity_load().
+  module_invoke_all('entity_load', array($entity), 'test_entity');
+  return $entity;
+}
+
+/**
+ * Store a value for the given phase.
+ */
+function title_test_phase_store($phase = NULL, $value = NULL) {
+  $store = &drupal_static(__FUNCTION__, array());
+  if (isset($phase)) {
+    $store[$phase] = $value;
+  }
+  return $store;
+}
+
+/**
+ * Check the entity label at a give phase.
+ */
+function title_test_phase_check($phase, $entity) {
+  $info = entity_get_info('test_entity');
+  $label_key = $info['entity keys']['label'];
+  $field_name = $label_key . '_field';
+  $value = $entity->{$label_key} == $entity->{$field_name}[LANGUAGE_NONE][0]['value'];
+  title_test_phase_store($phase, $value);
+  return $value;
+}
+
+/**
+ * Implements hook_entity_prepare_view().
+ */
+function title_test_entity_presave($entity, $type) {
+  if ($type == 'test_entity') {
+    $info = entity_get_info('test_entity');
+    $label_key = $info['entity keys']['label'];
+    $entity->{$label_key} = DrupalWebTestCase::randomName();
+  }
+}
+
+/**
+ * Implements hook_field_attach_load().
+ */
+function title_test_field_attach_load($entity_type, $entities, $age, $options) {
+  if ($entity_type == 'test_entity') {
+    title_test_phase_check('field_attach_load', current($entities));
+  }
+}
+
+/**
+ * Implements hook_entity_load().
+ */
+function title_test_entity_load($entities, $type) {
+  if ($type == 'test_entity') {
+    title_test_phase_check('entity_load', current($entities));
+  }
+}
+
+/**
+ * Implements hook_entity_prepare_view().
+ */
+function title_test_entity_prepare_view($entities, $type, $langcode = NULL) {
+  if ($type == 'test_entity') {
+    title_test_phase_check('entity_prepare_view', current($entities));
+  }
+}
diff --git a/title.admin.inc b/title.admin.inc
index cf189e4..bca3281 100644
--- a/title.admin.inc
+++ b/title.admin.inc
@@ -81,8 +81,12 @@ function title_field_replacement_form_submit($form, &$form_state) {
  *   The name of the legacy field to be replaced.
  */
 function title_toggle_field_replacement($entity_type, $bundle, $legacy_field) {
-  $entity_info = entity_get_info($entity_type);
-  $info = $entity_info['field replacement'][$legacy_field];
+  $info = title_field_replacement_info($entity_type, $legacy_field);
+
+  if (!$info) {
+    return;
+  }
+
   $field_name = $info['field']['field_name'];
   $instance = field_info_instance($entity_type, $field_name, $bundle);
 
diff --git a/title.core.inc b/title.core.inc
index 1822c12..50461ae 100644
--- a/title.core.inc
+++ b/title.core.inc
@@ -7,9 +7,9 @@
  */
 
 /**
- * Provide core legacy fields replacement info.
+ * Implements hook_entity_info().
  */
-function _title_field_replacement_core_info() {
+function title_entity_info() {
   $info = array();
 
   $field = array(
diff --git a/title.module b/title.module
index 384de15..4c87339 100644
--- a/title.module
+++ b/title.module
@@ -16,6 +16,7 @@
  * - Comment: subject
  *
  * @todo: tests
+ * @todo: API PHPdocs
  */
 
 module_load_include('inc', 'title', 'title.core');
@@ -24,8 +25,6 @@ module_load_include('inc', 'title', 'title.core');
  * Implements hook_entity_info_alter().
  */
 function title_entity_info_alter(&$info) {
-  $info = array_merge_recursive($info, _title_field_replacement_core_info());
-
   foreach ($info as $entity_type => $entity_info) {
     if ($entity_info['fieldable'] && !empty($info[$entity_type]['field replacement'])) {
       foreach ($info[$entity_type]['field replacement'] as $legacy_field => $data) {
@@ -47,8 +46,8 @@ function title_entity_info_alter(&$info) {
         );
 
         // Support add explicit support for entity_label().
-        if ($entity_info['entity keys']['label'] == $legacy_field) {
-          $entity_info['entity keys']['label callback'] = 'title_entity_label';
+        if (isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
+          $info[$entity_type]['label callback'] = 'title_entity_label';
         }
       }
     }
@@ -80,11 +79,11 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) {
  *   The entity label as a string value.
  */
 function title_entity_label($entity, $type, $langcode = NULL) {
-  $entity_info = entity_get_info($entity_type);
+  $entity_info = entity_get_info($type);
   $legacy_field = $entity_info['entity keys']['label'];
-  $info = $entity_info['field_replacement'][$legacy_field];
+  $info = $entity_info['field replacement'][$legacy_field];
   $langcode = field_language($type, $entity, $info['field']['field_name'], $langcode);
-  return $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
+  return $info['callbacks']['sync_get']($type, $entity, $legacy_field, $info, $langcode);
 }
 
 /**
-- 
1.7.4.msysgit.0


From 34d0f1bc3ac3df851e5e097ed1b460de548255df Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Thu, 17 Mar 2011 19:18:33 +0100
Subject: [PATCH 08/13] Issue #924968 by plach: Disabled entity_label() support until #1096446 is fixed

---
 tests/title.test |    8 +++++---
 title.module     |    5 ++++-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/tests/title.test b/tests/title.test
index 8e9d50c..f48478b 100644
--- a/tests/title.test
+++ b/tests/title.test
@@ -84,8 +84,10 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
     }
 
     // Change the value stored into the label field to check entity_label().
-    $label = $this->randomName();
-    $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $label;
-    $this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
+    if (isset($info['label callback'])) {
+      $label = $this->randomName();
+      $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $label;
+      $this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
+    }
   }
 }
diff --git a/title.module b/title.module
index 4c87339..57187b1 100644
--- a/title.module
+++ b/title.module
@@ -46,7 +46,10 @@ function title_entity_info_alter(&$info) {
         );
 
         // Support add explicit support for entity_label().
-        if (isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
+        // @todo Currently core does not pass the entity type to the label
+        // callback thus rendering impossible a genralized handling of the
+        // entity label (see http://drupal.org/node/1096446).
+        if (false && isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
           $info[$entity_type]['label callback'] = 'title_entity_label';
         }
       }
-- 
1.7.4.msysgit.0


From 782fc79b45a9b67d69ca7567cdcd20e568616156 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Thu, 17 Mar 2011 19:18:50 +0100
Subject: [PATCH 09/13] Issue #924968 by plach: Fixed comments/PHP docs

---
 tests/title_test.module |    2 +-
 title.module            |   14 ++++++++------
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/tests/title_test.module b/tests/title_test.module
index 92032cf..24ce4e7 100644
--- a/tests/title_test.module
+++ b/tests/title_test.module
@@ -93,7 +93,7 @@ function title_test_phase_check($phase, $entity) {
 }
 
 /**
- * Implements hook_entity_prepare_view().
+ * Implements hook_entity_presave().
  */
 function title_test_entity_presave($entity, $type) {
   if ($type == 'test_entity') {
diff --git a/title.module b/title.module
index 57187b1..34ebb23 100644
--- a/title.module
+++ b/title.module
@@ -47,8 +47,9 @@ function title_entity_info_alter(&$info) {
 
         // Support add explicit support for entity_label().
         // @todo Currently core does not pass the entity type to the label
-        // callback thus rendering impossible a genralized handling of the
-        // entity label (see http://drupal.org/node/1096446).
+        // callback thus rendering impossible a generalized handling of the
+        // entity label.
+        // @see http://drupal.org/node/1096446
         if (false && isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
           $info[$entity_type]['label callback'] = 'title_entity_label';
         }
@@ -110,9 +111,9 @@ function title_entity_insert($entity, $type) {
  * Implements hook_entity_update().
  *
  * Since Title is supposed to act as the first module on hook invocation, legacy
- * field values might be altered after reverse synchronization by subsequent
- * hook implementations. If this happens the field values must be synchronized
- * and the updated versions must be saved.
+ * field values might be altered by subsequent hook implementations after
+ * reverse synchronization has happened. If this happens the field values must
+ * be synchronized again and the updated versions must be saved.
  */
 function title_entity_update($entity, $type) {
   $fr_info = title_field_replacement_info($type);
@@ -180,7 +181,7 @@ function title_entitycache_load($entities, $type) {
  * different language might be specified while viewing the entity in which case
  * synchronization must be performed again.
  *
- * FIXME: $langcode is not passed along by entity_prepare_view() currently. We
+ * @todo $langcode is not passed along by entity_prepare_view() currently. We
  * will need to remove the default NULL value once this is fixed.
  * @see http://drupal.org/node/1089174
  */
@@ -410,6 +411,7 @@ function title_field_extra_fields_alter(&$info) {
       if (!empty($entity_info[$entity_type]['field replacement'])) {
         foreach ($entity_info[$entity_type]['field replacement'] as $field_name => $field_replacement_info) {
           if (title_field_replacement_enabled($entity_type, $bundle_name, $field_name)) {
+            // Remove the replaced legacy field.
             unset($info[$entity_type][$bundle_name]['form'][$field_name]);
           }
         }
-- 
1.7.4.msysgit.0


From f9d165489e89d71850988c4c3ef545e0a6ed6597 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Fri, 18 Mar 2011 11:17:00 +0100
Subject: [PATCH 10/13] Issue #924968 by plach: Polished API as title_field_replacement_toggle does not belong to title.admin.inc

---
 tests/title.test |    8 ++++----
 title.admin.inc  |   38 +-------------------------------------
 title.module     |   36 ++++++++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/tests/title.test b/tests/title.test
index f48478b..af650fa 100644
--- a/tests/title.test
+++ b/tests/title.test
@@ -25,18 +25,18 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
    * Test field replacement API.
    */
   function testFieldReplacementAPI() {
-    module_load_include('inc', 'title', 'title.admin');
-
     $info = entity_get_info('test_entity');
     $label_key = $info['entity keys']['label'];
     $field_name = $label_key . '_field';
-    title_toggle_field_replacement('test_entity', 'test_bundle', $label_key);
+
+    // Enable field replacement for the test entity.
+    title_field_replacement_toggle('test_entity', 'test_bundle', $label_key);
 
     $i = 0;
     $entity = field_test_create_stub_entity(FALSE, FALSE);
 
     while ($i++ <= 1) {
-      // The first time the entity gets greated the second time gets updated.
+      // The first time the entity gets created the second time gets updated.
       title_test_entity_save($entity);
 
       // Check that the replacing field value has been synchronized on save.
diff --git a/title.admin.inc b/title.admin.inc
index bca3281..003be9f 100644
--- a/title.admin.inc
+++ b/title.admin.inc
@@ -61,7 +61,7 @@ function title_field_replacement_form($form, $form_state, $entity_type, $bundle,
  * Process field replacement form subissions.
  */
 function title_field_replacement_form_submit($form, &$form_state) {
-  if (title_toggle_field_replacement($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
+  if (title_field_replacement_toggle($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
     drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
   }
   else {
@@ -69,39 +69,3 @@ function title_field_replacement_form_submit($form, &$form_state) {
   }
   $form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
 }
-
-/**
- * Toggle field replacement for the given field.
- *
- * @param $entity_type
- *   The name of the entity type.
- * @param $bundle
- *   The bundle the legacy field belongs to.
- * @param $legacy_field
- *   The name of the legacy field to be replaced.
- */
-function title_toggle_field_replacement($entity_type, $bundle, $legacy_field) {
-  $info = title_field_replacement_info($entity_type, $legacy_field);
-
-  if (!$info) {
-    return;
-  }
-
-  $field_name = $info['field']['field_name'];
-  $instance = field_info_instance($entity_type, $field_name, $bundle);
-
-  if (empty($instance)) {
-    $field = field_info_field($field_name);
-    if (empty($field)) {
-      field_create_field($info['field']);
-    }
-    $info['instance']['entity_type'] = $entity_type;
-    $info['instance']['bundle'] = $bundle;
-    field_create_instance($info['instance']);
-    return TRUE;
-  }
-  else {
-    field_delete_instance($instance);
-    return FALSE;
-  }
-}
diff --git a/title.module b/title.module
index 34ebb23..e3b6a6a 100644
--- a/title.module
+++ b/title.module
@@ -211,6 +211,42 @@ function title_field_replacement_enabled($entity_type, $bundle, $legacy_field) {
 }
 
 /**
+ * Toggle field replacement for the given field.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $bundle
+ *   The bundle the legacy field belongs to.
+ * @param $legacy_field
+ *   The name of the legacy field to be replaced.
+ */
+function title_field_replacement_toggle($entity_type, $bundle, $legacy_field) {
+  $info = title_field_replacement_info($entity_type, $legacy_field);
+
+  if (!$info) {
+    return;
+  }
+
+  $field_name = $info['field']['field_name'];
+  $instance = field_info_instance($entity_type, $field_name, $bundle);
+
+  if (empty($instance)) {
+    $field = field_info_field($field_name);
+    if (empty($field)) {
+      field_create_field($info['field']);
+    }
+    $info['instance']['entity_type'] = $entity_type;
+    $info['instance']['bundle'] = $bundle;
+    field_create_instance($info['instance']);
+    return TRUE;
+  }
+  else {
+    field_delete_instance($instance);
+    return FALSE;
+  }
+}
+
+/**
  * Synchronize replaced fields with the regular field values.
  *
  * @param $entity_type
-- 
1.7.4.msysgit.0


From 7358d58688ffc70936586b3bb00b9f543fe3afe2 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Fri, 18 Mar 2011 16:23:50 +0100
Subject: [PATCH 11/13] Issue #924968 by plach: Fixed field replacement checkbox broken

---
 title.admin.inc |   16 ++++++++++------
 1 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/title.admin.inc b/title.admin.inc
index 003be9f..05e23db 100644
--- a/title.admin.inc
+++ b/title.admin.inc
@@ -40,6 +40,7 @@ function title_field_replacement_form($form, $form_state, $entity_type, $bundle,
   $entity_info = entity_get_info($entity_type);
   $info = $entity_info['field replacement'][$field_name];
   $instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle_name);
+  $enabled = !empty($instance);
 
   $form['#entity_type'] = $entity_type;
   $form['#bundle'] = $bundle_name;
@@ -49,7 +50,8 @@ function title_field_replacement_form($form, $form_state, $entity_type, $bundle,
     '#type' => 'checkbox',
     '#title' => t('Replace %field with a field instance.', array('%field' => $field_name)),
     '#description' => t('If this is enabled the %field legacy field will be replaced with a regular field and will disappear from the <em>Manage fields</em> page. It will get back if the replacing field instance is deleted.', array('%field' => $field_name)),
-    '#default_value' => !empty($instance),
+    '#default_value' => $enabled,
+    '#disabled' => $enabled,
   );
 
   $form['actions'] = array('#type' => 'actions');
@@ -61,11 +63,13 @@ function title_field_replacement_form($form, $form_state, $entity_type, $bundle,
  * Process field replacement form subissions.
  */
 function title_field_replacement_form_submit($form, &$form_state) {
-  if (title_field_replacement_toggle($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
-    drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
-  }
-  else {
-    drupal_set_message(t('Field replacement removed.'));
+  if ($form_state['values']['enabled'] != $form['enabled']['#default_value']) {
+    if (title_field_replacement_toggle($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
+      drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
+    }
+    else {
+      drupal_set_message(t('Field replacement removed.'));
+    }
   }
   $form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
 }
-- 
1.7.4.msysgit.0


From 966e8d17208b08d9b13974f1789b3d10e5e4e605 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Fri, 18 Mar 2011 17:02:22 +0100
Subject: [PATCH 12/13] Issue #924968 by plach: Introduced tests for field replacement UI

---
 tests/title.test |   62 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 title.module     |    1 -
 2 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/tests/title.test b/tests/title.test
index af650fa..a67c47c 100644
--- a/tests/title.test
+++ b/tests/title.test
@@ -18,13 +18,13 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
   }
 
   function setUp() {
-    parent::setUp('entity', 'field_test', 'title_test');
+    parent::setUp('entity', 'field_test', 'title', 'title_test');
   }
 
   /**
-   * Test field replacement API.
+   * Test field replacement API and workflow.
    */
-  function testFieldReplacementAPI() {
+  function testFieldReplacementWorkflow() {
     $info = entity_get_info('test_entity');
     $label_key = $info['entity keys']['label'];
     $field_name = $label_key . '_field';
@@ -90,4 +90,60 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
       $this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
     }
   }
+
+  /**
+   * Test field replacement UI.
+   */
+  function testFieldReplacementUI() {
+    $admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer content types', 'administer taxonomy', 'administer comments'));
+    $this->drupalLogin($admin_user);
+
+    foreach (entity_get_info() as $entity_type => $entity_info) {
+      if (!empty($entity_info['field replacement'])) {
+        foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
+          if (isset($bundle_info['admin']['path'])) {
+            $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle) . '/fields';
+
+            foreach ($entity_info['field replacement'] as $legacy_field => $info) {
+              $path = $admin_path . '/replace/' . $legacy_field;
+              $xpath = '//a[@href=:url and text()=:label]';
+              $args = array(':url' => url($path), ':label' => t('replace'));
+              $targs = array('%legacy_field' => $legacy_field, '%entity_type' => $entity_type, '%bundle' => $bundle);
+              $field_name = $info['field']['field_name'];
+
+              // Check that the current legacy field has a "replace" operation.
+              $this->drupalGet($admin_path);
+              $link = $this->xpath($xpath, $args);
+              $this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
+
+              // Check that the legacy field has correctly been replaced through
+              // field replacement UI.
+              $this->drupalPost($path, array('enabled' => TRUE), t('Save settings'));
+              _field_info_collate_fields(TRUE);
+              $link = $this->xpath($xpath, $args);
+              $this->assertTrue(empty($link) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('%legacy_field successfully replaced for the bundle %bundle of the entity %entity_type.', $targs));
+
+              // Check that the enabled status cannot be changed unless the
+              // field instance is removed.
+              $this->drupalGet($path);
+              $this->assertFieldByXPath('//form//input[@name="enabled" and @checked="checked" and @disabled="disabled"]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
+              $this->drupalPost($path, array(), t('Save settings'));
+              _field_info_collate_fields(TRUE);
+              $this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('Submitting the form does not alter field replacement settings.'));
+
+              // Delete the field instance and check that the "replace"
+              // operation is available again.
+              $this->drupalPost($admin_path . '/' . $field_name . '/delete', array(), t('Delete'));
+              $link = $this->xpath($xpath, $args);
+              $this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
+
+              // Check that field replacement can be enabled again.
+              $this->drupalGet($path);
+              $this->assertFieldByXPath('//form//input[@name="enabled" and not(@checked) and not(@disabled)]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
+            }
+          }
+        }
+      }
+    }
+  }
 }
diff --git a/title.module b/title.module
index e3b6a6a..709edcc 100644
--- a/title.module
+++ b/title.module
@@ -15,7 +15,6 @@
  * - Taxonomy Term: name, description
  * - Comment: subject
  *
- * @todo: tests
  * @todo: API PHPdocs
  */
 
-- 
1.7.4.msysgit.0


From 62d1fcd4ecbf946c15877659566ad3156435cdf3 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach.git@psegno.it>
Date: Mon, 21 Mar 2011 12:12:39 +0100
Subject: [PATCH 13/13] Issue #924968 by plach: Improved comments/PHP docs

---
 tests/title.test |    2 +-
 title.module     |   11 ++++++++---
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/tests/title.test b/tests/title.test
index a67c47c..c950305 100644
--- a/tests/title.test
+++ b/tests/title.test
@@ -69,7 +69,7 @@ class TitleFieldReplacementTestCase extends DrupalWebTestCase {
 
     $this->assertNotIdentical($record->{$label_key}, $entity->{$label_key}, t('Entity label has been changed.'));
 
-    // Clear field cache so we synchrboization can be performed on field attach
+    // Clear field cache so synchronization can be performed on field attach
     // load.
     cache_clear_all('*', 'cache_field');
 
diff --git a/title.module b/title.module
index 709edcc..30b21d7 100644
--- a/title.module
+++ b/title.module
@@ -46,8 +46,8 @@ function title_entity_info_alter(&$info) {
 
         // Support add explicit support for entity_label().
         // @todo Currently core does not pass the entity type to the label
-        // callback thus rendering impossible a generalized handling of the
-        // entity label.
+        // callback thus making impossible a generalized handling of the entity
+        // label.
         // @see http://drupal.org/node/1096446
         if (false && isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
           $info[$entity_type]['label callback'] = 'title_entity_label';
@@ -59,6 +59,11 @@ function title_entity_info_alter(&$info) {
 
 /**
  * Return field replacement specific information.
+ *
+ * @param $entity_type
+ *   The name of the entity type.
+ * @param $legacy_field
+ *   (Otional) The legacy field name to be replaced.
  */
 function title_field_replacement_info($entity_type, $legacy_field = NULL) {
   $info = entity_get_info($entity_type);
@@ -74,7 +79,7 @@ function title_field_replacement_info($entity_type, $legacy_field = NULL) {
  * @param $entity
  *   The entity whose label has to be displayed.
  * @param $type
- *   The entity type.
+ *   The name of the entity type.
  * @param $langcode
  *   (Optional) The language the entity label has to be displayed in.
  *
-- 
1.7.4.msysgit.0

