You are here

values.module in Values 7

Same filename and directory in other branches
  1. 6 values.module

API for managing reusable value sets.

File

values.module
View source
<?php

/**
 * @file
 * API for managing reusable value sets.
 */

/***********************************************************************
 *
 * DRUPAL HOOKS
 *
 ***********************************************************************/

/**
 * Implements hook_permission().
 */
function values_permission() {
  return array(
    'administer values' => array(
      'title' => t('Administer values'),
      'description' => t('Access the values administration page.'),
    ),
    'create value sets' => array(
      'title' => t('Create value sets'),
    ),
    'edit value sets' => array(
      'title' => t('Edit value sets'),
    ),
    'delete value sets' => array(
      'title' => t('Delete value sets'),
    ),
    'import value sets' => array(
      'title' => t('Import value sets'),
    ),
    'export value sets' => array(
      'title' => t('Export value sets'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function values_menu() {
  $items = array();
  $items['admin/structure/values'] = array(
    'title' => 'Values',
    'description' => 'Allows the creation and configuration of reusable value sets.',
    'page callback' => 'values_sets_page',
    'access arguments' => array(
      'administer values',
    ),
  );
  $items['admin/structure/values/list'] = array(
    'title' => 'List',
    // 'weight' => -10,
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/structure/values/add'] = array(
    'title' => 'Create a value set',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_form',
      'add',
    ),
    'access arguments' => array(
      'create value sets',
    ),
    // 'weight' => -9,
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/structure/values/import'] = array(
    'title' => 'Import',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_import_form',
    ),
    'access arguments' => array(
      'import value sets',
    ),
    // 'weight' => -8,
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/structure/values/%values'] = array(
    'title' => 'Values',
    'title callback' => 'value_set_page_title',
    'title arguments' => array(
      3,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_form',
      'edit',
      3,
    ),
    'access arguments' => array(
      'edit value sets',
    ),
  );
  $items['admin/structure/values/%values/edit'] = array(
    'title' => 'Edit',
    'weight' => -9,
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/structure/values/%values/delete'] = array(
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_delete_confirm',
      3,
    ),
    'access callback' => 'values_delete_access',
    'access arguments' => array(
      3,
    ),
    'weight' => -8,
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/structure/values/%values/revert'] = array(
    'title' => 'Revert',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_delete_confirm',
      3,
    ),
    'access callback' => 'values_revert_access',
    'access arguments' => array(
      3,
    ),
    'weight' => -8,
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/structure/values/%values/export'] = array(
    'title' => 'Export',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'values_export_values',
      3,
    ),
    'access arguments' => array(
      'export value sets',
    ),
    'weight' => -7,
    'type' => MENU_LOCAL_TASK,
  );
  $items['values/js'] = array(
    'title' => 'Javascript Values Form',
    'page callback' => 'values_form_js',
    'access arguments' => array(
      'edit value sets',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function values_theme($existing, $type, $theme, $path) {
  return array(
    'values_value_fields' => array(
      'render element' => 'form',
    ),
  );
}

/**
 * Implements hook_ctools_plugin_api().
 *
 * Tell CTools that we support the default_values_sets API.
 */
function values_ctools_plugin_api($owner, $api) {
  if ($owner == 'values' && $api == 'default_values_sets') {
    return array(
      'version' => 1,
    );
  }
}

/**
 * Implements hook_i18n_string_info().
 */
function values_i18n_string_info() {
  $groups['values'] = array(
    'title' => t('Values'),
    'description' => t('Translatable field value sets.'),
    'format' => FALSE,
    // This group doesn't have strings with format
    'list' => TRUE,
  );
  return $groups;
}

/**
 * Implements hook_i18n_string_list().
 */
function values_i18n_string_list($group) {
  if ($group == 'values') {
    foreach (values_load_all() as $name => $value_set) {
      foreach ($value_set->data as $key => $value) {
        $strings['values'][$name][$value['key']]['value'] = $value['value'];
      }
    }
    return $strings;
  }
}

/***********************************************************************
 *
 * VALUES FORM
 *
 ***********************************************************************/

/**
 * Form for adding a new value set.
 */
function values_form($form, &$form_state, $action = 'edit', $value_set = NULL) {
  if (!empty($form_state['values']['data'])) {
    $value_set->data = $form_state['values']['data'];
    usort($value_set->data, 'values_sort_by_weight');
  }
  else {

    // If the argument is a string, aka a value set name, we load it
    // otherwise we assume it's a value set object
    if (is_string($value_set)) {
      $value_set = values_load($value_set, TRUE);
    }
  }
  $form = array();
  $form['action'] = array(
    '#type' => 'value',
    '#value' => $action,
  );
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Value set name'),
    '#description' => t('The human readable name of this value set.'),
    '#default_value' => isset($value_set->title) ? $value_set->title : '',
    '#required' => TRUE,
    '#weight' => -10,
  );
  $form['name'] = array(
    '#type' => 'machine_name',
    '#default_value' => isset($value_set->name) ? $value_set->name : '',
    '#required' => TRUE,
    '#weight' => -9,
    '#disabled' => isset($value_set->name),
    '#machine_name' => array(
      'source' => array(
        'title',
      ),
      'exists' => '_values_name_exists',
    ),
  );
  $form['description'] = array(
    '#type' => 'textfield',
    '#title' => t('Description'),
    '#description' => t('A short description of the value set'),
    '#default_value' => isset($value_set->description) ? $value_set->description : '',
    '#weight' => -8.5,
  );

  // Wrapper for values
  $form['values_wrapper'] = array(
    '#tree' => FALSE,
    '#title' => t('Values'),
    '#description' => t('These are the actual values associated with this value set.'),
    '#prefix' => '<div class="clear-block" id="values-value-wrapper">',
    '#suffix' => '</div>',
    '#weight' => -8,
  );

  // Container for value fields
  $form['values_wrapper']['data'] = array(
    '#tree' => TRUE,
    '#prefix' => '<div id="values-values">',
    '#suffix' => '</div>',
    '#theme' => 'values_value_fields',
    '#cache' => TRUE,
  );

  // Count values
  if (empty($form_state['values_count'])) {
    $form_state['values_count'] = max(2, empty($value_set->data) ? 2 : count($value_set->data));
  }

  // Add the current values to the form.
  for ($delta = 0; $delta < $form_state['values_count']; $delta++) {
    $weight = isset($value_set->data[$delta]['weight']) ? intval($value_set->data[$delta]['weight']) : $delta;
    $form['values_wrapper']['data'][$delta] = array(
      // Old way
      // 'value' => array(
      //   '#type' => 'textfield',
      //   '#title' => t('Value @n', array('@n' => ($delta + 1))),
      //   '#default_value' => isset($value_set->data[$delta]['value']) ? $value_set->data[$delta]['value'] : '',
      //   '#access' => user_access('administer values'),
      // ),
      'value' => array(
        '#type' => 'textfield',
        '#title' => t('Value @n', array(
          '@n' => $delta + 1,
        )),
        '#default_value' => isset($value_set->data[$delta]['value']) ? $value_set->data[$delta]['value'] : '',
        '#access' => user_access('administer values'),
      ),
      'key' => array(
        '#type' => 'machine_name',
        '#title' => t('Key for value @n', array(
          '@n' => $delta + 1,
        )),
        '#default_value' => isset($value_set->data[$delta]['key']) ? $value_set->data[$delta]['key'] : '',
        '#description' => t('A key for this value. Strips out HTML and other unsafe characters.'),
        '#after_build' => array(
          'values_form_value_after_build',
        ),
        '#machine_name' => array(
          'source' => array(
            'values_wrapper',
            'data',
            $delta,
            'value',
          ),
          'exists' => '_values_value_key_exists',
          // This is a dummy function
          'label' => 'Key',
        ),
      ),
      'remove' => array(
        '#type' => 'checkbox',
        '#title' => t(''),
        '#default_value' => isset($value_set->data[$delta]['remove']) ? $value_set->data[$delta]['remove'] : 0,
      ),
      'weight' => array(
        '#type' => 'weight',
        '#delta' => $form_state['values_count'],
        '#default_value' => $weight,
      ),
    );
  }

  // AJAX-enabled "Add more" button
  $form['values_wrapper']['values_add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more'),
    '#description' => t("If the amount of options above isn't enough, click here to add more."),
    '#weight' => 1,
    '#submit' => array(
      'values_add_more_submit',
    ),
    // If no javascript action.
    '#ajax' => array(
      'callback' => 'values_form_js',
      'wrapper' => 'values-values',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
    '#weight' => 10,
  );
  return $form;
}

/**
 * Alter the 'value' elements after the form is built
 */
function values_form_value_after_build($element, &$form_state) {

  // We don't want the element to be required because if the "Add more" button
  // is clicked and an empty row is submitted, we'll get an error.
  $element['#required'] = FALSE;

  // Remove the core element validate for the machine name field. We'll use our own.
  $element['#element_validate'] = array_diff($element['#element_validate'], array(
    'form_validate_machine_name',
  ));

  // Add our own custom validation handler to do our own "required" validation
  array_unshift($element['#element_validate'], 'values_form_value_validate');
  return $element;
}

/**
 * Validates the values form value element (type: machine_name)
 */
function values_form_value_validate(&$element, &$form_state) {
  $delta = preg_replace('/[^0-9]/', '', $element['#name']);
  $value = $form_state['values']['data'][$delta];

  // If the key has a value but no key is provided AND we're not removing
  // the row we set an error
  if ($value['key'] && !$value['key'] && !$value['remove']) {

    // This is borrowed from _form_validate() in form.inc line 1393
    if (isset($element['#title'])) {
      form_error($element, $t('!name field is required.', array(
        '!name' => $element['#title'],
      )));
    }
    else {
      form_error($element);
    }
  }
}

/**
 * Validates the values form
 */
function values_form_validate(&$element, &$form_state) {

  // not in use...
}

/**
 * Submits the values form.
 */
function values_form_submit(&$form, &$form_state) {
  $value_set = new stdClass();
  $value_set->name = $form_state['values']['name'];
  $value_set->title = $form_state['values']['title'];
  $value_set->description = $form_state['values']['description'];
  $value_set->data = array();
  foreach ($form_state['values']['data'] as $value) {
    if (!$value['remove']) {
      $value_set->data[] = array(
        'key' => filter_xss($value['key'], array()),
        'value' => filter_xss($value['value']),
        'weight' => $value['weight'],
      );
    }
  }
  values_save($value_set);
  $form_state['redirect'] = 'admin/structure/values';
}

/**
 * Submit handler to add more values to a value set. This handler is used when
 * javascript is not available. It makes changes to the form state and the
 * entire form is rebuilt during the page reload.
 */
function values_add_more_submit($form, &$form_state) {
  $form_state['values_count']++;
  $form_state['rebuild'] = TRUE;
}

/**
 * Menu callback for AJAX additions.
 */
function values_form_js($form, $form_state) {

  //unset($form['values_wrapper']['data']['#prefix'], $form['values_wrapper']['data']['#suffix']);
  unset($form['values_wrapper']['data']['#suffix']);
  return $form['values_wrapper']['data'];
}

/***********************************************************************
 *
 * DELETE VALUE SET FORM FUNCTIONS
 *
 ***********************************************************************/

/**
 * Confirmation form to delete a value object from the database.
 */
function values_delete_confirm($form, &$form_state, $value_set) {
  $form['value_set'] = array(
    '#type' => 'value',
    '#value' => $value_set,
  );
  $action = 'delete';

  // If ctools is installed we need to handle the delete link specially.
  if (module_exists('ctools')) {
    if ($value_set->export_type > 1) {
      $action = 'revert';
    }
  }
  return confirm_form($form, t('Are you sure you want to !action !name?', array(
    '!action' => $action,
    '!name' => $value_set->name,
  )), 'admin/structure/values', t('This action cannot be undone.'), t('!action', array(
    '!action' => ucwords($action),
  )), t('Cancel'));
}

/**
 * Calls deletion of a value object.
 */
function values_delete_confirm_submit(&$form, &$form_state) {
  $value_set = $form_state['values']['value_set'];
  values_delete($value_set);
  $form_state['redirect'] = 'admin/structure/values';
  $action = 'deleted';

  // If ctools is installed we need to change the language a bit of the message
  if (module_exists('ctools')) {
    if ($value_set->export_type > 1) {
      $action = 'reverted';
    }
  }
  drupal_set_message(t('Value list !name was !action.', array(
    '!action' => $action,
    '!name' => $value_set->name,
  )));
}

/***********************************************************************
 *
 * EXPORT CODE
 *
 ***********************************************************************/

/**
 * Export a value list and display it in a form.
 */
function values_export_values($form, &$form_state, $value_set) {
  if (!module_exists('ctools')) {
    return array(
      'message' => array(
        '#value' => t('For exporting capabilities, please install the !ctools module.', array(
          '!ctools' => l('Chaos tools suite', 'http://drupal.org/project/ctools'),
        )),
      ),
    );
  }
  drupal_set_title($value_set->title);

  // Ctools Export
  $code = values_export($value_set);
  $lines = substr_count($code, "\n");
  $form['export_ctools'] = array(
    '#title' => t('Export data'),
    '#type' => 'textarea',
    '#value' => $code,
    '#rows' => $lines,
    '#description' => t('Copy the export text and paste it into another value list using the import function.'),
  );

  // Flat List
  $code = '';
  foreach ($value_set->data as $value) {
    $code .= $value['key'] . '|' . $value['value'] . "\n";
  }
  $lines = count($value_set->data);
  $form['export_flat'] = array(
    '#title' => t('Flat Export List'),
    '#type' => 'textarea',
    '#value' => $code,
    '#rows' => $lines,
    '#description' => t('Flat list that emulates the CCK/Field module Allowed Values list style. Can also be used to import using the import function.'),
  );
  return $form;
}

/**
 * Export a values list.
 */
function values_export($value_set, $indent = '') {
  ctools_include('export');
  $output = ctools_export_object('values_sets', $value_set, $indent);
  return $output;
}

/**
 * Import a value list
 */
function values_import_form($form, &$form_state) {
  drupal_set_message(t('Importing a value list with a conflicting name will override it.'), 'warning');

  // If the user has the ctools import permission, we allow them to a ctools
  // export (raw php code that will be eval'd)
  if (module_exists('ctools') && user_access('use ctools import')) {
    $form['import_type'] = array(
      '#type' => 'radios',
      '#title' => t('Import Type'),
      '#description' => t('Choose an import type. Import a previously exported value set, or import a flat list of values in a textarea.'),
      '#options' => array(
        'ctools' => 'Ctools Export',
        'values' => 'Flat List',
      ),
      '#default_value' => 'ctools',
    );
    $form['import'] = array(
      '#type' => 'textarea',
      '#title' => t('Import data'),
      '#description' => t('Copy the text from an previously exported value list and paste it here.'),
      '#default_value' => isset($form_state['values']['import']) ? $form_state['values']['import'] : '',
      '#rows' => 10,
      '#states' => array(
        'visible' => array(
          ':input[name="import_type"]' => array(
            'value' => 'ctools',
          ),
        ),
      ),
    );
  }
  $form['import_values'] = array(
    '#type' => 'fieldset',
    '#title' => t('Import Values'),
    '#description' => 'Import a new value set as a flat set of key|value pairs. Useful for migrating from allowed values lists to value sets.',
    '#collapsible' => FALSE,
  );

  // Add a state for import type toggle if ctools import is also an option
  if (module_exists('ctools') && user_access('use ctools import')) {
    $form['import_values']['#states'] = array(
      'visible' => array(
        ':input[name="import_type"]' => array(
          'value' => 'values',
        ),
      ),
    );
  }
  $form['import_values']['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Value set name'),
    '#description' => t('The human readable name of this value set.'),
    '#default_value' => '',
    '#weight' => -10,
  );
  $form['import_values']['name'] = array(
    '#type' => 'machine_name',
    '#default_value' => '',
    '#weight' => -9,
    '#access' => !isset($value_set->name),
    '#after_build' => array(
      'values_import_form_after_build',
    ),
    '#machine_name' => array(
      'source' => array(
        'import_values',
        'title',
      ),
      'exists' => '_values_name_exists',
    ),
  );
  $form['import_values']['description'] = array(
    '#type' => 'textfield',
    '#title' => t('Description'),
    '#description' => t('A short description of the value set'),
    '#default_value' => isset($value_set->description) ? $value_set->description : '',
    '#weight' => -8.5,
  );

  // Wrapper for values
  $form['import_values']['values'] = array(
    '#type' => 'textarea',
    '#title' => t('Import data'),
    '#description' => t('Enter one value per line. Keys will be generated automatically. If you wish to specify the key, use the format key|value.'),
    '#default_value' => '',
    '#rows' => 10,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  return $form;
}

/**
 * Alter the 'value' elements after the form is built
 */
function values_import_form_after_build($element, &$form_state) {

  // We don't want the element to be required because if the form could be hidden
  $element['#required'] = FALSE;

  // // Add our own custom validation handler to do our own "required" validation
  array_unshift($element['#element_validate'], 'values_import_form_name_validate');
  return $element;
}

/**
 * Validation handler for values list import
 */
function values_import_form_name_validate(&$element, &$form_state) {

  // If the import type is values and there is no name set
  if ($form_state['values']['import_type'] == 'values' && !$form_state['values']['name']) {

    // This is borrowed from _form_validate() in form.inc line 1393
    if (isset($element['#title'])) {
      form_error($element, t('!name field is required.', array(
        '!name' => $element['#title'],
      )));
    }
    else {
      form_error($element);
    }
  }
}

/**
 * Validation handler for values list import
 */
function values_import_form_validate(&$form, &$form_state) {
  if ($form_state['values']['import_type'] == 'values' && !$form_state['values']['title']) {
    form_set_error('title', 'Value set title is required');
  }
}

/**
 * Submit function for value list import.
 */
function values_import_form_submit(&$form, &$form_state) {
  if ($form_state['values']['import_type'] == 'values') {
    $value_set = new stdClass();
    $value_set->name = $form_state['values']['name'];
    $value_set->title = $form_state['values']['title'];
    $value_set->description = $form_state['values']['description'];

    // Process the key|value pairs
    $list = explode("\n", $form_state['values']['values']);
    $list = array_map('trim', $list);
    $list = array_filter($list, 'strlen');
    foreach ($list as $position => $text) {
      $value_set->data[$position]['weight'] = $position;

      // Check for an explicit key.
      $matches = array();
      if (preg_match('/(.*)\\|(.*)/', $text, $matches)) {
        $value_set->data[$position]['key'] = $matches[1];
        $value_set->data[$position]['value'] = $matches[2];
      }
      else {
        $value_set->data[$position]['value'] = $text;
        $value_set->data[$position]['key'] = values_machine_name($text);
      }
    }
  }
  else {
    $code = $form_state['values']['import'] . "\nreturn \$values;";
    $value_set = eval($code);
  }
  values_save($value_set);
  $form_state['redirect'] = 'admin/structure/values/' . $value_set->name;
}

/***********************************************************************
 *
 * THEME FUNCTIONS
 *
 ***********************************************************************/

/**
 * Theme the admin values form.
 *
 * @ingroup themeable
 */
function theme_values_value_fields($variables) {
  $form = $variables['form'];
  $headers = array(
    array(
      'data' => t('Value'),
      'class' => 'value',
    ),
    array(
      'data' => t('Remove?'),
      'class' => 'remove',
    ),
    array(
      'data' => t('Weight'),
      'class' => 'weight',
    ),
  );

  // Build table rows
  $rows = array();
  foreach (element_children($form) as $key) {

    // No need to print the field title every time
    unset($form[$key]['key']['#title'], $form[$key]['value']['#title']);
    $row = array();
    $row[] = array(
      'data' => drupal_render($form[$key]['value']) . drupal_render($form[$key]['key']),
      'class' => 'value',
    );
    $row[] = array(
      'data' => drupal_render($form[$key]['remove']),
      'class' => 'remove',
    );
    $form[$key]['weight']['#attributes']['class'] = array(
      'values-weight-group',
    );
    $row[] = drupal_render($form[$key]['weight']);
    $rows[$key] = array(
      'data' => $row,
      'class' => array(
        'draggable',
      ),
    );
  }
  drupal_add_css(drupal_get_path('module', 'values') . '/css/values.css');
  drupal_add_tabledrag('values-value-list', 'order', 'sibling', 'values-weight-group');
  return theme('table', array(
    'header' => $headers,
    'rows' => $rows,
    'attributes' => array(
      'id' => 'values-value-list',
    ),
  )) . drupal_render_children($form);
}

/***********************************************************************
 *
 * PAGE CALLBACKS
 *
 ***********************************************************************/

/**
 * Displays a list of existing value sets.
 */
function values_sets_page() {
  $header = array(
    t('Name'),
    t('Description'),
    array(
      'data' => t('Operations'),
    ),
  );
  $rows = array();

  // Get all the configured value sets and create a nice table
  $value_sets = values_load_all();
  foreach ($value_sets as $value_set) {
    $operations = array();
    if (user_access('edit value sets')) {
      $operations[] = l(t('edit'), 'admin/structure/values/' . $value_set->name);
    }
    if (user_access('delete value sets')) {
      $delete_link = l(t('delete'), 'admin/structure/values/' . $value_set->name . '/delete');

      // If ctools is installed we need to handle the delete link specially.
      if (module_exists('ctools')) {
        if ($value_set->export_type == 2) {
          $delete_link = '';
        }
        if ($value_set->export_type == 3) {
          $delete_link = l(t('revert'), 'admin/structure/values/' . $value_set->name . '/revert');
        }
      }
      if ($delete_link) {
        $operations[] = $delete_link;
      }
    }
    if (user_access('export value sets')) {
      $operations[] = l(t('export'), 'admin/structure/values/' . $value_set->name . '/export');
    }
    $rows[] = array(
      $value_set->title,
      $value_set->description,
      implode(' | ', $operations),
    );
  }
  $table['values_sets'] = array(
    '#theme' => 'table',
    '#header' => $header,
    '#rows' => $rows,
    '#empty' => t('No values available. !link', array(
      '!link' => l(t('Create a value set'), 'admin/structure/values/add'),
    )),
  );
  return $table;
}

/***********************************************************************
 *
 * API FUNCTIONS
 *
 ***********************************************************************/

/**
 * Loads values object from the database.
 *
 * @param $name
 *    The name of the value set to load
 *
 * @param $reset
 *    Optional argument to force loading fresh from the db instead of from the
 *    cache. Defaults to FALSE
 *
 * @return
 *    A value set object
 */
function values_load($name, $reset = FALSE) {
  if (is_object($name)) {
    $name = $name->name;
  }
  if (module_exists('ctools')) {

    // Try using Chaos tools suite for exporting and caching
    ctools_include('export');
    $value_sets = ctools_export_load_object('values_sets', 'names', array(
      $name,
    ));
  }
  else {

    // In the absence of ctools, use our own basic static caching
    $value_sets =& drupal_static(__FUNCTION__, array(), $reset);
    if ($reset || !isset($value_sets[$name])) {
      $value_sets[$name] = db_query("SELECT * FROM {values_sets} WHERE name = :name", array(
        ':name' => $name,
      ))
        ->fetchObject();
      if (isset($value_sets[$name]->data)) {
        $value_sets[$name]->data = unserialize($value_sets[$name]->data);
      }
    }
  }
  if ($name && isset($value_sets[$name])) {
    if (!empty($value_sets[$name]->data)) {
      usort($value_sets[$name]->data, 'values_sort_by_weight');
    }
    return $value_sets[$name];
  }

  // @todo: What is this for? It was in the original d7 upgrade...
  // $node_additions = FALSE;
  // foreach ($node_additions as $property => &$value) {
  //   $name->$property = $value;
  // }
}

/**
 * Loads all value sets.
 *
 * @return
 *    An array of all value sets
 */
function values_load_all($reset = FALSE) {
  if (module_exists('ctools')) {

    // Try using Chaos tools suite for exporting and caching
    ctools_include('export');
    $value_sets = ctools_export_load_object('values_sets');
  }
  else {
    $query = db_query('SELECT name FROM {values_sets} ORDER BY title ASC');
    foreach ($query as $record) {
      $value_sets[$record->name] = values_load($record->name, $reset);
    }
  }
  return $value_sets;
}

/**
 * Saves a values object to the database.
 *
 * @param $value_set
 *    A value set object with the following properties
 *      - name: The machine name of the value set
 *      - description: The human readable name of the value set
 *      - values: An array of arrays of value data:
 *        'values' => array(
 *          array(
 *            'key' => ... The key for the value,
 *            'value' => ... The value,
 *            'weight' => ... The value weight,
 *          ),
 *        ),
 */
function values_save($value_set) {
  foreach ($value_set->data as $key => $data) {
    if (!strlen($data['value'])) {
      unset($value_set->data[$key]);
    }
  }
  usort($value_set->data, 'values_sort_by_weight');

  // Is this an update?
  if (db_query("SELECT * FROM {values_sets} WHERE name = :name", array(
    ':name' => $value_set->name,
  ))
    ->fetchObject()) {
    $update = 'name';
  }
  else {
    $update = array();
  }

  // Write to the database
  if ($success = drupal_write_record('values_sets', $value_set, $update)) {
    drupal_set_message(t('Value set !title was saved.', array(
      '!title' => $value_set->title,
    )));
  }
}

/**
 * Deletes a value set from the database.
 *
 * @param $name
 *    The Machine name of the value set to delete
 */
function values_delete($name) {
  if (is_object($name)) {
    $name = $name->name;
  }

  // TODO Please review the conversion of this statement to the D7 database API syntax.

  /* db_query("DELETE FROM {values_sets} WHERE name = '%s'", $name) */
  db_delete('values_sets')
    ->condition('name', $name)
    ->execute();
}

/**
 * Load a value set's values formatted as an options array
 * @param  string $name
 *         The machine name of the value list to load
 * @return array
 *         An array of key => value pairs
 */
function values_load_options($name) {
  $value_set = values_load($name);
  $options = array();
  if (!empty($value_set->data)) {
    foreach ($value_set->data as $delta => $value) {
      $options[$value['key']] = t('@value', array(
        '@value' => values_translate($name, $value['key'], $value['value']),
      ));
    }
  }
  return $options;
}

/***********************************************************************
 *
 * HELPER FUNCTIONS -- PUBLIC
 *
 ***********************************************************************/

/**
 * Return a translated value if capable and a translation exists.
 */
function values_translate($set, $key, $string, $options = array()) {
  global $language;
  $options += array(
    'langcode' => $language->language,
  );
  return function_exists('i18n_string') ? i18n_string('values:' . $set . ':' . $key . ':value', $string, $options) : $string;
}

/**
 * Sort list of values by weight. Meant for uasort()
 */
function values_sort_by_weight($a, $b) {
  $a_weight = is_array($a) && isset($a['weight']) ? $a['weight'] : 0;
  $b_weight = is_array($b) && isset($b['weight']) ? $b['weight'] : 0;
  if ($a_weight == $b_weight) {
    return 0;
  }
  return $a_weight < $b_weight ? -1 : 1;
}

/**
 * Converts a string to a machine name by stripping out non-alphanumeric
 * characters (except underscores)
 */
function values_machine_name($value) {
  $machine_name = trim(preg_replace('/[^a-z0-9]+/', '_', strtolower($value)), '_');
  return $machine_name;
}

/**
 * Page title callback for value sets
 */
function value_set_page_title($value_set) {
  return $value_set->title;
}

/**
 * Access callback for the revert page
 */
function values_revert_access($value_set) {
  $access = FALSE;
  if (module_exists('ctools') && $value_set->export_type > 2) {
    $access = TRUE;
  }
  return $access;
}

/**
 * Access callback for the delete page
 */
function values_delete_access($value_set) {
  $access = user_access('delete value sets');
  if (module_exists('ctools') && $value_set->export_type > 1) {
    $access = FALSE;
  }
  return $access;
}

/***********************************************************************
 *
 * HELPER FUNCTIONS -- PRIVATE
 *
 ***********************************************************************/

/**
 * Render API callback: Checks if the value set machine name is taken.
 *
 * @param $value
 *   The machine name.
 *
 * @return
 *   Whether or not the field machine name is taken.
 */
function _values_name_exists($value) {
  $value_set = values_load($value);
  if ($value_set) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Render API callback: Keys for values don't have to be unique so we just
 * return false so that it's always accepted
 *
 * @param $value
 *   The machine name.
 *
 * @return
 *   Whether or not the field machine name is taken.
 */
function _values_value_key_exists($value) {
  return FALSE;
}

/**
 * Clears an error against one form element.
 *
 * @param $name
 *   The name of the form element as defined in form_set_error.
 */
function _values_form_unset_error($name) {
  $errors =& drupal_static('form_set_error', array());
  $removed_messages = array();
  if (isset($errors[$name])) {
    $removed_messages[] = $errors[$name];
    unset($errors[$name]);
  }
  $_SESSION['messages']['error'] = array_diff($_SESSION['messages']['error'], $removed_messages);
  if (!$_SESSION['messages']['error']) {
    unset($_SESSION['messages']['error']);
  }
}

Functions

Namesort descending Description
theme_values_value_fields Theme the admin values form.
values_add_more_submit Submit handler to add more values to a value set. This handler is used when javascript is not available. It makes changes to the form state and the entire form is rebuilt during the page reload.
values_ctools_plugin_api Implements hook_ctools_plugin_api().
values_delete Deletes a value set from the database.
values_delete_access Access callback for the delete page
values_delete_confirm Confirmation form to delete a value object from the database.
values_delete_confirm_submit Calls deletion of a value object.
values_export Export a values list.
values_export_values Export a value list and display it in a form.
values_form Form for adding a new value set.
values_form_js Menu callback for AJAX additions.
values_form_submit Submits the values form.
values_form_validate Validates the values form
values_form_value_after_build Alter the 'value' elements after the form is built
values_form_value_validate Validates the values form value element (type: machine_name)
values_i18n_string_info Implements hook_i18n_string_info().
values_i18n_string_list Implements hook_i18n_string_list().
values_import_form Import a value list
values_import_form_after_build Alter the 'value' elements after the form is built
values_import_form_name_validate Validation handler for values list import
values_import_form_submit Submit function for value list import.
values_import_form_validate Validation handler for values list import
values_load Loads values object from the database.
values_load_all Loads all value sets.
values_load_options Load a value set's values formatted as an options array
values_machine_name Converts a string to a machine name by stripping out non-alphanumeric characters (except underscores)
values_menu Implements hook_menu().
values_permission Implements hook_permission().
values_revert_access Access callback for the revert page
values_save Saves a values object to the database.
values_sets_page Displays a list of existing value sets.
values_sort_by_weight Sort list of values by weight. Meant for uasort()
values_theme Implements hook_theme().
values_translate Return a translated value if capable and a translation exists.
value_set_page_title Page title callback for value sets
_values_form_unset_error Clears an error against one form element.
_values_name_exists Render API callback: Checks if the value set machine name is taken.
_values_value_key_exists Render API callback: Keys for values don't have to be unique so we just return false so that it's always accepted