Drupal7 tip: Alter a field widget

Posted by: 
Dominique De Cooman

We wanted to add a functionality to our file widget so it could register when to exclude a file from search results. Files are indexed and searched with apachesolr and tika. So our idea was to add a checkbox to the file widget and save its value. A hook apachesolr update index will then know if a file can be put in the index or not. We ll save the exclude as field in the file_managed table.

Create the table:

<?php
/**
 * Add exclude from search  field
 */
function glue_update_7006() {
  
db_add_field('file_managed''exclude', array('type' => 'int''not null' => TRUE'description' => 'Exclude.'));
}
?>

How to alter the widget
Normal form altering of a field widget wont work, since the render array is not fully constructed. What we need to do is use the #afterbuild functions provided by the render api. At that stage all the info needed to alter the widgets form is present. Here is how to do it:

<?php
/**
 * Implements hook_form_alter
 */
function glue_form_measure_node_form_alter(&$form, &$form_state$form_id) {
  
//Get lang
  
if (is_numeric(arg(1))) {
    
//Put in form state so the add another will able to use it since then the path is not node/[id]/edit but /system/ajax
    
$form_state['glue_measure_nid'] = arg(1);
  }
  
//Node is already in static cache so we can load it
  
$node node_load($form_state['glue_measure_nid']);   
    
  
//Add exclude search checkbox
  
$field_attachments_lang field_language('node'$node'field_attachments');  
  
$form_state['field_attachments_lang'] = $field_attachments_lang;
  
$form['field_attachments'][$field_attachments_lang]['#after_build'][] = 'glue_afterbuild_search_exclude';
  
$form['#submit'][] = 'glue_afterbuild_search_exclude_submit'
}
?>

With a hook form alter we add a custom afterbuild function and a submit function. Note that we store the field language in the form state since we ll need it in the afterbuild function.

<?php
/**
 * Afterbuild function for exclude files from search
 */
function glue_afterbuild_search_exclude($element, &$form_state) {
  foreach (
$element as $key => $item) {
    if (
is_numeric($key) && $item['#value']['fid']) {
      
$element[$key]['display_search']['#type'] = 'checkbox';
      
$element[$key]['display_search']['#checked'] = false;
      
$element[$key]['display_search']['#title'] = t('Exclude in search results');

      
$result db_select('file_managed''f')
          ->
fields('f')
          ->
condition('fid'$element[$key]['#value']['fid'], '=')
          ->
execute();
      
$file $result->fetch();

      if (
$file->exclude == 1) {
        
$element[$key]['display_search']['#value'] = 0;
        
$element[$key]['display_search']['#checked'] = true;
      }
      
      
$element[$key]['display_search']['#attributes']['class'][0] = 'field-attach-exclude-search';
      
$element[$key]['display_search']['#id'] = $element[$key]['display']['#id'] . '-exlude-search';

      
$element[$key]['display_search']['#name'] = 'field_attachments[' $element[$key]['#language'] . '][' $key '][display-exclude-search]';
    }
  }

  return 
$element;
}
?>

In the afterbuild function we can effectivly alter the render array to add our checkbox.

Finally in the submit function we store our values.

<?php
/**
 * Form submit for exclude files from search
 */
function glue_afterbuild_search_exclude_submit($form$form_state) {
  foreach (
$form_state['values']['field_attachments'][$form_state['field_attachments_lang']] as $key => $item) {    
    
$file file_load($item['fid']);
    if (
$file) {
      if (isset(
$item['display-exclude-search']) && $item['display-exclude-search'] == 1) {
        
$value 1;
      }
      else {
        
$value 0;
      }
      
db_update('file_managed')
          ->
fields(array('exclude' => $value,))
          ->
condition('fid'$file->fid'=')
          ->
execute();
    }
  }
}
?>

The result:

Add new comment