Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1045 diff -u -p -r1.1045 common.inc --- includes/common.inc 11 Nov 2009 00:48:56 -0000 1.1045 +++ includes/common.inc 12 Nov 2009 00:11:00 -0000 @@ -5549,9 +5549,6 @@ function drupal_common_theme() { 'form_required_marker' => array( 'arguments' => array('element' => NULL), ), - 'text_format_wrapper' => array( - 'render element' => 'element', - ), 'vertical_tabs' => array( 'render element' => 'element', ), Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.397 diff -u -p -r1.397 form.inc --- includes/form.inc 11 Nov 2009 08:52:53 -0000 1.397 +++ includes/form.inc 12 Nov 2009 00:11:25 -0000 @@ -1086,10 +1086,11 @@ function form_builder($form_id, $element // Check to see if a tree of child elements is present. If so, // continue down the tree if required. $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key); - $array_parents = isset($element['#array_parents']) ? $element['#array_parents'] : array(); - $array_parents[] = $key; - $element[$key]['#array_parents'] = $array_parents; } + // Ensure #array_parents matches the actual internal parents in the form. + $array_parents = isset($element['#array_parents']) ? $element['#array_parents'] : array(); + $array_parents[] = $key; + $element[$key]['#array_parents'] = $array_parents; // Assign a decimal placeholder weight to preserve original array order. if (!isset($element[$key]['#weight'])) { @@ -1982,110 +1983,6 @@ function form_process_radios($element) { } /** - * Add text format selector to text elements with the #text_format property. - * - * The #text_format property should be the ID of an text format, found in - * {filter_format}.format, which gets passed to filter_form(). - * - * If the property #text_format is set, the form element will be expanded into - * two separate form elements, one holding the content of the element, and the - * other holding the text format selector. The original element is shifted into - * a child element, but is otherwise unaltered, so that the format selector is - * at the same level as the text field which it affects. - * - * For example: - * @code - * // A simple textarea, such as a node body. - * $form['body'] = array( - * '#type' => 'textarea', - * '#title' => t('Body'), - * '#text_format' => isset($node->format) ? $node->format : filter_default_format(), - * ); - * @endcode - * - * Becomes: - * @code - * $form['body'] = array( - * // Type switches to 'markup', as we're only interested in submitting the child elements. - * '#type' => 'markup', - * // 'value' holds the original element. - * 'value' => array( - * '#type' => 'textarea', - * '#title' => t('Body'), - * '#parents' => array('body'), - * ), - * // 'format' holds the text format selector. - * 'format' => array( - * '#parents' => array('body_format'), - * ... - * ), - * ); - * @endcode - * - * And would result in: - * @code - * // Original, unaltered form element value. - * $form_state['values']['body'] = 'Example content'; - * // Chosen text format. - * $form_state['values']['body_format'] = 1; - * @endcode - * - * @see system_element_info(), filter_form() - */ -function form_process_text_format($element) { - if (isset($element['#text_format'])) { - // Determine the form element parents and element name to use for the input - // format widget. This simulates the 'element' and 'element_format' pair of - // parents that filter_form() expects. - $element_parents = $element['#parents']; - $element_name = array_pop($element_parents); - $element_parents[] = $element_name . '_format'; - - // We need to break references, otherwise form_builder recurses infinitely. - $element['value'] = (array)$element; - $element['value']['#weight'] = 0; - unset($element['value']['#description']); - $element['#type'] = 'markup'; - $element['#theme'] = NULL; - $element['#theme_wrappers'] = array('text_format_wrapper'); - $element['format'] = filter_form($element['#text_format'], 1, $element_parents); - - // We need to clear the #text_format from the new child otherwise we - // would get into an infinite loop. - unset($element['value']['#text_format']); - } - return $element; -} - -/** - * Theme a text format form element. - * - * @param $variables - * An associative array containing: - * - element: An associative array containing the properties of the element. - * Properties used: #children, #description - * - * @return - * A string representing the form element. - * - * @ingroup themeable - */ -function theme_text_format_wrapper($variables) { - $element = $variables['element']; - $output = '
' . "\n"; - - $output .= $element['#children'] . "\n"; - - if (!empty($element['#description'])) { - $output .= '
' . $element['#description'] . "
\n"; - } - - $output .= "
\n"; - - return $output; -} - -/** * Theme a checkbox form element. * * @param $variables Index: misc/form.js =================================================================== RCS file: /cvs/drupal/drupal/misc/form.js,v retrieving revision 1.12 diff -u -p -r1.12 form.js --- misc/form.js 16 Oct 2009 16:37:00 -0000 1.12 +++ misc/form.js 7 Nov 2009 22:47:19 -0000 @@ -69,23 +69,6 @@ Drupal.behaviors.multiselectSelector = { }; /** - * Automatically display the guidelines of the selected text format. - */ -Drupal.behaviors.filterGuidelines = { - attach: function (context) { - $('.filter-guidelines', context).once('filter-guidelines') - .find('label').hide() - .parents('.filter-wrapper').find('select.filter-list') - .bind('change', function () { - $(this).parents('.filter-wrapper') - .find('.filter-guidelines-item').hide() - .siblings('#filter-guidelines-' + this.value).show(); - }) - .change(); - } -}; - -/** * Prepopulate form fields with information from the visitor cookie. */ Drupal.behaviors.fillUserInfoFromCookie = { Index: modules/block/block.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v retrieving revision 1.62 diff -u -p -r1.62 block.admin.inc --- modules/block/block.admin.inc 11 Nov 2009 08:28:50 -0000 1.62 +++ modules/block/block.admin.inc 12 Nov 2009 00:11:01 -0000 @@ -446,7 +446,7 @@ function block_add_block_form_submit($fo ->fields(array( 'body' => $form_state['values']['body'], 'info' => $form_state['values']['info'], - 'format' => $form_state['values']['body_format'], + 'format' => $form_state['values']['format'], )) ->execute(); Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.398 diff -u -p -r1.398 block.module --- modules/block/block.module 11 Nov 2009 08:52:27 -0000 1.398 +++ modules/block/block.module 12 Nov 2009 00:11:01 -0000 @@ -421,10 +421,10 @@ function block_custom_block_form($edit = ); $form['body_field']['#weight'] = -17; $form['body_field']['body'] = array( - '#type' => 'textarea', + '#type' => 'textarea_format', '#title' => t('Block body'), '#default_value' => $edit['body'], - '#text_format' => isset($edit['format']) ? $edit['format'] : filter_default_format(), + '#format' => isset($edit['format']) ? $edit['format'] : NULL, '#rows' => 15, '#description' => t('The content of the block as shown to the user.'), '#required' => TRUE, @@ -453,7 +453,7 @@ function block_custom_block_save($edit, ->fields(array( 'body' => $edit['body'], 'info' => $edit['info'], - 'format' => $edit['body_format'], + 'format' => $edit['format'], )) ->condition('bid', $delta) ->execute(); Index: modules/block/block.test =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.test,v retrieving revision 1.31 diff -u -p -r1.31 block.test --- modules/block/block.test 10 Nov 2009 17:27:53 -0000 1.31 +++ modules/block/block.test 12 Nov 2009 00:11:01 -0000 @@ -79,7 +79,7 @@ class BlockTestCase extends DrupalWebTes $custom_block['info'] = $this->randomName(8); $custom_block['title'] = $this->randomName(8); $custom_block['body'] = '

Full HTML

'; - $custom_block['body_format'] = 2; + $custom_block['format'] = 2; $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); // Set the created custom block to a specific region. Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.803 diff -u -p -r1.803 comment.module --- modules/comment/comment.module 8 Nov 2009 10:02:41 -0000 1.803 +++ modules/comment/comment.module 12 Nov 2009 00:11:01 -0000 @@ -1288,7 +1288,7 @@ function comment_save($comment) { 'changed' => $comment->changed, 'subject' => $comment->subject, 'comment' => $comment->comment, - 'format' => $comment->comment_format, + 'format' => $comment->format, 'uid' => $comment->uid, 'name' => $comment->name, 'mail' => $comment->mail, @@ -1364,7 +1364,7 @@ function comment_save($comment) { 'uid' => $comment->uid, 'subject' => $comment->subject, 'comment' => $comment->comment, - 'format' => $comment->comment_format, + 'format' => $comment->format, 'hostname' => ip_address(), 'created' => $comment->created, 'changed' => $comment->changed, @@ -1841,11 +1841,11 @@ function comment_form($form, &$form_stat } $form['comment'] = array( - '#type' => 'textarea', + '#type' => 'textarea_format', '#title' => t('Comment'), '#rows' => 15, '#default_value' => $default, - '#text_format' => isset($comment->format) ? $comment->format : filter_default_format(), + '#format' => isset($comment->format) ? $comment->format : NULL, '#required' => TRUE, ); @@ -1923,8 +1923,6 @@ function comment_preview($comment) { $node = node_load($comment->nid); if (!form_get_errors()) { - $comment->format = $comment->comment_format; - // Attach the user and time information. if (!empty($comment->author)) { $account = user_load_by_name($comment->author); @@ -2053,7 +2051,7 @@ function comment_submit($comment) { // 1) Filter it into HTML // 2) Strip out all HTML tags // 3) Convert entities back to plain-text. - $comment['subject'] = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment['comment'], $comment['comment_format'])))), 29, TRUE); + $comment['subject'] = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment['comment'], $comment['format'])))), 29, TRUE); // Edge cases where the comment body is populated only by HTML tags will // require a default subject. if ($comment['subject'] == '') { Index: modules/comment/comment.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.pages.inc,v retrieving revision 1.29 diff -u -p -r1.29 comment.pages.inc --- modules/comment/comment.pages.inc 8 Nov 2009 10:02:41 -0000 1.29 +++ modules/comment/comment.pages.inc 12 Nov 2009 00:11:01 -0000 @@ -108,7 +108,6 @@ function comment_reply($node, $pid = NUL */ function comment_approve($comment) { $comment->status = COMMENT_PUBLISHED; - $comment->comment_format = $comment->format; comment_save($comment); drupal_set_message(t('Comment approved.')); Index: modules/field/modules/text/text.js =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.js,v retrieving revision 1.1 diff -u -p -r1.1 text.js --- modules/field/modules/text/text.js 11 Sep 2009 13:30:49 -0000 1.1 +++ modules/field/modules/text/text.js 12 Nov 2009 01:06:58 -0000 @@ -5,13 +5,13 @@ /** * Auto-hide summary textarea if empty and show hide and unhide links. */ -Drupal.behaviors.textTextareaSummary = { +Drupal.behaviors.textSummary = { attach: function (context, settings) { - $('textarea.text-textarea-summary:not(.text-textarea-summary-processed)', context).addClass('text-textarea-summary-processed').each(function () { - var $fieldset = $(this).closest('#body-wrapper'); - var $summary = $fieldset.find('div.text-summary-wrapper'); - var $summaryLabel = $summary.find('div.form-type-textarea label'); - var $full = $fieldset.find('div.text-full-wrapper'); + $('.text-summary', context).once('text-summary', function () { + var $widget = $(this).closest('div.field-type-text-with-summary'); + var $summary = $widget.find('div.text-summary-wrapper'); + var $summaryLabel = $summary.find('label'); + var $full = $widget.find('div.text-full-wrapper'); var $fullLabel = $full.find('div.form-type-textarea label'); // Setup the edit/hide summary link. Index: modules/field/modules/text/text.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v retrieving revision 1.36 diff -u -p -r1.36 text.module --- modules/field/modules/text/text.module 11 Nov 2009 08:32:35 -0000 1.36 +++ modules/field/modules/text/text.module 12 Nov 2009 01:03:44 -0000 @@ -146,7 +146,7 @@ function text_field_instance_settings_fo '#default_value' => $settings['text_processing'], '#options' => array( t('Plain text'), - t('Filtered text (user selects input format)'), + t('Filtered text (user selects text format)'), ), ); if ($field['type'] == 'text_with_summary') { @@ -154,7 +154,7 @@ function text_field_instance_settings_fo '#type' => 'checkbox', '#title' => t('Summary input'), '#default_value' => $settings['display_summary'], - '#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display format.'), + '#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display type.'), ); } @@ -170,13 +170,16 @@ function text_field_instance_settings_fo */ function text_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) { foreach ($items as $delta => $item) { - foreach (array('value' => t('full text'), 'summary' => t('summary')) as $column => $desc) { + // @todo Length is counted separately for summary and value, so the maximum + // length can be exceeded very easily. + foreach (array('value', 'summary') as $column) { if (!empty($item[$column])) { if (!empty($field['settings']['max_length']) && drupal_strlen($item[$column]) > $field['settings']['max_length']) { switch ($column) { case 'value': $message = t('%name: the text may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])); break; + case 'summary': $message = t('%name: the summary may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length'])); break; @@ -205,11 +208,11 @@ function text_field_load($obj_type, $obj if (!empty($instances[$id]['settings']['text_processing'])) { // Only process items with a cacheable format, the rest will be // handled by text_field_sanitize(). - $format = $item['format']; - if (filter_format_allowcache($format)) { - $items[$id][$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format, $langcode) : ''; + $format_id = $item['format']; + if (filter_format_allowcache($format_id)) { + $items[$id][$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format_id, $langcode) : ''; if ($field['type'] == 'text_with_summary') { - $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format, $langcode) : ''; + $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format_id, $langcode) : ''; } } } @@ -235,10 +238,10 @@ function text_field_sanitize($obj_type, // from a form preview. if (!isset($items[$delta]['safe'])) { if (!empty($instance['settings']['text_processing'])) { - $format = $item['format']; - $items[$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format, $langcode, TRUE) : ''; + $format_id = $item['format']; + $items[$delta]['safe'] = isset($item['value']) ? check_markup($item['value'], $format_id, $langcode, TRUE) : ''; if ($field['type'] == 'text_with_summary') { - $items[$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format, $langcode, TRUE) : ''; + $items[$delta]['safe_summary'] = isset($item['summary']) ? check_markup($item['summary'], $format_id, $langcode, TRUE) : ''; } } else { @@ -469,14 +472,6 @@ function text_summary($text, $format = N /** * Implement hook_field_widget_info(). - * - * Here we indicate that the field module will handle - * the default value and multiple values for these widgets. - * - * Callbacks can be omitted if default handing is used. - * They're included here just so this module can be used - * as an example for custom modules that might do things - * differently. */ function text_field_widget_info() { return array( @@ -528,49 +523,54 @@ function text_field_widget_settings_form } /** - * Implement hook_element_info(). - * - * Autocomplete_path is not used by text_field_widget but other - * widgets can use it (see nodereference and userreference). - */ -function text_element_info() { - $types['text_textfield'] = array( - '#input' => TRUE, - '#columns' => array('value'), - '#delta' => 0, - '#process' => array('text_textfield_elements_process'), - '#theme_wrappers' => array('text_textfield'), - '#autocomplete_path' => FALSE, - ); - $types['text_textarea'] = array( - '#input' => TRUE, - '#columns' => array('value', 'format'), - '#delta' => 0, - '#process' => array('text_textarea_elements_process'), - '#theme_wrappers' => array('text_textarea'), - '#filter_value' => filter_default_format(), - ); - $types['text_textarea_with_summary'] = array( - '#input' => TRUE, - '#columns' => array('value', 'format', 'summary'), - '#delta' => 0, - '#process' => array('text_textarea_with_summary_process'), - '#theme_wrappers' => array('text_textarea'), - '#filter_value' => filter_default_format(), - ); - return $types; -} - -/** * Implement hook_field_widget(). */ -function text_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - $element += array( - '#type' => $instance['widget']['type'], - '#default_value' => isset($items[$delta]) ? $items[$delta] : '', - ); +function text_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) { + // Add the Summary field as separate form element, if enabled. + if (isset($base['#columns'][1]) && $base['#columns'][1] == 'summary') { + $field_key = $base['#columns'][1]; + $display = !empty($items[$delta][$field_key]) || !empty($instance['settings']['display_summary']); + $element[$field_key] = array( + '#type' => $display ? 'textarea' : 'value', + '#default_value' => isset($items[$delta][$field_key]) ? $items[$delta][$field_key] : NULL, + '#title' => t('Summary'), + '#description' => t('Leave blank to use trimmed value of full text as the summary.'), + '#attached' => array( + 'js' => array(drupal_get_path('module', 'text') . '/text.js'), + ), + '#attributes' => array('class' => array('text-summary')), + '#prefix' => '
', + '#suffix' => '
', + '#weight' => -10, + ); + } + + $field_key = $base['#columns'][0]; + switch ($instance['widget']['type']) { + case 'text_textfield': + $element[$field_key] = $base + array( + '#type' => 'textfield', + '#default_value' => isset($items[$delta][$field_key]) ? $items[$delta][$field_key] : NULL, + '#size' => $instance['widget']['settings']['size'], + '#prefix' => '
', + '#suffix' => '
', + ); + break; + + default: + $element[$field_key] = $base + array( + '#type' => 'textarea', + '#default_value' => isset($items[$delta][$field_key]) ? $items[$delta][$field_key] : NULL, + '#prefix' => '
', + '#suffix' => '
', + ); + break; + } + + // Conditionally alter the form element's type if text processing is enabled. if (!empty($instance['settings']['text_processing'])) { - $element['#value_callback'] = 'text_field_widget_formatted_text_value'; + $element[$field_key]['#type'] .= '_format'; + $element[$field_key]['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL; } return $element; @@ -593,168 +593,3 @@ function text_field_widget_error($elemen form_error($error_element, $error['message']); } -/** - * 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']]. - * - * TODO: For widgets to be actual FAPI 'elements', reusable outside of a - * 'field' context, they shoudn't rely on $field and $instance. The bits of - * information needed to adjust the behavior of the 'element' should be - * extracted in hook_field_widget() above. - */ -function text_textfield_elements_process($element, $form_state, $form) { - $field = $form['#fields'][$element['#field_name']]['field']; - $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'], - '#title' => $element['#title'], - '#description' => $element['#description'], - '#required' => $element['#required'], - ); - - $element[$field_key]['#maxlength'] = !empty($field['settings']['max_length']) ? $field['settings']['max_length'] : NULL; - - 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_default_format(); - $element[$field_key]['#text_format'] = $format; - } - - 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_elements_process($element, $form_state, $form) { - $field = $form['#fields'][$element['#field_name']]['field']; - $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, - '#title' => $element['#title'], - '#description' => $element['#description'], - '#required' => $element['#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_default_format(); - $element[$field_key]['#text_format'] = $format; - } - - 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, $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.'), - '#display' => $display, - '#attached' => array('js' => array(drupal_get_path('module', 'text') . '/text.js')), - '#attributes' => array('class' => array('text-textarea-summary')), - '#prefix' => '
', - '#suffix' => '
', - ); - - $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'], - '#prefix' => '
', - '#suffix' => '
', - ); - - 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_default_format(); - $element[$field_key]['#text_format'] = $format; - } - - return $element; -} - -/** - * Helper function to determine the value for a formatted text widget. - * - * '#text_format' puts the format in '[column 0]_format' in incoming values, - * while we need it in '[column 1]'. - */ -function text_field_widget_formatted_text_value($form, $edit = FALSE) { - if ($edit !== FALSE) { - $field_key = $form['#columns'][0]; - $filter_key = (count($form['#columns']) == 2) ? $form['#columns'][1] : 'format'; - $default_key = $field_key . '_format'; - // The format selector uses #access = FALSE if only one format is - // available. In this case, we don't receive its value, and need to - // manually set it. - $edit['format'] = !empty($edit[$default_key]) ? $edit[$default_key] : filter_default_format(); - unset($edit[$default_key]); - return $edit; - } -} - -/** - * FAPI theme for an individual text elements. - * - * The textfield or textarea is already rendered by the - * textfield or textarea themes and the html output - * lives in $variables['element']['#children']. Override this theme to - * make custom changes to the output. - * - * $variables['element']['#field_name'] contains the field name - * $variables['element']['#delta] is the position of this element in the group - */ -function theme_text_textfield($variables) { - $element = $variables['element']; - return $element['#children']; -} - -function theme_text_textarea($variables) { - $element = $variables['element']; - return $element['#children']; -} Index: modules/field/modules/text/text.test =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.test,v retrieving revision 1.14 diff -u -p -r1.14 text.test --- modules/field/modules/text/text.test 16 Oct 2009 03:47:14 -0000 1.14 +++ modules/field/modules/text/text.test 7 Nov 2009 23:57:22 -0000 @@ -166,7 +166,7 @@ class TextFieldTestCase extends DrupalWe // no format selector will be displayed. $this->drupalGet('test-entity/add/test-bundle'); $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed')); - $this->assertNoFieldByName("{$this->field_name}[$langcode][0][value_format]", '', t('Format selector is not displayed')); + $this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is not displayed')); // Submit with data that should be filtered. $value = '' . $this->randomName() . ''; @@ -202,11 +202,11 @@ class TextFieldTestCase extends DrupalWe // We should now have a 'text format' selector. $this->drupalGet('test-entity/' . $id . '/edit'); $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed')); - $this->assertFieldByName("{$this->field_name}[$langcode][0][value_format]", '', t('Format selector is displayed')); + $this->assertFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is displayed')); // Edit and change the text format to the new one that was created. $edit = array( - "{$this->field_name}[$langcode][0][value_format]" => $format_id, + "{$this->field_name}[$langcode][0][format]" => $format_id, ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), t('Entity was updated')); Index: modules/filter/filter.css =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.css,v retrieving revision 1.1 diff -u -p -r1.1 filter.css --- modules/filter/filter.css 30 Mar 2009 03:15:40 -0000 1.1 +++ modules/filter/filter.css 7 Nov 2009 22:55:41 -0000 @@ -11,7 +11,6 @@ } .filter-wrapper .form-item { float: left; - margin: 0; padding: 0 0 0.5em 1.5em; } .filter-wrapper .form-item label { Index: modules/filter/filter.js =================================================================== RCS file: modules/filter/filter.js diff -N modules/filter/filter.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/filter/filter.js 7 Nov 2009 22:46:56 -0000 @@ -0,0 +1,21 @@ +// $Id: form.js,v 1.12 2009/10/16 16:37:00 dries Exp $ +(function ($) { + +/** + * Automatically display the guidelines of the selected text format. + */ +Drupal.behaviors.filterGuidelines = { + attach: function (context) { + $('.filter-guidelines', context).once('filter-guidelines') + .find('label').hide() + .parents('.filter-wrapper').find('select.filter-list') + .bind('change', function () { + $(this).parents('.filter-wrapper') + .find('.filter-guidelines-item').hide() + .siblings('#filter-guidelines-' + this.value).show(); + }) + .change(); + } +}; + +})(jQuery); Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.300 diff -u -p -r1.300 filter.module --- modules/filter/filter.module 23 Oct 2009 22:24:14 -0000 1.300 +++ modules/filter/filter.module 8 Nov 2009 04:03:49 -0000 @@ -49,6 +49,9 @@ function filter_theme() { 'variables' => array('tips' => NULL, 'long' => FALSE), 'file' => 'filter.pages.inc', ), + 'filter_format_wrapper' => array( + 'render element' => 'element', + ), 'filter_tips_more_info' => array( 'variables' => array(), ), @@ -59,6 +62,24 @@ function filter_theme() { } /** + * Implement of hook_elements(). + * + * Elements defined by Filter module are wrappers around the respective, + * non-prefixed elements and are expanded by filter_process_format(). + * + * @see filter_process_format() + */ +function filter_element_info() { + $type['textfield_format'] = array( + '#process' => array('filter_process_format'), + ); + $type['textarea_format'] = array( + '#process' => array('filter_process_format'), + ); + return $type; +} + +/** * Implement hook_menu(). */ function filter_menu() { @@ -617,69 +638,175 @@ function check_markup($text, $format_id /** * Generates a selector for choosing a format in a form. * - * @param $selected_format - * The ID of the format that is currently selected; uses the default format - * for the current user if not provided. - * @param $weight - * The weight of the form element within the form. - * @param $parents - * The parents array of the element. Required when defining multiple text - * formats on a single form or having a different parent than 'format'. + * The form element will be expanded into two separate form elements, one + * holding the original element, and the other holding the text format selector. + * The original element is shifted into a child element, but is otherwise + * unaltered, so that the format selector is at the same level as the element + * which it affects. + * + * The optional #format property should be the ID of an text format, found in + * {filter_format}.format, or NULL to use the default format for the current + * user. For example: + * @code + * $form['some']['field'] = array( + * '#type' => 'textarea_format', + * '#title' => t('My field'), + * '#format' => isset($node->format) ? $node->format : NULL, + * '#default_value' => 'Some to be filtered text.', + * ); + * @endcode + * + * Becomes: + * @code + * $form['some']['field'] = array( + * '#type' => 'markup', + * // 'value' holds the original element. + * 'value' => array( + * '#type' => 'textarea', + * '#title' => t('My field'), + * '#default_value' => 'Some to be filtered text.', + * // Albeit the form structure is changed, the submitted value will still + * // appear where the original element was located: + * '#parents' => array('some', 'field'), + * ), + * // 'format' holds the text format widget. + * 'format' => array( + * // The 'format' value will appear next to the original element. + * '#parents' => array('some', 'format'), + * ), + * ); + * @endcode + * + * Which results in the submitted form values: + * @code + * // Original form element value. + * $form_state['values']['some']['field'] = 'Some to be filtered text.'; + * // Chosen text format. + * $form_state['values']['some']['format'] = 1; + * @endcode + * + * @param $element + * The form element to process. Properties used: + * - #type: A form element #type suffixed with '_format'. The form element for + * 'value' will use this #type without the '_format' suffix. For example, + * if the to be processed form element specifies 'textarea_format', then the + * new child element in 'value' will use the #type 'textarea'. By default, + * Filter module registers 'textarea_format' and 'textfield_format' in + * filter_element_info(). Custom implementations needs to register the + * suffixed '_format' types in hook_element_info(). + * - #format: (optional) The text format id to preselect. If 0, NULL, or not + * defined, the default format for the current user will be used. * * @return - * Form API array for the form element. - * - * @ingroup forms + * The expanded element. */ -function filter_form($selected_format = NULL, $weight = NULL, $parents = array('format')) { +function filter_process_format($element) { global $user; - // Use the default format for this user if none was selected. - if (empty($selected_format)) { - $selected_format = filter_default_format($user); - } - + // The element will be expanded into two new child elements 'value' and + // 'format', but only in the form structure. The original element's value is + // expected at its original location of the form structure and the new born + // 'format' element's value is expected next to it on the same level. To do + // this, we take the original element's #parents property, apply it to the new + // 'value' element, and make the new 'format' element use the same parents. + $format_parents = $element['#parents']; + // Make 'format' appear on the same level as the passed in element. + array_pop($format_parents); + $format_parents[] = 'format'; + + // Move this element into sub-element 'value'. Break references and remove + // this #process function to prevent form_builder() to recurse infinitely. + $element['value'] = (array) $element; + $element['value']['#process'] = array_diff($element['value']['#process'], array('filter_process_format')); + + // Turn this element into a text format wrapper. + $element['#type'] = 'markup'; + unset($element['#theme']); + $element['#theme_wrappers'] = array('filter_format_wrapper'); + $path = drupal_get_path('module', 'filter'); + $element['#attached']['js'][] = $path . '/filter.js'; + $element['#attached']['css'][] = $path . '/filter.css'; + + // Setup child element for the value. + // The #type without '_format' suffix is expected to be a valid #type. This + // allows to enhance further (and custom) form elements with text formats. + $element['value']['#type'] = strtr($element['value']['#type'], array('_format' => '')); + $element['value'] += element_info($element['value']['#type']); + // Description is handled by theme_filter_format_wrapper() and weight needs + // to be reset to ensure proper ordering. + unset($element['value']['#description'], $element['value']['#weight']); + // Unset properties that will be processed for the original element already. + unset($element['value']['#prefix'], $element['value']['#suffix'], $element['value']['#attached']); + + // Setup child element for the text format widget. + $element['format'] = element_info('fieldset'); + $element['format']['#defaults_loaded'] = TRUE; + $element['format']['#attributes']['class'][] = 'filter-wrapper'; + + // Prepare text format guidelines. + $element['format']['guidelines'] = array( + '#type' => 'container', + '#attributes' => array('class' => array('filter-guidelines')), + '#weight' => 20, + ); // Get a list of formats that the current user has access to. $formats = filter_formats($user); - - drupal_add_js('misc/form.js'); - drupal_add_css(drupal_get_path('module', 'filter') . '/filter.css'); - $element_id = drupal_html_id('edit-' . implode('-', $parents)); - - $form = array( - '#type' => 'fieldset', - '#weight' => $weight, - '#attributes' => array('class' => array('filter-wrapper')), - ); - $form['format_guidelines'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#weight' => 2, - ); foreach ($formats as $format) { $options[$format->format] = $format->name; - $form['format_guidelines'][$format->format] = array( - '#markup' => theme('filter_guidelines', array('format' => $format)), + $element['format']['guidelines'][$format->format] = array( + '#theme' => 'filter_guidelines', + '#format' => $format, ); } - $form['format'] = array( + + // Use the default format for this user if none was selected. + if (empty($element['#format'])) { + $element['#format'] = filter_default_format($user); + } + $element['format']['format'] = array( '#type' => 'select', '#title' => t('Text format'), '#options' => $options, - '#default_value' => $selected_format, - '#parents' => $parents, + '#default_value' => $element['#format'], '#access' => count($formats) > 1, - '#id' => $element_id, + '#weight' => 10, '#attributes' => array('class' => array('filter-list')), + '#parents' => $format_parents, ); - $form['format_help'] = array( - '#prefix' => '
', - '#markup' => theme('filter_tips_more_info'), - '#suffix' => '
', - '#weight' => 1, + + $element['format']['help'] = array( + '#type' => 'container', + '#theme' => 'filter_tips_more_info', + '#attributes' => array('class' => array('filter-help')), + '#weight' => 0, ); - return $form; + return $element; +} + +/** + * Theme a text format form element. + * + * @param $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + * Properties used: #children, #description + * + * @return + * A string representing the form element. + * + * @ingroup themeable + */ +function theme_filter_format_wrapper($variables) { + $element = $variables['element']; + $output = '
'; + $output .= $element['#children']; + if (!empty($element['#description'])) { + $output .= '
' . $element['#description'] . '
'; + } + $output .= "
\n"; + + return $output; } /** Index: modules/filter/filter.test =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v retrieving revision 1.47 diff -u -p -r1.47 filter.test --- modules/filter/filter.test 10 Nov 2009 17:27:53 -0000 1.47 +++ modules/filter/filter.test 12 Nov 2009 00:11:02 -0000 @@ -277,7 +277,7 @@ class FilterAdminTestCase extends Drupal $langcode = FIELD_LANGUAGE_NONE; $edit["title[$langcode][0][value]"] = $this->randomName(); $edit["body[$langcode][0][value]"] = $text; - $edit["body[$langcode][0][value_format]"] = $filtered; + $edit["body[$langcode][0][format]"] = $filtered; $this->drupalPost('node/add/page', $edit, t('Save')); $this->assertRaw(t('Page %title has been created.', array('%title' => $edit["title[$langcode][0][value]"])), t('Filtered node created.')); @@ -289,7 +289,7 @@ class FilterAdminTestCase extends Drupal // Use plain text and see if it escapes all tags, whether allowed or not. $edit = array(); - $edit["body[$langcode][0][value_format]"] = $plain; + $edit["body[$langcode][0][format]"] = $plain; $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); $this->drupalGet('node/' . $node->nid); $this->assertText(check_plain($text), t('The "Plain text" text format escapes all HTML tags.')); @@ -1163,7 +1163,7 @@ class FilterHooksTestCase extends Drupal $custom_block['title'] = $this->randomName(8); $custom_block['body'] = $this->randomName(32); // Use the format created. - $custom_block['body_format'] = $format_id; + $custom_block['format'] = $format_id; $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block')); $this->assertText(t('The block has been created.'), t('New block successfully created.')); Index: modules/node/node.test =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.test,v retrieving revision 1.54 diff -u -p -r1.54 node.test --- modules/node/node.test 8 Nov 2009 12:30:35 -0000 1.54 +++ modules/node/node.test 12 Nov 2009 00:11:03 -0000 @@ -880,7 +880,7 @@ class NodeTypeTestCase extends DrupalWeb // Verify that title and body fields are displayed. $this->drupalGet('node/add/page'); $this->assertRaw('Title', t('Title field was found.')); - $this->assertRaw('Full text', t('Body field was found.')); + $this->assertRaw($instance['label'], t('Body field was found.')); // Rename the title field and remove the body field. $edit = array( @@ -914,7 +914,7 @@ class NodeTypeTestCase extends DrupalWeb $this->clickLink('Bar'); $this->assertEqual(url('node/add/bar', array('absolute' => TRUE)), $this->getUrl(), t('New machine name was used in URL.')); $this->assertRaw('Foo', t('Title field was found.')); - $this->assertRaw('Full text', t('Body field was found.')); + $this->assertRaw($instance['label'], t('Body field was found.')); } } Index: modules/php/php.test =================================================================== RCS file: /cvs/drupal/drupal/modules/php/php.test,v retrieving revision 1.18 diff -u -p -r1.18 php.test --- modules/php/php.test 11 Oct 2009 03:07:19 -0000 1.18 +++ modules/php/php.test 8 Nov 2009 00:02:17 -0000 @@ -65,7 +65,7 @@ class PHPFilterTestCase extends PHPTestC // Change filter to PHP filter and see that PHP code is evaluated. $edit = array(); $langcode = FIELD_LANGUAGE_NONE; - $edit["body[$langcode][0][value_format]"] = $this->php_code_format; + $edit["body[$langcode][0][format]"] = $this->php_code_format; $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); $this->assertRaw(t('Page %title has been updated.', array('%title' => $node->title[FIELD_LANGUAGE_NONE][0]['value'])), t('PHP code filter turned on.')); Index: modules/search/search.test =================================================================== RCS file: /cvs/drupal/drupal/modules/search/search.test,v retrieving revision 1.42 diff -u -p -r1.42 search.test --- modules/search/search.test 19 Oct 2009 23:28:40 -0000 1.42 +++ modules/search/search.test 8 Nov 2009 01:23:01 -0000 @@ -488,7 +488,7 @@ class SearchCommentTestCase extends Drup $edit_comment = array( 'subject' => $this->randomName(2), 'comment' => '

' . $comment_body . '

', - 'comment_format' => 2, + 'format' => 2, ); $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save')); Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.840 diff -u -p -r1.840 system.module --- modules/system/system.module 11 Nov 2009 17:14:59 -0000 1.840 +++ modules/system/system.module 12 Nov 2009 00:11:04 -0000 @@ -354,7 +354,7 @@ function system_element_info() { '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE, - '#process' => array('form_process_text_format', 'ajax_process_form'), + '#process' => array('ajax_process_form'), '#theme' => 'textfield', '#theme_wrappers' => array('form_element'), ); @@ -376,7 +376,7 @@ function system_element_info() { '#cols' => 60, '#rows' => 5, '#resizable' => TRUE, - '#process' => array('form_process_text_format', 'ajax_process_form'), + '#process' => array('ajax_process_form'), '#theme' => 'textarea', '#theme_wrappers' => array('form_element'), ); Index: modules/taxonomy/taxonomy.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v retrieving revision 1.83 diff -u -p -r1.83 taxonomy.admin.inc --- modules/taxonomy/taxonomy.admin.inc 8 Nov 2009 11:19:02 -0000 1.83 +++ modules/taxonomy/taxonomy.admin.inc 12 Nov 2009 00:11:04 -0000 @@ -649,13 +649,14 @@ function taxonomy_form_term($form, &$for '#title' => t('Term name'), '#default_value' => $edit['name'], '#maxlength' => 255, - '#required' => TRUE); + '#required' => TRUE, + ); $form['identification']['description'] = array( - '#type' => 'textarea', + '#type' => 'textarea_format', '#title' => t('Description'), '#default_value' => $edit['description'], '#description' => t('A description of the term. To be displayed on taxonomy/term pages and RSS feeds.'), - '#text_format' => $edit['format'], + '#format' => $edit['format'], ); $form['vocabulary_machine_name'] = array( @@ -771,10 +772,6 @@ function taxonomy_form_term_submit($form return; } - // Massage #text_format. - $form_state['values']['format'] = $form_state['values']['description_format']; - unset($form_state['values']['description_format']); - $term = taxonomy_form_term_submit_builder($form, $form_state); $status = taxonomy_term_save($term);