See #417122: Allow drupal_alter() on Field and Widget Settings

One of the hook added was hook_widget_settings_alter(). What is it in D7?

It would be nice if the change is documented in http://drupal.org/node/224333

Comments

Mark Trapp’s picture

Status: Active » Fixed

I believe you're looking for hook_field_widget_properties_alter().

mattyoung’s picture

Status: Fixed » Active

I tried that but hook_field_widget_properties_alter() didn't get call when I edit the field settings (admin/structure/types/manage/{CONTENT-TYPE}/fields/{FIELD}). In D6, I use hook_widget_settings_alter() to add form fields to the widget settings form (admin/content/node-type/{CONTENT-TYPE}/fields/{FIELD}) and have those values saved. I need the same hook for D7.

mattyoung’s picture

Ok, I clear the cache and my hook_field_widget_properties_alter() is called now. But I fail to see how to use this hook to do what I did in D6 with hook_widget_settings_alter():

function multicolumncheckboxesradios_widget_settings_alter(&$v, $op, $widget) {
  if (_widget_is_my_type($widget)) {
    switch ($op) {
      case 'form':
        $v['multicolumncheckboxesradios'] = array(
          '#type' => 'fieldset',
          '#title' => t('Multi-column checkbox/radio buttons widget display settings'),
          '#description' => t('Adjust settings to display checkbox/radio buttons widget in columns.'),
          '#collapsible' => TRUE,
          '#collapsed' => FALSE,
          '#weight' => 2,
        );
        $v['multicolumncheckboxesradios']['mccr_width'] = array(
          '#type' => 'select',
          '#title' => t('Columns'),
          '#options' => array(0 => t('Multi-column off'), 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9'),
          '#default_value' => isset($widget['mccr_width']) ? $widget['mccr_width'] : 0,
          '#description' => t('The number of columns desired.'),
        );
        $v['multicolumncheckboxesradios']['mccr_row-major'] = array(
          '#type' => 'checkbox',
          '#title' => t('Row-major'),
          '#default_value' => isset($widget['mccr_row-major']) ? $widget['mccr_row-major'] : 0,
          '#description' => t('Select this to display check boxes/radios button across the screen first, then down.'),
        );
        $v['multicolumncheckboxesradios']['mccr_indent'] = array(
          '#type' => 'select',
          '#title' => t('Indent'),
          '#options' => array(0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8'),
          '#default_value' => isset($widget['mccr_indent']) ? $widget['mccr_indent'] : 0,
          '#description' => t('The number of indentation desired.'),
        );
        $v['multicolumncheckboxesradios']['mccr_caption'] = array(
          '#type' => 'textfield',
          '#title' => t('Caption'),
          '#default_value' => isset($widget['mccr_caption']) ? $widget['mccr_caption'] : '',
          '#description' => t('Table caption for the multicolumn checkboxes/radios.'),
        );
        $v['multicolumncheckboxesradios']['mccr_column-heading'] = array(
          '#type' => 'textarea',
          '#title' => t('Column heading'),
          '#default_value' => isset($widget['mccr_column-heading']) ? $widget['mccr_column-heading'] : '',
          '#description' => t('The column heading each on a separate line.'),
        );
        $v['multicolumncheckboxesradios']['mccr_row-heading'] = array(
          '#type' => 'textarea',
          '#title' => t('Row heading'),
          '#default_value' => isset($widget['mccr_row-heading']) ? $widget['mccr_row-heading'] : '',
          '#description' => t('The row heading each on a separate line.'),
        );
        break;
      case 'save':
        $v = array_merge($v, array('mccr_width', 'mccr_row-major', 'mccr_indent', 'mccr_caption', 'mccr_column-heading', 'mccr_row-heading'));
        break;
    }
  }
}

How to I change this into D7?

mlncn’s picture

If there is no equivalent to this hook, it looks like it could be reproduced with form_alter(). Where were the settings saved to?

anrikun’s picture

Title: What is "hook_widget_settings_alter()" in D7? » What has become "hook_widget_settings_alter()" in D7?
anrikun’s picture

Title: What has become "hook_widget_settings_alter()" in D7? » What has become of "hook_widget_settings_alter()" in D7?
anrikun’s picture

Category: support » task
Issue tags: +Needs documentation
anrikun’s picture

Category: task » bug
Priority: Normal » Major

This issue really needs some documentation please.
Modules that relied on this hook can not be ported to D7.

Berdir’s picture

I don't think there is a specific hook for this, I worked around this by implementing hook_form_alter().

See http://api.worldempire.ch/api/properties/properties.module/function/prop... for an example.

Alan D.’s picture

Category: bug » task

There is a subpage for this info: http://drupal.org/node/728792 - Updating CCK Field Modules from D6 to D7.

anrikun’s picture

Thank you for the link Alan, but there is no info about hook_widget_settings_alter() yet.

bryancasler’s picture

subscribe

jhodgdon’s picture

a) Should this be discussed in the core issue queue? I don't think Drupal Core is necessarily responsible for migrating CCK to core Fields. I don't necessarily agree with that philosophy, but that's what I've been told in the past. This should either be a feature request (add a widget settings alter hook, in which case it is probably too late for D7 to add to the API), or it's a contrib doc task.

b) Fixing the tags, since this is regarding update documentation and we have a special tag for that.

mattyoung’s picture

#4:

> Where were the settings saved to?

The settings are saved in the CCK field. There is a $op="save" in hook_widget_settings_alter(&$v, $op, $widget) to indicate additional what settings to save. These settings are available in the '#field_info' key in the node edit form when that form is rendered.

> it looks like it could be reproduced with form_alter().

The form can be changed, but do the field widget save them? This is very important.

anrikun’s picture

Using form_alter() is nothing but a workaround and definitely not a viable solution.
It's stated everywhere that the Field module is CCK moved to Core.
If the Field module doesn't have the features that CCK provided, then it looks like a major regression to me.

Alan D.’s picture

Regression maybe, but it should still be possible to do this yourself. Try an #afterbuild & custom submit handler. This is the likely solution imagefield extended will take with changes from imagefield => image module.

marcingy’s picture

Fields is not CCK moved to core, it is cck like functionality added to core therefore this is not a regression.

jhodgdon’s picture

Title: What has become of "hook_widget_settings_alter()" in D7? » Document how to upgrade CCK modules using hook_widget_settings_alter
Priority: Major » Normal

Changing title and priority, since this appears to be an update doc issue.

heshanlk’s picture

sub

fax8’s picture

I'm affected by this lack of documentation. This is critical for porting ad_gallery to drupal 7.

jhodgdon’s picture

Title: Document how to upgrade CCK modules using hook_widget_settings_alter » Document how to upgrade CCK field modules that used hook_widget_settings_alter
Project: Drupal core » Documentation
Version: 7.x-dev »
Component: field system » Missing documentation
Category: task » bug
Issue tags: -Needs documentation updates +developer

This should be in the Documentation project, since that is where the CCK -> Fields update doc is being worked on.

heshanlk’s picture

This could be done with form alter. But have to add custom #submit handlers and properly identify the form elements, at least if we have #process callback for the widget element it would be easy to extend the widget form rather than using form_alter()

gagarine’s picture

pcambra’s picture

Suscribe

Alan D.’s picture

Here is a partial example :)

This shows how to do the form alters to hit both the field AND instance settings. Make sure you have a good look at the form element names, these may be different to what you expect. [Use the devel modules' dpm() or PHP var_dump() or print_r()]

I didn't have to save anything, rather I had to theme multiple fields into a nicer table display. I'm fairly confident that you could simply add an additional submit handler and save the values somewhere.

/**
 * Provides a hook into the theming of the field, instance and display settings,
 * using #pre_render callbacks.
 */
function address_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  $field = $form['#field'];

  // Very important otherwise you will alter all fields!!!!!
  if ($field['module'] != 'address') {
    return;
  }

  // Moves the instance settings into a nicer table.
  if (isset($form['instance']['settings'])) {
    if (!isset($form['instance']['#pre_render'])) {
      $form['instance']['#pre_render'] = array();
    }
    $form['instance']['#pre_render'][] = 'address_field_instance_settings_pre_render';
  }

  // Moves the field settings into a nicer table.
  if (isset($form['field']['settings'])) {
    if (!isset($form['field']['settings']['#pre_render'])) {
      $form['field']['settings']['#pre_render'] = array();
    }
    $form['field']['settings']['#pre_render'][] = 'address_field_settings_pre_render';
  }
}

function address_form_field_ui_field_settings_form_alter(&$form) {
  if ($form['field']['module']['#value'] != 'address') {
    return;
  }
  if (isset($form['field']['settings'])) {
    if (!isset($form['field']['settings']['#pre_render'])) {
      $form['field']['settings']['#pre_render'] = array();
    }
    $form['field']['settings']['#pre_render'][] = 'address_field_settings_pre_render';
  }
}

/**
 * A #pre_render function callback for formatting field instance settings.
 */
function address_field_instance_settings_pre_render($form) {
  module_load_include('inc', 'address', 'address.admin');
  return _address_field_instance_settings_pre_render($form);
}

If saving to the variables table, try something like:

$settings = variable_get('my_module_' . $field_name, array());

$form['my_module_' . $field_name] = array(
  '#tree' => TRUE, // This may not be needed, have a play!
);
... other bits
$form['my_module_' . $field_name]['width'] = array(
  '#default_value' => isset($settings['width']) ? $settings['width'] : '',
);

.... on submit
variable_set('my_module_' . $field_name, $form_state['values'][...]['my_module_' . $field_name]);

This stores all of the sub tree in a single variable, so much cleaner than having 100's of different ones floating around.

If someone completes a full working example, post it back and it may be added to the docs.

For those interested, the name field module has a full working themed example of the above code. It shows how you can theme up the forms nicely. But beware, there were about 50 fields themed up into two compact tables so it is not the easiest to follow. [btw, there were issues using the #theme property about a year ago, but I can not remember the details and these may be resolved now.]

Alan D.’s picture

To save the values appears a bit more complex. The #submit handlers that I added to the settings form did not kick in when I needed to do this for the name field module. So to save the values, I actually had to use the series of hook_field_ACTION_instance() to save these.

/**
 * Implements hook_field_create_instance().
 */
function name_field_create_instance($instance) {
  // I'm only targeting user entities here.
  if($instance['entity_type'] == 'user' && !empty($instance['settings']['name_user_preferred'])) {
    variable_set('name_user_preferred', $instance['settings']['name_user_preferred']);
  }
}

/**
 * Implements hook_field_update_instance().
 */
function name_field_update_instance($instance, $prior_instance) {
  if ($instance['entity_type'] == 'user') {
    if (!empty($instance['settings']['name_user_preferred'])) {
      variable_set('name_user_preferred', $instance['settings']['name_user_preferred']);
    }
    elseif (!empty($prior_instance['settings']['name_user_preferred'])) {
      variable_set('name_user_preferred', '');
    }
  }
}

/**
 * Implements hook_field_delete_instance().
 */
function name_field_delete_instance($instance) {
  if($instance['entity_type'] == 'user' && !empty($instance['settings']['name_user_preferred'])) {
    variable_set('name_user_preferred', '');
  }
}

Hope someone finds this helpful :)

anrikun’s picture

Priority: Normal » Major

There is no progress so far on this issue.

This is a real blocker for these modules:
http://drupal.org/project/imagefield_focus
http://drupal.org/project/ad_gallery
http://drupal.org/project/node_repeat

My question is: is it really a documentation problem?

Alan D.’s picture

Well, the above solution (#26) works for me, so albeit not great, there is a work-around. I've used this pattern 3 times now, and no issues to report yet.

Damien Tournoud’s picture

The only thing to document is how to use:

* hook_field_info_alter() or hook_field_widget_info_alter() to define a default value for a setting (field-level or widget-level),
* and hook_form_field_ui_field_edit_form_alter() to add the form for the setting.

Everything else (including saving the data) is handled for you by the Field API.

anrikun’s picture

For reference, I'm adding a quote from #672526: hook_widget_settings_alter() is missing in D7 (and D8) and several D6 modules cannot be ported to D7 because of this.:

yshed wrote:

Current way :
- use hook_field_widget_info_alter() to expose your additional settings
- use hook_form_field_ui_field_edit_form_alter() to add the form elements to edit the settings in the Field UI screen. If placed properly, the submitted values get saved automatically in the $instance['widget']['settings'] definition.
- use hook_field_attach_form() to alter the actual widgets in entity edit forms. You have to navigate through the $form to the location of the widgets you want to affect (in $form[$field_name][$langcode], the rest depends on the shape of the widget you want to alter), but the D6 versions probably had to do something similar. That part will get easier if/when #1204230: Missing hook_field_widget_form_alter() lands.

I hope that #1204230: Missing hook_field_widget_form_alter() gets committed to D8 and backported to D7 quickly.

goron’s picture

Does anyone have an example they could show of doing this in the way suggested (by yched) above? It would really help to see some code for this.

Thanks.

anrikun’s picture

Status: Active » Closed (fixed)

FYI #1204230: Missing hook_field_widget_form_alter() has been committed!
I guess this can be closed then.

Alan D.’s picture

Status: Closed (fixed) » Active

I think that people are wanting a way to also save this data. I'm using the way outlined above using the 3 hook_field_ACTION_instance() hooks and it works, but quoting Damien Tournoud from #29

The only thing to document is how to use:

* hook_field_info_alter() or hook_field_widget_info_alter() to define a default value for a setting (field-level or widget-level),
* and hook_form_field_ui_field_edit_form_alter() to add the form for the setting.

Everything else (including saving the data) is handled for you by the Field API.

So I tried adding a test value into the settings alter:

function name_form_field_ui_field_settings_form_alter(&$form) {
  if (isset($form['field']['settings'])) {
    $form['field']['settings']['test'] = array(
      '#type' => 'value',
      '#value' => 10,
    );
  }
}

And this value is nowhere to be seen in the field data array after saving, not the field, instance nor widget settings. So is this either a bug that needs to be resolved (either internally in this module or in core Drupal) or this magic from CCK has not been included in core.