Commit cb599de3 authored by webchick's avatar webchick

Issue #2056405 by plopesc, yched: Let field types provide their support for...

Issue #2056405 by plopesc, yched: Let field types provide their support for 'default values' and associated input UI.
parent 55c154d6
......@@ -79,4 +79,88 @@ protected function getDefaultValue() {
return $this->getInstance()->getFieldDefaultValue($this->getParent());
}
/**
* {@inheritdoc}
*/
public function defaultValuesForm(array &$form, array &$form_state) {
if (empty($this->getInstance()->default_value_function)) {
$entity = $this->getParent();
$widget = $this->defaultValueWidget($form_state);
// Place the input in a separate place in the submitted values tree.
$element = array('#parents' => array('default_value_input'));
$element += $widget->form($entity, $entity->language()->id, $this, $element, $form_state);
return $element;
}
}
/**
* {@inheritdoc}
*/
public function defaultValuesFormValidate(array $element, array &$form, array &$form_state) {
$entity = $this->getParent();
$langcode = $entity->language()->id;
$widget = $this->defaultValueWidget($form_state);
// Extract the submitted value, and validate it.
$widget->extractFormValues($entity, $langcode, $this, $element, $form_state);
$violations = $this->validate();
if (count($violations)) {
// Store reported errors in $form_state.
$field_name = $this->getFieldDefinition()->getFieldName();
$field_state = field_form_get_state($element['#parents'], $field_name, $langcode, $form_state);
$field_state['constraint_violations'] = $violations;
field_form_set_state($element['#parents'], $field_name, $langcode, $form_state, $field_state);
// Assign reported errors to the correct form element.
$widget->flagErrors($entity, $langcode, $this, $element, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state) {
$entity = $this->getParent();
$langcode = $entity->language()->id;
$widget = $this->defaultValueWidget($form_state);
// Extract the submitted value, and return it as an array.
$widget->extractFormValues($entity, $langcode, $this, $element, $form_state);
return $this->getValue();
}
/**
* Returns the widget object used in default value form.
*
* @param array $form_state
* The form state of the (entire) configuration form.
*
* @return \Drupal\field\Plugin\Type\Widget\WidgetInterface
* A Widget object.
*/
protected function defaultValueWidget(array &$form_state) {
if (!isset($form_state['default_value_widget'])) {
$entity = $this->getParent();
// Force a non-required widget.
$this->getFieldDefinition()->required = FALSE;
$this->getFieldDefinition()->description = '';
// Use the widget currently configured for the 'default' form mode, or
// fallback to the default widget for the field type.
$entity_form_display = entity_get_form_display($entity->entityType(), $entity->bundle(), 'default');
$widget = $entity_form_display->getRenderer($this->getFieldDefinition()->getFieldName());
if (!$widget) {
$widget = \Drupal::service('plugin.manager.field.widget')->getInstance(array('field_definition' => $this->getFieldDefinition()));
}
$form_state['default_value_widget'] = $widget;
}
return $form_state['default_value_widget'];
}
}
......@@ -21,4 +21,53 @@ interface ConfigFieldInterface extends FieldInterface {
*/
public function getInstance();
/**
* Returns a form for the default value input.
*
* Invoked from \Drupal\field_ui\Form\FieldInstanceEditForm to allow
* administrators to configure instance-level default value.
*
* @param array $form
* The form where the settings form is being included in.
* @param array $form_state
* The form state of the (entire) configuration form.
*
* @return array
* The form definition for the field instance default value.
*/
public function defaultValuesForm(array &$form, array &$form_state);
/**
* Validates the submitted default value.
*
* Invoked from \Drupal\field_ui\Form\FieldInstanceEditForm to allow
* administrators to configure instance-level default value.
*
* @param array $element
* The default value form element.
* @param array $form
* The form where the settings form is being included in.
* @param array $form_state
* The form state of the (entire) configuration form.
*/
public function defaultValuesFormValidate(array $element, array &$form, array &$form_state);
/**
* Processes the submitted default value.
*
* Invoked from \Drupal\field_ui\Form\FieldInstanceEditForm to allow
* administrators to configure instance-level default value.
*
* @param array $element
* The default value form element.
* @param array $form
* The form where the settings form is being included in.
* @param array $form_state
* The form state of the (entire) configuration form.
*
* @return array
* The field instance default value.
*/
public function defaultValuesFormSubmit(array $element, array &$form, array &$form_state);
}
......@@ -63,9 +63,11 @@ public function getDefinitions() {
$function = $module . '_field_info';
if (function_exists($function)) {
foreach ($function() as $plugin_id => $definition) {
$definition['id'] = $plugin_id;
$definition['provider'] = $module;
$definition['list_class'] = '\Drupal\field\Plugin\field\field_type\LegacyConfigField';
$definition += array(
'id' => $plugin_id,
'provider' => $module,
'list_class' => '\Drupal\field\Plugin\field\field_type\LegacyConfigField',
);
$definitions[$plugin_id] = $definition;
}
}
......
......@@ -87,12 +87,18 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
* A Widget object.
*/
public function getInstance(array $options) {
// Fill in defaults for missing properties.
$options += array(
'configuration' => array(),
'prepare' => TRUE,
);
$configuration = $options['configuration'];
$field_definition = $options['field_definition'];
$field_type = $field_definition->getFieldType();
// Fill in default configuration if needed.
if (!isset($options['prepare']) || $options['prepare'] == TRUE) {
if ($options['prepare']) {
$configuration = $this->prepareConfiguration($field_type, $configuration);
}
......
<?php
/**
* @file
* Definition of Drupal\field_test\Plugin\field\widget\TestFieldWidgetNoDefault.
*/
namespace Drupal\field_test\Plugin\field\widget;
use Drupal\field\Annotation\FieldWidget;
use Drupal\Core\Annotation\Translation;
use Drupal\field\Plugin\Type\Widget\WidgetBase;
/**
* Plugin implementation of the 'test_field_widget_no_default' widget.
*
* @FieldWidget(
* id = "test_field_widget_no_default",
* label = @Translation("Test widget - no default"),
* field_types = {
* "test_field"
* },
* settings = {
* "test_widget_setting_multiple" = "dummy test string"
* },
* default_value = FALSE
* )
*/
class TestFieldWidgetNoDefault extends TestFieldWidget {}
......@@ -10,11 +10,8 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Entity\Field\FieldTypePluginManager;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Language\Language;
use Drupal\field\FieldInstanceInterface;
use Drupal\field\Plugin\Type\Widget\WidgetPluginManager;
use Drupal\field_ui\FieldUI;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -30,13 +27,6 @@ class FieldInstanceEditForm extends FormBase {
*/
protected $instance;
/**
* The field widget plugin manager.
*
* @var \Drupal\field\Plugin\Type\Widget\WidgetPluginManager
*/
protected $widgetManager;
/**
* The entity manager.
*
......@@ -44,27 +34,14 @@ class FieldInstanceEditForm extends FormBase {
*/
protected $entityManager;
/**
* The field type manager.
*
* @var \Drupal\Core\Entity\Field\FieldTypePluginManager
*/
protected $fieldTypeManager;
/**
* Constructs a new field instance form.
*
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
* @param \Drupal\field\Plugin\Type\Widget\WidgetPluginManager $widget_manager
* The field widget plugin manager.
* @param \Drupal\Core\Entity\Field\FieldTypePluginManager $field_type_manager
* The field type manager.
*/
public function __construct(EntityManager $entity_manager, WidgetPluginManager $widget_manager, FieldTypePluginManager $field_type_manager) {
public function __construct(EntityManager $entity_manager) {
$this->entityManager = $entity_manager;
$this->widgetManager = $widget_manager;
$this->fieldTypeManager = $field_type_manager;
}
/**
......@@ -72,9 +49,7 @@ public function __construct(EntityManager $entity_manager, WidgetPluginManager $
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.entity'),
$container->get('plugin.manager.field.widget'),
$container->get('plugin.manager.entity.field.field_type')
$container->get('plugin.manager.entity')
);
}
......@@ -94,7 +69,6 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac
$bundle = $this->instance['bundle'];
$entity_type = $this->instance['entity_type'];
$field = $this->instance->getField();
$entity_form_display = entity_get_form_display($entity_type, $bundle, 'default');
$bundles = entity_get_bundles();
drupal_set_title($this->t('%instance settings for %bundle', array(
......@@ -103,11 +77,10 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac
)), PASS_THROUGH);
$form['#field'] = $field;
$form['#entity_form_display'] = $entity_form_display;
// Create an arbitrary entity object (used by the 'default value' widget).
$ids = (object) array('entity_type' => $this->instance['entity_type'], 'bundle' => $this->instance['bundle'], 'entity_id' => NULL);
$form['#entity'] = _field_create_entity_from_ids($ids);
$form['#entity']->field_ui_default_value = TRUE;
$items = $this->getFieldItems($form['#entity'], $this->instance['field_name']);
if (!empty($field['locked'])) {
$form['locked'] = array(
......@@ -161,12 +134,17 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac
);
// Add instance settings for the field type.
$form['instance']['settings'] = $this->getFieldItem($form['#entity'], $this->instance['field_name'])->instanceSettingsForm($form, $form_state);
$form['instance']['settings'] = $items[0]->instanceSettingsForm($form, $form_state);
$form['instance']['settings']['#weight'] = 10;
// Add handling for default value if not provided by any other module.
if (field_behaviors_widget('default_value', $this->instance) == FIELD_BEHAVIOR_DEFAULT && empty($this->instance['default_value_function'])) {
$form['instance']['default_value_widget'] = $this->getDefaultValueWidget($field, $form, $form_state);
// Add handling for default value.
if ($element = $items->defaultValuesForm($form, $form_state)) {
$element += array(
'#type' => 'details',
'#title' => $this->t('Default value'),
'#description' => $this->t('The default value for this field, used when creating new content.'),
);
$form['instance']['default_value'] = $element;
}
$form['actions'] = array('#type' => 'actions');
......@@ -186,30 +164,9 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac
* {@inheritdoc}
*/
public function validateForm(array &$form, array &$form_state) {
// Take the incoming values as the $this->instance definition, so that the 'default
// value' gets validated using the instance settings being submitted.
$field_name = $this->instance['field_name'];
$entity = $form['#entity'];
$entity_form_display = $form['#entity_form_display'];
if (isset($form['instance']['default_value_widget'])) {
$element = $form['instance']['default_value_widget'];
// Extract the 'default value'.
$items = $entity->getNGEntity()->{$field_name};
$entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
$violations = $items->validate();
// Report errors.
if (count($violations)) {
$field_state = field_form_get_state($element['#parents'], $field_name, Language::LANGCODE_NOT_SPECIFIED, $form_state);
// Store reported errors in $form_state.
$field_state['constraint_violations'] = $violations;
field_form_set_state($element['#parents'], $field_name, Language::LANGCODE_NOT_SPECIFIED, $form_state, $field_state);
// Assign reported errors to the correct form element.
$entity_form_display->getRenderer($this->instance->getField()->id)->flagErrors($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
}
if (isset($form['instance']['default_value'])) {
$items = $this->getFieldItems($form['#entity'], $this->instance['field_name']);
$items->defaultValuesFormValidate($form['instance']['default_value'], $form, $form_state);
}
}
......@@ -217,20 +174,13 @@ public function validateForm(array &$form, array &$form_state) {
* {@inheritdoc}
*/
public function submitForm(array &$form, array &$form_state) {
$field_name = $this->instance['field_name'];
$entity = $form['#entity'];
$entity_form_display = $form['#entity_form_display'];
// Handle the default value.
if (isset($form['instance']['default_value_widget'])) {
$element = $form['instance']['default_value_widget'];
// Extract field values.
$items = $entity->getNGEntity()->{$field_name};
$entity_form_display->getRenderer($this->instance->getField()->id)->extractFormValues($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
$this->instance['default_value'] = $items->getValue() ?: NULL;
$default_value = array();
if (isset($form['instance']['default_value'])) {
$items = $this->getFieldItems($form['#entity'], $this->instance['field_name']);
$default_value = $items->defaultValuesFormSubmit($form['instance']['default_value'], $form, $form_state);
}
$this->instance['default_value'] = $default_value;
// Merge incoming values into the instance.
foreach ($form_state['values']['instance'] as $key => $value) {
......@@ -256,74 +206,23 @@ public function delete(array &$form, array &$form_state) {
}
/**
* Builds the default value widget for a given field instance.
*/
protected function getDefaultValueWidget($field, array &$form, &$form_state) {
$entity = $form['#entity'];
$entity_form_display = $form['#entity_form_display'];
$element = array(
'#type' => 'details',
'#title' => $this->t('Default value'),
'#tree' => TRUE,
'#description' => $this->t('The default value for this field, used when creating new content.'),
// Stick to an empty 'parents' on this form in order not to breaks widgets
// that do not use field_widget_[field|instance]() and still access
// $form_state['field'] directly.
'#parents' => array(),
);
// Adjust the instance definition used for the form element. We want a
// non-required input and no description.
$this->instance['required'] = FALSE;
$this->instance['description'] = '';
// Adjust the instance definition to use the default widget of this field type
// instead of the hidden widget.
// @todo Clean this up since we don't have $this->instance['widget'] anymore.
// see https://drupal.org/node/2028759
if ($this->instance['widget']['type'] == 'hidden') {
$field_type = $this->fieldTypeManager->getDefinition($field['type']);
$default_widget = $this->widgetManager->getDefinition($field_type['default_widget']);
$this->instance['widget'] = array(
'type' => $default_widget['id'],
'settings' => $default_widget['settings'],
'weight' => 0,
);
}
// Insert the widget. Since we do not use the "official" instance definition,
// the whole flow cannot use field_invoke_method().
$items = $entity->getNGEntity()->{$this->instance->getField()->id};
if (!empty($this->instance['default_value'])) {
$items->setValue((array) $this->instance['default_value']);
}
$element += $entity_form_display->getRenderer($this->instance->getField()->id)->form($entity, Language::LANGCODE_NOT_SPECIFIED, $items, $element, $form_state);
return $element;
}
/**
* Returns a FieldItem object for an entity.
*
* @todo Remove when all entity types extend EntityNG.
* Returns a Field object for an entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* An entity.
* @param string $field_name
* The field name.
*
* @return \Drupal\field\Plugin\Type\FieldType\ConfigFieldItemInterface
* The field item object.
* @return \Drupal\field\Plugin\Type\FieldType\ConfigFieldInterface
* The field object.
*/
protected function getFieldItem(EntityInterface $entity, $field_name) {
protected function getFieldItems(EntityInterface $entity, $field_name) {
if ($entity instanceof EntityNG) {
$item = $entity->get($field_name)->offsetGet(0);
$item = $entity->get($field_name);
}
else {
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity->entityType(), $entity->bundle());
$item = \Drupal::typedData()->create($definitions[$field_name], array(), $field_name, $entity)->offsetGet(0);
$item = \Drupal::typedData()->create($definitions[$field_name], $this->instance->default_value, $field_name, $entity);
}
return $item;
}
......
......@@ -292,8 +292,8 @@ function testDefaultValue() {
$langcode = Language::LANGCODE_NOT_SPECIFIED;
$admin_path = 'admin/structure/types/manage/' . $this->type . '/fields/' . $instance->id();
$element_id = "edit-$field_name-$langcode-0-value";
$element_name = "{$field_name}[$langcode][0][value]";
$element_id = "edit-default-value-input-$field_name-$langcode-0-value";
$element_name = "default_value_input[{$field_name}][$langcode][0][value]";
$this->drupalGet($admin_path);
$this->assertFieldById($element_id, '', 'The default value widget was empty.');
......@@ -322,15 +322,11 @@ function testDefaultValue() {
$instance = field_info_instance('node', $field_name, $this->type);
$this->assertEqual($instance['default_value'], NULL, 'The default value was correctly saved.');
// Change the widget to TestFieldWidgetNoDefault.
// Check that the default widget is used when the field is hidden.
entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')
->setComponent($field_name, array(
'type' => 'test_field_widget_no_default',
))
->save();
->removeComponent($field_name)->save();
$this->drupalGet($admin_path);
$this->assertNoFieldById($element_id, '', 'No default value was possible for widget that disables default value.');
$this->assertFieldById($element_id, '', 'The default value widget was displayed when field is hidden.');
}
/**
......
......@@ -30,6 +30,7 @@ function file_field_info() {
'default_widget' => 'file_generic',
'default_formatter' => 'file_default',
'class' => '\Drupal\file\Type\FileItem',
'list_class' => '\Drupal\file\Type\FileField',
),
);
}
......
......@@ -24,8 +24,7 @@
* },
* settings = {
* "progress_indicator" = "throbber"
* },
* default_value = FALSE
* }
* )
*/
class FileWidget extends WidgetBase {
......
<?php
/**
* @file
* Contains \Drupal\file\Type\FileField.
*/
namespace Drupal\file\Type;
use Drupal\field\Plugin\field\field_type\LegacyConfigField;
/**
* Represents a configurable entity file field.
*/
class FileField extends LegacyConfigField {
/**
* {@inheritdoc}
*/
public function defaultValuesForm(array &$form, array &$form_state) { }
}
......@@ -49,6 +49,7 @@ function image_field_info() {
'default_widget' => 'image_image',
'default_formatter' => 'image',
'class' => '\Drupal\image\Type\ImageItem',
'list_class' => '\Drupal\image\Type\ImageField',
),
);
}
......
......@@ -9,7 +9,6 @@
use Drupal\field\Annotation\FieldWidget;
use Drupal\Core\Annotation\Translation;
use Drupal\field\Plugin\Type\Widget\WidgetBase;
use Drupal\file\Plugin\field\widget\FileWidget;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Field\FieldInterface;
......@@ -26,8 +25,7 @@
* settings = {
* "progress_indicator" = "throbber",
* "preview_image_style" = "thumbnail",
* },
* default_value = FALSE
* }
* )
*/
class ImageWidget extends FileWidget {
......
<?php
/**
* @file
* Contains \Drupal\image\Type\ImageField.
*/
namespace Drupal\image\Type;
use Drupal\field\Plugin\field\field_type\LegacyConfigField;
/**
* Represents a configurable entity image field.
*/
class ImageField extends LegacyConfigField {
/**
* {@inheritdoc}
*/
public function defaultValuesForm(array &$form, array &$form_state) { }