Index: misc/tabledrag.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tabledrag.js,v retrieving revision 1.38 diff -u -r1.38 tabledrag.js --- misc/tabledrag.js 18 May 2010 06:46:45 -0000 1.38 +++ misc/tabledrag.js 29 May 2010 14:10:56 -0000 @@ -451,7 +451,7 @@ self.rowObject.markChanged(); if (self.changed == false) { - $(Drupal.theme('tableDragChangedWarning')).insertBefore(self.table).hide().fadeIn('slow'); + self.rowObject.addChangedWarning(); self.changed = true; } } @@ -1056,6 +1056,10 @@ } }; +Drupal.tableDrag.prototype.row.prototype.addChangedWarning = function () { + $(Drupal.theme('tableDragChangedWarning')).insertBefore(this.table).hide().fadeIn('slow'); +} + /** * Stub function. Allows a custom handler when a row is indented. */ Index: modules/field_ui/field_ui.js =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.js,v retrieving revision 1.3 diff -u -r1.3 field_ui.js --- modules/field_ui/field_ui.js 23 May 2010 19:10:23 -0000 1.3 +++ modules/field_ui/field_ui.js 29 May 2010 14:10:57 -0000 @@ -102,6 +102,20 @@ checkEmptyRegions(this.table, this); }; + // The AJAX behavior on formatter settings updates the whole table, so we + // make sure the 'Save' warning is displayed only once on the page. + tableDrag.row.prototype.addChangedWarning = function () { + if (!$('#field-settings-changed-warning').length) { + $(Drupal.theme('tableDragChangedWarning')).insertBefore(this.table).hide().fadeIn('slow'); + } + } + + // Custom message with an HTML id so that addChangedWarning() can avoid + // duplicates. + Drupal.theme.prototype.tableDragChangedWarning = function () { + return '
' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '
'; + }; + // Add a handler to update the formatter selector when a row is dropped in // or out of the 'Hidden' section. tableDrag.onDrop = function () { @@ -134,6 +148,8 @@ var value = 'hidden'; } $select.val(value); + // Fire AJAX update of formatter settings. + $select.change(); } $select.removeData('noUpdate'); } Index: modules/field_ui/field_ui-display-overview-table.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui-display-overview-table.tpl.php,v retrieving revision 1.2 diff -u -r1.2 field_ui-display-overview-table.tpl.php --- modules/field_ui/field_ui-display-overview-table.tpl.php 26 May 2010 07:49:52 -0000 1.2 +++ modules/field_ui/field_ui-display-overview-table.tpl.php 29 May 2010 14:10:56 -0000 @@ -16,13 +16,17 @@ */ ?> +
+ +
* 
+ - + @@ -37,6 +41,9 @@ + settings_class) :?> class="settings_class?>"> + settings_wrapper) ? $row->settings_wrapper : ' '; ?> + @@ -52,9 +59,11 @@ +
weight . $row->hidden_name; ?> label)) print $row->label; ?> type; ?>
weight . $row->hidden_name; ?> label)) print $row->label; ?> type; ?> 
+
Index: modules/field_ui/field_ui.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.admin.inc,v retrieving revision 1.51 diff -u -r1.51 field_ui.admin.inc --- modules/field_ui/field_ui.admin.inc 23 May 2010 19:10:23 -0000 1.51 +++ modules/field_ui/field_ui.admin.inc 29 May 2010 14:10:57 -0000 @@ -591,6 +591,7 @@ $table = array( '#theme' => 'field_ui_display_overview_table', '#field_rows' => array(), + '#settings_changed' => FALSE, '#tree' => TRUE, ); @@ -632,7 +633,109 @@ '#type' => 'select', '#options' => $formatter_options, '#default_value' => $display['type'], + '#ajax' => array( + 'callback' => 'field_ui_formatter_settings_js', + 'wrapper' => 'field-display-overview-wrapper', + 'effect' => 'fade', + ), + '#field_name' => $name, ); + + // Formatter settings. + + // Base button element for the various formatter settings actions. + $base_button = array( + '#submit' => array('field_ui_formatter_settings_submit'), + '#ajax' => array( + 'callback' => 'field_ui_formatter_settings_js', + 'wrapper' => 'field-display-overview-wrapper', + 'effect' => 'fade', + ), + '#field_name' => $name, + ); + + // Check the currently selected formatter, and merge persisted values for + // formatter settings. + $settings_changed = FALSE; + if (isset($form_state['values']['settings'][$name]['type'])) { + $formatter_type = $form_state['values']['settings'][$name]['type']; + } + else { + $formatter_type = $display['type']; + } + if (isset($form_state['formatter_settings'][$name])) { + $settings = $form_state['formatter_settings'][$name]; + $settings_changed = TRUE; + } + else { + $settings = $display['settings']; + if (!is_array($settings)) { + dsm($field_name); + dsm($display); + } + } + //$settings += field_info_formatter_settings($formatter_type); + + $instance['display'][$view_mode]['type'] = $formatter_type; + $formatter = field_info_formatter_types($formatter_type); + $instance['display'][$view_mode]['module'] = $formatter['module']; + $instance['display'][$view_mode]['settings'] = $settings; + + if ($settings_changed) { + $table['#settings_changed'] = TRUE; + } + + if (isset($form_state['formatter_settings_edit']) && $form_state['formatter_settings_edit'] == $name) { + // We are currently editing this field's formatter settings. Display + // the settings form, and submit buttons. + $additions = module_invoke($formatter['module'], 'field_formatter_settings_form', $field, $instance, $view_mode); + if (is_array($additions)) { + $table[$name]['settings_wrapper']['settings'] = $additions; + } + $table[$name]['#settings_class'] = 'field-formatter-settings-editing'; + $table[$name]['settings_wrapper']['actions'] = array('#type' => 'actions'); + $table[$name]['settings_wrapper']['actions']['save_settings'] = $base_button + array( + '#type' => 'submit', + '#name' => $name . '_formatter_settings_save', + '#value' => t('Save'), + '#attributes' => array('class' => array('field-formatter-settings-save')), + '#op' => 'save', + ); + $table[$name]['settings_wrapper']['actions']['cancel_settings'] = $base_button + array( + '#type' => 'submit', + '#name' => $name . '_formatter_settings_cancel', + '#value' => t('Cancel'), + '#attributes' => array('class' => array('field-formatter-settings-cancel')), + '#op' => 'cancel', + // Do not check errors for the 'Cancel' button. + '#limit_validation_errors' => array(), + ); + $table[$name]['type']['#ajax'] += array( + 'trigger_as' => array('name' => $name . '_formatter_settings_save'), + ); + } + else { + // Display a summary of the current formatter settings. + $summary = module_invoke($formatter['module'], 'field_formatter_settings_summary', $field, $instance, $view_mode); + if ($summary) { + $table[$name]['settings_wrapper']['settings_edit'] = $base_button + array( + '#type' => 'image_button', + '#name' => $name . '_formatter_settings_edit', + '#src' => 'misc/configure.png', + '#attributes' => array('class' => array('field-formatter-settings-edit'), 'alt' => t('Edit')), + '#op' => 'edit', + // Do not check errors for the 'Edit' button. + '#limit_validation_errors' => array(), + ); + $table[$name]['settings_wrapper']['summary'] = array( + '#markup' => $summary, + ); + if ($settings_changed) { + $table[$name]['#settings_class'] = 'field-formatter-settings-changed'; + $table[$name]['settings_wrapper']['summary']['#prefix'] = '* '; + } + } + } $table['#field_rows'][] = $name; // Collect default formatters for the JS script. @@ -662,6 +765,7 @@ ); $table['#field_rows'][] = $name; } + $form['settings'] = $table; // Custom display settings. @@ -703,6 +807,50 @@ return $form; } + +/** + * Form submit handler for the formatter settings buttons. + */ +function field_ui_formatter_settings_submit($form, &$form_state) { + $trigger = $form_state['triggering_element']; + $field_name = $trigger['#field_name']; + $op = $trigger['#op']; + + switch ($op) { + case 'edit': + // Store the field whose settings are currently being edited. + $form_state['formatter_settings_edit'] = $field_name; + break; + + case 'save': + // Store the saved settings. + $values = $form_state['values']['settings'][$field_name]['settings_wrapper']['settings']; + $form_state['formatter_settings'][$field_name] = $values; + // Fall-through to the 'cancel' case. + case 'cancel': + // Unset the field as being currently edited. + unset($form_state['formatter_settings_edit']); + break; + } + + $form_state['rebuild'] = TRUE; +} + +/** + * Ajax handler for the formatter settings buttons. + */ +function field_ui_formatter_settings_js($form, &$form_state) { + $trigger = $form_state['triggering_element']; + $field_name = $trigger['#field_name']; + + $element = &$form['settings'][$field_name]['settings_wrapper']; + // Add a DIV to receive the AJAX effect. + $element['#prefix'] = '
' . (isset($element['#prefix']) ? $element['#prefix'] : ''); + $element['#suffix'] = (isset($element['#suffix']) ? $element['#suffix'] : '') . '
'; + + return $form['settings']; +} + /** * Theme preprocess function for field_ui-display-overview-table.tpl.php. */ @@ -731,15 +879,9 @@ $row = new stdClass(); foreach (element_children($element) as $child) { - if (array_key_exists('label', $element[$child])) { - $row->{$child} = new stdClass(); - $row->{$child}->label = drupal_render($element[$child]['label']); - $row->{$child}->type = drupal_render($element[$child]['type']); - } - else { - $row->{$child} = drupal_render($element[$child]); - } + $row->{$child} = drupal_render($element[$child]); } + $row->settings_class = (!empty($element['#settings_class']) ? $element['#settings_class'] : ''); $row->class = 'draggable'; $row->label_class = 'label-field'; $rows[$visibility][] = $row; @@ -747,6 +889,7 @@ } $vars['rows'] = $rows; + $vars['changed_warning'] = $elements['#settings_changed']; } /** @@ -761,7 +904,25 @@ // Save data for 'regular' fields. foreach ($form['#fields'] as $field_name) { $instance = field_info_instance($entity_type, $field_name, $bundle); - $instance['display'][$view_mode] = $form_values['settings'][$field_name]; + $values = $form_values['settings'][$field_name]; + // Get formatter settings. They lie either directly in submitted form + // values (if the whole form was submitted while some formatter + // settings were being edited), or have been persisted in + // $form_state. + $settings = $instance['display'][$view_mode]['settings']; + if (isset($values['settings_wrapper']['settings'])) { + $settings = $values['settings_wrapper']['settings']; + } + elseif (isset($form_state['formatter_settings'][$field_name])) { + $settings = $form_state['formatter_settings'][$field_name]; + } + + $instance['display'][$view_mode] = array( + 'label' => $values['label'], + 'type' => $values['type'], + 'weight' => $values['weight'], + 'settings' => $settings, + ); field_update_instance($instance); } Index: modules/field_ui/field_ui.css =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.css,v retrieving revision 1.2 diff -u -r1.2 field_ui.css --- modules/field_ui/field_ui.css 23 May 2010 19:10:23 -0000 1.2 +++ modules/field_ui/field_ui.css 29 May 2010 14:10:57 -0000 @@ -23,3 +23,27 @@ .field-display-overview tr.region-populated { display: none; } +.field-display-overview .field-formatter-settings-edit { + background: none; + border: 0; + margin: 0; + padding: 0; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; + vertical-align: -3px; +} +.field-display-overview .field-formatter-settings-save, +.field-display-overview .field-formatter-settings-cancel { + margin: 10px 0; +} +.field-display-overview td.field-formatter-settings-changed { + background: #FFFFBB; +} +.field-display-overview td.field-formatter-settings-editing { + background: #CCFFCC; +} +.field-display-overview tr.drag td.field-formatter-settings-changed, +.field-display-overview tr.drag td.field-formatter-settings-editing { + background: #FFEE77; +} \ No newline at end of file Index: modules/field_ui/field_ui.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.api.php,v retrieving revision 1.6 diff -u -r1.6 field_ui.api.php --- modules/field_ui/field_ui.api.php 4 May 2010 16:11:08 -0000 1.6 +++ modules/field_ui/field_ui.api.php 29 May 2010 14:10:57 -0000 @@ -131,6 +131,63 @@ return $form; } + +/** + * Returns a form for a formatter's settings. + * + * @param $field + * The field structure being configured. + * @param $instance + * The instance structure being configured. + * + * @return + * The form definition for the formatter settings. + */ +function hook_field_formatter_settings_form($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $form = array(); + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $form['trim_length'] = array( + '#title' => t('Length'), + '#type' => 'textfield', + '#size' => 20, + '#default_value' => $settings['trim_length'], + '#element_validate' => array('_element_validate_integer_positive'), + '#required' => TRUE, + ); + } + + return $form; + +} + +/** + * Returns a short summary for the current formatter settings of an instance. + * + * @param $field + * The field structure being configured. + * @param $instance + * The instance structure being configured. + * + * @return + * A string containing a short summary of the formatter settings. + */ +function hook_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = ''; + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $summary = t('Length: @chars chars', array('@chars' => $settings['trim_length'])); + } + + return $summary; +} + /** * Provide information on view mode tabs for an entity type. * Index: modules/field/modules/text/text.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v retrieving revision 1.54 diff -u -r1.54 text.module --- modules/field/modules/text/text.module 6 May 2010 05:59:31 -0000 1.54 +++ modules/field/modules/text/text.module 29 May 2010 14:10:56 -0000 @@ -249,6 +249,7 @@ 'text_trimmed' => array( 'label' => t('Trimmed'), 'field types' => array('text', 'text_long', 'text_with_summary'), + 'settings' => array('trim_length' => 600), ), // The 'summary or trimmed' field formatter for text_with_summary @@ -258,11 +259,51 @@ 'text_summary_or_trimmed' => array( 'label' => t('Summary or trimmed'), 'field types' => array('text_with_summary'), + 'settings' => array('trim_length' => 600), ), ); } /** + * Implements hook_field_formatter_settings_form(). + */ +function text_field_formatter_settings_form($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $form = array(); + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $form['trim_length'] = array( + '#title' => t('Length'), + '#type' => 'textfield', + '#size' => 20, + '#default_value' => $settings['trim_length'], + '#element_validate' => array('_element_validate_integer_positive'), + '#required' => TRUE, + ); + } + + return $form; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function text_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = ''; + + if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') { + $summary = t('Length: @length characters', array('@length' => $settings['trim_length'])); + } + + return $summary; +} + +/** * Implements hook_field_formatter_view(). */ function text_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { Index: modules/image/image.field.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/image/image.field.inc,v retrieving revision 1.21 diff -u -r1.21 image.field.inc --- modules/image/image.field.inc 30 Apr 2010 12:53:47 -0000 1.21 +++ modules/image/image.field.inc 29 May 2010 14:10:57 -0000 @@ -428,14 +428,17 @@ 'image' => array( 'label' => t('Image'), 'field types' => array('image'), + 'settings' => array('foo' => 1), ), 'image_link_content' => array( 'label' => t('Image linked to content'), 'field types' => array('image'), + 'settings' => array('foo' => 1), ), 'image_link_file' => array( 'label' => t('Image linked to file'), 'field types' => array('image'), + 'settings' => array('foo' => 1), ), ); @@ -443,14 +446,17 @@ $formatters['image__' . $style['name']] = array( 'label' => t('Image "@style"', array('@style' => $style['name'])), 'field types' => array('image'), + 'settings' => array('foo' => 1), ); $formatters['image_link_content__' . $style['name']] = array( 'label' => t('Image "@style" linked to content', array('@style' => $style['name'])), 'field types' => array('image'), + 'settings' => array('foo' => 1), ); $formatters['image_link_file__' . $style['name']] = array( 'label' => t('Image "@style" linked to file', array('@style' => $style['name'])), 'field types' => array('image'), + 'settings' => array('foo' => 1), ); } @@ -458,6 +464,39 @@ } /** + * Implements hook_field_formatter_settings_form(). + */ +function image_field_formatter_settings_form($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $form = array(); + + $form['foo'] = array( + '#title' => t('foo'), + '#type' => 'textfield', + '#size' => 20, + '#default_value' => $settings['foo'], + '#element_validate' => array('_element_validate_integer_positive'), + '#required' => TRUE, + ); + + return $form; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function image_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = t('Foo: @foo', array('@foo' => $settings['foo'])); + + return $summary; +} + +/** * Implements hook_field_formatter_view(). */ function image_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {