- Patch #639466 by yched: fixed hook_options_list() and XSS filtering, and added more tests.
parent
7e6fdd8540
commit
13984a7394
|
@ -5,5 +5,5 @@ package = Core - fields
|
|||
version = VERSION
|
||||
core = 7.x
|
||||
files[]=list.module
|
||||
files[]=list.test
|
||||
files[]=tests/list.test
|
||||
required = TRUE
|
||||
|
|
|
@ -115,7 +115,7 @@ function list_field_settings_form($field, $instance, $has_data) {
|
|||
'#required' => FALSE,
|
||||
'#rows' => 10,
|
||||
'#description' => '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and must be a %type value. The label is optional, and the key will be used as the label if no label is specified.', array('%type' => $field['type'] == 'list_text' ? t('text') : t('numeric'))) . '</p>',
|
||||
'#element_validate' => array('list_allowed_values_validate'),
|
||||
'#element_validate' => array('list_allowed_values_setting_validate'),
|
||||
'#list_field_type' => $field['type'],
|
||||
'#access' => empty($settings['allowed_values_function']),
|
||||
);
|
||||
|
@ -143,99 +143,11 @@ function list_field_settings_form($field, $instance, $has_data) {
|
|||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_create_field().
|
||||
*/
|
||||
function list_field_create_field($field) {
|
||||
if (array_key_exists($field['type'], list_field_info())) {
|
||||
// Clear the static cache of allowed values for $field.
|
||||
$allowed_values = &drupal_static('list_allowed_values', array());
|
||||
unset($allowed_values[$field['field_name']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update_field().
|
||||
*/
|
||||
function list_field_update_field($field, $prior_field, $has_data) {
|
||||
if (array_key_exists($field['type'], list_field_info())) {
|
||||
// Clear the static cache of allowed values for $field.
|
||||
$allowed_values = &drupal_static('list_allowed_values', array());
|
||||
unset($allowed_values[$field['field_name']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of allowed values for this field.
|
||||
*/
|
||||
function list_allowed_values($field) {
|
||||
// This static cache must be cleared whenever $field['field_name']
|
||||
// changes. This includes when it is created because a different
|
||||
// field with the same name may have previously existed, as well
|
||||
// as when it is updated.
|
||||
$allowed_values = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
if (isset($allowed_values[$field['field_name']])) {
|
||||
return $allowed_values[$field['field_name']];
|
||||
}
|
||||
|
||||
$allowed_values[$field['field_name']] = array();
|
||||
|
||||
$function = $field['settings']['allowed_values_function'];
|
||||
if (!empty($function) && function_exists($function)) {
|
||||
$allowed_values[$field['field_name']] = $function($field);
|
||||
}
|
||||
elseif (!empty($field['settings']['allowed_values'])) {
|
||||
$allowed_values[$field['field_name']] = list_allowed_values_list($field['settings']['allowed_values'], $field['type'] == 'list');
|
||||
}
|
||||
|
||||
return $allowed_values[$field['field_name']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of the allowed values for this field.
|
||||
*
|
||||
* Explode a string with keys and labels separated with '|' and with each new
|
||||
* value on its own line.
|
||||
*
|
||||
* @param $string_values
|
||||
* The list of choices as a string.
|
||||
* @param $position_keys
|
||||
* Boolean value indicating whether to generate keys based on the position of
|
||||
* the value if a key is not manually specified, effectively generating
|
||||
* integer-based keys. This should only be TRUE for fields that have a type of
|
||||
* "list". Otherwise the value will be used as the key if not specified.
|
||||
*/
|
||||
function list_allowed_values_list($string_values, $position_keys = FALSE) {
|
||||
$allowed_values = array();
|
||||
|
||||
$list = explode("\n", $string_values);
|
||||
$list = array_map('trim', $list);
|
||||
$list = array_filter($list, 'strlen');
|
||||
foreach ($list as $key => $value) {
|
||||
// Sanitize the user input with a permissive filter.
|
||||
$value = field_filter_xss($value);
|
||||
|
||||
// Check for a manually specified key.
|
||||
if (strpos($value, '|') !== FALSE) {
|
||||
list($key, $value) = explode('|', $value);
|
||||
}
|
||||
// Otherwise see if we need to use the value as the key. The "list" type
|
||||
// will automatically convert non-keyed lines to integers.
|
||||
elseif (!$position_keys) {
|
||||
$key = $value;
|
||||
}
|
||||
$allowed_values[$key] = (isset($value) && $value !== '') ? $value : $key;
|
||||
}
|
||||
|
||||
return $allowed_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Element validate callback; check that the entered values are valid.
|
||||
*/
|
||||
function list_allowed_values_validate($element, &$form_state) {
|
||||
$values = list_allowed_values_list($element['#value'], $element['#list_field_type'] == 'list');
|
||||
function list_allowed_values_setting_validate($element, &$form_state) {
|
||||
$values = list_extract_allowed_values($element['#value'], $element['#list_field_type'] == 'list');
|
||||
$field_type = $element['#list_field_type'];
|
||||
|
||||
// Check that keys are valid for the field type.
|
||||
|
@ -264,6 +176,86 @@ function list_allowed_values_validate($element, &$form_state) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update_field().
|
||||
*/
|
||||
function list_field_update_field($field, $prior_field, $has_data) {
|
||||
drupal_static_reset('list_allowed_values');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set 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.
|
||||
*
|
||||
* @return
|
||||
* The array of allowed values. Keys of the array are the raw stored values
|
||||
* (integer or text), values of the array are the display aliases.
|
||||
*/
|
||||
function list_allowed_values($field) {
|
||||
$allowed_values = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
if (!isset($allowed_values[$field['id']])) {
|
||||
$values = array();
|
||||
|
||||
$function = $field['settings']['allowed_values_function'];
|
||||
if (!empty($function) && function_exists($function)) {
|
||||
$values = $function($field);
|
||||
}
|
||||
elseif (!empty($field['settings']['allowed_values'])) {
|
||||
$position_keys = $field['type'] == 'list';
|
||||
$values = list_extract_allowed_values($field['settings']['allowed_values'], $position_keys);
|
||||
}
|
||||
|
||||
$allowed_values[$field['id']] = $values;
|
||||
}
|
||||
|
||||
return $allowed_values[$field['id']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of values from a string.
|
||||
*
|
||||
* Explode a string with keys and labels separated with '|' and with each new
|
||||
* value on its own line.
|
||||
*
|
||||
* @param $string_values
|
||||
* The list of choices as a string, in the format expected by the
|
||||
* 'allowed_values' setting:
|
||||
* - Values are separated by a carriage return.
|
||||
* - Each value is in the format "value|label" or "value".
|
||||
* @param $position_keys
|
||||
* Boolean value indicating whether to generate keys based on the position of
|
||||
* the value if a key is not manually specified, effectively generating
|
||||
* integer-based keys. This should only be TRUE for fields that have a type of
|
||||
* "list". Otherwise the value will be used as the key if not specified.
|
||||
*/
|
||||
function list_extract_allowed_values($string_values, $position_keys = FALSE) {
|
||||
$values = array();
|
||||
|
||||
$list = explode("\n", $string_values);
|
||||
$list = array_map('trim', $list);
|
||||
$list = array_filter($list, 'strlen');
|
||||
foreach ($list as $key => $value) {
|
||||
// Check for a manually specified key.
|
||||
if (strpos($value, '|') !== FALSE) {
|
||||
list($key, $value) = explode('|', $value);
|
||||
}
|
||||
// Otherwise see if we need to use the value as the key. The "list" type
|
||||
// will automatically convert non-keyed lines to integers.
|
||||
elseif (!$position_keys) {
|
||||
$key = $value;
|
||||
}
|
||||
$values[$key] = (isset($value) && $value !== '') ? $value : $key;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_validate().
|
||||
*
|
||||
|
@ -294,6 +286,33 @@ function list_field_is_empty($item, $field) {
|
|||
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', 'list_text', 'list_number', 'list_boolean'),
|
||||
'options_buttons' => array('list', 'list_text', 'list_number', '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) {
|
||||
return list_allowed_values($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_info().
|
||||
*/
|
||||
|
@ -321,11 +340,11 @@ function list_field_formatter($object_type, $object, $field, $instance, $langcod
|
|||
$allowed_values = list_allowed_values($field);
|
||||
foreach ($items as $delta => $item) {
|
||||
if (isset($allowed_values[$item['value']])) {
|
||||
$output = $allowed_values[$item['value']];
|
||||
$output = field_filter_xss($allowed_values[$item['value']]);
|
||||
}
|
||||
else {
|
||||
// If no match was found in allowed values, fall back to the key.
|
||||
$output = $value;
|
||||
$output = field_filter_xss($value);
|
||||
}
|
||||
$element[$delta] = array('#markup' => $output);
|
||||
}
|
||||
|
@ -333,7 +352,7 @@ function list_field_formatter($object_type, $object, $field, $instance, $langcod
|
|||
|
||||
case 'list_key':
|
||||
foreach ($items as $delta => $item) {
|
||||
$element[$delta] = array('#markup' => $item['value']);
|
||||
$element[$delta] = array('#markup' => field_filter_xss($item['value']));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,105 +1,133 @@
|
|||
<?php
|
||||
// $Id$
|
||||
|
||||
class ListFieldTestCase extends DrupalWebTestCase {
|
||||
/**
|
||||
* @file
|
||||
* Tests for the 'List' field types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for the 'List' field types.
|
||||
*/
|
||||
class ListFieldTestCase extends FieldTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'List field',
|
||||
'description' => "Test the List field type.",
|
||||
'group' => 'Field types'
|
||||
'name' => 'List field',
|
||||
'description' => 'Test the List field type.',
|
||||
'group' => 'Field types',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test');
|
||||
|
||||
$this->card_1 = array(
|
||||
'field_name' => 'card_1',
|
||||
$this->field_name = 'field_test';
|
||||
$this->field = array(
|
||||
'field_name' => $this->field_name,
|
||||
'type' => 'list',
|
||||
'cardinality' => 1,
|
||||
'settings' => array(
|
||||
'allowed_values' => "1|One\n2|Two\n3|Three\n",
|
||||
),
|
||||
);
|
||||
$this->card_1 = field_create_field($this->card_1);
|
||||
$this->field = field_create_field($this->field);
|
||||
|
||||
$this->instance_1 = array(
|
||||
'field_name' => $this->card_1['field_name'],
|
||||
$this->instance = array(
|
||||
'field_name' => $this->field_name,
|
||||
'object_type' => 'test_entity',
|
||||
'bundle' => 'test_bundle',
|
||||
'widget' => array(
|
||||
'type' => 'options_buttons',
|
||||
),
|
||||
);
|
||||
$this->instance_1 = field_create_instance($this->instance_1);
|
||||
$this->instance = field_create_instance($this->instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that allowed values can be updated and that the updates are
|
||||
* reflected in generated forms.
|
||||
* Test that allowed values can be updated.
|
||||
*/
|
||||
function testUpdateAllowedValues() {
|
||||
$langcode = LANGUAGE_NONE;
|
||||
|
||||
// All three options appear.
|
||||
$entity = field_test_create_stub_entity();
|
||||
$form = drupal_get_form('field_test_entity_form', $entity);
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][1]), t('Option 1 exists'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][3]), t('Option 3 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
|
||||
|
||||
// Removed options do not appear.
|
||||
$this->card_1['settings']['allowed_values'] = "2|Two";
|
||||
field_update_field($this->card_1);
|
||||
$this->field['settings']['allowed_values'] = "2|Two";
|
||||
field_update_field($this->field);
|
||||
$entity = field_test_create_stub_entity();
|
||||
$form = drupal_get_form('field_test_entity_form', $entity);
|
||||
$this->assertTrue(empty($form['card_1'][LANGUAGE_NONE][1]), t('Option 1 does not exist'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(empty($form['card_1'][LANGUAGE_NONE][3]), t('Option 3 does not exist'));
|
||||
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
|
||||
|
||||
// Completely new options appear.
|
||||
$this->card_1['settings']['allowed_values'] = "10|Update\n20|Twenty";
|
||||
field_update_field($this->card_1);
|
||||
$this->field['settings']['allowed_values'] = "10|Update\n20|Twenty";
|
||||
field_update_field($this->field);
|
||||
$form = drupal_get_form('field_test_entity_form', $entity);
|
||||
$this->assertTrue(empty($form['card_1'][LANGUAGE_NONE][1]), t('Option 1 does not exist'));
|
||||
$this->assertTrue(empty($form['card_1'][LANGUAGE_NONE][2]), t('Option 2 does not exist'));
|
||||
$this->assertTrue(empty($form['card_1'][LANGUAGE_NONE][3]), t('Option 3 does not exist'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][10]), t('Option 10 exists'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][20]), t('Option 20 exists'));
|
||||
$this->assertTrue(empty($form[$this->field_name][$langcode][1]), t('Option 1 does not exist'));
|
||||
$this->assertTrue(empty($form[$this->field_name][$langcode][2]), t('Option 2 does not exist'));
|
||||
$this->assertTrue(empty($form[$this->field_name][$langcode][3]), t('Option 3 does not exist'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][10]), t('Option 10 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][20]), t('Option 20 exists'));
|
||||
|
||||
// Options are reset when a new field with the same name is created.
|
||||
field_delete_field($this->card_1['field_name']);
|
||||
unset($this->card_1['id']);
|
||||
$this->card_1['settings']['allowed_values'] = "1|One\n2|Two\n3|Three\n";
|
||||
$this->card_1 = field_create_field($this->card_1);
|
||||
$this->instance_1 = array(
|
||||
'field_name' => $this->card_1['field_name'],
|
||||
field_delete_field($this->field_name);
|
||||
unset($this->field['id']);
|
||||
$this->field['settings']['allowed_values'] = "1|One\n2|Two\n3|Three\n";
|
||||
$this->field = field_create_field($this->field);
|
||||
$this->instance = array(
|
||||
'field_name' => $this->field_name,
|
||||
'object_type' => 'test_entity',
|
||||
'bundle' => 'test_bundle',
|
||||
'widget' => array(
|
||||
'type' => 'options_buttons',
|
||||
),
|
||||
);
|
||||
$this->instance_1 = field_create_instance($this->instance_1);
|
||||
$this->instance = field_create_instance($this->instance);
|
||||
$entity = field_test_create_stub_entity();
|
||||
$form = drupal_get_form('field_test_entity_form', $entity);
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][1]), t('Option 1 exists'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(!empty($form['card_1'][LANGUAGE_NONE][3]), t('Option 3 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][1]), t('Option 1 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][2]), t('Option 2 exists'));
|
||||
$this->assertTrue(!empty($form[$this->field_name][$langcode][3]), t('Option 3 exists'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* List module UI tests.
|
||||
*/
|
||||
class ListFieldUITestCase extends FieldUITestCase {
|
||||
class ListFieldUITestCase extends FieldTestCase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'List field UI tests',
|
||||
'name' => 'List field UI',
|
||||
'description' => 'Test the List field UI functionality.',
|
||||
'group' => 'Field types',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test', 'field_ui');
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Create content type, with underscores.
|
||||
$type_name = strtolower($this->randomName(8)) . '_' .'test';
|
||||
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
|
||||
$this->type = $type->type;
|
||||
// Store a valid URL name, with hyphens instead of underscores.
|
||||
$this->hyphen_type = str_replace('_', '-', $this->type);
|
||||
|
||||
// Create random field name.
|
||||
$this->field_label = $this->randomName(8);
|
||||
$this->field_name = 'field_' . strtolower($this->randomName(8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that allowed values are properly validated in the UI.
|
||||
*/
|
||||
|
@ -126,23 +154,23 @@ class ListFieldUITestCase extends FieldUITestCase {
|
|||
$edit = array($element_name => "1|one\n" . $this->randomName(255) . "|two");
|
||||
$this->drupalPost($admin_path, $edit, t('Save settings'));
|
||||
$this->assertText("each key must be a string less than 255 characters", t('Form vaildation failed.'));
|
||||
|
||||
|
||||
// Test 'List (boolean)' field type.
|
||||
$admin_path = $this->createListFieldAndEdit('list_boolean');
|
||||
$admin_path = $this->createListFieldAndEdit('list_boolean');
|
||||
// Check that invalid option keys are rejected.
|
||||
$edit = array($element_name => "1|one\n2|two");
|
||||
$this->drupalPost($admin_path, $edit, t('Save settings'));
|
||||
$this->assertText("keys must be either 0 or 1", t('Form vaildation failed.'));
|
||||
|
||||
|
||||
//Check that missing option causes failure.
|
||||
$edit = array($element_name => "1|one");
|
||||
$this->drupalPost($admin_path, $edit, t('Save settings'));
|
||||
$this->assertText("two values are required", t('Form vaildation failed.'));
|
||||
$this->assertText("two values are required", t('Form vaildation failed.'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to create list field of a given type and get the edit page.
|
||||
*
|
||||
*
|
||||
* @param string $type
|
||||
* 'list', 'list_boolean', 'list_number', or 'list_text'
|
||||
*/
|
||||
|
@ -164,6 +192,6 @@ class ListFieldUITestCase extends FieldUITestCase {
|
|||
$admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $field_name;
|
||||
return $admin_path;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
;$Id$
|
||||
name = "List test"
|
||||
description = "Support module for the List module tests."
|
||||
core = 7.x
|
||||
package = Testing
|
||||
files[] = list_test.module
|
||||
version = VERSION
|
||||
hidden = TRUE
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for the List module tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allowed values callback.
|
||||
*/
|
||||
function list_test_allowed_values_callback($field) {
|
||||
$values = array(
|
||||
'Group 1' => array(
|
||||
0 => 'Zero',
|
||||
),
|
||||
1 => 'One',
|
||||
'Group 2' => array(
|
||||
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
|
||||
),
|
||||
);
|
||||
|
||||
return $values;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Options module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the list of options to be displayed for a field.
|
||||
*
|
||||
* Field types willing to enable one or several of the widgets defined in
|
||||
* options.module (select, radios/checkboxes, on/off checkbox) need to
|
||||
* implement this hook to specify the list of options to display in the
|
||||
* widgets.
|
||||
*
|
||||
* @param $field
|
||||
* The field definition.
|
||||
*
|
||||
* @return
|
||||
* The array of options for the field. Array keys are the values to be
|
||||
* stored, and should be of the data type (string, number...) expected by
|
||||
* the first 'column' for the field type. Array values are the labels to
|
||||
* display within the widgets. The labels should NOT be sanitized,
|
||||
* options.module takes care of sanitation according to the needs of each
|
||||
* widget. The HTML tags defined in _field_filter_xss_allowed_tags() are
|
||||
* allowed, other tags will be filtered.
|
||||
*/
|
||||
function hook_options_list($field) {
|
||||
// Sample structure.
|
||||
$options = array(
|
||||
0 => t('Zero'),
|
||||
1 => t('One'),
|
||||
2 => t('Two'),
|
||||
3 => t('Three'),
|
||||
);
|
||||
|
||||
// Sample structure with groups. Only one level of nesting is allowed. This
|
||||
// is only supported by the 'options_select' widget. Other widgets will
|
||||
// flatten the array.
|
||||
$options = array(
|
||||
t('First group') => array(
|
||||
0 => t('Zero'),
|
||||
),
|
||||
t('Second group') => array(
|
||||
1 => t('One'),
|
||||
2 => t('Two'),
|
||||
),
|
||||
3 => t('Three'),
|
||||
);
|
||||
|
||||
// In actual implementations, the array of options will most probably depend
|
||||
// on properties of the field. Example from taxonomy.module:
|
||||
$options = array();
|
||||
foreach ($field['settings']['allowed_values'] as $tree) {
|
||||
$terms = taxonomy_get_tree($tree['vid'], $tree['parent']);
|
||||
if ($terms) {
|
||||
foreach ($terms as $term) {
|
||||
$options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
|
@ -32,26 +32,32 @@ function options_theme() {
|
|||
|
||||
/**
|
||||
* Implements hook_field_widget_info().
|
||||
*
|
||||
* Field type modules willing to use those widgets should:
|
||||
* - Use hook_field_widget_info_alter() to append their field own types to the
|
||||
* list of types supported by the widgets,
|
||||
* - Implement hook_options_list() to provide the list of options.
|
||||
* See list.module.
|
||||
*/
|
||||
function options_field_widget_info() {
|
||||
return array(
|
||||
'options_select' => array(
|
||||
'label' => t('Select list'),
|
||||
'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
|
||||
'field types' => array(),
|
||||
'behaviors' => array(
|
||||
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
||||
),
|
||||
),
|
||||
'options_buttons' => array(
|
||||
'label' => t('Check boxes/radio buttons'),
|
||||
'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
|
||||
'field types' => array(),
|
||||
'behaviors' => array(
|
||||
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
||||
),
|
||||
),
|
||||
'options_onoff' => array(
|
||||
'label' => t('Single on/off checkbox'),
|
||||
'field types' => array('list_boolean'),
|
||||
'field types' => array(),
|
||||
'behaviors' => array(
|
||||
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
|
||||
),
|
||||
|
@ -66,61 +72,64 @@ function options_field_widget(&$form, &$form_state, $field, $instance, $langcode
|
|||
// Abstract over the actual field columns, to allow different field types to
|
||||
// reuse those widgets.
|
||||
$value_key = key($field['columns']);
|
||||
$multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
|
||||
// Form API 'checkboxes' do not suport 0 as an option, so we replace it with
|
||||
// a placeholder within the form workflow.
|
||||
$zero_placeholder = $instance['widget']['type'] == 'options_buttons' && $multiple;
|
||||
// Collect available options for the field.
|
||||
$options = options_get_options($field, $instance, $zero_placeholder);
|
||||
// Put current field values in shape.
|
||||
$default_value = _options_storage_to_form($items, $options, $value_key, $zero_placeholder);
|
||||
|
||||
switch ($instance['widget']['type']) {
|
||||
case 'options_select':
|
||||
$type = str_replace('options_', '', $instance['widget']['type']);
|
||||
$multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
|
||||
$required = $element['#required'];
|
||||
$properties = _options_properties($type, $multiple, $required);
|
||||
|
||||
// Prepare the list of options.
|
||||
$options = _options_get_options($field, $instance, $properties);
|
||||
|
||||
// Put current field values in shape.
|
||||
$default_value = _options_storage_to_form($items, $options, $value_key, $properties);
|
||||
|
||||
switch ($type) {
|
||||
case 'select':
|
||||
$element += array(
|
||||
'#type' => 'select',
|
||||
'#default_value' => $default_value,
|
||||
// Do not display a 'multiple' select box if there is only one option.
|
||||
'#multiple' => $multiple && count($options) > 1,
|
||||
'#options' => $options,
|
||||
'#value_key' => $value_key,
|
||||
'#element_validate' => array('options_field_widget_validate'),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'options_buttons':
|
||||
$type = $multiple ? 'checkboxes' : 'radios';
|
||||
case 'buttons':
|
||||
// If required and there is one single option, preselect it.
|
||||
if ($element['#required'] && count($options) == 1) {
|
||||
if ($required && count($options) == 1) {
|
||||
reset($options);
|
||||
$default_value = array(key($options));
|
||||
}
|
||||
$element += array(
|
||||
'#type' => $type,
|
||||
'#type' => $multiple ? 'checkboxes' : 'radios',
|
||||
// Radio buttons need a scalar value.
|
||||
'#default_value' => ($type == 'radios') ? reset($default_value) : $default_value,
|
||||
'#default_value' => $multiple ? $default_value : reset($default_value),
|
||||
'#options' => $options,
|
||||
'#zero_placeholder' => $zero_placeholder,
|
||||
'#value_key' => $value_key,
|
||||
'#element_validate' => array('options_field_widget_validate'),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'options_onoff':
|
||||
case 'onoff':
|
||||
$keys = array_keys($options);
|
||||
$off_value = (!empty($keys) && isset($keys[0])) ? $keys[0] : NULL;
|
||||
$on_value = (!empty($keys) && isset($keys[1])) ? $keys[1] : NULL;
|
||||
$off_value = array_shift($keys);
|
||||
$on_value = array_shift($keys);
|
||||
$element += array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => isset($options[$on_value]) ? $options[$on_value] : '',
|
||||
'#default_value' => (isset($default_value[0]) && $default_value[0] == $on_value) ? 1 : 0,
|
||||
'#on_value' => $on_value,
|
||||
'#off_value' => $off_value,
|
||||
'#value_key' => $value_key,
|
||||
'#element_validate' => array('options_field_widget_validate'),
|
||||
);
|
||||
// Override the title from the incoming $element.
|
||||
$element['#title'] = isset($options[$on_value]) ? $options[$on_value] : '';
|
||||
break;
|
||||
}
|
||||
|
||||
$element += array(
|
||||
'#value_key' => $value_key,
|
||||
'#element_validate' => array('options_field_widget_validate'),
|
||||
'#properties' => $properties,
|
||||
);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
@ -135,54 +144,123 @@ function options_field_widget_validate($element, &$form_state) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepares the options for a field.
|
||||
* Describes the preparation steps required by each widget.
|
||||
*/
|
||||
function options_get_options($field, $instance, $zero_placeholder) {
|
||||
// Check if there is a module hook for the option values, otherwise try
|
||||
// list_allowed_values() for an options list.
|
||||
// @todo This should be turned into a hook_options_allowed_values(), exposed
|
||||
// by options.module.
|
||||
$function = $field['module'] . '_allowed_values';
|
||||
$options = function_exists($function) ? $function($field) : (array) list_allowed_values($field);
|
||||
function _options_properties($type, $multiple, $required) {
|
||||
$base = array(
|
||||
'zero_placeholder' => FALSE,
|
||||
'filter_xss' => FALSE,
|
||||
'strip_tags' => FALSE,
|
||||
'empty_value' => FALSE,
|
||||
'optgroups' => FALSE,
|
||||
);
|
||||
|
||||
switch ($type) {
|
||||
case 'select':
|
||||
$properties = array(
|
||||
// Select boxes do not support any HTML tag.
|
||||
'strip_tags' => TRUE,
|
||||
'empty_value' => !$required,
|
||||
'optgroups' => TRUE,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'buttons':
|
||||
$properties = array(
|
||||
'filter_xss' => TRUE,
|
||||
// Form API 'checkboxes' do not suport 0 as an option, so we replace it with
|
||||
// a placeholder within the form workflow.
|
||||
'zero_placeholder' => $multiple,
|
||||
// Checkboxes do not need a 'none' choice.
|
||||
'empty_value' => !$required && !$multiple,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'onoff':
|
||||
$properties = array(
|
||||
'filter_xss' => TRUE,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $properties + $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the options for a field.
|
||||
*/
|
||||
function _options_get_options($field, $instance, $properties) {
|
||||
// Get the list of options.
|
||||
$options = (array) module_invoke($field['module'], 'options_list', $field);
|
||||
|
||||
// Sanitize the options.
|
||||
_options_prepare_options($options, $properties);
|
||||
|
||||
if (!$properties['optgroups']) {
|
||||
$options = options_array_flatten($options);
|
||||
}
|
||||
|
||||
if ($properties['empty_value']) {
|
||||
$options = array('_none' => theme('options_none', array('instance' => $instance))) + $options;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the options.
|
||||
*
|
||||
* The function is recursive to support optgroups.
|
||||
*/
|
||||
function _options_prepare_options(&$options, $properties) {
|
||||
// Substitute the '_0' placeholder.
|
||||
if ($zero_placeholder) {
|
||||
if ($properties['zero_placeholder']) {
|
||||
$values = array_keys($options);
|
||||
$labels = array_values($options);
|
||||
// Use a strict comparison, because 0 == 'any string'.
|
||||
$index = array_search(0, $values, TRUE);
|
||||
if ($index !== FALSE) {
|
||||
if ($index !== FALSE && !is_array($options[$index])) {
|
||||
$values[$index] = '_0';
|
||||
$options = array_combine($values, array_values($options));
|
||||
$options = array_combine($values, $labels);
|
||||
}
|
||||
}
|
||||
|
||||
// Add an empty choice for
|
||||
// - non required radios
|
||||
// - non required selects
|
||||
if (!$instance['required']) {
|
||||
if (($instance['widget']['type'] == 'options_buttons' && ($field['cardinality'] == 1)) || ($instance['widget']['type'] == 'options_select')) {
|
||||
$options = array('_none' => theme('options_none', array('instance' => $instance))) + $options;
|
||||
foreach ($options as $value => $label) {
|
||||
// Recurse for optgroups.
|
||||
if (is_array($label)) {
|
||||
_options_prepare_options($options[$value], $properties);
|
||||
}
|
||||
else {
|
||||
if ($properties['strip_tags']) {
|
||||
$options[$value] = strip_tags($label);
|
||||
}
|
||||
if ($properties['filter_xss']) {
|
||||
$options[$value] = field_filter_xss($label);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms stored field values into the format the widgets need.
|
||||
*/
|
||||
function _options_storage_to_form($items, $options, $column, $zero_placeholder) {
|
||||
function _options_storage_to_form($items, $options, $column, $properties) {
|
||||
$items_transposed = options_array_transpose($items);
|
||||
$values = (isset($items_transposed[$column]) && is_array($items_transposed[$column])) ? $items_transposed[$column] : array();
|
||||
|
||||
// Substitute the '_0' placeholder.
|
||||
if ($zero_placeholder) {
|
||||
if ($properties['zero_placeholder']) {
|
||||
$index = array_search('0', $values);
|
||||
if ($index !== FALSE) {
|
||||
$values[$index] = '_0';
|
||||
}
|
||||
}
|
||||
|
||||
// Discard values that are not in the current list of options.
|
||||
// Discard values that are not in the current list of options. Flatten the
|
||||
// array if needed.
|
||||
if ($properties['optgroups']) {
|
||||
$options = options_array_flatten($options);
|
||||
}
|
||||
$values = array_values(array_intersect($values, array_keys($options)));
|
||||
return $values;
|
||||
}
|
||||
|
@ -192,6 +270,7 @@ function _options_storage_to_form($items, $options, $column, $zero_placeholder)
|
|||
*/
|
||||
function _options_form_to_storage($element) {
|
||||
$values = array_values((array) $element['#value']);
|
||||
$properties = $element['#properties'];
|
||||
|
||||
// On/off checkbox: transform '0 / 1' into the 'on / off' values.
|
||||
if ($element['#type'] == 'checkbox') {
|
||||
|
@ -199,7 +278,7 @@ function _options_form_to_storage($element) {
|
|||
}
|
||||
|
||||
// Substitute the '_0' placeholder.
|
||||
if (!empty($element['#zero_placeholder'])) {
|
||||
if ($properties['zero_placeholder']) {
|
||||
$index = array_search('_0', $values);
|
||||
if ($index !== FALSE) {
|
||||
$values[$index] = 0;
|
||||
|
@ -208,9 +287,11 @@ function _options_form_to_storage($element) {
|
|||
|
||||
// Filter out the 'none' option. Use a strict comparison, because
|
||||
// 0 == 'any string'.
|
||||
$index = array_search('_none', $values, TRUE);
|
||||
if ($index !== FALSE) {
|
||||
unset($values[$index]);
|
||||
if ($properties['empty_value']) {
|
||||
$index = array_search('_none', $values, TRUE);
|
||||
if ($index !== FALSE) {
|
||||
unset($values[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we populate at least an empty value.
|
||||
|
@ -252,6 +333,29 @@ function options_array_transpose($array) {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an array of allowed values.
|
||||
*
|
||||
* @param $array
|
||||
* A single or multidimensional array.
|
||||
* @return
|
||||
* A flattened array.
|
||||
*/
|
||||
function options_array_flatten($array) {
|
||||
$result = array();
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$result += options_array_flatten($value);
|
||||
}
|
||||
else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_error().
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test');
|
||||
parent::setUp('field_test', 'list_test');
|
||||
|
||||
// Field with cardinality 1.
|
||||
$this->card_1 = array(
|
||||
|
@ -20,7 +20,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
'cardinality' => 1,
|
||||
'settings' => array(
|
||||
// Make sure that 0 works as an option.
|
||||
'allowed_values' => "0|Zero\n1|One\n2|Two\n",
|
||||
'allowed_values' => "0|Zero\n1|One\n2|Some <script>dangerous</script> & unescaped <strong>markup</strong>\n",
|
||||
),
|
||||
);
|
||||
$this->card_1 = field_create_field($this->card_1);
|
||||
|
@ -32,7 +32,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
'cardinality' => 2,
|
||||
'settings' => array(
|
||||
// Make sure that 0 works as an option.
|
||||
'allowed_values' => "0|Zero\n1|One\n2|Two\n",
|
||||
'allowed_values' => "0|Zero\n1|One\n2|Some <script>dangerous</script> & unescaped <strong>markup</strong>\n",
|
||||
),
|
||||
);
|
||||
$this->card_2 = field_create_field($this->card_2);
|
||||
|
@ -44,7 +44,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
'cardinality' => 1,
|
||||
'settings' => array(
|
||||
// Make sure that 0 works as a 'on' value'.
|
||||
'allowed_values' => "1|No\n0|Yes\n",
|
||||
'allowed_values' => "1|No\n0|Some <script>dangerous</script> & unescaped <strong>markup</strong>\n",
|
||||
),
|
||||
);
|
||||
$this->bool = field_create_field($this->bool);
|
||||
|
@ -81,6 +81,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
$this->assertNoFieldChecked("edit-card-1-$langcode-0");
|
||||
$this->assertNoFieldChecked("edit-card-1-$langcode-1");
|
||||
$this->assertNoFieldChecked("edit-card-1-$langcode-2");
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
|
||||
// Select first option.
|
||||
$edit = array("card_1[$langcode]" => 0);
|
||||
|
@ -98,7 +99,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_init, 'card_1', $langcode, array());
|
||||
|
||||
// Required radios with one option is auto-selected.
|
||||
// Check that required radios with one option is auto-selected.
|
||||
$this->card_1['settings']['allowed_values'] = '99|Only allowed value';
|
||||
field_update_field($this->card_1);
|
||||
$instance['required'] = TRUE;
|
||||
|
@ -137,6 +138,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
$this->assertNoFieldChecked("edit-card-2-$langcode--0");
|
||||
$this->assertNoFieldChecked("edit-card-2-$langcode-1");
|
||||
$this->assertNoFieldChecked("edit-card-2-$langcode-2");
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
|
||||
// Submit form: select first and third options.
|
||||
$edit = array(
|
||||
|
@ -223,6 +225,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
|
||||
$this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.'));
|
||||
|
||||
// Submit form: select first option.
|
||||
$edit = array("card_1[$langcode]" => 0);
|
||||
|
@ -248,6 +251,38 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
|
||||
// We do not have to test that a required select list with one option is
|
||||
// auto-selected because the browser does it for us.
|
||||
|
||||
// Test optgroups.
|
||||
|
||||
$this->card_1['settings']['allowed_values'] = NULL;
|
||||
$this->card_1['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
|
||||
field_update_field($this->card_1);
|
||||
$instance['required'] = FALSE;
|
||||
field_update_instance($instance);
|
||||
|
||||
// Display form: with no field data, nothing is selected
|
||||
$this->drupalGet('test-entity/' . $entity->ftid .'/edit');
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
|
||||
$this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Group 1', t('Option groups are displayed.'));
|
||||
|
||||
// Submit form: select first option.
|
||||
$edit = array("card_1[$langcode]" => 0);
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
|
||||
|
||||
// Display form: check that the right options are selected.
|
||||
$this->drupalGet('test-entity/' . $entity->ftid .'/edit');
|
||||
$this->assertOptionSelected("edit-card-1-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-1-$langcode", 2);
|
||||
|
||||
// Submit form: Unselect the option.
|
||||
$edit = array("card_1[$langcode]" => '_none');
|
||||
$this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_init, 'card_1', $langcode, array());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,6 +312,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
|
||||
$this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.'));
|
||||
|
||||
// Submit form: select first and third options.
|
||||
$edit = array("card_2[$langcode][]" => array(0 => 0, 2 => 2));
|
||||
|
@ -331,6 +367,39 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
|
||||
// We do not have to test that a required select list with one option is
|
||||
// auto-selected because the browser does it for us.
|
||||
|
||||
// Test optgroups.
|
||||
|
||||
// Use a callback function defining optgroups.
|
||||
$this->card_2['settings']['allowed_values'] = NULL;
|
||||
$this->card_2['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
|
||||
field_update_field($this->card_2);
|
||||
$instance['required'] = FALSE;
|
||||
field_update_instance($instance);
|
||||
|
||||
// Display form: with no field data, nothing is selected.
|
||||
$this->drupalGet('test-entity/' . $entity->ftid .'/edit');
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
|
||||
$this->assertRaw('Some dangerous & unescaped markup', t('Option text was properly filtered.'));
|
||||
$this->assertRaw('Group 1', t('Option groups are displayed.'));
|
||||
|
||||
// Submit form: select first option.
|
||||
$edit = array("card_2[$langcode][]" => array(0 => 0));
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
|
||||
|
||||
// Display form: check that the right options are selected.
|
||||
$this->drupalGet('test-entity/' . $entity->ftid .'/edit');
|
||||
$this->assertOptionSelected("edit-card-2-$langcode", 0);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 1);
|
||||
$this->assertNoOptionSelected("edit-card-2-$langcode", 2);
|
||||
|
||||
// Submit form: Unselect the option.
|
||||
$edit = array("card_2[$langcode][]" => array('_none' => '_none'));
|
||||
$this->drupalPost('test-entity/' . $entity->ftid .'/edit', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_init, 'card_2', $langcode, array());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,6 +427,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
|
|||
// Display form: with no field data, option is unchecked.
|
||||
$this->drupalGet('test-entity/' . $entity->ftid .'/edit');
|
||||
$this->assertNoFieldChecked("edit-bool-$langcode");
|
||||
$this->assertRaw('Some dangerous & unescaped <strong>markup</strong>', t('Option text was properly filtered.'));
|
||||
|
||||
// Submit form: check the option.
|
||||
$edit = array("bool[$langcode]" => TRUE);
|
||||
|
|
|
@ -983,16 +983,6 @@ function taxonomy_field_info() {
|
|||
|
||||
/**
|
||||
* Implements hook_field_widget_info().
|
||||
*
|
||||
* We need custom handling of multiple values because we need
|
||||
* to combine them into a options list rather than display
|
||||
* cardinality elements. We will use the field module's default
|
||||
* handling for default values.
|
||||
*
|
||||
* Callbacks can be omitted if default handing is used.
|
||||
* They're included here just so this module can be used
|
||||
* as an example for custom modules that might do things
|
||||
* differently.
|
||||
*/
|
||||
function taxonomy_field_widget_info() {
|
||||
return array(
|
||||
|
@ -1018,6 +1008,13 @@ function taxonomy_field_widget_info_alter(&$info) {
|
|||
$info['options_buttons']['field types'][] = 'taxonomy_term';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_options_list().
|
||||
*/
|
||||
function taxonomy_options_list($field) {
|
||||
return taxonomy_allowed_values($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_schema().
|
||||
*/
|
||||
|
@ -1122,12 +1119,12 @@ function taxonomy_field_formatter($object_type, $object, $field, $instance, $lan
|
|||
}
|
||||
|
||||
/**
|
||||
* Create an array of the allowed values for this field.
|
||||
* Returns the set of valid terms for a taxonomy field.
|
||||
*
|
||||
* Call the field's allowed_values function to retrieve the allowed
|
||||
* values array.
|
||||
*
|
||||
* @see _taxonomy_term_select()
|
||||
* @param $field
|
||||
* The field definition.
|
||||
* @return
|
||||
* The array of valid terms for this field, keyed by term id.
|
||||
*/
|
||||
function taxonomy_allowed_values($field) {
|
||||
$options = array();
|
||||
|
|
Loading…
Reference in New Issue