field_example.module

Same filename in other branches
  1. 3.x modules/field_example/field_example.module
  2. 4.0.x modules/field_example/field_example.module

An example field using the Field Types API.

File

field_example/field_example.module

View source
<?php


/**
 * @file
 * An example field using the Field Types API.
 */

/**
 * @defgroup field_example Example: Field Types API
 * @ingroup examples
 * @{
 * Examples using Field Types API.
 *
 * Providing a field requires:
 * - Defining a field:
 *   - hook_field_info()
 *   - hook_field_schema()
 *   - hook_field_validate()
 *   - hook_field_is_empty()
 *
 * - Defining a formatter for the field (the portion that outputs the field for
 *   display):
 *   - hook_field_formatter_info()
 *   - hook_field_formatter_view()
 *
 * - Defining a widget for the edit form:
 *   - hook_field_widget_info()
 *   - hook_field_widget_form()
 *
 * Our module defines the field in field_example_field_info(),
 * field_example_field_validate() and field_example_field_is_empty().
 * field_example_field_schema() is implemented in field_example.install.
 *
 * Our module sets up a formatter in field_example_field_formatter_info() and
 * field_example_field_formatter_view(). These are the API hooks that present
 * formatted and themed output to the user.
 *
 * And finally, our module defines the widget in
 * field_example_field_widget_info() and field_example_field_widget_form().
 * The widget is the form element used to receive input from the user
 * when the field is being populated.
 *
 * @see field_types
 * @see field
 */

/***************************************************************
 * Field Type API hooks
 ***************************************************************/

/**
 * Implements hook_field_info().
 *
 * Provides the description of the field.
 */
function field_example_field_info() {
    return array(
        // We name our field as the associative name of the array.
'field_example_rgb' => array(
            'label' => t('Example Color RGB'),
            'description' => t('Demonstrates a field composed of an RGB color.'),
            'default_widget' => 'field_example_3text',
            'default_formatter' => 'field_example_simple_text',
        ),
    );
}

/**
 * Implements hook_field_validate().
 *
 * This hook gives us a chance to validate content that's in our
 * field. We're really only interested in the $items parameter, since
 * it holds arrays representing content in the field we've defined.
 * We want to verify that the items only contain RGB hex values like
 * this: #RRGGBB. If the item validates, we do nothing. If it doesn't
 * validate, we add our own error notification to the $errors parameter.
 *
 * @see field_example_field_widget_error()
 */
function field_example_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
    foreach ($items as $delta => $item) {
        if (!empty($item['rgb'])) {
            if (!preg_match('@^#[0-9a-f]{6}$@', $item['rgb'])) {
                $errors[$field['field_name']][$langcode][$delta][] = array(
                    'error' => 'field_example_invalid',
                    'message' => t('Color must be in the HTML format #abcdef.'),
                );
            }
        }
    }
}

/**
 * Implements hook_field_is_empty().
 *
 * hook_field_is_empty() is where Drupal asks us if this field is empty.
 * Return TRUE if it does not contain data, FALSE if it does. This lets
 * the form API flag an error when required fields are empty.
 */
function field_example_field_is_empty($item, $field) {
    return empty($item['rgb']);
}

/**
 * Implements hook_field_formatter_info().
 *
 * We need to tell Drupal that we have two different types of formatters
 * for this field. One will change the text color, and the other will
 * change the background color.
 *
 * @see field_example_field_formatter_view()
 */
function field_example_field_formatter_info() {
    return array(
        // This formatter just displays the hex value in the color indicated.
'field_example_simple_text' => array(
            'label' => t('Simple text-based formatter'),
            'field types' => array(
                'field_example_rgb',
            ),
        ),
        // This formatter changes the background color of the content region.
'field_example_color_background' => array(
            'label' => t('Change the background of the output text'),
            'field types' => array(
                'field_example_rgb',
            ),
        ),
    );
}

/**
 * Implements hook_field_formatter_view().
 *
 * Two formatters are implemented.
 * - field_example_simple_text just outputs markup indicating the color that
 *   was entered and uses an inline style to set the text color to that value.
 * - field_example_color_background does the same but also changes the
 *   background color of div.region-content.
 *
 * @see field_example_field_formatter_info()
 */
function field_example_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
    $element = array();
    switch ($display['type']) {
        // This formatter simply outputs the field as text and with a color.
        case 'field_example_simple_text':
            foreach ($items as $delta => $item) {
                $element[$delta] = array(
                    // We create a render array to produce the desired markup,
                    // "<p style="color: #hexcolor">The color code ... #hexcolor</p>".
                    // See theme_html_tag().
'#type' => 'html_tag',
                    '#tag' => 'p',
                    '#attributes' => array(
                        'style' => 'color: ' . $item['rgb'],
                    ),
                    '#value' => t('The color code in this field is @code', array(
                        '@code' => $item['rgb'],
                    )),
                );
            }
            break;
        // This formatter adds css to the page changing the '.region-content' area's
        // background color. If there are many fields, the last one will win.
        case 'field_example_color_background':
            foreach ($items as $delta => $item) {
                $element[$delta] = array(
                    '#type' => 'html_tag',
                    '#tag' => 'p',
                    '#value' => t('The content area color has been changed to @code', array(
                        '@code' => $item['rgb'],
                    )),
                    '#attached' => array(
                        'css' => array(
                            array(
                                'data' => 'div.region-content { background-color:' . $item['rgb'] . ';}',
                                'type' => 'inline',
                            ),
                        ),
                    ),
                );
            }
            break;
    }
    return $element;
}

/**
 * Implements hook_field_widget_info().
 *
 * Three widgets are provided.
 * - A simple text-only widget where the user enters the '#ffffff'.
 * - A 3-textfield widget that gathers the red, green, and blue values
 *   separately.
 * - A farbtastic colorpicker widget that chooses the value graphically.
 *
 * These widget types will eventually show up in hook_field_widget_form,
 * where we will have to flesh them out.
 *
 * @see field_example_field_widget_form()
 */
function field_example_field_widget_info() {
    return array(
        'field_example_text' => array(
            'label' => t('RGB value as #ffffff'),
            'field types' => array(
                'field_example_rgb',
            ),
        ),
        'field_example_3text' => array(
            'label' => t('RGB text field'),
            'field types' => array(
                'field_example_rgb',
            ),
        ),
        'field_example_colorpicker' => array(
            'label' => t('Color Picker'),
            'field types' => array(
                'field_example_rgb',
            ),
        ),
    );
}

/**
 * Implements hook_field_widget_form().
 *
 * hook_widget_form() is where Drupal tells us to create form elements for
 * our field's widget.
 *
 * We provide one of three different forms, depending on the widget type of
 * the Form API item provided.
 *
 * The 'field_example_colorpicker' and 'field_example_text' are essentially
 * the same, but field_example_colorpicker adds a javascript colorpicker
 * helper.
 *
 * field_example_3text displays three text fields, one each for red, green,
 * and blue. However, the field type defines a single text column,
 * rgb, which needs an HTML color spec. Define an element validate
 * handler that converts our r, g, and b fields into a simulated single
 * 'rgb' form element.
 */
function field_example_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
    $value = isset($items[$delta]['rgb']) ? $items[$delta]['rgb'] : '';
    $widget = $element;
    $widget['#delta'] = $delta;
    switch ($instance['widget']['type']) {
        case 'field_example_colorpicker':
            $widget += array(
                '#suffix' => '<div class="field-example-colorpicker"></div>',
                '#attributes' => array(
                    'class' => array(
                        'edit-field-example-colorpicker',
                    ),
                ),
                '#attached' => array(
                    // Add Farbtastic color picker.
'library' => array(
                        array(
                            'system',
                            'farbtastic',
                        ),
                    ),
                    // Add javascript to trigger the colorpicker.
'js' => array(
                        drupal_get_path('module', 'field_example') . '/field_example.js',
                    ),
                ),
            );
        // DELIBERATE fall-through: From here on the field_example_text and
        // field_example_colorpicker are exactly the same.
        case 'field_example_text':
            $widget += array(
                '#type' => 'textfield',
                '#default_value' => $value,
                // Allow a slightly larger size that the field length to allow for some
                // configurations where all characters won't fit in input field.
'#size' => 7,
                '#maxlength' => 7,
            );
            break;
        case 'field_example_3text':
            // Convert rgb value into r, g, and b for #default_value.
            if (!empty($value)) {
                preg_match_all('@..@', substr($value, 1), $match);
            }
            else {
                $match = array(
                    array(),
                );
            }
            // Make this a fieldset with the three text fields.
            $widget += array(
                '#type' => 'fieldset',
                '#element_validate' => array(
                    'field_example_3text_validate',
                ),
                // #delta is set so that the validation function will be able
                // to access external value information which otherwise would be
                // unavailable.
'#delta' => $delta,
                '#attached' => array(
                    'css' => array(
                        drupal_get_path('module', 'field_example') . '/field_example.css',
                    ),
                ),
            );
            // Create a textfield for saturation values for Red, Green, and Blue.
            foreach (array(
                'r' => t('Red'),
                'g' => t('Green'),
                'b' => t('Blue'),
            ) as $key => $title) {
                $widget[$key] = array(
                    '#type' => 'textfield',
                    '#title' => $title,
                    '#size' => 2,
                    '#default_value' => array_shift($match[0]),
                    '#attributes' => array(
                        'class' => array(
                            'rgb-entry',
                        ),
                    ),
                    '#description' => t('The 2-digit hexadecimal representation of @color saturation, like "a1" or "ff"', array(
                        '@color' => $title,
                    )),
                );
                // Since Form API doesn't allow a fieldset to be required, we
                // have to require each field element individually.
                if ($instance['required'] == 1) {
                    $widget[$key]['#required'] = 1;
                }
            }
            break;
    }
    $element['rgb'] = $widget;
    return $element;
}

/**
 * Validate the individual fields and then convert to RGB string.
 */
function field_example_3text_validate($element, &$form_state) {
    // @todo: Isn't there a better way to find out which element?
    $delta = $element['#delta'];
    $field = $form_state['field'][$element['#field_name']][$element['#language']]['field'];
    $field_name = $field['field_name'];
    if (isset($form_state['values'][$field_name][$element['#language']][$delta]['rgb'])) {
        $values = $form_state['values'][$field_name][$element['#language']][$delta]['rgb'];
        foreach (array(
            'r',
            'g',
            'b',
        ) as $colorfield) {
            $colorfield_value = hexdec($values[$colorfield]);
            // If they left any empty, we'll set the value empty and quit.
            if (strlen($values[$colorfield]) == 0) {
                form_set_value($element, '', $form_state);
                return;
            }
            // If they gave us anything that's not hex, reject it.
            if (strlen($values[$colorfield]) != 2 || $colorfield_value < 0 || $colorfield_value > 255) {
                form_error($element[$colorfield], t("Saturation value must be a 2-digit hexadecimal value between 00 and ff."));
            }
        }
        $value = sprintf('#%02s%02s%02s', $values['r'], $values['g'], $values['b']);
        form_set_value($element, $value, $form_state);
    }
}

/**
 * Implements hook_field_widget_error().
 *
 * hook_field_widget_error() lets us figure out what to do with errors
 * we might have generated in hook_field_validate(). Generally, we'll just
 * call form_error().
 *
 * @see field_example_field_validate()
 * @see form_error()
 */
function field_example_field_widget_error($element, $error, $form, &$form_state) {
    switch ($error['error']) {
        case 'field_example_invalid':
            form_error($element, $error['message']);
            break;
    }
}

/**
 * Implements hook_menu().
 *
 * Provides a simple user interface that tells the developer where to go.
 */
function field_example_menu() {
    $items['examples/field_example'] = array(
        'title' => 'Field Example',
        'page callback' => '_field_example_page',
        'access callback' => TRUE,
    );
    return $items;
}

/**
 * A simple page to explain to the developer what to do.
 */
function _field_example_page() {
    return t("The Field Example provides a field composed of an HTML RGB value, like #ff00ff. To use it, add the field to a content type.");
}

/**
 * @} End of "defgroup field_example".
 */

Functions

Title Deprecated Summary
field_example_3text_validate Validate the individual fields and then convert to RGB string.
field_example_field_formatter_info Implements hook_field_formatter_info().
field_example_field_formatter_view Implements hook_field_formatter_view().
field_example_field_info Implements hook_field_info().
field_example_field_is_empty Implements hook_field_is_empty().
field_example_field_validate Implements hook_field_validate().
field_example_field_widget_error Implements hook_field_widget_error().
field_example_field_widget_form Implements hook_field_widget_form().
field_example_field_widget_info Implements hook_field_widget_info().
field_example_menu Implements hook_menu().
_field_example_page A simple page to explain to the developer what to do.