Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663

This commit is contained in:
Greg Anderson 2015-10-08 11:40:12 -07:00
parent eb34d130a8
commit f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions

View file

@ -10,9 +10,9 @@ namespace Drupal\Core\Field;
* Useful methods when dealing with displaying allowed tags.
*
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
* \Drupal\Core\Field\FieldFilteredString instead.
* \Drupal\Core\Field\FieldFilteredMarkup instead.
*
* @see \Drupal\Core\Field\FieldFilteredString
* @see \Drupal\Core\Field\FieldFilteredMarkup
*/
trait AllowedTagsXssTrait {
@ -24,7 +24,7 @@ trait AllowedTagsXssTrait {
*
* Used for items entered by administrators, like field descriptions, allowed
* values, where some (mainly inline) mark-up may be desired (so
* \Drupal\Component\Utility\SafeMarkup::checkPlain() is not acceptable).
* \Drupal\Component\Utility\Html::escape() is not acceptable).
*
* @param string $string
* The string with raw HTML in it.
@ -34,21 +34,21 @@ trait AllowedTagsXssTrait {
* valid UTF-8.
*/
public function fieldFilterXss($string) {
return FieldFilteredString::create($string);
return FieldFilteredMarkup::create($string);
}
/**
* Returns a list of tags allowed by AllowedTagsXssTrait::fieldFilterXss().
*/
public function allowedTags() {
return FieldFilteredString::allowedTags();
return FieldFilteredMarkup::allowedTags();
}
/**
* Returns a human-readable list of allowed tags for display in help texts.
*/
public function displayAllowedTags() {
return FieldFilteredString::displayAllowedTags();
return FieldFilteredMarkup::displayAllowedTags();
}
}

View file

@ -146,16 +146,36 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
}
/**
* Sets field settings.
* {@inheritdoc}
*
* @param array $settings
* The value to set.
* Note that the method does not unset existing settings not specified in the
* incoming $settings array.
*
* @return static
* The object itself for chaining.
* For example:
* @code
* // Given these are the default settings.
* $field_definition->getSettings() === [
* 'fruit' => 'apple',
* 'season' => 'summer',
* ];
* // Change only the 'fruit' setting.
* $field_definition->setSettings(['fruit' => 'banana']);
* // The 'season' setting persists unchanged.
* $field_definition->getSettings() === [
* 'fruit' => 'banana',
* 'season' => 'summer',
* ];
* @endcode
*
* For clarity, it is preferred to use setSetting() if not all available
* settings are supplied.
*/
public function setSettings(array $settings) {
$this->getItemDefinition()->setSettings($settings);
// Assign settings individiually, in order to keep the current values
// of settings not specified in $settings.
foreach ($settings as $setting_name => $setting) {
$this->getItemDefinition()->setSetting($setting_name, $setting);
}
return $this;
}
@ -167,15 +187,7 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
}
/**
* Sets a field setting.
*
* @param string $setting_name
* The field setting to set.
* @param mixed $value
* The value to set.
*
* @return static
* The object itself for chaining.
* {@inheritdoc}
*/
public function setSetting($setting_name, $value) {
$this->getItemDefinition()->setSetting($setting_name, $value);
@ -427,16 +439,30 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
return isset($this->definition['display'][$display_context]['configurable']) ? $this->definition['display'][$display_context]['configurable'] : FALSE;
}
/**
* {@inheritdoc}
*/
public function getDefaultValueLiteral() {
return isset($this->definition['default_value']) ? $this->definition['default_value'] : [];
}
/**
* {@inheritdoc}
*/
public function getDefaultValueCallback() {
return isset($this->definition['default_value_callback']) ? $this->definition['default_value_callback'] : NULL;
}
/**
* {@inheritdoc}
*/
public function getDefaultValue(FieldableEntityInterface $entity) {
// Allow custom default values function.
if (!empty($this->definition['default_value_callback'])) {
$value = call_user_func($this->definition['default_value_callback'], $entity, $this);
if ($callback = $this->getDefaultValueCallback()) {
$value = call_user_func($callback, $entity, $this);
}
else {
$value = isset($this->definition['default_value']) ? $this->definition['default_value'] : NULL;
$value = $this->getDefaultValueLiteral();
}
// Normalize into the "array keyed by delta" format.
if (isset($value) && !is_array($value)) {
@ -452,22 +478,27 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
}
/**
* Sets a custom default value callback.
*
* If set, the callback overrides any set default value.
*
* @param string|null $callback
* The callback to invoke for getting the default value (pass NULL to unset
* a previously set callback). The callback will be invoked with the
* following arguments:
* - \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity being created.
* - \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
* It should return the default value in the format accepted by the
* setDefaultValue() method.
*
* @return $this
* {@inheritdoc}
*/
public function setDefaultValue($value) {
if ($value === NULL) {
$value = [];
}
// Unless the value is an empty array, we may need to transform it.
if (!is_array($value) || !empty($value)) {
if (!is_array($value)) {
$value = array(array($this->getMainPropertyName() => $value));
}
elseif (is_array($value) && !is_numeric(array_keys($value)[0])) {
$value = array(0 => $value);
}
}
$this->definition['default_value'] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function setDefaultValueCallback($callback) {
if (isset($callback) && !is_string($callback)) {
@ -477,38 +508,6 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
return $this;
}
/**
* Sets a default value.
*
* Note that if a default value callback is set, it will take precedence over
* any value set here.
*
* @param mixed $value
* The default value for the field. This can be either:
* - a literal, in which case it will be assigned to the first property of
* the first item.
* - a numerically indexed array of items, each item being a property/value
* array.
* - a non-numerically indexed array, in which case the array is assumed to
* be a property/value array and used as the first item
* - NULL or array() for no default value.
*
* @return $this
*/
public function setDefaultValue($value) {
// Unless the value is NULL or an empty array, we may need to transform it.
if (!(is_null($value) || (is_array($value) && empty($value)))) {
if (!is_array($value)) {
$value = array(array($this->getMainPropertyName() => $value));
}
elseif (!is_numeric(array_keys($value)[0])) {
$value = array(0 => $value);
}
}
$this->definition['default_value'] = $value;
return $this;
}
/**
* {@inheritdoc}
*/

View file

@ -163,8 +163,7 @@ class BaseFieldOverride extends FieldConfigBase {
* {@inheritdoc}
*
* @throws \Drupal\Core\Field\FieldException
* If the bundle is being changed and
* BaseFieldOverride::allowBundleRename() has not been called.
* If the bundle is being changed.
*/
public function preSave(EntityStorageInterface $storage) {
// Filter out unknown settings and make sure all settings are present, so
@ -189,7 +188,7 @@ class BaseFieldOverride extends FieldConfigBase {
if ($this->entity_type != $this->original->entity_type) {
throw new FieldException("Cannot change the entity_type of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})");
}
if ($this->bundle != $this->original->bundle && empty($this->bundleRenameAllowed)) {
if ($this->bundle != $this->original->bundle) {
throw new FieldException("Cannot change the bundle of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})");
}
$previous_definition = $this->original;

View file

@ -27,12 +27,12 @@ class EntityReferenceFieldItemList extends FieldItemList implements EntityRefere
// "autocreate" entities that are already populated in $item->entity.
$target_entities = $ids = array();
foreach ($this->list as $delta => $item) {
if ($item->hasNewEntity()) {
$target_entities[$delta] = $item->entity;
}
elseif ($item->target_id !== NULL) {
if ($item->target_id !== NULL) {
$ids[$delta] = $item->target_id;
}
elseif ($item->hasNewEntity()) {
$target_entities[$delta] = $item->entity;
}
}
// Load and add the existing entities.
@ -104,7 +104,13 @@ class EntityReferenceFieldItemList extends FieldItemList implements EntityRefere
// Convert numeric IDs to UUIDs to ensure config deployability.
$ids = array();
foreach ($default_value as $delta => $properties) {
$ids[] = $properties['target_id'];
if (isset($properties['entity']) && $properties['entity']->isNew()) {
// This may be a newly created term.
$properties['entity']->save();
$default_value[$delta]['target_id'] = $properties['entity']->id();
unset($default_value[$delta]['entity']);
}
$ids[] = $default_value[$delta]['target_id'];
}
$entities = \Drupal::entityManager()
->getStorage($this->getSetting('target_type'))

View file

@ -145,7 +145,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
*
* @var array
*/
public $default_value = array();
protected $default_value = array();
/**
* The name of a callback function that returns default values.
@ -179,13 +179,6 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
*/
protected $itemDefinition;
/**
* Flag indicating whether the bundle name can be renamed or not.
*
* @var bool
*/
protected $bundleRenameAllowed = FALSE;
/**
* Array of constraint options keyed by constraint plugin ID.
*
@ -252,7 +245,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
$bundle_config_dependency = $this->entityManager()->getDefinition($this->entity_type)->getBundleConfigDependency($this->bundle);
$this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
return $this->dependencies;
return $this;
}
/**
@ -350,7 +343,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
* {@inheritdoc}
*/
public function setSettings(array $settings) {
$this->settings = $settings;
$this->settings = $settings + $this->settings;
return $this;
}
@ -393,25 +386,58 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
*/
public function getDefaultValue(FieldableEntityInterface $entity) {
// Allow custom default values function.
if ($callback = $this->default_value_callback) {
if ($callback = $this->getDefaultValueCallback()) {
$value = call_user_func($callback, $entity, $this);
}
else {
$value = $this->default_value;
}
// Normalize into the "array keyed by delta" format.
if (isset($value) && !is_array($value)) {
$properties = $this->getFieldStorageDefinition()->getPropertyNames();
$property = reset($properties);
$value = array(
array($property => $value),
);
$value = $this->getDefaultValueLiteral();
}
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
}
/**
* {@inheritdoc}
*/
public function getDefaultValueLiteral() {
return $this->default_value;
}
/**
* {@inheritdoc}
*/
public function setDefaultValue($value) {
if (!is_array($value)) {
if ($value === NULL) {
$value = [];
}
$key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
// Convert to the multi value format to support fields with a cardinality
// greater than 1.
$value = array(
array($key => $value),
);
}
$this->default_value = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDefaultValueCallback() {
return $this->default_value_callback;
}
/**
* {@inheritdoc}
*/
public function setDefaultValueCallback($callback) {
$this->default_value_callback = $callback;
return $this;
}
/**
* Implements the magic __sleep() method.
*
@ -423,7 +449,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
// Only serialize necessary properties, excluding those that can be
// recalculated.
$properties = get_object_vars($this);
unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['bundleRenameAllowed'], $properties['original']);
unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
return array_keys($properties);
}
@ -495,29 +521,6 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
return $this->itemDefinition;
}
/**
* {@inheritdoc}
*/
public function setDefaultValue($value) {
if (!is_array($value)) {
$key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
// Convert to the multi value format to support fields with a cardinality
// greater than 1.
$value = array(
array($key => $value),
);
}
$this->default_value = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function allowBundleRename() {
$this->bundleRenameAllowed = TRUE;
}
/**
* {@inheritdoc}
*/

View file

@ -55,7 +55,29 @@ interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInt
public function setTranslatable($translatable);
/**
* Sets field settings (overwrites existing settings).
* Sets field settings.
*
* Note that the method does not unset existing settings not specified in the
* incoming $settings array.
*
* For example:
* @code
* // Given these are the default settings.
* $field_definition->getSettings() === [
* 'fruit' => 'apple',
* 'season' => 'summer',
* ];
* // Change only the 'fruit' setting.
* $field_definition->setSettings(['fruit' => 'banana']);
* // The 'season' setting persists unchanged.
* $field_definition->getSettings() === [
* 'fruit' => 'banana',
* 'season' => 'summer',
* ];
* @endcode
*
* For clarity, it is preferred to use setSetting() if not all available
* settings are supplied.
*
* @param array $settings
* The array of field settings.
@ -100,13 +122,39 @@ interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInt
* any value set here.
*
* @param mixed $value
* The default value in the format as returned by
* \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
* The default value for the field. This can be either:
* - a literal, in which case it will be assigned to the first property of
* the first item.
* - a numerically indexed array of items, each item being a property/value
* array.
* - a non-numerically indexed array, in which case the array is assumed to
* be a property/value array and used as the first item
* - NULL or array() for no default value.
*
* @return $this
*/
public function setDefaultValue($value);
/**
* Sets a custom default value callback.
*
* If set, the callback overrides any set default value.
*
* @param string|null $callback
* The callback to invoke for getting the default value (pass NULL to unset
* a previously set callback). The callback will be invoked with the
* following arguments:
* - \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity being created.
* - \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
* It should return the default value in the format accepted by the
* setDefaultValue() method.
*
* @return $this
*/
public function setDefaultValueCallback($callback);
/**
* Sets constraints for a given field item property.
*
@ -178,7 +226,7 @@ interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInt
* @code
* // Add a constraint to the 'field_username' FieldItemList.
* // e.g. $node->field_username
* $fields['field_username']->addConstraint('UserNameUnique', []);
* $fields['field_username']->addConstraint('UniqueField');
* @endcode
*
* If you wish to apply a constraint to a \Drupal\Core\Field\FieldItem instead
@ -234,13 +282,4 @@ interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInt
*/
public function setConstraints(array $constraints);
/**
* Allows a bundle to be renamed.
*
* Renaming a bundle on the instance is allowed when an entity's bundle
* is renamed and when field_entity_bundle_rename() does internal
* housekeeping.
*/
public function allowBundleRename();
}

View file

@ -178,15 +178,54 @@ interface FieldDefinitionInterface extends ListDataDefinitionInterface, Cacheabl
*/
public function isRequired();
/**
* Returns the default value literal for the field.
*
* This method retrieves the raw property assigned to the field definition.
* When computing the runtime default value for a field in a given entity,
* ::getDefaultValue() should be used instead.
*
* @return array
* The default value for the field, as a numerically indexed array of items,
* each item being a property/value array (array() for no default value).
*
* @see FieldDefinitionInterface::getDefaultValue()
* @see FieldDefinitionInterface::getDefaultValueCallback()
*/
public function getDefaultValueLiteral();
/**
* Returns the default value callback for the field.
*
* This method retrieves the raw property assigned to the field definition.
* When computing the runtime default value for a field in a given entity,
* ::getDefaultValue() should be used instead.
*
* @return string|null
* The default value callback for the field.
*
* @see FieldDefinitionInterface::getDefaultValue()
* @see FieldDefinitionInterface::getDefaultValueLiteral()
*/
public function getDefaultValueCallback();
/**
* Returns the default value for the field in a newly created entity.
*
* This method computes the runtime default value for a field in a given
* entity. To access the raw properties assigned to the field definition,
* ::getDefaultValueLiteral() or ::getDefaultValueCallback() should be used
* instead.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity for which the default value is generated.
*
* @return array
* The default value for the field, as a numerically indexed array of items,
* each item being a property/value array (array() for no default value).
*
* @see FieldDefinitionInterface::getDefaultValueLiteral()
* @see FieldDefinitionInterface::getDefaultValueCallback()
*/
public function getDefaultValue(FieldableEntityInterface $entity);

View file

@ -2,14 +2,14 @@
/**
* @file
* Contains \Drupal\Core\Field\FieldFilteredString.
* Contains \Drupal\Core\Field\FieldFilteredMarkup.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Component\Utility\SafeStringTrait;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Render\MarkupTrait;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
@ -23,18 +23,18 @@ use Drupal\Component\Utility\Xss;
* This object is marked as internal because it should only be used by the
* Field module and field-related plugins.
*
* @see \Drupal\Core\Render\SafeString
* @see \Drupal\Core\Render\Markup
*/
final class FieldFilteredString implements SafeStringInterface, \Countable {
use SafeStringTrait;
final class FieldFilteredMarkup implements MarkupInterface, \Countable {
use MarkupTrait;
/**
* Overrides \Drupal\Component\Utility\SafeStringTrait::create().
* Overrides \Drupal\Component\Render\MarkupTrait::create().
*
* @return string|\Drupal\Component\Utility\SafeStringInterface
* @return string|\Drupal\Component\Render\MarkupInterface
* A safe string filtered with the allowed tag list and normalized.
*
* @see \Drupal\Core\Field\FieldFilteredString::allowedTags()
* @see \Drupal\Core\Field\FieldFilteredMarkup::allowedTags()
* @see \Drupal\Component\Utility\Xss::filter()
* @see \Drupal\Component\Utility\Html::normalize()
*/

View file

@ -267,6 +267,13 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
return array();
}
/**
* {@inheritdoc}
*/
public static function calculateStorageDependencies(FieldStorageDefinitionInterface $field_definition) {
return [];
}
/**
* {@inheritdoc}
*/

View file

@ -418,6 +418,39 @@ interface FieldItemInterface extends ComplexDataInterface {
*/
public static function calculateDependencies(FieldDefinitionInterface $field_definition);
/**
* Calculates dependencies for field items on the storage level.
*
* Dependencies are saved in the field storage configuration entity and are
* used to determine configuration synchronization order. For example, if the
* field type storage depends on a particular entity type, this method should
* return an array of dependencies listing the module that provides the entity
* type.
*
* Dependencies returned from this method are stored in field storage
* configuration and are always considered hard dependencies. If the
* dependency is removed the field storage configuration must be deleted.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
* The field storage definition.
*
* @return array
* An array of dependencies grouped by type (config, content, module,
* theme). For example:
* @code
* [
* 'config' => ['user.role.anonymous', 'user.role.authenticated'],
* 'content' => ['node:article:f0a189e6-55fb-47fb-8005-5bef81c44d6d'],
* 'module' => ['node', 'user'],
* 'theme' => ['seven'],
* ];
* @endcode
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
*/
public static function calculateStorageDependencies(FieldStorageDefinitionInterface $field_storage_definition);
/**
* Informs the plugin that a dependency of the field will be deleted.
*

View file

@ -281,7 +281,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
// widgets.
$cardinality = $this->getFieldDefinition()->getFieldStorageDefinition()->getCardinality();
if ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$constraints[] = \Drupal::typedDataManager()
$constraints[] = $this->getTypedDataManager()
->getValidationConstraintManager()
->create('Count', array(
'max' => $cardinality,
@ -296,14 +296,17 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
* {@inheritdoc}
*/
public function defaultValuesForm(array &$form, FormStateInterface $form_state) {
if (empty($this->getFieldDefinition()->default_value_callback)) {
// Place the input in a separate place in the submitted values tree.
$widget = $this->defaultValueWidget($form_state);
if (empty($this->getFieldDefinition()->getDefaultValueCallback())) {
if ($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($this, $element, $form_state);
$element = array('#parents' => array('default_value_input'));
$element += $widget->form($this, $element, $form_state);
return $element;
return $element;
}
else {
return ['#markup' => $this->t('No widget available for: %type.', ['%type' => $this->getFieldDefinition()->getType()])];
}
}
}
@ -312,16 +315,17 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
*/
public function defaultValuesFormValidate(array $element, array &$form, FormStateInterface $form_state) {
// Extract the submitted value, and validate it.
$widget = $this->defaultValueWidget($form_state);
$widget->extractFormValues($this, $element, $form_state);
// Force a non-required field definition.
// @see self::defaultValueWidget().
$this->getFieldDefinition()->setRequired(FALSE);
$violations = $this->validate();
if ($widget = $this->defaultValueWidget($form_state)) {
$widget->extractFormValues($this, $element, $form_state);
// Force a non-required field definition.
// @see self::defaultValueWidget().
$this->getFieldDefinition()->setRequired(FALSE);
$violations = $this->validate();
// Assign reported errors to the correct form element.
if (count($violations)) {
$widget->flagErrors($this, $violations, $element, $form_state);
// Assign reported errors to the correct form element.
if (count($violations)) {
$widget->flagErrors($this, $violations, $element, $form_state);
}
}
}
@ -330,9 +334,10 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
*/
public function defaultValuesFormSubmit(array $element, array &$form, FormStateInterface $form_state) {
// Extract the submitted value, and return it as an array.
$widget = $this->defaultValueWidget($form_state);
$widget->extractFormValues($this, $element, $form_state);
return $this->getValue();
if ($widget = $this->defaultValueWidget($form_state)) {
$widget->extractFormValues($this, $element, $form_state);
return $this->getValue();
}
}
/**
@ -348,8 +353,8 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the (entire) configuration form.
*
* @return \Drupal\Core\Field\WidgetInterface
* A Widget object.
* @return \Drupal\Core\Field\WidgetInterface|null
* A Widget object or NULL if no widget is available.
*/
protected function defaultValueWidget(FormStateInterface $form_state) {
if (!$form_state->has('default_value_widget')) {

View file

@ -57,19 +57,23 @@ interface FieldStorageDefinitionInterface extends CacheableDependencyInterface {
public function getType();
/**
* Returns the field settings.
* Returns the storage settings.
*
* Each field type defines the settings that are meaningful for that type.
* For example, a text field can define a 'max_length' setting, and an image
* field can define a 'alt_field_required' setting.
*
* The method always returns an array of all available settings for this field
* type, possibly with the default values merged in if values have not been
* provided for all available settings.
*
* @return mixed[]
* An array of key/value pairs.
*/
public function getSettings();
/**
* Returns the value of a given field setting.
* Returns the value of a given storage setting.
*
* @param string $setting_name
* The setting name.

View file

@ -13,7 +13,7 @@ use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\TypedData\TypedDataManager;
use Drupal\Core\TypedData\TypedDataManagerInterface;
/**
* Plugin manager for 'field type' plugins.
@ -27,7 +27,7 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl
/**
* The typed data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManager
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
*/
protected $typedDataManager;
@ -41,10 +41,10 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler.
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
* The typed data manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, TypedDataManager $typed_data_manager) {
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, TypedDataManagerInterface $typed_data_manager) {
parent::__construct('Plugin/Field/FieldType', $namespaces, $module_handler, 'Drupal\Core\Field\FieldItemInterface', 'Drupal\Core\Field\Annotation\FieldType');
$this->alterInfo('field_info');
$this->setCacheBackend($cache_backend, 'field_types_plugins');

View file

@ -8,6 +8,7 @@
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Render\Element;
/**
@ -76,8 +77,12 @@ abstract class FormatterBase extends PluginSettingsBase implements FormatterInte
/**
* {@inheritdoc}
*/
public function view(FieldItemListInterface $items) {
$elements = $this->viewElements($items);
public function view(FieldItemListInterface $items, $langcode = NULL) {
// Default the language to the current content language.
if (empty($langcode)) {
$langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
}
$elements = $this->viewElements($items, $langcode);
// If there are actual renderable children, use #theme => field, otherwise,
// let access cacheability metadata pass through for correct bubbling.
@ -99,6 +104,7 @@ abstract class FormatterBase extends PluginSettingsBase implements FormatterInte
'#object' => $entity,
'#items' => $items,
'#formatter' => $this->getPluginId(),
'#is_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(),
);
$elements = array_merge($info, $elements);

View file

@ -69,23 +69,28 @@ interface FormatterInterface extends PluginSettingsInterface {
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
* @param string $langcode
* (optional) The language that should be used to render the field. Defaults
* to the current content language.
*
* @return array
* A renderable array for a themed field with its label and all its values.
*/
public function view(FieldItemListInterface $items);
public function view(FieldItemListInterface $items, $langcode = NULL);
/**
* Builds a renderable array for a field value.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
* @param string $langcode
* The language that should be used to render the field.
*
* @return array
* A renderable array for $items, as an array of child elements keyed by
* consecutive numeric indexes starting from 0.
*/
public function viewElements(FieldItemListInterface $items);
public function viewElements(FieldItemListInterface $items, $langcode);
/**
* Returns if the formatter can be used for the provided field.

View file

@ -7,7 +7,6 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
@ -31,13 +30,17 @@ class BasicStringFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
foreach ($items as $delta => $item) {
// The text value has no text format assigned to it, so the user input
// should equal the output, including newlines.
$elements[$delta] = array('#markup' => nl2br(Html::escape($item->value)));
$elements[$delta] = [
'#type' => 'inline_template',
'#template' => '{{ value|nl2br }}',
'#context' => ['value' => $item->value],
];
}
return $elements;

View file

@ -116,7 +116,7 @@ class BooleanFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$formats = $this->getOutputFormats();

View file

@ -10,6 +10,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -119,11 +120,11 @@ class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implem
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$view_mode = $this->getSetting('view_mode');
$elements = array();
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
// Protect ourselves from recursive rendering.
static $depth = 0;
$depth++;

View file

@ -36,24 +36,25 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
*
* @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items
* The item list.
* @param string $langcode
* The language code of the referenced entities to display.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The array of referenced entities to display, keyed by delta.
*
* @see ::prepareView()
*/
protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items) {
protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items, $langcode) {
$entities = array();
$parent_entity_langcode = $items->getEntity()->language()->getId();
foreach ($items as $delta => $item) {
// Ignore items where no entity could be loaded in prepareView().
if (!empty($item->_loaded)) {
$entity = $item->entity;
// Set the entity in the correct language for display.
if ($entity instanceof TranslatableInterface && $entity->hasTranslation($parent_entity_langcode)) {
$entity = $entity->getTranslation($parent_entity_langcode);
if ($entity instanceof TranslatableInterface) {
$entity = \Drupal::entityManager()->getTranslationFromContext($entity, $langcode);
}
$access = $this->checkAccess($entity);
@ -76,8 +77,8 @@ abstract class EntityReferenceFormatterBase extends FormatterBase {
* @see ::prepareView()
* @see ::getEntitiestoView()
*/
public function view(FieldItemListInterface $items) {
$elements = parent::view($items);
public function view(FieldItemListInterface $items, $langcode = NULL) {
$elements = parent::view($items, $langcode);
$field_level_access_cacheability = new CacheableMetadata();

View file

@ -8,6 +8,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Language\LanguageInterface;
/**
* Plugin implementation of the 'entity reference ID' formatter.
@ -26,10 +27,10 @@ class EntityReferenceIdFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
if ($entity->id()) {
$elements[$delta] = array(
'#plain_text' => $entity->id(),

View file

@ -59,11 +59,11 @@ class EntityReferenceLabelFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$output_as_link = $this->getSetting('link');
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
$label = $entity->label();
// If the link is to be displayed and the entity has a uri, display a
// link.

View file

@ -27,7 +27,7 @@ class MailToFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {

View file

@ -66,7 +66,7 @@ abstract class NumericFormatterBase extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$settings = $this->getFieldSettings();
@ -75,15 +75,15 @@ abstract class NumericFormatterBase extends FormatterBase {
// Account for prefix and suffix.
if ($this->getSetting('prefix_suffix')) {
$prefixes = isset($settings['prefix']) ? array_map(array('Drupal\Core\Field\FieldFilteredString', 'create'), explode('|', $settings['prefix'])) : array('');
$suffixes = isset($settings['suffix']) ? array_map(array('Drupal\Core\Field\FieldFilteredString', 'create'), explode('|', $settings['suffix'])) : array('');
$prefixes = isset($settings['prefix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['prefix'])) : array('');
$suffixes = isset($settings['suffix']) ? array_map(array('Drupal\Core\Field\FieldFilteredMarkup', 'create'), explode('|', $settings['suffix'])) : array('');
$prefix = (count($prefixes) > 1) ? $this->formatPlural($item->value, $prefixes[0], $prefixes[1]) : $prefixes[0];
$suffix = (count($suffixes) > 1) ? $this->formatPlural($item->value, $suffixes[0], $suffixes[1]) : $suffixes[0];
$output = $prefix . $output . $suffix;
}
// Output the raw value in a content attribute if the text of the HTML
// element differs from the raw value (for example when a prefix is used).
if (!empty($item->_attributes) && $item->value != $output) {
if (isset($item->_attributes) && $item->value != $output) {
$item->_attributes += array('content' => $item->value);
}

View file

@ -28,7 +28,7 @@ class NumericUnformattedFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {

View file

@ -7,9 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FormatterBase;
@ -119,7 +117,7 @@ class StringFormatter extends FormatterBase implements ContainerFactoryPluginInt
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$url = NULL;
if ($this->getSetting('link_to_entity')) {
@ -137,7 +135,7 @@ class StringFormatter extends FormatterBase implements ContainerFactoryPluginInt
];
}
else {
$elements[$delta] = is_array($view_value) ? $view_value : ['#markup' => $view_value];
$elements[$delta] = $view_value;
}
}
return $elements;
@ -156,7 +154,9 @@ class StringFormatter extends FormatterBase implements ContainerFactoryPluginInt
// The text value has no text format assigned to it, so the user input
// should equal the output, including newlines.
return [
'#markup' => nl2br(Html::escape($item->value))
'#type' => 'inline_template',
'#template' => '{{ value|nl2br }}',
'#context' => ['value' => $item->value],
];
}

View file

@ -8,7 +8,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
@ -35,7 +35,7 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
@ -63,12 +63,12 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
* The view mode.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, Request $request) {
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $date_formatter, Request $request) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
@ -153,7 +153,7 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {

View file

@ -7,7 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
@ -34,7 +34,7 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
@ -62,12 +62,12 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin
* The view mode.
* @param array $third_party_settings
* Third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\Entity\EntityStorageInterface $date_format_storage
* The date format storage.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatter $date_formatter, EntityStorageInterface $date_format_storage) {
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $date_formatter, EntityStorageInterface $date_format_storage) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
@ -126,7 +126,7 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin
$elements['custom_date_format'] = array(
'#type' => 'textfield',
'#title' => $this->t('Custom date format'),
'#description' => $this->t('See <a href="@url" target="_blank">the documentation for PHP date formats</a>.', ['@url' => 'http://php.net/manual/function.date.php']),
'#description' => $this->t('See <a href=":url" target="_blank">the documentation for PHP date formats</a>.', [':url' => 'http://php.net/manual/function.date.php']),
'#default_value' => $this->getSetting('custom_date_format') ?: '',
);
@ -165,7 +165,7 @@ class TimestampFormatter extends FormatterBase implements ContainerFactoryPlugin
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$date_format = $this->getSetting('date_format');

View file

@ -27,7 +27,7 @@ class UriLinkFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {

View file

@ -12,6 +12,7 @@ use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\OptionsProviderInterface;
use Drupal\Core\TypedData\DataDefinition;
@ -33,8 +34,8 @@ class BooleanItem extends FieldItemBase implements OptionsProviderInterface {
*/
public static function defaultFieldSettings() {
return array(
'on_label' => t('On'),
'off_label' => t('Off'),
'on_label' => new TranslatableMarkup('On'),
'off_label' => new TranslatableMarkup('Off'),
) + parent::defaultFieldSettings();
}

View file

@ -18,6 +18,8 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType;
* label = @Translation("Last changed"),
* description = @Translation("An entity field containing a UNIX timestamp of when the entity has been last updated."),
* no_ui = TRUE,
* default_widget = "datetime_default",
* default_formatter = "timestamp",
* list_class = "\Drupal\Core\Field\ChangedFieldItemList"
* )
*

View file

@ -15,7 +15,8 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType;
* label = @Translation("Created"),
* description = @Translation("An entity field containing a UNIX timestamp of when the entity has been created."),
* no_ui = TRUE,
* default_formatter = "timestamp",
* default_widget = "datetime_default",
* default_formatter = "timestamp"
* )
*/
class CreatedItem extends TimestampItem {

View file

@ -38,7 +38,6 @@ class DecimalItem extends NumericItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
@ -92,6 +91,25 @@ class DecimalItem extends NumericItemBase {
return $element;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints = parent::getConstraints();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Regex' => array(
'pattern' => '/^[+-]?((\d+(\.\d*)?)|(\.\d+))$/i',
)
),
));
return $constraints;
}
/**
* {@inheritdoc}
*/

View file

@ -32,7 +32,7 @@ class EmailItem extends FieldItemBase {
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('email')
->setLabel(t('E-mail'))
->setLabel(t('Email'))
->setRequired(TRUE);
return $properties;

View file

@ -7,44 +7,45 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataReferenceDefinition;
use Drupal\Core\TypedData\DataReferenceTargetDefinition;
use Drupal\Core\TypedData\OptionsProviderInterface;
use Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint;
/**
* Defines the 'entity_reference' entity field type.
*
* Supported settings (below the definition's 'settings' key) are:
* - target_type: The entity type to reference. Required.
* - target_bundle: (optional): If set, restricts the entity bundles which may
* may be referenced. May be set to an single bundle, or to an array of
* allowed bundles.
*
* @FieldType(
* id = "entity_reference",
* label = @Translation("Entity reference"),
* description = @Translation("An entity field containing an entity reference."),
* category = @Translation("Reference"),
* no_ui = TRUE,
* default_widget = "entity_reference_autocomplete",
* default_formatter = "entity_reference_label",
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* default_widget = "entity_reference_autocomplete",
* default_formatter = "entity_reference_label",
* constraints = {"ValidReference" = {}}
* )
*/
class EntityReferenceItem extends FieldItemBase {
/**
* Marker value to identify a newly created entity.
*
* @var int
*/
protected static $NEW_ENTITY_MARKER = -1;
class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface {
/**
* {@inheritdoc}
@ -52,7 +53,6 @@ class EntityReferenceItem extends FieldItemBase {
public static function defaultStorageSettings() {
return array(
'target_type' => \Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user',
'target_bundle' => NULL,
) + parent::defaultStorageSettings();
}
@ -61,7 +61,7 @@ class EntityReferenceItem extends FieldItemBase {
*/
public static function defaultFieldSettings() {
return array(
'handler' => 'default:' . (\Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user'),
'handler' => 'default',
'handler_settings' => array(),
) + parent::defaultFieldSettings();
}
@ -82,34 +82,29 @@ class EntityReferenceItem extends FieldItemBase {
}
if ($target_id_data_type === 'integer') {
$target_id_definition = DataDefinition::create('integer')
->setLabel(new TranslationWrapper('@label ID', ['@label' => $target_type_info->getLabel()]))
$target_id_definition = DataReferenceTargetDefinition::create('integer')
->setLabel(new TranslatableMarkup('@label ID', ['@label' => $target_type_info->getLabel()]))
->setSetting('unsigned', TRUE);
}
else {
$target_id_definition = DataDefinition::create('string')
->setLabel(new TranslationWrapper('@label ID', ['@label' => $target_type_info->getLabel()]));
$target_id_definition = DataReferenceTargetDefinition::create('string')
->setLabel(new TranslatableMarkup('@label ID', ['@label' => $target_type_info->getLabel()]));
}
$target_id_definition->setRequired(TRUE);
$properties['target_id'] = $target_id_definition;
$properties['entity'] = DataReferenceDefinition::create('entity')
->setLabel($target_type_info->getLabel())
->setDescription(new TranslationWrapper('The referenced entity'))
->setDescription(new TranslatableMarkup('The referenced entity'))
// The entity object is computed out of the entity ID.
->setComputed(TRUE)
->setReadOnly(FALSE)
->setTargetDefinition(EntityDataDefinition::create($settings['target_type']))
// We can add a constraint for the target entity type. The list of
// referenceable bundles is a field setting, so the corresponding
// constraint is added dynamically in ::getConstraints().
->addConstraint('EntityType', $settings['target_type']);
if (isset($settings['target_bundle'])) {
$properties['entity']->addConstraint('Bundle', $settings['target_bundle']);
// Set any further bundle constraints on the target definition as well,
// such that it can derive more special data types if possible. For
// example, "entity:node:page" instead of "entity:node".
$properties['entity']->getTargetDefinition()
->addConstraint('Bundle', $settings['target_bundle']);
}
return $properties;
}
@ -158,6 +153,35 @@ class EntityReferenceItem extends FieldItemBase {
return $schema;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
// Remove the 'AllowedValuesConstraint' validation constraint because entity
// reference fields already use the 'ValidReference' constraint.
foreach ($constraints as $key => $constraint) {
if ($constraint instanceof AllowedValuesConstraint) {
unset($constraints[$key]);
}
}
list($current_handler) = explode(':', $this->getSetting('handler'), 2);
if ($current_handler === 'default') {
$handler_settings = $this->getSetting('handler_settings');
if (isset($handler_settings['target_bundles'])) {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints[] = $constraint_manager->create('ComplexData', [
'entity' => [
'Bundle' => [
'bundle' => $handler_settings['target_bundles'],
],
],
]);
}
}
return $constraints;
}
/**
* {@inheritdoc}
*/
@ -171,19 +195,23 @@ class EntityReferenceItem extends FieldItemBase {
parent::setValue($values, FALSE);
// Support setting the field item with only one property, but make sure
// values stay in sync if only property is passed.
if (isset($values['target_id']) && !isset($values['entity'])) {
// NULL is a valid value, so we use array_key_exists().
if (is_array($values) && array_key_exists('target_id', $values) && !isset($values['entity'])) {
$this->onChange('target_id', FALSE);
}
elseif (!isset($values['target_id']) && isset($values['entity'])) {
elseif (is_array($values) && !array_key_exists('target_id', $values) && isset($values['entity'])) {
$this->onChange('entity', FALSE);
}
elseif (isset($values['target_id']) && isset($values['entity'])) {
elseif (is_array($values) && array_key_exists('target_id', $values) && isset($values['entity'])) {
// If both properties are passed, verify the passed values match. The
// only exception we allow is when we have a new entity: in this case
// its actual id and target_id will be different, due to the new entity
// marker.
$entity_id = $this->get('entity')->getTargetIdentifier();
if ($entity_id != $values['target_id'] && ($values['target_id'] != static::$NEW_ENTITY_MARKER || !$this->entity->isNew())) {
// If the entity has been saved and we're trying to set both the
// target_id and the entity values with a non-null target ID, then the
// value for target_id should match the ID of the entity value.
if (!$this->entity->isNew() && $values['target_id'] !== NULL && ($entity_id !== $values['target_id'])) {
throw new \InvalidArgumentException('The target id and entity passed to the entity reference item do not match.');
}
}
@ -216,10 +244,10 @@ class EntityReferenceItem extends FieldItemBase {
// Make sure that the target ID and the target property stay in sync.
if ($property_name == 'entity') {
$property = $this->get('entity');
$target_id = $property->isTargetNew() ? static::$NEW_ENTITY_MARKER : $property->getTargetIdentifier();
$target_id = $property->isTargetNew() ? NULL : $property->getTargetIdentifier();
$this->writePropertyValue('target_id', $target_id);
}
elseif ($property_name == 'target_id' && $this->target_id != static::$NEW_ENTITY_MARKER) {
elseif ($property_name == 'target_id') {
$this->writePropertyValue('entity', $this->target_id);
}
parent::onChange($property_name, $notify);
@ -252,6 +280,9 @@ class EntityReferenceItem extends FieldItemBase {
// react properly.
$this->target_id = $this->entity->id();
}
if (!$this->isEmpty() && $this->target_id === NULL) {
$this->target_id = $this->entity->id();
}
}
/**
@ -266,6 +297,103 @@ class EntityReferenceItem extends FieldItemBase {
}
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$element['target_type'] = array(
'#type' => 'select',
'#title' => t('Type of item to reference'),
'#options' => \Drupal::entityManager()->getEntityTypeLabels(TRUE),
'#default_value' => $this->getSetting('target_type'),
'#required' => TRUE,
'#disabled' => $has_data,
'#size' => 1,
);
return $element;
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$field = $form_state->getFormObject()->getEntity();
// Get all selection plugins for this entity type.
$selection_plugins = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionGroups($this->getSetting('target_type'));
$handlers_options = array();
foreach (array_keys($selection_plugins) as $selection_group_id) {
// We only display base plugins (e.g. 'default', 'views', ...) and not
// entity type specific plugins (e.g. 'default:node', 'default:user',
// ...).
if (array_key_exists($selection_group_id, $selection_plugins[$selection_group_id])) {
$handlers_options[$selection_group_id] = Html::escape($selection_plugins[$selection_group_id][$selection_group_id]['label']);
}
elseif (array_key_exists($selection_group_id . ':' . $this->getSetting('target_type'), $selection_plugins[$selection_group_id])) {
$selection_group_plugin = $selection_group_id . ':' . $this->getSetting('target_type');
$handlers_options[$selection_group_plugin] = Html::escape($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
}
}
$form = array(
'#type' => 'container',
'#process' => array(array(get_class($this), 'fieldSettingsAjaxProcess')),
'#element_validate' => array(array(get_class($this), 'fieldSettingsFormValidate')),
);
$form['handler'] = array(
'#type' => 'details',
'#title' => t('Reference type'),
'#open' => TRUE,
'#tree' => TRUE,
'#process' => array(array(get_class($this), 'formProcessMergeParent')),
);
$form['handler']['handler'] = array(
'#type' => 'select',
'#title' => t('Reference method'),
'#options' => $handlers_options,
'#default_value' => $field->getSetting('handler'),
'#required' => TRUE,
'#ajax' => TRUE,
'#limit_validation_errors' => array(),
);
$form['handler']['handler_submit'] = array(
'#type' => 'submit',
'#value' => t('Change handler'),
'#limit_validation_errors' => array(),
'#attributes' => array(
'class' => array('js-hide'),
),
'#submit' => array(array(get_class($this), 'settingsAjaxSubmit')),
);
$form['handler']['handler_settings'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('entity_reference-settings')),
);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
$form['handler']['handler_settings'] += $handler->buildConfigurationForm(array(), $form_state);
return $form;
}
/**
* Form element validation handler; Invokes selection plugin's validation.
*
* @param array $form
* The form where the settings form is being included in.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the (entire) configuration form.
*/
public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {
$field = $form_state->getFormObject()->getEntity();
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
$handler->validateConfigurationForm($form, $form_state);
}
/**
* Determines whether the item holds an unsaved entity.
*
@ -277,19 +405,22 @@ class EntityReferenceItem extends FieldItemBase {
* TRUE if the item holds an unsaved entity.
*/
public function hasNewEntity() {
return $this->target_id === static::$NEW_ENTITY_MARKER;
return !$this->isEmpty() && $this->target_id === NULL && $this->entity->isNew();
}
/**
* {@inheritdoc}
*/
public static function calculateDependencies(FieldDefinitionInterface $field_definition) {
$dependencies = [];
if (is_array($field_definition->default_value) && count($field_definition->default_value)) {
$target_entity_type = \Drupal::entityManager()->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type'));
foreach ($field_definition->default_value as $default_value) {
if (is_array($default_value) && isset($default_value['target_uuid'])) {
$entity = \Drupal::entityManager()->loadEntityByUuid($target_entity_type->id(), $default_value['target_uuid']);
$dependencies = parent::calculateDependencies($field_definition);
$manager = \Drupal::entityManager();
$target_entity_type = $manager->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type'));
// Depend on default values entity types configurations.
if ($default_value = $field_definition->getDefaultValueLiteral()) {
foreach ($default_value as $value) {
if (is_array($value) && isset($value['target_uuid'])) {
$entity = \Drupal::entityManager()->loadEntityByUuid($target_entity_type->id(), $value['target_uuid']);
// If the entity does not exist do not create the dependency.
// @see \Drupal\Core\Field\EntityReferenceFieldItemList::processDefaultValue()
if ($entity) {
@ -298,6 +429,29 @@ class EntityReferenceItem extends FieldItemBase {
}
}
}
// Depend on target bundle configurations.
$handler = $field_definition->getSetting('handler_settings');
if (!empty($handler['target_bundles'])) {
if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) {
if ($storage = $manager->getStorage($bundle_entity_type_id)) {
foreach ($storage->loadMultiple($handler['target_bundles']) as $bundle) {
$dependencies[$bundle->getConfigDependencyKey()][] = $bundle->getConfigDependencyName();
}
}
}
}
return $dependencies;
}
/**
* {@inheritdoc}
*/
public static function calculateStorageDependencies(FieldStorageDefinitionInterface $field_definition) {
$dependencies = parent::calculateStorageDependencies($field_definition);
$target_entity_type = \Drupal::entityManager()->getDefinition($field_definition->getSetting('target_type'));
$dependencies['module'][] = $target_entity_type->getProvider();
return $dependencies;
}
@ -305,21 +459,201 @@ class EntityReferenceItem extends FieldItemBase {
* {@inheritdoc}
*/
public static function onDependencyRemoval(FieldDefinitionInterface $field_definition, array $dependencies) {
$changed = FALSE;
if (!empty($field_definition->default_value)) {
$target_entity_type = \Drupal::entityManager()->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type'));
foreach ($field_definition->default_value as $key => $default_value) {
if (is_array($default_value) && isset($default_value['target_uuid'])) {
$entity = \Drupal::entityManager()->loadEntityByUuid($target_entity_type->id(), $default_value['target_uuid']);
$changed = parent::onDependencyRemoval($field_definition, $dependencies);
$entity_manager = \Drupal::entityManager();
$target_entity_type = $entity_manager->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type'));
// Try to update the default value config dependency, if possible.
if ($default_value = $field_definition->getDefaultValueLiteral()) {
foreach ($default_value as $key => $value) {
if (is_array($value) && isset($value['target_uuid'])) {
$entity = $entity_manager->loadEntityByUuid($target_entity_type->id(), $value['target_uuid']);
// @see \Drupal\Core\Field\EntityReferenceFieldItemList::processDefaultValue()
if ($entity && isset($dependencies[$entity->getConfigDependencyKey()][$entity->getConfigDependencyName()])) {
unset($field_definition->default_value[$key]);
unset($default_value[$key]);
$changed = TRUE;
}
}
}
if ($changed) {
$field_definition->setDefaultValue($default_value);
}
}
// Update the 'target_bundles' handler setting if a bundle config dependency
// has been removed.
$bundles_changed = FALSE;
$handler_settings = $field_definition->getSetting('handler_settings');
if (!empty($handler_settings['target_bundles'])) {
if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) {
if ($storage = $entity_manager->getStorage($bundle_entity_type_id)) {
foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) {
if (isset($dependencies[$bundle->getConfigDependencyKey()][$bundle->getConfigDependencyName()])) {
unset($handler_settings['target_bundles'][$bundle->id()]);
$bundles_changed = TRUE;
// In case we deleted the only target bundle allowed by the field
// we have to log a warning message because the field will not
// function correctly anymore.
if ($handler_settings['target_bundles'] === []) {
\Drupal::logger('entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [
'%target_bundle' => $bundle->label(),
'%target_entity_type' => $bundle->getEntityType()->getBundleOf(),
'%field_name' => $field_definition->getName(),
'%entity_type' => $field_definition->getTargetEntityTypeId(),
'%bundle' => $field_definition->getTargetBundle()
]);
}
}
}
}
}
}
if ($bundles_changed) {
$field_definition->setSetting('handler_settings', $handler_settings);
}
$changed |= $bundles_changed;
return $changed;
}
/**
* {@inheritdoc}
*/
public function getPossibleValues(AccountInterface $account = NULL) {
return $this->getSettableValues($account);
}
/**
* {@inheritdoc}
*/
public function getPossibleOptions(AccountInterface $account = NULL) {
return $this->getSettableOptions($account);
}
/**
* {@inheritdoc}
*/
public function getSettableValues(AccountInterface $account = NULL) {
// Flatten options first, 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) {
$field_definition = $this->getFieldDefinition();
if (!$options = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field_definition, $this->getEntity())->getReferenceableEntities()) {
return array();
}
// Rebuild the array by changing the bundle key into the bundle label.
$target_type = $field_definition->getSetting('target_type');
$bundles = \Drupal::entityManager()->getBundleInfo($target_type);
$return = array();
foreach ($options as $bundle => $entity_ids) {
// The label does not need sanitizing since it is used as an optgroup
// which is only supported by select elements and auto-escaped.
$bundle_label = (string) $bundles[$bundle]['label'];
$return[$bundle_label] = $entity_ids;
}
return count($return) == 1 ? reset($return) : $return;
}
/**
* Render API callback: Processes the field settings form and allows access to
* the form state.
*
* @see static::fieldSettingsForm()
*/
public static function fieldSettingsAjaxProcess($form, FormStateInterface $form_state) {
static::fieldSettingsAjaxProcessElement($form, $form);
return $form;
}
/**
* Adds entity_reference specific properties to AJAX form elements from the
* field settings form.
*
* @see static::fieldSettingsAjaxProcess()
*/
public static function fieldSettingsAjaxProcessElement(&$element, $main_form) {
if (!empty($element['#ajax'])) {
$element['#ajax'] = array(
'callback' => array(get_called_class(), 'settingsAjax'),
'wrapper' => $main_form['#id'],
'element' => $main_form['#array_parents'],
);
}
foreach (Element::children($element) as $key) {
static::fieldSettingsAjaxProcessElement($element[$key], $main_form);
}
}
/**
* Render API callback: Moves entity_reference specific Form API elements
* (i.e. 'handler_settings') up a level for easier processing by the
* validation and submission handlers.
*
* @see _entity_reference_field_settings_process()
*/
public static function formProcessMergeParent($element) {
$parents = $element['#parents'];
array_pop($parents);
$element['#parents'] = $parents;
return $element;
}
/**
* Ajax callback for the handler settings form.
*
* @see static::fieldSettingsForm()
*/
public static function settingsAjax($form, FormStateInterface $form_state) {
return NestedArray::getValue($form, $form_state->getTriggeringElement()['#ajax']['element']);
}
/**
* Submit handler for the non-JS case.
*
* @see static::fieldSettingsForm()
*/
public static function settingsAjaxSubmit($form, FormStateInterface $form_state) {
$form_state->setRebuild();
}
/**
* {@inheritdoc}
*/
public static function getPreconfiguredOptions() {
$options = array();
// Add all the commonly referenced entity types as distinct pre-configured
// options.
$entity_types = \Drupal::entityManager()->getDefinitions();
$common_references = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
return $entity_type->isCommonReferenceTarget();
});
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
foreach ($common_references as $entity_type) {
$options[$entity_type->id()] = [
'label' => $entity_type->getLabel(),
'field_storage_config' => [
'settings' => [
'target_type' => $entity_type->id(),
]
]
];
}
return $options;
}
}

View file

@ -44,7 +44,6 @@ class LanguageItem extends FieldItemBase {
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Language code'))
->setSetting('is_ascii', TRUE)
->setRequired(TRUE);
$properties['language'] = DataReferenceDefinition::create('language')
@ -74,9 +73,8 @@ class LanguageItem extends FieldItemBase {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'type' => 'varchar_ascii',
'length' => 12,
'is_ascii' => TRUE,
),
),
);

View file

@ -9,7 +9,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
/**
@ -29,10 +29,10 @@ class PasswordItem extends StringItem {
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('The hashed password'))
->setLabel(new TranslatableMarkup('The hashed password'))
->setSetting('case_sensitive', TRUE);
$properties['existing'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('Existing password'));
->setLabel(new TranslatableMarkup('Existing password'));
return $properties;
}
@ -46,7 +46,7 @@ class PasswordItem extends StringItem {
$entity = $this->getEntity();
// Update the user password if it has changed.
if ($entity->isNew() || ($this->value && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) {
if ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) {
// Allow alternate password hashing schemes.
$this->value = \Drupal::service('password')->hash(trim($this->value));
// Abort if the hashing failed and returned FALSE.

View file

@ -9,7 +9,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
/**
@ -31,9 +31,9 @@ abstract class StringItemBase extends FieldItemBase {
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
// This is called very early by the user entity roles field. Prevent
// early t() calls by using the TranslationWrapper.
// early t() calls by using the TranslatableMarkup.
$properties['value'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('Text value'))
->setLabel(new TranslatableMarkup('Text value'))
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'))
->setRequired(TRUE);

View file

@ -19,6 +19,7 @@ use Drupal\Core\TypedData\DataDefinition;
* label = @Translation("Timestamp"),
* description = @Translation("An entity field containing a UNIX timestamp value."),
* no_ui = TRUE,
* default_widget = "datetime_default",
* default_formatter = "timestamp",
* constraints = {
* "ComplexData" = {

View file

@ -73,7 +73,7 @@ class EntityReferenceAutocompleteWidget extends WidgetBase {
$operators = $this->getMatchOperatorOptions();
$summary[] = t('Autocomplete matching: @match_operator', array('@match_operator' => $operators[$this->getSetting('match_operator')]));
$summary[] = t('Textfield size: !size', array('!size' => $this->getSetting('size')));
$summary[] = t('Textfield size: @size', array('@size' => $this->getSetting('size')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
@ -120,7 +120,7 @@ class EntityReferenceAutocompleteWidget extends WidgetBase {
* {@inheritdoc}
*/
public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
return $element['target_id'];
return isset($element['target_id']) ? $element['target_id'] : FALSE;
}
/**

View file

@ -7,7 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FieldFilteredMarkup;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
@ -102,11 +102,11 @@ class NumberWidget extends WidgetBase {
// Add prefix and suffix.
if ($field_settings['prefix']) {
$prefixes = explode('|', $field_settings['prefix']);
$element['#field_prefix'] = FieldFilteredString::create(array_pop($prefixes));
$element['#field_prefix'] = FieldFilteredMarkup::create(array_pop($prefixes));
}
if ($field_settings['suffix']) {
$suffixes = explode('|', $field_settings['suffix']);
$element['#field_suffix'] = FieldFilteredString::create(array_pop($suffixes));
$element['#field_suffix'] = FieldFilteredMarkup::create(array_pop($suffixes));
}
return array('value' => $element);

View file

@ -18,6 +18,7 @@ use Drupal\Core\Form\FormStateInterface;
* label = @Translation("Check boxes/radio buttons"),
* field_types = {
* "boolean",
* "entity_reference",
* "list_integer",
* "list_float",
* "list_string",

View file

@ -18,6 +18,7 @@ use Drupal\Core\Form\FormStateInterface;
* id = "options_select",
* label = @Translation("Select list"),
* field_types = {
* "entity_reference",
* "list_integer",
* "list_float",
* "list_string"

View file

@ -9,7 +9,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FieldFilteredMarkup;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
@ -73,7 +73,7 @@ abstract class OptionsWidgetBase extends WidgetBase {
*/
public static function validateElement(array $element, FormStateInterface $form_state) {
if ($element['#required'] && $element['#value'] == '_none') {
$form_state->setError($element, t('!name field is required.', array('!name' => $element['#title'])));
$form_state->setError($element, t('@name field is required.', array('@name' => $element['#title'])));
}
// Massage submitted form values.
@ -191,7 +191,7 @@ abstract class OptionsWidgetBase extends WidgetBase {
*/
protected function sanitizeLabel(&$label) {
// Allow a limited set of HTML tags.
$label = FieldFilteredString::create($label);
$label = FieldFilteredMarkup::create($label);
}
/**

View file

@ -60,7 +60,7 @@ class StringTextareaWidget extends WidgetBase {
public function settingsSummary() {
$summary = array();
$summary[] = t('Number of rows: !rows', array('!rows' => $this->getSetting('rows')));
$summary[] = t('Number of rows: @rows', array('@rows' => $this->getSetting('rows')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));

View file

@ -60,7 +60,7 @@ class StringTextfieldWidget extends WidgetBase {
public function settingsSummary() {
$summary = array();
$summary[] = t('Textfield size: !size', array('!size' => $this->getSetting('size')));
$summary[] = t('Textfield size: @size', array('@size' => $this->getSetting('size')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));

View file

@ -60,7 +60,7 @@ class UriWidget extends WidgetBase {
public function settingsSummary() {
$summary = array();
$summary[] = $this->t('URI field size: !size', array('!size' => $this->getSetting('size')));
$summary[] = $this->t('URI field size: @size', array('@size' => $this->getSetting('size')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = $this->t('Placeholder: @placeholder', array('@placeholder' => $placeholder));

View file

@ -94,6 +94,16 @@ abstract class PluginSettingsBase extends PluginBase implements PluginSettingsIn
return $this;
}
/**
* {@inheritdoc}
*/
public function getThirdPartySettings($module = NULL) {
if ($module) {
return isset($this->thirdPartySettings[$module]) ? $this->thirdPartySettings[$module] : NULL;
}
return $this->thirdPartySettings;
}
/**
* {@inheritdoc}
*/
@ -109,6 +119,26 @@ abstract class PluginSettingsBase extends PluginBase implements PluginSettingsIn
return $this;
}
/**
* {@inheritdoc}
*/
public function unsetThirdPartySetting($module, $key) {
unset($this->thirdPartySettings[$module][$key]);
// If the third party is no longer storing any information, completely
// remove the array holding the settings for this module.
if (empty($this->thirdPartySettings[$module])) {
unset($this->thirdPartySettings[$module]);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getThirdPartyProviders() {
return array_keys($this->thirdPartySettings);
}
/**
* {@inheritdoc}
*/
@ -122,4 +152,17 @@ abstract class PluginSettingsBase extends PluginBase implements PluginSettingsIn
return array();
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
$changed = FALSE;
if (!empty($this->thirdPartySettings) && !empty($dependencies['module'])) {
$old_count = count($this->thirdPartySettings);
$this->thirdPartySettings = array_diff_key($this->thirdPartySettings, array_flip($dependencies['module']));
$changed = $old_count != count($this->thirdPartySettings);
}
return $changed;
}
}

View file

@ -8,11 +8,12 @@
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
/**
* Interface definition for plugin with settings.
*/
interface PluginSettingsInterface extends PluginInspectionInterface {
interface PluginSettingsInterface extends PluginInspectionInterface, ThirdPartySettingsInterface {
/**
* Defines the default settings for this plugin.
@ -65,34 +66,27 @@ interface PluginSettingsInterface extends PluginInspectionInterface {
public function setSetting($key, $value);
/**
* Returns the value of a third-party setting, or $default if not set.
* Informs the plugin that some configuration it depends on will be deleted.
*
* @param string $module
* The module providing the third-party setting.
* @param string $key
* The setting name.
* @param mixed $default
* (optional) The default value if the third party setting is not set.
* Defaults to NULL.
* This method allows plugins to keep their configuration up-to-date when a
* dependency calculated with ::calculateDependencies() is removed. For
* example, an entity view display contains a formatter having a setting
* pointing to an arbitrary config entity. When that config entity is deleted,
* this method is called by the view display to react to the dependency
* removal by updating its configuration.
*
* @return mixed|NULL
* The setting value. Returns NULL if the setting does not exist and
* $default is not provided.
* This method must return TRUE if the removal event updated the plugin
* configuration or FALSE otherwise.
*
* @param array $dependencies
* An array of dependencies that will be deleted keyed by dependency type.
* Dependency types are 'config', 'content', 'module' and 'theme'.
*
* @return bool
* TRUE if the plugin configuration has changed, FALSE if not.
*
* @see \Drupal\Core\Entity\EntityDisplayBase
*/
public function getThirdPartySetting($module, $key, $default = NULL);
/**
* Sets the value of a third-party setting for the plugin.
*
* @param string $module
* The module providing the third-party setting.
* @param string $key
* The setting name.
* @param mixed $value
* The setting value.
*
* @return $this
*/
public function setThirdPartySetting($module, $key, $value);
public function onDependencyRemoval(array $dependencies);
}

View file

@ -85,7 +85,7 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
$delta = isset($get_delta) ? $get_delta : 0;
$element = array(
'#title' => $this->fieldDefinition->getLabel(),
'#description' => FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
'#description' => FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
);
$element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
@ -164,7 +164,7 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
}
$title = $this->fieldDefinition->getLabel();
$description = FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
$description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
$elements = array();

View file

@ -151,7 +151,7 @@ class WidgetPluginManager extends DefaultPluginManager {
// If no widget is specified, use the default widget.
if (!isset($configuration['type'])) {
$field_type = $this->fieldTypeManager->getDefinition($field_type);
$configuration['type'] = $field_type['default_widget'];
$configuration['type'] = isset($field_type['default_widget']) ? $field_type['default_widget'] : NULL;
}
// Filter out unknown settings, and fill in defaults for missing settings.
$default_settings = $this->getDefaultSettings($configuration['type']);