#898558: Clean-up the node upgrade path.

From: Damien Tournoud <damien@commerceguys.com>


---
 comment/comment.install |    4 +-
 field/field.install     |   61 +++++++++++++++++++++++++++++++++
 node/node.install       |   86 ++++++++++++++++++++++++++++++-----------------
 3 files changed, 116 insertions(+), 35 deletions(-)

diff --git modules/comment/comment.install modules/comment/comment.install
index fc07644..ef71aa3 100644
--- modules/comment/comment.install
+++ modules/comment/comment.install
@@ -274,7 +274,7 @@ function comment_update_7005() {
   );
 
   $types = _update_7010_node_get_types();
-  foreach ($types as $type) {
+  foreach ($types as $type => $type_object) {
     $instance = $generic_instance;
     $instance['bundle'] = 'comment_node_' . $type;
     _update_7000_field_create_instance($field, $instance);
@@ -291,7 +291,7 @@ function comment_update_7006(&$sandbox) {
     $sandbox['types'] = array();
     if ($comments) {
       $sandbox['etid'] = _field_sql_storage_etid('comment');
-      $sandbox['types'] = _update_7010_node_get_types();
+      $sandbox['types'] = array_keys(_update_7010_node_get_types());
     }
     $sandbox['total'] = count($sandbox['types']);
   }
diff --git modules/field/field.install modules/field/field.install
index 344f6aa..a130795 100644
--- modules/field/field.install
+++ modules/field/field.install
@@ -168,7 +168,6 @@ function field_schema() {
   return $schema;
 }
 
-
 /**
  * @defgroup update-api-6.x-to-7.x Update versions of API functions.
  * @{
@@ -280,6 +279,62 @@ function _update_7000_field_create_instance($field, &$instance) {
 }
 
 /**
+ * Utility function: write field data directly to the SQL storage.
+ *
+ * This function writes the data according to the schema version 7000.
+ *
+ */
+function _update_7000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
+  $etid = _field_sql_storage_etid($entity_type);
+  $table_name = "field_data_{$field_name}";
+  $revision_name = "field_revision_{$field_name}";
+
+  db_delete($table_name)
+    ->condition('etid', $etid)
+    ->condition('entity_id', $entity_id)
+    ->execute();
+  db_delete($revision_name)
+    ->condition('etid', $etid)
+    ->condition('entity_id', $entity_id)
+    ->condition('revision_id', $revision_id)
+    ->execute();
+
+  $columns = array();
+  foreach ($data as $langcode => $items) {
+    foreach ($items as $delta => $item) {
+      $record = array(
+        'etid' => $etid,
+        'entity_id' => $entity_id,
+        'revision_id' => $revision_id,
+        'bundle' => $bundle,
+        'delta' => $delta,
+        'language' => $langcode,
+      );
+      foreach ($item as $column => $value) {
+        $record[_field_sql_storage_columnname($field_name, $column)] = $value;
+      }
+
+      $records[] = $record;
+      // Record the columns used.
+      $columns += $record;
+    }
+  }
+
+  if ($columns) {
+    $query = db_insert($table_name)->fields(array_keys($columns));
+    $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+    foreach ($records as $record) {
+      $query->values($record);
+      if ($revision_id) {
+        $revision_query->values($record);
+      }
+    }
+    $query->execute();
+    $revision_query->execute();
+  }
+}
+
+/**
  * @} End of "defgroup update-api-6.x-to-7.x"
  */
 
@@ -297,3 +352,7 @@ function field_update_7000() {
   // function needs to exist but as field is a new module in Drupal 7, it
   // does not need to do anything.
 }
+
+/**
+ * @} End of "defgroup field-updates-6.x-to-7.x"
+ */
diff --git modules/node/node.install modules/node/node.install
index 4851bf2..4e0ce09 100644
--- modules/node/node.install
+++ modules/node/node.install
@@ -420,9 +420,13 @@ function node_update_dependencies() {
 
 /**
  * Utility function: fetch the node types directly from the database.
+ *
+ * This function expects a database structure according to schema 7010.
+ *
+ * @ingroup update-api-6.x-to-7.x
  */
 function _update_7010_node_get_types() {
-  return db_query('SELECT type FROM {node_type}')->fetchCol();
+  return db_query('SELECT * FROM {node_type}')->fetchAllAssoc('type', PDO::FETCH_OBJ);
 }
 
 /**
@@ -478,10 +482,9 @@ function node_update_7004() {
   // Map old preview setting to new values order.
   $original_preview ? $original_preview = 2 : $original_preview = 1;
   drupal_static_reset('_node_types_build');
-  $type_list = node_type_get_types();
 
   // Apply original settings to all types.
-  foreach ($type_list as $type => $entity) {
+  foreach (_update_7010_node_get_types() as $type => $type_object) {
     variable_set('teaser_length_' . $type, $original_length);
     variable_set('node_preview_' . $type, $original_preview);
   }
@@ -510,18 +513,27 @@ function node_update_7006(&$sandbox) {
 
   // Get node type info for every invocation.
   drupal_static_reset('_node_types_build');
-  $node_types = node_type_get_types();
 
   if (!isset($sandbox['total'])) {
     // Initial invocation.
 
+    // First, create the body field.
+    $body_field = array(
+      'field_name' => 'body',
+      'type' => 'text_with_summary',
+      'module' => 'text',
+      'cardinality' => 1,
+      'entity_types' => array('node'),
+      'translatable' => TRUE,
+    );
+    _update_7000_field_create_field($body_field);
+
+    $default_trim_length = variable_get('teaser_length', 600);
+
     // Get node type info, specifically the body field settings.
-    $result = db_select('node_type', 'node_type')
-      ->fields('node_type')
-      ->execute();
-    foreach ($result as $type_object) {
-      $node_types[$type_object->type] = $type_object;
-    }
+    $node_types = _update_7010_node_get_types();
+
+    // Create default settings for orphan nodes.
     $extra_types = db_query('SELECT DISTINCT type FROM {node} WHERE type NOT IN (:types)', array(':types' => array_keys($node_types)))->fetchCol();
     foreach ($extra_types as $type) {
       $type_object = new stdClass;
@@ -534,26 +546,38 @@ function node_update_7006(&$sandbox) {
       $node_types[$type_object->type] = $type_object;
     }
 
-    $default_trim_length = variable_get('teaser_length', 600);
     // Add body field instances for existing node types.
     foreach ($node_types as $node_type) {
       if ($node_type->has_body) {
-        $instance = node_add_body_field($node_type, $node_type->body_label);
-        // Update newly created instance to convert teaser_length variable
-        // into formatter settings.
         $trim_length = variable_get('teaser_length_' . $node_type->type, $default_trim_length);
-        $instance_changed = FALSE;
-        foreach ($instance['display'] as $view_mode => $view_mode_info) {
-          if ($view_mode_info['type'] == 'text_trimmed' || $view_mode_info['type'] == 'text_summary_or_trimmed') {
-            if (!isset($view_mode_info['settings']['trim_length'])) {
-              $instance['display'][$view_mode]['settings']['trim_length'] = $trim_length;
-              $instance_changed = TRUE;
-            }
-          }
-        }
-        if ($instance_changed) {
-          field_update_instance($instance);
-        }
+
+        $instance = array(
+          'entity_type' => 'node',
+          'bundle' => $node_type->type,
+          'label' => $node_type->body_label,
+          'widget' => array(
+            'type' => 'text_textarea_with_summary',
+            'settings' => array(
+              'rows' => 20,
+              'summary_rows' => 5,
+            ),
+            'weight' => -4,
+            'module' => 'text',
+          ),
+          'settings' => array('display_summary' => TRUE),
+          'display' => array(
+            'default' => array(
+              'label' => 'hidden',
+              'type' => 'text_default',
+            ),
+            'teaser' => array(
+              'label' => 'hidden',
+              'type' => 'text_summary_or_trimmed',
+              'trim_length' => $trim_length,
+            ),
+          ),
+        );
+        _update_7000_field_create_instance($body_field, $instance);
         variable_del('teaser_length_' . $node_type->type);
       }
       // Leave 'teaser_length' variable for aggregator module upgrade.
@@ -570,14 +594,12 @@ function node_update_7006(&$sandbox) {
     $query = db_select('node', 'n');
     $query->join('node_revision', 'nr', 'n.vid = nr.vid');
     $sandbox['total'] = $query->countQuery()->execute()->fetchField();
+
+    $sandbox['body_field_id'] = $body_field['id'];
   }
   else {
     // Subsequent invocations.
 
-    // Grab the body field ID for field_sql_storage_field_storage_write().
-    $body_field = field_info_field('body');
-    $body_field_id = $body_field['id'];
-
     $found = FALSE;
     if ($sandbox['total']) {
       // Operate on every revision of every node (whee!), in batches.
@@ -628,7 +650,7 @@ function node_update_7006(&$sandbox) {
           $node->body[$langcode][0]['format'] = !empty($revision->format) ? $revision->format : variable_get('filter_default_format', 1);
           // This is a core update and no contrib modules are enabled yet, so
           // we can assume default field storage for a faster update.
-          field_sql_storage_field_storage_write('node', $node, FIELD_STORAGE_INSERT, array($body_field_id));
+          _update_7000_field_sql_storage_write('node', $node->type, $node->nid, $node->vid, 'body', $node->body);
         }
 
         // Migrate the status columns to the {node_revision} table.
@@ -680,7 +702,7 @@ function node_update_7007() {
 function node_update_7008() {
   $roles = user_roles(FALSE, 'administer nodes');
   foreach ($roles as $rid => $role) {
-    user_role_grant_permissions($rid, array('access content overview'));
+    _update_user_role_grant_permissions($rid, array('access content overview'), 'node');
   }
 }
 
