Recently, I was tasked with adding a WYSIWYG editor to the summary field of one of Drupal 7’s “Text with Summary” fields. Since the summary and main field content are actually stored in two separate fields, the WYSIWYG module’s “Teaser break” thing doesn’t really work. After Googling around and finding nothing, here’s what I came up with.

First, a couple things to note:

  • We will be using the WYSIWYG module, which automatically applies the WYSIWYG editor to any textarea where #type is text_format.
  • We do not want the full formatting options for the summary field; the summary should use whatever format is selected for the content field it is attached to.

Since we want to apply this to all “Text with summary” fields, the best place to intervene is with hook_field_widget_form_alter(). This hook is called every time a field widget is rendered, so we’ll just make our changes if the field has a summary attribute.

/**
 * Implementation of hook_field_widget_form_alter().
 *
 * Add WYSIWYG treatment to textarea summary form items.
 */
function MY_MODULE_field_widget_form_alter(&$element, &$form_state, $context) {
  if (!isset($element['summary'])) {
    return;
  }

  drupal_add_css(drupal_get_path('module', 'MY_MODULE') . '/assets/textarea-summary.css');
  drupal_add_js(drupal_get_path('module', 'MY_MODULE') . '/assets/textarea-summary.js');
  $element['summary']['#type'] = 'text_format';
  $element['summary']['#format'] = $element['#format'];
}

/**
 * Implements hook_field_attach_presave().
 */
function MY_MODULE_field_attach_presave($entity_type, $entity) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);

  foreach (field_info_instances($entity_type, $bundle) as $instance) {
    $field = field_info_field_by_id($instance['field_id']);
    $field_name = $field['field_name'];
    if ($field['type'] !== 'text_with_summary' || empty($entity->$field_name)) {
      continue;
    }

    $language = isset($entity->language) ? $entity->language : LANGUAGE_NONE;
    foreach ($entity->{$field_name}[$language] as $id => &$value) {
      if (!is_array($value['summary'])) {
        continue;
      }

      $value['summary'] = $value['summary']['value'];
    }
  }
}

Notice also that we need to implement hook_field_attach_presave(). This is because changing the summary field to a formatted text area actually changes the data structure of the field instance; it makes $field['summary'] into an array that also stores the format. We will get a database exception if we do not change the structure back to what the Field API expects.

Then, let’s hide the format fieldset on the summary field with CSS.

div.text-summary-wrapper fieldset.filter-wrapper { display: none; }

Then, we’ll change the hidden format field whenever the main field’s format is changed.

(function ($) {

  Drupal.behaviors.myModuleTextareaSummary = {
    attach: function (context, settings) {

      // Change the value of the summary format whenever the main textarea
      // format changes.
      $('.field-type-text-with-summary div.text-format-wrapper > fieldset.filter-wrapper select').change(function(event) {
        var value = $(this).val();
        $('.text-summary-wrapper select', $(this).closest('.text-format-wrapper')).val(value);
      });

    }
  };

}(jQuery));

And there you have it.