First of all, I'm not complaining. CCK friggin' rocks; I count my lucky stars everyday that I have access to such great software. After over two years, this is the first somewhat-serious issue I've ran across that I haven't found a solution to.

Not sure if this is a bug or was intentional, but I'll try to explain the circumstances.

I'm using the Single on/off check box CCK text field. I'm restricting access for 'non-admin' users using hook_form_alter() (pretty straight forward, right?):

function mymodule_form_alter(&$form, $form_state, $form_id) {
  $form['field_onoffcheckbox']['#access'] = FALSE;

If the hidden (restricted) checkbox is left as 'off' and the node is submitted, no problems. Everything validates, and comes out alright. Now if an admin sets the checkbox as "on" and afterwards a non-admin tries to edit the node, the site returns an error like such:

"warning: array_key_exists() [function.array-key-exists]: The first argument should be either a string or an integer in /home/mysite/public_html/sites/all/modules/contrib/cck/modules/text/text.module on line 156."

Looking in, line 156 of text.module validates the submitted checkbox value by comparing it to the "Allowed Values" list defined in the field's CCK settings and sets a form error if not found:

if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) {
  form_set_error($error_element, t('%name: illegal value.', array('%name' => t($field['widget']['label']))));

As far as I can tell, when the node form is submitted, if the checkbox in "on" and it's '#access' is FALSE, instead of passing the "on" value (string) to $form_state, a value of TRUE gets passed instead. So when the text.module attempts to validate the checkbox value with array_key_exists(), it returns an error since a boolean value is not an acceptable '$key' parameter.

I looked over the $form array tree for my node form build and the value defined for the checkbox is correct ($form['field_onoffcheckbox']['#default_value'][0]['value']). If this field is hidden (restricted), then that value should be passed along when the node form is submitted, correct?

With some further testing, I discovered that when the form is submitted, and the checkbox is validating (in text.module), the $form_state value of the checkbox has somehow been converted to TRUE instead of the '#default_value' mentioned above ($form_state['values']['field_onoffcheckbox'][0]['value']).

So without a workaround, it basically means that the single on/off checkbox has to be visible to all users at all times, or else it's value could get converted to TRUE regardless of it's previous value.

If it is of help to anyone, here's my workaround:

1. Using hook_form_alter(), add a custom validator to your single on/off checkbox (more on field validators at http://api.drupal.org/api/drupal/developer--topics--forms_api_reference....):

$form['field_onoffcheckbox'][''#element_validate''] = mymodule_single_checkbox_validate;

2. Create the validator function. This basically looks to see if the checkbox value is set to TRUE and sets it back to it's previous value (defined by '#default_value'):

mymodule_single_checkbox_validate($element, &$form_state) {
  if ($form_state['values'][$element['#field_name']][0]['value'] === TRUE) {
    $form_state['values'][$element['#field_name']][0]['value'] = $element['#default_value'][0]['value'];

My workaround is probably full of holes, but it's just that: A workaround. It would be great to have someone who is far smarter than me take a crack at this one.


#19 716408-19.patch1.18 KBroderik
#4 716408.patch825 bytesmanarth


MattBrigade’s picture

Oops... Step #1 of my workaround contained a typo (didn't use quotations properly). It should be:

$form['field_onoffcheckbox']['#element_validate'] = mymodule_single_checkbox_validate;

- Matt

MattBrigade’s picture

Okay. Sorry to post again, but I found a much better solution to anyone else who might also be having this problem.

I'm an idiot and totally overlooked the "#value" form element property. As per usual, you can solve this quite elegantly with the Drupal API instead of my weird, custom validator solution that manipulates the $form_state variable.

In your hook_form_alter() call, all you need to do is give the on/off checkbox a '#value' and remove it's '#default_value' (as it will still return an error if included):

$form['field_onoffcheckbox']['#value'] = $form['field_onoffcheckbox']['#default_value'];

So far, this seems to be doing the trick.

legion80’s picture

Looks like this is related to: https://drupal.org/node/283341

I'm still running into this issue with the second workaround for whatever reason, although I'm using 6.x-3.x-dev (2010-Jan-25). Your original, more hacky workaround worked better, although for me I had to make sure to append it to the existing array instead of setting the element to the function name directly, so:

$form['field_onoffcheckbox']['#element_validate'][] = 'mymodule_single_checkbox_validate';
manarth’s picture

Status:Active» Needs review
new825 bytes

Here's a patch for the issue.

#default_value was being set to: TRUE or FALSE.
It should be set (like #return_value) to: $on_value or FALSE.

This appears to be work properly on a couple of my builds, but it's a widely-used module so it'd be great to get more testing around this.

robertjd’s picture

I was having the problem described by the first post in this thread, and the patch in #4 fixes the problem for me.

MilanT’s picture

Patch worked perfectly.


manarth’s picture

Status:Needs review» Reviewed & tested by the community

Guess this is now a potential candidate for commit?

arski’s picture

Version:6.x-2.6» 6.x-2.x-dev
Component:text.module» optionwidgets.module

works like a charm for me too, let's get this committed :)

yan’s picture

Code snippet: How to set the disabled attribute of a CCK field

jpl’s picture

Looks like I solved the same problem in another way, while wasting a few hours in the process. :-(
Anyway, my analysis might be useful: http://drupal.org/node/283341#comment-3813606

The patch from comment #4 fixes the issue and is prettier and closer to the point than mine posted in the other thread. However, I noticed that it has one unwanted side effect. If I save the form while the checkbox field ihas #access == FALSE and if the checkbox was unchecked to start with, then the field's value in the database will change from whatever key is used for the "unchecked" state to NULL. It doesn't matter much for the presentation layer, as a checkbox with NULL value is rendered unchecked anyway, but it may break queries that depend on the value really being equal to the "unchecked" key and/or remaining constant.

epicflux’s picture

Patch in #4 is working on benton.org. We were hiding a single on/off field using the Rules module and getting that error when trying to save the node.

soulfroys’s picture

Patch #4 works! Thanks!
(Drupal 6.20 / CCK 6.x-2.x-dev - 2010-08-26)

arski’s picture

will this ever be committed? :(

j0rd’s picture

Same problem.

This problem also exists with optionwidgets_buttons. I'm trying to find a solution. If anyone else could help it would be appreciated.

As mentioned here: #283341: Illegal value in text field; field's array is being changed Comment #10 there appears to be a problem with optionwidgets_form2data.

date module has had similar problems as well. You can see my issue on that here:
#1304344: Date values cleared, instead of saved when updating a node when $form['myfield']['#access'] = FALSE

I would assume most widget modules have this type of problem actually, since #access = FALSE isn't something that's normally tested. I would assume the real issue lies with poor implementation (aka bug) in CCK which then got ported to fields.

arski’s picture

did you try the patch in http://drupal.org/node/716408#comment-2755586 ? if it doesn't work for you (and if you have the same problem), then you should probably mark this as "need work"..

j0rd’s picture

Status:Reviewed & tested by the community» Needs work


I've seen and applied the patch in #4. While this solves the problem for optionwidgets_onoff (a single on / off checkbox), it does not solve the problem for optionwidget_buttons (radio buttons / multiple checkboxes).

I've been looking into this bug for hours today and still haven't figure out a good solution. The trouble lies in the two functions which translate between form and database data formats.

These functions are called:
optionwidgets_form2data and optionwidgets_data2form.

This is the same bug which is fixed in patch #4 for optionwidgets_onoff, but because the real problem lies in these functions not performing their jobs correctly, I believe there may be a better fix. Unfortunately I may not be wise enough to figure it out myself and we really need a developer who is familiar with CCK and custom fields / widgets.

With that said, since patch #4 does fix one problem. It may be worth applying until a more correct solution can be found.

j0rd’s picture

I've compiled a small list of issues where #access = FALSE is a problem.

I would recommend that simpletests be written for this, and (if possible) cascade down into all cck/fields contributed widgets as this is a problem for contrib modules as well. This should at the very least allow us to see where this is a problem and how it can be best resolved.

Some other issues related to #access = FALSE

Option Widget OnOff
#716408: Single on/off checkbox: hook_form_alter() with ['#access'] = FALSE changes 'On' value to TRUE

Option Widget Checkboxes
#1114480: expand_checkboxes #default_value when #access=FALSE
#765962: Cannot assign default values to checkboxes when #access is set to FALSE

#995846: Can't save decimal number with #access = FALSE

Date module
#1304344: Date values cleared, instead of saved when updating a node when $form['myfield']['#access'] = FALSE

Organic Groups
#1114402: Support for FAPI #access=FALSE


Reason this is an issue for me, is I regularly use #access = FALSE to hide fields from site administrators which are not currently important to improve UX. Here's a module I've created which hides i18n synchronized fields from a site translator using #access = FALSE, when new translations are created:

j0rd’s picture

I'm doing a lot of work related to field widgets at the moment and found this little article which may prove helpful to someone looking at resolving this issue more completely.


roderik’s picture

new1.18 KB

#4/manarth patch with one little change (and some comments).

#10 / jpl: After testing and thinking I agree that this approach is better than your patch from #283341-10: Illegal value in text field; field's array is being changed
. But thanks anyway for it, because the comments helped me wrap my head around this thing.

As you describe in there, the POSTed values are 'on_value' or 0. So that is what we should fill in the element's #default_value. Not '$on_value or FALSE', but '$on_value or 0'. This fixes the data issue you reported in #10 above.

Waiting for RTBC again... :)

@j0rd: thanks for the overview of the different issues; it gave me a head start. However, when testing this, I could not reproduce this problem for optionwidget_buttons. I'm willing to test, but could you then tell me the exact settings of a field you have a problem with? I'd prefer you do this in #283341: Illegal value in text field; field's array is being changed, because this issue title & the patch really is specific to checkboxes (i.e. the fact that only one of the states actually POSTs a value).

roderik’s picture

Status:Needs work» Needs review

meh. As I said, waiting for RTBC again.

retorque’s picture

Patch in #19 worked for me.

matkeane’s picture

Just ran in to this issue on a site still running D6 & CCK 6.x-2.9.
Some checkbox fields are hidden (using ['#access'] = false) for non-admin users. Once the checkboxes have had a value set by an admin user, non-admins get an error when trying to edit the node.
The patches in #4 and #19 both work for me, and solved a real problem.