### Eclipse Workspace Patch 1.0
#P title
Index: title.api.php
===================================================================
RCS file: title.api.php
diff -N title.api.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ title.api.php	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,22 @@
+<?php
+/**
+ * @file
+ * Documentation of the hooks provided by this module
+ */
+
+/**
+ * Is called as soon as a translation of a title is saved.
+ *
+ * Use the param $title instead of $entity->title.
+ *
+ * @param string $entity_type
+ * @param stdClass $entity
+ *  <b>Attention:</b> This is a clone, changes to it won't affect the
+ *  "real" entity
+ * @param string $title
+ * @param string $langcode 2-Char lang code
+ *
+ * @see title_title_translate()
+ */
+function hook_title_translate($entity_type, $entity, $title, $langcode) {
+}
\ No newline at end of file
Index: title.info
===================================================================
RCS file: title.info
diff -N title.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ title.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,9 @@
+name = Title
+description = Provides translatable Titles
+core = 7.x
+dependencies[] = locale
+dependencies[] = translation
+package = Multilingual
+files[] = title.module
+files[] = title.title.inc
+files[] = title.migrate.inc
\ No newline at end of file
Index: title.install
===================================================================
RCS file: title.install
diff -N title.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ title.install	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file
+ * Installation file for the automatic nodetitle module
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function title_uninstall() {
+
+  // Remove field and all instances
+  // TODO What's the right way to do this?
+  $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+  foreach ($entity_types as $entity_type) {
+    $entity_info = entity_get_info($entity_type);
+    foreach ( $entity_info['bundles'] as $bundle) {
+      $instance = field_info_instances($entity_type, 'translatable_title', $bundle);
+      field_delete_instance($instance);
+      field_purge_instance($instance);
+    }
+  }
+  field_delete_field('translatable_title');
+  field_purge_field(field_info_field('translatable_title'));
+  db_drop_table('field_data_translatable_title');
+  db_drop_table('field_revision_translatable_title');
+
+  variable_del('translation_title_entity_types');
+  variable_del('translation_title_entity_types_controller');
+}
\ No newline at end of file
Index: title.migrate.inc
===================================================================
RCS file: title.migrate.inc
diff -N title.migrate.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ title.migrate.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @file
+ * Support for translatable_title fields in module migrate
+ */
+
+
+/**
+ * Translatable title handler for migrate
+ */
+class MigrateTranslatableTitleEntityHandler extends MigrateDestinationHandler {
+
+  /**
+   * Register all entity types as handled
+   */
+  public function __construct() {
+    $this->registerTypes(array('entity'));
+  }
+
+  /**
+   * Maps the title field to the translatable_title field if applicaple
+   * @param Migration $migration
+   * @param stdClass $entity
+   * @param stdClass $row
+   */
+  public function prepare(Migration $migration, stdClass $entity, stdClass $row) {
+    $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+    $entity_type = $migration->getDestination()->getEntityType();
+
+    // Entity has translation enabled?
+    if (isset($entity_types[$entity_type])) {
+      // Check if a mapping is necessary
+      if (property_exists($entity, 'title')) {
+        if (!property_exists($entity, 'translatable_title')) {
+          $entity->translatable_title = array($entity->language => array());
+        }
+        if (!isset($entity->translatable_title[$entity->language][0]['value'])) {
+          $entity->translatable_title[$entity->language][] = array('value' => $entity->title);
+        }
+      }
+      // Load orignal entity if there's already one.
+      // Make sure the default title is preserved to the original language value
+      if (property_exists($entity, 'nid') && strlen($entity->nid)) {
+        $orginal_entity = reset(entity_load($entity_type, array($entity->nid)));
+        if ($orginal_entity->translations->original != $entity->language) {
+          // What's not there can't be saved...
+          unset($entity->title);
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
Index: title.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/title/title.module,v
retrieving revision 1.18
diff -u -r1.18 title.module
--- title.module	8 Oct 2004 17:24:24 -0000	1.18
+++ title.module	28 Sep 2010 16:13:10 -0000
@@ -1,108 +1,446 @@
 <?php
-// $Id: title.module,v 1.18 2004/10/08 17:24:24 ax Exp $
+/**
+ * @file
+ * Makes entity title tranlatable
+ */
 
 /**
- * Implementation of hook_help().
+ * Implements hook_field_info().
  */
-function title_help($section) {
-  switch ($section) {
-    case 'admin/modules#description':
-      return t('Enables users to link to stories, articles or similar content by title.');
-  }
+function title_field_info() {
+  return array(
+    'translatable_title' => array(
+      'label' => t('Title'),
+      'description' => t('This field stores a translated node title.'),
+      'default_widget' => 'text_textfield',
+      'default_formatter' => 'text_default',
+      'no_ui' => TRUE,
+    ),
+  );
 }
 
 /**
- * Implementation of hook_menu().
+ * Implements hook_field_is_empty().
  */
-function title_menu() {
-  $items = array();
-  $items[] = array('path' => 'title', 'title' => t('search'),
-    'callback' => 'title_page',
-    'access' => user_access('access content'),
-    'type' => MENU_CALLBACK);
-  return $items;
+function title_field_is_empty($item, $field) {
+  return empty($item['value']);
 }
 
 /**
- * Menu callback; displays the matching node or a list of search results.
+ * Implements hook_field_schema().
+ * @TODO Do we need an index?
  */
-function title_page($query) {
-  $title = urldecode($query);
-  $result = db_query("SELECT n.*, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.title = '%s' AND n.status = 1 ORDER BY n.created DESC", $title);
+function title_field_schema() {
+  $columns = array(
+    'value' => array(
+      'description' => 'The language specific title of a entity',
+      'type' => 'varchar',
+      'length' => 255,
+      'default' => '',
+      'not null' => TRUE,
+    ),
+  );
+
+  return array(
+    'columns' => $columns,
+  );
+}
 
-  $title = trim(str_replace(array('_', '%', '*'), ' ', $title));
-  if (db_num_rows($result) == 0) {
-    // No node with exact title found; try a substring.
-    $result = db_query("SELECT n.*, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE n.title LIKE '%%%s%%' AND n.status = 1 ORDER BY n.created DESC", $title);
+/**
+ * Implements hook_hook_info().
+ */
+function title_hook_info() {
+  $hooks['title_translate'] = array(
+    'group' => 'title',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ *
+ * Overwrite the common EntityController of the entity type with the one
+ * from this module.
+ */
+function title_entity_info_alter(&$entity_info) {
+  $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+  $original_controllers = array();
+  foreach ($entity_types as $entity_type) {
+    if (isset($entity_info[$entity_type])) {
+      $original_controllers[$entity_type] = $entity_info[$entity_type]['controller class'];
+      $entity_info[$entity_type]['controller class'] = 'TitleEntityController';
+    }
   }
+  variable_set('translation_title_entity_types_controller', $original_controllers);
+}
 
-  if (db_num_rows($result) == 0 && module_exist('search')) {
-    // Still no matches, so return a full-text search.
-    search_view($title);
+/**
+ * Implements hook_field_attach_form().
+ */
+function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+  $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+  if (isset($entity_types[$entity_type])) {
+    // TODO Hide or remove?
+    $form['title']['#type'] = 'hidden';
+    unset(
+      $form['title']['#required'],
+      $form['title']['#weight'],
+      $form['title']['#title']
+    );
   }
-  else if (db_num_rows($result) == 1) {
-    $node = db_fetch_object($result);
-    $node = node_load(array('nid' => $node->nid));
-    print theme('page', node_show($node, NULL), $node->title);
+}
+
+/**
+ * Implements hook_field_attach_submit().
+ *
+ * @TODO How to make sure this module is the first whichs called?
+ */
+function title_field_attach_submit($entity_type, $entity, $form, &$form_state) {
+  $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+
+  // Choose the appropriate language - on insert the original language in
+  // translation is not set!
+  $langcode = (strlen($entity->translations->original))? $entity->translations->original: $entity->language;
+
+  // If the original language is changed, change the entity title value too
+  if (isset($entity_types[$entity_type]) && isset($entity->translatable_title[$langcode][0]['value'])) {
+    $entity->title = $entity->translatable_title[$langcode][0]['value'];
   }
-  else {
-    $header = array(t('Type'), t('Title'), t('Author'));
-    while ($node = db_fetch_object($result)) {
-      $type = ucfirst(module_invoke($node->type, 'node', 'name'));
-      $title = l($node->title, "node/$node->nid");
-      $author = format_name($node);
-      $rows[] = array(array('data' => $type, 'class' => 'type'), array('data' => $title, 'class' => 'content'), array('data' => $author, 'class' => 'author'));
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds the title settings to the content translation configuration
+ */
+function title_form_translation_admin_form_alter(&$form, &$form_state, $form_id) {
+  $options = array();
+
+  foreach (entity_get_info() as $entity_type => $info) {
+    if ($info['fieldable']) {
+      $options[$entity_type] = $info['label'];
     }
+  }
+  $form['translation_title'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Enable translation for titles of entity types'),
+    );
+
+  $form['translation_title']['translation_title_entity_types'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Entity types'),
+    '#description' => t('Select which entities should have a translatable title.'),
+    '#options' => $options,
+    '#default_value' => variable_get('translation_title_entity_types', array()),
+  );
+
+  $form['#submit'][] = 'title_form_translation_admin_form_submit';
+}
+
+/**
+ * Enable the title field on entities of the selected entity_types
+ */
+function title_form_translation_admin_form_submit($form, &$form_state) {
+  // Store new settings
+  variable_set('translation_title_entity_types', $form_state['values']['translation_title_entity_types']);
+
+  // Update field instances
+  title_configure_entity_types();
+}
+
+/**
+ * Implements hook_field_attach_create_bundle().
+ */
+function title_field_attach_create_bundle($entity_type, $bundle) {
+  // Make sure that new bundles, with title translation enabled, have the translatable_title instance
+  title_configure_entity_types();
+}
 
-    $output  = '<div id="title">';
-    $output .= theme('table', $header, $rows);
-    $output .= '</div>';
+/**
+ * Makes sure the translatable_title field is attached / removed according
+ * to the configuration
+ */
+function title_configure_entity_types() {
+  $entity_types = variable_get('translation_title_entity_types', array());
 
-    drupal_set_title(t('Matching Posts'));
-    print theme('page', $output);
+  // Make sure theres such a field to create a instance of
+  $field = field_read_field('translatable_title');
+  if (empty($field)) {
+    $field = array(
+      'field_name' => 'translatable_title',
+      'type' => 'translatable_title',
+      'cardinality' => 1,
+      'translatable' => TRUE,
+      'locked' => TRUE,
+    );
+    $field = field_create_field($field);
+  }
+
+  $created_instances = array();
+  $removed_instances = array();
+
+  // Assign the node title field to all translatable entites which translatable
+  // titles enabled
+  $field_type_info = field_info_field_types('translatable_title');
+  foreach ($entity_types as $entity_type => $state) {
+    // Skip entity_types where translation is disabled
+    if (translation_enabled($entity_type)) {
+      $bundles = field_info_bundles($entity_type);
+      foreach ($bundles as $bundle => $bundle_info) {
+        $instance = field_info_instance($entity_type, 'translatable_title', $bundle);
+        switch (TRUE) {
+          // Enable translatable title
+          case $state && !$instance:
+
+            // TODO There must be a smarter way to do this
+            $instances = field_info_instances($entity_type, $bundle);
+            $weight = 0;
+            foreach (field_info_instances($entity_type, $bundle) as $existing_instance) {
+              if (isset($existing_instance['widget']['weight']) && $existing_instance['widget']['weight'] < $weight) {
+                $weight = $existing_instance['widget']['weight'];
+              }
+            }
+            $weight--;
+            $instance = array(
+              'label' => $field_type_info['label'],
+              'description' => $field_type_info['description'],
+              'required' => TRUE,
+              'locked' => TRUE,
+              'field_name' => 'translatable_title',
+              'entity_type' => $entity_type,
+              'bundle' => $bundle,
+              'widget' => array('weight' => $weight),
+            );
+            field_create_instance($instance);
+            $created_instances[] = $bundle;
+            break;
+
+          // Disable translatable title
+          case !$state && $instance:
+            field_delete_instance($instance);
+            $removed_instances[] = $bundle;
+            break;
+        }
+      }
+    }
+  }
+  if (count($created_instances)) {
+    drupal_set_message(t('Translatable title was added to: @bundles.', array('@bundles' => implode(', ', $created_instances))));
+  }
+  if (count($removed_instances)) {
+    drupal_set_message(t('Translatable title was removed from: @bundles.', array('@bundles' => implode(', ', $removed_instances))));
+  }
+  if (!count($removed_instances) && !count($created_instances)) {
+    drupal_set_message(t('No changes for translatable title necessary.'));
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Remove default title field and remove settings link from translatable_title
+ */
+function title_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
+  if (isset($form['fields']['translatable_title'])) {
+    $form['fields']['translatable_title']['type']['#markup'] = $form['fields']['translatable_title']['type']['#title'];
+    $form['fields']['translatable_title']['type']['#cell_attributes']['colspan'] = 2;
+
+    unset(
+      $form['fields']['translatable_title']['type']['#type'],
+      $form['fields']['translatable_title']['type']['#title'],
+      $form['fields']['translatable_title']['type']['#href'],
+      $form['fields']['translatable_title']['type']['#options'],
+      $form['fields']['translatable_title']['widget_type'],
+      $form['fields']['title']
+    );
   }
 }
 
 /**
- * Implementation of hook_filter().
+ * Implements hook_form_FORM_ID_alter().
  */
-function title_filter($op, $delta = 0, $format = -1, $text = ''){
-  switch ($op) {
-    case 'list':
-      return array(0 => t('Title filter'));
-    case 'no cache':
-      return false;
-    case 'description':
-      return t('Enables users to link to stories, articles or similar content by title.');
-    case 'name':
-      return t('Title filter');
-    case 'process':
-      return _title_filter_process($text);
-    case 'settings':
-      return;
-    default:
-      return $text;
+function title_form_translation_edit_form_alter(&$form, &$form_state, $form_id) {
+  $form['#submit'][] = 'title_form_translation_edit_form_submit';
+  // TODO Why do we have to define it here? Could we change module translate?
+  if (isset($form['submit']['#submit'])) {
+    $form['submit']['#submit'][] = 'title_form_translation_edit_form_submit';
   }
 }
 
 /**
- * Filter [Node title|Description] links. '|Description' is optional.
+ * Function to trigger hook_title_translate().
+ */
+function title_form_translation_edit_form_submit($form, &$form_state) {
+  // Clone the entity to prevent unintentional changes...
+  $entity = clone $form['#entity'];
+
+  // TODO Is this a good idea?
+  $entity->language = $form['#language'];
+  $entity->title = $entity->translatable_title[$form['#language']][0]['value'];
+
+  module_invoke_all(
+    'title_translate',
+    $form['#entity_type'],
+    $entity,
+    $entity->translatable_title[$form['#language']][0]['value'],
+    $form['#language']
+  );
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * TODO Better idea needed - anyone?
  */
-function _title_filter_process($text) {
-  $pattern = '\[([^\|\]\n]+)(?>\|?)([^\]\n]*)\]';
-  // $1 == title: matches at least 1 char up to the first '|' or ']'.
-  // $2 == text: matches all after a following '|' (if there is) up to the next ']'.
-  //   May include '|'s.
-  $replacement = 'l(\'$2\' ? \'$2\' : \'$1\', \'title/\'. urlencode(\'$1\'))';
-  return preg_replace("/$pattern/e", $replacement, $text);
+function title_menu_alter(&$items) {
+    // Create tabs for all possible entity types.
+  foreach (entity_get_info() as $entity_type => $info) {
+    if (translation_enabled($entity_type)) {
+      $changeItem = $entity_type . '/%' . $entity_type . '/translate';
+      if (isset($items[$changeItem])) {
+        $items[$changeItem]['page callback'] = 'title_translation_overview_alter';
+      }
+    }
+  }
 }
 
-function title_filter_tips($delta, $format, $long = false) {
-  if ($long){
-    return t('Wiki-like [node title|text] links are enabled. These shortcuts generate a link labeled "text" to the node with the title "node title". If you omit "|text", the label becomes "node title". You may use a substring of a node title if desired. When multiple matching titles are found, a list of matching nodes will be displayed. If no matching titles are found, a full-text search is returned.');
-  }else{
-    return t('Link to content on this site using <em>[title|text]</em>.');
+/**
+ * Change translation overview
+ *
+ * TODO Better idea needed - anyone?
+ */
+function title_translation_overview_alter($entity_type, $entity) {
+
+  $build = translation_overview($entity_type, $entity);
+
+  // Add translated title to the overview of applicable
+  $entity_types = array_filter(variable_get('translation_title_entity_types', array()));
+  if (isset($entity_types[$entity_type]) && property_exists($entity, 'translatable_title')) {
+
+    $handler = translation_get_handler($entity_type, $entity);
+    $path = $handler->getViewPath();
+
+    $i = 0;
+    foreach (language_list() as $langcode => $language) {
+      if (isset($entity->translatable_title[$langcode])) {
+        $label = $entity->translatable_title[$langcode][0]['value'];
+        $rowTitle = $path ? l($label, $path, array('language' => $language)) : $label;
+        $build['translation_overview']['#rows'][$i][2] = $rowTitle;
+      }
+      $i++;
+    }
   }
+
+  return $build;
 }
 
-?>
\ No newline at end of file
+/**
+ * Wrapper arround EntityController of a entity type.
+ * Designed to be compatible with all other changes made to controller class
+ * by adding a wrapper instead simply overwrite the original controller.
+ *
+ * @ TODO Review desperately needed - this is some strange solution :)
+ *
+ * @link http://drupal.org/project/entity_api (perhaps we should be using this)
+ */
+class TitleEntityController implements DrupalEntityControllerInterface {
+
+  /**
+   * @var DrupalEntityController
+   */
+  protected $aggregatedController;
+
+  /**
+   * Constructor: sets basic variables.
+   * @param $entityType
+   *   The entity type for which the instance is created.
+   */
+  public function __construct($entityType) {
+
+    $aggregatedController = 'DrupalDefaultEntityController';
+    $original_controllers = variable_get('translation_title_entity_types_controller', array());
+    if (isset($original_controllers[$entityType])) {
+      $aggregatedController = $original_controllers[$entityType];
+    }
+    $this->aggregatedController = new $aggregatedController($entityType);
+  }
+
+  /**
+   * Make sure the wrapper grants access to the wrapped methods
+   * @TODO add check if called method is public?
+   */
+  public function __call($name, array $arguments ) {
+    $reflectMethod = new ReflectionMethod($this->aggregatedController, $name);
+    if (!$reflectMethod->isPublic()) {
+      throw new Exception('Fatal error: Call to private method ' . get_class($this->aggregatedController) . '::' . $name . '()');
+    }
+    $params = func_get_args();
+    return call_user_func_array(array($this->aggregatedController, $name), $params);
+  }
+
+  /**
+   * Make sure the wrapper grants access to the wrapped properties
+   * @TODO add check if requested property is public?
+   */
+  public function __get($name) {
+    $reflectProperty = new ReflectionProperty($this->aggregatedController, $name);
+    if (!$reflectProperty->isPublic()) {
+      throw new Exception('Fatal error: Call to private property ' . get_class($this->aggregatedController) . '::$' . $name);
+    }
+    return $this->aggregatedController->$name;
+  }
+
+  /**
+   * Make sure the wrapper grants access to the wrapped properties
+   * @TODO add check if requested property is public?
+   */
+  public function __set($name, $value) {
+    $reflectProperty = new ReflectionProperty($this->aggregatedController, $name);
+    if (!$reflectProperty->isPublic()) {
+      throw new Exception('Fatal error: Call to private property ' . get_class($this->aggregatedController) . '::$' . $name);
+    }
+    $this->aggregatedController->$name = $value;
+  }
+
+  /**
+   * Resets the internal, static entity cache.
+   */
+  public function resetCache() {
+    $params = func_get_args();
+    return call_user_func_array(array($this->aggregatedController, 'resetCache'), $params);
+  }
+
+  /**
+   * Loads one or more entities.
+   *
+   * @param $ids
+   *   An array of entity IDs, or FALSE to load all entities.
+   * @param $conditions
+   *   An array of conditions in the form 'field' => $value.
+   *
+   * @return
+   *   An array of entity objects indexed by their ids.
+   */
+  public function load($ids = array(), $conditions = array()) {
+    global $language_content;
+
+    $params = func_get_args();
+    $entities = call_user_func_array(array($this->aggregatedController, 'load'), $params);
+
+    foreach ($entities as &$entity) {
+      if (property_exists($entity, 'title')) {
+        // Make sure the original title is synced into the translatable_title
+        if (!isset($entity->translatable_title[$entity->translations->original])) {
+          $entity->translatable_title[$entity->language][0]['value'] = $entity->title;
+        }
+
+        // Use original language a default
+        $entity->title = $entity->translatable_title[$entity->translations->original][0]['value'];
+        if (isset($entity->translatable_title[$language_content->language][0]['value'])) {
+          $entity->title = $entity->translatable_title[$language_content->language][0]['value'];
+        }
+      }
+    }
+    return $entities;
+  }
+}
\ No newline at end of file
Index: title.title.inc
===================================================================
RCS file: title.title.inc
diff -N title.title.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ title.title.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Hook usage
+ */
+
+/**
+ * Implements hook_title_translate().
+ *
+ * Manages title translations for other modules as long as the don't
+ * implement this hook
+ *
+ * @param string $entity_type
+ * @param stdClass $entity
+ *  <b>Attention:</b> This is a clone, changes to it won't affect
+ *  the "real" entity
+ * @param string $title
+ * @param string $langcode
+ */
+function title_title_translate($entity_type, $entity, $title, $langcode) {
+  // Trigger pathauto
+  if (module_exists('pathauto')) {
+    $uri = entity_uri($entity_type, $entity);
+    module_load_include('inc', 'pathauto');
+    pathauto_create_alias(
+      $entity_type,
+      'update',
+      $uri['path'],
+      array($entity_type => $entity),
+      $entity->type,
+      $langcode
+    );
+  }
+}
\ No newline at end of file
