You are here

dimension.module in Dimension 7

Same filename and directory in other branches
  1. 2.1.x dimension.module
  2. 2.0.x dimension.module

File

dimension.module
View source
<?php

define('DIMENSION_MODE_LENGTH', 1);
define('DIMENSION_MODE_AREA', 2);
define('DIMENSION_MODE_VOLUME', 3);
define('DIMENSION_DATATYPE_INTEGER', 1);
define('DIMENSION_DATATYPE_DECIMAL', 2);

/**
 * ------------------------ Field implementation -------------------------------
 */

/**
 * Implements hook_field_info();
 */
function dimension_field_info() {
  return array(
    'dimension' => array(
      'label' => t('Dimensions'),
      'description' => t('This field stores length or area (width/height) or volume (width/height/depth).'),
      'settings' => _dimension_default_field_instance_settings(),
      'default_widget' => 'dimension_widget',
      'default_formatter' => 'dimension_formatter',
      'property_type' => 'dimension',
      'property_callbacks' => array(
        'dimension_field_dimension_property_callback',
      ),
    ),
  );
}

/**
 * Additional callback to adapt the property info for dimension fields.
 *
 * @see entity_metadata_field_entity_property_info()
 * @see entity_field_info_alter()
 */
function dimension_field_dimension_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
  $property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
  unset($property['query callback']);
  list($has_length, $has_more) = _dimension_get_modes($field['settings']['mode']);
  if ($has_length) {
    $property['property info']['length'] = array(
      'type' => 'integer',
      'label' => t('Length'),
      'setter callback' => 'entity_property_verbatim_set',
    );
  }
  if ($has_more) {
    $property['property info']['width'] = array(
      'type' => 'integer',
      'label' => t('Width'),
      'setter callback' => 'entity_property_verbatim_set',
    );
    $property['property info']['height'] = array(
      'type' => 'integer',
      'label' => t('Height'),
      'setter callback' => 'entity_property_verbatim_set',
    );
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function dimension_field_formatter_info() {
  return array(
    'dimension_formatter' => array(
      'label' => t('Dimensions as table'),
      'description' => t('Display the dimensions as a table.'),
      'field types' => array(
        'dimension',
      ),
    ),
    'dimension_formatter_simple' => array(
      'label' => t('Dimensions as simple line'),
      'description' => t('Display the dimensions as a single line.'),
      'field types' => array(
        'dimension',
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_info().
 */
function dimension_field_widget_info() {
  return array(
    'dimension_widget' => array(
      'label' => t('Dimension Widget'),
      'description' => t('TODO.'),
      'field types' => array(
        'dimension',
      ),
    ),
  );
}

/**
 * Implements hook_field_settings_form().
 */
function dimension_field_settings_form($field, $instance, $has_data) {
  $settings = $field['settings'];
  $form['mode'] = array(
    '#type' => 'select',
    '#title' => t('Dimensions'),
    '#options' => dimension_get_modes_select(),
    '#default_value' => $settings['mode'],
  );
  return $form;
}

/**
 * Implements hook_field_instance_settings_form().
 */
function dimension_field_instance_settings_form($field, $instance) {
  $settings = drupal_array_merge_deep(_dimension_default_field_instance_settings(), $instance['settings']);
  list($has_length, $has_more) = _dimension_get_modes($field['settings']['mode']);
  $field_names = array(
    'height',
    'width',
    'length',
  );
  $columns = array(
    'label',
    'prefix',
    'suffix',
    'datatype',
    'decimals',
    'factor',
    'descr',
  );
  $form['#tree'] = TRUE;
  $form['fields'] = array(
    '#type' => 'container',
    '#prefix' => '<table><tr>
        <th>' . t('Dimension') . '</th>
        <th>' . t('Label') . '</th>
        <th>' . t('Prefix') . '</th>
        <th>' . t('Suffix') . '</th>
        <th>' . t('Data type') . '</th>
        <th>' . t('Decimals') . '</th>
        <th>' . t('Factor') . '</th>
        <th>' . t('Description') . '</th>
        </tr>',
    '#suffix' => '</table>',
  );
  $n = 0;
  foreach ($field_names as $field_name) {
    $n++;
    $form['fields'][$field_name] = array(
      '#type' => 'container',
      '#prefix' => '<tr><td>' . t('Dimension %n', array(
        '%n' => $n,
      )) . '</td>',
      '#suffix' => '</tr>',
      '#access' => $field_name == 'length' ? $has_length : $has_more,
    );
    foreach ($columns as $column) {
      if ($column == 'datatype') {
        $form['fields'][$field_name][$column] = array(
          '#type' => 'select',
          '#default_value' => $settings['fields'][$field_name][$column],
          '#options' => array(
            DIMENSION_DATATYPE_INTEGER => t('Integer'),
            DIMENSION_DATATYPE_DECIMAL => t('Decimal'),
          ),
          '#prefix' => '<td>',
          '#suffix' => '</td>',
        );
      }
      else {
        $form['fields'][$field_name][$column] = array(
          '#type' => 'textfield',
          '#default_value' => $settings['fields'][$field_name][$column],
          '#size' => 6,
          '#prefix' => '<td>',
          '#suffix' => '</td>',
        );
      }
    }
  }
  if ($has_more) {
    $form['dimension_unit'] = array(
      '#type' => 'textfield',
      '#title' => t('Unit for the !name', array(
        '!name' => $has_length ? t('volume') : t('area'),
      )),
      '#default_value' => empty($settings['dimension_unit']) ? '' : $settings['dimension_unit'],
      '#size' => 6,
    );
  }
  return $form;
}

/**
 * Implements hook_field_validate().
 */
function dimension_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  if (empty($field)) {

    // This got called by the field settings form, we ignore it for now.
    return;
  }
  $settings = $instance['settings'];
  list($has_length, $has_more) = _dimension_get_modes($field['settings']['mode']);
  $field_names = $has_length ? array(
    'length',
  ) : array();
  $field_names += $has_more ? array(
    'height',
    'width',
  ) : array();
  foreach ($items as $delta => $item) {
    foreach ($field_names as $field_name) {
      $value = trim($item[$field_name]);
      $field_settings = $settings['fields'][$field_name];
      switch ($field_settings['datatype']) {
        case DIMENSION_DATATYPE_INTEGER:
          if (strval((int) $value) !== $value) {
            $errors[$instance['field_name']][$langcode][$delta][] = array(
              'error' => 'Dimension requires an integer value',
              'message' => t('The value for %label needs to be an integer.', array(
                '%label' => $field_settings['label'],
              )),
            );
          }
          break;
        case DIMENSION_DATATYPE_DECIMAL:
          if (strval((double) $value) !== $value) {
            $errors[$instance['field_name']][$langcode][$delta][] = array(
              'error' => 'Dimension requires a decimal value',
              'message' => t('The value for %label needs to be an decimal.', array(
                '%label' => $field_settings['label'],
              )),
            );
          }

          // TODO: Finalize validation to make sure that the maximum number of decimals is not exceeded.
          break;
        default:
          break;
      }
      if ($value <= 0) {

        // Dimensions always have to be positive.
        $errors[$instance['field_name']][$langcode][$delta][] = array(
          'error' => 'Dimension with negative value',
          'message' => t('The value for %label needs to be positive.', array(
            '%label' => $field_settings['label'],
          )),
        );
      }
    }
  }
}

/**
 * Implements hook_field_widget_form().
 */
function dimension_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $settings = $instance['settings'];
  list($has_length, $has_more) = _dimension_get_modes($field['settings']['mode']);
  $item = isset($items[$delta]) ? $items[$delta] : array();
  $dimensions = array();
  _dimension_field_widget_form_field($dimensions, 'length', $has_length, $item, $settings);
  _dimension_field_widget_form_field($dimensions, 'width', $has_more, $item, $settings);
  _dimension_field_widget_form_field($dimensions, 'height', $has_more, $item, $settings);
  $element['#type'] = 'fieldset';
  $element += $dimensions;
  return $element;
}

/**
 * Implements hook_field_formatter_view().
 */
function dimension_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $instance['settings'];
  switch ($display['type']) {
    case 'dimension_formatter':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#theme' => 'dimension_theme_table',
          '#mode' => $field['settings']['mode'],
          '#item' => $item,
          '#settings' => $settings,
        );
      }
      break;
    case 'dimension_formatter_simple':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#theme' => 'dimension_theme_simple',
          '#mode' => $field['settings']['mode'],
          '#item' => $item,
          '#settings' => $settings,
        );
      }
      break;
  }
  return $element;
}

/**
 * Implements hook_field_is_empty().
 */
function dimension_field_is_empty($item, $field) {
  return FALSE;
}

/**
 * ------------------------ Themeing -------------------------------------------
 */

/**
 * Implements hook_theme().
 */
function dimension_theme($existing, $type, $theme, $path) {
  return array(
    'dimension_theme_table' => array(
      'variables' => array(
        'mode' => DIMENSION_MODE_LENGTH,
        'item' => array(),
        'settings' => array(),
      ),
    ),
    'dimension_theme_simple' => array(
      'variables' => array(
        'mode' => DIMENSION_MODE_LENGTH,
        'item' => array(),
        'settings' => array(),
      ),
    ),
    'dimension_theme_value' => array(
      'variables' => array(
        'value' => array(),
        'settings' => array(),
        'label' => TRUE,
      ),
    ),
  );
}

/**
 * Callback to theme an individual value of a dimension field.
 *
 * @param array $variable
 * @return string
 */
function theme_dimension_theme_value($variable) {
  $value = $variable['value'];
  $settings = $variable['settings'];
  $container = $variable['label'] ? 'div' : 'span';
  $output = '<' . $container . ' class="dimension">';
  if ($variable['label']) {
    $output .= '<span class="label">' . $settings['label'] . ':</span>';
  }
  $output .= '<span class="value">' . $settings['prefix'] . $value . $settings['suffix'] . '</span>';
  $output .= '</' . $container . '>';
  return $output;
}

/**
 * Callback to theme a complete dimension field with one value per line and a
 * leading label for each of them.
 *
 * @param array $variable
 * @return string
 */
function theme_dimension_theme_table($variable) {
  $mode = $variable['mode'];
  $item = $variable['item'];
  $settings = $variable['settings'];
  list($has_length, $has_more) = _dimension_get_modes($mode);
  $output = '<div class="dimension dimension-table">';
  if ($has_length) {
    $output .= theme('dimension_theme_value', array(
      'value' => $item['length'],
      'settings' => $settings['fields']['length'],
    ));
  }
  if ($has_more) {
    $output .= theme('dimension_theme_value', array(
      'value' => $item['width'],
      'settings' => $settings['fields']['width'],
    ));
    $output .= theme('dimension_theme_value', array(
      'value' => $item['height'],
      'settings' => $settings['fields']['height'],
    ));
  }
  if ($has_more && !empty($settings['dimension_unit'])) {
    $label = $has_length ? t('Volume') : t('Area');
    $class = $has_length ? 'volume' : 'area';
    $dimension = dimension_calculate($item, $settings['fields']);
    $output .= '<div class="dimension ' . $class . '"><span class="label">' . $label . ':</span><span class="value">' . $dimension . $settings['dimension_unit'] . '</span></div>';
  }
  $output .= '</div>';
  drupal_add_css(drupal_get_path('module', 'dimension') . '/dimension.css');
  return $output;
}

/**
 * Callback to theme a complete dimension field with all values combined into
 * a single line with the labels shortened to the first character.
 *
 * @param array $variable
 * @return string
 */
function theme_dimension_theme_simple($variable) {
  $mode = $variable['mode'];
  $item = $variable['item'];
  $settings = $variable['settings'];
  list($has_length, $has_more) = _dimension_get_modes($mode);
  $output = '<div class="dimension dimension-simple">';
  $labels = array();
  $values = array();
  if ($has_length) {
    $labels[] = substr($settings['fields']['length']['label'], 0, 1);
    $values[] = theme('dimension_theme_value', array(
      'value' => $item['length'],
      'settings' => $settings['fields']['length'],
      'label' => FALSE,
    ));
  }
  if ($has_more) {
    $labels[] = substr($settings['fields']['width']['label'], 0, 1);
    $values[] = theme('dimension_theme_value', array(
      'value' => $item['width'],
      'settings' => $settings['fields']['height'],
      'label' => FALSE,
    ));
    $labels[] = substr($settings['fields']['height']['label'], 0, 1);
    $values[] = theme('dimension_theme_value', array(
      'value' => $item['height'],
      'settings' => $settings['fields']['height'],
      'label' => FALSE,
    ));
  }
  $output .= '<span class="label">' . implode('/', $labels) . ':</span>';
  $output .= '<span class="value">' . implode('/', $values) . '</span>';
  if ($has_more && !empty($settings['dimension_unit'])) {
    $label = $has_length ? t('Volume') : t('Area');
    $class = $has_length ? 'volume' : 'area';
    $dimension = dimension_calculate($item, $settings['fields']);
    $output .= '<span class="dimension ' . $class . '"><span class="label">' . $label . ':</span><span class="value">' . $dimension . $settings['dimension_unit'] . '</span></span>';
  }
  $output .= '</div>';
  drupal_add_css(drupal_get_path('module', 'dimension') . '/dimension.css');
  return $output;
}

/**
 * ------------------------ Helper functions -----------------------------------
 */
function dimension_calculate($dimensions, $settings) {
  $length = (isset($dimensions['length']) ? $dimensions['length'] : 1) * $settings['length']['factor'];
  $height = (isset($dimensions['height']) ? $dimensions['height'] : 1) * $settings['height']['factor'];
  $width = (isset($dimensions['width']) ? $dimensions['width'] : 1) * $settings['width']['factor'];
  return $length * $width * $height;
}
function dimension_get_modes_select() {
  return array(
    DIMENSION_MODE_LENGTH => t('Length'),
    DIMENSION_MODE_AREA => t('Width and Height'),
    DIMENSION_MODE_VOLUME => t('Width, Height and Depth'),
  );
}

/**
 * Callback to determine which dimension components are supported by the $mode
 * of a dimension field.
 *
 * @param int $mode
 *   One of the values defined in DIMENSION_MODE_LENGTH, DIMENSION_MODE_AREA
 *   and DIMENSION_MODE_VOLUME.
 *
 * @return array
 *   An array containing to boolean values where the first one determines if the
 *   length component is supported and the second one if width and height are
 *   supported.
 */
function _dimension_get_modes($mode) {
  $has_length = in_array($mode, array(
    DIMENSION_MODE_LENGTH,
    DIMENSION_MODE_VOLUME,
  ));
  $has_more = in_array($mode, array(
    DIMENSION_MODE_AREA,
    DIMENSION_MODE_VOLUME,
  ));
  return array(
    $has_length,
    $has_more,
  );
}

/**
 * Callback to provide the default settings of a dimension field.
 *
 * @return array
 */
function _dimension_default_field_instance_settings() {
  return array(
    'mode' => DIMENSION_MODE_LENGTH,
    'fields' => array(
      'length' => array(
        'label' => t('Length'),
        'prefix' => '',
        'suffix' => 'mm',
        'descr' => '',
        'datatype' => DIMENSION_DATATYPE_INTEGER,
        'decimals' => 0,
        'factor' => 1,
      ),
      'height' => array(
        'label' => t('Height'),
        'prefix' => '',
        'suffix' => 'mm',
        'descr' => '',
        'datatype' => DIMENSION_DATATYPE_INTEGER,
        'decimals' => 0,
        'factor' => 1,
      ),
      'width' => array(
        'label' => t('Width'),
        'prefix' => '',
        'suffix' => 'mm',
        'descr' => '',
        'datatype' => DIMENSION_DATATYPE_INTEGER,
        'decimals' => 0,
        'factor' => 1,
      ),
    ),
  );
}

/**
 * Callback called by dimension_field_widget_form() to define the field
 * settings for one of the dimension components.
 *
 * @param array $form_fields
 *   The form fragment which contains all the field definitions to which the
 *   dimension component is supposed to be added.
 * @param string $name
 *   The internal name of the dimension component. Supported values are 'length',
 *   'width' and 'height'.
 * @param bool $access
 *   If TRUE, the field will be visible, FALSE otherwise.
 * @param array $item
 *   An array containing all the dimension values.
 * @param array $settings
 *   An array containing the field settings.
 */
function _dimension_field_widget_form_field(&$form_fields, $name, $access, $item, $settings) {
  $settings = drupal_array_merge_deep(_dimension_default_field_instance_settings(), $settings);
  $field_settings = $settings['fields'][$name];
  $form_fields[$name] = array(
    '#type' => 'textfield',
    '#title' => $field_settings['label'],
    '#field_prefix' => $field_settings['prefix'],
    '#field_suffix' => $field_settings['suffix'],
    '#description' => $field_settings['descr'],
    '#default_value' => isset($item[$name]) ? $item[$name] : 1,
    '#size' => 6,
    '#access' => $access,
  );
}

Functions

Namesort descending Description
dimension_calculate ------------------------ Helper functions -----------------------------------
dimension_field_dimension_property_callback Additional callback to adapt the property info for dimension fields.
dimension_field_formatter_info Implements hook_field_formatter_info().
dimension_field_formatter_view Implements hook_field_formatter_view().
dimension_field_info Implements hook_field_info();
dimension_field_instance_settings_form Implements hook_field_instance_settings_form().
dimension_field_is_empty Implements hook_field_is_empty().
dimension_field_settings_form Implements hook_field_settings_form().
dimension_field_validate Implements hook_field_validate().
dimension_field_widget_form Implements hook_field_widget_form().
dimension_field_widget_info Implements hook_field_widget_info().
dimension_get_modes_select
dimension_theme Implements hook_theme().
theme_dimension_theme_simple Callback to theme a complete dimension field with all values combined into a single line with the labels shortened to the first character.
theme_dimension_theme_table Callback to theme a complete dimension field with one value per line and a leading label for each of them.
theme_dimension_theme_value Callback to theme an individual value of a dimension field.
_dimension_default_field_instance_settings Callback to provide the default settings of a dimension field.
_dimension_field_widget_form_field Callback called by dimension_field_widget_form() to define the field settings for one of the dimension components.
_dimension_get_modes Callback to determine which dimension components are supported by the $mode of a dimension field.

Constants