### Eclipse Workspace Patch 1.0
#P drupal_test_7
Index: modules/field/field.info.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v
retrieving revision 1.6
diff -u -r1.6 field.info.inc
--- modules/field/field.info.inc	5 Jun 2009 18:25:41 -0000	1.6
+++ modules/field/field.info.inc	27 Jun 2009 00:36:50 -0000
@@ -123,16 +123,18 @@
         foreach ($fieldable_types as $name => $fieldable_info) {
           // Provide defaults.
           $fieldable_info += array(
-            'revision key' => '',
-            'bundle key' => '',
             'cacheable' => TRUE,
             'bundles' => array(),
           );
+          $fieldable_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($fieldable_info['bundle key'])) {
-            $fieldable_info['bundles'] = array($name => $fieldable_info['name']);
+          if (empty($fieldable_info['object keys']['bundle']) && empty($fieldable_info['bundles'])) {
+            $fieldable_info['bundles'] = array($name => array('label' => $fieldable_info['label']));
           }
           $info['fieldable types'][$name] = $fieldable_info;
           $info['fieldable types'][$name]['module'] = $module;
@@ -363,7 +365,7 @@
 }
 
 /**
- * Identity the type of entity that created a bundle.
+ * Identify the type of entity that created a bundle.
  * // TODO : might not be needed depending on how we solve
  * // the 'namespace bundle names' issue
  */
Index: modules/field/field.attach.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.attach.inc,v
retrieving revision 1.26
diff -u -r1.26 field.attach.inc
--- modules/field/field.attach.inc	25 Jun 2009 06:23:37 -0000	1.26
+++ modules/field/field.attach.inc	27 Jun 2009 00:36:50 -0000
@@ -382,8 +382,8 @@
  *   The type of $object; e.g. 'node' or 'user'.
  * @param $objects
  *   An array of objects for which to load fields, keyed by object id.
- *   Each object needs to have its 'bundle key', 'id key' and (if applicable)
- *   'revision key' filled.
+ *   Each object needs to have its 'bundle', 'id' and (if applicable)
+ *   'revision' keys filled.
  * @param $age
  *   FIELD_LOAD_CURRENT to load the most recent revision for all
  *   fields, or FIELD_LOAD_REVISION to load the version indicated by
@@ -477,8 +477,8 @@
  *   The type of $object; e.g. 'node' or 'user'.
  * @param $objects
  *   An array of objects for which to load fields, keyed by object id.
- *   Each object needs to have its 'bundle key', 'id key' and 'revision key'
- *   filled.
+ *   Each object needs to have its 'bundle', 'id' and (if applicable)
+ *   'revision' keys filled.
  * @returns
  *   On return, the objects in $objects are modified by having the
  *   appropriate set of fields added.
@@ -538,8 +538,8 @@
  * @param $obj_type
  *   The type of $object; e.g. 'node' or 'user'.
  * @param $object
- *   The object being submitted. The 'bundle key', 'id key' and (if applicable)
- *   'revision key' should be present. The actual field values will be read
+ *   The object being submitted. The 'bundle', 'id' and (if applicable)
+ *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
  * @param $form
  *   The form structure.
@@ -570,8 +570,8 @@
  * @param $obj_type
  *   The type of $object; e.g. 'node' or 'user'.
  * @param $object
- *   The object being submitted. The 'bundle key', 'id key' and (if applicable)
- *   'revision key' should be present. The actual field values will be read
+ *   The object being submitted. The 'bundle', 'id' and (if applicable)
+ *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
  * @param $form
  *   The form structure to fill in.
@@ -1008,20 +1008,42 @@
  *   2: bundle name of the object
  *   3: whether $obj_type's fields should be cached (TRUE/FALSE)
  */
-function field_attach_extract_ids($object_type, $object) {
+function field_attach_extract_ids($obj_type, $object) {
   // TODO D7 : prevent against broken 3rd party $node without 'type'.
-  $info = field_info_fieldable_types($object_type);
+  $info = field_info_fieldable_types($obj_type);
   // Objects being created might not have id/vid yet.
-  $id = isset($object->{$info['id key']}) ? $object->{$info['id key']} : NULL;
-  $vid = ($info['revision key'] && isset($object->{$info['revision key']})) ? $object->{$info['revision key']} : NULL;
+  $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['bundle key'] ? $object->{$info['bundle key']} : $object_type;
+  $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.
+ *
+ * @param $obj_type
+ *   The type of $object; e.g. 'node' or 'user'.
+ * @param $bundle
+ *   The bundle object (or string if bundles for this object type do not exist
+ *   as standalone objects).
+ * @return
+ *   The bundle name.
+ */
+function field_attach_extract_bundle($obj_type, $bundle) {
+  if (is_string($bundle)) {
+    return $bundle;
+  }
+
+  $info = field_info_fieldable_types($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_attach_extract_ids().
@@ -1040,12 +1062,12 @@
 function field_attach_create_stub_object($obj_type, $ids) {
   $object = new stdClass();
   $info = field_info_fieldable_types($obj_type);
-  $object->{$info['id key']} = $ids[0];
-  if (isset($info['revision key']) && !is_null($ids[1])) {
-    $object->{$info['revision key']} = $ids[1];
+  $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['bundle key']) {
-    $object->{$info['bundle key']} = $ids[2];
+  if ($info['object keys']['bundle']) {
+    $object->{$info['object keys']['bundle']} = $ids[2];
   }
   return $object;
 }
Index: modules/field/field.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.api.php,v
retrieving revision 1.15
diff -u -r1.15 field.api.php
--- modules/field/field.api.php	22 Jun 2009 09:10:04 -0000	1.15
+++ modules/field/field.api.php	27 Jun 2009 00:36:49 -0000
@@ -9,47 +9,81 @@
 /**
  * Inform the Field API about one or more fieldable types.
  *
- * Inform the Field API about one or more fieldable types, (object
- * types to which fields can be attached).
+ * Inform the Field API about one or more fieldable types (object types to
+ * which fields can be attached).
  *
  * @return
  *   An array whose keys are fieldable object type names and
- *   whose values identify properties of those types that the Field
- *   system needs to know about:
- *
- *   name: The human-readable name of the type.
- *   id key: The object property that contains the primary id for the
- *     object. Every object passed to the Field API must
- *     have this property and its value must be numeric.
- *   revision key: The object property that contains the revision id
- *     for the object, or NULL if the object type is not
- *     versioned. The Field API assumes that all revision ids are
- *     unique across all instances of a type; this means, for example,
- *     that every object's revision ids cannot be 0, 1, 2, ...
- *   bundle key: The object property that contains the bundle name for
- *     the object (bundle name is what nodes call "content type").
- *     The bundle name defines which fields are connected to the object.
- *   cacheable: A boolean indicating whether Field API should cache
+ *   whose values are arrays with the following key/value pairs:
+ *   - label: The human-readable name of the type.
+ *   - object keys: An array describing how the Field API can extract the
+ *     informations it needs from the objects of the type.
+ *     - id: The name of the property that contains the primary id of the
+ *       object. Every object passed to the Field API must have this property
+ *       and its value must be numeric.
+ *     - revision: The name of the property that contains the revision id of
+ *       the object. The Field API assumes that all revision ids are unique
+ *       across all objects of a type.
+ *       This element can be omitted if the objects of this type are not
+ *       versionable.
+ *     - bundle: The name of the property that contains the bundle name for the
+ *       object. The bundle name defines which set of fields are attached to
+ *       the object (e.g. what nodes call "content type").
+ *       This element can be omitted if this type has no bundles (all objects
+ *       have the same fields).
+ *   - bundle keys: An array describing how the Field API can extract the
+ *     informations it needs from the bundle objects for this type (e.g
+ *     $vocabulary objects for terms; not applicable for nodes).
+ *     This element can be omitted if this type's bundles do not exist as
+ *     standalone objects.
+ *     - bundle: The name of the property that contains the name of the bundle
+ *       object.
+ *   - cacheable: A boolean indicating whether Field API should cache
  *     loaded fields for each object, reducing the cost of
  *     field_attach_load().
- *   bundles: An array of all existing bundle names for this object
- *     type. TODO: Define format. TODO: I'm unclear why we need
- *     this.
+ *   - bundles: An array describing all bundles for this object type.
+ *     Keys are bundles machine names, as found in the objects' 'bundle'
+ *     property (defined in the 'object keys' entry above).
+ *     - label: The human-readable name of the bundle.
+ *     - admin: An array of informations that allow Field UI pages (currently
+ *       implemented in a contributed module) to attach themselves to the
+ *       existing administration pages for the bundle.
+ *       - path: the path of the bundle's main administration page, as defined
+ *         in hook_menu(). If the path includes a placeholder for the bundle,
+ *         the 'bundle argument', 'bundle helper' and 'real path' keys below
+ *         are required.
+ *       - bundle argument: The position of the placeholder in 'path', if any.
+ *       - real path: The actual path (no placeholder) of the bundle's main
+ *         administration page. This will be used to generate links.
+ *       - access callback: As in hook_menu(). 'user_access' will be assumed if
+ *         no value is provided.
+ *       - access arguments: As in hook_menu().
  */
 function hook_fieldable_info() {
   $return = array(
-    'node' => array(
-      'name' => t('Node'),
-      'id key' => 'nid',
-      'revision key' => 'vid',
-      'bundle key' => 'type',
-      // Node.module handles its own caching.
-      'cacheable' => FALSE,
-      // Bundles must provide human readable name so
-      // we can create help and error messages about them.
-      'bundles' => node_type_get_names(),
+    'taxonomy_term' => array(
+      'label' => t('Taxonomy term'),
+      'object keys' => array(
+        'id' => 'tid',
+        'bundle' => 'vocabulary_machine_name',
+      ),
+      'bundle keys' => array(
+        'bundle' => 'machine_name',
+      ),
+      'bundles' => array(),
     ),
   );
+  foreach (taxonomy_get_vocabularies() as $vocabulary) {
+    $return['taxonomy_term']['bundles'][$vocabulary->machine_name] = array(
+      'label' => $vocabulary->name,
+      'admin' => array(
+        'path' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+        'real path' => 'admin/content/taxonomy/' . $vocabulary->vid,
+        'bundle argument' => 3,
+        'access arguments' => array('administer taxonomy'),
+      ),
+    );
+  }
   return $return;
 }
 
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1073
diff -u -r1.1073 node.module
--- modules/node/node.module	24 Jun 2009 18:16:38 -0000	1.1073
+++ modules/node/node.module	27 Jun 2009 00:36:52 -0000
@@ -127,17 +127,28 @@
 function node_fieldable_info() {
   $return = array(
     'node' => array(
-      'name' => t('Node'),
-      'id key' => 'nid',
-      'revision key' => 'vid',
-      'bundle key' => 'type',
+      'label' => t('Node'),
+      'object keys' => array(
+        'id' => 'nid',
+        'revision' => 'vid',
+        'bundle' => 'type',
+      ),
       // Node.module handles its own caching.
       // 'cacheable' => FALSE,
-      // Bundles must provide human readable name so
-      // we can create help and error messages about them.
-      'bundles' => node_type_get_names(),
+      'bundles' => array(),
     ),
   );
+  // Bundles must provide a human readable name so we can create help and error
+  // messages, and the path to attach Field admin pages to.
+  foreach (node_type_get_names() as $type => $name) {
+    $return['node']['bundles'][$type] = array(
+      'label' => $name,
+      'admin' => array(
+        'path' => 'admin/build/node-type/' . str_replace('_', '-', $type),
+        'access arguments' => array('administer content types'),
+      ),
+    );
+  }
   return $return;
 }
 
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.14
diff -u -r1.14 field_sql_storage.module
--- modules/field/modules/field_sql_storage/field_sql_storage.module	15 Jun 2009 10:10:46 -0000	1.14
+++ modules/field/modules/field_sql_storage/field_sql_storage.module	27 Jun 2009 00:36:50 -0000
@@ -415,7 +415,7 @@
     // 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);
-    $id = ($load_current || empty($entity_type['revision key'])) ? $row->entity_id : $row->revision_id;
+    $id = ($load_current || empty($entity_type['object keys']['revision'])) ? $row->entity_id : $row->revision_id;
 
     if ($load_values) {
       // Populate actual field values.
Index: modules/simpletest/tests/field_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/field_test.module,v
retrieving revision 1.9
diff -u -r1.9 field_test.module
--- modules/simpletest/tests/field_test.module	27 May 2009 18:34:00 -0000	1.9
+++ modules/simpletest/tests/field_test.module	27 Jun 2009 00:36:52 -0000
@@ -26,12 +26,12 @@
  */
 function field_test_menu() {
   $items = array();
-  $info = field_test_fieldable_info();
+  $bundles = field_info_bundles('test_entity');
 
-  foreach (array_keys($info['test_entity']['bundles']) as $bundle) {
-    $bundle_url_str = str_replace('_', '-', $bundle);
+  foreach ($bundles as $bundle_name => $bundle_info) {
+    $bundle_url_str = str_replace('_', '-', $bundle_name);
     $items['test-entity/add/' . $bundle_url_str] = array(
-      'title' => "Add $bundle test_entity",
+      'title' => t('Add %bundle test_entity', array('%bundle' => $bundle_info['label'])),
       'page callback' => 'field_test_entity_add',
       'page arguments' => array(2),
       'access arguments' => array('administer field_test content'),
@@ -61,23 +61,27 @@
  * Define a test fieldable entity.
  */
 function field_test_fieldable_info() {
-  $bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
+  $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
   return array(
     'test_entity' => array(
       'name' => t('Test Entity'),
-      'id key' => 'ftid',
-      'revision key' => 'ftvid',
+      'object keys' => array(
+        'id' => 'ftid',
+        'revision' => 'ftvid',
+        'bundle' => 'fttype',
+      ),
       'cacheable' => FALSE,
-      'bundle key' => 'fttype',
       'bundles' => $bundles,
     ),
     // This entity type doesn't get form handling for now...
     'test_cacheable_entity' => array(
       'name' => t('Test Entity, cacheable'),
-      'id key' => 'ftid',
-      'revision key' => 'ftvid',
+      'object keys' => array(
+        'id' => 'ftid',
+        'revision' => 'ftvid',
+        'bundle' => 'fttype',
+      ),
       'cacheable' => TRUE,
-      'bundle key' => 'fttype',
       'bundles' => $bundles,
     ),
   );
@@ -86,18 +90,18 @@
 /**
  * Create a new bundle for test_entity objects.
  *
- * @param $bundle
+ * @param $bundle_name
  *   The machine-readable name of the bundle.
  * @param $text
  *   The human-readable name of the bundle. If none is provided, the machine
  *   name will be used.
  */
-function field_test_create_bundle($bundle, $text = NULL) {
-  $bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
-  $bundles += array($bundle => $text ? $text : $bundle);
+function field_test_create_bundle($bundle_name, $text = NULL) {
+  $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
+  $bundles += array($bundle_name => array('label' => $text ? $text : $bundle_name));
   variable_set('field_test_bundles', $bundles);
 
-  field_attach_create_bundle($bundle);
+  field_attach_create_bundle($bundle_name);
 }
 
 /**
@@ -109,7 +113,7 @@
  *   The new machine-readable name of the bundle.
  */
 function field_test_rename_bundle($bundle_old, $bundle_new) {
-  $bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
+  $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
   $bundles[$bundle_new] = $bundles[$bundle_old];
   unset($bundles[$bundle_old]);
   variable_set('field_test_bundles', $bundles);
@@ -120,15 +124,15 @@
 /**
  * Delete a bundle for test_entity objects.
  *
- * @param $bundle
+ * @param $bundle_name
  *   The machine-readable name of the bundle to delete.
  */
-function field_test_delete_bundle($bundle) {
-  $bundles = variable_get('field_test_bundles', array('test_bundle' => 'Test Bundle'));
-  unset($bundles[$bundle]);
+function field_test_delete_bundle($bundle_name) {
+  $bundles = variable_get('field_test_bundles', array('test_bundle' => array('label' => 'Test Bundle')));
+  unset($bundles[$bundle_name]);
   variable_set('field_test_bundles', $bundles);
 
-  field_attach_delete_bundle($bundle);
+  field_attach_delete_bundle($bundle_name);
 }
 
 /**
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1002
diff -u -r1.1002 user.module
--- modules/user/user.module	22 Jun 2009 09:10:07 -0000	1.1002
+++ modules/user/user.module	27 Jun 2009 00:36:54 -0000
@@ -89,8 +89,19 @@
 function user_fieldable_info() {
   $return = array(
     'user' => array(
-      'name' => t('User'),
-      'id key' => 'uid',
+      'label' => t('User'),
+      'object keys' => array(
+        'id' => 'uid',
+      ),
+      'bundles' => array(
+        'user' => array(
+          'label' => t('User'),
+          'admin' => array(
+            'path' => 'admin/settings/user',
+            'access arguments' => array('administer users'),
+          ),
+        ),
+      ),
     ),
   );
   return $return;
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.480
diff -u -r1.480 taxonomy.module
--- modules/taxonomy/taxonomy.module	22 Jun 2009 09:10:06 -0000	1.480
+++ modules/taxonomy/taxonomy.module	27 Jun 2009 00:36:53 -0000
@@ -24,12 +24,28 @@
 function taxonomy_fieldable_info() {
   $return = array(
     'taxonomy_term' => array(
-      'name' => t('Taxonomy term'),
-      'id key' => 'tid',
-      'bundle key' => 'vocabulary_machine_name',
-      'bundles' => taxonomy_vocabulary_get_names(),
+      'label' => t('Taxonomy term'),
+      'object keys' => array(
+        'id' => 'tid',
+        'bundle' => 'vocabulary_machine_name',
+      ),
+      'bundle keys' => array(
+        'bundle' => 'machine_name',
+      ),
+      'bundles' => array(),
     ),
   );
+  foreach (taxonomy_get_vocabularies() as $vocabulary) {
+    $return['taxonomy_term']['bundles'][$vocabulary->machine_name] = array(
+      'label' => $vocabulary->name,
+      'admin' => array(
+        'path' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+        'real path' => 'admin/content/taxonomy/' . $vocabulary->vid,
+        'bundle argument' => 3,
+        'access arguments' => array('administer taxonomy'),
+      ),
+    );
+  }
   return $return;
 }
 
