Index: modules/field/field.multilingual.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.multilingual.inc,v
retrieving revision 1.3
diff -u -r1.3 field.multilingual.inc
--- modules/field/field.multilingual.inc	16 Oct 2009 15:47:46 -0000	1.3
+++ modules/field/field.multilingual.inc	24 Oct 2009 17:27:55 -0000
@@ -95,7 +95,7 @@
  *   TRUE, if the handler is allowed to manage field translations.
  */
 function field_multilingual_check_translation_handlers($obj_type, $handler = NULL) {
-  $obj_info = field_info_fieldable_types($obj_type);
+  $obj_info = entity_get_info($obj_type);
 
   if (isset($handler)) {
     return isset($obj_info['translation'][$handler]) && !empty($obj_info['translation'][$handler]);
Index: modules/field/field.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.test,v
retrieving revision 1.64
diff -u -r1.64 field.test
--- modules/field/field.test	22 Oct 2009 00:49:12 -0000	1.64
+++ modules/field/field.test	24 Oct 2009 17:27:57 -0000
@@ -1789,7 +1789,7 @@
     $count = $query->execute()->fetchField();
     $this->assertEqual($count, 0, 'A field_config row for the field does not exist.');
   }
-    
+
   /**
    * Test reading back a field definition.
    */
@@ -1943,7 +1943,7 @@
   function testUpdateFieldType() {
     $field = array('field_name' => 'field_type', 'type' => 'number_decimal');
     $field = field_create_field($field);
-    
+
     $test_field = array('field_name' => 'field_type', 'type' => 'number_integer');
     try {
       field_update_field($test_field);
@@ -2463,7 +2463,7 @@
   function testTranslatableFieldSaveLoad() {
     // Enable field translations for nodes.
     field_test_entity_info_translatable('node', TRUE);
-    $obj_info = field_info_fieldable_types('node');
+    $obj_info = entity_get_info('node');
     $this->assertTrue(count($obj_info['translation']), t('Nodes are translatable.'));
 
     // Prepare the field translations.
@@ -2472,7 +2472,7 @@
     $object = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
     $field_translations = array();
     $available_languages = field_multilingual_available_languages($obj_type, $this->field);
-    $this->assertTrue(count($available_languages) > 1, t('Field is translatable.')); 
+    $this->assertTrue(count($available_languages) > 1, t('Field is translatable.'));
     foreach ($available_languages as $langcode) {
       $field_translations[$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
     }
@@ -2529,7 +2529,7 @@
   function _generateStubObjects($obj_type, $objects, $field_name = NULL) {
     $stubs = array();
     foreach ($objects as $obj) {
-      $stub = field_create_stub_entity($obj_type, field_extract_ids($obj_type, $obj));
+      $stub = entity_create_stub_entity($obj_type, entity_extract_ids($obj_type, $obj));
       if (isset($field_name)) {
         $stub->{$field_name} = $obj->{$field_name};
       }
Index: modules/field/field.crud.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.crud.inc,v
retrieving revision 1.40
diff -u -r1.40 field.crud.inc
--- modules/field/field.crud.inc	22 Oct 2009 00:49:12 -0000	1.40
+++ modules/field/field.crud.inc	24 Oct 2009 17:27:54 -0000
@@ -258,7 +258,7 @@
   // Disallow reserved field names. This can't prevent all field name
   // collisions with existing object properties, but some is better
   // than none.
-  foreach (field_info_fieldable_types() as $type => $info) {
+  foreach (entity_get_info() as $type => $info) {
     if (in_array($field['field_name'], $info['object keys'])) {
       throw new FieldException(t('Attempt to create field name %name which is reserved by entity type %type.', array('%name' => $field['field_name'], '%type' => $type)));
     }
@@ -387,7 +387,7 @@
     throw new FieldException('Attempt to update a non-existent field.');
   }
 
-  // Use the prior field values for anything not specifically set by the new 
+  // Use the prior field values for anything not specifically set by the new
   // field to be sure that all values are set.
   $field += $prior_field;
   $field['settings'] += $prior_field['settings'];
@@ -441,9 +441,9 @@
 
   // Clear caches
   field_cache_clear(TRUE);
-  
+
   // Invoke external hooks after the cache is cleared for API consistency.
-  module_invoke_all('field_update_field', $field, $prior_field, $has_data);  
+  module_invoke_all('field_update_field', $field, $prior_field, $has_data);
 }
 
 /**
Index: modules/field/field.info.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v
retrieving revision 1.24
diff -u -r1.24 field.info.inc
--- modules/field/field.info.inc	16 Oct 2009 02:47:16 -0000	1.24
+++ modules/field/field.info.inc	24 Oct 2009 17:27:54 -0000
@@ -53,12 +53,6 @@
  * formatter_type.
  *   * label, field types, behaviors: from hook_field_formatter_info()
  *   * module: module that exposes the formatter type
-
- * fieldable types: array of hook_entity_info() results, keyed by entity_type.
- *   * name, id key, revision key, bundle key, cacheable, bundles: from
- *     hook_entity_info()
- *   * module: module that exposes the entity type
- * @TODO use entity_get_info().
  */
 function _field_info_collate_types($reset = FALSE) {
   static $info;
@@ -139,34 +133,6 @@
       }
       drupal_alter('field_storage_info', $info['storage types']);
 
-      // Populate information about 'fieldable' entities.
-      foreach (module_implements('entity_info') as $module) {
-        $entities = (array) module_invoke($module, 'entity_info');
-        foreach ($entities as $name => $entity_info) {
-          if (!empty($entity_info['fieldable'])) {
-            // Provide defaults.
-            $entity_info += array(
-              'cacheable' => TRUE,
-              'translation' => array(),
-              'bundles' => array(),
-            );
-            $entity_info['object keys'] += array(
-              'revision' => '',
-              'bundle' => '',
-            );
-            // If no bundle key provided, then we assume a single bundle, named
-            // after the type of the object. Make sure the bundle created
-            // has the human-readable name we need for bundle messages.
-            if (empty($entity_info['object keys']['bundle']) && empty($entity_info['bundles'])) {
-              $entity_info['bundles'] = array($name => array('label' => $entity_info['label']));
-            }
-            $info['fieldable types'][$name] = $entity_info;
-            $info['fieldable types'][$name]['module'] = $module;
-          }
-        }
-      }
-      drupal_alter('entity_info', $info['fieldable types']);
-
       cache_set('field_info_types', $info, 'cache_field');
     }
   }
@@ -191,7 +157,7 @@
  *     both deleted and non-deleted fields. The bundles element is the same as
  *     for 'fields'.
  *   - instances: Array of existing instances, keyed by object type, bundle
- *     name and field name. This entry only lists non-deleted instances. 
+ *     name and field name. This entry only lists non-deleted instances.
  */
 function _field_info_collate_fields($reset = FALSE) {
   static $info;
@@ -456,29 +422,6 @@
 }
 
 /**
- * Return hook_fieldable_info() data.
- *
- * @param $obj_type
- *   (optional) A fieldable type name. If ommitted, all fieldable types will be
- *   returned.
- * @return
- *   Either a fieldable type description, as provided by hook_fieldable_info(),
- *   or an array of all existing fieldable types, keyed by fieldable type name.
- */
-function field_info_fieldable_types($obj_type = NULL) {
-  $info = _field_info_collate_types();
-  $fieldable_types = $info['fieldable types'];
-  if ($obj_type) {
-    if (isset($fieldable_types[$obj_type])) {
-      return $fieldable_types[$obj_type];
-    }
-  }
-  else {
-    return $fieldable_types;
-  }
-}
-
-/**
  * Return information about existing bundles.
  *
  * @param $obj_type
Index: modules/field/field.attach.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v
retrieving revision 1.55
diff -u -r1.55 field.attach.inc
--- modules/field/field.attach.inc	22 Oct 2009 00:49:12 -0000	1.55
+++ modules/field/field.attach.inc	24 Oct 2009 17:27:53 -0000
@@ -181,7 +181,7 @@
 
   // Iterate through the object's field instances.
   $return = array();
-  list(, , $bundle) = field_extract_ids($obj_type, $object);
+  list(, , $bundle) = entity_extract_ids($obj_type, $object);
 
   if ($options['deleted']) {
     $instances = field_read_instances(array('object_type' => $obj_type, 'bundle' => $bundle), array('include_deleted' => $options['deleted']));
@@ -298,7 +298,7 @@
   // is deleted, so we reference field data via the
   // $object->$field_name property.
   foreach ($objects as $object) {
-    list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+    list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
 
     if ($options['deleted']) {
       $instances = field_read_field(array('bundle' => $bundle, array('include_deleted' => $options['deleted'])));
@@ -488,7 +488,7 @@
   $form += (array) _field_invoke_default('form', $obj_type, $object, $form, $form_state, $options);
 
   // Add custom weight handling.
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $form['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
   $form['#pre_render'][] = '_field_extra_weights_pre_render';
   $form['#extra_fields'] = field_extra_fields($bundle);
@@ -536,7 +536,7 @@
   );
   $options += $default_options;
 
-  $info = field_info_fieldable_types($obj_type);
+  $info = entity_get_info($obj_type);
   // Only the most current revision of non-deleted fields for
   // cacheable fieldable types can be cached.
   $cache_read = $load_current && $info['cacheable'] && empty($options['deleted']);
@@ -591,7 +591,7 @@
     // Collect the storage backends used by the remaining fields in the objects.
     $storages = array();
     foreach ($queried_objects as $obj) {
-      list($id, $vid, $bundle) = field_extract_ids($obj_type, $obj);
+      list($id, $vid, $bundle) = entity_extract_ids($obj_type, $obj);
       if ($options['deleted']) {
         $instances = field_read_instances(array('object_type' => $obj_type, 'bundle' => $bundle), array('include_deleted' => $options['deleted']));
       }
@@ -636,7 +636,7 @@
     if ($cache_write) {
       foreach ($queried_objects as $id => $object) {
         $data = array();
-        list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+        list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
         $instances = field_info_instances($obj_type, $bundle);
         foreach ($instances as $instance) {
           $data[$instance['field_name']] = $queried_objects[$id]->{$instance['field_name']};
@@ -819,7 +819,7 @@
   _field_invoke_default('insert', $obj_type, $object);
   _field_invoke('insert', $obj_type, $object);
 
-  list($id, $vid, $bundle, $cacheable) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object);
 
   // Let other modules act on inserting the object, accumulating saved
   // fields along the way.
@@ -865,7 +865,7 @@
 function field_attach_update($obj_type, $object) {
   _field_invoke('update', $obj_type, $object);
 
-  list($id, $vid, $bundle, $cacheable) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object);
 
   // Let other modules act on updating the object, accumulating saved
   // fields along the way.
@@ -916,7 +916,7 @@
 function field_attach_delete($obj_type, $object) {
   _field_invoke('delete', $obj_type, $object);
 
-  list($id, $vid, $bundle, $cacheable) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object);
 
   // Collect the storage backends used by the fields in the objects.
   $storages = array();
@@ -955,7 +955,7 @@
 function field_attach_delete_revision($obj_type, $object) {
   _field_invoke('delete_revision', $obj_type, $object);
 
-  list($id, $vid, $bundle, $cacheable) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle, $cacheable) = entity_extract_ids($obj_type, $object);
 
   // Collect the storage backends used by the fields in the objects.
   $storages = array();
@@ -1193,7 +1193,7 @@
   $output = _field_invoke_default('view', $obj_type, $object, $build_mode, $null, $options);
 
   // Add custom weight handling.
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $output['#attached']['css'][] = drupal_get_path('module', 'field') . '/theme/field.css';
   $output['#pre_render'][] = '_field_extra_weights_pre_render';
   $output['#extra_fields'] = field_extra_fields($bundle);
@@ -1229,7 +1229,7 @@
  *   values.
  */
 function field_attach_preprocess($obj_type, $object, $element, &$variables) {
-  list(, , $bundle) = field_extract_ids($obj_type, $object);
+  list(, , $bundle) = entity_extract_ids($obj_type, $object);
 
   foreach (field_info_instances($obj_type, $bundle) as $instance) {
     $field_name = $instance['field_name'];
Index: modules/field/field.default.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.default.inc,v
retrieving revision 1.21
diff -u -r1.21 field.default.inc
--- modules/field/field.default.inc	16 Oct 2009 03:21:23 -0000	1.21
+++ modules/field/field.default.inc	24 Oct 2009 17:27:54 -0000
@@ -83,7 +83,7 @@
  * @see field_attach_view()
  */
 function field_default_view($obj_type, $object, $field, $instance, $langcode, $items, $build_mode) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
 
   $addition = array();
   $display = $instance['display'][$build_mode];
Index: modules/field/field.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.module,v
retrieving revision 1.44
diff -u -r1.44 field.module
--- modules/field/field.module	23 Oct 2009 22:24:13 -0000	1.44
+++ modules/field/field.module	24 Oct 2009 17:27:55 -0000
@@ -506,7 +506,7 @@
     $field_type = field_info_field_types($field['type']);
 
     // We need $field, $instance, $obj_type, $object to be able to display a value...
-    list(, , $bundle) = field_extract_ids($obj_type, $object);
+    list(, , $bundle) = entity_extract_ids($obj_type, $object);
     $instance = field_info_instance($obj_type, $field['field_name'], $bundle);
 
     $display = array(
@@ -655,36 +655,7 @@
 }
 
 /**
- * Helper function to extract id, vid, and bundle name from an object.
- *
- * @param $obj_type
- *   The type of $object; e.g. 'node' or 'user'.
- * @param $object
- *   The object from which to extract values.
- * @return
- *   A numerically indexed array (not a hash table) containing these
- *   elements:
- *
- *   0: primary id of the object
- *   1: revision id of the object, or NULL if $obj_type is not versioned
- *   2: bundle name of the object
- *   3: whether $obj_type's fields should be cached (TRUE/FALSE)
- */
-function field_extract_ids($obj_type, $object) {
-  // TODO D7 : prevent against broken 3rd party $node without 'type'.
-  $info = field_info_fieldable_types($obj_type);
-  // Objects being created might not have id/vid yet.
-  $id = isset($object->{$info['object keys']['id']}) ? $object->{$info['object keys']['id']} : NULL;
-  $vid = ($info['object keys']['revision'] && isset($object->{$info['object keys']['revision']})) ? $object->{$info['object keys']['revision']} : NULL;
-  // If no bundle key provided, then we assume a single bundle, named after the
-  // type of the object.
-  $bundle = $info['object keys']['bundle'] ? $object->{$info['object keys']['bundle']} : $obj_type;
-  $cacheable = $info['cacheable'];
-  return array($id, $vid, $bundle, $cacheable);
-}
-
-/**
- * Helper function to extract id, vid, and bundle name from an object.
+ * Helper function to extract the bundle name of from a bundle object.
  *
  * @param $obj_type
  *   The type of $object; e.g. 'node' or 'user'.
@@ -699,49 +670,20 @@
     return $bundle;
   }
 
-  $info = field_info_fieldable_types($obj_type);
+  $info = entity_get_info($obj_type);
   if (is_object($bundle) && isset($info['bundle keys']['bundle']) && isset($bundle->{$info['bundle keys']['bundle']})) {
     return $bundle->{$info['bundle keys']['bundle']};
   }
 }
 
 /**
- * Helper function to assemble an object structure with initial ids.
- *
- * This function can be seen as reciprocal to field_extract_ids().
- *
- * @param $obj_type
- *   The type of $object; e.g. 'node' or 'user'.
- * @param $ids
- *   A numerically indexed array, as returned by field_extract_ids(),
- *   containing these elements:
- *   0: primary id of the object
- *   1: revision id of the object, or NULL if $obj_type is not versioned
- *   2: bundle name of the object
- * @return
- *   An $object structure, initialized with the ids provided.
- */
-function field_create_stub_entity($obj_type, $ids) {
-  $object = new stdClass();
-  $info = field_info_fieldable_types($obj_type);
-  $object->{$info['object keys']['id']} = $ids[0];
-  if (isset($info['object keys']['revision']) && !is_null($ids[1])) {
-    $object->{$info['object keys']['revision']} = $ids[1];
-  }
-  if ($info['object keys']['bundle']) {
-    $object->{$info['object keys']['bundle']} = $ids[2];
-  }
-  return $object;
-}
-
-/**
  * Theme preprocess function for field.tpl.php.
  *
  * @see field.tpl.php
  */
 function template_preprocess_field(&$variables) {
   $element = $variables['element'];
-  list(, , $bundle) = field_extract_ids($element['#object_type'], $element['#object']);
+  list(, , $bundle) = entity_extract_ids($element['#object_type'], $element['#object']);
   $instance = field_info_instance($element['#object_type'], $element['#field_name'], $bundle);
   $field = field_info_field($element['#field_name']);
 
Index: modules/field/field.form.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.form.inc,v
retrieving revision 1.29
diff -u -r1.29 field.form.inc
--- modules/field/field.form.inc	15 Oct 2009 12:44:34 -0000	1.29
+++ modules/field/field.form.inc	24 Oct 2009 17:27:54 -0000
@@ -13,7 +13,7 @@
   // This could be called with no object, as when a UI module creates a
   // dummy form to set default values.
   if ($object) {
-    list($id, , ) = field_extract_ids($obj_type, $object);
+    list($id, , ) = entity_extract_ids($obj_type, $object);
   }
   $addition = array();
 
Index: modules/image/image.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/image/image.field.inc,v
retrieving revision 1.2
diff -u -r1.2 image.field.inc
--- modules/image/image.field.inc	17 Oct 2009 02:14:52 -0000	1.2
+++ modules/image/image.field.inc	24 Oct 2009 17:27:59 -0000
@@ -477,7 +477,7 @@
  */
 function theme_field_formatter_image_link_content($variables) {
   $element = $variables['element'];
-  list($id, $vid, $bundle) = field_extract_ids($element['#object_type'], $element['#object']);
+  list($id, $vid, $bundle) = entity_extract_ids($element['#object_type'], $element['#object']);
   return l(theme('field_formatter_image', $variables), $element['#object_type'] . '/' . $id, array('html' => TRUE));
 }
 
Index: modules/file/file.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.field.inc,v
retrieving revision 1.7
diff -u -r1.7 file.field.inc
--- modules/file/file.field.inc	15 Oct 2009 12:44:36 -0000	1.7
+++ modules/file/file.field.inc	24 Oct 2009 17:27:58 -0000
@@ -267,7 +267,7 @@
   }
 
   // Delete items from original object.
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $load_function = $obj_type . '_load';
 
   $original = $load_function($id);
@@ -287,7 +287,7 @@
  * Implement hook_field_delete().
  */
 function file_field_delete($obj_type, $object, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   foreach ($items as $delta => $item) {
     // For hook_file_references(), remember that this is being deleted.
     $item['file_field_name'] = $field['field_name'];
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.10
diff -u -r1.10 file.module
--- modules/file/file.module	23 Oct 2009 22:24:14 -0000	1.10
+++ modules/file/file.module	24 Oct 2009 17:27:59 -0000
@@ -858,7 +858,7 @@
  */
 function file_get_file_reference_count($file, $field = NULL, $field_type = 'file') {
   $fields = field_info_fields(NULL, $field, $field_type);
-  $types = field_info_fieldable_types();
+  $types = entity_get_info();
   $reference_count = 0;
 
   foreach ($fields as $field) {
@@ -875,7 +875,7 @@
       // If deleting the entire piece of content, decrement references.
       if (isset($file->file_field_type) && isset($file->file_field_id)) {
         if ($file->file_field_type == $obj_type) {
-          $info = field_info_fieldable_types($obj_type);
+          $info = entity_get_info($obj_type);
           $id = $types[$obj_type]['object keys']['id'];
           foreach ($type_references as $reference) {
             if ($file->file_field_id == $reference->$id) {
Index: modules/field_ui/field_ui.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.module,v
retrieving revision 1.11
diff -u -r1.11 field_ui.module
--- modules/field_ui/field_ui.module	23 Oct 2009 22:24:13 -0000	1.11
+++ modules/field_ui/field_ui.module	24 Oct 2009 17:27:58 -0000
@@ -47,83 +47,85 @@
     return $items;
   }
   // Create tabs for all possible bundles.
-  foreach (field_info_fieldable_types() as $obj_type => $info) {
-    foreach ($info['bundles'] as $bundle_name => $bundle_info) {
-      if (isset($bundle_info['admin'])) {
-        // Extract informations from the bundle description.
-        $path = $bundle_info['admin']['path'];
-        $bundle_arg = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $bundle_name;
-        $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
-        $instance_position = count(explode('/', $path)) + 1;
-
-        $items["$path/fields"] = array(
-          'title' => 'Manage fields',
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_field_overview_form', $obj_type, $bundle_arg),
-          'type' => MENU_LOCAL_TASK,
-          'weight' => 1,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $items["$path/fields/%field_ui_menu"] = array(
-          'title callback' => 'field_ui_menu_label',
-          'title arguments' => array($instance_position),
-          'load arguments' => array($obj_type, $bundle_arg),
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
-          'type' => MENU_LOCAL_TASK,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $items["$path/fields/%field_ui_menu/edit"] = array(
-          'title' => 'Edit instance settings',
-          'load arguments' => array($obj_type, $bundle_arg),
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
-          'type' => MENU_DEFAULT_LOCAL_TASK,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $items["$path/fields/%field_ui_menu/field-settings"] = array(
-          'title' => 'Edit field settings',
-          'load arguments' => array($obj_type, $bundle_arg),
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_field_settings_form', $obj_type, $bundle_arg, $instance_position),
-          'type' => MENU_LOCAL_TASK,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $items["$path/fields/%field_ui_menu/widget-type"] = array(
-          'title' => 'Change widget type',
-          'load arguments' => array($obj_type, $bundle_arg),
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_widget_type_form', $obj_type, $bundle_arg, $instance_position),
-          'type' => MENU_LOCAL_TASK,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $items["$path/fields/%field_ui_menu/delete"] = array(
-          'title' => 'Delete instance',
-          'load arguments' => array($obj_type, $bundle_arg),
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_field_delete_form', $obj_type, $bundle_arg, $instance_position),
-          'type' => MENU_LOCAL_TASK,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-
-        // 'Display fields' tab and context secondary tabs.
-        $items["$path/display"] = array(
-          'title' => 'Display fields',
-          'page callback' => 'drupal_get_form',
-          'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg),
-          'type' => MENU_LOCAL_TASK,
-          'weight' => 2,
-          'file' => 'field_ui.admin.inc',
-        ) + $access;
-        $tabs = field_ui_build_modes_tabs($obj_type);
-        foreach ($tabs as $key => $tab) {
-          $items["$path/display/$key"] = array(
-            'title' => $tab['title'],
-            'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg, $key),
-            'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
-            'weight' => $key == 'basic' ? 0 : 1,
+  foreach (entity_get_info() as $obj_type => $info) {
+    if ($info['fieldable']) {
+      foreach ($info['bundles'] as $bundle_name => $bundle_info) {
+        if (isset($bundle_info['admin'])) {
+          // Extract informations from the bundle description.
+          $path = $bundle_info['admin']['path'];
+          $bundle_arg = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $bundle_name;
+          $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
+          $instance_position = count(explode('/', $path)) + 1;
+
+          $items["$path/fields"] = array(
+            'title' => 'Manage fields',
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_field_overview_form', $obj_type, $bundle_arg),
+            'type' => MENU_LOCAL_TASK,
+            'weight' => 1,
             'file' => 'field_ui.admin.inc',
           ) + $access;
+          $items["$path/fields/%field_ui_menu"] = array(
+            'title callback' => 'field_ui_menu_label',
+            'title arguments' => array($instance_position),
+            'load arguments' => array($obj_type, $bundle_arg),
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
+            'type' => MENU_LOCAL_TASK,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+          $items["$path/fields/%field_ui_menu/edit"] = array(
+            'title' => 'Edit instance settings',
+            'load arguments' => array($obj_type, $bundle_arg),
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
+            'type' => MENU_DEFAULT_LOCAL_TASK,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+          $items["$path/fields/%field_ui_menu/field-settings"] = array(
+            'title' => 'Edit field settings',
+            'load arguments' => array($obj_type, $bundle_arg),
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_field_settings_form', $obj_type, $bundle_arg, $instance_position),
+            'type' => MENU_LOCAL_TASK,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+          $items["$path/fields/%field_ui_menu/widget-type"] = array(
+            'title' => 'Change widget type',
+            'load arguments' => array($obj_type, $bundle_arg),
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_widget_type_form', $obj_type, $bundle_arg, $instance_position),
+            'type' => MENU_LOCAL_TASK,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+          $items["$path/fields/%field_ui_menu/delete"] = array(
+            'title' => 'Delete instance',
+            'load arguments' => array($obj_type, $bundle_arg),
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_field_delete_form', $obj_type, $bundle_arg, $instance_position),
+            'type' => MENU_LOCAL_TASK,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+
+          // 'Display fields' tab and context secondary tabs.
+          $items["$path/display"] = array(
+            'title' => 'Display fields',
+            'page callback' => 'drupal_get_form',
+            'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg),
+            'type' => MENU_LOCAL_TASK,
+            'weight' => 2,
+            'file' => 'field_ui.admin.inc',
+          ) + $access;
+          $tabs = field_ui_build_modes_tabs($obj_type);
+          foreach ($tabs as $key => $tab) {
+            $items["$path/display/$key"] = array(
+              'title' => $tab['title'],
+              'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg, $key),
+              'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+              'weight' => $key == 'basic' ? 0 : 1,
+              'file' => 'field_ui.admin.inc',
+            ) + $access;
+          }
         }
       }
     }
Index: modules/field/modules/field_sql_storage/field_sql_storage.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module,v
retrieving revision 1.29
diff -u -r1.29 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	22 Oct 2009 00:49:12 -0000	1.29
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	24 Oct 2009 17:27:57 -0000
@@ -344,7 +344,7 @@
  * Implement hook_field_storage_write().
  */
 function field_sql_storage_field_storage_write($obj_type, $object, $op, $fields) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $etid = _field_sql_storage_etid($obj_type);
 
   foreach ($fields as $field_id) {
@@ -433,7 +433,7 @@
  * This function deletes data for all fields for an object from the database.
  */
 function field_sql_storage_field_storage_delete($obj_type, $object, $fields) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $etid = _field_sql_storage_etid($obj_type);
 
   foreach (field_info_instances($obj_type, $bundle) as $instance) {
@@ -451,7 +451,7 @@
  * an object.
  */
 function field_sql_storage_field_storage_purge($obj_type, $object, $field, $instance) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $etid = _field_sql_storage_etid($obj_type);
 
   $table_name = _field_sql_storage_tablename($field);
@@ -569,11 +569,11 @@
       $options['cursor']++;
       // If querying all revisions and the entity type has revisions, we need
       // to key the results by revision_ids.
-      $entity_type = field_info_fieldable_types($row->type);
+      $entity_type = entity_get_info($row->type);
       $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id;
 
       if (!isset($return[$row->type][$id])) {
-        $return[$row->type][$id] = field_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
+        $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
         $obj_count++;
       }
     }
@@ -594,7 +594,7 @@
  * This function actually deletes the data from the database.
  */
 function field_sql_storage_field_storage_delete_revision($obj_type, $object, $fields) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   $etid = _field_sql_storage_etid($obj_type);
 
   if (isset($vid)) {
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1030
diff -u -r1.1030 common.inc
--- includes/common.inc	24 Oct 2009 05:13:43 -0000	1.1030
+++ includes/common.inc	24 Oct 2009 17:27:52 -0000
@@ -5948,7 +5948,20 @@
           'controller class' => 'DrupalDefaultEntityController',
           'static cache' => TRUE,
           'load hook' => $name . '_load',
+          'bundles' => array(),
+          'object keys' => array(),
+          'cacheable' => TRUE,
+          'translation' => array(),
         );
+        $entity_info[$name]['object keys'] += array(
+          'revision' => '',
+          'bundle' => '',
+        );
+        // If no bundle key is provided, assume a single bundle, named after
+        // the entity type.
+        if (empty($entity_info[$name]['object keys']['bundle']) && empty($entity_info[$name]['bundles'])) {
+          $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label']));
+        }
       }
       // Let other modules alter the entity info.
       drupal_alter('entity_info', $entity_info);
@@ -5960,6 +5973,63 @@
 }
 
 /**
+ * Helper function to extract id, vid, and bundle name from an entity.
+ *
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ *   The entity from which to extract values.
+ * @return
+ *   A numerically indexed array (not a hash table) containing these
+ *   elements:
+ *
+ *   0: primary id of the entity
+ *   1: revision id of the entity, or NULL if $entity_type is not versioned
+ *   2: bundle name of the entity
+ *   3: whether $entity_type's fields should be cached (TRUE/FALSE)
+ */
+function entity_extract_ids($entity_type, $entity) {
+  $info = entity_get_info($entity_type);
+  // Objects being created might not have id/vid yet.
+  $id = isset($entity->{$info['object keys']['id']}) ? $entity->{$info['object keys']['id']} : NULL;
+  $vid = ($info['object keys']['revision'] && isset($entity->{$info['object keys']['revision']})) ? $entity->{$info['object keys']['revision']} : NULL;
+  // If no bundle key provided, then we assume a single bundle, named after the
+  // entity type.
+  $bundle = $info['object keys']['bundle'] ? $entity->{$info['object keys']['bundle']} : $entity_type;
+  $cacheable = $info['cacheable'];
+  return array($id, $vid, $bundle, $cacheable);
+}
+
+/**
+ * Helper function to assemble an object structure with initial ids.
+ *
+ * This function can be seen as reciprocal to entity_extract_ids().
+ *
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $ids
+ *   A numerically indexed array, as returned by entity_extract_ids(),
+ *   containing these elements:
+ *   0: primary id of the entity
+ *   1: revision id of the entity, or NULL if $entity_type is not versioned
+ *   2: bundle name of the entity
+ * @return
+ *   An entity structure, initialized with the ids provided.
+ */
+function entity_create_stub_entity($entity_type, $ids) {
+  $entity = new stdClass();
+  $info = entity_get_info($entity_type);
+  $entity->{$info['object keys']['id']} = $ids[0];
+  if (isset($info['object keys']['revision']) && !is_null($ids[1])) {
+    $entity->{$info['object keys']['revision']} = $ids[1];
+  }
+  if ($info['object keys']['bundle']) {
+    $entity->{$info['object keys']['bundle']} = $ids[2];
+  }
+  return $entity;
+}
+
+/**
  * Load entities from the database.
  *
  * This function should be used whenever you need to load more than one entity
Index: includes/entity.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/entity.inc,v
retrieving revision 1.2
diff -u -r1.2 entity.inc
--- includes/entity.inc	16 Oct 2009 03:47:13 -0000	1.2
+++ includes/entity.inc	24 Oct 2009 17:27:52 -0000
@@ -68,7 +68,7 @@
     $this->idKey = $this->entityInfo['object keys']['id'];
 
     // Check if the entity type supports revisions.
-    if (isset($this->entityInfo['object keys']['revision'])) {
+    if (!empty($this->entityInfo['object keys']['revision'])) {
       $this->revisionKey = $this->entityInfo['object keys']['revision'];
       $this->revisionTable = $this->entityInfo['revision table'];
     }
Index: modules/rdf/rdf.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v
retrieving revision 1.5
diff -u -r1.5 rdf.module
--- modules/rdf/rdf.module	23 Oct 2009 22:24:17 -0000	1.5
+++ modules/rdf/rdf.module	24 Oct 2009 17:27:59 -0000
@@ -310,7 +310,7 @@
 function rdf_entity_load($entities, $type) {
   foreach ($entities as $entity) {
     // Extracts the bundle of the entity being loaded.
-    list($id, $vid, $bundle) = field_extract_ids($type, $entity);
+    list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
     $entity->rdf_mapping = rdf_mapping_load($type, $bundle);
   }
 }
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.530
diff -u -r1.530 taxonomy.module
--- modules/taxonomy/taxonomy.module	24 Oct 2009 05:13:44 -0000	1.530
+++ modules/taxonomy/taxonomy.module	24 Oct 2009 17:28:01 -0000
@@ -1162,7 +1162,7 @@
 
   // Determine object types that are not cacheable.
   $obj_types = array();
-  foreach (field_info_fieldable_types() as $obj_type => $info) {
+  foreach (entity_get_info() as $obj_type => $info) {
     if (isset($info['cacheable']) && !$info['cacheable']) {
       $obj_types[] = $obj_type;
     }
Index: modules/locale/locale.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.field.inc,v
retrieving revision 1.1
diff -u -r1.1 locale.field.inc
--- modules/locale/locale.field.inc	16 Oct 2009 02:04:42 -0000	1.1
+++ modules/locale/locale.field.inc	24 Oct 2009 17:27:59 -0000
@@ -17,7 +17,7 @@
   $available_languages = field_multilingual_content_languages();
   // @todo: Unify language neutral language codes.
   $selected_language = empty($node->language) ? FIELD_LANGUAGE_NONE : $node->language;
-  list(, , $bundle) = field_extract_ids('node', $node);
+  list(, , $bundle) = entity_extract_ids('node', $node);
 
   foreach (field_info_instances('node', $bundle) as $instance) {
     $field_name = $instance['field_name'];
@@ -44,7 +44,7 @@
   // Lazily init fallback values and candidates to avoid unnecessary calls.
   $fallback_values = array();
   $fallback_candidates = NULL;
-  list(, , $bundle) = field_extract_ids($context['obj_type'], $context['object']);
+  list(, , $bundle) = entity_extract_ids($context['obj_type'], $context['object']);
 
   foreach (field_info_instances($context['obj_type'], $bundle) as $instance) {
     $field_name = $instance['field_name'];
Index: modules/simpletest/tests/field_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.module,v
retrieving revision 1.34
diff -u -r1.34 field_test.module
--- modules/simpletest/tests/field_test.module	23 Oct 2009 22:24:17 -0000	1.34
+++ modules/simpletest/tests/field_test.module	24 Oct 2009 17:28:00 -0000
@@ -98,7 +98,7 @@
     $entity_info[$obj_type]['translation']['field_test'] = $translatable;
   }
   // Disable locale as a translation handler.
-  foreach (field_info_fieldable_types() as $obj_type => $info) {
+  foreach ($entity_info as $obj_type => $info) {
     $entity_info[$obj_type]['translation']['locale'] = FALSE;
   }
 }
@@ -817,7 +817,7 @@
 function field_test_field_storage_write($obj_type, $object, $op, $fields) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
 
   foreach ($fields as $field_id) {
     $field = field_info_field_by_id($field_id);
@@ -885,7 +885,7 @@
  * Implement hook_field_storage_delete().
  */
 function field_test_field_storage_delete($obj_type, $object, $fields) {
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
 
   // Note: reusing field_test_storage_purge(), like field_sql_storage.module
   // does, is highly inefficient in our case...
@@ -903,7 +903,7 @@
 function field_test_field_storage_purge($obj_type, $object, $field, $instance) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
 
   $field_data = &$data[$field['id']];
   foreach (array('current', 'revisions') as $sub_table) {
@@ -923,7 +923,7 @@
 function field_test_field_storage_delete_revision($obj_type, $object, $fields) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = field_extract_ids($obj_type, $object);
+  list($id, $vid, $bundle) = entity_extract_ids($obj_type, $object);
   foreach ($fields as $field_id) {
     $field_data = &$data[$field_id];
     foreach (array('current', 'revisions') as $sub_table) {
@@ -1019,11 +1019,11 @@
           $cursor++;
           // If querying all revisions and the entity type has revisions, we need
           // to key the results by revision_ids.
-          $entity_type = field_info_fieldable_types($row->type);
+          $entity_type = entity_get_info($row->type);
           $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id;
 
           if (!isset($return[$row->type][$id])) {
-            $return[$row->type][$id] = field_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
+            $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
             $obj_count++;
           }
         }
@@ -1069,7 +1069,7 @@
   if ($field['storage']['type'] == 'field_test_storage_failure') {
     throw new Exception('field_test_storage_failure engine always fails to create fields');
   }
-    
+
   $data = _field_test_storage_data();
 
   $data[$field['id']] = array(
