Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,157 @@
# Schema for the configuration files of the Options module.
field.storage_settings.list_integer:
type: mapping
label: 'List (integer) settings'
mapping:
allowed_values:
type: sequence
label: 'Allowed values list'
sequence:
type: mapping
label: 'Allowed value with label'
mapping:
value:
type: integer
label: 'Value'
label:
type: label
label: 'Label'
allowed_values_function:
type: string
label: 'Allowed values function'
field.field_settings.list_integer:
label: 'List (integer) settings'
type: mapping
field.value.list_integer:
type: mapping
label: 'Default value'
mapping:
value:
type: integer
label: 'Value'
field.storage_settings.list_float:
type: mapping
label: 'List (float) settings'
mapping:
allowed_values:
type: sequence
label: 'Allowed values list'
sequence:
type: mapping
label: 'Allowed value with label'
mapping:
value:
type: float
label: 'Value'
label:
type: label
label: 'Label'
allowed_values_function:
type: string
label: 'Allowed values function'
field.field_settings.list_float:
label: 'List (float) settings'
type: mapping
field.value.list_float:
type: mapping
label: 'Default value'
mapping:
value:
type: string
label: 'Value'
field.storage_settings.list_string:
type: mapping
label: 'List (text) settings'
mapping:
allowed_values:
type: sequence
label: 'Allowed values list'
sequence:
type: mapping
label: 'Allowed value with label'
mapping:
value:
type: string
label: 'Value'
label:
type: label
label: 'Label'
allowed_values_function:
type: string
label: 'Allowed values function'
field.field_settings.list_string:
label: 'List (text) settings'
type: mapping
field.value.list_string:
type: mapping
label: 'Default value'
mapping:
value:
type: string
label: 'Value'
field.formatter.settings.list_default:
type: mapping
label: 'Options list default display settings'
field.formatter.settings.list_key:
type: mapping
label: 'Key format settings'
field.widget.settings.options_buttons:
type: mapping
label: 'Check boxes/radio buttons format settings'
field.widget.settings.options_select:
type: mapping
label: 'Select list format settings'
views.argument.number_list_field:
type: views.argument.numeric
mapping:
summary:
type: mapping
label: 'Display a summary'
mapping:
sort_order:
type: string
label: 'Sort order'
number_of_records:
type: integer
label: 'Sort by'
format:
type: string
label: 'Format'
human:
type: boolean
views.argument.string_list_field:
type: views.argument.string
mapping:
summary:
type: mapping
label: 'Display a summary'
mapping:
sort_order:
type: string
label: 'Sort order'
number_of_records:
type: integer
label: 'Sort by'
format:
type: string
label: 'Format'
human:
type: boolean
views.filter.list_field:
type: views.filter.many_to_one

View file

@ -0,0 +1,103 @@
<?php
/**
* @file
* Hooks provided by the Options module.
*/
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Alters the list of options to be displayed for a field.
*
* This hook can notably be used to change the label of the empty option.
*
* @param array $options
* The array of options for the field, as returned by
* \Drupal\Core\TypedData\OptionsProviderInterface::getSettableOptions(). An
* empty option (_none) might have been added, depending on the field
* properties.
*
* @param array $context
* An associative array containing:
* - field_definition: The field definition
* (\Drupal\Core\Field\FieldDefinitionInterface).
* - entity: The entity object the field is attached to
* (\Drupal\Core\Entity\EntityInterface).
*
* @ingroup hooks
* @see hook_options_list()
*/
function hook_options_list_alter(array &$options, array $context) {
// Check if this is the field we want to change.
if ($context['field']->id() == 'field_option') {
// Change the label of the empty option.
$options['_none'] = t('== Empty ==');
}
}
/**
* Provide the allowed values for a 'list_*' field.
*
* Callback for options_allowed_values().
*
* 'list_*' fields can specify a callback to define the set of their allowed
* values using the 'allowed_values_function' storage setting.
*
* That function will be called:
* - either in the context of a specific entity, which is then provided as the
* $entity parameter,
* - or for the field generally without the context of any specific entity or
* entity bundle (typically, Views needing a list of values for an exposed
* filter), in which case the $entity parameter is NULL.
* This lets the callback restrict the set of allowed values or adjust the
* labels depending on some conditions on the containing entity.
*
* For consistency, the set of values returned when an $entity is provided
* should be a subset of the values returned when no $entity is provided.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
* The field storage definition.
* @param \Drupal\Core\Entity\FieldableEntityInterface|null $entity
* (optional) The entity context if known, or NULL if the allowed values are
* being collected without the context of a specific entity.
* @param bool &$cacheable
* (optional) If an $entity is provided, the $cacheable parameter should be
* modified by reference and set to FALSE if the set of allowed values
* returned was specifically adjusted for that entity and cannot not be reused
* for other entities. Defaults to TRUE.
*
* @return array
* 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. If $entity
* is NULL, you should return the list of all the possible allowed values in
* any context so that other code (e.g. Views filters) can support the allowed
* values for all possible entities and bundles.
*
* @ingroup callbacks
* @see options_allowed_values()
* @see options_test_allowed_values_callback()
* @see options_test_dynamic_values_callback()
*/
function callback_allowed_values_function(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL, &$cacheable = TRUE) {
if (isset($entity) && ($entity->bundle() == 'not_a_programmer')) {
$values = array(
1 => 'One',
2 => 'Two',
);
}
else {
$values = array(
'Group 1' => array(
0 => 'Zero',
1 => 'One',
),
'Group 2' => array(
2 => 'Two',
),
);
}
return $values;
}

View file

@ -0,0 +1,9 @@
name: Options
type: module
description: 'Defines selection, check box and radio button widgets for text and numeric fields.'
package: Field types
version: VERSION
core: 8.x
dependencies:
- field
- text

View file

@ -0,0 +1,138 @@
<?php
/**
* @file
* Defines selection, check box and radio button widgets for text and numeric fields.
*/
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\field\FieldStorageConfigInterface;
/**
* Implements hook_help().
*/
function options_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.options':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Options module allows you to create fields where data values are selected from a fixed list of options. Usually these items are entered through a select list, checkboxes, or radio buttons. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href="!options_do">online documentation for the Options module</a>.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => (\Drupal::moduleHandler()->moduleExists('field_ui')) ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#', '!options_do' => 'https://www.drupal.org/documentation/modules/options')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Managing and displaying list fields') . '</dt>';
$output .= '<dd>' . t('The <em>settings</em> and the <em>display</em> of the list fields can be configured separately. See the <a href="!field_ui">Field UI help</a> for more information on how to manage fields and their display.', array('!field_ui' => (\Drupal::moduleHandler()->moduleExists('field_ui')) ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#')) . '</dd>';
$output .= '<dt>' . t('Defining option keys and labels') . '</dt>';
$output .= '<dd>' . t('When you define the list options you can define a key and a label for each option in the list. The label will be shown to the users while the key gets stored in the database.') . '</dd>';
$output .= '<dt>' . t('Choosing list field type') . '</dt>';
$output .= '<dd>' . t('There are three types of list fields, which store different types of data: <em>float</em>, <em>integer</em> or, <em>text</em>. The <em>float</em> type allows storing approximate decimal values. The <em>integer</em> type allows storing whole numbers, such as years (for example, 2012) or values (for example, 1, 2, 5, 305). The <em>text</em> list field type allows storing text values. No matter which type of list field you choose, you can define whatever labels you wish for data entry.') . '</dd>';
$output .= '</dl>';
return $output;
}
}
/**
* Implements hook_ENTITY_TYPE_update() for 'field_storage_config'.
*/
function options_field_storage_config_update(FieldStorageConfigInterface $field_storage) {
drupal_static_reset('options_allowed_values');
}
/**
* Implements hook_ENTITY_TYPE_delete() for 'field_storage_config'.
*/
function options_field_storage_config_delete(FieldStorageConfigInterface $field_storage) {
drupal_static_reset('options_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 \Drupal\Core\Field\AllowedTagsXssTrait::fieldFilterXss()
* before being displayed.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
* The field storage definition.
* @param \Drupal\Core\Entity\FieldableEntityInterface|NULL $entity
* (optional) The specific entity when this function is called from the
* context of a specific field on a specific entity. This allows custom
* 'allowed_values_function' callbacks to either restrict the values or
* customize the labels for particular bundles and entities. NULL when
* there is not a specific entity available, such as for Views filters.
*
* @return array
* 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.
*
* @see callback_allowed_values_function()
*/
function options_allowed_values(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL) {
$allowed_values = &drupal_static(__FUNCTION__, array());
$cache_keys = array($definition->getTargetEntityTypeId(), $definition->getName());
if ($entity) {
$cache_keys[] = 'entity';
}
$cache_id = implode(':', $cache_keys);
if (!isset($allowed_values[$cache_id])) {
$function = $definition->getSetting('allowed_values_function');
// If $cacheable is FALSE, then the allowed values are not statically
// cached. See options_test_dynamic_values_callback() for an example of
// generating dynamic and uncached values.
$cacheable = TRUE;
if (!empty($function)) {
$values = $function($definition, $entity, $cacheable);
}
else {
$values = $definition->getSetting('allowed_values');
}
if ($cacheable) {
$allowed_values[$cache_id] = $values;
}
else {
return $values;
}
}
return $allowed_values[$cache_id];
}
/**
* Implements hook_field_storage_config_update_forbid().
*/
function options_field_storage_config_update_forbid(FieldStorageConfigInterface $field_storage, FieldStorageConfigInterface $prior_field_storage) {
if ($field_storage->getTypeProvider() == 'options' && $field_storage->hasData()) {
// Forbid any update that removes allowed values with actual data.
$allowed_values = $field_storage->getSetting('allowed_values');
$prior_allowed_values = $prior_field_storage->getSetting('allowed_values');
$lost_keys = array_keys(array_diff_key($prior_allowed_values, $allowed_values));
if (_options_values_in_use($field_storage->getTargetEntityTypeId(), $field_storage->getName(), $lost_keys)) {
throw new FieldStorageDefinitionUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field_storage->getName())));
}
}
}
/**
* Checks if a list of values are being used in actual field values.
*/
function _options_values_in_use($entity_type, $field_name, $values) {
if ($values) {
$factory = \Drupal::service('entity.query');
$result = $factory->get($entity_type)
->condition($field_name . '.value', $values, 'IN')
->count()
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($result) {
return TRUE;
}
}
return FALSE;
}

View file

@ -0,0 +1,39 @@
<?php
/**
* @file
* Provide Views data for options.module.
*
* @ingroup views_module_handlers
*/
use Drupal\field\FieldStorageConfigInterface;
/**
* Implements hook_field_views_data().
*
* Views integration for list fields. Have a different filter handler and
* argument handlers for list fields. This should allow to select values of
* the list.
*/
function options_field_views_data(FieldStorageConfigInterface $field) {
$data = views_field_default_views_data($field);
foreach ($data as $table_name => $table_data) {
foreach ($table_data as $field_name => $field_data) {
if (isset($field_data['filter']) && $field_name != 'delta') {
$data[$table_name][$field_name]['filter']['id'] = 'list_field';
}
if (isset($field_data['argument']) && $field_name != 'delta') {
if ($field->getType() == 'list_string') {
$data[$table_name][$field_name]['argument']['id'] = 'string_list_field';
}
else {
$data[$table_name][$field_name]['argument']['id'] = 'number_list_field';
}
}
}
}
return $data;
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldFormatter\OptionsDefaultFormatter.
*/
namespace Drupal\options\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\OptGroup;
/**
* Plugin implementation of the 'list_default' formatter.
*
* @FieldFormatter(
* id = "list_default",
* label = @Translation("Default"),
* field_types = {
* "list_integer",
* "list_float",
* "list_string",
* }
* )
*/
class OptionsDefaultFormatter extends FormatterBase {
use AllowedTagsXssTrait;
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
// Only collect allowed options if there are actually items to display.
if ($items->count()) {
$provider = $items->getFieldDefinition()
->getFieldStorageDefinition()
->getOptionsProvider('value', $items->getEntity());
// Flatten the possible options, to support opt groups.
$options = OptGroup::flattenOptions($provider->getPossibleOptions());
foreach ($items as $delta => $item) {
$value = $item->value;
// If the stored value is in the current set of allowed values, display
// the associated label, otherwise just display the raw value.
$output = isset($options[$value]) ? $options[$value] : $value;
$elements[$delta] = array('#markup' => $this->fieldFilterXss($output));
}
}
return $elements;
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldFormatter\OptionsKeyFormatter.
*/
namespace Drupal\options\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
/**
* Plugin implementation of the 'list_key' formatter.
*
* @FieldFormatter(
* id = "list_key",
* label = @Translation("Key"),
* field_types = {
* "list_integer",
* "list_float",
* "list_string",
* }
* )
*/
class OptionsKeyFormatter extends FormatterBase {
use AllowedTagsXssTrait;
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
$elements[$delta] = array('#markup' => $this->fieldFilterXss($item->value));
}
return $elements;
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldType\ListFloatItem.
*/
namespace Drupal\options\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the 'list_float' field type.
*
* @FieldType(
* id = "list_float",
* label = @Translation("List (float)"),
* description = @Translation("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."),
* category = @Translation("Number"),
* default_widget = "options_select",
* default_formatter = "list_default",
* )
*/
class ListFloatItem extends ListItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('float')
->setLabel(t('Float value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'float',
),
),
'indexes' => array(
'value' => array('value'),
),
);
}
/**
* {@inheritdoc}
*/
protected function allowedValuesDescription() {
$description = '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label.');
$description .= '<br/>' . t('The key is the stored value, and must be numeric. The label will be used in displayed values and edit forms.');
$description .= '<br/>' . t('The label is optional: if a line contains a single number, it will be used as key and label.');
$description .= '<br/>' . t('Lists of labels are also accepted (one label per line), only if the field does not hold any values yet. Numeric keys will be automatically generated from the positions in the list.');
$description .= '</p>';
$description .= '<p>' . t('Allowed HTML tags in labels: @tags', array('@tags' => $this->displayAllowedTags())) . '</p>';
return $description;
}
/**
* {@inheritdoc}
*/
protected static function extractAllowedValues($string, $has_data) {
$values = parent::extractAllowedValues($string, $has_data);
if ($values) {
$keys = array_keys($values);
$labels = array_values($values);
$keys = array_map(function ($key) {
// Float keys are represented as strings and need to be disambiguated
// ('.5' is '0.5').
return is_numeric($key) ? (string) (float) $key : $key;
}, $keys);
return array_combine($keys, $labels);
}
}
/**
* {@inheritdoc}
*/
protected static function validateAllowedValue($option) {
if (!is_numeric($option)) {
return t('Allowed values list: each key must be a valid integer or decimal.');
}
}
/**
* {@inheritdoc}
*/
public static function simplifyAllowedValues(array $structured_values) {
$values = array();
foreach ($structured_values as $item) {
// Nested elements are embedded in the label.
if (is_array($item['label'])) {
$item['label'] = static::simplifyAllowedValues($item['label']);
}
// Cast the value to a float first so that .5 and 0.5 are the same value
// and then cast to a string so that values like 0.5 can be used as array
// keys.
// @see http://php.net/manual/en/language.types.array.php
$values[(string) (float) $item['value']] = $item['label'];
}
return $values;
}
/**
* {@inheritdoc}
*/
protected static function castAllowedValue($value) {
return (float) $value;
}
}

View file

@ -0,0 +1,83 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldType\ListIntegerItem.
*/
namespace Drupal\options\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the 'list_integer' field type.
*
* @FieldType(
* id = "list_integer",
* label = @Translation("List (integer)"),
* description = @Translation("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."),
* category = @Translation("Number"),
* default_widget = "options_select",
* default_formatter = "list_default",
* )
*/
class ListIntegerItem extends ListItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('integer')
->setLabel(t('Integer value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
),
),
'indexes' => array(
'value' => array('value'),
),
);
}
/**
* {@inheritdoc}
*/
protected function allowedValuesDescription() {
$description = '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label.');
$description .= '<br/>' . t('The key is the stored value, and must be numeric. The label will be used in displayed values and edit forms.');
$description .= '<br/>' . t('The label is optional: if a line contains a single number, it will be used as key and label.');
$description .= '<br/>' . t('Lists of labels are also accepted (one label per line), only if the field does not hold any values yet. Numeric keys will be automatically generated from the positions in the list.');
$description .= '</p>';
$description .= '<p>' . t('Allowed HTML tags in labels: @tags', array('@tags' => $this->displayAllowedTags())) . '</p>';
return $description;
}
/**
* {@inheritdoc}
*/
protected static function validateAllowedValue($option) {
if (!preg_match('/^-?\d+$/', $option)) {
return t('Allowed values list: keys must be integers.');
}
}
/**
* {@inheritdoc}
*/
protected static function castAllowedValue($value) {
return (int) $value;
}
}

View file

@ -0,0 +1,337 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldType\ListItemBase.
*/
namespace Drupal\options\Plugin\Field\FieldType;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\OptionsProviderInterface;
/**
* Plugin base class inherited by the options field types.
*/
abstract class ListItemBase extends FieldItemBase implements OptionsProviderInterface {
use AllowedTagsXssTrait;
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'allowed_values' => array(),
'allowed_values_function' => '',
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public function getPossibleValues(AccountInterface $account = NULL) {
// Flatten options firstly, because Possible Options may contain group
// arrays.
$flatten_options = OptGroup::flattenOptions($this->getPossibleOptions($account));
return array_keys($flatten_options);
}
/**
* {@inheritdoc}
*/
public function getPossibleOptions(AccountInterface $account = NULL) {
return $this->getSettableOptions($account);
}
/**
* {@inheritdoc}
*/
public function getSettableValues(AccountInterface $account = NULL) {
// Flatten options firstly, because Settable Options may contain group
// arrays.
$flatten_options = OptGroup::flattenOptions($this->getSettableOptions($account));
return array_keys($flatten_options);
}
/**
* {@inheritdoc}
*/
public function getSettableOptions(AccountInterface $account = NULL) {
$allowed_options = options_allowed_values($this->getFieldDefinition()->getFieldStorageDefinition(), $this->getEntity());
return $allowed_options;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
// @todo Implement this once https://www.drupal.org/node/2238085 lands.
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
return empty($this->value) && (string) $this->value !== '0';
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$allowed_values = $this->getSetting('allowed_values');
$allowed_values_function = $this->getSetting('allowed_values_function');
$element['allowed_values'] = array(
'#type' => 'textarea',
'#title' => t('Allowed values list'),
'#default_value' => $this->allowedValuesString($allowed_values),
'#rows' => 10,
'#access' => empty($allowed_values_function),
'#element_validate' => array(array(get_class($this), 'validateAllowedValues')),
'#field_has_data' => $has_data,
'#field_name' => $this->getFieldDefinition()->getName(),
'#entity_type' => $this->getEntity()->getEntityTypeId(),
'#allowed_values' => $allowed_values,
);
$element['allowed_values']['#description'] = $this->allowedValuesDescription();
$element['allowed_values_function'] = 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' => $allowed_values_function)),
'#access' => !empty($allowed_values_function),
'#value' => $allowed_values_function,
);
return $element;
}
/**
* Provides the field type specific allowed values form element #description.
*
* @return string
* The field type allowed values form specific description.
*/
abstract protected function allowedValuesDescription();
/**
* #element_validate callback for options field allowed values.
*
* @param $element
* An associative array containing the properties and children of the
* generic form element.
* @param $form_state
* The current state of the form for the form this element belongs to.
*
* @see \Drupal\Core\Render\Element\FormElement::processPattern()
*/
public static function validateAllowedValues($element, FormStateInterface $form_state) {
$values = static::extractAllowedValues($element['#value'], $element['#field_has_data']);
if (!is_array($values)) {
$form_state->setError($element, t('Allowed values list: invalid input.'));
}
else {
// Check that keys are valid for the field type.
foreach ($values as $key => $value) {
if ($error = static::validateAllowedValue($key)) {
$form_state->setError($element, $error);
break;
}
}
// Prevent removing values currently in use.
if ($element['#field_has_data']) {
$lost_keys = array_keys(array_diff_key($element['#allowed_values'], $values));
if (_options_values_in_use($element['#entity_type'], $element['#field_name'], $lost_keys)) {
$form_state->setError($element, t('Allowed values list: some values are being removed while currently in use.'));
}
}
$form_state->setValueForElement($element, $values);
}
}
/**
* Extracts the allowed values array from the allowed_values element.
*
* @param string $string
* The raw string to extract values from.
* @param bool $has_data
* The current field already has data inserted or not.
*
* @return array|null
* The array of extracted key/value pairs, or NULL if the string is invalid.
*
* @see \Drupal\options\Plugin\Field\FieldType\ListTextItem::allowedValuesString()
*/
protected static function extractAllowedValues($string, $has_data) {
$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) {
// Check for an explicit key.
$matches = array();
if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
$key = trim($matches[1]);
$value = trim($matches[2]);
$explicit_keys = TRUE;
}
// Otherwise see if we can use the value as the key.
elseif (!static::validateAllowedValue($text)) {
$key = $value = $text;
$explicit_keys = TRUE;
}
// Otherwise see if we can generate a key from the position.
elseif (!$has_data) {
$key = (string) $position;
$value = $text;
$generated_keys = TRUE;
}
else {
return;
}
$values[$key] = $value;
}
// We generate keys only if the list contains no explicit key at all.
if ($explicit_keys && $generated_keys) {
return;
}
return $values;
}
/**
* Checks whether a candidate allowed value is valid.
*
* @param string $option
* The option value entered by the user.
*
* @return string
* The error message if the specified value is invalid, NULL otherwise.
*/
protected static function validateAllowedValue($option) { }
/**
* Generates a string representation of an array of 'allowed values'.
*
* This string format is suitable for edition in a textarea.
*
* @param array $values
* An array of values, where array keys are values and array values are
* labels.
*
* @return string
* The string representation of the $values array:
* - Values are separated by a carriage return.
* - Each value is in the format "value|label" or "value".
*/
protected function allowedValuesString($values) {
$lines = array();
foreach ($values as $key => $value) {
$lines[] = "$key|$value";
}
return implode("\n", $lines);
}
/**
* @inheritdoc.
*/
public static function storageSettingsToConfigData(array $settings) {
if (isset($settings['allowed_values'])) {
$settings['allowed_values'] = static::structureAllowedValues($settings['allowed_values']);
}
return $settings;
}
/**
* @inheritdoc.
*/
public static function storageSettingsFromConfigData(array $settings) {
if (isset($settings['allowed_values'])) {
$settings['allowed_values'] = static::simplifyAllowedValues($settings['allowed_values']);
}
return $settings;
}
/**
* Simplifies allowed values to a key-value array from the structured array.
*
* @param array $structured_values
* Array of items with a 'value' and 'label' key each for the allowed
* values.
*
* @return array
* Allowed values were the array key is the 'value' value, the value is
* the 'label' value.
*
* @see Drupal\options\Plugin\Field\FieldType\ListItemBase::structureAllowedValues()
*/
protected static function simplifyAllowedValues(array $structured_values) {
$values = array();
foreach ($structured_values as $item) {
if (is_array($item['label'])) {
// Nested elements are embedded in the label.
$item['label'] = static::simplifyAllowedValues($item['label']);
}
$values[$item['value']] = $item['label'];
}
return $values;
}
/**
* Creates a structured array of allowed values from a key-value array.
*
* @param array $values
* Allowed values were the array key is the 'value' value, the value is
* the 'label' value.
*
* @return array
* Array of items with a 'value' and 'label' key each for the allowed
* values.
*
* @see Drupal\options\Plugin\Field\FieldType\ListItemBase::simplifyAllowedValues()
*/
protected static function structureAllowedValues(array $values) {
$structured_values = array();
foreach ($values as $value => $label) {
if (is_array($label)) {
$label = static::structureAllowedValues($label);
}
$structured_values[] = array(
'value' => static::castAllowedValue($value),
'label' => $label,
);
}
return $structured_values;
}
/**
* Converts a value to the correct type.
*
* @param mixed $value
* The value to cast.
*
* @return mixed
* The casted value.
*/
protected static function castAllowedValue($value) {
return $value;
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\Field\FieldType\ListStringItem.
*/
namespace Drupal\options\Plugin\Field\FieldType;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Plugin implementation of the 'list_string' field type.
*
* @FieldType(
* id = "list_string",
* label = @Translation("List (text)"),
* description = @Translation("This field stores text values from a list of allowed 'value => label' pairs, i.e. 'US States': IL => Illinois, IA => Iowa, IN => Indiana."),
* category = @Translation("Text"),
* default_widget = "options_select",
* default_formatter = "list_default",
* )
*/
class ListStringItem extends ListItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Text value'))
->addConstraint('Length', array('max' => 255))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => 255,
),
),
'indexes' => array(
'value' => array('value'),
),
);
}
/**
* {@inheritdoc}
*/
protected function allowedValuesDescription() {
$description = '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label.');
$description .= '<br/>' . t('The key is the stored value. The label will be used in displayed values and edit forms.');
$description .= '<br/>' . t('The label is optional: if a line contains a single string, it will be used as key and label.');
$description .= '</p>';
$description .= '<p>' . t('Allowed HTML tags in labels: @tags', array('@tags' => $this->displayAllowedTags())) . '</p>';
return $description;
}
/**
* {@inheritdoc}
*/
protected static function validateAllowedValue($option) {
if (Unicode::strlen($option) > 255) {
return t('Allowed values list: each key must be a string at most 255 characters long.');
}
}
/**
* {@inheritdoc}
*/
protected static function castAllowedValue($value) {
return (string) $value;
}
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\views\argument\NumberListField.
*/
namespace Drupal\options\Plugin\views\argument;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\argument\NumericArgument;
/**
* Argument handler for list field to show the human readable name in the
* summary.
*
* @ingroup views_argument_handlers
*
* @ViewsArgument("number_list_field")
*/
class NumberListField extends NumericArgument {
use AllowedTagsXssTrait;
use FieldAPIHandlerTrait;
/**
* Stores the allowed values of this field.
*
* @var array
*/
protected $allowedValues = NULL;
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$field_storage = $this->getFieldStorageDefinition();
$this->allowedValues = options_allowed_values($field_storage);
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['summary']['contains']['human'] = ['default' => FALSE];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['summary']['human'] = [
'#title' => $this->t('Display list value as human readable'),
'#type' => 'checkbox',
'#default_value' => $this->options['summary']['human'],
'#states' => [
'visible' => [
':input[name="options[default_action]"]' => ['value' => 'summary'],
],
],
];
}
/**
* {@inheritdoc}
*/
public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it.
if (isset($this->allowedValues[$value]) && !empty($this->options['summary']['human'])) {
return $this->fieldFilterXss($this->allowedValues[$value]);
}
// Else, fallback to the key.
else {
return SafeMarkup::checkPlain($value);
}
}
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\views\argument\StringListField.
*/
namespace Drupal\options\Plugin\views\argument;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\argument\StringArgument;
use Drupal\Component\Utility\SafeMarkup;
/**
* Argument handler for list field to show the human readable name in summary.
*
* @ingroup views_argument_handlers
*
* @ViewsArgument("string_list_field")
*/
class StringListField extends StringArgument {
use AllowedTagsXssTrait;
use FieldAPIHandlerTrait;
/**
* Stores the allowed values of this field.
*
* @var array
*/
protected $allowedValues = NULL;
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$field_storage = $this->getFieldStorageDefinition();
$this->allowedValues = options_allowed_values($field_storage);
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['summary']['contains']['human'] = ['default' => FALSE];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['summary']['human'] = [
'#title' => $this->t('Display list value as human readable'),
'#type' => 'checkbox',
'#default_value' => $this->options['summary']['human'],
'#states' => [
'visible' => [
':input[name="options[default_action]"]' => ['value' => 'summary'],
],
],
];
}
/**
* {@inheritdoc}
*/
public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it.
if (isset($this->allowedValues[$value]) && !empty($this->options['summary']['human'])) {
return $this->caseTransform($this->fieldFilterXss($this->allowedValues[$value]), $this->options['case']);
}
// Else, fallback to the key.
else {
return $this->caseTransform(SafeMarkup::checkPlain($value), $this->options['case']);
}
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\options\Plugin\views\filter\ListField.
*/
namespace Drupal\options\Plugin\views\filter;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\filter\ManyToOne;
use Drupal\views\ViewExecutable;
/**
* Filter handler which uses list-fields as options.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("list_field")
*/
class ListField extends ManyToOne {
use FieldAPIHandlerTrait;
/**
* {@inheritdoc}
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$field_storage = $this->getFieldStorageDefinition();
// Set valueOptions here so getValueOptions() will just return it.
$this->valueOptions = options_allowed_values($field_storage);
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsDynamicValuesApiTest.
*/
namespace Drupal\options\Tests;
/**
* Tests the options allowed values api.
*
* @group options
*/
class OptionsDynamicValuesApiTest extends OptionsDynamicValuesTestBase {
/**
* Tests options_allowed_values().
*
* @see options_test_dynamic_values_callback()
*/
public function testOptionsAllowedValues() {
// Test allowed values without passed $items.
$values = options_allowed_values($this->fieldStorage);
$this->assertEqual([], $values);
$values = options_allowed_values($this->fieldStorage, $this->entity);
$expected_values = array(
$this->entity->label(),
$this->entity->url(),
$this->entity->uuid(),
$this->entity->bundle(),
);
$expected_values = array_combine($expected_values, $expected_values);
$this->assertEqual($expected_values, $values);
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsDynamicValuesTestBase.
*/
namespace Drupal\options\Tests;
use Drupal\field\Tests\FieldTestBase;
/**
* Base class for testing allowed values of options fields.
*/
abstract class OptionsDynamicValuesTestBase extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['options', 'entity_test', 'options_test'];
/**
* The created entity.
*
* @var \Drupal\Core\Entity\Entity
*/
protected $entity;
/**
* The field storage.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorage;
protected function setUp() {
parent::setUp();
$field_name = 'test_options';
$this->fieldStorage = entity_create('field_storage_config', [
'field_name' => $field_name,
'entity_type' => 'entity_test_rev',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
]);
$this->fieldStorage->save();
$this->field = entity_create('field_config', [
'field_name' => $field_name,
'entity_type' => 'entity_test_rev',
'bundle' => 'entity_test_rev',
'required' => TRUE,
])->save();
entity_get_form_display('entity_test_rev', 'entity_test_rev', 'default')
->setComponent($field_name, [
'type' => 'options_select',
])
->save();
// Create an entity and prepare test data that will be used by
// options_test_dynamic_values_callback().
$values = [
'user_id' => mt_rand(1, 10),
'name' => $this->randomMachineName(),
];
$this->entity = entity_create('entity_test_rev', $values);
$this->entity->save();
$this->test = [
'label' => $this->entity->label(),
'uuid' => $this->entity->uuid(),
'bundle' => $this->entity->bundle(),
'uri' => $this->entity->url(),
];
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsDynamicValuesValidationTest.
*/
namespace Drupal\options\Tests;
/**
* Tests the Options field allowed values function.
*
* @group options
*/
class OptionsDynamicValuesValidationTest extends OptionsDynamicValuesTestBase {
/**
* Test that allowed values function gets the entity.
*/
function testDynamicAllowedValues() {
// Verify that validation passes against every value we had.
foreach ($this->test as $key => $value) {
$this->entity->test_options->value = $value;
$violations = $this->entity->test_options->validate();
$this->assertEqual(count($violations), 0, "$key is a valid value");
}
// Now verify that validation does not pass against anything else.
foreach ($this->test as $key => $value) {
$this->entity->test_options->value = is_numeric($value) ? (100 - $value) : ('X' . $value);
$violations = $this->entity->test_options->validate();
$this->assertEqual(count($violations), 1, "$key is not a valid value");
}
}
}

View file

@ -0,0 +1,100 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsFieldTest.
*/
namespace Drupal\options\Tests;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
/**
* Tests for the 'Options' field types.
*
* @group options
*/
class OptionsFieldTest extends OptionsFieldUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('options');
/**
* Test that allowed values can be updated.
*/
function testUpdateAllowedValues() {
// All three options appear.
$entity = entity_create('entity_test');
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists');
// Use one of the values in an actual entity, and check that this value
// cannot be removed from the list.
$entity = entity_create('entity_test');
$entity->{$this->fieldName}->value = 1;
$entity->save();
$this->fieldStorage->setSetting('allowed_values', [2 => 'Two']);
try {
$this->fieldStorage->save();
$this->fail(t('Cannot update a list field storage to not include keys with existing data.'));
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
$this->pass(t('Cannot update a list field storage to not include keys with existing data.'));
}
// Empty the value, so that we can actually remove the option.
unset($entity->{$this->fieldName});
$entity->save();
// Removed options do not appear.
$this->fieldStorage->setSetting('allowed_values', [2 => 'Two']);
$this->fieldStorage->save();
$entity = entity_create('entity_test');
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists');
$this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist');
// Completely new options appear.
$this->fieldStorage->setSetting('allowed_values', [10 => 'Update', 20 => 'Twenty']);
$this->fieldStorage->save();
// The entity holds an outdated field object with the old allowed values
// setting, so we need to reinitialize the entity object.
$entity = entity_create('entity_test');
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist');
$this->assertTrue(empty($form[$this->fieldName]['widget'][2]), 'Option 2 does not exist');
$this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][10]), 'Option 10 exists');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][20]), 'Option 20 exists');
// Options are reset when a new field with the same name is created.
$this->fieldStorage->delete();
entity_create('field_storage_config', $this->fieldStorageDefinition)->save();
entity_create('field_config', array(
'field_name' => $this->fieldName,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
))->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->fieldName, array(
'type' => 'options_buttons',
))
->save();
$entity = entity_create('entity_test');
$form = \Drupal::service('entity.form_builder')->getForm($entity);
$this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists');
$this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists');
// Test the generateSampleValue() method.
$entity = entity_create('entity_test');
$entity->{$this->fieldName}->generateSampleItems();
$this->entityValidateAndSave($entity);
}
}

View file

@ -0,0 +1,358 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsFieldUITest.
*/
namespace Drupal\options\Tests;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\FieldTestBase;
/**
* Tests the Options field UI functionality.
*
* @group options
*/
class OptionsFieldUITest extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'options', 'field_test', 'taxonomy', 'field_ui');
/**
* The name of the created content type.
*
* @var string
*/
protected $typeName;
/**
* Machine name of the created content type.
*
* @var string
*/
protected $type;
/**
* Name of the option field.
*
* @var string
*/
protected $fieldName;
/**
* Admin path to manage field storage settings.
*
* @var string
*/
protected $adminPath;
protected function setUp() {
parent::setUp();
// Create test user.
$admin_user = $this->drupalCreateUser(['access content', 'administer taxonomy', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'bypass node access', 'administer node fields', 'administer node display']);
$this->drupalLogin($admin_user);
// Create content type, with underscores.
$this->typeName = 'test_' . strtolower($this->randomMachineName());
$type = $this->drupalCreateContentType(['name' => $this->typeName, 'type' => $this->typeName]);
$this->type = $type->id();
}
/**
* Options (integer) : test 'allowed values' input.
*/
function testOptionsAllowedValuesInteger() {
$this->fieldName = 'field_options_integer';
$this->createOptionsField('list_integer');
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit integer keys.
$string = "0|Zero\n2|Two";
$array = array('0' => 'Zero', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-integer keys.
$this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
$this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
'type' => $this->type,
$this->fieldName => array(array('value' => 1)),
);
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n1|One\n2|Two";
$array = array('0' => 'Zero', '1' => 'One', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n1|One";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
$node->delete();
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
// Check that the same key can only be used once.
$string = "0|Zero\n0|One";
$array = array('0' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
}
/**
* Options (float) : test 'allowed values' input.
*/
function testOptionsAllowedValuesFloat() {
$this->fieldName = 'field_options_float';
$this->createOptionsField('list_float');
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('0' => 'Zero', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit numeric keys.
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
// Check that values can be added and removed.
$string = "0|Zero\n.5|Point five\n1.0|One";
$array = array('0' => 'Zero', '0.5' => 'Point five', '1' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Non-numeric keys.
$this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
// Mixed list of keyed and unkeyed values.
$this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
// Create a node with actual data for the field.
$settings = array(
'type' => $this->type,
$this->fieldName => array(array('value' => .5)),
);
$node = $this->drupalCreateNode($settings);
// Check that a flat list of values is rejected once the field has data.
$this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "0|Zero\n.5|Point five\n2|Two";
$array = array('0' => 'Zero', '0.5' => 'Point five', '2' => 'Two');
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "0|Zero\n.5|Point five";
$array = array('0' => 'Zero', '0.5' => 'Point five');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
$node->delete();
$string = "0|Zero";
$array = array('0' => 'Zero');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
// Check that the same key can only be used once.
$string = "0.5|Point five\n0.5|Half";
$array = array('0.5' => 'Half');
$this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
// Check that different forms of the same float value cannot be used.
$string = "0|Zero\n.5|Point five\n0.5|Half";
$array = array('0' => 'Zero', '0.5' => 'Half');
$this->assertAllowedValuesInput($string, $array, 'Different forms of the same value cannot be used.');
}
/**
* Options (text) : test 'allowed values' input.
*/
function testOptionsAllowedValuesText() {
$this->fieldName = 'field_options_text';
$this->createOptionsField('list_string');
// Flat list of textual values.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
// Explicit keys.
$string = "zero|Zero\none|One";
$array = array('zero' => 'Zero', 'one' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
// Check that values can be added and removed.
$string = "zero|Zero\ntwo|Two";
$array = array('zero' => 'Zero', 'two' => 'Two');
$this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
// Mixed list of keyed and unkeyed values.
$string = "zero|Zero\nOne\n";
$array = array('zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
// Overly long keys.
$this->assertAllowedValuesInput("zero|Zero\n" . $this->randomMachineName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
// Create a node with actual data for the field.
$settings = array(
'type' => $this->type,
$this->fieldName => array(array('value' => 'One')),
);
$node = $this->drupalCreateNode($settings);
// Check that flat lists of values are still accepted once the field has
// data.
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
// Check that values can be added but values in use cannot be removed.
$string = "Zero\nOne\nTwo";
$array = array('Zero' => 'Zero', 'One' => 'One', 'Two' => 'Two');
$this->assertAllowedValuesInput($string, $array, 'Values can be added.');
$string = "Zero\nOne";
$array = array('Zero' => 'Zero', 'One' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
$this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
// Delete the node, remove the value.
$node->delete();
$string = "Zero";
$array = array('Zero' => 'Zero');
$this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
// Check that string values with dots can be used.
$string = "Zero\nexample.com|Example";
$array = array('Zero' => 'Zero', 'example.com' => 'Example');
$this->assertAllowedValuesInput($string, $array, 'String value with dot is supported.');
// Check that the same key can only be used once.
$string = "zero|Zero\nzero|One";
$array = array('zero' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Same value cannot be used multiple times.');
}
/**
* Options (text) : test 'trimmed values' input.
*/
function testOptionsTrimmedValuesText() {
$this->fieldName = 'field_options_trimmed_text';
$this->createOptionsField('list_string');
// Explicit keys.
$string = "zero |Zero\none | One";
$array = array('zero' => 'Zero', 'one' => 'One');
$this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted and trimmed.');
}
/**
* Helper function to create list field of a given type.
*
* @param string $type
* 'list_integer', 'list_float' or 'list_string'
*/
protected function createOptionsField($type) {
// Create a field.
entity_create('field_storage_config', array(
'field_name' => $this->fieldName,
'entity_type' => 'node',
'type' => $type,
))->save();
entity_create('field_config', array(
'field_name' => $this->fieldName,
'entity_type' => 'node',
'bundle' => $this->type,
))->save();
entity_get_form_display('node', $this->type, 'default')->setComponent($this->fieldName)->save();
$this->adminPath = 'admin/structure/types/manage/' . $this->type . '/fields/node.' . $this->type . '.' . $this->fieldName . '/storage';
}
/**
* Tests a string input for the 'allowed values' form element.
*
* @param $input_string
* The input string, in the pipe-linefeed format expected by the form
* element.
* @param $result
* Either an expected resulting array in
* $field->getSetting('allowed_values'), or an expected error message.
* @param $message
* Message to display.
*/
function assertAllowedValuesInput($input_string, $result, $message) {
$edit = array('settings[allowed_values]' => $input_string);
$this->drupalPostForm($this->adminPath, $edit, t('Save field settings'));
$this->assertNoRaw('&amp;lt;', 'The page does not have double escaped HTML tags.');
if (is_string($result)) {
$this->assertText($result, $message);
}
else {
$field_storage = FieldStorageConfig::loadByName('node', $this->fieldName);
$this->assertIdentical($field_storage->getSetting('allowed_values'), $result, $message);
}
}
/**
* Tests normal and key formatter display on node display.
*/
function testNodeDisplay() {
$this->fieldName = strtolower($this->randomMachineName());
$this->createOptionsField('list_integer');
$node = $this->drupalCreateNode(array('type' => $this->type));
$on = $this->randomMachineName();
$off = $this->randomMachineName();
$edit = array(
'settings[allowed_values]' =>
"1|$on
0|$off",
);
$this->drupalPostForm($this->adminPath, $edit, t('Save field settings'));
$this->assertText(format_string('Updated field !field_name field settings.', array('!field_name' => $this->fieldName)), "The 'On' and 'Off' form fields work for boolean fields.");
// Select a default value.
$edit = array(
$this->fieldName => '1',
);
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
// Check the node page and see if the values are correct.
$file_formatters = array('list_default', 'list_key');
foreach ($file_formatters as $formatter) {
$edit = array(
"fields[$this->fieldName][type]" => $formatter,
);
$this->drupalPostForm('admin/structure/types/manage/' . $this->typeName . '/display', $edit, t('Save'));
$this->drupalGet('node/' . $node->id());
if ($formatter == 'list_default') {
$output = $on;
}
else {
$output = '1';
}
$elements = $this->xpath('//div[text()="' . $output . '"]');
$this->assertEqual(count($elements), 1, 'Correct options found.');
}
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsFieldUnitTestBase.
*/
namespace Drupal\options\Tests;
use Drupal\field\Tests\FieldUnitTestBase;
/**
* Base class for Options module integration tests.
*/
abstract class OptionsFieldUnitTestBase extends FieldUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('options');
/**
* The field name used in the test.
*
* @var string
*/
protected $fieldName = 'test_options';
/**
* The field storage definition used to created the field storage.
*
* @var array
*/
protected $fieldStorageDefinition;
/**
* The list field storage used in the test.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $fieldStorage;
/**
* The list field used in the test.
*
* @var \Drupal\field\Entity\FieldConfig
*/
protected $field;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('router.builder')->rebuild();
$this->fieldStorageDefinition = array(
'field_name' => $this->fieldName,
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => array(
'allowed_values' => array(1 => 'One', 2 => 'Two', 3 => 'Three'),
),
);
$this->fieldStorage = entity_create('field_storage_config', $this->fieldStorageDefinition);
$this->fieldStorage->save();
$this->field = entity_create('field_config', array(
'field_storage' => $this->fieldStorage,
'bundle' => 'entity_test',
));
$this->field->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->fieldName, array(
'type' => 'options_buttons',
))
->save();
}
}

View file

@ -0,0 +1,77 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsFloatFieldImportTest.
*/
namespace Drupal\options\Tests;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\FieldTestBase;
/**
* Tests option fields can be updated and created through config synchronization.
*
* @group options
*/
class OptionsFloatFieldImportTest extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'options', 'field_ui', 'config', 'options_config_install_test');
protected function setUp() {
parent::setUp();
// Create test user.
$admin_user = $this->drupalCreateUser(array('synchronize configuration', 'access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'bypass node access', 'administer node fields', 'administer node display'));
$this->drupalLogin($admin_user);
}
/**
* Tests that importing list_float fields works.
*/
public function testImport() {
$field_name = 'field_options_float';
$type = 'options_install_test';
// Test the results on installing options_config_install_test. All the
// necessary configuration for this test is created by installing that
// module.
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
$admin_path = 'admin/structure/types/manage/' . $type . '/fields/node.' . $type . '.' . $field_name . '/storage';
// Export active config to staging
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
// Set the active to not use dots in the allowed values key names.
$edit = array('settings[allowed_values]' => "0|Zero\n1|One");
$this->drupalPostForm($admin_path, $edit, t('Save field settings'));
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '1' => 'One'));
// Import configuration with dots in the allowed values key names. This
// tests \Drupal\Core\Config\Entity\ConfigEntityStorage::importUpdate().
$this->drupalGet('admin/config/development/configuration');
$this->drupalPostForm(NULL, array(), t('Import all'));
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
// Delete field to test creation. This tests
// \Drupal\Core\Config\Entity\ConfigEntityStorage::importCreate().
FieldConfig::loadByName('node', $type, $field_name)->delete();
$this->drupalGet('admin/config/development/configuration');
$this->drupalPostForm(NULL, array(), t('Import all'));
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsFormattersTest.
*/
namespace Drupal\options\Tests;
/**
* Tests the Options field type formatters.
*
* @group options
* @see \Drupal\options\Plugin\field\formatter\OptionsDefaultFormatter
* @see \Drupal\options\Plugin\field\formatter\OptionsKeyFormatter
*/
class OptionsFormattersTest extends OptionsFieldUnitTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
}
/**
* Tests the formatters.
*/
public function testFormatter() {
$entity = entity_create('entity_test');
$entity->{$this->fieldName}->value = 1;
$items = $entity->get($this->fieldName);
$build = $items->view();
$this->assertEqual($build['#formatter'], 'list_default', 'Ensure to fall back to the default formatter.');
$this->assertEqual($build[0]['#markup'], 'One');
$build = $items->view(array('type' => 'list_key'));
$this->assertEqual($build['#formatter'], 'list_key', 'The chosen formatter is used.');
$this->assertEqual($build[0]['#markup'], 1);
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsSelectDynamicValuesTest.
*/
namespace Drupal\options\Tests;
/**
* Tests an options select with a dynamic allowed values function.
*
* @group options
*/
class OptionsSelectDynamicValuesTest extends OptionsDynamicValuesTestBase {
/**
* Tests the 'options_select' widget (single select).
*/
function testSelectListDynamic() {
// Create an entity.
$this->entity->save();
// Create a web user.
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
$this->drupalLogin($web_user);
// Display form.
$this->drupalGet('entity_test_rev/manage/' . $this->entity->id());
$options = $this->xpath('//select[@id="edit-test-options"]/option');
$this->assertEqual(count($options), count($this->test) + 1);
foreach ($options as $option) {
$value = (string) $option['value'];
if ($value != '_none') {
$this->assertTrue(array_search($value, $this->test));
}
}
}
}

View file

@ -0,0 +1,496 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\OptionsWidgetsTest.
*/
namespace Drupal\options\Tests;
use Drupal\field\Tests\FieldTestBase;
/**
* Tests the Options widgets.
*
* @group options
*/
class OptionsWidgetsTest extends FieldTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'options', 'entity_test', 'options_test', 'taxonomy', 'field_ui'];
/**
* A field storage with cardinality 1 to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $card1;
/**
* A field storage with cardinality 2 to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $card2;
protected function setUp() {
parent::setUp();
// Field storage with cardinality 1.
$this->card1 = entity_create('field_storage_config', [
'field_name' => 'card_1',
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
// Make sure that HTML entities in option text are not double-encoded.
3 => 'Some HTML encoded markup with &lt; &amp; &gt;',
],
],
]);
$this->card1->save();
// Field storage with cardinality 2.
$this->card2 = entity_create('field_storage_config', [
'field_name' => 'card_2',
'entity_type' => 'entity_test',
'type' => 'list_integer',
'cardinality' => 2,
'settings' => [
'allowed_values' => [
// Make sure that 0 works as an option.
0 => 'Zero',
1 => 'One',
// Make sure that option text is properly sanitized.
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
],
],
]);
$this->card2->save();
// Create a web user.
$this->drupalLogin($this->drupalCreateUser(['view test entity', 'administer entity_test content']));
}
/**
* Tests the 'options_buttons' widget (single select).
*/
function testRadioButtons() {
// Create an instance of the 'single value' field.
$field = entity_create('field_config', [
'field_storage' => $this->card1,
'bundle' => 'entity_test',
]);
$field->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card1->getName(), [
'type' => 'options_buttons',
])
->save();
// Create an entity.
$entity = entity_create('entity_test', [
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
$entity_init = clone $entity;
// With no field data, no buttons are checked.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertNoFieldChecked('edit-card-1-0');
$this->assertNoFieldChecked('edit-card-1-1');
$this->assertNoFieldChecked('edit-card-1-2');
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
$this->assertRaw('Some HTML encoded markup with &lt; &amp; &gt;');
// Select first option.
$edit = array('card_1' => 0);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array(0));
// Check that the selected button is checked.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFieldChecked('edit-card-1-0');
$this->assertNoFieldChecked('edit-card-1-1');
$this->assertNoFieldChecked('edit-card-1-2');
// Unselect option.
$edit = array('card_1' => '_none');
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array());
// Check that required radios with one option is auto-selected.
$this->card1->setSetting('allowed_values', [99 => 'Only allowed value']);
$this->card1->save();
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFieldChecked('edit-card-1-99');
}
/**
* Tests the 'options_buttons' widget (multiple select).
*/
function testCheckBoxes() {
// Create an instance of the 'multiple values' field.
$field = entity_create('field_config', array(
'field_storage' => $this->card2,
'bundle' => 'entity_test',
));
$field->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card2->getName(), array(
'type' => 'options_buttons',
))
->save();
// Create an entity.
$entity = entity_create('entity_test', array(
'user_id' => 1,
'name' => $this->randomMachineName(),
));
$entity->save();
$entity_init = clone $entity;
// Display form: with no field data, nothing is checked.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertNoFieldChecked('edit-card-2-0');
$this->assertNoFieldChecked('edit-card-2-1');
$this->assertNoFieldChecked('edit-card-2-2');
$this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array(
'card_2[0]' => TRUE,
'card_2[1]' => FALSE,
'card_2[2]' => TRUE,
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0, 2));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFieldChecked('edit-card-2-0');
$this->assertNoFieldChecked('edit-card-2-1');
$this->assertFieldChecked('edit-card-2-2');
// Submit form: select only first option.
$edit = array(
'card_2[0]' => TRUE,
'card_2[1]' => FALSE,
'card_2[2]' => FALSE,
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFieldChecked('edit-card-2-0');
$this->assertNoFieldChecked('edit-card-2-1');
$this->assertNoFieldChecked('edit-card-2-2');
// Submit form: select the three options while the field accepts only 2.
$edit = array(
'card_2[0]' => TRUE,
'card_2[1]' => TRUE,
'card_2[2]' => TRUE,
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array(
'card_2[0]' => FALSE,
'card_2[1]' => FALSE,
'card_2[2]' => FALSE,
);
$this->drupalPostForm(NULL, $edit, t('Save'));
// Check that the value was saved.
$this->assertFieldValues($entity_init, 'card_2', array());
// Required checkbox with one option is auto-selected.
$this->card2->setSetting('allowed_values', [99 => 'Only allowed value']);
$this->card2->save();
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFieldChecked('edit-card-2-99');
}
/**
* Tests the 'options_select' widget (single select).
*/
function testSelectListSingle() {
// Create an instance of the 'single value' field.
$field = entity_create('field_config', array(
'field_storage' => $this->card1,
'bundle' => 'entity_test',
'required' => TRUE,
));
$field->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card1->getName(), array(
'type' => 'options_select',
))
->save();
// Create an entity.
$entity = entity_create('entity_test', array(
'user_id' => 1,
'name' => $this->randomMachineName(),
));
$entity->save();
$entity_init = clone $entity;
// Display form.
$this->drupalGet('entity_test/manage/' . $entity->id());
// A required field without any value has a "none" option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1', ':label' => t('- Select a value -'))), 'A required select list has a "Select a value" choice.');
// With no field data, nothing is selected.
$this->assertNoOptionSelected('edit-card-1', '_none');
$this->assertNoOptionSelected('edit-card-1', 0);
$this->assertNoOptionSelected('edit-card-1', 1);
$this->assertNoOptionSelected('edit-card-1', 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
// Submit form: select invalid 'none' option.
$edit = array('card_1' => '_none');
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertRaw(t('!title field is required.', array('!title' => $field->getName())), 'Cannot save a required field when selecting "none" from the select list.');
// Submit form: select first option.
$edit = array('card_1' => 0);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array(0));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
// A required field with a value has no 'none' option.
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1')), 'A required select list with an actual value has no "none" choice.');
$this->assertOptionSelected('edit-card-1', 0);
$this->assertNoOptionSelected('edit-card-1', 1);
$this->assertNoOptionSelected('edit-card-1', 2);
// Make the field non required.
$field->setRequired(FALSE);
$field->save();
// Display form.
$this->drupalGet('entity_test/manage/' . $entity->id());
// A non-required field has a 'none' option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1', ':label' => t('- None -'))), 'A non-required select list has a "None" choice.');
// Submit form: Unselect the option.
$edit = array('card_1' => '_none');
$this->drupalPostForm('entity_test/manage/' . $entity->id(), $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array());
// Test optgroups.
$this->card1->setSetting('allowed_values', []);
$this->card1->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
$this->card1->save();
// Display form: with no field data, nothing is selected
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertNoOptionSelected('edit-card-1', 0);
$this->assertNoOptionSelected('edit-card-1', 1);
$this->assertNoOptionSelected('edit-card-1', 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('More &lt;script&gt;dangerous&lt;/script&gt; markup', 'Option group text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array('card_1' => 0);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array(0));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertOptionSelected('edit-card-1', 0);
$this->assertNoOptionSelected('edit-card-1', 1);
$this->assertNoOptionSelected('edit-card-1', 2);
// Submit form: Unselect the option.
$edit = array('card_1' => '_none');
$this->drupalPostForm('entity_test/manage/' . $entity->id(), $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_1', array());
}
/**
* Tests the 'options_select' widget (multiple select).
*/
function testSelectListMultiple() {
// Create an instance of the 'multiple values' field.
$field = entity_create('field_config', array(
'field_storage' => $this->card2,
'bundle' => 'entity_test',
));
$field->save();
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card2->getName(), array(
'type' => 'options_select',
))
->save();
// Create an entity.
$entity = entity_create('entity_test', array(
'user_id' => 1,
'name' => $this->randomMachineName(),
));
$entity->save();
$entity_init = clone $entity;
// Display form: with no field data, nothing is selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertOptionSelected("edit-card-2", '_none');
$this->assertNoOptionSelected('edit-card-2', 0);
$this->assertNoOptionSelected('edit-card-2', 1);
$this->assertNoOptionSelected('edit-card-2', 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
// Submit form: select first and third options.
$edit = array('card_2[]' => array(0 => 0, 2 => 2));
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0, 2));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertOptionSelected('edit-card-2', 0);
$this->assertNoOptionSelected('edit-card-2', 1);
$this->assertOptionSelected('edit-card-2', 2);
// Submit form: select only first option.
$edit = array('card_2[]' => array(0 => 0));
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertOptionSelected('edit-card-2', 0);
$this->assertNoOptionSelected('edit-card-2', 1);
$this->assertNoOptionSelected('edit-card-2', 2);
// Submit form: select the three options while the field accepts only 2.
$edit = array('card_2[]' => array(0 => 0, 1 => 1, 2 => 2));
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
// Submit form: uncheck all options.
$edit = array('card_2[]' => array());
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array());
// Test the 'None' option.
// Check that the 'none' option has no effect if actual options are selected
// as well.
$edit = array('card_2[]' => array('_none' => '_none', 0 => 0));
$this->drupalPostForm('entity_test/manage/' . $entity->id(), $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0));
// Check that selecting the 'none' option empties the field.
$edit = array('card_2[]' => array('_none' => '_none'));
$this->drupalPostForm('entity_test/manage/' . $entity->id(), $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array());
// A required select list does not have an empty key.
$field->setRequired(TRUE);
$field->save();
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2')), 'A required select list does not have an empty key.');
// 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->card2->setSetting('allowed_values', []);
$this->card2->setSetting('allowed_values_function', 'options_test_allowed_values_callback');
$this->card2->save();
$field->setRequired(FALSE);
$field->save();
// Display form: with no field data, nothing is selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertNoOptionSelected('edit-card-2', 0);
$this->assertNoOptionSelected('edit-card-2', 1);
$this->assertNoOptionSelected('edit-card-2', 2);
$this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
$this->assertRaw('More &lt;script&gt;dangerous&lt;/script&gt; markup', 'Option group text was properly filtered.');
$this->assertRaw('Group 1', 'Option groups are displayed.');
// Submit form: select first option.
$edit = array('card_2[]' => array(0 => 0));
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array(0));
// Display form: check that the right options are selected.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertOptionSelected('edit-card-2', 0);
$this->assertNoOptionSelected('edit-card-2', 1);
$this->assertNoOptionSelected('edit-card-2', 2);
// Submit form: Unselect the option.
$edit = array('card_2[]' => array('_none' => '_none'));
$this->drupalPostForm('entity_test/manage/' . $entity->id(), $edit, t('Save'));
$this->assertFieldValues($entity_init, 'card_2', array());
}
/**
* Tests the 'options_select' and 'options_button' widget for empty value.
*/
function testEmptyValue() {
// Create an instance of the 'single value' field.
$field = entity_create('field_config', [
'field_storage' => $this->card1,
'bundle' => 'entity_test',
]);
$field->save();
// Change it to the check boxes/radio buttons widget.
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card1->getName(), [
'type' => 'options_buttons',
])
->save();
// Create an entity.
$entity = entity_create('entity_test', [
'user_id' => 1,
'name' => $this->randomMachineName(),
]);
$entity->save();
// Display form: check that _none options are present and has label.
$this->drupalGet('entity_test/manage/' . $entity->id());
$this->assertTrue($this->xpath('//div[@id=:id]//input[@value=:value]', array(':id' => 'edit-card-1', ':value' => '_none')), 'A test radio button has a "None" choice.');
$this->assertTrue($this->xpath('//div[@id=:id]//label[@for=:for and text()=:label]', array(':id' => 'edit-card-1', ':for' => 'edit-card-1-none', ':label' => 'N/A')), 'A test radio button has a "N/A" choice.');
// Change it to the select widget.
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($this->card1->getName(), array(
'type' => 'options_select',
))
->save();
// Display form: check that _none options are present and has label.
$this->drupalGet('entity_test/manage/' . $entity->id());
// A required field without any value has a "none" option.
$this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1', ':label' => t('- None -'))), 'A test select has a "None" choice.');
}
}

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\Views\OptionsListArgumentTest.
*/
namespace Drupal\options\Tests\Views;
use Drupal\views\Views;
/**
* Tests options list argument for views.
*
* @see \Drupal\options\Plugin\views\argument\NumberListField.
* @group views
*/
class OptionsListArgumentTest extends OptionsTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_options_list_argument_numeric', 'test_options_list_argument_string'];
/**
* Tests the options field argument.
*/
public function testViewsTestOptionsListArgument() {
$view = Views::getView('test_options_list_argument_numeric');
$this->executeView($view, [1]);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
$view = Views::getView('test_options_list_argument_string');
$this->executeView($view, ['man', 'woman']);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\Views\OptionsListFilterTest.
*/
namespace Drupal\options\Tests\Views;
use Drupal\views\Views;
/**
* Tests options list filter for views.
*
* @see \Drupal\field\Plugin\views\filter\ListField.
* @group views
*/
class OptionsListFilterTest extends OptionsTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_options_list_filter'];
/**
* Tests options list field filter.
*/
public function testViewsTestOptionsListFilter() {
$view = Views::getView('test_options_list_filter');
$this->executeView($view);
$resultset = [
['nid' => $this->nodes[0]->nid->value],
['nid' => $this->nodes[1]->nid->value],
];
$column_map = ['nid' => 'nid'];
$this->assertIdenticalResultset($view, $resultset, $column_map);
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\Views\OptionsTestBase.
*/
namespace Drupal\options\Tests\Views;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\views\Tests\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Tests\ViewUnitTestBase;
/**
* Base class for options views tests.
*/
abstract class OptionsTestBase extends ViewUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['options', 'options_test_views', 'node', 'user', 'field'];
/**
* Stores the nodes used for the different tests.
*
* @var array
*/
protected $nodes = [];
/**
* Stores the field values used for the different tests.
*
* @var array
*/
protected $fieldValues = [];
/**
* The used field names.
*
* @var string[]
*/
protected $fieldNames;
protected function setUp() {
parent::setUp();
$this->mockStandardInstall();
ViewTestData::createTestViews(get_class($this), ['options_test_views']);
$settings = [];
$settings['type'] = 'article';
$settings['field_test_list_string'][]['value'] = $this->fieldValues[0];
$settings['field_test_list_integer'][]['value'] = 0;
$node = Node::create($settings);
$node->save();
$this->nodes[] = $node;
$node = $node->createDuplicate();
$node->save();
$this->nodes[] = $node;
}
/**
* Provides a workaround for the inability to use the standard profile.
*
* @see https://www.drupal.org/node/1708692
*/
protected function mockStandardInstall() {
$this->installEntitySchema('user');
$this->installEntitySchema('node');
NodeType::create(
['type' => 'article']
)->save();
$this->fieldValues = [
$this->randomMachineName(),
$this->randomMachineName(),
];
$this->fieldNames = ['field_test_list_string', 'field_test_list_integer'];
// Create two field entities.
FieldStorageConfig::create([
'field_name' => $this->fieldNames[0],
'entity_type' => 'node',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
$this->fieldValues[0] => $this->fieldValues[0],
$this->fieldValues[1] => $this->fieldValues[1],
],
],
])->save();
FieldStorageConfig::create([
'field_name' => $this->fieldNames[1],
'entity_type' => 'node',
'type' => 'list_integer',
'cardinality' => 1,
'settings' => [
'allowed_values' => [
$this->fieldValues[0],
$this->fieldValues[1],
],
],
])->save();
foreach ($this->fieldNames as $field_name) {
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'label' => 'Test options list field',
'bundle' => 'article',
])->save();
}
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* @file
* Contains \Drupal\options\Tests\Views\ViewsDataTest.
*/
namespace Drupal\options\Tests\Views;
/**
* Test to ensure views data is properly created for the Options module.
*
* @group views
*/
class ViewsDataTest extends OptionsTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['options', 'options_test', 'entity_test', 'views'];
/**
* The field storage.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorage;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$field_name = 'test_options';
$this->fieldStorage = entity_create('field_storage_config', [
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'list_string',
'cardinality' => 1,
'settings' => [
'allowed_values_function' => 'options_test_dynamic_values_callback',
],
]);
$this->fieldStorage->save();
$this->field = entity_create('field_config', [
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'required' => TRUE,
])->save();
}
/**
* Tests the option module's implementation of hook_field_views_data().
*/
public function testOptionsFieldViewsData() {
$field_data = \Drupal::service('views.views_data')->get('entity_test__test_options');
// Check that the options module has properly overridden default views data.
$test_options_field = $field_data['test_options_value'];
$this->assertEqual($test_options_field['argument']['id'], 'string_list_field', 'Argument handler is properly set for fields with allowed value callbacks.');
$this->assertEqual($test_options_field['filter']['id'], 'list_field', 'Filter handler is properly set for fields with allowed value callbacks.');
}
}

View file

@ -0,0 +1,56 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.options_install_test.body
- node.type.options_install_test
module:
- entity_reference
- text
id: node.options_install_test.default
targetEntityType: node
bundle: options_install_test
mode: default
content:
title:
type: string_textfield
weight: -5
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
settings:
display_label: true
weight: 15
third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
display_label: true
weight: 16
third_party_settings: { }
body:
type: text_textarea_with_summary
weight: 26
settings:
rows: 9
summary_rows: 3
placeholder: ''
third_party_settings: { }
hidden: { }
third_party_settings: { }

View file

@ -0,0 +1,25 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.options_install_test.body
- node.type.options_install_test
module:
- text
- user
id: node.options_install_test.default
targetEntityType: node
bundle: options_install_test
mode: default
content:
links:
weight: 100
body:
label: hidden
type: text_default
weight: 101
settings: { }
third_party_settings: { }
hidden:
langcode: true
third_party_settings: { }

View file

@ -0,0 +1,27 @@
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
- field.field.node.options_install_test.body
- node.type.options_install_test
module:
- text
- user
id: node.options_install_test.teaser
targetEntityType: node
bundle: options_install_test
mode: teaser
content:
links:
weight: 100
body:
label: hidden
type: text_summary_or_trimmed
weight: 101
settings:
trim_length: 600
third_party_settings: { }
hidden:
langcode: true
third_party_settings: { }

View file

@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
- node.type.options_install_test
id: node.options_install_test.body
field_name: body
entity_type: node
bundle: options_install_test
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: true
third_party_settings: { }
field_type: text_with_summary

View file

@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_options_float
- node.type.options_install_test
id: node.options_install_test.field_options_float
field_name: field_options_float
entity_type: node
bundle: options_install_test
label: field_options_float
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings: { }
third_party_settings: { }
field_type: list_float

View file

@ -0,0 +1,25 @@
langcode: en
status: true
dependencies:
module:
- node
- options
id: node.field_options_float
field_name: field_options_float
entity_type: node
type: list_float
settings:
allowed_values:
-
value: !!float 0
label: Zero
-
value: 0.5
label: 'Point five'
allowed_values_function: ''
module: options
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false

View file

@ -0,0 +1,11 @@
langcode: en
status: true
dependencies: { }
name: options_install_test
type: options_install_test
description: null
help: null
new_revision: false
preview_mode: 1
display_submitted: true
third_party_settings: { }

View file

@ -0,0 +1,9 @@
name: 'Options config install test'
type: module
description: 'Support module for the Options module tests.'
core: 8.x
package: Testing
version: VERSION
dependencies:
- node
- options

View file

@ -0,0 +1,6 @@
name: 'Options test'
type: module
description: 'Support module for the Options module tests.'
core: 8.x
package: Testing
version: VERSION

View file

@ -0,0 +1,56 @@
<?php
/**
* @file
* Helper module for the List module tests.
*/
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Implements callback_allowed_values_function().
*
* @see options_allowed_values().
*/
function options_test_allowed_values_callback(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL) {
$values = array(
'Group 1' => array(
0 => 'Zero',
),
1 => 'One',
'Group 2' => array(
2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
),
'More <script>dangerous</script> markup' => array(
3 => 'Three',
),
);
return $values;
}
/**
* Implements callback_allowed_values_function().
*
* @todo This function violates the recommendation in options_allowed_values()
* to return a list of all possible values in any context when $items is
* NULL. Since this is not yet used for testing Views integration, that is
* alright for now. Fix this in https://www.drupal.org/node/2012130.
*
* @see options_allowed_values().
*/
function options_test_dynamic_values_callback(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL, &$cacheable = NULL) {
$values = array();
if (isset($entity)) {
$cacheable = FALSE;
$values = array(
$entity->label(),
$entity->url(),
$entity->uuid(),
$entity->bundle(),
);
}
// We need the values of the entity as keys.
return array_combine($values, $values);
}

View file

@ -0,0 +1,9 @@
name: 'Options test views'
type: module
description: 'Provides default views for views options tests.'
package: Testing
version: VERSION
core: 8.x
dependencies:
- options
- views

View file

@ -0,0 +1,200 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_argument_numeric
label: 'test options list argument (numeric)'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: true
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: 'test options list argument'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
field_test_list_integer_value:
id: field_test_list_integer_value
table: field_data_field_test_list_integer
field: field_test_list_integer_value
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
items_per_page: 25
count: false
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
human: true
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
plugin_id: number_list_field
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View file

@ -0,0 +1,199 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_argument_string
label: 'test options list argument (string)'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: true
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: '0'
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: 'test options list argument'
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
field_test_list_string_value:
id: field_test_list_string_value
table: field_data_field_test_list_string
field: field_test_list_string_value
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
items_per_page: 25
count: false
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
human: true
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
plugin_id: string_list_field
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }

View file

@ -0,0 +1,204 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- user
id: test_options_list_filter
label: test_options_list_filter
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 1
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: some
options:
items_per_page: 5
offset: 0
style:
type: default
row:
type: fields
fields:
title:
id: title
table: node_field_data
field: title
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
plugin_id: field
filters:
status:
value: true
table: node_field_data
field: status
id: status
expose:
operator: ''
group: 1
plugin_id: boolean
field_test_list_string_value:
id: field_test_list_string_value
table: field_data_field_test_list_string
field: field_test_list_string_value
relationship: none
group_type: group
admin_label: ''
operator: or
value:
man: man
woman: woman
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
plugin_id: list_field
type:
id: type
table: node_field_data
field: type
relationship: none
group_type: group
admin_label: ''
operator: in
value:
article: article
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: bundle
sorts:
nid:
id: nid
table: nid
field: nid
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
plugin_id: standard
title: test_options_list_filter
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }