TRUE, // Use theme('textfield') to format this element on output. '#theme' => array('textfield'), // Do not provide autocomplete. '#autocomplete_path' => FALSE, // Allow theme('form_element') to control the markup surrounding this // value on output. '#theme_wrappers' => array('form_element'), ); // form_example_checkbox is mostly a copy of the system-defined checkbox // element. $types['form_example_checkbox'] = array( // This is an HTML . '#input' => TRUE, // @todo: Explain #return_value. '#return_value' => TRUE, // Our #process array will use the standard process functions used for a // regular checkbox. '#process' => array('form_process_checkbox', 'ajax_process_form'), // Use theme('form_example_checkbox') to render this element on output. '#theme' => 'form_example_checkbox', // Use theme('form_element') to provide HTML wrappers for this element. '#theme_wrappers' => array('form_element'), // Place the title after the element (to the right of the checkbox). // This attribute affects the behavior of theme_form_element(). '#title_display' => 'after', // We use the default function name for the value callback, so it does not // have to be listed explicitly. The pattern for the default function name // is form_type_TYPENAME_value(). // '#value_callback' => 'form_type_form_example_checkbox_value', ); // This discrete phonenumber element keeps its values as the separate elements // area code, prefix, extension. $types['form_example_phonenumber_discrete'] = array( // #input == TRUE means that the form value here will be used to determine // what #value will be. '#input' => TRUE, // #process is an array of callback functions executed when this element is // processed. Here it provides the child form elements which define // areacode, prefix, and extension. '#process' => array('form_example_phonenumber_discrete_process'), // Validation handlers for this element. These are in addition to any // validation handlers that might. '#element_validate' => array('form_example_phonenumber_discrete_validate'), '#autocomplete_path' => FALSE, '#theme_wrappers' => array('form_example_inline_form_element'), ); // Define form_example_phonenumber_combined, which combines the phone // number into a single validated text string. $types['form_example_phonenumber_combined'] = array( '#input' => TRUE , '#process' => array('form_example_phonenumber_combined_process'), '#element_validate' => array('form_example_phonenumber_combined_validate'), '#autocomplete_path' => FALSE, '#value_callback' => 'form_example_phonenumber_combined_value', '#default_value' => array( 'areacode' => '', 'prefix' => '', 'extension' => '', ), '#theme_wrappers' => array('form_example_inline_form_element'), ); return $types; } /** * Value callback for form_example_phonenumber_combined. * * Builds the current combined value of the phone number only when the form * builder is not processing the input. * * @param array $element * Form element. * @param array $input * Input. * @param array $form_state * Form state. * * @return array * The modified element. */ function form_example_phonenumber_combined_value(&$element, $input = FALSE, $form_state = NULL) { if (!$form_state['process_input']) { $matches = array(); $match = preg_match('/^(\d{3})(\d{3})(\d{4})$/', $element['#default_value'], $matches); if ($match) { // Get rid of the "all match" element. array_shift($matches); list($element['areacode'], $element['prefix'], $element['extension']) = $matches; } } return $element; } /** * Value callback for form_example_checkbox element type. * * Copied from form_type_checkbox_value(). * * @param array $element * The form element whose value is being populated. * @param mixed $input * The incoming input to populate the form element. If this is FALSE, meaning * there is no input, the element's default value should be returned. * * @return int * The value represented by the form element. */ function form_type_form_example_checkbox_value($element, $input = FALSE) { if ($input === FALSE) { return isset($element['#default_value']) ? $element['#default_value'] : 0; } else { return isset($input) ? $element['#return_value'] : 0; } } /** * Process callback for the discrete version of phonenumber. */ function form_example_phonenumber_discrete_process($element, &$form_state, $complete_form) { // #tree = TRUE means that the values in $form_state['values'] will be stored // hierarchically. In this case, the parts of the element will appear in // $form_state['values'] as // $form_state['values']['']['areacode'], // $form_state['values']['']['prefix'], // etc. This technique is preferred when an element has member form // elements. $element['#tree'] = TRUE; // Normal FAPI field definitions, except that #value is defined. $element['areacode'] = array( '#type' => 'textfield', '#size' => 3, '#maxlength' => 3, '#value' => $element['#value']['areacode'], '#required' => TRUE, '#prefix' => '(', '#suffix' => ')', ); $element['prefix'] = array( '#type' => 'textfield', '#size' => 3, '#maxlength' => 3, '#required' => TRUE, '#value' => $element['#value']['prefix'], ); $element['extension'] = array( '#type' => 'textfield', '#size' => 4, '#maxlength' => 4, '#value' => $element['#value']['extension'], ); return $element; } /** * Validation handler for the discrete version of the phone number. * * Uses regular expressions to check that: * - the area code is a three digit number. * - the prefix is numeric 3-digit number. * - the extension is a numeric 4-digit number. * * Any problems are shown on the form element using form_error(). */ function form_example_phonenumber_discrete_validate($element, &$form_state) { if (isset($element['#value']['areacode'])) { if (0 == preg_match('/^\d{3}$/', $element['#value']['areacode'])) { form_error($element['areacode'], t('The area code is invalid.')); } } if (isset($element['#value']['prefix'])) { if (0 == preg_match('/^\d{3}$/', $element['#value']['prefix'])) { form_error($element['prefix'], t('The prefix is invalid.')); } } if (isset($element['#value']['extension'])) { if (0 == preg_match('/^\d{4}$/', $element['#value']['extension'])) { form_error($element['extension'], t('The extension is invalid.')); } } return $element; } /** * Process callback for the combined version of the phonenumber element. */ function form_example_phonenumber_combined_process($element, &$form_state, $complete_form) { // #tree = TRUE means that the values in $form_state['values'] will be stored // hierarchically. In this case, the parts of the element will appear in // $form_state['values'] as // $form_state['values']['']['areacode'], // $form_state['values']['']['prefix'], // etc. This technique is preferred when an element has member form // elements. $element['#tree'] = TRUE; // Normal FAPI field definitions, except that #value is defined. $element['areacode'] = array( '#type' => 'textfield', '#size' => 3, '#maxlength' => 3, '#required' => TRUE, '#prefix' => '(', '#suffix' => ')', ); $element['prefix'] = array( '#type' => 'textfield', '#size' => 3, '#maxlength' => 3, '#required' => TRUE, ); $element['extension'] = array( '#type' => 'textfield', '#size' => 4, '#maxlength' => 4, '#required' => TRUE, ); $matches = array(); $match = preg_match('/^(\d{3})(\d{3})(\d{4})$/', $element['#default_value'], $matches); if ($match) { // Get rid of the "all match" element. array_shift($matches); list($element['areacode']['#default_value'], $element['prefix']['#default_value'], $element['extension']['#default_value']) = $matches; } return $element; } /** * Phone number validation function for the combined phonenumber. * * Uses regular expressions to check that: * - the area code is a three digit number * - the prefix is numeric 3-digit number * - the extension is a numeric 4-digit number * * Any problems are shown on the form element using form_error(). * * The combined value is then updated in the element. */ function form_example_phonenumber_combined_validate($element, &$form_state) { $lengths = array( 'areacode' => 3, 'prefix' => 3, 'extension' => 4, ); foreach ($lengths as $member => $length) { $regex = '/^\d{' . $length . '}$/'; if (!empty($element['#value'][$member]) && 0 == preg_match($regex, $element['#value'][$member])) { form_error($element[$member], t('@member is invalid', array('@member' => $member))); } } // Consolidate into the three parts into one combined value. $value = $element['areacode']['#value'] . $element['prefix']['#value'] . $element['extension']['#value']; form_set_value($element, $value, $form_state); return $element; } /** * Called by form_example_theme() to provide hook_theme(). * * This is kept in this file so it can be with the theme functions it presents. * Otherwise it would get lonely. */ function _form_example_element_theme() { return array( 'form_example_inline_form_element' => array( 'render element' => 'element', 'file' => 'form_example_elements.inc', ), 'form_example_checkbox' => array( 'render element' => 'element', 'file' => 'form_example_elements.inc', ), ); } /** * Themes a custom checkbox. * * This doesn't actually do anything, but is here to show that theming can * be done here. */ function theme_form_example_checkbox($variables) { $element = $variables['element']; return theme('checkbox', $element); } /** * Formats child form elements as inline elements. */ function theme_form_example_inline_form_element($variables) { $element = $variables['element']; // Add element #id for #type 'item'. if (isset($element['#markup']) && !empty($element['#id'])) { $attributes['id'] = $element['#id']; } // Add element's #type and #name as class to aid with JS/CSS selectors. $attributes['class'] = array('form-item'); if (!empty($element['#type'])) { $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-'); } if (!empty($element['#name'])) { $attributes['class'][] = 'form-item-' . strtr($element['#name'], array( ' ' => '-', '_' => '-', '[' => '-', ']' => '', ) ); } // Add a class for disabled elements to facilitate cross-browser styling. if (!empty($element['#attributes']['disabled'])) { $attributes['class'][] = 'form-disabled'; } $output = '' . "\n"; // If #title is not set, we don't display any label or required marker. if (!isset($element['#title'])) { $element['#title_display'] = 'none'; } $prefix = isset($element['#field_prefix']) ? '' . $element['#field_prefix'] . ' ' : ''; $suffix = isset($element['#field_suffix']) ? ' ' . $element['#field_suffix'] . '' : ''; switch ($element['#title_display']) { case 'before': $output .= ' ' . theme('form_element_label', $variables); $output .= ' ' . '
' . $prefix . $element['#children'] . $suffix . "
\n"; break; case 'invisible': case 'after': $output .= ' ' . $prefix . $element['#children'] . $suffix; $output .= ' ' . theme('form_element_label', $variables) . "\n"; break; case 'none': case 'attribute': // Output no label and no required marker, only the children. $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n"; break; } if (!empty($element['#description'])) { $output .= '
' . $element['#description'] . "
\n"; } $output .= "\n"; return $output; } /** * Form content for examples/form_example/element_example. * * Simple form to demonstrate how to use the various new FAPI elements * we've defined. */ function form_example_element_demo_form($form, &$form_state) { $form['a_form_example_textfield'] = array( '#type' => 'form_example_textfield', '#title' => t('Form Example textfield'), '#default_value' => variable_get('form_example_textfield', ''), '#description' => t('form_example_textfield is a new type, but it is actually uses the system-provided functions of textfield'), ); $form['a_form_example_checkbox'] = array( '#type' => 'form_example_checkbox', '#title' => t('Form Example checkbox'), '#default_value' => variable_get('form_example_checkbox', FALSE), '#description' => t('Nothing more than a regular checkbox but with a theme provided by this module.'), ); $form['a_form_example_element_discrete'] = array( '#type' => 'form_example_phonenumber_discrete', '#title' => t('Discrete phone number'), '#default_value' => variable_get( 'form_example_element_discrete', array( 'areacode' => '999', 'prefix' => '999', 'extension' => '9999', ) ), '#description' => t('A phone number : areacode (XXX), prefix (XXX) and extension (XXXX). This one uses a "discrete" element type, one which stores the three parts of the telephone number separately.'), ); $form['a_form_example_element_combined'] = array( '#type' => 'form_example_phonenumber_combined', '#title' => t('Combined phone number'), '#default_value' => variable_get('form_example_element_combined', '0000000000'), '#description' => t('form_example_element_combined one uses a "combined" element type, one with a single 10-digit value which is broken apart when needed.'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } /** * Submit handler for form_example_element_demo_form(). */ function form_example_element_demo_form_submit($form, &$form_state) { // Exclude unnecessary elements. unset($form_state['values']['submit'], $form_state['values']['form_id'], $form_state['values']['op'], $form_state['values']['form_token'], $form_state['values']['form_build_id']); foreach ($form_state['values'] as $key => $value) { variable_set($key, $value); drupal_set_message( t('%name has value %value', array( '%name' => $key, '%value' => print_r($value, TRUE), ) ) ); } }