In an earlier version I showed you how to make CCK fields read-only using Drupal 6. CCK was merged into Drupal 7 as the fields API, so a new approach is required. This new post will show an easy and safe technique to make Drupal 7 fields read-only.
Generally, using form alter on any form field and setting #disabled => TRUE
works just fine. However, form alter is too early of a hook to use on entity fields.
Moreover, if you disable a field, the browser won't even post the value. This
can result in annoying validation issues.
The correct and safe way to do lock changes to a widget is to mark its HTML
elements as read only, not as disabled. To do this, we use the
readonly
attribute. This ensures that the browser posts the value, unlike
disabled. But we
can't just stop here. We need to make sure the value isn't changed by a crafty
user modifying his or her post request. Just imagine if they user the Chrome
Inspector and altered the HTML attributes directly: they'd be able to modify
the field. We need to prevent this.
In case you don't already have a field you want to lock down, let's create one really quickly.
- Browse to
admin/structure/types. - Find an existing content type, and click on manage fields.
- Add a field and give it the label Locked. The machine name should appear automatically as field_locked. Be sure to choose a text field type, using a textfield widget.
Now, let's hook in to the node form so we can add our #after_build callback.
Keep in mind that in this example, we're adding our callback to all node
forms. You might want to further limit this to a specific content type. You
might also want to check if this is an existing node, as locking the field on
node creation might not be necessary.
<?php
/**
* Implements hook_form_alter().
*/
function example_form_alter(&$form, &$form_state, $form_id) {
if (isset($form['#node']) && $form_id == $form['#node']->type .'_node_form') {
$form['#after_build'][] = 'example_after_build';
}
}
Let's keep things simple and modify a text widget. For simple widgets, the
fields API generally uses the key value.
<?php
function example_after_build($form, &$form_state) {
$field = 'field_locked';
$form[$field]['und'][0]['value']['#attributes']['readonly'] = 'readonly';
$form_state['values'][$field]['und'][0]['value'] = $form[$field]['und'][0]['value']['#default_value'];
return $form;
}
And that's it! You can now count on your field being read-only. It will still be posted by the browser. And even if a user tries to post a changed value - or uses the browser inspector to edit the contents of the fields - our build function restores the original value. Fast, safe, and secure.
Below is the complete code for reference.
<?php
/**
* Implements hook_form_alter().
*/
function example_form_alter(&$form, &$form_state, $form_id) {
if (isset($form['#node']) && $form_id == $form['#node']->type .'_node_form') {
$form['#after_build'][] = 'example_after_build';
}
}
function example_after_build($form, &$form_state) {
$field = 'field_locked';
$form[$field]['und'][0]['value']['#attributes']['readonly'] = 'readonly';
$form_state['values'][$field]['und'][0]['value'] = $form[$field]['und'][0]['value']['#default_value'];
return $form;
}
Nice, I was looking for a way to do this the other day. Thanks