Index: title.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/title/title.info,v
retrieving revision 1.2
diff -u -r1.2 title.info
--- title.info	11 Oct 2010 19:59:57 -0000	1.2
+++ title.info	3 Jan 2011 03:51:00 -0000
@@ -1,8 +1,6 @@
 ; $Id: title.info,v 1.2 2010/10/11 19:59:57 sun Exp $
 name = Title
-description = Allows entity titles/labels to be translated.
-package = Multilingual
+description = Replaces entity labels with fields.
 core = 7.x
-dependencies[] = locale
 files[] = title.module
 files[] = tests/title.test
Index: title.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/title/title.install,v
retrieving revision 1.1
diff -u -r1.1 title.install
--- title.install	11 Oct 2010 19:56:17 -0000	1.1
+++ title.install	3 Jan 2011 03:51:00 -0000
@@ -6,3 +6,13 @@
  * Installation functions for Title module.
  */
 
+/**
+ * Implements hook_install().
+ */
+function title_install() {
+  // Make sure the title is properly handled before any other module access it.
+  db_update('system')
+    ->fields(array('weight' => -100))
+    ->condition('name', 'title')
+    ->execute();
+}
Index: title.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/title/title.module,v
retrieving revision 1.19
diff -u -r1.19 title.module
--- title.module	11 Oct 2010 19:56:17 -0000	1.19
+++ title.module	3 Jan 2011 03:51:00 -0000
@@ -3,6 +3,363 @@
 
 /**
  * @file
- * Translatable entity title/label functionality.
+ * Replaces entity labels with fields.
  */
 
+/**
+ * Implements hook_field_widget_info().
+ */
+function title_field_widget_info_alter(&$info) {
+  $info['text_textfield']['field types'][] = 'title';
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function title_field_is_empty($item, $field) {
+  return !isset($item['value']) || $item['value'] === '';
+}
+
+/**
+ * Implements hook_field_presave().
+ */
+function title_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  title_field_sync($entity_type, $entity, FALSE);
+}
+
+/**
+ * Implements hook_field_load().
+ *
+ * @see title_entity_load()
+ */
+function title_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
+  title_entity_load($entities, $entity_type);
+}
+
+/**
+ * Implements hook_entity_load().
+ *
+ * Since the result of field_attach_load() is cached, title synchronization must
+ * be performed also here to ensure that there is always the correct value in
+ * $entity->title.
+ */
+function title_entity_load($entities, $type) {
+  if ($type == 'node') {
+    foreach ($entities as &$entity) {
+      title_field_sync($type, $entity);
+    }
+  }
+}
+
+/**
+ * Implements hook_field_attach_create_bundle().
+ *
+ * Make sure that new field bundles having title translation enabled contain a
+ * title_field field instance.
+ */
+function title_field_attach_create_bundle($entity_type, $bundle) {
+//  title_configure_bundle($entity_type, $bundle);
+}
+
+/**
+ * API function to sync the node title.
+ *
+ * @param string $entity_type
+ *   The name of the entity type.
+ * @param object $entity
+ *   The entity to work with.
+ * @param boolean $display
+ *   Specifies if the display- or the node-language is used.
+ */
+function title_field_sync($entity_type, &$entity, $display = TRUE) {
+  if (isset($entity->title) && title_field_replacement_enabled('node', $entity->type, 'title')) {
+    // Choose the appropriate language.
+    $langcode = ($display ? field_language($entity_type, $entity, 'title_field') : $entity->language);
+
+    if (isset($entity->title_field[$langcode][0]['value'])) {
+      $entity->title = $entity->title_field[$langcode][0]['value'];
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Add a submit handler to node forms and hide the standard title field.
+ */
+function title_form_node_form_alter(&$form, &$form_state, $form_id) {
+  if (title_field_replacement_enabled('node', $form['#node']->type, 'title')) {
+    array_unshift($form['#submit'], 'title_node_form_submit');
+    $form['title']['#access'] = FALSE;
+  }
+}
+
+/**
+ * Submit handler for node forms.
+ */
+function title_node_form_submit($form, &$form_state) {
+  $values = (object) $form_state['values'];
+  title_field_sync($form['#entity_type'], $values, FALSE);
+  $form_state['values'] = (array) $values;
+}
+
+/**
+ * Field Replacement
+ *
+ * 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. Field definitions have to be
+ * provided by the modules exploiting the API.
+ *
+ * Field Replacement implements its own entity description API to describe core
+ * legacy pseudo-fields:
+ * - Node: title
+ * - Taxonomy Term: name, description
+ * - Comment: subject
+ * - User: name 
+ *
+ * @todo Field Replacement could exploit an OOP-based Entity Description Handler
+ * (@see TranslationHandlerInterface) to offer a cleaner way to describe which
+ * entity properties actually are legacy pseudo fields that can be replaced. The
+ * EDH would also allow to have an easier way to generalize entities behaviors
+ * and to access entity properties: for instance to implement synchronization,
+ * Field Replacement has to deal with entity language which is defined only for
+ * nodes and comments.
+ */
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function title_entity_info_alter(&$info) {
+  $field_replacement_info = module_invoke_all('field_replacement_info');
+  drupal_alter('field_replacement_info', $field_replacement_info);
+
+  foreach ($info as $entity_type => $entity_info) {
+    if ($entity_info['fieldable'] && !empty($field_replacement_info[$entity_type])) {
+      $info[$entity_type]['field replacement'] = $field_replacement_info[$entity_type];
+      foreach ($info[$entity_type]['field replacement'] as $field_name => $fr_info) {
+        if (empty($fr_info['field']['field_name'])) {
+          $info[$entity_type]['field replacement'][$field_name]['field']['field_name'] = $field_name . '_field';
+        }
+        $info[$entity_type]['field replacement'][$field_name]['instance']['field_name'] = $info[$entity_type]['field replacement'][$field_name]['field']['field_name'];
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_replacement_info().
+ */
+function title_field_replacement_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(
+    'title' => array(
+      'field' => $field,
+      'instance' => array(
+        'label' => t('Title'),
+        'description' => t('A field replacing node title.'),
+      ) + $instance,
+    ),
+  );
+
+  $info['taxonomy_term'] = 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.'),
+      ) + $instance,
+    ),
+  );
+
+  $info['comment'] = array(
+    'subject' => array(
+      'field' => $field,
+      'instance' => array(
+        'label' => t('Subject'),
+        'description' => t('A field replacing comment subject.'),
+      ) + $instance,
+    ),
+  );
+
+  return $info;
+}
+
+/**
+ * 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) {
+        if (isset($bundle_info['admin'])) {
+          $path = $bundle_info['admin']['path'];
+
+          $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_name),
+//            'file' => 'title.admin.inc',
+          ) + $access;
+        }
+      }
+    }
+  }
+
+  return $items;
+}
+
+/**
+ * TODO
+ */
+function title_field_replacement_enabled($entity_type, $bundle, $field_name) {
+  $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);
+  return !empty($instance);
+}
+
+/**
+ * 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();
+      }
+    }
+  }
+}
+
+/**
+ * TODO
+ */
+function title_field_replacement_form($form, $form_state, $entity_type, $bundle, $field_name) {
+  $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);
+
+  $form['#entity_type'] = $entity_type;
+  $form['#bundle'] = $bundle;
+  $form['#field_name'] = $field_name;
+
+  $form['enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Replace %field with a field instance.', array('%field' => $field_name)),
+    '#description' => '', // @todo
+    '#default_value' => !empty($instance),
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
+  return $form;
+}
+
+/**
+ * TODO
+ */
+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';
+}
+
+/**
+ * TODO
+ */
+function title_toggle_field_replacement($entity_type, $bundle, $field_name) {
+  $entity_info = entity_get_info($entity_type);
+  $info = $entity_info['field replacement'][$field_name];
+  $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;
+  }
+}
