? 649298_features_content_revert_32.diff
? features-649298-20.patch
Index: includes/features.content.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/features/includes/features.content.inc,v
retrieving revision 1.1.2.14
diff -u -p -r1.1.2.14 features.content.inc
--- includes/features.content.inc	2 Jul 2010 03:17:04 -0000	1.1.2.14
+++ includes/features.content.inc	20 Jul 2010 15:36:11 -0000
@@ -139,9 +139,21 @@ function content_features_rebuild($modul
     content_clear_type_cache(TRUE);
 
     foreach ($fields as $field) {
-      $existing_field = content_fields($field['field_name']);
-      $existing_instance = content_fields($field['field_name'], $field['type_name']);
-      if ($existing_field && $existing_instance) {
+      // We use content_field_instance_read() here so that we can get inactive 
+      // fields too. We can't just pass $field to it as the fnc will add every
+      // item of the array to the WHERE clause.
+      $param = array('field_name' => $field['field_name'], 'type_name' => $field['type_name']);
+      $existing_instance = content_field_instance_read($param, TRUE);
+
+      if ($existing_instance) {
+        // If this existing instance is inactive, we need to activate it before
+        // running content_field_instance_update().
+        if (!$existing_instance['widget_active']) {
+          db_query("UPDATE {". content_instance_tablename() ."} SET widget_active = 1 WHERE field_name = '%s' AND type_name = '%s'", $field['field_name'], $field['type_name']);
+          // We need to clear the type cache again, unfortunately.
+          content_clear_type_cache(TRUE);
+        }
+
         content_field_instance_update($field, FALSE);
       }
       else {
@@ -157,3 +169,29 @@ function content_features_rebuild($modul
     }
   }
 }
+
+/**
+ * Helper function: retrieve default fields by node type.
+ */
+function content_features_fields_default($node_type, $reset = FALSE) {
+  features_include_defaults('content');
+  static $content_default_fields;
+  if (!isset($content_default_fields) || $reset) {
+    foreach (module_invoke_all('content_default_fields') as $field) {
+      $content_default_fields[$field['type_name']][] = $field['field_name'];
+    }
+  }
+  return isset($content_default_fields[$node_type]) ? $content_default_fields[$node_type] : array();
+}
+
+/**
+ * Helper function: retrieve normal fields by node type.
+ */
+function content_features_fields_normal($node_type) {
+  $normal_fields = array();
+  $content_info = content_types($node_type);
+  if (!empty($content_info['fields'])) {
+    $normal_fields = array_keys($content_info['fields']);
+  }
+  return $normal_fields;
+}
Index: includes/features.node.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/features/includes/features.node.inc,v
retrieving revision 1.1.2.21
diff -u -p -r1.1.2.21 features.node.inc
--- includes/features.node.inc	19 Jul 2010 18:38:51 -0000	1.1.2.21
+++ includes/features.node.inc	20 Jul 2010 15:36:11 -0000
@@ -93,7 +93,7 @@ function node_features_export($data, &$e
 /**
  * Implementation of hook_features_export_render().
  */
-function node_features_export_render($module = 'foo', $data) {
+function node_features_export_render($module, $data, $export = NULL) {
   $elements = array(
     'name' => TRUE,
     'module' => FALSE,
@@ -126,6 +126,21 @@ function node_features_export_render($mo
         $output[] = "      '{$key}' => '{$info->$key}',";
       }
     }
+
+    // Detect whether there are extra fields on this content type than have
+    // been described in defaults. If so, add an additional key to the node
+    // type info allowing it to be detected by Features as a component that can
+    // be reverted (delete those extra fields) or updated (export those extra
+    // fields to code).
+    // Note that this key is only added if $export is not set - ie. we never
+    // *actually* write this key to code.
+    if (!isset($export) && module_exists('content') && module_exists($module)) {
+      // If there are deleted fields, mark the content type as such
+      $deleted_fields = array_diff(content_features_fields_normal($type), content_features_fields_default($type));
+      if (!empty($deleted_fields)) {
+        $output[] = "      'content_has_extra_fields' => TRUE,";
+      }
+    }
     $output[] = "    ),";
   }
   $output[] = '  );';
@@ -145,12 +160,25 @@ function node_features_revert($module = 
   if (module_hook($module, 'node_info')){
     $default_types = module_invoke($module, 'node_info');
 
-    // Delete node types
-    // We don't use node_type_delete() because we do not actually
-    // want to delete the node type (and invoke hook_node_type()).
-    // This can lead to bad consequences like CCK deleting field
-    // storage in the DB.
     foreach ($default_types as $type_name => $type_info) {
+      // We need to de-activate any missing fields. De-activating allows us to 
+      // preserve data. We de-activate by setting the widget_active flag to 0;
+      // widget_active is incorrectly named, and really should be 
+      // instance_active
+      if (module_exists('content')) {
+        // Our existing fields ($fields) needs to be the first argument here,
+        // so only fields that don't exist in code can be de-activated.
+        if ($deleted_fields = array_diff(content_features_fields_normal($type_name), content_features_fields_default($type_name))) {
+          foreach($deleted_fields as $field_name) {
+            db_query("UPDATE {". content_instance_tablename() ."} SET widget_active = 0 WHERE field_name = '%s' AND type_name = '%s'", $field_name, $type_name);
+          }
+        }
+      }
+
+      // Delete node types.
+      // We don't use node_type_delete() because we do not actually want to 
+      // delete the node type (and invoke hook_node_type()). This can lead to 
+      // bad consequences like CCK deleting field storage in the DB.
       db_query("DELETE FROM {node_type} WHERE type = '%s'", $type_name);
     }
   }
