Skip to content
colorfield.module 15.2 KiB
Newer Older
Lukas White's avatar
Lukas White committed
<?php
/**
 * @file
 * A simple color field module - with picker - based closely on the module
 * in Examples.
Lukas White's avatar
Lukas White committed
 */

/**
 * Implements hook_theme().
 */
function colorfield_theme() {
  return array(
    'colorfield_simpletext' => array(
      'variables' => array(
        'text_color' => 'black', 'text_message' => '',
      ),
      'template' => 'templates/colorfield-simpletext',
Lukas White's avatar
Lukas White committed
/***************************************************************
 * Field Type API hooks
 ***************************************************************/

/**
 * Implements hook_field_info().
 *
 * Provides the description of the field.
 */
function colorfield_field_info() {
  return array(
    // We name our field as the associative name of the array.
    'colorfield_rgb' => array(
      'label' => t('Color Picker (RGB)'),
      'description' => t('A field composed of an RGB color.'),
      'default_widget' => 'colorfield_3text',
      'default_formatter' => 'colorfield_simple_text',
      'instance_settings' => array('text_processing' => 0),
Lukas White's avatar
Lukas White committed
    ),
  );
}

/**
 * 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 colorfield_field_widget_error()
 */
function colorfield_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  foreach ($items as $delta => $item) {
    if (!empty($item['rgb'])) {
      if (!preg_match('@^#[0-9a-fA-F]{6}$@', $item['rgb'])) {
Lukas White's avatar
Lukas White committed
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'colorfield_invalid',
          'message' => t('Color must be in the HTML format #abcdef.'),
        );
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 *
 * hook_field_is_emtpy() 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 colorfield_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 colorfield_field_formatter_view()
 */
function colorfield_field_formatter_info() {
  return array(
    // This formatter displays the raw value of the color.
    'colorfield_raw_rgb' => array(
Julien Dubois's avatar
Julien Dubois committed
      'label' => t('Raw RGB value'),
      'field types' => array('colorfield_rgb'),
      'settings' => array('display_hash' => TRUE),
    ),
    // This formatter displays a DIV of the specified color
    'colorfield_color_swatch' => array(
Julien Dubois's avatar
Julien Dubois committed
      'label' => t('Color swatch'),
      'field types' => array('colorfield_rgb'),
      'settings' => array('width' => 20, 'height' => 20),
    ),
Lukas White's avatar
Lukas White committed
    // This formatter just displays the hex value in the color indicated.
    'colorfield_simple_text' => array(
      'label' => t('Simple text-based formatter'),
      'field types' => array('colorfield_rgb'),
      'settings' => array('message' => t('The color code in this field is @code')),
Lukas White's avatar
Lukas White committed
    ),
    // This formatter changes the background color of the content region.
    'colorfield_color_background' => array(
Julien Dubois's avatar
Julien Dubois committed
      'label' => t('Background of the output text'),
Lukas White's avatar
Lukas White committed
      'field types' => array('colorfield_rgb'),
    ),
  );
}


/**
 * Implements hook_field_formatter_settings_form().
 */
function colorfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  // Expose the message to display as a setting.
  if ($display['type'] == 'colorfield_simple_text') {
    $element['message'] = array(
      '#type' => 'textfield',
      '#title' => t('Message to display'),
      '#default_value' => $settings['message'],
      '#description' => t('Note that you can use @code to display the value of the code in the message.'),
    );
  }
  // Expose the the block width and height.
  else if ($display['type'] == 'colorfield_color_swatch') {
    $element['width'] = array(
      '#type' => 'textfield',
      '#title' => t('Width of the block'),
      '#size' => 3,
      '#required' => TRUE,
      '#element_validate' => array('element_validate_number'),
      '#default_value' => $settings['width'],
    );

    $element['height'] = array(
      '#type' => 'textfield',
      '#title' => t('Height of the block'),
      '#size' => 3,
      '#required' => TRUE,
      '#element_validate' => array('element_validate_number'),
      '#default_value' => $settings['height'],
    );
  }
  else if ($display['type'] == 'colorfield_raw_rgb') {
    $element['display_hash'] = array(
      '#type' => 'checkbox',
      '#title' => t('Display the # in the output of the color'),
      '#default_value' => $settings['display_hash'],
    );
  }

  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function colorfield_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $summary = array();

  // Displays the dynamic message and replace @code by the value of the color
  // code if it exists in the message.
  if ($display['type'] == 'colorfield_simple_text') {
    $summary[] = t('Message displayed: %message', array('%message' => $settings['message']));
    if (strpos($settings['message'], '@code')) {
      $summary[] = '<small>' . t('Note that @code will be replaced by the color picked.') . '</small>';
    }
  }
  // Displays the width & height of the block as summary.
  else if ($display['type'] == 'colorfield_color_swatch') {
    $summary[] = t('Width: @width px', array('@width' => $settings['width']));
    $summary[] = t('Height: @height px', array('@height' => $settings['height']));
  }
  // Displays if the hash is displayed or not.
  else if ($display['type'] == 'colorfield_raw_rgb') {
Julien Dubois's avatar
Julien Dubois committed
    $summary[] = ($settings['display_hash']) ? t('The raw will be prefixed with #.') : t('The raw will not be prefixed with #.');
Lukas White's avatar
Lukas White committed
/**
 * Implements hook_field_formatter_view().
 *
 * Two formatters are implemented.
 * - colorfield_simple_text just outputs markup indicating the color that
 *   was entered and uses an inline style to set the text color to that value.
 * - colorfield_color_background does the same but also changes the
 *   background color of div.region-content.
 *
 * @see colorfield_field_formatter_info()
 */
function colorfield_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 'colorfield_simple_text':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#theme' => 'colorfield_simpletext',
          '#text_color' => $item['rgb'],
          '#text_message' => t($display['settings']['message'], array('@code' => $item['rgb'])),
Lukas White's avatar
Lukas White committed
        );
      }
      break;
    // This formatter simply outputs the raw RGB value prefixed or not with
    // the hash.
    case 'colorfield_raw_rgb':
      foreach ($items as $delta => $item) {
        $color = ($display['settings']['display_hash'])? $item['rgb'] : substr($item['rgb'], 1);
        $element[$delta] = array('#markup' => $color);
      }
      break;
Lukas White's avatar
Lukas White committed
    // 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 'colorfield_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;
    // Adds an empty DIV, the background of which uses the selected colour.
    // Could be used, for example, to display a swatch of the color.
    case 'colorfield_color_swatch':
Lukas White's avatar
Lukas White committed
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#type' => 'html_tag',
          '#tag' => 'div',
          '#attributes' => array(
            'class' => array('colorfield-color-swatch'),
            'style' => 'width: ' . $display['settings']['width'] . 'px; height: ' . $display['settings']['height'] . 'px; background-color:' . $item['rgb'] . ';'
          ),
        );
Lukas White's avatar
Lukas White committed
      }
      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 colorfield_field_widget_form()
 */
function colorfield_field_widget_info() {
  return array(
    'colorfield_text' => array(
      'label' => t('RGB value as #ffffff'),
      'field types' => array('colorfield_rgb'),
    ),
    'colorfield_3text' => array(
      'label' => t('RGB text field'),
      'field types' => array('colorfield_rgb'),
Lukas White's avatar
Lukas White committed
    ),
    'colorfield_colorpicker' => array(
      'label' => t('Color Picker'),
      'field types' => array('colorfield_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 'colorfield_colorpicker' and 'colorfield_text' are essentially
 * the same, but colorfield_colorpicker adds a javascript colorpicker
 * helper.
 *
 * colorfield_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 colorfield_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 'colorfield_colorpicker':
      $widget += array(
        '#suffix' => '<div class="colorfield-colorpicker"></div>',
        '#attributes' => array('class' => array('edit-colorfield-colorpicker')),
        '#attached' => array(
          // Add Farbtastic color picker.
          /**
          'library' => array(
          array('system', 'farbtastic'),
Lukas White's avatar
Lukas White committed
          ), 
           */
          // Add javascript to trigger the colorpicker.                    
          'js' => array(
            drupal_get_path('module', 'colorfield') . '/lib/js/colorpicker.js',
            drupal_get_path('module', 'colorfield') . '/colorfield.js'
          ),
Lukas White's avatar
Lukas White committed
          // And the CSS
          'css' => array(drupal_get_path('module', 'colorfield') . '/lib/css/colorpicker.css'),
Lukas White's avatar
Lukas White committed
        ),
      );

    // DELIBERATE fall-through: From here on the colorfield_text and
    // colorfield_colorpicker are exactly the same.
    case 'colorfield_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 'colorfield_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('colorfield_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', 'colorfield') . '/colorfield.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 the @color saturation, like "a1" or "ff"', array('@color' => $title)),
        );
      }
      break;

  }

  $element['rgb'] = $widget;
  return $element;
}


/**
 * Validate the individual fields and then convert them into a single HTML RGB
 * value as text.
 */
function colorfield_3text_validate($element, &$form_state) {
  $delta = $element['#delta']; // TODO: Isn't there a better way to find out which element?
  $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) {
Lukas White's avatar
Lukas White committed
        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 colorfield_field_validate()
 * @see form_error()
 */
function colorfield_field_widget_error($element, $error, $form, &$form_state) {
  switch ($error['error']) {
    case 'colorfield_invalid':
      form_error($element, $error['message']);
      break;
  }
}