Index: field/field.info.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.info.inc,v
retrieving revision 1.4
diff -u -r1.4 field.info.inc
--- field/field.info.inc	10 Feb 2009 16:09:00 -0000	1.4
+++ field/field.info.inc	21 Mar 2009 15:47:53 -0000
@@ -142,7 +142,7 @@
       cache_set('field_info_types', $info, 'cache_field');
     }
   }
-
+  
   return $info;
 }
 
Index: field/modules/list/list.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/list/list.info,v
retrieving revision 1.2
diff -u -r1.2 list.info
--- field/modules/list/list.info	5 Feb 2009 03:42:57 -0000	1.2
+++ field/modules/list/list.info	21 Mar 2009 15:16:35 -0000
@@ -4,3 +4,4 @@
 package = Core - fields
 core = 7.x
 files[]=list.module
+required = TRUE
Index: field/modules/number/number.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/number/number.info,v
retrieving revision 1.2
diff -u -r1.2 number.info
--- field/modules/number/number.info	5 Feb 2009 03:42:57 -0000	1.2
+++ field/modules/number/number.info	21 Mar 2009 15:16:35 -0000
@@ -4,3 +4,4 @@
 package = Core - fields
 core = 7.x
 files[]=number.module
+required = TRUE
Index: field/modules/options/options.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.info,v
retrieving revision 1.1
diff -u -r1.1 options.info
--- field/modules/options/options.info	3 Feb 2009 17:30:11 -0000	1.1
+++ field/modules/options/options.info	21 Mar 2009 15:16:36 -0000
@@ -4,3 +4,4 @@
 package = Core - fields
 core = 7.x
 files[]=options.module
+required = TRUE
Index: field/modules/text/text.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.info,v
retrieving revision 1.2
diff -u -r1.2 text.info
--- field/modules/text/text.info	5 Feb 2009 03:42:57 -0000	1.2
+++ field/modules/text/text.info	21 Mar 2009 15:16:36 -0000
@@ -4,3 +4,4 @@
 package = Core - fields
 core = 7.x
 files[]=text.module
+required = TRUE
Index: field/modules/text/text.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v
retrieving revision 1.4
diff -u -r1.4 text.module
--- field/modules/text/text.module	10 Mar 2009 09:45:31 -0000	1.4
+++ field/modules/text/text.module	22 Mar 2009 01:18:26 -0000
@@ -14,6 +14,9 @@
     'text_textarea' => array(
       'arguments' => array('element' => NULL),
     ),
+    'text_textarea_with_summary' => array(
+      'arguments' => array('element' => NULL),
+    ),
     'text_textfield' => array(
       'arguments' => array('element' => NULL),
     ),
@@ -26,11 +29,23 @@
     'field_formatter_text_trimmed' => array(
       'arguments' => array('element' => NULL),
     ),
+    'field_formatter_text_summary_or_trimmed' => array(
+      'arguments' => array('element' => NULL),
+    ),
   );
 }
 
 /**
  * Implementation of hook_field_info().
+ * 
+ * @param $max_length 
+ *   The maximum length for a varchar field.
+ * @param $text_processing
+ *   Whether text input filters should be used.
+ * @param $display_summary
+ *   Whether the summary field should be displayed. When empty
+ *   and not displayed the summary will take its value from the trimmed
+ *   value of the main text field.
  */
 function text_field_info() {
   return array(
@@ -39,7 +54,6 @@
       'description' => t('This field stores varchar text in the database.'),
       'settings' => array('max_length' => 255),
       'instance_settings' => array('text_processing' => 0),
-      'widget_settings' => array('size' => 60),
       'default_widget' => 'text_textfield',
       'default_formatter' => 'text_default',
     ),
@@ -47,10 +61,16 @@
       'label' => t('Long text'),
       'description' => t('This field stores long text in the database.'),
       'instance_settings' => array('text_processing' => 0),
-      'widget_settings' => array('rows' => 5),
       'default_widget' => 'text_textarea',
       'default_formatter' => 'text_default',
     ),
+    'text_with_summary' => array(
+      'label' => t('Long text with a summary field'),
+      'description' => t('This field stores long text in the database along with optional summary/teaser text.'),
+      'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
+      'default_widget' => 'text_textarea_with_summary',
+      'default_formatter' => 'text_summary_or_trimmed',
+    ),
   );
 }
 
@@ -65,6 +85,30 @@
         'size' => 'big',
         'not null' => FALSE,
       ),
+      'format' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+      ),
+    );
+  }
+  else if ($field['type'] == 'text_with_summary') {
+    $columns = array(
+      'value' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'not null' => FALSE,
+      ),
+      'summary' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'not null' => FALSE,
+      ),
+      'format' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+      ),
     );
   }
   else {
@@ -74,15 +118,13 @@
         'length' => $field['settings']['max_length'],
         'not null' => FALSE,
       ),
+      'format' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+      ),
     );
   }
-  $columns += array(
-    'format' => array(
-      'type' => 'int',
-      'unsigned' => TRUE,
-      'not null' => FALSE,
-    ),
-  );
   return $columns;
 }
 
@@ -94,9 +136,11 @@
     foreach ($items as $delta => $item) {
       $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
       if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']);
-      if (!empty($item['value'])) {
-        if (!empty($field['settings']['max_length']) && drupal_strlen($item['value']) > $field['settings']['max_length']) {
-          form_set_error($error_element, t('%name: the value may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])));
+      foreach (array('value', 'summary') as $column) {
+        if (!empty($item[$column])) {
+          if (!empty($field['settings']['max_length']) && drupal_strlen($item[$column]) > $field['settings']['max_length']) {
+            form_set_error($error_element, t('%name: the value may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])));
+          }
         }
       }
     }
@@ -107,14 +151,22 @@
   global $language;
   foreach ($items as $delta => $item) {
     // TODO D7 : this code is really node-related.
+    $format = $item['format'];
     if (!empty($instance['settings']['text_processing'])) {
       $check = is_null($object) || (isset($object->build_mode) && $object->build_mode == NODE_BUILD_PREVIEW);
-      $text = isset($item['value']) ? check_markup($item['value'], $item['format'], isset($object->language) ? $object->language : $language, $check) : '';
+      $text = isset($item['value']) ? check_markup($item['value'], $format, isset($object->language) ? $object->language : $language, $check) : '';
+      if ($field['type'] == 'text_with_summary') {
+        $summary = isset($item['summary']) ? check_markup($item['summary'], $format, isset($object->language) ? $object->language : $language, $check) : '';
+      }
     }
     else {
       $text = check_plain($item['value']);
+      if ($field['type'] == 'text_with_summary') {
+        $summary = check_plain($item['summary']);
+      }
     }
     $items[$delta]['safe'] = $text;
+    $items[$delta]['safe_summary'] = isset($summary) ? $summary : '';
   }
 }
 
@@ -135,14 +187,14 @@
   return array(
     'text_default' => array(
       'label' => t('Default'),
-      'field types' => array('text', 'text_long'),
+      'field types' => array('text', 'text_long', 'text_with_summary'),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'text_plain' => array(
       'label' => t('Plain text'),
-      'field types' => array('text', 'text_long'),
+      'field types' => array('text', 'text_long', 'text_with_summary'),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
@@ -153,6 +205,13 @@
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
+    ), 
+    'text_summary_or_trimmed' => array(
+      'label' => t('Summary or Trimmed'),
+      'field types' => array('text_with_summary'),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+      ),
     ),
   );
 }
@@ -177,7 +236,136 @@
 function theme_field_formatter_text_trimmed($element) {
   $field = field_info_field($element['#field_name']);
   $instance = field_info_instance($element['#field_name'], $element['#bundle']);
-  return $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL;
+  return text_teaser($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL);
+}
+
+/**
+ * Theme function for 'summary or trimmed' text field formatter.
+ */
+function theme_field_formatter_text_summary_or_trimmed($element) {
+  $field = field_info_field($element['#field_name']);
+  $instance = field_info_instance($element['#field_name'], $element['#bundle']);
+  
+  if (!empty($element['#item']['safe_summary'])) {
+    return $element['#item']['safe_summary'];
+  }
+  else {
+    return text_teaser($element['#item']['safe'], $instance['settings']['text_processing'] ? $element['#item']['format'] : NULL);
+  }
+}
+
+/**
+ * Generate a trimmed, formatted version of a text field value.
+ *
+ * If the end of the teaser is not indicated using the <!--break--> delimiter
+ * then we generate the teaser automatically, trying to end it at a sensible
+ * place such as the end of a paragraph, a line break, or the end of a
+ * sentence (in that order of preference).
+ *
+ * @param $body
+ *   The content for which a teaser will be generated.
+ * @param $format
+ *   The format of the content. If the content contains PHP code, we do not
+ *   split it up to prevent parse errors. If the line break filter is present
+ *   then we treat newlines embedded in $body as line breaks.
+ * @param $size
+ *   The desired character length of the teaser. If omitted, the default
+ *   value will be used. Ignored if the special delimiter is present
+ *   in $body.
+ * @return
+ *   The generated teaser.
+ */
+function text_teaser($body, $format = NULL, $size = NULL) {
+
+  if (!isset($size)) {
+    $size = variable_get('teaser_length', 600);
+  }
+
+  // Find where the delimiter is in the body
+  $delimiter = strpos($body, '<!--break-->');
+
+  // If the size is zero, and there is no delimiter, the entire body is the teaser.
+  if ($size == 0 && $delimiter === FALSE) {
+    return $body;
+  }
+
+  // If a valid delimiter has been specified, use it to chop off the teaser.
+  if ($delimiter !== FALSE) {
+    return substr($body, 0, $delimiter);
+  }
+
+  // We check for the presence of the PHP evaluator filter in the current
+  // format. If the body contains PHP code, we do not split it up to prevent
+  // parse errors.
+  if (isset($format)) {
+    $filters = filter_list_format($format);
+    if (isset($filters['php/0']) && strpos($body, '<?') !== FALSE) {
+      return $body;
+    }
+  }
+
+  // If we have a short body, the entire body is the teaser.
+  if (drupal_strlen($body) <= $size) {
+    return $body;
+  }
+
+  // If the delimiter has not been specified, try to split at paragraph or
+  // sentence boundaries.
+
+  // The teaser may not be longer than maximum length specified. Initial slice.
+  $teaser = truncate_utf8($body, $size);
+
+  // Store the actual length of the UTF8 string -- which might not be the same
+  // as $size.
+  $max_rpos = strlen($teaser);
+
+  // How much to cut off the end of the teaser so that it doesn't end in the
+  // middle of a paragraph, sentence, or word.
+  // Initialize it to maximum in order to find the minimum.
+  $min_rpos = $max_rpos;
+
+  // Store the reverse of the teaser. We use strpos on the reversed needle and
+  // haystack for speed and convenience.
+  $reversed = strrev($teaser);
+
+  // Build an array of arrays of break points grouped by preference.
+  $break_points = array();
+
+  // A paragraph near the end of sliced teaser is most preferable.
+  $break_points[] = array('</p>' => 0);
+
+  // If no complete paragraph then treat line breaks as paragraphs.
+  $line_breaks = array('<br />' => 6, '<br>' => 4);
+  // Newline only indicates a line break if line break converter
+  // filter is present.
+  if (isset($filters['filter/1'])) {
+    $line_breaks["\n"] = 1;
+  }
+  $break_points[] = $line_breaks;
+
+  // If the first paragraph is too long, split at the end of a sentence.
+  $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
+
+  // Iterate over the groups of break points until a break point is found.
+  foreach ($break_points as $points) {
+    // Look for each break point, starting at the end of the teaser.
+    foreach ($points as $point => $offset) {
+      // The teaser is already reversed, but the break point isn't.
+      $rpos = strpos($reversed, strrev($point));
+      if ($rpos !== FALSE) {
+        $min_rpos = min($rpos + $offset, $min_rpos);
+      }
+    }
+
+    // If a break point was found in this group, slice and return the teaser.
+    if ($min_rpos !== $max_rpos) {
+      // Don't slice with length 0. Length must be <0 to slice from RHS.
+      return ($min_rpos === 0) ? $teaser : substr($teaser, 0, 0 - $min_rpos);
+    }
+  }
+
+  // If a break point was not found, still return a teaser.
+  return $teaser;
 }
 
 /**
@@ -211,6 +399,15 @@
         'default value' => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
+    'text_textarea_with_summary' => array(
+      'label' => t('Text area with a summary'),
+      'field types' => array('text_with_summary'),
+      'settings' => array('rows' => 20, 'summary_rows' => 5),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+        'default value' => FIELD_BEHAVIOR_DEFAULT,
+      ),
+    ),
   );
 }
 
@@ -232,12 +429,21 @@
       '#input' => TRUE,
       '#columns' => array('value'), '#delta' => 0,
       '#process' => array('text_textfield_process'),
+      '#theme_wrapper' => 'text_textfield',
       '#autocomplete_path' => FALSE,
       ),
     'text_textarea' => array(
       '#input' => TRUE,
       '#columns' => array('value', 'format'), '#delta' => 0,
       '#process' => array('text_textarea_process'),
+      '#theme_wrapper' => 'text_textarea',
+      '#filter_value' => FILTER_FORMAT_DEFAULT,
+      ),
+    'text_textarea_with_summary' => array(
+      '#input' => TRUE,
+      '#columns' => array('value', 'format', 'summary'), '#delta' => 0,
+      '#process' => array('text_textarea_with_summary_process'),
+      '#theme_wrapper' => 'text_textarea_with_summary',
       '#filter_value' => FILTER_FORMAT_DEFAULT,
       ),
   );
@@ -301,33 +507,27 @@
   $instance = $form['#fields'][$element['#field_name']]['instance'];
   $field_key = $element['#columns'][0];
   $delta = $element['#delta'];
-
+      
   $element[$field_key] = array(
     '#type' => 'textfield',
     '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
     '#autocomplete_path' => $element['#autocomplete_path'],
     '#size' => $instance['widget']['settings']['size'],
     '#attributes' => array('class' => 'text'),
-    // The following values were set by the field module and need
-    // to be passed down to the nested element.
     '#title' => $element['#title'],
     '#description' => $element['#description'],
     '#required' => $element['#required'],
-    '#field_name' => $element['#field_name'],
-    '#bundle' => $element['#bundle'],
-    '#delta' => $element['#delta'],
-    '#columns' => $element['#columns'],
-  );
+    );
 
   $element[$field_key]['#maxlength'] = !empty($field['settings']['max_length']) ? $field['settings']['max_length'] : NULL;
 
   if (!empty($instance['settings']['text_processing'])) {
-    $filter_key = $element['#columns'][1];
+    $filter_key  = (count($element['#columns']) == 2) ? $element['#columns'][1] : 'format';
     $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT;
-    $parents = array_merge($element['#parents'] , array($filter_key));
-    $element[$filter_key] = filter_form($format, 1, $parents);
+    $element[$field_key]['#text_format'] = $format;
+    $element['#element_validate'][] = 'text_field_widget_validate';
   }
-
+    
   // Used so that hook_field('validate') knows where to flag an error.
   // TODO: rework that. See http://groups.drupal.org/node/18019.
   $element['_error_element'] = array(
@@ -351,27 +551,22 @@
   $instance = $form['#fields'][$element['#field_name']]['instance'];
   $field_key = $element['#columns'][0];
   $delta = $element['#delta'];
+      
   $element[$field_key] = array(
     '#type' => 'textarea',
     '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
     '#rows' => $instance['widget']['settings']['rows'],
     '#weight' => 0,
-    // The following values were set by the field module and need
-    // to be passed down to the nested element.
     '#title' => $element['#title'],
     '#description' => $element['#description'],
     '#required' => $element['#required'],
-    '#field_name' => $element['#field_name'],
-    '#bundle' => $element['#bundle'],
-    '#delta' => $element['#delta'],
-    '#columns' => $element['#columns'],
   );
 
   if (!empty($instance['settings']['text_processing'])) {
     $filter_key  = (count($element['#columns']) == 2) ? $element['#columns'][1] : 'format';
     $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT;
-    $parents = array_merge($element['#parents'] , array($filter_key));
-    $element[$filter_key] = filter_form($format, 1, $parents);
+    $element[$field_key]['#text_format'] = $format;
+    $element['#element_validate'][] = 'text_field_widget_validate';
   }
 
   // Used so that hook_field('validate') knows where to flag an error.
@@ -379,10 +574,82 @@
     '#type' => 'value',
     '#value' => implode('][', array_merge($element['#parents'], array($field_key))),
   );
+
+  return $element;
+}
+
+/**
+ * Process an individual element.
+ *
+ * Build the form element. When creating a form using FAPI #process,
+ * note that $element['#value'] is already set.
+ *
+ * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
+ */
+function text_textarea_with_summary_process($element, $edit, $form_state, $form) {
+  $field = $form['#fields'][$element['#field_name']]['field'];
+  $instance = $form['#fields'][$element['#field_name']]['instance'];
+  $delta = $element['#delta'];
+      
+  $field_key = $element['#columns'][1];
+  $display = !empty($element['#value'][$field_key]) || !empty($instance['settings']['display_summary']);
+  $element[$field_key] = array(
+    '#title' => t('Summary'),
+    '#type' => $display ? 'textarea' : 'value',
+    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
+    '#rows' => $instance['widget']['settings']['summary_rows'],
+    '#weight' => 0,
+    '#title' => t('Summary'),
+    '#description' => t('Leave blank to use trimmed value of full text as the summary.'),
+    '#required' => $element['#required'],
+    '#display' => $display,
+  );
+
+  $field_key = $element['#columns'][0];
+  $element[$field_key] = array(
+    '#type' => 'textarea',
+    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
+    '#rows' => $instance['widget']['settings']['rows'],
+    '#weight' => 1,
+    '#title' => $display ? t('Full text') : $element['#title'],
+    '#description' => $element['#description'],
+    '#required' => $element['#required'],
+    '#required' => $instance['required'],
+  );
+
+  if (!empty($instance['settings']['text_processing'])) {
+    $filter_key  = (count($element['#columns']) == 2) ? $element['#columns'][1] : 'format';
+    $format = isset($element['#value'][$filter_key]) ? $element['#value'][$filter_key] : FILTER_FORMAT_DEFAULT;
+    $element[$field_key]['#text_format'] = $format;
+    $element['#element_validate'][] = 'text_field_widget_validate';
+  }
+  
+  // Used so that hook_field('validate') knows where to flag an error.
+  $element['_error_element'] = array(
+    '#type' => 'value',
+    '#value' => implode('][', array_merge($element['#parents'], array($field_key))),
+  );
+  $element['_error_element'][1]['#type'] = 'value';
+  $element['_error_element'][1]['#value'] = implode('][', array_merge($element['#parents'], array($element['#columns'][1])));
+  
   return $element;
 }
 
 /**
+ * FAPI validation of an individual text widget.
+ *
+ * Extract the input format into the expected 'format' value.
+ */
+function text_field_widget_validate($element, &$form_state) {
+  $field_key = $element['#columns'][0];
+  $filter_key  = (count($element['#columns']) == 2) ? $element['#columns'][1] : 'format';
+
+  $element['#value'][$filter_key] = $element['#value'][$field_key . '_format'];
+  unset($element['#value'][$field_key . '_format']);
+  form_set_value($element, $element['#value'], $form_state);
+}
+
+/**
  * FAPI theme for an individual text elements.
  *
  * The textfield or textarea is already rendered by the
@@ -399,4 +666,21 @@
 
 function theme_text_textarea($element) {
   return $element['#children'];
-}
\ No newline at end of file
+}
+
+function theme_text_textarea_with_summary($element) {
+  // If displaying both a textarea and a summary field, wrap them
+  // in a fieldset to make it clear they belong together.
+  $field_key = $element['#columns'][1];
+  if (!empty($element[$field_key]['#display'])) {
+    $fieldset = array(
+      '#title' => $element['#title'],
+      '#value' => $element['#children'],
+      '#attributes' => array('class' => 'text-textarea'),
+      );
+    return theme('fieldset', $fieldset);
+  }
+  else {
+    return $element['#children'];
+  }
+}
Index: field/modules/text/text.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.test,v
retrieving revision 1.1
diff -u -r1.1 text.test
--- field/modules/text/text.test	3 Feb 2009 17:30:12 -0000	1.1
+++ field/modules/text/text.test	21 Mar 2009 23:50:51 -0000
@@ -14,6 +14,9 @@
 
   function setUp() {
     parent::setUp('field', 'text', 'field_test');
+
+    $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+    $this->drupalLogin($web_user);
   }
 
   // Test widgets.
@@ -32,6 +35,68 @@
    * Test textarea widget.
    */
 
+
+  /**
+   * Test 'formatted_text' setting.
+   */
+  function testTextfieldFormattedForm() {
+    $entity_type = 'test_entity';
+
+    $this->field_name = drupal_strtolower($this->randomName(). '_field_name');
+    $this->field = array('field_name' => $this->field_name, 'type' => 'text', );
+    field_create_field($this->field);
+    $this->instance = array(
+      'field_name' => $this->field_name,
+      'bundle' => FIELD_TEST_BUNDLE,
+      'label' => $this->randomName(). '_label',
+      'settings' => array(
+        'text_processing' => TRUE,
+      ),
+      'widget' => array(
+        'type' => 'text_textfield',
+      )
+    );
+    field_create_instance($this->instance);
+
+    // Allow the user to use the 'Full HTML' format.
+    db_update('filter_format')->fields(array('roles' => ',2,'))->condition('format', 2)->execute();
+
+    // Display creation form.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertFieldByName($this->field_name . '[0][value]', '', 'Widget is displayed');
+    $this->assertFieldByName($this->field_name . '[0][value_format]', '1', 'Format selector is displayed');
+
+    // Submit with data that should be filtered.
+    $value = $this->randomName() . '<br />' . $this->randomName();
+    $edit = array(
+      $this->field_name . '[0][value]' => $value,
+      $this->field_name . '[0][value_format]' => 1,
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+    preg_match('|test-entity/(\d+)/edit|', $this->url, $match);
+    $id = $match[1];
+    $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+
+    // Display the object.
+    $entity = field_test_entity_load($id);
+    $entity->content = field_attach_view($entity_type, $entity);
+    $this->content = drupal_render($entity->content);
+    $this->assertNoRaw($value, 'Filtered tags are not displayed');
+    $this->assertRaw(str_replace('<br />', '', $value), 'Filtered value is displayed correctly');
+
+    // Edit and change the format to 'Full HTML'.
+    $edit = array(
+      $this->field_name . '[0][value_format]' => 2,
+    );
+    $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save'));
+    $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
+
+    // Display the object.
+    $entity = field_test_entity_load($id);
+    $entity->content = field_attach_view($entity_type, $entity);
+    $this->content = drupal_render($entity->content);
+    $this->assertRaw($value, 'Value is displayed unfiltered');
+  }
   // Test formatters.
   /**
    *
Index: field/theme/field.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/theme/field.css,v
retrieving revision 1.3
diff -u -r1.3 field.css
--- field/theme/field.css	10 Mar 2009 09:45:32 -0000	1.3
+++ field/theme/field.css	21 Mar 2009 19:16:30 -0000
@@ -32,7 +32,11 @@
   margin: .5em 0 0;
 }
 
-.form-item .number {
+form .form-item .text {
   display: inline;
   width: auto;
-}
\ No newline at end of file
+}
+form .form-item .number {
+  display: inline;
+  width: auto;
+}
Index: node/node.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.install,v
retrieving revision 1.14
diff -u -r1.14 node.install
--- node/node.install	17 Mar 2009 12:41:54 -0000	1.14
+++ node/node.install	21 Mar 2009 15:16:36 -0000
@@ -239,18 +239,6 @@
         'not null' => TRUE,
         'default' => '',
       ),
-      'body' => array(
-        'description' => 'The body of this version.',
-        'type' => 'text',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
-      'teaser' => array(
-        'description' => 'The teaser of this version.',
-        'type' => 'text',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
       'log' => array(
         'description' => 'The log entry explaining the changes in this version.',
         'type' => 'text',
@@ -326,7 +314,7 @@
         'default' => '',
       ),
       'has_body' => array(
-        'description' => 'Boolean indicating whether this type uses the {node_revision}.body field.',
+        'description' => 'Boolean indicating whether this type has the body field attached.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
Index: node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1032
diff -u -r1.1032 node.module
--- node/node.module	20 Mar 2009 19:18:10 -0000	1.1032
+++ node/node.module	22 Mar 2009 01:10:04 -0000
@@ -46,6 +46,11 @@
 define('NODE_BUILD_PRINT', 5);
 
 /**
+ * Name of the default body field.
+ */
+define('NODE_BODY_FIELD', 'node_body');
+
+/**
  * Implementation of hook_help().
  */
 function node_help($path, $arg) {
@@ -290,186 +295,6 @@
 }
 
 /**
- * See if the user used JS to submit a teaser.
- */
-function node_teaser_js(&$form, &$form_state) {
-  if (isset($form_state['input']['teaser_js'])) {
-    // Glue the teaser to the body.
-    if (trim($form_state['values']['teaser_js'])) {
-      // Space the teaser from the body
-      $body = trim($form_state['values']['teaser_js']) . "\r\n<!--break-->\r\n" . trim($form_state['values']['body']);
-    }
-    else {
-      // Empty teaser, no spaces.
-      $body = '<!--break-->' . $form_state['values']['body'];
-    }
-    // Pass updated body value on to preview/submit form processing.
-    form_set_value($form['body'], $body, $form_state);
-    // Pass updated body value back onto form for those cases
-    // in which the form is redisplayed.
-    $form['body']['#value'] = $body;
-  }
-  return $form;
-}
-
-/**
- * Ensure value of "teaser_include" checkbox is consistent with other form data.
- *
- * This handles two situations in which an unchecked checkbox is rejected:
- *
- *   1. The user defines a teaser (summary) but it is empty;
- *   2. The user does not define a teaser (summary) (in this case an
- *      unchecked checkbox would cause the body to be empty, or missing
- *      the auto-generated teaser).
- *
- * If JavaScript is active then it is used to force the checkbox to be
- * checked when hidden, and so the second case will not arise.
- *
- * In either case a warning message is output.
- */
-function node_teaser_include_verify(&$form, &$form_state) {
-  $message = '';
-
-  // $form_state['input'] is set only when the form is built for preview/submit.
-  if (isset($form_state['input']['body']) && isset($form_state['values']['teaser_include']) && !$form_state['values']['teaser_include']) {
-    // "teaser_include" checkbox is present and unchecked.
-    if (strpos($form_state['values']['body'], '<!--break-->') === 0) {
-      // Teaser is empty string.
-      $message = t('You specified that the summary should not be shown when this post is displayed in full view. This setting is ignored when the summary is empty.');
-    }
-    elseif (strpos($form_state['values']['body'], '<!--break-->') === FALSE) {
-      // Teaser delimiter is not present in the body.
-      $message = t('You specified that the summary should not be shown when this post is displayed in full view. This setting has been ignored since you have not defined a summary for the post. (To define a summary, insert the delimiter "&lt;!--break--&gt;" (without the quotes) in the Body of the post to indicate the end of the summary and the start of the main content.)');
-    }
-
-    if (!empty($message)) {
-      drupal_set_message($message, 'warning');
-      // Pass new checkbox value on to preview/submit form processing.
-      form_set_value($form['teaser_include'], 1, $form_state);
-      // Pass new checkbox value back onto form for those cases
-      // in which form is redisplayed.
-      $form['teaser_include']['#value'] = 1;
-    }
-  }
-
-  return $form;
-}
-
-/**
- * Generate a teaser for a node body.
- *
- * If the end of the teaser is not indicated using the <!--break--> delimiter
- * then we generate the teaser automatically, trying to end it at a sensible
- * place such as the end of a paragraph, a line break, or the end of a
- * sentence (in that order of preference).
- *
- * @param $body
- *   The content for which a teaser will be generated.
- * @param $format
- *   The format of the content. If the content contains PHP code, we do not
- *   split it up to prevent parse errors. If the line break filter is present
- *   then we treat newlines embedded in $body as line breaks.
- * @param $size
- *   The desired character length of the teaser. If omitted, the default
- *   value will be used. Ignored if the special delimiter is present
- *   in $body.
- * @return
- *   The generated teaser.
- */
-function node_teaser($body, $format = NULL, $size = NULL) {
-
-  if (!isset($size)) {
-    $size = variable_get('teaser_length', 600);
-  }
-
-  // Find where the delimiter is in the body
-  $delimiter = strpos($body, '<!--break-->');
-
-  // If the size is zero, and there is no delimiter, the entire body is the teaser.
-  if ($size == 0 && $delimiter === FALSE) {
-    return $body;
-  }
-
-  // If a valid delimiter has been specified, use it to chop off the teaser.
-  if ($delimiter !== FALSE) {
-    return substr($body, 0, $delimiter);
-  }
-
-  // We check for the presence of the PHP evaluator filter in the current
-  // format. If the body contains PHP code, we do not split it up to prevent
-  // parse errors.
-  if (isset($format)) {
-    $filters = filter_list_format($format);
-    if (isset($filters['php/0']) && strpos($body, '<?') !== FALSE) {
-      return $body;
-    }
-  }
-
-  // If we have a short body, the entire body is the teaser.
-  if (drupal_strlen($body) <= $size) {
-    return $body;
-  }
-
-  // If the delimiter has not been specified, try to split at paragraph or
-  // sentence boundaries.
-
-  // The teaser may not be longer than maximum length specified. Initial slice.
-  $teaser = truncate_utf8($body, $size);
-
-  // Store the actual length of the UTF8 string -- which might not be the same
-  // as $size.
-  $max_rpos = strlen($teaser);
-
-  // How much to cut off the end of the teaser so that it doesn't end in the
-  // middle of a paragraph, sentence, or word.
-  // Initialize it to maximum in order to find the minimum.
-  $min_rpos = $max_rpos;
-
-  // Store the reverse of the teaser. We use strpos on the reversed needle and
-  // haystack for speed and convenience.
-  $reversed = strrev($teaser);
-
-  // Build an array of arrays of break points grouped by preference.
-  $break_points = array();
-
-  // A paragraph near the end of sliced teaser is most preferable.
-  $break_points[] = array('</p>' => 0);
-
-  // If no complete paragraph then treat line breaks as paragraphs.
-  $line_breaks = array('<br />' => 6, '<br>' => 4);
-  // Newline only indicates a line break if line break converter
-  // filter is present.
-  if (isset($filters['filter/1'])) {
-    $line_breaks["\n"] = 1;
-  }
-  $break_points[] = $line_breaks;
-
-  // If the first paragraph is too long, split at the end of a sentence.
-  $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
-
-  // Iterate over the groups of break points until a break point is found.
-  foreach ($break_points as $points) {
-    // Look for each break point, starting at the end of the teaser.
-    foreach ($points as $point => $offset) {
-      // The teaser is already reversed, but the break point isn't.
-      $rpos = strpos($reversed, strrev($point));
-      if ($rpos !== FALSE) {
-        $min_rpos = min($rpos + $offset, $min_rpos);
-      }
-    }
-
-    // If a break point was found in this group, slice and return the teaser.
-    if ($min_rpos !== $max_rpos) {
-      // Don't slice with length 0. Length must be <0 to slice from RHS.
-      return ($min_rpos === 0) ? $teaser : substr($teaser, 0, 0 - $min_rpos);
-    }
-  }
-
-  // If a break point was not found, still return a teaser.
-  return $teaser;
-}
-
-/**
  * Builds a list of available node types, and returns all of part of this list
  * in the specified format.
  *
@@ -583,6 +408,7 @@
     if (!empty($type->old_type) && $type->old_type != $type->type) {
       field_attach_rename_bundle($type->old_type, $type->type);
     }
+    node_configure_fields($type);
     module_invoke_all('node_type', 'update', $type);
     return SAVED_UPDATED;
   }
@@ -591,13 +417,66 @@
     db_insert('node_type')->fields($fields)->execute();
 
     field_attach_create_bundle($type->type);
-
+    node_configure_fields($type);
     module_invoke_all('node_type', 'insert', $type);
     return SAVED_NEW;
   }
 }
 
 /**
+ * Manage the field(s) for a node type.
+ */
+function node_configure_fields($type) {
+
+   // Add or remove the body field, as needed.
+  $field = field_info_field(NODE_BODY_FIELD);
+  $instance = field_info_instance(NODE_BODY_FIELD, $type->type);
+  if ($type->has_body) {
+    if (empty($field)) {
+      $field = array(
+        'field_name' => NODE_BODY_FIELD,
+        'type' => 'text_with_summary',
+      );
+      $field = field_create_field($field);
+    }
+    if (empty($instance)) {
+      $instance = array(
+        'field_name' => NODE_BODY_FIELD,
+        'bundle' => $type->type,
+        'label' => $type->body_label,
+        'widget_type' => 'text_textarea_with_summary',
+        
+        // With no UI in core, we have to define default
+        // formatters for the teaser and full view.
+        // This may change if the method of handling displays
+        // is changed or if a UI gets into core.
+        'display' => array(
+          'full' => array(
+            'label' => 'above',
+            'type' => 'text_default',
+            'exclude' => 0,
+            ),
+          'teaser' => array(
+            'label' => 'above',
+            'type' => 'text_summary_or_trimmed',
+            'exclude' => 0,
+            ),
+          ),
+        );
+      field_create_instance($instance);
+    }
+    else {
+      $instance['label'] = $type->body_label;
+      field_update_instance($instance);      
+    }
+  }
+  elseif (!empty($instance)) {
+    field_delete_instance($instance);
+  }
+
+}
+
+/**
  * Deletes a node type from the database.
  *
  * @param $type
@@ -974,7 +853,8 @@
 
   // Make sure the body has the minimum number of words.
   // TODO : use a better word counting algorithm that will work in other languages
-  if (!empty($type->min_word_count) && isset($node->body) && count(explode(' ', $node->body)) < $type->min_word_count) {
+  if (!empty($type->min_word_count) && isset($node->{NODE_BODY_FIELD}[0]['value']) && count(explode(' ', $node->{NODE_BODY_FIELD}[0]['value'])) < $type->min_word_count) {
+    // TODO: Use Field API to set this error.
     form_set_error('body', t('The body of your @type is too short. You need at least %words words.', array('%words' => $type->min_word_count, '@type' => $type->name)));
   }
 
@@ -1014,25 +894,6 @@
   // Convert the node to an object, if necessary.
   $node = (object)$node;
 
-  // Generate the teaser, but only if it hasn't been set (e.g. by a
-  // module-provided 'teaser' form item).
-  if (!isset($node->teaser)) {
-    if (isset($node->body)) {
-      $node->format = (!empty($node->body_format) ? $node->body_format : FILTER_FORMAT_DEFAULT);
-      $node->teaser = node_teaser($node->body, isset($node->format) ? $node->format : NULL);
-      // Chop off the teaser from the body if needed. The teaser_include
-      // property might not be set (eg. in Blog API postings), so only act on
-      // it, if it was set with a given value.
-      if (isset($node->teaser_include) && !$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
-        $node->body = substr($node->body, strlen($node->teaser));
-      }
-    }
-    else {
-      $node->teaser = '';
-      $node->format = 0;
-    }
-  }
-
   if (user_access('administer nodes')) {
     // Populate the "authored by" field.
     if ($account = user_load_by_name($node->name)) {
@@ -1072,16 +933,6 @@
     if (!isset($node->log)) {
       $node->log = '';
     }
-
-    // For the same reasons, make sure we have $node->teaser and
-    // $node->body. We should consider making these fields nullable
-    // in a future version since node types are not required to use them.
-    if (!isset($node->teaser)) {
-      $node->teaser = '';
-    }
-    if (!isset($node->body)) {
-      $node->body = '';
-    }
   }
   elseif (!empty($node->revision)) {
     $node->old_vid = $node->vid;
@@ -1216,30 +1067,6 @@
 }
 
 /**
- * Apply filters and build the node's standard elements.
- */
-function node_prepare($node, $teaser = FALSE) {
-  // First we'll overwrite the existing node teaser and body with
-  // the filtered copies! Then, we'll stick those into the content
-  // array and set the read more flag if appropriate.
-  $node->readmore = (strlen($node->teaser) < strlen($node->body));
-
-  if ($teaser == FALSE) {
-    $node->body = check_markup($node->body, $node->format, $node->language, FALSE);
-  }
-  else {
-    $node->teaser = check_markup($node->teaser, $node->format, $node->language, FALSE);
-  }
-
-  $node->content['body'] = array(
-    '#markup' => $teaser ? $node->teaser : $node->body,
-    '#weight' => 0,
-  );
-
-  return $node;
-}
-
-/**
  * Builds a structured array representing the node's content.
  *
  * @param $node
@@ -1249,7 +1076,7 @@
  *
  * @return
  *   An structured array containing the individual elements
- *   of the node's body.
+ *   of the node's content.
  */
 function node_build_content($node, $teaser = FALSE) {
 
@@ -1258,21 +1085,33 @@
     $node->build_mode = NODE_BUILD_NORMAL;
   }
 
-  // Remove the delimiter (if any) that separates the teaser from the body.
-  $node->body = isset($node->body) ? str_replace('<!--break-->', '', $node->body) : '';
-
   // The 'view' hook can be implemented to overwrite the default function
   // to display nodes.
   if (node_hook($node, 'view')) {
     $node = node_invoke($node, 'view', $teaser);
   }
-  else {
-    $node = node_prepare($node, $teaser);
-  }
 
   // Build fields content.
+  if (empty($node->content)) {
+    $node->content = array();
+  };
   $node->content += field_attach_view('node', $node, $teaser);
 
+  // Always display a read more link on teasers because we have no way
+  // to know when a teaser view is different than a full view.
+  $links = array();
+  if ($teaser) {
+    $links['node_readmore'] = array(
+      'title' => t('Read more'),
+      'href' => 'node/' . $node->nid,
+      'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title))
+    );
+  }
+  $node->content['links']['node'] = array(
+    '#type' => 'node_links',
+    '#value' => $links
+  );
+  
   // Allow modules to make their own additions to the node.
   node_invoke_node($node, 'view', $teaser);
 
@@ -1492,16 +1331,16 @@
       // Load results.
       $results = array();
       foreach ($find as $item) {
-        // Build the node body.
+        // Render the node.
         $node = node_load($item->sid);
         $node->build_mode = NODE_BUILD_SEARCH_RESULT;
         $node = node_build_content($node, FALSE, FALSE);
-        $node->body = drupal_render($node->content);
+        $node->rendered = drupal_render($node->content);
 
         // Fetch comments for snippet.
-        $node->body .= module_invoke('comment', 'node', $node, 'update_index');
+        $node->rendered .= module_invoke('comment', 'node', $node, 'update_index');
         // Fetch terms for snippet.
-        $node->body .= module_invoke('taxonomy', 'node', $node, 'update_index');
+        $node->rendered .= module_invoke('taxonomy', 'node', $node, 'update_index');
 
         $extra = node_invoke_node($node, 'search_result');
 
@@ -1514,7 +1353,7 @@
           'node' => $node,
           'extra' => $extra,
           'score' => $total ? ($item->calculated_score / $total) : 0,
-          'snippet' => search_excerpt($keys, $node->body),
+          'snippet' => search_excerpt($keys, $node->rendered),
         );
       }
       return $results;
@@ -1623,7 +1462,7 @@
   $links = array();
 
   if ($type == 'node') {
-    if ($teaser == 1 && $node->teaser && !empty($node->readmore)) {
+    if ($teaser == 1) {
       $links['node_read_more'] = array(
         'title' => t('Read more'),
         'href' => "node/$node->nid",
@@ -1929,24 +1768,11 @@
       if (node_hook($item, 'view')) {
         $item = node_invoke($item, 'view', $teaser, FALSE);
       }
-      else {
-        $item = node_prepare($item, $teaser);
-      }
 
       // Allow modules to change $node->content before the node is rendered.
       node_invoke_node($item, 'view', $teaser, FALSE);
 
-      // Set the proper node property, then unset unused $node property so that a
-      // bad theme can not open a security hole.
-      $content = drupal_render($item->content);
-      if ($teaser) {
-        $item->teaser = $content;
-        unset($item->body);
-      }
-      else {
-        $item->body = $content;
-        unset($item->teaser);
-      }
+      $item->rendered = drupal_render($item->content);
     
       // Allow modules to modify the fully-built node.
       node_invoke_node($item, 'alter', $teaser, FALSE);
@@ -1964,13 +1790,11 @@
     // Prepare the item description
     switch ($item_length) {
       case 'fulltext':
-        $item_text = $item->body;
+        $item_text = $item->rendered;
         break;
       case 'teaser':
-        $item_text = $item->teaser;
-        if (!empty($item->readmore)) {
-          $item_text .= '<p>' . l(t('read more'), 'node/' . $item->nid, array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . '</p>';
-        }
+        $item_text = $item->rendered;
+        $item_text .= '<p>' . l(t('read more'), 'node/' . $item->nid, array('absolute' => TRUE, 'attributes' => array('target' => '_blank'))) . '</p>';
         break;
       case 'title':
         $item_text = '';
@@ -2103,12 +1927,12 @@
   // save the changed time of the most recent indexed node, for the search results half-life calculation
   variable_set('node_cron_last', $node->changed);
 
-  // Build the node body.
+  // Render the node.
   $node->build_mode = NODE_BUILD_SEARCH_INDEX;
   $node = node_build_content($node, FALSE, FALSE);
-  $node->body = drupal_render($node->content);
+  $node->rendered = drupal_render($node->content);
 
-  $text = '<h1>' . check_plain($node->title) . '</h1>' . $node->body;
+  $text = '<h1>' . check_plain($node->title) . '</h1>' . $node->rendered;
 
   // Fetch extra data normally not visible
   $extra = node_invoke_node($node, 'update_index');
@@ -2792,10 +2616,6 @@
     );
   }
 
-  if ($type->has_body) {
-    $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
-  }
-
   return $form;
 }
 
Index: node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.57
diff -u -r1.57 node.pages.inc
--- node/node.pages.inc	14 Mar 2009 23:01:36 -0000	1.57
+++ node/node.pages.inc	21 Mar 2009 18:06:46 -0000
@@ -106,7 +106,7 @@
     $form['#prefix'] = $form_state['node_preview'];
   }
   $node = (object)$node;
-  foreach (array('body', 'title', 'format') as $key) {
+  foreach (array('title') as $key) {
     if (!isset($node->$key)) {
       $node->$key = NULL;
     }
@@ -267,48 +267,6 @@
 }
 
 /**
- * Return a node body field, with format and teaser.
- */
-function node_body_field(&$node, $label, $word_count) {
-
-  // Check if we need to restore the teaser at the beginning of the body.
-  $include = !isset($node->teaser) || ($node->teaser == substr($node->body, 0, strlen($node->teaser)));
-
-  $form = array(
-    '#after_build' => array('node_teaser_js', 'node_teaser_include_verify'));
-
-  $form['#prefix'] = '<div class="body-field-wrapper">';
-  $form['#suffix'] = '</div>';
-
-  $form['teaser_js'] = array(
-    '#type' => 'textarea',
-    '#rows' => 10,
-    '#teaser' => 'edit-body',
-    '#teaser_checkbox' => 'edit-teaser-include',
-    '#disabled' => TRUE,
-  );
-
-  $form['teaser_include'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Show summary in full view'),
-    '#default_value' => $include,
-    '#prefix' => '<div class="teaser-checkbox">',
-    '#suffix' => '</div>',
-  );
-
-  $form['body'] = array(
-    '#type' => 'textarea',
-    '#title' => check_plain($label),
-    '#default_value' => $include ? $node->body : ($node->teaser . $node->body),
-    '#rows' => 20,
-    '#required' => ($word_count > 0),
-    '#text_format' => isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT,
-  );
-
-  return $form;
-}
-
-/**
  * Button submit function: handle the 'Delete' button on the node form.
  */
 function node_form_delete_submit($form, &$form_state) {
@@ -369,16 +327,6 @@
 
     $node->changed = REQUEST_TIME;
 
-    // Extract a teaser, if it hasn't been set (e.g. by a module-provided
-    // 'teaser' form item).
-    if (!isset($node->teaser)) {
-      $node->teaser = empty($node->body) ? '' : node_teaser($node->body, $node->format);
-      // Chop off the teaser from the body if needed.
-      if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
-        $node->body = substr($node->body, strlen($node->teaser));
-      }
-    }
-
     // Display a preview of the node.
     // Previewing alters $node so it needs to be cloned.
     if (!form_get_errors()) {
@@ -404,28 +352,20 @@
   $output = '<div class="preview">';
 
   $preview_trimmed_version = FALSE;
-  // Do we need to preview trimmed version of post as well as full version?
-  if (isset($node->teaser) && isset($node->body)) {
-    $teaser = trim($node->teaser);
-    $body = trim(str_replace('<!--break-->', '', $node->body));
-
-    // Preview trimmed version if teaser and body will appear different;
-    // also (edge case) if both teaser and body have been specified by the user
-    // and are actually the same.
-    if ($teaser != $body || ($body && strpos($node->body, '<!--break-->') === 0)) {
-      $preview_trimmed_version = TRUE;
-    }
-  }
 
-  if ($preview_trimmed_version) {
+  $trimmed = drupal_render(node_build(clone $node, TRUE));
+  $full = drupal_render(node_build($node, FALSE));
+
+  // Do we need to preview trimmed version of post as well as full version?
+  if ($trimmed != $full) {
     drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
     $output .= '<h3>' . t('Preview trimmed version') . '</h3>';
-    $output .= drupal_render(node_build(clone $node, TRUE));
+    $output .= $trimmed;
     $output .= '<h3>' . t('Preview full version') . '</h3>';
-    $output .= drupal_render(node_build($node, FALSE));
+    $output .= $full;
   }
   else {
-    $output .= drupal_render(node_build($node, FALSE));
+    $output .= $full;
   }
   $output .= "</div>\n";
 

