Hi,

In a custom module's hook_form_alter() implementation, adding an '#attributes' array or using the '#disabled' property doesn't work for cck fields.

Example:


function mymodule_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'my_cck_type_node_form') {
   // this works
    $form['title']['#disabled'] = true; 
    $form['title']['#attributes']['class'] = 'my_class';

    // this doesn't
    $form['my_cck_field']['#disabled'] = true; 
    $form['my_cck_field']['#attributes']['class'] = 'my_class'; 
  }

}

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Category: bug » support
Status: Active » Fixed

This has become a little more complex in CCK D6 : it's only once the form element has been 'processed' that it expands into its actual FAPI basic element (textfield, select, etc).
So what you need to do in your form_alter is to add a #process' function (making sure that it comes after the one that already exists, and does not override it.
Then, in this process function, you can set your '#disabled' or '#attributes'

function mymodule_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'my_cck_type_node_form') {
   // this works
    $form['title']['#disabled'] = true;
    $form['title']['#attributes']['class'] = 'my_class';

    $process = isset($form['my_cck_field']['#process]) ? $form['my_cck_field']['#process] : array();
    $form['my_cck_field']['#process] = array_merge($process, array('my_process_function');
  }
}

function my_process_function($element, $edit, $form_state, $form) {
  $field_key  = $element['#columns'][0];
  $element[$field_key]['#disabled'] = TRUE;
}

This should work for all/most widgets and field types that ship with CCK, but might need to be adapted a little for other field types or widgets.
Do a dsm($element) in my_process_function() to see how the element is structured and where you need to alter stuff.
(dsm() is defined by devel.module)

KarenS’s picture

I think #after_build would be safer -- it works the same as #process but you are guaranteed to have the complete processed form. If you use #process, you may get there before your field has been processed and added to the form, depending on the weight of your module compared to the weight of CCK and the module that is processing the field. With #after_build you don't have that problem.

Also note that form elements may not be where you expect -- it depends on whether you are using fieldgroup (or multigroup, once it's working) or other modules that alter the form. I would assume that all other modules will be done manipulating the form by the time you get to #after_build though.

danielb’s picture

yched's code does not work for me, I've tried modifying it to match the values I see in the arrays... all it succeeds in doing is making the cck field vanish completely.
My module's weight is 99 I shouldn't even need to do this - but the regular form alter doesn't work - even though I see the fapi arrays in there.

joachim’s picture

With noderef (on D5, admittedly) I can lock it, but then noderef's validation complains.

owahab’s picture

Status: Fixed » Active

Same here, the code yched mentioned doesn't work in 100% of the cases.
Even with #process replaced with #after_build, I still can only make the field vanish.
Also the my_process_function() does never get executed.

Please help.

owahab’s picture

What worked for me is the normal FAPI #after_build implementation for the whole form:

function mymodule_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'my_cck_type_node_form') {
    $form['my_cck_type_node_form']['#after_build'][] = 'my_process_function';
  }
}

function my_process_function($form) {
  // Uncomment this to figure out how the form will look like
  // drupal_set_message(dprint_r($form, TRUE));
  $form['my_cck_field']['#disabled'] = TRUE;
}
fairwind@drupal.ru’s picture

(sorry for my english)
It does not work for me, both for form#after_build & field#after_build.
What i do:

function my_form_alter (&$form, $form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $form['#after_build'][] = 'my_form_process';
  }
}

function my_form_process($form, $form_element) {
  $form['field_myfiled'][0]['#disabled'] = TRUE;
  $form['field_myfiled'][0]['value']['#disabled'] = TRUE;
  $form['field_myfiled']['#disabled'] = TRUE;
  drupal_set_message(dprint_r($form['field_myfiled'],true));
  return $form;
}

As you can see i trying all places that can affect #disabled property, but without success. In "drupal_set_message" output i can see '#disabled', but not in form.

$form['title']['#disabled'] = TRUE; in #after_build handler don't work too, but works in _form_alter.

Weight of my module set to 100.
Where is my mistake? Help please!

markus_petrux’s picture

Try this one:

function my_form_alter(&$form, $form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $form['#after_build'][] = 'my_form_process';
  }
}
function my_form_process($form, &$form_state) {
  my_fix_disabled($form['field_myfiled']);
  return $form;
}
function my_fix_disabled(&$elements) {
  foreach (element_children($elements) as $key) {
    if (isset($elements[$key]) && $elements[$key]) {
      // Recurse through all children elements.
      my_fix_disabled($elements[$key]);
    }
  }
  if (!isset($elements['#attributes'])) {
    $elements['#attributes'] = array();
  }
  $elements['#attributes']['disabled'] = 'disabled';
}

When after_build handler is invoked, the code in FAPI that deals with $elements['#disabled'] has already been processed (_form_builder_handle_input_element executes() is executed before #after_build handlers are processed, see form_builder() ), so now you need to generate the $element['#attributes']['disabled'] = 'disabled' for the corresponding theme function of the element.

fairwind@drupal.ru’s picture

Thank you wery much, markus_petrux! It's works!

markus_petrux’s picture

Sweet!

I have created a wiki page in the CCK handbook with a code snippet for this purpose.

http://drupal.org/node/357328

kenorb’s picture

subscribing

dazmcg’s picture

In response to #8:

Any tips on how to get this sort of thing to work so I can set default values - useful for when I include a button which uses one field to go off and populate other fields and bring the form back to the user with these fields populated with said values....

I'm using these lines:

....
$form['field_kg_url']['#default_value'] = "well";
...
  $form_state['rebuild'] = TRUE;
...

in the "my_form_process" function.

thanks!

kenorb’s picture

igor.ro’s picture

FileSize
772 bytes

We could not change options of cck fields on form alter. This is a problem of cck. In drupal 6 each widget is form element.
Some widgets are simple cover over default form elements. But cck does not allow to change all options of default element.
I offer to make a rule:
If your widget use default form element, you should implement all settings of this element.

Using pre_render or after_build for modifing is not correct.
Because changes we do on form alter will be cached. But on pre_render after_build will not.

Adding patch for cck option widget.
Patches for all other widgets comming soon :)

kenorb’s picture

I agree with patch #15, some attributes are hard-coded and that's why it doesn't work.
See this: http://drupal.org/node/470260#comment-1618594

kenorb’s picture

czeky’s picture

subscribing

markus_petrux’s picture

Status: Active » Fixed

Please, see: Code snippet: How to set the disabled attribute of a CCK field.

The same method can be used to alter any other attribute of the elements.

Here's another example using a similar technique: Code snippet: How to change the label of the "Add more items" button.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

csc4’s picture

Not sure why this is closed - is the proposed patch not necessary?

asmdec’s picture

Status: Closed (fixed) » Needs review
danielb’s picture

Status: Needs review » Fixed

Did you not read the rest of this issue? Just use #after_build to set a callback function, and then in that function you can do whatever you need.

asmdec’s picture

Yes, I read, did you read the solution proposed in that link? I don't know if that solution have some problem, but that's working for me as I can set #disabled just like I do with any other field.

I don't like to code 2 more function just to set the field disabled... sorry if I don't understand if I'm wrong, but anyone explain why that's not possible. It's just what I ask...

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

d.olaresko’s picture

Version: 6.x-2.1 » 6.x-3.x-dev
Component: General » optionwidgets.module
Category: support » bug
Status: Closed (fixed) » Active
FileSize
3.55 KB

There is small patch for optionwidgets module that will add all additional element properties to select element.

kenorb’s picture

Issue summary: View changes
Status: Active » Fixed

If extra patch is still needed, create a new issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.