- <?php
- * @file
- * Defines list field types that can be used with the Options module.
- */
-
- * Implements hook_field_info().
- */
- function list_field_info() {
- return array(
- 'list_integer' => array(
- 'label' => t('List (integer)'),
- 'description' => t("This field stores integer values from a list of allowed 'value => label' pairs, i.e. 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month."),
- 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
- 'default_widget' => 'options_select',
- 'default_formatter' => 'list_default',
- 'default_token_formatter' => 'list_default',
- ),
- 'list_float' => array(
- 'label' => t('List (float)'),
- 'description' => t("This field stores float values from a list of allowed 'value => label' pairs, i.e. 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1."),
- 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
- 'default_widget' => 'options_select',
- 'default_formatter' => 'list_default',
- 'default_token_formatter' => 'list_default',
- ),
- 'list_text' => array(
- 'label' => t('List (text)'),
- 'description' => t("This field stores text values from a list of allowed 'value => label' pairs, i.e. 'US States': IL => Illinois, IA => Iowa, IN => Indiana."),
- 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
- 'default_widget' => 'options_select',
- 'default_formatter' => 'list_default',
- 'default_token_formatter' => 'list_default',
- ),
- 'list_boolean' => array(
- 'label' => t('Boolean'),
- 'description' => t('This field stores simple on/off or yes/no options.'),
- 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
- 'default_widget' => 'options_buttons',
- 'default_formatter' => 'list_default',
- 'default_token_formatter' => 'list_default',
- ),
- );
- }
-
- * Implements hook_field_settings_form().
- */
- function list_field_settings_form($field, $instance, $has_data) {
- $settings = $field['settings'];
-
- switch ($field['type']) {
- case 'list_integer':
- case 'list_float':
- case 'list_text':
-
- $options = $field['settings']['allowed_values'];
-
- $key_type_toggled = !empty($options);
-
- $form['allowed_values'] = array(
- '#type' => 'options',
- '#title' => t('Allowed values list'),
- '#description' => '',
- '#default_value' => '',
- '#default_value_allowed' => FALSE,
- '#options' => $options,
- '#element_validate' => array('list_allowed_values_setting_validate'),
- '#field_has_data' => $has_data,
- '#field' => $field,
- '#field_type' => $field['type'],
- '#key_type' => 'mixed',
- '#key_type_toggle' => t('Custom keys'),
- '#key_type_toggled' => $key_type_toggled,
- '#access' => empty($settings['allowed_values_function']),
- '#multiple' => !($field['cardinality'] == 1),
- );
-
-
- $form['#validate'][] = 'options_field_settings_validate';
- break;
-
- case 'list_boolean':
- $values = $settings['allowed_values'];
- $off_value = array_shift($values);
- $on_value = array_shift($values);
-
- $form['allowed_values'] = array(
- '#type' => 'value',
- '#description' => '',
- '#value_callback' => 'list_boolean_allowed_values_callback',
- '#access' => empty($settings['allowed_values_function']),
- );
- $form['allowed_values']['on'] = array(
- '#type' => 'textfield',
- '#title' => t('On value'),
- '#default_value' => $on_value,
- '#required' => FALSE,
- '#description' => t('If left empty, "1" will be used.'),
-
-
- '#parents' => array('on'),
- );
- $form['allowed_values']['off'] = array(
- '#type' => 'textfield',
- '#title' => t('Off value'),
- '#default_value' => $off_value,
- '#required' => FALSE,
- '#description' => t('If left empty, "0" will be used.'),
-
-
- '#parents' => array('off'),
- );
-
-
-
- $form['allowed_values']['#on_parents'] = &$form['allowed_values']['on']['#parents'];
- $form['allowed_values']['#off_parents'] = &$form['allowed_values']['off']['#parents'];
-
- break;
- }
-
-
- if ($instance['widget']['type'] == 'options_onoff') {
- $form['allowed_values']['#description'] .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>';
- }
- elseif ($instance['widget']['type'] == 'options_buttons') {
- $form['allowed_values']['#description'] .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the <em>Number of values</em> option is greater than 1 for this field, otherwise radios will be displayed.") . '</p>';
- }
-
- $form['allowed_values_function'] = array(
- '#type' => 'value',
- '#value' => $settings['allowed_values_function'],
- );
- $form['allowed_values_function_display'] = array(
- '#type' => 'item',
- '#title' => t('Allowed values list'),
- '#markup' => t('The value of this field is being determined by the %function function and may not be changed.', array('%function' => $settings['allowed_values_function'])),
- '#access' => !empty($settings['allowed_values_function']),
- );
-
- return $form;
- }
-
- * Implements hook_form_FORM_ID_alter().
- */
- function list_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
- $field = $form['#field'];
- $instance = $form['#instance'];
-
- if (in_array($field['type'], array('list_integer', 'list_float', 'list_text'))) {
-
- if ($instance['default_value_function']) {
- return;
- }
-
-
- $form['instance']['default_value_widget']['#access'] = FALSE;
-
- $default_field_value = $form['instance']['default_value_widget'][$field['field_name']][LANGUAGE_NONE]['#default_value'];
-
-
- $form['field']['settings']['allowed_values']['#default_value_allowed'] = TRUE;
- $form['field']['settings']['allowed_values']['#default_value'] = $default_field_value;
-
-
- $form['#validate'][] = 'options_field_instance_settings_validate';
- }
- }
-
- * Element validate callback; check that the entered values are valid.
- */
- function list_allowed_values_setting_validate($element, &$form_state) {
- $field = $element['#field'];
- $has_data = $element['#field_has_data'];
- $field_type = $field['type'];
- $generate_keys = ($field_type == 'list_integer' || $field_type == 'list_float') && !$has_data;
- $values = list_extract_allowed_values($element['#value']['options_text'], $field['type'], $generate_keys);
-
- if (!is_array($values)) {
- form_error($element, t('Allowed values list: invalid input.'));
- }
- else {
-
- foreach ($values as $key => $value) {
- if ($field_type == 'list_integer' && !preg_match('/^-?\d+$/', $key)) {
- form_error($element, t('Allowed values list: keys must be integers.'));
- break;
- }
- if ($field_type == 'list_float' && !is_numeric($key)) {
- form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
- break;
- }
- elseif ($field_type == 'list_text' && backdrop_strlen($key) > 255) {
- form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
- break;
- }
- }
-
-
- if ($has_data) {
- $lost_keys = array_diff(array_keys($field['settings']['allowed_values']), array_keys($values));
- if (_list_values_in_use($field, $lost_keys)) {
- form_error($element, t('Allowed values list: some values are being removed while currently in use.'));
- }
- }
-
- form_set_value($element, $values, $form_state);
- }
- }
-
- * Form element #value_callback: assembles the allowed values for 'boolean' fields.
- */
- function list_boolean_allowed_values_callback($element, $input, $form_state) {
- $on = backdrop_array_get_nested_value($form_state['input'], $element['#on_parents']);
- $off = backdrop_array_get_nested_value($form_state['input'], $element['#off_parents']);
- return array($off, $on);
- }
-
- * Implements hook_field_update_field().
- */
- function list_field_update_field($field, $prior_field, $has_data) {
- backdrop_static_reset('list_allowed_values');
- }
-
- * Implements hook_field_delete_field().
- */
- function list_field_delete_field($field) {
- backdrop_static_reset('list_allowed_values');
- }
-
- * Returns the array of allowed values for a list field.
- *
- * The strings are not safe for output. Keys and values of the array should be
- * sanitized through field_filter_xss() before being displayed.
- *
- * @param $field
- * The field definition.
- * @param $instance
- * (optional) A field instance array. Defaults to NULL.
- * @param $entity_type
- * (optional) The type of entity; e.g. 'node' or 'user'. Defaults to NULL.
- * @param $entity
- * (optional) The entity object. Defaults to NULL.
- *
- * @return
- * The array of allowed values. Keys of the array are the raw stored values
- * (number or text), values of the array are the display labels.
- */
- function list_allowed_values($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
- $allowed_values = &backdrop_static(__FUNCTION__, array());
-
- if (!isset($allowed_values[$field['field_name']])) {
- $function = $field['settings']['allowed_values_function'];
-
-
- $cacheable = TRUE;
- if (!empty($function)) {
- $values = $function($field, $instance, $entity_type, $entity, $cacheable);
- }
- else {
- $values = $field['settings']['allowed_values'];
- }
-
- if ($cacheable) {
- $allowed_values[$field['field_name']] = $values;
- }
- else {
- return $values;
- }
- }
-
- return $allowed_values[$field['field_name']];
- }
-
- * Parses a string of 'allowed values' into an array.
- *
- * @param $string
- * The list of allowed values in string format described in
- * list_allowed_values_string().
- * @param $field_type
- * The field type. Either 'list_number' or 'list_text'.
- * @param $generate_keys
- * Boolean value indicating whether to generate keys based on the position of
- * the value if a key is not manually specified, and if the value cannot be
- * used as a key. This should only be TRUE for fields of type 'list_number'.
- *
- * @return
- * The array of extracted key/value pairs, or NULL if the string is invalid.
- *
- * @see list_allowed_values_string()
- */
- function list_extract_allowed_values($string, $field_type, $generate_keys) {
- $values = array();
-
- $list = explode("\n", $string);
- $list = array_map('trim', $list);
- $list = array_filter($list, 'strlen');
-
- $generated_keys = $explicit_keys = FALSE;
- foreach ($list as $position => $text) {
- $value = $key = FALSE;
-
-
- $matches = array();
- if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
- $key = $matches[1];
- $value = $matches[2];
- $explicit_keys = TRUE;
- }
-
-
- elseif ($field_type == 'list_text'
- || ($field_type == 'list_float' && is_numeric($text))
- || ($field_type == 'list_integer' && is_numeric($text) && (float) $text == intval($text))) {
- $key = $value = $text;
- $explicit_keys = TRUE;
- }
-
- elseif ($generate_keys) {
- $key = (string) $position;
- $value = $text;
- $generated_keys = TRUE;
- }
- else {
- return;
- }
-
-
-
- if ($field_type == 'list_float' && is_numeric($key)) {
- $key = (string) (float) $key;
- }
-
- $values[$key] = $value;
- }
-
-
- if ($explicit_keys && $generated_keys) {
- return;
- }
-
- return $values;
- }
-
- * Generates a string representation of an array of 'allowed values'.
- *
- * This string format is suitable for edition in a textarea.
- *
- * @param $values
- * An array of values, where array keys are values and array values are
- * labels.
- *
- * @return
- * The string representation of the $values array:
- * - Values are separated by a carriage return.
- * - Each value is in the format "value|label" or "value".
- */
- function list_allowed_values_string($values) {
- $lines = array();
- $previous_key = FALSE;
-
- foreach ($values as $key => $value) {
-
- if (is_array($value)) {
- $lines[] = "<$key>";
- foreach ($value as $subkey => $subvalue) {
- $lines[] = "$subkey|$subvalue";
- }
- $previous_key = $key;
- }
-
- else {
-
- if (isset($values[$previous_key]) && is_array($values[$previous_key])) {
- $lines[] = "<>";
- }
-
- if ($values[$key] !== '') {
- $lines[] = "$key|$value";
- }
- $previous_key = $key;
- }
- }
- return implode("\n", $lines);
- }
-
- * Implements hook_field_update_forbid().
- */
- function list_field_update_forbid($field, $prior_field, $has_data) {
- if ($field['module'] == 'list' && $has_data) {
-
- $lost_keys = array_diff(array_keys($prior_field['settings']['allowed_values']), array_keys($field['settings']['allowed_values']));
- if (_list_values_in_use($field, $lost_keys)) {
- throw new FieldUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field['field_name'])));
- }
- }
- }
-
- * Checks if a list of values are being used in actual field values.
- */
- function _list_values_in_use($field, $values) {
- if ($values) {
- $query = new EntityFieldQuery();
- $found = $query
- ->fieldCondition($field['field_name'], 'value', $values)
- ->range(0, 1)
- ->execute();
- return !empty($found);
- }
-
- return FALSE;
- }
-
- * Implements hook_field_validate().
- *
- * Possible error codes:
- * - 'list_illegal_value': The value is not part of the list of allowed values.
- */
- function list_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
- $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
- foreach ($items as $delta => $item) {
- if (!empty($item['value'])) {
- if (!empty($allowed_values) && !isset($allowed_values[$item['value']])) {
- $errors[$field['field_name']][$langcode][$delta][] = array(
- 'error' => 'list_illegal_value',
- 'message' => t('%name: illegal value.', array('%name' => $instance['label'])),
- );
- }
- }
- }
- }
-
- * Implements hook_field_is_empty().
- */
- function list_field_is_empty($item, $field) {
- if (empty($item['value']) && (string) $item['value'] !== '0') {
- return TRUE;
- }
- return FALSE;
- }
-
- * Implements hook_field_widget_info_alter().
- *
- * The List module does not implement widgets of its own, but reuses the
- * widgets defined in options.module.
- *
- * @see list_options_list()
- */
- function list_field_widget_info_alter(&$info) {
- $widgets = array(
- 'options_select' => array('list_integer', 'list_float', 'list_text'),
- 'options_buttons' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
- 'options_onoff' => array('list_boolean'),
- );
-
- foreach ($widgets as $widget => $field_types) {
- $info[$widget]['field types'] = array_merge($info[$widget]['field types'], $field_types);
- }
- }
-
- * Implements hook_options_list().
- */
- function list_options_list($field, $instance, $entity_type, $entity) {
- return list_allowed_values($field, $instance, $entity_type, $entity);
- }
-
- * Implements hook_field_formatter_info().
- */
- function list_field_formatter_info() {
- return array(
- 'list_default' => array(
- 'label' => t('Default'),
- 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
- ),
- 'list_key' => array(
- 'label' => t('Key'),
- 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
- ),
- 'boolean_yes_no' => array(
- 'label' => t('Yes/No'),
- 'field types' => array('list_boolean'),
- 'settings' => array(
- 'format' => 'yes-no',
- 'custom_on' => '',
- 'custom_off' => '',
- 'reverse' => 0,
- ),
- ),
- );
- }
-
- * Implements hook_field_formatter_view().
- */
- function list_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
- $element = array();
- $settings = $display['settings'];
-
- switch ($display['type']) {
- case 'list_default':
- $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
- foreach ($items as $delta => $item) {
- if (isset($allowed_values[$item['value']])) {
- $output = field_filter_xss($allowed_values[$item['value']]);
- }
- else {
-
- $output = field_filter_xss($item['value']);
- }
- $element[$delta] = array('#markup' => $output);
- }
- break;
-
- case 'list_key':
- foreach ($items as $delta => $item) {
- $element[$delta] = array('#markup' => field_filter_xss($item['value']));
- }
- break;
- case 'boolean_yes_no':
- foreach ($items as $delta => $item) {
- $output = list_boolean_display_value_with_format($item['value'], $settings['format'], $settings);
- $element[$delta] = array('#markup' => field_filter_xss($output));
- }
- break;
- }
-
- return $element;
- }
-
- * List of boolean display formats.
- */
- function list_boolean_display_formats(array $options = array()) {
- $formats = array(
- 'yes-no' => array(t('Yes'), t('No')),
- 'true-false' => array(t('True'), t('False')),
- 'on-off' => array(t('On'), t('Off')),
- 'enabled-disabled' => array(t('Enabled'), t('Disabled')),
- 'boolean' => array(1, 0),
- 'unicode-yes-no' => array('✔', '✖'),
- 'custom' => array(t('Custom')),
- );
- if (isset($options['custom_on']) && isset($options['custom_off'])) {
- $formats['custom'] = array($options['custom_on'], $options['custom_off']);
- }
- return $formats;
- }
-
- * The display format options for boolean fields.
- */
- function list_boolean_display_format_options(array $options = array()) {
- $format_options = array();
- foreach (list_boolean_display_formats($options) as $key => $format) {
- $format_options[$key] = implode('/', $format);
- }
- return $format_options;
- }
-
- * Prepare the boolean display.
- */
- function list_boolean_display_value_with_format($value, $format, array $options = array()) {
- $formats = list_boolean_display_formats($options);
- if (!isset($formats[$format])) {
-
- reset($formats);
- $format = key($formats);
- }
- if (!empty($options['reverse'])) {
- $value = !(bool) $value;
- }
-
- return !empty($value) ? $formats[$format][0] : $formats[$format][1];
- }
-
- * Implements hook_field_formatter_settings_form().
- */
- function list_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
- $display = $instance['display'][$view_mode];
- $settings = $display['settings'];
- $element = array();
-
- switch ($display['type']) {
- case 'boolean_yes_no':
- $element['format'] = array(
- '#type' => 'select',
- '#title' => t('Output format'),
- '#options' => list_boolean_display_format_options(),
- '#default_value' => $settings['format'],
- );
-
- $element['custom_on'] = array(
- '#type' => 'textfield',
- '#title' => t('Custom output for On'),
- '#default_value' => $settings['custom_on'],
- '#states' => array(
- 'visible' => array(
- array(
- 'select[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][format]"]' => array('value' => 'custom'),
- ),
- array(
- 'select[name="options[settings][format]"]' => array('value' => 'custom'),
- ),
- ),
- ),
- );
-
- $element['custom_off'] = array(
- '#type' => 'textfield',
- '#title' => t('Custom output for Off'),
- '#default_value' => $settings['custom_off'],
- '#states' => array(
- 'visible' => array(
- array(
- 'select[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][format]"]' => array('value' => 'custom'),
- ),
- array(
- 'select[name="options[settings][format]"]' => array('value' => 'custom'),
- ),
- ),
- ),
- );
-
- $element['reverse'] = array(
- '#type' => 'checkbox',
- '#title' => t('Reverse'),
- '#description' => t('If checked, true will be displayed as false.'),
- '#default_value' => $settings['reverse'],
- );
- break;
- }
-
- return $element;
- }
-
- * Implements hook_field_formatter_settings_summary().
- */
- function list_field_formatter_settings_summary($field, $instance, $view_mode) {
- $display = $instance['display'][$view_mode];
- $settings = $display['settings'];
- $summary = array();
-
- switch ($display['type']) {
- case 'boolean_yes_no':
- $formats = list_boolean_display_format_options($settings);
- $summary[] = t('Output format: %format', array('%format' => $formats[$settings['format']]));
- $summary[] = t('Reversed: @reversed', array('@reversed' => list_boolean_display_value_with_format($settings['reverse'], 'yes-no', array('reverse' => FALSE))));
- break;
- }
-
- return implode('<br/>', $summary);
- }