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

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

View file

@ -0,0 +1,62 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\AllowedTagsXssTrait.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
/**
* Useful methods when dealing with displaying allowed tags.
*/
trait AllowedTagsXssTrait {
/**
* Filters an HTML string to prevent XSS vulnerabilities.
*
* Like \Drupal\Component\Utility\Xss::filterAdmin(), but with a shorter list
* of allowed tags.
*
* 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).
*
* @param string $string
* The string with raw HTML in it.
*
* @return \Drupal\Component\Utility\SafeMarkup
* An XSS safe version of $string, or an empty string if $string is not
* valid UTF-8.
*/
public function fieldFilterXss($string) {
// All known XSS vectors are filtered out by
// \Drupal\Component\Utility\Xss::filter(), all tags in the markup are
// allowed intentionally by the trait, and no danger is added in by
// \Drupal\Component\Utility\HTML::normalize(). Since the normalized value
// is essentially the same markup, designate this string as safe as well.
// This method is an internal part of field sanitization, so the resultant,
// sanitized string should be printable as is.
//
// @todo Free this memory in https://www.drupal.org/node/2505963.
return SafeMarkup::set(Html::normalize(Xss::filter($string, $this->allowedTags())));
}
/**
* Returns a list of tags allowed by AllowedTagsXssTrait::fieldFilterXss().
*/
public function allowedTags() {
return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img');
}
/**
* Returns a human-readable list of allowed tags for display in help texts.
*/
public function displayAllowedTags() {
return '<' . implode('> <', $this->allowedTags()) . '>';
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Annotation\FieldFormatter.
*/
namespace Drupal\Core\Field\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a FieldFormatter annotation object.
*
* Formatters handle the display of field values. They are typically
* instantiated and invoked by an EntityDisplay object.
*
* Additional annotation keys for formatters can be defined in
* hook_field_formatter_info_alter().
*
* @Annotation
*
* @see \Drupal\Core\Field\FormatterPluginManager
* @see \Drupal\Core\Field\FormatterInterface
*
* @ingroup field_formatter
*/
class FieldFormatter extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the formatter type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
/**
* A short description of the formatter type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $description;
/**
* The name of the field formatter class.
*
* This is not provided manually, it will be added by the discovery mechanism.
*
* @var string
*/
public $class;
/**
* An array of field types the formatter supports.
*
* @var array
*/
public $field_types = array();
/**
* An integer to determine the weight of this formatter relative to other
* formatter in the Field UI when selecting a formatter for a given field
* instance.
*
* @var int optional
*/
public $weight = NULL;
}

View file

@ -0,0 +1,99 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Annotation\FieldType.
*/
namespace Drupal\Core\Field\Annotation;
use Drupal\Core\TypedData\Annotation\DataType;
/**
* Defines a FieldType annotation object.
*
* Additional annotation keys for field types can be defined in
* hook_field_info_alter().
*
* @ingroup field_types
*
* @Annotation
*/
class FieldType extends DataType {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The name of the module providing the field type plugin.
*
* @var string
*/
public $module;
/**
* The human-readable name of the field type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
/**
* A short human readable description for the field type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $description;
/**
* The category under which the field type should be listed in the UI.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $category = '';
/**
* The plugin_id of the default widget for this field type.
*
* This widget must be available whenever the field type is available (i.e.
* provided by the field type module, or by a module the field type module
* depends on).
*
* @var string
*/
public $default_widget;
/**
* The plugin_id of the default formatter for this field type.
*
* This formatter must be available whenever the field type is available (i.e.
* provided by the field type module, or by a module the field type module
* depends on).
*
* @var string
*/
public $default_formatter;
/**
* A boolean stating that fields of this type cannot be created through the UI.
*
* @var bool
*/
public $no_ui = FALSE;
/**
* {@inheritdoc}
*/
public $list_class;
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Annotation\FieldWidget.
*/
namespace Drupal\Core\Field\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a FieldWidget annotation object.
*
* Widgets handle how fields are displayed in edit forms.
*
* Additional annotation keys for widgets can be defined in
* hook_field_widget_info_alter().
*
* @Annotation
*
* @see \Drupal\Core\Field\WidgetPluginManager
* @see \Drupal\Core\Field\WidgetInterface
*
* @ingroup field_widget
*/
class FieldWidget extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the widget type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
/**
* A short description of the widget type.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $description;
/**
* The name of the widget class.
*
* This is not provided manually, it will be added by the discovery mechanism.
*
* @var string
*/
public $class;
/**
* An array of field types the widget supports.
*
* @var array
*/
public $field_types = array();
/**
* Does the field widget handles multiple values at once.
*
* @var bool
*/
public $multiple_values = FALSE;
/**
* An integer to determine the weight of this widget relative to other widgets
* in the Field UI when selecting a widget for a given field.
*
* @var int optional
*/
public $weight = NULL;
}

View file

@ -0,0 +1,732 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\BaseFieldDefinition.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Core\TypedData\ListDataDefinition;
use Drupal\Core\TypedData\OptionsProviderInterface;
/**
* A class for defining entity fields.
*/
class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionInterface, FieldStorageDefinitionInterface {
use UnchangingCacheableDependencyTrait;
/**
* The field type.
*
* @var string
*/
protected $type;
/**
* An array of field property definitions.
*
* @var \Drupal\Core\TypedData\DataDefinitionInterface[]
*
* @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
*/
protected $propertyDefinitions;
/**
* The field schema.
*
* @var array
*/
protected $schema;
/**
* @var array
*/
protected $indexes = array();
/**
* Creates a new field definition.
*
* @param string $type
* The type of the field.
*
* @return static
* A new field definition object.
*/
public static function create($type) {
$field_definition = new static(array());
$field_definition->type = $type;
$field_definition->itemDefinition = FieldItemDataDefinition::create($field_definition);
// Create a definition for the items, and initialize it with the default
// settings for the field type.
// @todo Cleanup in https://www.drupal.org/node/2116341.
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$default_settings = $field_type_manager->getDefaultStorageSettings($type) + $field_type_manager->getDefaultFieldSettings($type);
$field_definition->itemDefinition->setSettings($default_settings);
return $field_definition;
}
/**
* Creates a new field definition based upon a field storage definition.
*
* In cases where one needs a field storage definitions to act like full
* field definitions, this creates a new field definition based upon the
* (limited) information available. That way it is possible to use the field
* definition in places where a full field definition is required; e.g., with
* widgets or formatters.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
* The field storage definition to base the new field definition upon.
*
* @return $this
*/
public static function createFromFieldStorageDefinition(FieldStorageDefinitionInterface $definition) {
return static::create($definition->getType())
->setCardinality($definition->getCardinality())
->setConstraints($definition->getConstraints())
->setCustomStorage($definition->hasCustomStorage())
->setDescription($definition->getDescription())
->setLabel($definition->getLabel())
->setName($definition->getName())
->setProvider($definition->getProvider())
->setQueryable($definition->isQueryable())
->setRevisionable($definition->isRevisionable())
->setSettings($definition->getSettings())
->setTargetEntityTypeId($definition->getTargetEntityTypeId())
->setTranslatable($definition->isTranslatable());
}
/**
* {@inheritdoc}
*/
public static function createFromItemType($item_type) {
// The data type of a field item is in the form of "field_item:$field_type".
$parts = explode(':', $item_type, 2);
return static::create($parts[1]);
}
/**
* {@inheritdoc}
*/
public function getName() {
return $this->definition['field_name'];
}
/**
* Sets the field name.
*
* @param string $name
* The field name to set.
*
* @return static
* The object itself for chaining.
*/
public function setName($name) {
$this->definition['field_name'] = $name;
return $this;
}
/**
* {@inheritdoc}
*/
public function getType() {
return $this->type;
}
/**
* {@inheritdoc}
*/
public function getSettings() {
return $this->getItemDefinition()->getSettings();
}
/**
* Sets field settings.
*
* @param array $settings
* The value to set.
*
* @return static
* The object itself for chaining.
*/
public function setSettings(array $settings) {
$this->getItemDefinition()->setSettings($settings);
return $this;
}
/**
* {@inheritdoc}
*/
public function getSetting($setting_name) {
return $this->getItemDefinition()->getSetting($setting_name);
}
/**
* 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.
*/
public function setSetting($setting_name, $value) {
$this->getItemDefinition()->setSetting($setting_name, $value);
return $this;
}
/**
* {@inheritdoc}
*/
public function getProvider() {
return isset($this->definition['provider']) ? $this->definition['provider'] : NULL;
}
/**
* Sets the name of the provider of this field.
*
* @param string $provider
* The provider name to set.
*
* @return $this
*/
public function setProvider($provider) {
$this->definition['provider'] = $provider;
return $this;
}
/**
* {@inheritdoc}
*/
public function isTranslatable() {
return !empty($this->definition['translatable']);
}
/**
* Sets whether the field is translatable.
*
* @param bool $translatable
* Whether the field is translatable.
*
* @return $this
* The object itself for chaining.
*/
public function setTranslatable($translatable) {
$this->definition['translatable'] = $translatable;
return $this;
}
/**
* {@inheritdoc}
*/
public function isRevisionable() {
return !empty($this->definition['revisionable']);
}
/**
* Sets whether the field is revisionable.
*
* @param bool $revisionable
* Whether the field is revisionable.
*
* @return $this
* The object itself for chaining.
*/
public function setRevisionable($revisionable) {
$this->definition['revisionable'] = $revisionable;
return $this;
}
/**
* {@inheritdoc}
*/
public function getCardinality() {
// @todo: Allow to control this.
return isset($this->definition['cardinality']) ? $this->definition['cardinality'] : 1;
}
/**
* Sets the maximum number of items allowed for the field.
*
* Possible values are positive integers or
* FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED.
*
* @param int $cardinality
* The field cardinality.
*
* @return $this
*/
public function setCardinality($cardinality) {
$this->definition['cardinality'] = $cardinality;
return $this;
}
/**
* {@inheritdoc}
*/
public function isMultiple() {
$cardinality = $this->getCardinality();
return ($cardinality == static::CARDINALITY_UNLIMITED) || ($cardinality > 1);
}
/**
* {@inheritdoc}
*/
public function isQueryable() {
return isset($this->definition['queryable']) ? $this->definition['queryable'] : !$this->isComputed();
}
/**
* Sets whether the field is queryable.
*
* @param bool $queryable
* Whether the field is queryable.
*
* @return static
* The object itself for chaining.
*/
public function setQueryable($queryable) {
$this->definition['queryable'] = $queryable;
return $this;
}
/**
* Sets constraints for a given field item property.
*
* Note: this overwrites any existing property constraints. If you need to
* add to the existing constraints, use
* \Drupal\Core\Field\BaseFieldDefinition::addPropertyConstraints()
*
* @param string $name
* The name of the property to set constraints for.
* @param array $constraints
* The constraints to set.
*
* @return static
* The object itself for chaining.
*/
public function setPropertyConstraints($name, array $constraints) {
$item_constraints = $this->getItemDefinition()->getConstraints();
$item_constraints['ComplexData'][$name] = $constraints;
$this->getItemDefinition()->setConstraints($item_constraints);
return $this;
}
/**
* Adds constraints for a given field item property.
*
* Adds a constraint to a property of a base field item. e.g.
* @code
* // Limit the field item's value property to the range 0 through 10.
* // e.g. $node->size->value.
* $field->addPropertyConstraints('value', [
* 'Range' => [
* 'min' => 0,
* 'max' => 10,
* ]
* ]);
* @endcode
*
* If you want to add a validation constraint that applies to the
* \Drupal\Core\Field\FieldItemList, use BaseFieldDefinition::addConstraint()
* instead.
*
* Note: passing a new set of options for an existing property constraint will
* overwrite with the new options.
*
* @param string $name
* The name of the property to set constraints for.
* @param array $constraints
* The constraints to set.
*
* @return static
* The object itself for chaining.
*
* @see \Drupal\Core\Field\BaseFieldDefinition::addConstraint()
*/
public function addPropertyConstraints($name, array $constraints) {
$item_constraints = $this->getItemDefinition()->getConstraint('ComplexData') ?: [];
if (isset($item_constraints[$name])) {
// Add the new property constraints, overwriting as required.
$item_constraints[$name] = $constraints + $item_constraints[$name];
}
else {
$item_constraints[$name] = $constraints;
}
$this->getItemDefinition()->addConstraint('ComplexData', $item_constraints);
return $this;
}
/**
* Sets the display options for the field in forms or rendered entities.
*
* This enables generic rendering of the field with widgets / formatters,
* including automated support for "In place editing", and with optional
* configurability in the "Manage display" / "Manage form display" UI screens.
*
* Unless this method is called, the field remains invisible (or requires
* ad-hoc rendering logic).
*
* @param string $display_context
* The display context. Either 'view' or 'form'.
* @param array $options
* An array of display options. Refer to
* \Drupal\Core\Field\FieldDefinitionInterface::getDisplayOptions() for
* a list of supported keys. The options should include at least a 'weight',
* or specify 'type' = 'hidden'. The 'default_widget' / 'default_formatter'
* for the field type will be used if no 'type' is specified.
*
* @return static
* The object itself for chaining.
*/
public function setDisplayOptions($display_context, array $options) {
$this->definition['display'][$display_context]['options'] = $options;
return $this;
}
/**
* Sets whether the display for the field can be configured.
*
* @param string $display_context
* The display context. Either 'view' or 'form'.
* @param bool $configurable
* Whether the display options can be configured (e.g., via the "Manage
* display" / "Manage form display" UI screens). If TRUE, the options
* specified via getDisplayOptions() act as defaults.
*
* @return static
* The object itself for chaining.
*/
public function setDisplayConfigurable($display_context, $configurable) {
// If no explicit display options have been specified, default to 'hidden'.
if (empty($this->definition['display'][$display_context])) {
$this->definition['display'][$display_context]['options'] = array('type' => 'hidden');
}
$this->definition['display'][$display_context]['configurable'] = $configurable;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDisplayOptions($display_context) {
return isset($this->definition['display'][$display_context]['options']) ? $this->definition['display'][$display_context]['options'] : NULL;
}
/**
* {@inheritdoc}
*/
public function isDisplayConfigurable($display_context) {
return isset($this->definition['display'][$display_context]['configurable']) ? $this->definition['display'][$display_context]['configurable'] : FALSE;
}
/**
* {@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);
}
else {
$value = isset($this->definition['default_value']) ? $this->definition['default_value'] : NULL;
}
// Normalize into the "array keyed by delta" format.
if (isset($value) && !is_array($value)) {
$properties = $this->getPropertyNames();
$property = reset($properties);
$value = array(
array($property => $value),
);
}
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
}
/**
* 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) {
if (isset($callback) && !is_string($callback)) {
throw new \InvalidArgumentException('Default value callback must be a string, like "function_name" or "ClassName::methodName"');
}
$this->definition['default_value_callback'] = $callback;
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}
*/
public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
// If the field item class implements the interface, create an orphaned
// runtime item object, so that it can be used as the options provider
// without modifying the entity being worked on.
if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) {
$items = $entity->get($this->getName());
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
}
// @todo: Allow setting custom options provider, see
// https://www.drupal.org/node/2002138.
}
/**
* {@inheritdoc}
*/
public function getPropertyDefinition($name) {
if (!isset($this->propertyDefinitions)) {
$this->getPropertyDefinitions();
}
if (isset($this->propertyDefinitions[$name])) {
return $this->propertyDefinitions[$name];
}
}
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
if (!isset($this->propertyDefinitions)) {
$class = $this->getFieldItemClass();
$this->propertyDefinitions = $class::propertyDefinitions($this);
}
return $this->propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public function getPropertyNames() {
return array_keys($this->getPropertyDefinitions());
}
/**
* {@inheritdoc}
*/
public function getMainPropertyName() {
$class = $this->getFieldItemClass();
return $class::mainPropertyName();
}
/**
* Helper to retrieve the field item class.
*
* @todo: Remove once getClass() adds in defaults. See
* https://www.drupal.org/node/2116341.
*/
protected function getFieldItemClass() {
if ($class = $this->getItemDefinition()->getClass()) {
return $class;
}
else {
$type_definition = \Drupal::typedDataManager()
->getDefinition($this->getItemDefinition()->getDataType());
return $type_definition['class'];
}
}
/**
* {@inheritdoc}
*/
public function __sleep() {
// Do not serialize the statically cached property definitions.
$vars = get_object_vars($this);
unset($vars['propertyDefinitions']);
return array_keys($vars);
}
/**
* {@inheritdoc}
*/
public function getTargetEntityTypeId() {
return isset($this->definition['entity_type']) ? $this->definition['entity_type'] : NULL;
}
/**
* Sets the ID of the type of the entity this field is attached to.
*
* @param string $entity_type_id
* The name of the target entity type to set.
*
* @return $this
*/
public function setTargetEntityTypeId($entity_type_id) {
$this->definition['entity_type'] = $entity_type_id;
return $this;
}
/**
* {@inheritdoc}
*/
public function getTargetBundle() {
return isset($this->definition['bundle']) ? $this->definition['bundle'] : NULL;
}
/**
* Sets the bundle this field is defined for.
*
* @param string|null $bundle
* The bundle, or NULL if the field is not bundle-specific.
*
* @return $this
*/
public function setTargetBundle($bundle) {
$this->definition['bundle'] = $bundle;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSchema() {
if (!isset($this->schema)) {
// Get the schema from the field item class.
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getType());
$class = $definition['class'];
$schema = $class::schema($this);
// Fill in default values.
$schema += array(
'columns' => array(),
'unique keys' => array(),
'indexes' => array(),
'foreign keys' => array(),
);
// Merge custom indexes with those specified by the field type. Custom
// indexes prevail.
$schema['indexes'] = $this->indexes + $schema['indexes'];
$this->schema = $schema;
}
return $this->schema;
}
/**
* {@inheritdoc}
*/
public function getColumns() {
$schema = $this->getSchema();
// A typical use case for the method is to iterate on the columns, while
// some other use cases rely on identifying the first column with the key()
// function. Since the schema is persisted in the Field object, we take care
// of resetting the array pointer so that the former does not interfere with
// the latter.
reset($schema['columns']);
return $schema['columns'];
}
/**
* {@inheritdoc}
*/
public function hasCustomStorage() {
return !empty($this->definition['custom_storage']) || $this->isComputed();
}
/**
* {@inheritdoc}
*/
public function isBaseField() {
return TRUE;
}
/**
* Sets the storage behavior for this field.
*
* @param bool $custom_storage
* Pass FALSE if the storage takes care of storing the field,
* TRUE otherwise.
*
* @return $this
*
* @throws \LogicException
* Thrown if custom storage is to be set to FALSE for a computed field.
*/
public function setCustomStorage($custom_storage) {
if (!$custom_storage && $this->isComputed()) {
throw new \LogicException("Entity storage cannot store a computed field.");
}
$this->definition['custom_storage'] = $custom_storage;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFieldStorageDefinition() {
return $this;
}
/**
* {@inheritdoc}
*/
public function getUniqueStorageIdentifier() {
return $this->getTargetEntityTypeId() . '-' . $this->getName();
}
/**
* {@inheritdoc}
*/
public function getConfig($bundle) {
$override = BaseFieldOverride::loadByName($this->getTargetEntityTypeId(), $bundle, $this->getName());
if ($override) {
return $override;
}
return BaseFieldOverride::createFromBaseFieldDefinition($this, $bundle);
}
}

View file

@ -0,0 +1,53 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\BaseFieldOverrideStorage.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Component\Uuid\UuidInterface;
/**
* Storage class for base field overrides.
*/
class BaseFieldOverrideStorage extends FieldConfigStorageBase {
/**
* Constructs a BaseFieldOverrideStorage object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
* The UUID service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The field type plugin manager.
*/
public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, FieldTypePluginManagerInterface $field_type_manager) {
parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager);
$this->fieldTypeManager = $field_type_manager;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('config.factory'),
$container->get('uuid'),
$container->get('language_manager'),
$container->get('plugin.manager.field.field_type')
);
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\ChangedFieldItemList.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
/**
* Defines a item list class for changed fields.
*/
class ChangedFieldItemList extends FieldItemList {
/**
* {@inheritdoc}
*/
public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
// It is not possible to edit the changed field.
return AccessResult::allowedIf($operation !== 'edit');
}
}

View file

@ -0,0 +1,248 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Entity\BaseFieldOverride.
*/
namespace Drupal\Core\Field\Entity;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldConfigBase;
use Drupal\Core\Field\FieldException;
/**
* Defines the base field override entity.
*
* Allows base fields to be overridden on the bundle level.
*
* @ConfigEntityType(
* id = "base_field_override",
* label = @Translation("Base field override"),
* controllers = {
* "storage" = "Drupal\Core\Field\BaseFieldOverrideStorage"
* },
* config_prefix = "base_field_override",
* entity_keys = {
* "id" = "id",
* "label" = "label"
* },
* config_export = {
* "id",
* "field_name",
* "entity_type",
* "bundle",
* "label",
* "description",
* "required",
* "translatable",
* "default_value",
* "default_value_callback",
* "settings",
* "field_type",
* }
* )
*/
class BaseFieldOverride extends FieldConfigBase {
/**
* The base field definition.
*
* @var \Drupal\Core\Field\BaseFieldDefinition
*/
protected $baseFieldDefinition;
/**
* Creates a base field override object.
*
* @param \Drupal\Core\Field\BaseFieldDefinition $base_field_definition
* The base field definition to override.
* @param string $bundle
* The bundle to which the override applies.
*
* @return \Drupal\Core\Field\Entity\BaseFieldOverride
* A new base field override object.
*/
public static function createFromBaseFieldDefinition(BaseFieldDefinition $base_field_definition, $bundle) {
$values = $base_field_definition->toArray();
$values['bundle'] = $bundle;
$values['baseFieldDefinition'] = $base_field_definition;
return \Drupal::entityManager()->getStorage('base_field_override')->create($values);
}
/**
* Constructs a BaseFieldOverride object.
*
* In most cases, base field override entities are created via
* BaseFieldOverride::createFromBaseFieldDefinition($definition, 'bundle')
*
* @param array $values
* An array of base field bundle override properties, keyed by property
* name. The field to override is specified by referring to an existing
* field with:
* - field_name: The field name.
* - entity_type: The entity type.
* Additionally, a 'bundle' property is required to indicate the entity
* bundle to which the bundle field override is attached to. Other array
* elements will be used to set the corresponding properties on the class;
* see the class property documentation for details.
* @param string $entity_type
* (optional) The type of the entity to create. Defaults to
* 'base_field_override'.
*
* @see entity_create()
*
* @throws \Drupal\Core\Field\FieldException
* Exception thrown if $values does not contain a field_name, entity_type or
* bundle value.
*/
public function __construct(array $values, $entity_type = 'base_field_override') {
if (empty($values['field_name'])) {
throw new FieldException('Attempt to create a base field bundle override of a field without a field_name');
}
if (empty($values['entity_type'])) {
throw new FieldException(SafeMarkup::format('Attempt to create a base field bundle override of field @field_name without an entity_type', array('@field_name' => $values['field_name'])));
}
if (empty($values['bundle'])) {
throw new FieldException(SafeMarkup::format('Attempt to create a base field bundle override of field @field_name without a bundle', array('@field_name' => $values['field_name'])));
}
parent::__construct($values, $entity_type);
}
/**
* {@inheritdoc}
*/
public function getFieldStorageDefinition() {
return $this->getBaseFieldDefinition()->getFieldStorageDefinition();
}
/**
* {@inheritdoc}
*/
public function isDisplayConfigurable($context) {
return $this->getBaseFieldDefinition()->isDisplayConfigurable($context);
}
/**
* {@inheritdoc}
*/
public function getDisplayOptions($display_context) {
return $this->getBaseFieldDefinition()->getDisplayOptions($display_context);
}
/**
* {@inheritdoc}
*/
public function isReadOnly() {
return $this->getBaseFieldDefinition()->isReadOnly();
}
/**
* {@inheritdoc}
*/
public function isComputed() {
return $this->getBaseFieldDefinition()->isComputed();
}
/**
* Gets the base field definition.
*
* @return \Drupal\Core\Field\BaseFieldDefinition
*/
protected function getBaseFieldDefinition() {
if (!isset($this->baseFieldDefinition)) {
$fields = $this->entityManager()->getBaseFieldDefinitions($this->entity_type);
$this->baseFieldDefinition = $fields[$this->getName()];
}
return $this->baseFieldDefinition;
}
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Field\FieldException
* If the bundle is being changed and
* BaseFieldOverride::allowBundleRename() has not been called.
*/
public function preSave(EntityStorageInterface $storage) {
// Filter out unknown settings and make sure all settings are present, so
// that a complete field definition is passed to the various hooks and
// written to config.
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$default_settings = $field_type_manager->getDefaultFieldSettings($this->getType());
$this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
// Call the parent's presave method to perform validate and calculate
// dependencies.
parent::preSave($storage);
if ($this->isNew()) {
// @todo This assumes that the previous definition isn't some
// non-config-based override, but that might not be the case:
// https://www.drupal.org/node/2321071.
$previous_definition = $this->getBaseFieldDefinition();
}
else {
// Some updates are always disallowed.
if ($this->entity_type != $this->original->entity_type) {
throw new FieldException(SafeMarkup::format('Cannot change the entity_type of an existing base field bundle override (entity type:@entity_type, bundle:@bundle, field name: @field_name)', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type, '@bundle' => $this->original->bundle)));
}
if ($this->bundle != $this->original->bundle && empty($this->bundleRenameAllowed)) {
throw new FieldException(SafeMarkup::format('Cannot change the bundle of an existing base field bundle override (entity type:@entity_type, bundle:@bundle, field name: @field_name)', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type, '@bundle' => $this->original->bundle)));
}
$previous_definition = $this->original;
}
// Notify the entity storage.
$this->entityManager()->getStorage($this->getTargetEntityTypeId())->onFieldDefinitionUpdate($this, $previous_definition);
}
/**
* {@inheritdoc}
*/
public static function postDelete(EntityStorageInterface $storage, array $field_overrides) {
$entity_manager = \Drupal::entityManager();
// Clear the cache upfront, to refresh the results of getBundles().
$entity_manager->clearCachedFieldDefinitions();
/** @var \Drupal\Core\Field\Entity\BaseFieldOverride $field_override */
foreach ($field_overrides as $field_override) {
// Inform the system that the field definition is being updated back to
// its non-overridden state.
// @todo This assumes that there isn't a non-config-based override that
// we're returning to, but that might not be the case:
// https://www.drupal.org/node/2321071.
$entity_manager->getStorage($field_override->getTargetEntityTypeId())->onFieldDefinitionUpdate($field_override->getBaseFieldDefinition(), $field_override);
}
}
/**
* Loads a base field bundle override config entity.
*
* @param string $entity_type_id
* ID of the entity type.
* @param string $bundle
* Bundle name.
* @param string $field_name
* Name of the field.
*
* @return static
* The base field bundle override config entity if one exists for the
* provided field name, otherwise NULL.
*/
public static function loadByName($entity_type_id, $bundle, $field_name) {
return \Drupal::entityManager()->getStorage('base_field_override')->load($entity_type_id . '.' . $bundle . '.' . $field_name);
}
/**
* Implements the magic __sleep() method.
*/
public function __sleep() {
// Only serialize necessary properties, excluding those that can be
// recalculated.
unset($this->baseFieldDefinition);
return parent::__sleep();
}
}

View file

@ -0,0 +1,120 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\EntityReferenceFieldItemList.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines a item list class for entity reference fields.
*/
class EntityReferenceFieldItemList extends FieldItemList implements EntityReferenceFieldItemListInterface {
/**
* {@inheritdoc}
*/
public function referencedEntities() {
if (empty($this->list)) {
return array();
}
// Collect the IDs of existing entities to load, and directly grab the
// "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) {
$ids[$delta] = $item->target_id;
}
}
// Load and add the existing entities.
if ($ids) {
$target_type = $this->getFieldDefinition()->getSetting('target_type');
$entities = \Drupal::entityManager()->getStorage($target_type)->loadMultiple($ids);
foreach ($ids as $delta => $target_id) {
if (isset($entities[$target_id])) {
$target_entities[$delta] = $entities[$target_id];
}
}
// Ensure the returned array is ordered by deltas.
ksort($target_entities);
}
return $target_entities;
}
/**
* {@inheritdoc}
*/
public static function processDefaultValue($default_value, FieldableEntityInterface $entity, FieldDefinitionInterface $definition) {
$default_value = parent::processDefaultValue($default_value, $entity, $definition);
if ($default_value) {
// Convert UUIDs to numeric IDs.
$uuids = array();
foreach ($default_value as $delta => $properties) {
if (isset($properties['target_uuid'])) {
$uuids[$delta] = $properties['target_uuid'];
}
}
if ($uuids) {
$target_type = $definition->getSetting('target_type');
$entity_ids = \Drupal::entityQuery($target_type)
->condition('uuid', $uuids, 'IN')
->execute();
$entities = \Drupal::entityManager()
->getStorage($target_type)
->loadMultiple($entity_ids);
$entity_uuids = array();
foreach ($entities as $id => $entity) {
$entity_uuids[$entity->uuid()] = $id;
}
foreach ($uuids as $delta => $uuid) {
if (isset($entity_uuids[$uuid])) {
$default_value[$delta]['target_id'] = $entity_uuids[$uuid];
unset($default_value[$delta]['target_uuid']);
}
else {
unset($default_value[$delta]);
}
}
}
// Ensure we return consecutive deltas, in case we removed unknown UUIDs.
$default_value = array_values($default_value);
}
return $default_value;
}
/**
* {@inheritdoc}
*/
public function defaultValuesFormSubmit(array $element, array &$form, FormStateInterface $form_state) {
$default_value = parent::defaultValuesFormSubmit($element, $form, $form_state);
// Convert numeric IDs to UUIDs to ensure config deployability.
$ids = array();
foreach ($default_value as $delta => $properties) {
$ids[] = $properties['target_id'];
}
$entities = \Drupal::entityManager()
->getStorage($this->getSetting('target_type'))
->loadMultiple($ids);
foreach ($default_value as $delta => $properties) {
unset($default_value[$delta]['target_id']);
$default_value[$delta]['target_uuid'] = $entities[$properties['target_id']]->uuid();
}
return $default_value;
}
}

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\EntityReferenceFieldItemListInterface.
*/
namespace Drupal\Core\Field;
/**
* Interface for entity reference lists of field items.
*/
interface EntityReferenceFieldItemListInterface extends FieldItemListInterface {
/**
* Gets the entities referenced by this field, preserving field item deltas.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* An array of entity objects keyed by field item deltas.
*/
public function referencedEntities();
}

View file

@ -0,0 +1,574 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldConfigBase.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Component\Utility\SafeMarkup;
/**
* Base class for configurable field definitions.
*/
abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
/**
* The field ID.
*
* The ID consists of 3 parts: the entity type, bundle and the field name.
*
* Example: node.article.body, user.user.field_main_image.
*
* @var string
*/
protected $id;
/**
* The field name.
*
* @var string
*/
protected $field_name;
/**
* The field type.
*
* This property is denormalized from the field storage for optimization of
* the "entity and render cache hits" critical paths. If not present in the
* $values passed to create(), it is populated from the field storage in
* postCreate(), and saved in config records so that it is present on
* subsequent loads.
*
* @var string
*/
protected $field_type;
/**
* The name of the entity type the field is attached to.
*
* @var string
*/
protected $entity_type;
/**
* The name of the bundle the field is attached to.
*
* @var string
*/
protected $bundle;
/**
* The human-readable label for the field.
*
* This will be used as the title of Form API elements for the field in entity
* edit forms, or as the label for the field values in displayed entities.
*
* If not specified, this defaults to the field_name (mostly useful for fields
* created in tests).
*
* @var string
*/
protected $label;
/**
* The field description.
*
* A human-readable description for the field when used with this bundle.
* For example, the description will be the help text of Form API elements for
* this field in entity edit forms.
*
* @var string
*/
protected $description = '';
/**
* Field-type specific settings.
*
* An array of key/value pairs. The keys and default values are defined by the
* field type.
*
* @var array
*/
protected $settings = array();
/**
* Flag indicating whether the field is required.
*
* TRUE if a value for this field is required when used with this bundle,
* FALSE otherwise. Currently, required-ness is only enforced at the Form API
* level in entity edit forms, not during direct API saves.
*
* @var bool
*/
protected $required = FALSE;
/**
* Flag indicating whether the field is translatable.
*
* Defaults to TRUE.
*
* @var bool
*/
protected $translatable = TRUE;
/**
* Default field value.
*
* The default value is used when an entity is created, either:
* - through an entity creation form; the form elements for the field are
* prepopulated with the default value.
* - through direct API calls (i.e. $entity->save()); the default value is
* added if the $entity object provides no explicit entry (actual values or
* "the field is empty") for the field.
*
* The default value is expressed as a numerically indexed array of items,
* each item being an array of key/value pairs matching the set of 'columns'
* defined by the "field schema" for the field type, as exposed in
* hook_field_schema(). If the number of items exceeds the cardinality of the
* field, extraneous items will be ignored.
*
* This property is overlooked if the $default_value_callback is non-empty.
*
* Example for a integer field:
* @code
* array(
* array('value' => 1),
* array('value' => 2),
* )
* @endcode
*
* @var array
*/
public $default_value = array();
/**
* The name of a callback function that returns default values.
*
* The function will be called with the following arguments:
* - \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity being created.
* - \Drupal\Core\Field\FieldDefinitionInterface $definition
* The field definition.
* It should return an array of default values, in the same format as the
* $default_value property.
*
* This property takes precedence on the list of fixed values specified in the
* $default_value property.
*
* @var string
*/
protected $default_value_callback = '';
/**
* The field storage object.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorage;
/**
* The data definition of a field item.
*
* @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition
*/
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.
*
* @var array
*/
protected $constraints = [];
/**
* {@inheritdoc}
*/
public function id() {
return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
}
/**
* {@inheritdoc}
*/
public function getName() {
return $this->field_name;
}
/**
* {@inheritdoc}
*/
public function getType() {
return $this->field_type;
}
/**
* {@inheritdoc}
*/
public function getTargetEntityTypeId() {
return $this->entity_type;
}
/**
* {@inheritdoc}
*/
public function getTargetBundle() {
return $this->bundle;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
parent::calculateDependencies();
// Add dependencies from the field type plugin. We can not use
// self::calculatePluginDependencies() because instantiation of a field item
// plugin requires a parent entity.
/** @var $field_type_manager \Drupal\Core\Field\FieldTypePluginManagerInterface */
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$definition = $field_type_manager->getDefinition($this->getType());
$this->addDependency('module', $definition['provider']);
// Plugins can declare additional dependencies in their definition.
if (isset($definition['config_dependencies'])) {
$this->addDependencies($definition['config_dependencies']);
}
// Let the field type plugin specify its own dependencies.
// @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
$this->addDependencies($definition['class']::calculateDependencies($this));
// If the target entity type uses entities to manage its bundles then
// depend on the bundle entity.
$bundle_entity_type_id = $this->entityManager()->getDefinition($this->entity_type)->getBundleEntityType();
if ($bundle_entity_type_id != 'bundle') {
if (!$bundle_entity = $this->entityManager()->getStorage($bundle_entity_type_id)->load($this->bundle)) {
throw new \LogicException(SafeMarkup::format('Missing bundle entity, entity type %type, entity id %bundle.', array('%type' => $bundle_entity_type_id, '%bundle' => $this->bundle)));
}
$this->addDependency('config', $bundle_entity->getConfigDependencyName());
}
return $this->dependencies;
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$definition = $field_type_manager->getDefinition($this->getType());
$changed = $definition['class']::onDependencyRemoval($this, $dependencies);
return $changed;
}
/**
* {@inheritdoc}
*/
public function postCreate(EntityStorageInterface $storage) {
parent::postCreate($storage);
// If it was not present in the $values passed to create(), (e.g. for
// programmatic creation), populate the denormalized field_type property
// from the field storage, so that it gets saved in the config record.
if (empty($this->field_type)) {
$this->field_type = $this->getFieldStorageDefinition()->getType();
}
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
// Clear the cache.
$this->entityManager()->clearCachedFieldDefinitions();
// Invalidate the render cache for all affected entities.
$entity_type = $this->getFieldStorageDefinition()->getTargetEntityTypeId();
if ($this->entityManager()->hasHandler($entity_type, 'view_builder')) {
$this->entityManager()->getViewBuilder($entity_type)->resetCache();
}
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label();
}
/**
* {@inheritdoc}
*/
public function setLabel($label) {
$this->label = $label;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* {@inheritdoc}
*/
public function isTranslatable() {
// A field can be enabled for translation only if translation is supported.
return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
}
/**
* {@inheritdoc}
*/
public function setTranslatable($translatable) {
$this->translatable = $translatable;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSettings() {
return $this->settings + $this->getFieldStorageDefinition()->getSettings();
}
/**
* {@inheritdoc}
*/
public function setSettings(array $settings) {
$this->settings = $settings;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSetting($setting_name) {
if (array_key_exists($setting_name, $this->settings)) {
return $this->settings[$setting_name];
}
else {
return $this->getFieldStorageDefinition()->getSetting($setting_name);
}
}
/**
* {@inheritdoc}
*/
public function setSetting($setting_name, $value) {
$this->settings[$setting_name] = $value;
}
/**
* {@inheritdoc}
*/
public function isRequired() {
return $this->required;
}
/**
* [@inheritdoc}
*/
public function setRequired($required) {
$this->required = $required;
return $this;
}
/**
* {@inheritdoc}
*/
public function getDefaultValue(FieldableEntityInterface $entity) {
// Allow custom default values function.
if ($callback = $this->default_value_callback) {
$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),
);
}
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
}
/**
* Implements the magic __sleep() method.
*
* Using the Serialize interface and serialize() / unserialize() methods
* breaks entity forms in PHP 5.4.
* @todo Investigate in https://www.drupal.org/node/2074253.
*/
public function __sleep() {
// Only serialize necessary properties, excluding those that can be
// recalculated.
$properties = get_object_vars($this);
unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['bundleRenameAllowed'], $properties['original']);
return array_keys($properties);
}
/**
* {@inheritdoc}
*/
public static function createFromItemType($item_type) {
// Forward to the field definition class for creating new data definitions
// via the typed manager.
return BaseFieldDefinition::createFromItemType($item_type);
}
/**
* {@inheritdoc}
*/
public static function createFromDataType($type) {
// Forward to the field definition class for creating new data definitions
// via the typed manager.
return BaseFieldDefinition::createFromDataType($type);
}
/**
* {@inheritdoc}
*/
public function getDataType() {
return 'list';
}
/**
* {@inheritdoc}
*/
public function isList() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getClass() {
// Derive list class from the field type.
$type_definition = \Drupal::service('plugin.manager.field.field_type')
->getDefinition($this->getType());
return $type_definition['list_class'];
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
}
/**
* {@inheritdoc}
*/
public function getConstraint($constraint_name) {
$constraints = $this->getConstraints();
return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
}
/**
* {@inheritdoc}
*/
public function getItemDefinition() {
if (!isset($this->itemDefinition)) {
$this->itemDefinition = FieldItemDataDefinition::create($this)
->setSettings($this->getSettings());
}
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}
*/
public function getConfig($bundle) {
return $this;
}
/**
* {@inheritdoc}
*/
public function setConstraints(array $constraints) {
$this->constraints = $constraints;
}
/**
* {@inheritdoc}
*/
public function addConstraint($constraint_name, $options = NULL) {
$this->constraints[$constraint_name] = $options;
}
/**
* {@inheritdoc}
*/
public function setPropertyConstraints($name, array $constraints) {
$item_constraints = $this->getItemDefinition()->getConstraints();
$item_constraints['ComplexData'][$name] = $constraints;
$this->getItemDefinition()->setConstraints($item_constraints);
return $this;
}
/**
* {@inheritdoc}
*/
public function addPropertyConstraints($name, array $constraints) {
$item_constraints = $this->getItemDefinition()->getConstraint('ComplexData') ?: [];
if (isset($item_constraints[$name])) {
// Add the new property constraints, overwriting as required.
$item_constraints[$name] = $constraints + $item_constraints[$name];
}
else {
$item_constraints[$name] = $constraints;
}
$this->getItemDefinition()->addConstraint('ComplexData', $item_constraints);
return $this;
}
}

View file

@ -0,0 +1,246 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldConfigInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Defines an interface for configurable field definitions.
*
* This interface allows both configurable fields and overridden base fields to
* share a common interface. The interface also extends ConfigEntityInterface
* to ensure that implementations have the expected save() method.
*
* @see \Drupal\Core\Field\Entity\BaseFieldOverride
* @see \Drupal\field\Entity\FieldConfig
*/
interface FieldConfigInterface extends FieldDefinitionInterface, ConfigEntityInterface {
/**
* Sets the field definition label.
*
* @param string $label
* The label to set.
*
* @return $this
*/
public function setLabel($label);
/**
* Sets a human readable description.
*
* Descriptions are usually used on user interfaces where the data is edited
* or displayed.
*
* @param string $description
* The description for this field.
*
* @return $this
*/
public function setDescription($description);
/**
* Sets whether the field is translatable.
*
* @param bool $translatable
* Whether the field is translatable.
*
* @return $this
*/
public function setTranslatable($translatable);
/**
* Sets field settings (overwrites existing settings).
*
* @param array $settings
* The array of field settings.
*
* @return $this
*/
public function setSettings(array $settings);
/**
* Sets the value for a field setting by name.
*
* @param string $setting_name
* The name of the setting.
* @param mixed $value
* The value of the setting.
*
* @return $this
*/
public function setSetting($setting_name, $value);
/**
* Sets whether the field can be empty.
*
* If a field is required, an entity needs to have at least a valid,
* non-empty item in that field's FieldItemList in order to pass validation.
*
* An item is considered empty if its isEmpty() method returns TRUE.
* Typically, that is if at least one of its required properties is empty.
*
* @param bool $required
* TRUE if the field is required. FALSE otherwise.
*
* @return $this
* The current object, for a fluent interface.
*/
public function setRequired($required);
/**
* 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 in the format as returned by
* \Drupal\Core\Field\FieldDefinitionInterface::getDefaultValue().
*
* @return $this
*/
public function setDefaultValue($value);
/**
* Sets constraints for a given field item property.
*
* Note: this overwrites any existing property constraints. If you need to
* add to the existing constraints, use
* \Drupal\Core\Field\FieldConfigInterface::addPropertyConstraints()
*
* Note that constraints added via this method are not stored in configuration
* and as such need to be added at runtime using
* hook_entity_bundle_field_info_alter().
*
* @param string $name
* The name of the property to set constraints for.
* @param array $constraints
* The constraints to set.
*
* @return static
* The object itself for chaining.
*
* @see hook_entity_bundle_field_info_alter()
*/
public function setPropertyConstraints($name, array $constraints);
/**
* Adds constraints for a given field item property.
*
* Adds a constraint to a property of a field item. e.g.
* @code
* // Limit the field item's value property to the range 0 through 10.
* // e.g. $node->field_how_many->value.
* $field->addPropertyConstraints('value', [
* 'Range' => [
* 'min' => 0,
* 'max' => 10,
* ]
* ]);
* @endcode
*
* If you want to add a validation constraint that applies to the
* \Drupal\Core\Field\FieldItemList, use FieldConfigInterface::addConstraint()
* instead.
*
* Note: passing a new set of options for an existing property constraint will
* overwrite with the new options.
*
* Note that constraints added via this method are not stored in configuration
* and as such need to be added at runtime using
* hook_entity_bundle_field_info_alter().
*
* @param string $name
* The name of the property to set constraints for.
* @param array $constraints
* The constraints to set.
*
* @return static
* The object itself for chaining.
*
* @see \Drupal\Core\Field\FieldConfigInterface::addConstraint()
* @see hook_entity_bundle_field_info_alter()
*/
public function addPropertyConstraints($name, array $constraints);
/**
* Adds a validation constraint to the FieldItemList.
*
* Note: If you wish to apply a constraint to just a property of a FieldItem
* use \Drupal\Core\Field\FieldConfigInterface::addPropertyConstraints()
* instead.
* @code
* // Add a constraint to the 'field_username' FieldItemList.
* // e.g. $node->field_username
* $fields['field_username']->addConstraint('UserNameUnique', []);
* @endcode
*
* If you wish to apply a constraint to a \Drupal\Core\Field\FieldItem instead
* of a property or FieldItemList, you can use the
* \Drupal\Core\Field\FieldConfigBase::getItemDefinition() method.
* @code
* // Add a constraint to the 'field_entity_reference' FieldItem (entity
* // reference item).
* $fields['field_entity_reference']->getItemDefinition()->addConstraint('MyCustomFieldItemValidationPlugin', []);
* @endcode
*
* See \Drupal\Core\TypedData\DataDefinitionInterface::getConstraints() for
* details.
*
* Note that constraints added via this method are not stored in configuration
* and as such need to be added at runtime using
* hook_entity_bundle_field_info_alter().
*
* @param string $constraint_name
* The name of the constraint to add, i.e. its plugin id.
* @param array|null $options
* The constraint options as required by the constraint plugin, or NULL.
*
* @return static
* The object itself for chaining.
*
* @see \Drupal\Core\Field\FieldItemList
* @see \Drupal\Core\Field\FieldConfigInterface::addPropertyConstraints()
* @see hook_entity_bundle_field_info_alter()
*/
public function addConstraint($constraint_name, $options = NULL);
/**
* Sets the array of validation constraints for the FieldItemList.
*
* NOTE: This will overwrite any previously set constraints. In most cases
* FieldConfigInterface::addConstraint() should be used instead.
*
* Note that constraints added via this method are not stored in configuration
* and as such need to be added at runtime using
* hook_entity_bundle_field_info_alter().
*
* @param array $constraints
* The array of constraints. See
* \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
*
* @return $this
*
* @see \Drupal\Core\TypedData\DataDefinition::addConstraint()
* @see \Drupal\Core\TypedData\DataDefinition::getConstraints()
* @see \Drupal\Core\Field\FieldItemList
* @see hook_entity_bundle_field_info_alter()
*/
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

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldConfigStorageBase.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityInterface;
/**
* Base storage class for field config entities.
*/
abstract class FieldConfigStorageBase extends ConfigEntityStorage {
/**
* The field type plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypeManager;
/**
* {@inheritdoc}
*/
protected function mapFromStorageRecords(array $records) {
foreach ($records as &$record) {
$class = $this->fieldTypeManager->getPluginClass($record['field_type']);
$record['settings'] = $class::fieldSettingsFromConfigData($record['settings']);
}
return parent::mapFromStorageRecords($records);
}
/**
* {@inheritdoc}
*/
protected function mapToStorageRecord(EntityInterface $entity) {
$record = parent::mapToStorageRecord($entity);
$class = $this->fieldTypeManager->getPluginClass($record['field_type']);
$record['settings'] = $class::fieldSettingsToConfigData($record['settings']);
return $record;
}
}

View file

@ -0,0 +1,225 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldDefinitionInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\TypedData\ListDataDefinitionInterface;
/**
* Defines an interface for entity field definitions.
*
* An entity field is a data object that holds the values of a particular field
* for a particular entity (see \Drupal\Core\Field\FieldItemListInterface). For
* example, $node_1->body and $node_2->body contain different data and therefore
* are different field objects.
*
* In contrast, an entity field *definition* is an object that returns
* information *about* a field (e.g., its type and settings) rather than its
* values. As such, if all the information about $node_1->body and $node_2->body
* is the same, then the same field definition object can be used to describe
* both.
*
* It is up to the class implementing this interface to manage where the
* information comes from. For example, field.module provides an implementation
* based on two levels of configuration. It allows the site administrator to add
* custom fields to any entity type and bundle via the "field_storage_config"
* and "field_config" configuration entities. The former for storing
* configuration that is independent of which entity type and bundle the field
* is added to, and the latter for storing configuration that is specific to the
* entity type and bundle. The class that implements "field_config"
* configuration entities also implements this interface, returning information
* from either itself, or from the corresponding "field_storage_config"
* configuration, as appropriate.
*
* However, entity base fields, such as $node->title, are not managed by
* field.module and its "field_storage_config"/"field_config"
* configuration entities. Therefore, their definitions are provided by
* different objects based on the class \Drupal\Core\Field\BaseFieldDefinition,
* which implements this interface as well.
*
* Field definitions may fully define a concrete data object (e.g.,
* $node_1->body), or may provide a best-guess definition for a data object that
* might come into existence later. For example, $node_1->body and $node_2->body
* may have different definitions (e.g., if the node types are different). When
* adding the "body" field to a View that can return nodes of different types,
* the View can get a field definition that represents the "body" field
* abstractly, and present Views configuration options to the administrator
* based on that abstract definition, even though that abstract definition can
* differ from the concrete definition of any particular node's body field.
*/
interface FieldDefinitionInterface extends ListDataDefinitionInterface, CacheableDependencyInterface {
/**
* Returns the machine name of the field.
*
* This defines how the field data is accessed from the entity. For example,
* if the field name is "foo", then $entity->foo returns its data.
*
* @return string
* The field name.
*/
public function getName();
/**
* Returns the field type.
*
* @return string
* The field type, i.e. the id of a field type plugin. For example 'text'.
*
* @see \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
public function getType();
/**
* Returns the ID of the entity type the field is attached to.
*
* This method should not be confused with EntityInterface::getEntityTypeId()
* (configurable fields are config entities, and thus implement both
* interfaces):
* - FieldDefinitionInterface::getTargetEntityTypeId() answers "as a field,
* which entity type are you attached to?".
* - EntityInterface::getEntityTypeId() answers "as a (config) entity, what
* is your own entity type?".
*
* @return string
* The entity type ID.
*/
public function getTargetEntityTypeId();
/**
* Gets the bundle the field is attached to.
*
* This method should not be confused with EntityInterface::bundle()
* (configurable fields are config entities, and thus implement both
* interfaces):
* - FieldDefinitionInterface::getTargetBundle() answers "as a field,
* which bundle are you attached to?".
* - EntityInterface::bundle() answers "as a (config) entity, what
* is your own bundle?" (not relevant in our case, the config entity types
* used to store the definitions of configurable fields do not have
* bundles).
*
* @return string|null
* The bundle the field is defined for, or NULL if it is a base field; i.e.,
* it is not bundle-specific.
*/
public function getTargetBundle();
/**
* Returns whether the display for the field can be configured.
*
* @param string $display_context
* The display context. Either 'view' or 'form'.
*
* @return bool
* TRUE if the display for this field is configurable in the given context.
* If TRUE, the display options returned by getDisplayOptions() may be
* overridden via the respective entity display.
*
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface
*/
public function isDisplayConfigurable($display_context);
/**
* Returns the default display options for the field.
*
* If the field's display is configurable, the returned display options act
* as default values and may be overridden via the respective entity display.
* Otherwise, the display options will be applied to entity displays as is.
*
* @param string $display_context
* The display context. Either 'view' or 'form'.
*
* @return array|null
* The array of display options for the field, or NULL if the field is not
* displayed. The following key/value pairs may be present:
* - label: (string) Position of the field label. The default 'field' theme
* implementation supports the values 'inline', 'above' and 'hidden'.
* Defaults to 'above'. Only applies to 'view' context.
* - type: (string) The plugin (widget or formatter depending on
* $display_context) to use, or 'hidden'. If not specified or if the
* requested plugin is unknown, the 'default_widget' / 'default_formatter'
* for the field type will be used.
* - settings: (array) Settings for the plugin specified above. The default
* settings for the plugin will be used for settings left unspecified.
* - third_party_settings: (array) Settings provided by other extensions
* through hook_field_formatter_third_party_settings_form().
* - weight: (float) The weight of the element. Not needed if 'type' is
* 'hidden'.
* The defaults of the various display options above get applied by the used
* entity display.
*
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface
*/
public function getDisplayOptions($display_context);
/**
* Returns whether the field can be empty.
*
* If a field is required, an entity needs to have at least a valid,
* non-empty item in that field's FieldItemList in order to pass validation.
*
* An item is considered empty if its isEmpty() method returns TRUE.
* Typically, that is if at least one of its required properties is empty.
*
* @return bool
* TRUE if the field is required.
*
* @see \Drupal\Core\TypedData\Plugin\DataType\ItemList::isEmpty()
* @see \Drupal\Core\Field\FieldItemInterface::isEmpty()
* @see \Drupal\Core\TypedData\DataDefinitionInterface:isRequired()
* @see \Drupal\Core\TypedData\TypedDataManager::getDefaultConstraints()
*/
public function isRequired();
/**
* Returns the default value for the field in a newly created entity.
*
* @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).
*/
public function getDefaultValue(FieldableEntityInterface $entity);
/**
* Returns whether the field is translatable.
*
* @return bool
* TRUE if the field is translatable.
*/
public function isTranslatable();
/**
* Returns the field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
* The field storage definition.
*/
public function getFieldStorageDefinition();
/**
* Gets an object that can be saved in configuration.
*
* Base fields are defined in code. In order to configure field definition
* properties per bundle use this method to create an override that can be
* saved in configuration.
*
* @see \Drupal\Core\Field\Entity\BaseFieldBundleOverride
*
* @param string $bundle
* The bundle to get the configurable field for.
*
* @return \Drupal\Core\Field\FieldConfigInterface
*/
public function getConfig($bundle);
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldDefinitionListenerInterface.
*/
namespace Drupal\Core\Field;
/**
* Defines an interface for reacting to field creation, deletion, and updates.
*/
interface FieldDefinitionListenerInterface {
/**
* Reacts to the creation of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition created.
*/
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition);
/**
* Reacts to the update of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being updated.
* @param \Drupal\Core\Field\FieldDefinitionInterface $original
* The original field definition; i.e., the definition before the update.
*/
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original);
/**
* Reacts to the deletion of a field.
*
* Stored values should not be wiped at once, but marked as 'deleted' so that
* they can go through a proper purge process later on.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being deleted.
*/
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition);
}

View file

@ -0,0 +1,16 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldException.
*/
namespace Drupal\Core\Field;
/**
* Base class for all exceptions thrown by the Entity Field API functions.
*
* This class has no functionality of its own other than allowing all
* Entity Field API exceptions to be caught by a single catch block.
*/
class FieldException extends \RuntimeException {}

View file

@ -0,0 +1,282 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldItemBase.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\Plugin\DataType\Map;
use Drupal\Core\TypedData\TypedDataInterface;
/**
* An entity field item.
*
* Entity field items making use of this base class have to implement
* the static method propertyDefinitions().
*
* @see \Drupal\Core\Field\FieldItemInterface
* @ingroup field_types
*/
abstract class FieldItemBase extends Map implements FieldItemInterface {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array();
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return array();
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
return 'value';
}
/**
* {@inheritdoc}
*/
public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
parent::__construct($definition, $name, $parent);
// Initialize computed properties by default, such that they get cloned
// with the whole item.
foreach ($this->definition->getPropertyDefinitions() as $name => $definition) {
if ($definition->isComputed()) {
$this->properties[$name] = \Drupal::typedDataManager()->getPropertyInstance($this, $name);
}
}
}
/**
* {@inheritdoc}
*/
public function getEntity() {
return $this->getParent()->getEntity();
}
/**
* {@inheritdoc}
*/
public function getLangcode() {
return $this->parent->getLangcode();
}
/**
* {@inheritdoc}
*/
public function getFieldDefinition() {
return $this->definition->getFieldDefinition();
}
/**
* Returns the array of field settings.
*
* @return array
* The array of settings.
*/
protected function getSettings() {
return $this->getFieldDefinition()->getSettings();
}
/**
* Returns the value of a field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
protected function getSetting($setting_name) {
return $this->getFieldDefinition()->getSetting($setting_name);
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the first property, if no array is
// given.
if (isset($values) && !is_array($values)) {
$keys = array_keys($this->definition->getPropertyDefinitions());
$values = array($keys[0] => $values);
}
parent::setValue($values, $notify);
}
/**
* {@inheritdoc}
*
* Different to the parent Map class, we avoid creating property objects as
* far as possible in order to optimize performance. Thus we just update
* $this->values if no property object has been created yet.
*/
protected function writePropertyValue($property_name, $value) {
// For defined properties there is either a property object or a plain
// value that needs to be updated.
if (isset($this->properties[$property_name])) {
$this->properties[$property_name]->setValue($value, FALSE);
}
// Allow setting plain values for not-defined properties also.
else {
$this->values[$property_name] = $value;
}
}
/**
* {@inheritdoc}
*/
public function __get($name) {
// There is either a property object or a plain value - possibly for a
// not-defined property. If we have a plain value, directly return it.
if (isset($this->properties[$name])) {
return $this->properties[$name]->getValue();
}
elseif (isset($this->values[$name])) {
return $this->values[$name];
}
}
/**
* {@inheritdoc}
*/
public function __set($name, $value) {
// Support setting values via property objects, but take care in as the
// value of the 'entity' property is typed data also.
if ($value instanceof TypedDataInterface && !($value instanceof EntityInterface)) {
$value = $value->getValue();
}
$this->set($name, $value);
}
/**
* {@inheritdoc}
*/
public function __isset($name) {
if (isset($this->properties[$name])) {
return $this->properties[$name]->getValue() !== NULL;
}
return isset($this->values[$name]);
}
/**
* {@inheritdoc}
*/
public function __unset($name) {
if ($this->definition->getPropertyDefinition($name)) {
$this->set($name, NULL);
}
else {
// Explicitly unset the property in $this->values if a non-defined
// property is unset, such that its key is removed from $this->values.
unset($this->values[$name]);
}
}
/**
* {@inheritdoc}
*/
public function view($display_options = array()) {
$view_builder = \Drupal::entityManager()->getViewBuilder($this->getEntity()->getEntityTypeId());
return $view_builder->viewFieldItem($this, $display_options);
}
/**
* {@inheritdoc}
*/
public function preSave() { }
/**
* {@inheritdoc}
*/
public function insert() { }
/**
* {@inheritdoc}
*/
public function update() { }
/**
* {@inheritdoc}
*/
public function delete() { }
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) { }
/**
* {@inheritdoc}
*/
public function deleteRevision() { }
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
return array();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
return array();
}
/**
* {@inheritdoc}
*/
public static function storageSettingsToConfigData(array $settings) {
return $settings;
}
/**
* {@inheritdoc}
*/
public static function storageSettingsFromConfigData(array $settings) {
return $settings;
}
/**
* {@inheritdoc}
*/
public static function fieldSettingsToConfigData(array $settings) {
return $settings;
}
/**
* {@inheritdoc}
*/
public static function fieldSettingsFromConfigData(array $settings) {
return $settings;
}
/**
* {@inheritdoc}
*/
public static function calculateDependencies(FieldDefinitionInterface $field_definition) {
return array();
}
/**
* {@inheritdoc}
*/
public static function onDependencyRemoval(FieldDefinitionInterface $field_definition, array $dependencies) {
return FALSE;
}
}

View file

@ -0,0 +1,424 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldItemInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
/**
* Interface for entity field items.
*
* Entity field items are typed data objects containing the field values, i.e.
* implementing the ComplexDataInterface.
*
* When implementing this interface which extends Traversable, make sure to list
* IteratorAggregate or Iterator before this interface in the implements clause.
*
* @see \Drupal\Core\Field\FieldItemListInterface
* @see \Drupal\Core\Field\FieldItemBase
* @ingroup field_types
*/
interface FieldItemInterface extends ComplexDataInterface {
/**
* Defines field item properties.
*
* Properties that are required to constitute a valid, non-empty item should
* be denoted with \Drupal\Core\TypedData\DataDefinition::setRequired().
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface[]
* An array of property definitions of contained properties, keyed by
* property name.
*
* @see \Drupal\Core\Field\BaseFieldDefinition
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition);
/**
* Returns the name of the main property, if any.
*
* Some field items consist mainly of one main property, e.g. the value of a
* text field or the @code target_id @endcode of an entity reference. If the
* field item has no main property, the method returns NULL.
*
* @return string|null
* The name of the value property, or NULL if there is none.
*
* @see \Drupal\Core\Field\BaseFieldDefinition
*/
public static function mainPropertyName();
/**
* Returns the schema for the field.
*
* This method is static because the field schema information is needed on
* creation of the field. FieldItemInterface objects instantiated at that
* time are not reliable as field settings might be missing.
*
* Computed fields having no schema should return an empty array.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_definition
* The field definition.
*
* @return array
* An empty array if there is no schema, or an associative array with the
* following key/value pairs:
* - columns: An array of Schema API column specifications, keyed by column
* name. The columns need to be a subset of the properties defined in
* propertyDefinitions(). The 'not null' property is ignored if present,
* as it is determined automatically by the storage controller depending
* on the table layout and the property definitions. It is recommended to
* avoid having the column definitions depend on field settings when
* possible. No assumptions should be made on how storage engines
* internally use the original column name to structure their storage.
* - unique keys: (optional) An array of Schema API unique key definitions.
* Only columns that appear in the 'columns' array are allowed.
* - indexes: (optional) An array of Schema API index definitions. Only
* columns that appear in the 'columns' array are allowed. Those indexes
* will be used as default indexes. Field definitions can specify
* additional indexes or, at their own risk, modify the default indexes
* specified by the field-type module. Some storage engines might not
* support indexes.
* - foreign keys: (optional) An array of Schema API foreign key
* definitions. Note, however, that the field data is not necessarily
* stored in SQL. Also, the possible usage is limited, as you cannot
* specify another field as related, only existing SQL tables,
* such as {taxonomy_term_data}.
*/
public static function schema(FieldStorageDefinitionInterface $field_definition);
/**
* Gets the entity that field belongs to.
*
* @return \Drupal\Core\Entity\FieldableEntityInterface
* The entity object.
*/
public function getEntity();
/**
* Gets the langcode of the field values held in the object.
*
* @return $langcode
* The langcode.
*/
public function getLangcode();
/**
* Gets the field definition.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition.
*/
public function getFieldDefinition();
/**
* Magic method: Gets a property value.
*
* @param $property_name
* The name of the property to get; e.g., 'title' or 'name'.
*
* @throws \InvalidArgumentException
* If a not existing property is accessed.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
* The property object.
*/
public function __get($property_name);
/**
* Magic method: Sets a property value.
*
* @param $property_name
* The name of the property to set; e.g., 'title' or 'name'.
* @param $value
* The value to set, or NULL to unset the property. Optionally, a typed
* data object implementing Drupal\Core\TypedData\TypedDataInterface may be
* passed instead of a plain value.
*
* @throws \InvalidArgumentException
* If a not existing property is set.
*/
public function __set($property_name, $value);
/**
* Magic method: Determines whether a property is set.
*
* @param $property_name
* The name of the property to get; e.g., 'title' or 'name'.
*
* @return boolean
* Returns TRUE if the property exists and is set, FALSE otherwise.
*/
public function __isset($property_name);
/**
* Magic method: Unsets a property.
*
* @param $property_name
* The name of the property to get; e.g., 'title' or 'name'.
*/
public function __unset($property_name);
/**
* Returns a renderable array for a single field item.
*
* @param array $display_options
* Can be either the name of a view mode, or an array of display settings.
* See EntityViewBuilderInterface::viewField() for more information.
*
* @return array
* A renderable array for the field item.
*
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewField()
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewFieldItem()
* @see \Drupal\Core\Field\FieldItemListInterface::view()
*/
public function view($display_options = array());
/**
* Defines custom presave behavior for field values.
*
* This method is called before insert() and update() methods, and before
* values are written into storage.
*/
public function preSave();
/**
* Defines custom insert behavior for field values.
*
* This method is called during the process of inserting an entity, just
* before values are written into storage.
*/
public function insert();
/**
* Defines custom update behavior for field values.
*
* This method is called during the process of updating an entity, just before
* values are written into storage.
*/
public function update();
/**
* Defines custom delete behavior for field values.
*
* This method is called during the process of deleting an entity, just before
* values are deleted from storage.
*/
public function delete();
/**
* Defines custom revision delete behavior for field values.
*
* This method is called from during the process of deleting an entity
* revision, just before the field values are deleted from storage. It is only
* called for entity types that support revisioning.
*/
public function deleteRevision();
/**
* Generates placeholder field values.
*
* Useful when populating site with placeholder content during site building
* or profiling.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
*
* @return array
* An associative array of values.
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition);
/**
* Defines the storage-level settings for this plugin.
*
* @return array
* A list of default settings, keyed by the setting name.
*/
public static function defaultStorageSettings();
/**
* Defines the field-level settings for this plugin.
*
* @return array
* A list of default settings, keyed by the setting name.
*/
public static function defaultFieldSettings();
/**
* Returns a settings array that can be stored as a configuration value.
*
* For all use cases where field settings are stored and managed as
* configuration, this method is used to map from the field type's
* representation of its settings to a representation compatible with
* deployable configuration. This includes:
* - Array keys at any depth must not contain a ".".
* - Ideally, array keys at any depth are either numeric or can be enumerated
* as a "mapping" within the configuration schema. While not strictly
* required, this simplifies configuration translation UIs, configuration
* migrations between Drupal versions, and other use cases.
* - To support configuration deployments, references to content entities
* must use UUIDs rather than local IDs.
*
* An example of a conversion between representations might be an
* "allowed_values" setting that's structured by the field type as a
* \Drupal\Core\TypedData\OptionsProviderInterface::getPossibleOptions()
* result (i.e., values as keys and labels as values). For such a use case,
* in order to comply with the above, this method could convert that
* representation to a numerically indexed array whose values are sub-arrays
* with the schema definable keys of "value" and "label".
*
* @param array $settings
* The field's settings in the field type's canonical representation.
*
* @return array
* An array (either the unmodified $settings or a modified representation)
* that is suitable for storing as a deployable configuration value.
*
* @see \Drupal\Core\Config\Config::set()
*/
public static function storageSettingsToConfigData(array $settings);
/**
* Returns a settings array in the field type's canonical representation.
*
* This function does the inverse of static::storageSettingsToConfigData(). It's
* called when loading a field's settings from a configuration object.
*
* @param array $settings
* The field's settings, as it is stored within a configuration object.
*
* @return array
* The settings, in the representation expected by the field type and code
* that interacts with it.
*
* @see \Drupal\Core\Field\FieldItemInterface::storageSettingsToConfigData()
*/
public static function storageSettingsFromConfigData(array $settings);
/**
* Returns a settings array that can be stored as a configuration value.
*
* Same as static::storageSettingsToConfigData(), but for the field's settings.
*
* @param array $settings
* The field's settings in the field type's canonical representation.
*
* @return array
* An array (either the unmodified $settings or a modified representation)
* that is suitable for storing as a deployable configuration value.
*
* @see \Drupal\Core\Field\FieldItemInterface::storageSettingsToConfigData()
*/
public static function fieldSettingsToConfigData(array $settings);
/**
* Returns a settings array in the field type's canonical representation.
*
* This function does the inverse of static::fieldSettingsToConfigData().
* It's called when loading a field's settings from a configuration
* object.
*
* @param array $settings
* The field's settings, as it is stored within a configuration
* object.
*
* @return array
* The field settings, in the representation expected by the field type
* and code that interacts with it.
*
* @see \Drupal\Core\Field\FieldItemInterface::fieldSettingsToConfigData()
*/
public static function fieldSettingsFromConfigData(array $settings);
/**
* Returns a form for the storage-level settings.
*
* Invoked from \Drupal\field_ui\Form\FieldStorageConfigEditForm to allow
* administrators to configure storage-level settings.
*
* Field storage might reject settings changes that affect the field
* storage schema if the storage already has data. When the $has_data
* parameter is TRUE, the form should not allow changing the settings that
* take part in the schema() method. It is recommended to set #access to
* FALSE on the corresponding elements.
*
* @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.
* @param bool $has_data
* TRUE if the field already has data, FALSE if not.
*
* @return
* The form definition for the field settings.
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data);
/**
* Returns a form for the field-level settings.
*
* Invoked from \Drupal\field_ui\Form\FieldConfigEditForm to allow
* administrators to configure field-level settings.
*
* @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.
*
* @return array
* The form definition for the field settings.
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state);
/**
* Calculates dependencies for field items.
*
* Dependencies are saved in the field configuration entity and are used to
* determine configuration synchronization order. For example, if the field
* type's default value is a content entity, this method should return an
* array of dependencies listing the content entities.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
*
* @return array
* An array of dependencies grouped by type (config, content, module,
* theme). For example:
* @code
* array(
* 'config' => array('user.role.anonymous', 'user.role.authenticated'),
* 'content' => array('node:article:f0a189e6-55fb-47fb-8005-5bef81c44d6d'),
* 'module' => array('node', 'user'),
* 'theme' => array('seven'),
* );
* @endcode
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getConfigDependencyName()
*/
public static function calculateDependencies(FieldDefinitionInterface $field_definition);
/**
* Informs the plugin that a dependency of the field will be deleted.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $dependencies
* An array of dependencies that will be deleted keyed by dependency type.
* Dependency types are, for example, entity, module and theme.
*
* @return bool
* TRUE if the field definition has been changed as a result, FALSE if not.
*
* @see \Drupal\Core\Config\ConfigEntityInterface::onDependencyRemoval()
*/
public static function onDependencyRemoval(FieldDefinitionInterface $field_definition, array $dependencies);
}

View file

@ -0,0 +1,409 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldItemList.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
use Drupal\Core\TypedData\TypedDataInterface;
/**
* Represents an entity field; that is, a list of field item objects.
*
* An entity field is a list of field items, each containing a set of
* properties. Note that even single-valued entity fields are represented as
* list of field items, however for easy access to the contained item the entity
* field delegates __get() and __set() calls directly to the first item.
*/
class FieldItemList extends ItemList implements FieldItemListInterface {
/**
* Numerically indexed array of field items.
*
* @var \Drupal\Core\Field\FieldItemInterface[]
*/
protected $list = array();
/**
* The langcode of the field values held in the object.
*
* @var string
*/
protected $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
/**
* {@inheritdoc}
*/
protected function createItem($offset = 0, $value = NULL) {
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($this, $offset, $value);
}
/**
* {@inheritdoc}
*/
public function getEntity() {
// The "parent" is the TypedData object for the entity, we need to unwrap
// the actual entity.
return $this->getParent()->getValue();
}
/**
* {@inheritdoc}
*/
public function setLangcode($langcode) {
$this->langcode = $langcode;
}
/**
* {@inheritdoc}
*/
public function getLangcode() {
return $this->langcode;
}
/**
* {@inheritdoc}
*/
public function getFieldDefinition() {
return $this->definition;
}
/**
* {@inheritdoc}
*/
public function getSettings() {
return $this->definition->getSettings();
}
/**
* {@inheritdoc}
*/
public function getSetting($setting_name) {
return $this->definition->getSetting($setting_name);
}
/**
* {@inheritdoc}
*/
public function filterEmptyItems() {
$this->filter(function ($item) {
return !$item->isEmpty();
});
return $this;
}
/**
* {@inheritdoc}
* @todo Revisit the need when all entity types are converted to NG entities.
*/
public function getValue($include_computed = FALSE) {
$values = array();
foreach ($this->list as $delta => $item) {
$values[$delta] = $item->getValue($include_computed);
}
return $values;
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
// Support passing in only the value of the first item, either as a litteral
// (value of the first property) or as an array of properties.
if (isset($values) && (!is_array($values) || (!empty($values) && !is_numeric(current(array_keys($values)))))) {
$values = array(0 => $values);
}
parent::setValue($values, $notify);
}
/**
* {@inheritdoc}
*/
public function __get($property_name) {
// For empty fields, $entity->field->property is NULL.
if ($item = $this->first()) {
return $item->__get($property_name);
}
}
/**
* {@inheritdoc}
*/
public function __set($property_name, $value) {
// For empty fields, $entity->field->property = $value automatically
// creates the item before assigning the value.
$item = $this->first() ?: $this->appendItem();
$item->__set($property_name, $value);
}
/**
* {@inheritdoc}
*/
public function __isset($property_name) {
if ($item = $this->first()) {
return $item->__isset($property_name);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function __unset($property_name) {
if ($item = $this->first()) {
$item->__unset($property_name);
}
}
/**
* {@inheritdoc}
*/
public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
$access_control_handler = \Drupal::entityManager()->getAccessControlHandler($this->getEntity()->getEntityTypeId());
return $access_control_handler->fieldAccess($operation, $this->getFieldDefinition(), $account, $this, $return_as_object);
}
/**
* {@inheritdoc}
*/
public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
// Grant access per default.
return AccessResult::allowed();
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
if ($value = $this->getFieldDefinition()->getDefaultValue($this->getEntity())) {
$this->setValue($value, $notify);
}
else {
// Create one field item and give it a chance to apply its defaults.
// Remove it if this ended up doing nothing.
// @todo Having to create an item in case it wants to set a value is
// absurd. Remove that in https://www.drupal.org/node/2356623.
$item = $this->first() ?: $this->appendItem();
$item->applyDefaultValue(FALSE);
$this->filterEmptyItems();
}
return $this;
}
/**
* {@inheritdoc}
*/
public function preSave() {
// Filter out empty items.
$this->filterEmptyItems();
$this->delegateMethod('preSave');
}
/**
* {@inheritdoc}
*/
public function insert() {
$this->delegateMethod('insert');
}
/**
* {@inheritdoc}
*/
public function update() {
$this->delegateMethod('update');
}
/**
* {@inheritdoc}
*/
public function delete() {
$this->delegateMethod('delete');
}
/**
* {@inheritdoc}
*/
public function deleteRevision() {
$this->delegateMethod('deleteRevision');
}
/**
* Calls a method on each FieldItem.
*
* @param string $method
* The name of the method.
*/
protected function delegateMethod($method) {
foreach ($this->list as $item) {
$item->{$method}();
}
}
/**
* {@inheritdoc}
*/
public function view($display_options = array()) {
$view_builder = \Drupal::entityManager()->getViewBuilder($this->getEntity()->getEntityTypeId());
return $view_builder->viewField($this, $display_options);
}
/**
* {@inheritdoc}
*/
public function generateSampleItems($count = 1) {
$field_definition = $this->getFieldDefinition();
$field_type_class = \Drupal::service('plugin.manager.field.field_type')->getPluginClass($field_definition->getType());
for ($delta = 0; $delta < $count; $delta++) {
$values[$delta] = $field_type_class::generateSampleValue($field_definition);
}
$this->setValue($values);
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
// Check that the number of values doesn't exceed the field cardinality. For
// form submitted values, this can only happen with 'multiple value'
// widgets.
$cardinality = $this->getFieldDefinition()->getFieldStorageDefinition()->getCardinality();
if ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
$constraints[] = \Drupal::typedDataManager()
->getValidationConstraintManager()
->create('Count', array(
'max' => $cardinality,
'maxMessage' => t('%name: this field cannot hold more than @count values.', array('%name' => $this->getFieldDefinition()->getLabel(), '@count' => $cardinality)),
));
}
return $constraints;
}
/**
* {@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);
$element = array('#parents' => array('default_value_input'));
$element += $widget->form($this, $element, $form_state);
return $element;
}
}
/**
* {@inheritdoc}
*/
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();
// Assign reported errors to the correct form element.
if (count($violations)) {
$widget->flagErrors($this, $violations, $element, $form_state);
}
}
/**
* {@inheritdoc}
*/
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();
}
/**
* {@inheritdoc}
*/
public static function processDefaultValue($default_value, FieldableEntityInterface $entity, FieldDefinitionInterface $definition) {
return $default_value;
}
/**
* Returns the widget object used in default value form.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the (entire) configuration form.
*
* @return \Drupal\Core\Field\WidgetInterface
* A Widget object.
*/
protected function defaultValueWidget(FormStateInterface $form_state) {
if (!$form_state->has('default_value_widget')) {
$entity = $this->getEntity();
// Force a non-required widget.
$definition = $this->getFieldDefinition();
$definition->setRequired(FALSE);
$definition->setDescription('');
// 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->getEntityTypeId(), $entity->bundle(), 'default');
$widget = $entity_form_display->getRenderer($this->getFieldDefinition()->getName());
if (!$widget) {
$widget = \Drupal::service('plugin.manager.field.widget')->getInstance(array('field_definition' => $this->getFieldDefinition()));
}
$form_state->set('default_value_widget', $widget);
}
return $form_state->get('default_value_widget');
}
/**
* {@inheritdoc}
*/
public function equals(FieldItemListInterface $list_to_compare) {
$columns = $this->getFieldDefinition()->getFieldStorageDefinition()->getColumns();
$count1 = count($this);
$count2 = count($list_to_compare);
if ($count1 === 0 && $count2 === 0) {
// Both are empty we can safely assume that it did not change.
return TRUE;
}
if ($count1 !== $count2) {
// One of them is empty but not the other one so the value changed.
return FALSE;
}
$value1 = $this->getValue();
$value2 = $list_to_compare->getValue();
if ($value1 === $value2) {
return TRUE;
}
// If the values are not equal ensure a consistent order of field item
// properties and remove properties which will not be saved.
$callback = function (&$value) use ($columns) {
if (is_array($value)) {
$value = array_intersect_key($value, $columns);
ksort($value);
}
};
array_walk($value1, $callback);
array_walk($value2, $callback);
return $value1 == $value2;
}
}

View file

@ -0,0 +1,274 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldItemListInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\TypedData\ListInterface;
/**
* Interface for fields, being lists of field items.
*
* This interface must be implemented by every entity field, whereas contained
* field items must implement the FieldItemInterface.
* Some methods of the fields are delegated to the first contained item, in
* particular get() and set() as well as their magic equivalences.
*
* Optionally, a typed data object implementing
* Drupal\Core\TypedData\TypedDataInterface may be passed to
* ArrayAccess::offsetSet() instead of a plain value.
*
* When implementing this interface which extends Traversable, make sure to list
* IteratorAggregate or Iterator before this interface in the implements clause.
*/
interface FieldItemListInterface extends ListInterface, AccessibleInterface {
/**
* Gets the entity that field belongs to.
*
* @return \Drupal\Core\Entity\EntityInterface
* The entity object.
*/
public function getEntity();
/**
* Sets the langcode of the field values held in the object.
*
* @param string $langcode
* The langcode.
*/
public function setLangcode($langcode);
/**
* Gets the langcode of the field values held in the object.
*
* @return $langcode
* The langcode.
*/
public function getLangcode();
/**
* Gets the field definition.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition.
*/
public function getFieldDefinition();
/**
* Returns the array of field settings.
*
* @return array
* An array of key/value pairs.
*/
public function getSettings();
/**
* Returns the value of a given field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getSetting($setting_name);
/**
* Contains the default access logic of this field.
*
* See \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess() for
* the parameter documentation.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function defaultAccess($operation = 'view', AccountInterface $account = NULL);
/**
* Filters out empty field items and re-numbers the item deltas.
*
* @return $this
*/
public function filterEmptyItems();
/**
* Magic method: Gets a property value of to the first field item.
*
* @see \Drupal\Core\Field\FieldItemInterface::__get()
*/
public function __get($property_name);
/**
* Magic method: Sets a property value of the first field item.
*
* @see \Drupal\Core\Field\FieldItemInterface::__set()
*/
public function __set($property_name, $value);
/**
* Magic method: Determines whether a property of the first field item is set.
*
* @see \Drupal\Core\Field\FieldItemInterface::__isset()
*/
public function __isset($property_name);
/**
* Magic method: Unsets a property of the first field item.
*
* @see \Drupal\Core\Field\FieldItemInterface::__unset()
*/
public function __unset($property_name);
/**
* Defines custom presave behavior for field values.
*
* This method is called before either insert() or update() methods, and
* before values are written into storage.
*/
public function preSave();
/**
* Defines custom insert behavior for field values.
*
* This method is called after the save() method, and before values are
* written into storage.
*/
public function insert();
/**
* Defines custom update behavior for field values.
*
* This method is called after the save() method, and before values are
* written into storage.
*/
public function update();
/**
* Defines custom delete behavior for field values.
*
* This method is called during the process of deleting an entity, just before
* values are deleted from storage.
*/
public function delete();
/**
* Defines custom revision delete behavior for field values.
*
* This method is called from during the process of deleting an entity
* revision, just before the field values are deleted from storage. It is only
* called for entity types that support revisioning.
*/
public function deleteRevision();
/**
* Returns a renderable array for the field items.
*
* @param array $display_options
* Can be either the name of a view mode, or an array of display settings.
* See EntityViewBuilderInterface::viewField() for more information.
*
* @return array
* A renderable array for the field values.
*
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewField()
* @see \Drupal\Core\Field\FieldItemInterface::view()
*/
public function view($display_options = array());
/*
* Populates a specified number of field items with valid sample data.
*
* @param int $count
* The number of items to create.
*/
public function generateSampleItems($count = 1);
/**
* Returns a form for the default value input.
*
* Invoked from \Drupal\field_ui\Form\FieldConfigEditForm to allow
* administrators to configure instance-level default value.
*
* @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.
*
* @return array
* The form definition for the field default value.
*/
public function defaultValuesForm(array &$form, FormStateInterface $form_state);
/**
* Validates the submitted default value.
*
* Invoked from \Drupal\field_ui\Form\FieldConfigEditForm 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 \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the (entire) configuration form.
*/
public function defaultValuesFormValidate(array $element, array &$form, FormStateInterface $form_state);
/**
* Processes the submitted default value.
*
* Invoked from \Drupal\field_ui\Form\FieldConfigEditForm 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 \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the (entire) configuration form.
*
* @return array
* The field default value.
*/
public function defaultValuesFormSubmit(array $element, array &$form, FormStateInterface $form_state);
/**
* Processes the default value before being applied.
*
* Defined or configured default values of a field might need some processing
* in order to be a valid runtime value for the field type; e.g., a date field
* could process the defined value of 'NOW' to a valid date.
*
* @param array
* The unprocessed default value defined for the field, as a numerically
* indexed array of items, each item being an array of property/value pairs.
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity for which the default value is generated.
* @param \Drupal\Core\Field\FieldDefinitionInterface $definition
* The definition of the field.
*
* @return array
* The return default value for the field.
*/
public static function processDefaultValue($default_value, FieldableEntityInterface $entity, FieldDefinitionInterface $definition);
/**
* Determines equality to another object implementing FieldItemListInterface.
*
* @param \Drupal\Core\Field\FieldItemListInterface $list_to_compare
* The field item list to compare to.
*
* @return bool
* TRUE if the field item lists are equal, FALSE if not.
*/
public function equals(FieldItemListInterface $list_to_compare);
}

View file

@ -0,0 +1,70 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldModuleUninstallValidator.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\FieldableEntityStorageInterface;
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Validates module uninstall readiness based on defined storage definitions.
*
* @todo Remove this once we support field purging for base fields. See
* https://www.drupal.org/node/2282119.
*/
class FieldModuleUninstallValidator implements ModuleUninstallValidatorInterface {
use StringTranslationTrait;
/**
* Constructs the object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
$this->entityManager = $entity_manager;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public function validate($module_name) {
$reasons = array();
// We skip fields provided by the Field module as it implements field
// purging.
if ($module_name != 'field') {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
// We skip entity types defined by the module as there must be no
// content to be able to uninstall them anyway.
// See \Drupal\Core\Entity\ContentUninstallValidator.
if ($entity_type->getProvider() != $module_name && $entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
foreach ($this->entityManager->getFieldStorageDefinitions($entity_type_id) as $storage_definition) {
if ($storage_definition->getProvider() == $module_name) {
$storage = $this->entityManager->getStorage($entity_type_id);
if ($storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
$reasons[] = $this->t('There is data for the field @field-name on entity type @entity_type', array(
'@field-name' => $storage_definition->getName(),
'@entity_type' => $entity_type->getLabel(),
));
}
}
}
}
}
}
return $reasons;
}
}

View file

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEvent.
*/
namespace Drupal\Core\Field;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Defines a base class for all field storage definition events.
*/
class FieldStorageDefinitionEvent extends GenericEvent {
/**
* The field storage definition.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $fieldStorageDefinition;
/**
* The original field storage definition.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
protected $original;
/**
* Constructs a new FieldStorageDefinitionEvent.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
* The field storage definition.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
* (optional) The original field storage definition. This should be passed
* only when updating the storage definition.
*/
public function __construct(FieldStorageDefinitionInterface $field_storage_definition, FieldStorageDefinitionInterface $original = NULL) {
$this->fieldStorageDefinition = $field_storage_definition;
$this->original = $original;
}
/**
* The field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
public function getFieldStorageDefinition() {
return $this->fieldStorageDefinition;
}
/**
* The original field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
*/
public function getOriginal() {
return $this->original;
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait.
*/
namespace Drupal\Core\Field;
/**
* Helper methods for FieldStorageDefinitionListenerInterface.
*
* This allows a class implementing FieldStorageDefinitionListenerInterface to
* subscribe and react to field storage definition events.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface
* @see \Drupal\Core\Field\FieldStorageDefinitionListenerInterface
*/
trait FieldStorageDefinitionEventSubscriberTrait {
/**
* Returns the subscribed events.
*
* @return array
* An array of subscribed event names.
*
* @see \Symfony\Component\EventDispatcher\EventSubscriberInterface::getSubscribedEvents()
*/
public static function getFieldStorageDefinitionEvents() {
$event = array('onFieldStorageDefinitionEvent', 100);
$events[FieldStorageDefinitionEvents::CREATE][] = $event;
$events[FieldStorageDefinitionEvents::UPDATE][] = $event;
$events[FieldStorageDefinitionEvents::DELETE][] = $event;
return $events;
}
/**
* Listener method for any field storage definition event.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionEvent $event
* The field storage definition event object.
* @param string $event_name
* The event name.
*/
public function onFieldStorageDefinitionEvent(FieldStorageDefinitionEvent $event, $event_name) {
switch ($event_name) {
case FieldStorageDefinitionEvents::CREATE:
$this->onFieldStorageDefinitionCreate($event->getFieldStorageDefinition());
break;
case FieldStorageDefinitionEvents::UPDATE:
$this->onFieldStorageDefinitionUpdate($event->getFieldStorageDefinition(), $event->getOriginal());
break;
case FieldStorageDefinitionEvents::DELETE:
$this->onFieldStorageDefinitionDelete($event->getFieldStorageDefinition());
break;
}
}
/**
* {@inheritdoc}
*/
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
}
/**
* {@inheritdoc}
*/
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
}
/**
* {@inheritdoc}
*/
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionEvents.
*/
namespace Drupal\Core\Field;
/**
* Contains all events thrown while handling field storage definitions.
*/
final class FieldStorageDefinitionEvents {
/**
* Name of the event triggered for field storage definition creation.
*
* This event allows you to respond to the creation of a new field storage
* definition. The event listener method receives a
* \Drupal\Core\Field\FieldStorageDefinitionEvent instance.
*
* @Event
*
* @see \Drupal\Core\Field\FieldStorageDefinitionEvent
* @see \Drupal\Core\Entity\EntityManager::onFieldStorageDefinitionCreate()
* @see \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait
*
* @var string
*/
const CREATE = 'field_storage.definition.create';
/**
* Name of the event triggered for field storage definition update.
*
* This event allows you to respond anytime a field storage definition is
* updated. The event listener method receives a
* \Drupal\Core\Field\FieldStorageDefinitionEvent instance.
*
* @Event
*
* @see \Drupal\Core\Field\FieldStorageDefinitionEvent
* @see \Drupal\Core\Entity\EntityManager::onFieldStorageDefinitionUpdate()
* @see \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait
*
* @var string
*/
const UPDATE = 'field_storage.definition.update';
/**
* Name of the event triggered for field storage definition deletion.
*
* This event allows you to respond anytime a field storage definition is
* deleted. The event listener method receives a
* \Drupal\Core\Field\FieldStorageDefinitionEvent instance.
*
* @Event
*
* @see \Drupal\Core\Field\FieldStorageDefinitionEvent
* @see \Drupal\Core\Entity\EntityManager::onFieldStorageDefinitionDelete()
* @see \Drupal\Core\Field\FieldStorageDefinitionEventSubscriberTrait
*
* @var string
*/
const DELETE = 'field_storage.definition.delete';
}

View file

@ -0,0 +1,335 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
/**
* Defines an interface for entity field storage definitions.
*
* Field storage definitions represent the part of full field definitions (see
* FieldDefinitionInterface) that is responsible for defining how the field is
* stored. While field definitions may differ by entity bundle, all of those
* bundle fields have to share the same common field storage definition. Thus,
* the storage definitions can be defined by entity type only.
* The bundle fields corresponding to a field storage definition may provide
* additional information; e.g., they may provide bundle-specific settings or
* constraints that are not present in the storage definition. However bundle
* fields may not override or alter any information provided by the storage
* definition except for the label and the description; e.g., any constraints
* and settings on the storage definition must be present on the bundle field as
* well.
*
* @see hook_entity_field_storage_info()
*/
interface FieldStorageDefinitionInterface extends CacheableDependencyInterface {
/**
* Value indicating a field accepts an unlimited number of values.
*/
const CARDINALITY_UNLIMITED = -1;
/**
* Returns the machine name of the field.
*
* This defines how the field data is accessed from the entity. For example,
* if the field name is "foo", then $entity->foo returns its data.
*
* @return string
* The field name.
*/
public function getName();
/**
* Returns the field type.
*
* @return string
* The field type, i.e. the id of a field type plugin. For example 'text'.
*
* @see \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
public function getType();
/**
* Returns the field 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.
*
* @return mixed[]
* An array of key/value pairs.
*/
public function getSettings();
/**
* Returns the value of a given field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getSetting($setting_name);
/**
* Returns whether the field supports translation.
*
* @return bool
* TRUE if the field supports translation.
*/
public function isTranslatable();
/**
* Sets whether the field supports translation.
*
* @param bool $translatable
* Whether the field supports translation.
*
* @return $this
*/
public function setTranslatable($translatable);
/**
* Returns whether the field is revisionable.
*
* @return bool
* TRUE if the field is revisionable.
*/
public function isRevisionable();
/**
* Determines whether the field is queryable via QueryInterface.
*
* @return bool
* TRUE if the field is queryable.
*/
public function isQueryable();
/**
* Returns the human-readable label for the field.
*
* @return string
* The field label.
*/
public function getLabel();
/**
* Returns the human-readable description for the field.
*
* This is displayed in addition to the label in places where additional
* descriptive information is helpful. For example, as help text below the
* form element in entity edit forms.
*
* @return string|null
* The field description, or NULL if no description is available.
*/
public function getDescription();
/**
* Gets an options provider for the given field item property.
*
* @param string $property_name
* The name of the property to get options for; e.g., 'value'.
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity for which the options should be provided.
*
* @return \Drupal\Core\TypedData\OptionsProviderInterface|null
* An options provider, or NULL if no options are defined.
*/
public function getOptionsProvider($property_name, FieldableEntityInterface $entity);
/**
* Returns whether the field can contain multiple items.
*
* @return bool
* TRUE if the field can contain multiple items, FALSE otherwise.
*/
public function isMultiple();
/**
* Returns the maximum number of items allowed for the field.
*
* Possible values are positive integers or
* FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED.
*
* @return int
* The field cardinality.
*/
public function getCardinality();
/**
* Gets the definition of a contained property.
*
* @param string $name
* The name of property.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface|null
* The definition of the property or NULL if the property does not exist.
*/
public function getPropertyDefinition($name);
/**
* Gets an array of property definitions of contained properties.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface[]
* An array of property definitions of contained properties, keyed by
* property name.
*/
public function getPropertyDefinitions();
/**
* Returns the names of the field's subproperties.
*
* A field is a list of items, and each item can contain one or more
* properties. All items for a given field contain the same property names,
* but the values can be different for each item.
*
* For example, an email field might just contain a single 'value' property,
* while a link field might contain 'title' and 'url' properties, and a text
* field might contain 'value', 'summary', and 'format' properties.
*
* @return string[]
* The property names.
*/
public function getPropertyNames();
/**
* Returns the name of the main property, if any.
*
* Some field items consist mainly of one main property, e.g. the value of a
* text field or the @code target_id @endcode of an entity reference. If the
* field item has no main property, the method returns NULL.
*
* @return string|null
* The name of the value property, or NULL if there is none.
*/
public function getMainPropertyName();
/**
* Returns the ID of the entity type the field is attached to.
*
* This method should not be confused with EntityInterface::getEntityTypeId()
* (configurable fields are config entities, and thus implement both
* interfaces):
* - FieldStorageDefinitionInterface::getTargetEntityTypeId() answers "as a
* field storage, which entity type are you attached to?".
* - EntityInterface::getEntityTypeId() answers "as a (config) entity, what
* is your own entity type?".
*
* @return string
* The entity type ID.
*/
public function getTargetEntityTypeId();
/**
* Returns the field schema.
*
* Note that this method returns an empty array for computed fields which have
* no schema.
*
* @return array[]
* The field schema, as an array of key/value pairs in the format returned
* by hook_field_schema():
* - columns: An array of Schema API column specifications, keyed by column
* name. This specifies what comprises a single value for a given field.
* No assumptions should be made on how storage backends internally use
* the original column name to structure their storage.
* - indexes: An array of Schema API index definitions. Some storage
* backends might not support indexes.
* - foreign keys: An array of Schema API foreign key definitions. Note,
* however, that depending on the storage backend specified for the field,
* the field data is not necessarily stored in SQL.
*/
public function getSchema();
/**
* Returns the field columns, as defined in the field schema.
*
* @return array[]
* The array of field columns, keyed by column name, in the same format
* returned by getSchema().
*
* @see \Drupal\Core\Field\FieldStorageDefinitionInterface::getSchema()
*/
public function getColumns();
/**
* Returns an array of validation constraints.
*
* See \Drupal\Core\TypedData\DataDefinitionInterface::getConstraints() for
* details.
*
* @return array[]
* An array of validation constraint definitions, keyed by constraint name.
* Each constraint definition can be used for instantiating
* \Symfony\Component\Validator\Constraint objects.
*
* @see \Symfony\Component\Validator\Constraint
*/
public function getConstraints();
/**
* Returns a validation constraint.
*
* See \Drupal\Core\TypedData\DataDefinitionInterface::getConstraints() for
* details.
*
* @param string $constraint_name
* The name of the constraint, i.e. its plugin id.
*
* @return array
* A validation constraint definition which can be used for instantiating a
* \Symfony\Component\Validator\Constraint object.
*
* @see \Symfony\Component\Validator\Constraint
*/
public function getConstraint($constraint_name);
/**
* Returns the name of the provider of this field.
*
* @return string
* The provider name; e.g., the module name.
*/
public function getProvider();
/**
* Returns the storage behavior for this field.
*
* Indicates whether the entity type's storage should take care of storing the
* field values or whether it is handled separately; e.g. by the
* module providing the field.
*
* @return bool
* FALSE if the storage takes care of storing the field, TRUE otherwise.
*/
public function hasCustomStorage();
/**
* Determines whether the field is a base field.
*
* Base fields are not specific to a given bundle or a set of bundles. This
* excludes configurable fields, as they are always attached to a specific
* bundle.
*
* @return bool
* Whether the field is a base field.
*/
public function isBaseField();
/**
* Returns a unique identifier for the field.
*
* @return string
*/
public function getUniqueStorageIdentifier();
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldStorageDefinitionListenerInterface.
*/
namespace Drupal\Core\Field;
/**
* Defines an interface for reacting to field storage definition creation, deletion, and updates.
*/
interface FieldStorageDefinitionListenerInterface {
/**
* Reacts to the creation of a field storage definition.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The definition being created.
*/
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition);
/**
* Reacts to the update of a field storage definition.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field being updated.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
* The original storage definition; i.e., the definition before the update.
*
* @throws \Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException
* Thrown when the update to the field is forbidden.
*/
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
/**
* Reacts to the deletion of a field storage definition.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field being deleted.
*/
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition);
}

View file

@ -0,0 +1,166 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldTypePluginManager.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBackendInterface;
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;
/**
* Plugin manager for 'field type' plugins.
*
* @ingroup field_types
*/
class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
use CategorizingPluginManagerTrait;
/**
* The typed data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManager
*/
protected $typedDataManager;
/**
* Constructs the FieldTypePluginManager object
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler.
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
* The typed data manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, TypedDataManager $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');
$this->typedDataManager = $typed_data_manager;
}
/**
* {@inheritdoc}
*
* Creates a field item, which is not part of an entity or field item list.
*
* @param string $field_type
* The field type, for which a field item should be created.
* @param array $configuration
* The plugin configuration array, i.e. an array with the following keys:
* - field_definition: The field definition object, i.e. an instance of
* Drupal\Core\Field\FieldDefinitionInterface.
*
* @return \Drupal\Core\Field\FieldItemInterface
* The instantiated object.
*/
public function createInstance($field_type, array $configuration = array()) {
$configuration['data_definition'] = $configuration['field_definition']->getItemDefinition();
return $this->typedDataManager->createInstance("field_item:$field_type", $configuration);
}
/**
* {@inheritdoc}
*/
public function createFieldItemList(FieldableEntityInterface $entity, $field_name, $values = NULL) {
// Leverage prototyping of the Typed Data API for fast instantiation.
return $this->typedDataManager->getPropertyInstance($entity->getTypedData(), $field_name, $values);
}
/**
* {@inheritdoc}
*/
public function createFieldItem(FieldItemListInterface $items, $index, $values = NULL) {
// Leverage prototyping of the Typed Data API for fast instantiation.
return $this->typedDataManager->getPropertyInstance($items, $index, $values);
}
/**
* {@inheritdoc}
*/
public function processDefinition(&$definition, $plugin_id) {
parent::processDefinition($definition, $plugin_id);
if (!isset($definition['list_class'])) {
$definition['list_class'] = '\Drupal\Core\Field\FieldItemList';
}
// Ensure that every field type has a category.
if (empty($definition['category'])) {
$definition['category'] = $this->t('General');
}
}
/**
* {@inheritdoc}
*/
public function getDefaultStorageSettings($type) {
$plugin_definition = $this->getDefinition($type, FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($type, $plugin_definition);
return $plugin_class::defaultStorageSettings();
}
return array();
}
/**
* {@inheritdoc}
*/
public function getDefaultFieldSettings($type) {
$plugin_definition = $this->getDefinition($type, FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($type, $plugin_definition);
return $plugin_class::defaultFieldSettings();
}
return array();
}
/**
* {@inheritdoc}
*/
public function getUiDefinitions() {
$definitions = $this->getDefinitions();
// Filter out definitions that can not be configured in Field UI.
$definitions = array_filter($definitions, function ($definition) {
return empty($definition['no_ui']);
});
// Add preconfigured definitions.
foreach ($definitions as $id => $definition) {
if (is_subclass_of($definition['class'], '\Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface')) {
foreach ($definition['class']::getPreconfiguredOptions() as $key => $option) {
$definitions['field_ui:' . $id . ':' . $key] = [
'label' => $option['label'],
] + $definition;
if (isset($option['category'])) {
$definitions['field_ui:' . $id . ':' . $key]['category'] = $option['category'];
}
}
}
}
return $definitions;
}
/**
* @inheritdoc
*/
public function getPluginClass($type) {
$plugin_definition = $this->getDefinition($type, FALSE);
return $plugin_definition['class'];
}
}

View file

@ -0,0 +1,103 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldTypePluginManagerInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\CategorizingPluginManagerInterface;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
/**
* Defines an interface for the field type plugin manager.
*
* @ingroup field_types
*/
interface FieldTypePluginManagerInterface extends PluginManagerInterface, CategorizingPluginManagerInterface {
/**
* Creates a new field item list.
*
* The provided entity is assigned as the parent of the created item list.
* However, it is the responsibility of the caller (usually the parent entity
* itself) to make the parent aware of the field as a new child.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity this field item list will be part of.
* @param string $field_name
* The name of the field.
* @param mixed $values
* (optional) The data value. If set, it has to match one of the supported
* data type format as documented for the data type classes.
*
* @return \Drupal\Core\Field\FieldItemListInterface
* The instantiated object.
*/
public function createFieldItemList(FieldableEntityInterface $entity, $field_name, $values = NULL);
/**
* Creates a new field item as part of a field item list.
*
* The provided item list is assigned as the parent of the created item. It
* However, it is the responsibility of the caller (usually the parent list
* itself) to have the parent aware of the item as a new child.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field item list, for which to create a new item.
* @param int $index
* The list index at which the item is created.
* @param array|null $values
* (optional) The values to assign to the field item properties.
*
* @return \Drupal\Core\Field\FieldItemInterface
* The instantiated object.
*/
public function createFieldItem(FieldItemListInterface $items, $index, $values = NULL);
/**
* Returns the default field-level settings for a field type.
*
* @param string $type
* A field type name.
*
* @return array
* The field's default settings, as provided by the plugin definition, or
* an empty array if type or settings are undefined.
*/
public function getDefaultFieldSettings($type);
/**
* Returns the default storage-level settings for a field type.
*
* @param string $type
* A field type name.
*
* @return array
* The type's default settings, as provided by the plugin definition, or an
* empty array if type or settings are undefined.
*/
public function getDefaultStorageSettings($type);
/**
* Gets the definition of all field types that can be added via UI.
*
* @return array
* An array of field type definitions.
*/
public function getUiDefinitions();
/**
* Returns the PHP class that implements the field type plugin.
*
* @param string $type
* A field type name.
*
* @return string
* Field type plugin class name.
*/
public function getPluginClass($type);
}

View file

@ -0,0 +1,160 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FormatterBase.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
/**
* Base class for 'Field formatter' plugin implementations.
*
* @ingroup field_formatter
*/
abstract class FormatterBase extends PluginSettingsBase implements FormatterInterface {
/**
* The field definition.
*
* @var \Drupal\Core\Field\FieldDefinitionInterface
*/
protected $fieldDefinition;
/**
* The formatter settings.
*
* @var array
*/
protected $settings;
/**
* The label display setting.
*
* @var string
*/
protected $label;
/**
* The view mode.
*
* @var string
*/
protected $viewMode;
/**
* Constructs a FormatterBase object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings) {
parent::__construct(array(), $plugin_id, $plugin_definition);
$this->fieldDefinition = $field_definition;
$this->settings = $settings;
$this->label = $label;
$this->viewMode = $view_mode;
$this->thirdPartySettings = $third_party_settings;
}
/**
* {@inheritdoc}
*/
public function view(FieldItemListInterface $items) {
$elements = $this->viewElements($items);
// If there are actual renderable children, use #theme => field, otherwise,
// let access cacheability metadata pass through for correct bubbling.
if (Element::children($elements)) {
$entity = $items->getEntity();
$entity_type = $entity->getEntityTypeId();
$field_name = $this->fieldDefinition->getName();
$info = array(
'#theme' => 'field',
'#title' => $this->fieldDefinition->getLabel(),
'#label_display' => $this->label,
'#view_mode' => $this->viewMode,
'#language' => $items->getLangcode(),
'#field_name' => $field_name,
'#field_type' => $this->fieldDefinition->getType(),
'#field_translatable' => $this->fieldDefinition->isTranslatable(),
'#entity_type' => $entity_type,
'#bundle' => $entity->bundle(),
'#object' => $entity,
'#items' => $items,
'#formatter' => $this->getPluginId(),
);
$elements = array_merge($info, $elements);
}
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
return array();
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
return array();
}
/**
* {@inheritdoc}
*/
public function prepareView(array $entities_items) { }
/**
* Returns the array of field settings.
*
* @return array
* The array of settings.
*/
protected function getFieldSettings() {
return $this->fieldDefinition->getSettings();
}
/**
* Returns the value of a field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
protected function getFieldSetting($setting_name) {
return $this->fieldDefinition->getSetting($setting_name);
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
// By default, formatters are available for all fields.
return TRUE;
}
}

View file

@ -0,0 +1,101 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FormatterInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
/**
* Interface definition for field formatter plugins.
*
* @ingroup field_formatter
*/
interface FormatterInterface extends PluginSettingsInterface {
/**
* Returns a form to configure settings for the formatter.
*
* Invoked from \Drupal\field_ui\Form\EntityDisplayFormBase to allow
* administrators to configure the formatter. The field_ui module takes care
* of handling submitted form values.
*
* @param array $form
* The form where the settings form is being included in.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form elements for the formatter settings.
*/
public function settingsForm(array $form, FormStateInterface $form_state);
/**
* Returns a short summary for the current formatter settings.
*
* If an empty result is returned, a UI can still be provided to display
* a settings form in case the formatter has configurable settings.
*
* @return array()
* A short summary of the formatter settings.
*/
public function settingsSummary();
/**
* Allows formatters to load information for field values being displayed.
*
* This should be used when a formatter needs to load additional information
* from the database in order to render a field, for example a reference
* field that displays properties of the referenced entities such as name or
* type.
*
* This method operates on multiple entities. The $entities_items parameter
* is an array keyed by entity ID. For performance reasons, information for
* all involved entities should be loaded in a single query where possible.
*
* Changes or additions to field values are done by directly altering the
* items.
*
* @param \Drupal\Core\Field\FieldItemListInterface[] $entities_items
* An array with the field values from the multiple entities being rendered.
*/
public function prepareView(array $entities_items);
/**
* Builds a renderable array for a fully themed field.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
*
* @return array
* A renderable array for a themed field with its label and all its values.
*/
public function view(FieldItemListInterface $items);
/**
* Builds a renderable array for a field value.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
*
* @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);
/**
* Returns if the formatter can be used for the provided field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition that should be checked.
*
* @return bool
* TRUE if the formatter can be used, FALSE otherwise.
*/
public static function isApplicable(FieldDefinitionInterface $field_definition);
}

View file

@ -0,0 +1,215 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\FormatterPluginManager.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Plugin type manager for field formatters.
*
* @ingroup field_formatter
*/
class FormatterPluginManager extends DefaultPluginManager {
/**
* An array of formatter options for each field type.
*
* @var array
*/
protected $formatterOptions;
/**
* The field type manager to define field.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypeManager;
/**
* Constructs a FormatterPluginManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The 'field type' plugin manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, FieldTypePluginManagerInterface $field_type_manager) {
parent::__construct('Plugin/Field/FieldFormatter', $namespaces, $module_handler, 'Drupal\Core\Field\FormatterInterface', 'Drupal\Core\Field\Annotation\FieldFormatter');
$this->setCacheBackend($cache_backend, 'field_formatter_types_plugins');
$this->alterInfo('field_formatter_info');
$this->fieldTypeManager = $field_type_manager;
}
/**
* {@inheritdoc}
*/
public function createInstance($plugin_id, array $configuration = array()) {
$plugin_definition = $this->getDefinition($plugin_id);
$plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
// @todo This is copied from \Drupal\Core\Plugin\Factory\ContainerFactory.
// Find a way to restore sanity to
// \Drupal\Core\Field\FormatterBase::__construct().
// If the plugin provides a factory method, pass the container to it.
if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
}
return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings']);
}
/**
* Overrides PluginManagerBase::getInstance().
*
* @param array $options
* An array with the following key/value pairs:
* - field_definition: (FieldDefinitionInterface) The field definition.
* - view_mode: (string) The view mode.
* - prepare: (bool, optional) Whether default values should get merged in
* the 'configuration' array. Defaults to TRUE.
* - configuration: (array) the configuration for the formatter. The
* following key value pairs are allowed, and are all optional if
* 'prepare' is TRUE:
* - label: (string) Position of the label. The default 'field' theme
* implementation supports the values 'inline', 'above' and 'hidden'.
* Defaults to 'above'.
* - type: (string) The formatter to use. Defaults to the
* 'default_formatter' for the field type, The default formatter will
* also be used if the requested formatter is not available.
* - settings: (array) Settings specific to the formatter. Each setting
* defaults to the default value specified in the formatter definition.
* - third_party_settings: (array) Settings provided by other extensions
* through hook_field_formatter_third_party_settings_form().
*
* @return \Drupal\Core\Field\FormatterInterface|null
* A formatter object or NULL when plugin is not found.
*/
public function getInstance(array $options) {
$configuration = $options['configuration'];
$field_definition = $options['field_definition'];
$field_type = $field_definition->getType();
// Fill in default configuration if needed.
if (!isset($options['prepare']) || $options['prepare'] == TRUE) {
$configuration = $this->prepareConfiguration($field_type, $configuration);
}
$plugin_id = $configuration['type'];
// Switch back to default formatter if either:
// - the configuration does not specify a formatter class
// - the field type is not allowed for the formatter
// - the formatter is not applicable to the field definition.
$definition = $this->getDefinition($configuration['type'], FALSE);
if (!isset($definition['class']) || !in_array($field_type, $definition['field_types']) || !$definition['class']::isApplicable($field_definition)) {
// Grab the default widget for the field type.
$field_type_definition = $this->fieldTypeManager->getDefinition($field_type);
if (empty($field_type_definition['default_formatter'])) {
return NULL;
}
$plugin_id = $field_type_definition['default_formatter'];
}
$configuration += array(
'field_definition' => $field_definition,
'view_mode' => $options['view_mode'],
);
return $this->createInstance($plugin_id, $configuration);
}
/**
* Merges default values for formatter configuration.
*
* @param string $field_type
* The field type.
* @param array $properties
* An array of formatter configuration.
*
* @return array
* The display properties with defaults added.
*/
public function prepareConfiguration($field_type, array $configuration) {
// Fill in defaults for missing properties.
$configuration += array(
'label' => 'above',
'settings' => array(),
'third_party_settings' => array(),
);
// If no formatter is specified, use the default formatter.
if (!isset($configuration['type'])) {
$field_type = $this->fieldTypeManager->getDefinition($field_type);
$configuration['type'] = $field_type['default_formatter'];
}
// Filter out unknown settings, and fill in defaults for missing settings.
$default_settings = $this->getDefaultSettings($configuration['type']);
$configuration['settings'] = array_intersect_key($configuration['settings'], $default_settings) + $default_settings;
return $configuration;
}
/**
* Returns an array of formatter options for a field type.
*
* @param string|null $field_type
* (optional) The name of a field type, or NULL to retrieve all formatters.
*
* @return array
* If no field type is provided, returns a nested array of all formatters,
* keyed by field type.
*/
public function getOptions($field_type = NULL) {
if (!isset($this->formatterOptions)) {
$options = array();
$field_types = $this->fieldTypeManager->getDefinitions();
$formatter_types = $this->getDefinitions();
uasort($formatter_types, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
foreach ($formatter_types as $name => $formatter_type) {
foreach ($formatter_type['field_types'] as $formatter_field_type) {
// Check that the field type exists.
if (isset($field_types[$formatter_field_type])) {
$options[$formatter_field_type][$name] = $formatter_type['label'];
}
}
}
$this->formatterOptions = $options;
}
if ($field_type) {
return !empty($this->formatterOptions[$field_type]) ? $this->formatterOptions[$field_type] : array();
}
return $this->formatterOptions;
}
/**
* Returns the default settings of a field formatter.
*
* @param string $type
* A field formatter type name.
*
* @return array
* The formatter type's default settings, as provided by the plugin
* definition, or an empty array if type or settings are undefined.
*/
public function getDefaultSettings($type) {
$plugin_definition = $this->getDefinition($type, FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($type, $plugin_definition);
return $plugin_class::defaultSettings();
}
return array();
}
}

View file

@ -0,0 +1,88 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver.
*/
namespace Drupal\Core\Field\Plugin\DataType\Deriver;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides data type plugins for each existing field type plugin.
*/
class FieldItemDeriver implements ContainerDeriverInterface {
/**
* List of derivative definitions.
*
* @var array
*/
protected $derivatives = array();
/**
* The base plugin ID this derivative is for.
*
* @var string
*/
protected $basePluginId;
/**
* The field type plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypePluginManager;
/**
* Constructs a FieldItemDeriver object.
*
* @param string $base_plugin_id
* The base plugin ID.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The field type plugin manager.
*/
public function __construct($base_plugin_id, FieldTypePluginManagerInterface $field_type_plugin_manager) {
$this->basePluginId = $base_plugin_id;
$this->fieldTypePluginManager = $field_type_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$base_plugin_id,
$container->get('plugin.manager.field.field_type')
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinition($derivative_id, $base_plugin_definition) {
if (!isset($this->derivatives)) {
$this->getDerivativeDefinitions($base_plugin_definition);
}
if (isset($this->derivatives[$derivative_id])) {
return $this->derivatives[$derivative_id];
}
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->fieldTypePluginManager->getDefinitions() as $plugin_id => $definition) {
$definition['definition_class'] = '\Drupal\Core\Field\TypedData\FieldItemDataDefinition';
$definition['list_definition_class'] = '\Drupal\Core\Field\BaseFieldDefinition';
$definition['unwrap_for_canonical_representation'] = FALSE;
$this->derivatives[$plugin_id] = $definition;
}
return $this->derivatives;
}
}

View file

@ -0,0 +1,26 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\DataType\FieldItem.
*/
namespace Drupal\Core\Field\Plugin\DataType;
/**
* Defines the base plugin for deriving data types for field types.
*
* Note that the class only register the plugin, and is actually never used.
* \Drupal\Core\Field\FieldItemBase is available for use as base class.
*
* @DataType(
* id = "field_item",
* label = @Translation("Field item"),
* list_class = "\Drupal\Core\Field\FieldItemList",
* deriver = "Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver"
* )
*/
abstract class FieldItem {
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\BasicStringFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
/**
* Plugin implementation of the 'basic_string' formatter.
*
* @FieldFormatter(
* id = "basic_string",
* label = @Translation("Plain text"),
* field_types = {
* "string_long",
* "email"
* },
* quickedit = {
* "editor" = "plain_text"
* }
* )
*/
class BasicStringFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
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(SafeMarkup::checkPlain($item->value)));
}
return $elements;
}
}

View file

@ -0,0 +1,138 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\BooleanFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'boolean' formatter.
*
* @FieldFormatter(
* id = "boolean",
* label = @Translation("Boolean"),
* field_types = {
* "boolean",
* }
* )
*/
class BooleanFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = [];
// Fall back to field settings by default.
$settings['format'] = 'default';
$settings['format_custom_false'] = '';
$settings['format_custom_true'] = '';
return $settings;
}
/**
* Gets the available format options.
*
* @return array|string
* A list of output formats. Each entry is keyed by the machine name of the
* format. The value is an array, of which the first item is the result for
* boolean TRUE, the second is for boolean FALSE. The value can be also an
* array, but this is just the case for the custom format.
*/
protected function getOutputFormats() {
$formats = [
'default' => [$this->getFieldSetting('on_label'), $this->getFieldSetting('off_label')],
'yes-no' => [$this->t('Yes'), $this->t('No')],
'true-false' => [$this->t('True'), $this->t('False')],
'on-off' => [$this->t('On'), $this->t('Off')],
'enabled-disabled' => [$this->t('Enabled'), $this->t('Disabled')],
'boolean' => [1, 0],
'unicode-yes-no' => ['✔', '✖'],
'custom' => $this->t('Custom'),
];
return $formats;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$formats = [];
foreach ($this->getOutputFormats() as $format_name => $format) {
if (is_array($format)) {
if ($format_name == 'default') {
$formats[$format_name] = $this->t('Field settings (@on_label / @off_label)', array('@on_label' => $format[0], '@off_label' => $format[1]));
}
else {
$formats[$format_name] = $this->t('@on_label / @off_label', array('@on_label' => $format[0], '@off_label' => $format[1]));
}
}
else {
$formats[$format_name] = $format;
}
}
$form['format'] = [
'#type' => 'select',
'#title' => $this->t('Output format'),
'#default_value' => $this->getSetting('format'),
'#options' => $formats,
];
$form['format_custom_true'] = [
'#type' => 'textfield',
'#title' => $this->t('Custom output for TRUE'),
'#default_value' => $this->getSetting('format_custom_true'),
'#states' => [
'visible' => [
'select[name="fields[field_boolean][settings_edit_form][settings][format]"]' => ['value' => 'custom'],
],
],
];
$form['format_custom_false'] = [
'#type' => 'textfield',
'#title' => $this->t('Custom output for FALSE'),
'#default_value' => $this->getSetting('format_custom_false'),
'#states' => [
'visible' => [
'select[name="fields[field_boolean][settings_edit_form][settings][format]"]' => ['value' => 'custom'],
],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = [];
$formats = $this->getOutputFormats();
foreach ($items as $delta => $item) {
$format = $this->getSetting('format');
if ($format == 'custom') {
$elements[$delta] = ['#markup' => $item->value ? $this->getSetting('format_custom_true') : $this->getSetting('format_custom_false')];
}
else {
$elements[$delta] = ['#markup' => $item->value ? $formats[$format][0] : $formats[$format][1]];
}
}
return $elements;
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\DecimalFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'number_decimal' formatter.
*
* The 'Default' formatter is different for integer fields on the one hand, and
* for decimal and float fields on the other hand, in order to be able to use
* different settings.
*
* @FieldFormatter(
* id = "number_decimal",
* label = @Translation("Default"),
* field_types = {
* "decimal",
* "float"
* }
* )
*/
class DecimalFormatter extends NumericFormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'thousand_separator' => '',
'decimal_separator' => '.',
'scale' => 2,
'prefix_suffix' => TRUE,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
$elements['decimal_separator'] = array(
'#type' => 'select',
'#title' => t('Decimal marker'),
'#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
'#default_value' => $this->getSetting('decimal_separator'),
'#weight' => 5,
);
$range = range(0, 10);
$elements['scale'] = array(
'#type' => 'select',
'#title' => t('Scale', array(), array('decimal places')),
'#options' => array_combine($range, $range),
'#default_value' => $this->getSetting('scale'),
'#description' => t('The number of digits to the right of the decimal.'),
'#weight' => 6,
);
return $elements;
}
/**
* {@inheritdoc}
*/
protected function numberFormat($number) {
return number_format($number, $this->getSetting('scale'), $this->getSetting('decimal_separator'), $this->getSetting('thousand_separator'));
}
}

View file

@ -0,0 +1,165 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter.
*/
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\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'entity reference rendered entity' formatter.
*
* @FieldFormatter(
* id = "entity_reference_entity_view",
* label = @Translation("Rendered entity"),
* description = @Translation("Display the referenced entities rendered by entity_view()."),
* field_types = {
* "entity_reference"
* }
* )
*/
class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implements ContainerFactoryPluginInterface {
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* Constructs a StringFormatter instance.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings settings.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param LoggerChannelFactoryInterface $logger_factory
* The logger factory.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, LoggerChannelFactoryInterface $logger_factory) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->loggerFactory = $logger_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'view_mode' => 'default',
'link' => FALSE,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['view_mode'] = array(
'#type' => 'select',
'#options' => \Drupal::entityManager()->getViewModeOptions($this->getFieldSetting('target_type')),
'#title' => t('View mode'),
'#default_value' => $this->getSetting('view_mode'),
'#required' => TRUE,
);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$view_modes = \Drupal::entityManager()->getViewModeOptions($this->getFieldSetting('target_type'));
$view_mode = $this->getSetting('view_mode');
$summary[] = t('Rendered as @mode', array('@mode' => isset($view_modes[$view_mode]) ? $view_modes[$view_mode] : $view_mode));
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$view_mode = $this->getSetting('view_mode');
$elements = array();
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
// Protect ourselves from recursive rendering.
static $depth = 0;
$depth++;
if ($depth > 20) {
$this->loggerFactory->get('entity')->error('Recursive rendering detected when rendering entity @entity_type @entity_id. Aborting rendering.', array('@entity_type' => $entity->getEntityTypeId(), '@entity_id' => $entity->id()));
return $elements;
}
if ($entity->id()) {
$elements[$delta] = entity_view($entity, $view_mode, $entity->language()->getId());
// Add a resource attribute to set the mapping property's value to the
// entity's url. Since we don't know what the markup of the entity will
// be, we shouldn't rely on it for structured data such as RDFa.
if (!empty($items[$delta]->_attributes)) {
$items[$delta]->_attributes += array('resource' => $entity->url());
}
}
else {
// This is an "auto_create" item.
$elements[$delta] = array('#markup' => $entity->label());
}
$depth = 0;
}
return $elements;
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
// This formatter is only available for entity types that have a view
// builder.
$target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
return \Drupal::entityManager()->getDefinition($target_type)->hasViewBuilderClass();
}
}

View file

@ -0,0 +1,190 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\TypedData\TranslatableInterface;
/**
* Parent plugin for entity reference formatters.
*/
abstract class EntityReferenceFormatterBase extends FormatterBase {
/**
* Returns the referenced entities for display.
*
* The method takes care of:
* - checking entity access,
* - placing the entities in the language expected for display.
* It is thus strongly recommended that formatters use it in their
* implementation of viewElements($items) rather than dealing with $items
* directly.
*
* For each entity, the EntityReferenceItem by which the entity is referenced
* is available in $entity->_referringItem. This is useful for field types
* that store additional values next to the reference itself.
*
* @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items
* The item list.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The array of referenced entities to display, keyed by delta.
*
* @see ::prepareView()
*/
protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items) {
$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);
}
$access = $this->checkAccess($entity);
// Add the access result's cacheability, ::view() needs it.
$item->_accessCacheability = CacheableMetadata::createFromObject($access);
if ($access->isAllowed()) {
// Add the referring item, in case the formatter needs it.
$entity->_referringItem = $items[$delta];
$entities[$delta] = $entity;
}
}
}
return $entities;
}
/**
* {@inheritdoc}
*
* @see ::prepareView()
* @see ::getEntitiestoView()
*/
public function view(FieldItemListInterface $items) {
$elements = parent::view($items);
$field_level_access_cacheability = new CacheableMetadata();
// Try to map the cacheability of the access result that was set at
// _accessCacheability in getEntitiesToView() to the corresponding render
// subtree. If no such subtree is found, then merge it with the field-level
// access cacheability.
foreach ($items as $delta => $item) {
// Ignore items for which access cacheability could not be determined in
// prepareView().
if (!empty($item->_accessCacheability)) {
if (isset($elements[$delta])) {
CacheableMetadata::createFromRenderArray($elements[$delta])
->merge($item->_accessCacheability)
->applyTo($elements[$delta]);
}
else {
$field_level_access_cacheability = $field_level_access_cacheability->merge($item->_accessCacheability);
}
}
}
// Apply the cacheability metadata for the inaccessible entities and the
// entities for which the corresponding render subtree could not be found.
// This causes the field to be rendered (and cached) according to the cache
// contexts by which the access results vary, to ensure only users with
// access to this field can view it. It also tags this field with the cache
// tags on which the access results depend, to ensure users that cannot view
// this field at the moment will gain access once any of those cache tags
// are invalidated.
$field_level_access_cacheability->applyTo($elements);
return $elements;
}
/**
* {@inheritdoc}
*
* Loads the entities referenced in that field across all the entities being
* viewed.
*/
public function prepareView(array $entities_items) {
// Collect entity IDs to load. For performance, we want to use a single
// "multiple entity load" to load all the entities for the multiple
// "entity reference item lists" being displayed. We thus cannot use
// \Drupal\Core\Field\EntityReferenceFieldItemList::referencedEntities().
$ids = array();
foreach ($entities_items as $items) {
foreach ($items as $item) {
// To avoid trying to reload non-existent entities in
// getEntitiesToView(), explicitly mark the items where $item->entity
// contains a valid entity ready for display. All items are initialized
// at FALSE.
$item->_loaded = FALSE;
if ($this->needsEntityLoad($item)) {
$ids[] = $item->target_id;
}
}
}
if ($ids) {
$target_type = $this->getFieldSetting('target_type');
$target_entities = \Drupal::entityManager()->getStorage($target_type)->loadMultiple($ids);
}
// For each item, pre-populate the loaded entity in $item->entity, and set
// the 'loaded' flag.
foreach ($entities_items as $items) {
foreach ($items as $item) {
if (isset($target_entities[$item->target_id])) {
$item->entity = $target_entities[$item->target_id];
$item->_loaded = TRUE;
}
elseif ($item->hasNewEntity()) {
$item->_loaded = TRUE;
}
}
}
}
/**
* Returns whether the entity referenced by an item needs to be loaded.
*
* @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $item
* The item to check.
*
* @return bool
* TRUE if the entity needs to be loaded.
*/
protected function needsEntityLoad(EntityReferenceItem $item) {
return !$item->hasNewEntity();
}
/**
* Checks access to the given entity.
*
* By default, entity access is checked. However, a subclass can choose to
* exclude certain items from entity access checking by immediately granting
* access.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to check.
*
* @return \Drupal\Core\Access\AccessResult
* A cacheable access result.
*/
protected function checkAccess(EntityInterface $entity) {
return $entity->access('view', NULL, TRUE);
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceIdFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Component\Utility\SafeMarkup;
/**
* Plugin implementation of the 'entity reference ID' formatter.
*
* @FieldFormatter(
* id = "entity_reference_entity_id",
* label = @Translation("Entity ID"),
* description = @Translation("Display the ID of the referenced entities."),
* field_types = {
* "entity_reference"
* }
* )
*/
class EntityReferenceIdFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
if ($entity->id()) {
$elements[$delta] = array(
'#markup' => SafeMarkup::checkPlain($entity->id()),
// Create a cache tag entry for the referenced entity. In the case
// that the referenced entity is deleted, the cache for referring
// entities must be cleared.
'#cache' => array(
'tags' => $entity->getCacheTags(),
),
);
}
}
return $elements;
}
}

View file

@ -0,0 +1,109 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceLabelFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'entity reference label' formatter.
*
* @FieldFormatter(
* id = "entity_reference_label",
* label = @Translation("Label"),
* description = @Translation("Display the label of the referenced entities."),
* field_types = {
* "entity_reference"
* }
* )
*/
class EntityReferenceLabelFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'link' => TRUE,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['link'] = array(
'#title' => t('Link label to the referenced entity'),
'#type' => 'checkbox',
'#default_value' => $this->getSetting('link'),
);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$summary[] = $this->getSetting('link') ? t('Link to the referenced entity') : t('No link');
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
$output_as_link = $this->getSetting('link');
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
$label = $entity->label();
// If the link is to be displayed and the entity has a uri, display a
// link.
if ($output_as_link && !$entity->isNew()) {
try {
$uri = $entity->urlInfo();
}
catch (UndefinedLinkTemplateException $e) {
// This exception is thrown by \Drupal\Core\Entity\Entity::urlInfo()
// and it means that the entity type doesn't have a link template nor
// a valid "uri_callback", so don't bother trying to output a link for
// the rest of the referenced entities.
$output_as_link = FALSE;
}
}
if ($output_as_link && isset($uri) && !$entity->isNew()) {
$elements[$delta] = [
'#type' => 'link',
'#title' => $label,
'#url' => $uri,
'#options' => $uri->getOptions(),
];
if (!empty($items[$delta]->_attributes)) {
$elements[$delta]['#options'] += array('attributes' => array());
$elements[$delta]['#options']['attributes'] += $items[$delta]->_attributes;
// Unset field item attributes since they have been included in the
// formatter output and shouldn't be rendered in the field template.
unset($items[$delta]->_attributes);
}
}
else {
$elements[$delta] = array('#markup' => SafeMarkup::checkPlain($label));
}
$elements[$delta]['#cache']['tags'] = $entity->getCacheTags();
}
return $elements;
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\IntegerFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
/**
* Plugin implementation of the 'number_integer' formatter.
*
* The 'Default' formatter is different for integer fields on the one hand, and
* for decimal and float fields on the other hand, in order to be able to use
* different settings.
*
* @FieldFormatter(
* id = "number_integer",
* label = @Translation("Default"),
* field_types = {
* "integer"
* }
* )
*/
class IntegerFormatter extends NumericFormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'thousand_separator' => '',
'prefix_suffix' => TRUE,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
protected function numberFormat($number) {
return number_format($number, 0, '', $this->getSetting('thousand_separator'));
}
}

View file

@ -0,0 +1,128 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\LanguageFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'language' formatter.
*
* @FieldFormatter(
* id = "language",
* label = @Translation("Language"),
* field_types = {
* "language"
* }
* )
*/
class LanguageFormatter extends StringFormatter {
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* Constructs a LanguageFormatter instance.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings settings.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $entity_manager);
$this->languageManager = $language_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('entity.manager'),
$container->get('language_manager')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = parent::defaultSettings();
$settings['native_language'] = FALSE;
return $settings;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$form['native_language'] = array(
'#title' => $this->t('Display in native language'),
'#type' => 'checkbox',
'#default_value' => $this->getSetting('native_language'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
if ($this->getSetting('native_language')) {
$summary[] = $this->t('Displayed in native language');
}
return $summary;
}
/**
* {@inheritdoc}
*/
protected function viewValue(FieldItemInterface $item) {
// The 'languages' cache context is not necessary because the language is
// either displayed in its configured form (loaded directly from config
// storage by LanguageManager::getLanguages()) or in its native language
// name. That only depends on formatter settings and no language condition.
$languages = $this->getSetting('native_language') ? $this->languageManager->getNativeLanguages() : $this->languageManager->getLanguages();
return $item->language ? SafeMarkup::checkPlain($languages[$item->language->getId()]->getName()) : '';
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\MailToFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
/**
* Plugin implementation of the 'email_mailto' formatter.
*
* @FieldFormatter(
* id = "email_mailto",
* label = @Translation("Email"),
* field_types = {
* "email"
* }
* )
*/
class MailToFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
$elements[$delta] = array(
'#type' => 'link',
'#title' => $item->value,
'#url' => Url::fromUri('mailto:' . $item->value),
);
}
return $elements;
}
}

View file

@ -0,0 +1,107 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\NumericFormatterBase.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Parent plugin for decimal and integer formatters.
*/
abstract class NumericFormatterBase extends FormatterBase {
use AllowedTagsXssTrait;
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$options = array(
'' => t('- None -'),
'.' => t('Decimal point'),
',' => t('Comma'),
' ' => t('Space'),
chr(8201) => t('Thin space'),
"'" => t('Apostrophe'),
);
$elements['thousand_separator'] = array(
'#type' => 'select',
'#title' => t('Thousand marker'),
'#options' => $options,
'#default_value' => $this->getSetting('thousand_separator'),
'#weight' => 0,
);
$elements['prefix_suffix'] = array(
'#type' => 'checkbox',
'#title' => t('Display prefix and suffix'),
'#default_value' => $this->getSetting('prefix_suffix'),
'#weight' => 10,
);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$summary[] = $this->numberFormat(1234.1234567890);
if ($this->getSetting('prefix_suffix')) {
$summary[] = t('Display with prefix and suffix.');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
$settings = $this->getFieldSettings();
foreach ($items as $delta => $item) {
$output = $this->numberFormat($item->value);
// Account for prefix and suffix.
if ($this->getSetting('prefix_suffix')) {
$prefixes = isset($settings['prefix']) ? array_map(array($this, 'fieldFilterXss'), explode('|', $settings['prefix'])) : array('');
$suffixes = isset($settings['suffix']) ? array_map(array($this, 'fieldFilterXss'), 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) {
$item->_attributes += array('content' => $item->value);
}
$elements[$delta] = array('#markup' => $output);
}
return $elements;
}
/**
* Formats a number.
*
* @param mixed $number
* The numeric value.
*
* @return string
* The formatted number.
*/
abstract protected function numberFormat($number);
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\NumericUnformattedFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
/**
* Plugin implementation of the 'number_unformatted' formatter.
*
* @FieldFormatter(
* id = "number_unformatted",
* label = @Translation("Unformatted"),
* field_types = {
* "integer",
* "decimal",
* "float"
* }
* )
*/
class NumericUnformattedFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
$elements[$delta] = array('#markup' => $item->value);
}
return $elements;
}
}

View file

@ -0,0 +1,161 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\StringFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
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;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'string' formatter.
*
* @FieldFormatter(
* id = "string",
* label = @Translation("Plain text"),
* field_types = {
* "string",
* "uri",
* },
* quickedit = {
* "editor" = "plain_text"
* }
* )
*/
class StringFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* Constructs a StringFormatter instance.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings settings.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityManagerInterface $entity_manager) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$options = parent::defaultSettings();
$options['link_to_entity'] = FALSE;
return $options;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$entity_type = $this->entityManager->getDefinition($this->fieldDefinition->getTargetEntityTypeId());
$form['link_to_entity'] = [
'#type' => 'checkbox',
'#title' => $this->t('Link to the @entity_label', ['@entity_label' => $entity_type->getLabel()]),
'#default_value' => $this->getSetting('link_to_entity'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = [];
if ($this->getSetting('link_to_entity')) {
$entity_type = $this->entityManager->getDefinition($this->fieldDefinition->getTargetEntityTypeId());
$summary[] = $this->t('Linked to the @entity_label', ['@entity_label' => $entity_type->getLabel()]);
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
$url = NULL;
if ($this->getSetting('link_to_entity')) {
// For the default revision this falls back to 'canonical'
$url = $items->getEntity()->urlInfo('revision');
}
foreach ($items as $delta => $item) {
$string = $this->viewValue($item);
if ($url) {
$elements[$delta] = [
'#type' => 'link',
'#title' => $string,
'#url' => $url,
];
}
else {
$elements[$delta] = ['#markup' => $string];
}
}
return $elements;
}
/**
* Generate the output appropriate for one field item.
*
* @param \Drupal\Core\Field\FieldItemInterface $item
* One field item.
*
* @return string
* The textual output generated.
*/
protected function viewValue(FieldItemInterface $item) {
// The text value has no text format assigned to it, so the user input
// should equal the output, including newlines.
return nl2br(SafeMarkup::checkPlain($item->value));
}
}

View file

@ -0,0 +1,194 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Plugin implementation of the 'timestamp' formatter as time ago.
*
* @FieldFormatter(
* id = "timestamp_ago",
* label = @Translation("Time ago"),
* field_types = {
* "timestamp",
* "created",
* "changed",
* }
* )
*/
class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;
/**
* The current Request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* Constructs a TimestampAgoFormatter object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $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) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
$this->request = $request;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
// @see \Drupal\Core\Field\FormatterPluginManager::createInstance().
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('date.formatter'),
$container->get('request_stack')->getCurrentRequest()
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'future_format' => '@interval hence',
'past_format' => '@interval ago',
'granularity' => 2,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
$form['future_format'] = array(
'#type' => 'textfield',
'#title' => $this->t('Future format'),
'#default_value' => $this->getSetting('future_format'),
'#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
);
$form['past_format'] = array(
'#type' => 'textfield',
'#title' => $this->t('Past format'),
'#default_value' => $this->getSetting('past_format'),
'#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
);
$elements['granularity'] = array(
'#type' => 'number',
'#title' => $this->t('Granularity'),
'#description' => $this->t('How many time interval units should be shown in the formatted output.'),
'#default_value' => $this->getSetting('granularity') ?: 2,
'#min' => 1,
'#max' => 6,
);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$future_date = strtotime('1 year 1 month 1 week 1 day 1 hour 1 minute');
$past_date = strtotime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
$summary[] = $this->t('Future date: %display', array('%display' => $this->formatTimestamp($future_date)));
$summary[] = $this->t('Past date: %display', array('%display' => $this->formatTimestamp($past_date)));
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
if ($item->value) {
$updated = $this->formatTimestamp($item->value);
}
else {
$updated = $this->t('never');
}
$elements[$delta] = array('#markup' => $updated);
}
return $elements;
}
/**
* Formats a timestamp.
*
* @param int $timestamp
* A UNIX timestamp to format.
*
* @return string
* The formatted timestamp string using the past or future format setting.
*/
protected function formatTimestamp($timestamp) {
$granularity = $this->getSetting('granularity');
$options = ['granularity' => $granularity];
if ($this->request->server->get('REQUEST_TIME') > $timestamp) {
return SafeMarkup::format($this->getSetting('past_format'), ['@interval' => $this->dateFormatter->formatTimeDiffSince($timestamp, $options)]);
}
else {
return SafeMarkup::format($this->getSetting('future_format'), ['@interval' => $this->dateFormatter->formatTimeDiffUntil($timestamp, $options)]);
}
}
}

View file

@ -0,0 +1,196 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'timestamp' formatter.
*
* @FieldFormatter(
* id = "timestamp",
* label = @Translation("Default"),
* field_types = {
* "timestamp",
* "created",
* "changed",
* }
* )
*/
class TimestampFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;
/**
* The date format entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $dateFormatStorage;
/**
* Constructs a new TimestampFormatter.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Third party settings.
* @param \Drupal\Core\Datetime\DateFormatter $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) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
$this->dateFormatStorage = $date_format_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('date.formatter'),
$container->get('entity.manager')->getStorage('date_format')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'date_format' => 'medium',
'custom_date_format' => '',
'timezone' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
$date_formats = array();
foreach ($this->dateFormatStorage->loadMultiple() as $machine_name => $value) {
$date_formats[$machine_name] = $this->t('@name format: @date', array('@name' => $value->label(), '@date' => $this->dateFormatter->format(REQUEST_TIME, $machine_name)));
}
$date_formats['custom'] = $this->t('Custom');
$elements['date_format'] = array(
'#type' => 'select',
'#title' => $this->t('Date format'),
'#options' => $date_formats,
'#default_value' => $this->getSetting('date_format') ?: 'medium',
);
$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']),
'#default_value' => $this->getSetting('custom_date_format') ?: '',
);
$elements['custom_date_format']['#states']['visible'][] = array(
':input[name="options[settings][date_format]"]' => array('value' => 'custom'),
);
$elements['timezone'] = array(
'#type' => 'select',
'#title' => $this->t('Time zone'),
'#options' => array('' => $this->t('- Default site/user time zone -')) + system_time_zones(FALSE),
'#default_value' => $this->getSetting('timezone'),
);
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$date_format = $this->getSetting('date_format');
$summary[] = $this->t('Date format: @date_format', array('@date_format' => $date_format));
if ($this->getSetting('date_format') === 'custom' && ($custom_date_format = $this->getSetting('custom_date_format'))) {
$summary[] = $this->t('Custom date format: @custom_date_format', array('@custom_date_format' => $custom_date_format));
}
if ($timezone = $this->getSetting('timezone')) {
$summary[] = $this->t('Time zone: @timezone', array('@timezone' => $timezone));
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
$date_format = $this->getSetting('date_format');
$custom_date_format = '';
$timezone = $this->getSetting('timezone') ?: NULL;
$langcode = NULL;
// If an RFC2822 date format is requested, then the month and day have to
// be in English. @see http://www.faqs.org/rfcs/rfc2822.html
if ($date_format === 'custom' && ($custom_date_format = $this->getSetting('custom_date_format')) === 'r') {
$langcode = 'en';
}
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#cache' => [
'contexts' => [
'timezone',
],
],
'#markup' => $this->dateFormatter->format($item->value, $date_format, $custom_date_format, $timezone, $langcode),
];
}
return $elements;
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldFormatter\UriLinkFormatter.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
/**
* Plugin implementation of the 'uri_link' formatter.
*
* @FieldFormatter(
* id = "uri_link",
* label = @Translation("Link to URI"),
* field_types = {
* "uri",
* }
* )
*/
class UriLinkFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
if (!$item->isEmpty()) {
$elements[$delta] = [
'#type' => 'link',
'#url' => Url::fromUri($item->value),
'#title' => $item->value,
];
}
}
return $elements;
}
}

View file

@ -0,0 +1,127 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\OptionsProviderInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'boolean' entity field type.
*
* @FieldType(
* id = "boolean",
* label = @Translation("Boolean"),
* description = @Translation("An entity field containing a boolean value."),
* default_widget = "boolean_checkbox",
* default_formatter = "boolean",
* )
*/
class BooleanItem extends FieldItemBase implements OptionsProviderInterface {
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return array(
'on_label' => t('On'),
'off_label' => t('Off'),
) + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('boolean')
->setLabel(t('Boolean value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
'size' => 'tiny',
),
),
);
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = array();
$element['on_label'] = array(
'#type' => 'textfield',
'#title' => $this->t('"On" label'),
'#default_value' => $this->getSetting('on_label'),
'#required' => TRUE,
);
$element['off_label'] = array(
'#type' => 'textfield',
'#title' => $this->t('"Off" label'),
'#default_value' => $this->getSetting('off_label'),
'#required' => TRUE,
);
return $element;
}
/**
* {@inheritdoc}
*/
public function getPossibleValues(AccountInterface $account = NULL) {
return array(0, 1);
}
/**
* {@inheritdoc}
*/
public function getPossibleOptions(AccountInterface $account = NULL) {
return array(
0 => $this->getSetting('off_label'),
1 => $this->getSetting('on_label'),
);
}
/**
* {@inheritdoc}
*/
public function getSettableValues(AccountInterface $account = NULL) {
return array(0, 1);
}
/**
* {@inheritdoc}
*/
public function getSettableOptions(AccountInterface $account = NULL) {
return $this->getPossibleOptions($account);
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$values['value'] = mt_rand(0, 1);
return $values;
}
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\ChangedItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
/**
* Defines the 'changed' entity field type.
*
* Based on a field of this type, entity types can easily implement the
* EntityChangedInterface.
*
* @FieldType(
* id = "changed",
* label = @Translation("Last changed"),
* description = @Translation("An entity field containing a UNIX timestamp of when the entity has been last updated."),
* no_ui = TRUE,
* list_class = "\Drupal\Core\Field\ChangedFieldItemList"
* )
*
* @see \Drupal\Core\Entity\EntityChangedInterface
*/
class ChangedItem extends CreatedItem {
/**
* {@inheritdoc}
*/
public function preSave() {
parent::preSave();
// Set the timestamp to request time if it is not set.
if (!$this->value) {
$this->value = REQUEST_TIME;
}
else {
// On an existing entity translation, the changed timestamp will only be
// set to the request time automatically if at least one other field value
// of the entity has changed. This detection does not run on new entities
// and will be turned off if the changed timestamp is set manually before
// save, for example during migrations or by using
// \Drupal\content_translation\ContentTranslationMetadataWrapperInterface::setChangedTime().
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->getEntity();
/** @var \Drupal\Core\Entity\ContentEntityInterface $original */
$original = $entity->original;
$langcode = $entity->language()->getId();
if (!$entity->isNew() && $original->hasTranslation($langcode)) {
$original_value = $original->getTranslation($langcode)->get($this->getFieldDefinition()->getName())->value;
if ($this->value == $original_value && $entity->hasTranslationChanges()) {
$this->value = REQUEST_TIME;
}
}
}
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\CreatedItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
/**
* Defines the 'created' entity field type.
*
* @FieldType(
* id = "created",
* 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",
* )
*/
class CreatedItem extends TimestampItem {
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
parent::applyDefaultValue($notify);
// Created fields default to the current timestamp.
$this->setValue(array('value' => REQUEST_TIME), $notify);
return $this;
}
}

View file

@ -0,0 +1,162 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\DecimalItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'decimal' field type.
*
* @FieldType(
* id = "decimal",
* label = @Translation("Number (decimal)"),
* description = @Translation("This field stores a number in the database in a fixed decimal format."),
* category = @Translation("Number"),
* default_widget = "number",
* default_formatter = "number_decimal"
* )
*/
class DecimalItem extends NumericItemBase {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'precision' => 10,
'scale' => 2,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Decimal value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'numeric',
'precision' => $field_definition->getSetting('precision'),
'scale' => $field_definition->getSetting('scale'),
)
),
);
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$element = array();
$settings = $this->getSettings();
$range = range(10, 32);
$element['precision'] = array(
'#type' => 'select',
'#title' => t('Precision'),
'#options' => array_combine($range, $range),
'#default_value' => $settings['precision'],
'#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
'#disabled' => $has_data,
);
$range = range(0, 10);
$element['scale'] = array(
'#type' => 'select',
'#title' => t('Scale', array(), array('decimal places')),
'#options' => array_combine($range, $range),
'#default_value' => $settings['scale'],
'#description' => t('The number of digits to the right of the decimal.'),
'#disabled' => $has_data,
);
return $element;
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = parent::fieldSettingsForm($form, $form_state);
$settings = $this->getSettings();
$element['min']['#step'] = pow(0.1, $settings['scale']);
$element['max']['#step'] = pow(0.1, $settings['scale']);
return $element;
}
/**
* {@inheritdoc}
*/
public function preSave() {
$this->value = round($this->value, $this->getSetting('scale'));
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$settings = $field_definition->getSettings();
$precision = $settings['precision'] ?: 10;
$scale = $settings['scale'] ?: 2;
// $precision - $scale is the number of digits on the left of the decimal
// point.
// The maximum number you can get with 3 digits is 10^3 - 1 --> 999.
// The minimum number you can get with 3 digits is -1 * (10^3 - 1).
$max = is_numeric($settings['max']) ?: pow(10, ($precision - $scale)) - 1;
$min = is_numeric($settings['min']) ?: -pow(10, ($precision - $scale)) + 1;
// Get the number of decimal digits for the $max
$decimal_digits = self::getDecimalDigits($max);
// Do the same for the min and keep the higher number of decimal digits.
$decimal_digits = max(self::getDecimalDigits($min), $decimal_digits);
// If $min = 1.234 and $max = 1.33 then $decimal_digits = 3
$scale = rand($decimal_digits, $scale);
// @see "Example #1 Calculate a random floating-point number" in
// http://php.net/manual/en/function.mt-getrandmax.php
$random_decimal = $min + mt_rand() / mt_getrandmax() * ($max - $min);
$values['value'] = self::truncateDecimal($random_decimal, $scale);
return $values;
}
/**
* Helper method to get the number of decimal digits out of a decimal number.
*
* @param int $decimal
* The number to calculate the number of decimals digits from.
*
* @return int
* The number of decimal digits.
*/
protected static function getDecimalDigits($decimal) {
$digits = 0;
while ($decimal - round($decimal)) {
$decimal *= 10;
$digits++;
}
return $digits;
}
}

View file

@ -0,0 +1,90 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\EmailItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Utility\Random;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Render\Element\Email;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'email' field type.
*
* @FieldType(
* id = "email",
* label = @Translation("Email"),
* description = @Translation("An entity field containing an email value."),
* default_widget = "email_default",
* default_formatter = "basic_string"
* )
*/
class EmailItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('email')
->setLabel(t('E-mail'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => Email::EMAIL_MAX_LENGTH,
),
),
);
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints = parent::getConstraints();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Length' => array(
'max' => Email::EMAIL_MAX_LENGTH,
'maxMessage' => t('%name: the email address can not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => Email::EMAIL_MAX_LENGTH)),
)
),
));
return $constraints;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$random = new Random();
$values['value'] = $random->name() . '@example.com';
return $values;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
return $this->value === NULL || $this->value === '';
}
}

View file

@ -0,0 +1,324 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
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\TypedData\DataDefinition;
use Drupal\Core\TypedData\DataReferenceDefinition;
/**
* 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_formatter = "entity_reference_label",
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* constraints = {"ValidReference" = {}}
* )
*/
class EntityReferenceItem extends FieldItemBase {
/**
* Marker value to identify a newly created entity.
*
* @var int
*/
protected static $NEW_ENTITY_MARKER = -1;
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'target_type' => \Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user',
'target_bundle' => NULL,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return array(
'handler' => 'default:' . (\Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'user'),
'handler_settings' => array(),
) + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$settings = $field_definition->getSettings();
$target_type_info = \Drupal::entityManager()->getDefinition($settings['target_type']);
$target_id_data_type = 'string';
if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
$id_definition = \Drupal::entityManager()->getBaseFieldDefinitions($settings['target_type'])[$target_type_info->getKey('id')];
if ($id_definition->getType() === 'integer') {
$target_id_data_type = 'integer';
}
}
if ($target_id_data_type === 'integer') {
$target_id_definition = DataDefinition::create('integer')
->setLabel(t('@label ID', array($target_type_info->getLabel())))
->setSetting('unsigned', TRUE);
}
else {
$target_id_definition = DataDefinition::create('string')
->setLabel(t('@label ID', array($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(t('The referenced entity'))
// The entity object is computed out of the entity ID.
->setComputed(TRUE)
->setReadOnly(FALSE)
->setTargetDefinition(EntityDataDefinition::create($settings['target_type']))
->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;
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
return 'target_id';
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$target_type = $field_definition->getSetting('target_type');
$target_type_info = \Drupal::entityManager()->getDefinition($target_type);
$properties = static::propertyDefinitions($field_definition)['target_id'];
if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface') && $properties->getDataType() === 'integer') {
$columns = array(
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'int',
'unsigned' => TRUE,
),
);
}
else {
$columns = array(
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'varchar_ascii',
// If the target entities act as bundles for another entity type,
// their IDs should not exceed the maximum length for bundles.
'length' => $target_type_info->getBundleOf() ? EntityTypeInterface::BUNDLE_MAX_LENGTH : 255,
),
);
}
$schema = array(
'columns' => $columns,
'indexes' => array(
'target_id' => array('target_id'),
),
);
return $schema;
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
if (isset($values) && !is_array($values)) {
// If either a scalar or an object was passed as the value for the item,
// assign it to the 'entity' property since that works for both cases.
$this->set('entity', $values, $notify);
}
else {
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'])) {
$this->onChange('target_id', FALSE);
}
elseif (!isset($values['target_id']) && isset($values['entity'])) {
$this->onChange('entity', FALSE);
}
elseif (isset($values['target_id']) && 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())) {
throw new \InvalidArgumentException('The target id and entity passed to the entity reference item do not match.');
}
}
// Notify the parent if necessary.
if ($notify && $this->parent) {
$this->parent->onChange($this->getName());
}
}
}
/**
* {@inheritdoc}
*/
public function getValue() {
$values = parent::getValue();
// If there is an unsaved entity, return it as part of the field item values
// to ensure idempotency of getValue() / setValue().
if ($this->hasNewEntity()) {
$values['entity'] = $this->entity;
}
return $values;
}
/**
* {@inheritdoc}
*/
public function onChange($property_name, $notify = TRUE) {
// 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();
$this->writePropertyValue('target_id', $target_id);
}
elseif ($property_name == 'target_id' && $this->target_id != static::$NEW_ENTITY_MARKER) {
$this->writePropertyValue('entity', $this->target_id);
}
parent::onChange($property_name, $notify);
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
// Avoid loading the entity by first checking the 'target_id'.
if ($this->target_id !== NULL) {
return FALSE;
}
if ($this->entity && $this->entity instanceof EntityInterface) {
return FALSE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function preSave() {
if ($this->hasNewEntity()) {
// Save the entity if it has not already been saved by some other code.
if ($this->entity->isNew()) {
$this->entity->save();
}
// Make sure the parent knows we are updating this property so it can
// react properly.
$this->target_id = $this->entity->id();
}
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$manager = \Drupal::service('plugin.manager.entity_reference_selection');
if ($referenceable = $manager->getSelectionHandler($field_definition)->getReferenceableEntities()) {
$group = array_rand($referenceable);
$values['target_id'] = array_rand($referenceable[$group]);
return $values;
}
}
/**
* Determines whether the item holds an unsaved entity.
*
* This is notably used for "autocreate" widgets, and more generally to
* support referencing freshly created entities (they will get saved
* automatically as the hosting entity gets saved).
*
* @return bool
* TRUE if the item holds an unsaved entity.
*/
public function hasNewEntity() {
return $this->target_id === static::$NEW_ENTITY_MARKER;
}
/**
* {@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']);
// If the entity does not exist do not create the dependency.
// @see \Drupal\Core\Field\EntityReferenceFieldItemList::processDefaultValue()
if ($entity) {
$dependencies[$target_entity_type->getConfigDependencyKey()][] = $entity->getConfigDependencyName();
}
}
}
}
return $dependencies;
}
/**
* {@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']);
// @see \Drupal\Core\Field\EntityReferenceFieldItemList::processDefaultValue()
if ($entity && isset($dependencies[$entity->getConfigDependencyKey()][$entity->getConfigDependencyName()])) {
unset($field_definition->default_value[$key]);
$changed = TRUE;
}
}
}
}
return $changed;
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\FloatItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'float' field type.
*
* @FieldType(
* id = "float",
* label = @Translation("Number (float)"),
* description = @Translation("This field stores a number in the database in a floating point format."),
* category = @Translation("Number"),
* default_widget = "number",
* default_formatter = "number_decimal"
* )
*/
class FloatItem extends NumericItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('float')
->setLabel(t('Float'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'float',
),
),
);
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = parent::fieldSettingsForm($form, $form_state);
$element['min']['#step'] = 'any';
$element['max']['#step'] = 'any';
return $element;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$settings = $field_definition->getSettings();
$precision = rand(10, 32);
$scale = rand(0, 2);
$max = is_numeric($settings['max']) ?: pow(10, ($precision - $scale)) - 1;
$min = is_numeric($settings['min']) ?: -pow(10, ($precision - $scale)) + 1;
// @see "Example #1 Calculate a random floating-point number" in
// http://php.net/manual/en/function.mt-getrandmax.php
$random_decimal = $min + mt_rand() / mt_getrandmax() * ($max - $min);
$values['value'] = self::truncateDecimal($random_decimal, $scale);
return $values;
}
}

View file

@ -0,0 +1,117 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'integer' field type.
*
* @FieldType(
* id = "integer",
* label = @Translation("Number (integer)"),
* description = @Translation("This field stores a number in the database as an integer."),
* category = @Translation("Number"),
* default_widget = "number",
* default_formatter = "number_integer"
* )
*/
class IntegerItem extends NumericItemBase {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'unsigned' => FALSE,
// Valid size property values include: 'tiny', 'small', 'medium', 'normal'
// and 'big'.
'size' => 'normal',
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return array(
'min' => '',
'max' => '',
'prefix' => '',
'suffix' => '',
) + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('integer')
->setLabel(t('Integer value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
// If this is an unsigned integer, add a validation constraint for the
// integer to be positive.
if ($this->getSetting('unsigned')) {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Range' => array(
'min' => 0,
'minMessage' => t('%name: The integer must be larger or equal to %min.', array(
'%name' => $this->getFieldDefinition()->getLabel(),
'%min' => 0,
)),
),
),
));
}
return $constraints;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
// Expose the 'unsigned' setting in the field item schema.
'unsigned' => $field_definition->getSetting('unsigned'),
// Expose the 'size' setting in the field item schema. For instance,
// supply 'big' as a value to produce a 'bigint' type.
'size' => $field_definition->getSetting('size'),
),
),
);
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$min = $field_definition->getSetting('min') ?: 0;
$max = $field_definition->getSetting('max') ?: 999;
$values['value'] = mt_rand($min, $max);
return $values;
}
}

View file

@ -0,0 +1,127 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\DataReferenceDefinition;
/**
* Defines the 'language' entity field item.
*
* @FieldType(
* id = "language",
* label = @Translation("Language"),
* description = @Translation("An entity field referencing a language."),
* default_widget = "language_select",
* default_formatter = "language",
* no_ui = TRUE,
* constraints = {
* "ComplexData" = {
* "value" = {
* "Length" = {"max" = 12},
* "AllowedValues" = {"callback" = "\Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem::getAllowedLanguageCodes" }
* }
* }
* }
* )
*
* @todo Define the AllowedValues constraint via an options provider once
* https://www.drupal.org/node/2329937 is completed.
*/
class LanguageItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
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')
->setLabel(t('Language object'))
->setDescription(t('The referenced language'))
// The language object is retrieved via the language code.
->setComputed(TRUE)
->setReadOnly(FALSE);
return $properties;
}
/**
* Defines allowed language codes for the field's AllowedValues constraint.
*
* @return string[]
* The allowed values.
*/
public static function getAllowedLanguageCodes() {
return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_ALL));
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => 12,
'is_ascii' => TRUE,
),
),
);
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
// Treat the values as property value of the language property, if no array
// is given as this handles language codes and objects.
if (isset($values) && !is_array($values)) {
$this->set('language', $values, $notify);
}
else {
// Make sure that the 'language' property gets set as 'value'.
if (isset($values['value']) && !isset($values['language'])) {
$values['language'] = $values['value'];
}
parent::setValue($values, $notify);
}
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to the site's default language. When language module is enabled,
// this behavior is configurable, see language_field_info_alter().
$this->setValue(array('value' => \Drupal::languageManager()->getDefaultLanguage()->getId()), $notify);
return $this;
}
/**
* {@inheritdoc}
*/
public function onChange($property_name, $notify = TRUE) {
// Make sure that the value and the language property stay in sync.
if ($property_name == 'value') {
$this->writePropertyValue('language', $this->value);
}
elseif ($property_name == 'language') {
$this->writePropertyValue('value', $this->get('language')->getTargetIdentifier());
}
parent::onChange($property_name, $notify);
}
}

View file

@ -0,0 +1,122 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\MapItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'map' entity field type.
*
* @FieldType(
* id = "map",
* label = @Translation("Map"),
* description = @Translation("An entity field for storing a serialized array of values."),
* no_ui = TRUE
* )
*/
class MapItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
// The properties are dynamic and can not be defined statically.
return array();
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'blob',
'size' => 'big',
'serialize' => TRUE,
),
),
);
}
/**
* {@inheritdoc}
*/
public function toArray() {
// The default implementation of toArray() only returns known properties.
// For a map, return everything as the properties are not pre-defined.
return $this->getValue();
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
$this->values = array();
if (!isset($values)) {
return;
}
if (!is_array($values)) {
if ($values instanceof MapItem) {
$values = $values->getValue();
}
else {
$values = unserialize($values);
}
}
$this->values = $values;
// Notify the parent of any changes.
if ($notify && isset($this->parent)) {
$this->parent->onChange($this->name);
}
}
/**
* {@inheritdoc}
*/
public function __get($name) {
if (!isset($this->values[$name])) {
$this->values[$name] = array();
}
return $this->values[$name];
}
/**
* {@inheritdoc}
*/
public function __set($name, $value) {
if (isset($value)) {
$this->values[$name] = $value;
}
else {
unset($this->values[$name]);
}
}
/**
* {@inheritdoc}
*/
public static function mainPropertyName() {
// A map item has no main property.
return NULL;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
return empty($this->values);
}
}

View file

@ -0,0 +1,129 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\NumericItemBase.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Base class for numeric configurable field types.
*/
abstract class NumericItemBase extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return array(
'min' => '',
'max' => '',
'prefix' => '',
'suffix' => '',
) + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element = array();
$settings = $this->getSettings();
$element['min'] = array(
'#type' => 'number',
'#title' => t('Minimum'),
'#default_value' => $settings['min'],
'#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
);
$element['max'] = array(
'#type' => 'number',
'#title' => t('Maximum'),
'#default_value' => $settings['max'],
'#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
);
$element['prefix'] = array(
'#type' => 'textfield',
'#title' => t('Prefix'),
'#default_value' => $settings['prefix'],
'#size' => 60,
'#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
);
$element['suffix'] = array(
'#type' => 'textfield',
'#title' => t('Suffix'),
'#default_value' => $settings['suffix'],
'#size' => 60,
'#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
if (empty($this->value) && (string) $this->value !== '0') {
return TRUE;
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints = parent::getConstraints();
$settings = $this->getSettings();
$label = $this->getFieldDefinition()->getLabel();
if (!empty($settings['min'])) {
$min = $settings['min'];
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Range' => array(
'min' => $min,
'minMessage' => t('%name: the value may be no less than %min.', array('%name' => $label, '%min' => $min)),
)
),
));
}
if (!empty($settings['max'])) {
$max = $settings['max'];
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Range' => array(
'max' => $max,
'maxMessage' => t('%name: the value may be no greater than %max.', array('%name' => $label, '%max' => $max)),
)
),
));
}
return $constraints;
}
/**
* Helper method to truncate a decimal number to a given number of decimals.
*
* @param float $decimal
* Decimal number to truncate.
* @param int $num
* Number of digits the output will have.
*
* @return float
* Decimal number truncated.
*/
protected static function truncateDecimal($decimal, $num) {
return floor($decimal * pow(10, $num)) / pow(10, $num);
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem.
*/
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\TypedData\DataDefinition;
/**
* Defines the 'password' entity field type.
*
* @FieldType(
* id = "password",
* label = @Translation("Password"),
* description = @Translation("An entity field containing a password value."),
* no_ui = TRUE,
* )
*/
class PasswordItem extends StringItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('The hashed password'))
->setSetting('case_sensitive', TRUE);
$properties['existing'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('Existing password'));
return $properties;
}
/**
* {@inheritdoc}
*/
public function preSave() {
parent::preSave();
$entity = $this->getEntity();
// Update the user password if it has changed.
if ($entity->isNew() || ($this->value && $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.
if (!$this->value) {
throw new EntityMalformedException('The entity does not have a password.');
}
}
if (!$entity->isNew()) {
// If the password is empty, that means it was not changed, so use the
// original password.
if (empty($this->value)) {
$this->value = $entity->original->{$this->getFieldDefinition()->getName()}->value;
}
}
}
}

View file

@ -0,0 +1,103 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\StringItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Utility\Random;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines the 'string' entity field type.
*
* @FieldType(
* id = "string",
* label = @Translation("Text (plain)"),
* description = @Translation("A field containing a plain string value."),
* category = @Translation("Text"),
* default_widget = "string_textfield",
* default_formatter = "string"
* )
*/
class StringItem extends StringItemBase {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'max_length' => 255,
'is_ascii' => FALSE,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => $field_definition->getSetting('is_ascii') === TRUE ? 'varchar_ascii' : 'varchar',
'length' => (int) $field_definition->getSetting('max_length'),
'binary' => $field_definition->getSetting('case_sensitive'),
),
),
);
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
if ($max_length = $this->getSetting('max_length')) {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Length' => array(
'max' => $max_length,
'maxMessage' => t('%name: may not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => $max_length)),
),
),
));
}
return $constraints;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$random = new Random();
$values['value'] = $random->word(mt_rand(1, $field_definition->getSetting('max_length')));
return $values;
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$element = array();
$element['max_length'] = array(
'#type' => 'number',
'#title' => t('Maximum length'),
'#default_value' => $this->getSetting('max_length'),
'#required' => TRUE,
'#description' => t('The maximum length of the field in characters.'),
'#min' => 1,
'#disabled' => $has_data,
);
return $element;
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\StringItemBase.
*/
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\TypedData\DataDefinition;
/**
* Base class for string field types.
*/
abstract class StringItemBase extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'case_sensitive' => FALSE,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
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.
$properties['value'] = DataDefinition::create('string')
->setLabel(new TranslationWrapper('Text value'))
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'))
->setRequired(TRUE);
return $properties;
}
}

View file

@ -0,0 +1,51 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\StringLongItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Utility\Random;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Defines the 'string_long' field type.
*
* @FieldType(
* id = "string_long",
* label = @Translation("Text (plain, long)"),
* description = @Translation("A field containing a long string value."),
* category = @Translation("Text"),
* default_widget = "string_textarea",
* default_formatter = "basic_string",
* )
*/
class StringLongItem extends StringItemBase {
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => $field_definition->getSetting('case_sensitive') ? 'blob' : 'text',
'size' => 'big',
),
),
);
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$random = new Random();
$values['value'] = $random->paragraphs();
return $values;
}
}

View file

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'timestamp' entity field type.
*
* @FieldType(
* id = "timestamp",
* label = @Translation("Timestamp"),
* description = @Translation("An entity field containing a UNIX timestamp value."),
* no_ui = TRUE,
* default_formatter = "timestamp",
* constraints = {
* "ComplexData" = {
* "value" = {
* "Range" = {
* "min" = "-2147483648",
* "max" = "2147483648",
* }
* }
* }
* }
* )
* )
*/
class TimestampItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('timestamp')
->setLabel(t('Timestamp value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
),
),
);
}
}

View file

@ -0,0 +1,77 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\UriItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'uri' entity field type.
*
* URIs are not length limited by RFC 2616, but we need to provide a sensible
* default. There is a de-facto limit of 2000 characters in browsers and other
* implementors, so we go with 2048.
*
* @FieldType(
* id = "uri",
* label = @Translation("URI"),
* description = @Translation("An entity field containing a URI."),
* no_ui = TRUE,
* default_formatter = "uri_link",
* )
*/
class UriItem extends StringItem {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'max_length' => 2048,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('uri')
->setLabel(t('URI value'))
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => (int) $field_definition->getSetting('max_length'),
'binary' => $field_definition->getSetting('case_sensitive'),
),
),
);
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->getValue();
if (!isset($value['value']) || $value['value'] === '') {
return TRUE;
}
return parent::isEmpty();
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\UuidItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Defines the 'uuid' entity field type.
*
* The field uses a newly generated UUID as default value.
*
* @FieldType(
* id = "uuid",
* label = @Translation("UUID"),
* description = @Translation("An entity field containing a UUID."),
* no_ui = TRUE,
* default_formatter = "string"
* )
*/
class UuidItem extends StringItem {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return array(
'max_length' => 128,
'is_ascii' => TRUE,
) + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public function applyDefaultValue($notify = TRUE) {
// Default to one field item with a generated UUID.
$uuid = \Drupal::service('uuid');
$this->setValue(array('value' => $uuid->generate()), $notify);
return $this;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['unique keys']['value'] = array('value');
return $schema;
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\BooleanCheckboxWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'boolean_checkbox' widget.
*
* @FieldWidget(
* id = "boolean_checkbox",
* label = @Translation("Single on/off checkbox"),
* field_types = {
* "boolean"
* },
* multiple_values = TRUE
* )
*/
class BooleanCheckboxWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'display_label' => FALSE,
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['display_label'] = array(
'#type' => 'checkbox',
'#title' => t('Use field label instead of the "On label" as label'),
'#default_value' => $this->getSetting('display_label'),
'#weight' => -1,
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$display_label = $this->getSetting('display_label');
$summary[] = t('Use field label: @display_label', array('@display_label' => ($display_label ? t('Yes') : 'No')));
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'checkbox',
'#default_value' => !empty($items[0]->value),
);
// Override the title from the incoming $element.
if ($this->getSetting('display_label')) {
$element['value']['#title'] = $this->fieldDefinition->getLabel();
}
else {
$element['value']['#title'] = $this->fieldDefinition->getSetting('on_label');
}
return $element;
}
}

View file

@ -0,0 +1,78 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\EmailDefaultWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'email_default' widget.
*
* @FieldWidget(
* id = "email_default",
* label = @Translation("Email"),
* field_types = {
* "email"
* }
* )
*/
class EmailDefaultWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
}
else {
$summary[] = t('No placeholder');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'email',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
'#placeholder' => $this->getSetting('placeholder'),
);
return $element;
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteTagsWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'entity_reference_autocomplete_tags' widget.
*
* @FieldWidget(
* id = "entity_reference_autocomplete_tags",
* label = @Translation("Autocomplete (Tags style)"),
* description = @Translation("An autocomplete text field with tagging support."),
* field_types = {
* "entity_reference"
* },
* multiple_values = TRUE
* )
*/
class EntityReferenceAutocompleteTagsWidget extends EntityReferenceAutocompleteWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['target_id']['#tags'] = TRUE;
$element['target_id']['#default_value'] = $items->referencedEntities();
return $element;
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
return $values['target_id'];
}
}

View file

@ -0,0 +1,184 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
/**
* Plugin implementation of the 'entity_reference_autocomplete' widget.
*
* @FieldWidget(
* id = "entity_reference_autocomplete",
* label = @Translation("Autocomplete"),
* description = @Translation("An autocomplete text field."),
* field_types = {
* "entity_reference"
* }
* )
*/
class EntityReferenceAutocompleteWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'match_operator' => 'CONTAINS',
'size' => '60',
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['match_operator'] = array(
'#type' => 'radios',
'#title' => t('Autocomplete matching'),
'#default_value' => $this->getSetting('match_operator'),
'#options' => array(
'STARTS_WITH' => t('Starts with'),
'CONTAINS' => t('Contains'),
),
'#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of entities.'),
);
$element['size'] = array(
'#type' => 'number',
'#title' => t('Size of textfield'),
'#default_value' => $this->getSetting('size'),
'#min' => 1,
'#required' => TRUE,
);
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$summary[] = t('Autocomplete matching: @match_operator', array('@match_operator' => $this->getSetting('match_operator')));
$summary[] = t('Textfield size: !size', array('!size' => $this->getSetting('size')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
}
else {
$summary[] = t('No placeholder');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$entity = $items->getEntity();
$referenced_entities = $items->referencedEntities();
$element += array(
'#type' => 'entity_autocomplete',
'#target_type' => $this->getFieldSetting('target_type'),
'#selection_handler' => $this->getFieldSetting('handler'),
'#selection_settings' => $this->getFieldSetting('handler_settings'),
// Entity reference field items are handling validation themselves via
// the 'ValidReference' constraint.
'#validate_reference' => FALSE,
'#maxlength' => 1024,
'#default_value' => isset($referenced_entities[$delta]) ? $referenced_entities[$delta] : NULL,
'#size' => $this->getSetting('size'),
'#placeholder' => $this->getSetting('placeholder'),
);
if ($this->getSelectionHandlerSetting('auto_create')) {
$element['#autocreate'] = array(
'bundle' => $this->getAutocreateBundle(),
'uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id()
);
}
return array('target_id' => $element);
}
/**
* {@inheritdoc}
*/
public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
return $element['target_id'];
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
foreach ($values as $key => $value) {
// The entity_autocomplete form element returns an array when an entity
// was "autocreated", so we need to move it up a level.
if (is_array($value['target_id'])) {
unset($values[$key]['target_id']);
$values[$key] += $value['target_id'];
}
}
return $values;
}
/**
* Returns the name of the bundle which will be used for autocreated entities.
*
* @return string
* The bundle name.
*/
protected function getAutocreateBundle() {
$bundle = NULL;
if ($this->getSelectionHandlerSetting('auto_create')) {
// If the 'target_bundles' setting is restricted to a single choice, we
// can use that.
if (($target_bundles = $this->getSelectionHandlerSetting('target_bundles')) && count($target_bundles) == 1) {
$bundle = reset($target_bundles);
}
// Otherwise use the first bundle as a fallback.
else {
// @todo Expose a proper UI for choosing the bundle for autocreated
// entities in https://www.drupal.org/node/2412569.
$bundles = entity_get_bundles($this->getFieldSetting('target_type'));
$bundle = key($bundles);
}
}
return $bundle;
}
/**
* Returns the value of a setting for the entity reference selection handler.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
protected function getSelectionHandlerSetting($setting_name) {
$settings = $this->getFieldSetting('handler_settings');
return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\LanguageSelectWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
/**
* Plugin implementation of the 'Language' widget.
*
* @FieldWidget(
* id = "language_select",
* label = @Translation("Language select"),
* field_types = {
* "language"
* }
* )
*/
class LanguageSelectWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'language_select',
'#default_value' => $items[$delta]->value,
'#languages' => LanguageInterface::STATE_ALL,
);
return $element;
}
}

View file

@ -0,0 +1,121 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
/**
* Plugin implementation of the 'number' widget.
*
* @FieldWidget(
* id = "number",
* label = @Translation("Text field"),
* field_types = {
* "integer",
* "decimal",
* "float"
* }
* )
*/
class NumberWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
}
else {
$summary[] = t('No placeholder');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$value = isset($items[$delta]->value) ? $items[$delta]->value : NULL;
$field_settings = $this->getFieldSettings();
$element += array(
'#type' => 'number',
'#default_value' => $value,
'#placeholder' => $this->getSetting('placeholder'),
);
// Set the step for floating point and decimal numbers.
switch ($this->fieldDefinition->getType()) {
case 'decimal':
$element['#step'] = pow(0.1, $field_settings['scale']);
break;
case 'float':
$element['#step'] = 'any';
break;
}
// Set minimum and maximum.
if (is_numeric($field_settings['min'])) {
$element['#min'] = $field_settings['min'];
}
if (is_numeric($field_settings['max'])) {
$element['#max'] = $field_settings['max'];
}
// Add prefix and suffix.
if ($field_settings['prefix']) {
$prefixes = explode('|', $field_settings['prefix']);
$element['#field_prefix'] = $this->fieldFilterXss(array_pop($prefixes));
}
if ($field_settings['suffix']) {
$suffixes = explode('|', $field_settings['suffix']);
$element['#field_suffix'] = $this->fieldFilterXss(array_pop($suffixes));
}
return array('value' => $element);
}
/**
* {@inheritdoc}
*/
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state) {
return $element['value'];
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsButtonsWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'options_buttons' widget.
*
* @FieldWidget(
* id = "options_buttons",
* label = @Translation("Check boxes/radio buttons"),
* field_types = {
* "boolean",
* "list_integer",
* "list_float",
* "list_string",
* },
* multiple_values = TRUE
* )
*/
class OptionsButtonsWidget extends OptionsWidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$options = $this->getOptions($items->getEntity());
$selected = $this->getSelectedOptions($items);
// If required and there is one single option, preselect it.
if ($this->required && count($options) == 1) {
reset($options);
$selected = array(key($options));
}
if ($this->multiple) {
$element += array(
'#type' => 'checkboxes',
'#default_value' => $selected,
'#options' => $options,
);
}
else {
$element += array(
'#type' => 'radios',
// Radio buttons need a scalar value. Take the first default value, or
// default to NULL so that the form element is properly recognized as
// not having a default value.
'#default_value' => $selected ? reset($selected) : NULL,
'#options' => $options,
);
}
return $element;
}
/**
* {@inheritdoc}
*/
protected function getEmptyLabel() {
if (!$this->required && !$this->multiple) {
return t('N/A');
}
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsSelectWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'options_select' widget.
*
* @FieldWidget(
* id = "options_select",
* label = @Translation("Select list"),
* field_types = {
* "list_integer",
* "list_float",
* "list_string"
* },
* multiple_values = TRUE
* )
*/
class OptionsSelectWidget extends OptionsWidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element += array(
'#type' => 'select',
'#options' => $this->getOptions($items->getEntity()),
'#default_value' => $this->getSelectedOptions($items, $delta),
// Do not display a 'multiple' select box if there is only one option.
'#multiple' => $this->multiple && count($this->options) > 1,
);
return $element;
}
/**
* {@inheritdoc}
*/
protected function sanitizeLabel(&$label) {
// Select form inputs allow unencoded HTML entities, but no HTML tags.
$label = Html::decodeEntities(strip_tags($label));
}
/**
* {@inheritdoc}
*/
protected function supportsGroups() {
return TRUE;
}
/**
* {@inheritdoc}
*/
protected function getEmptyLabel() {
if ($this->multiple) {
// Multiple select: add a 'none' option for non-required fields.
if (!$this->required) {
return t('- None -');
}
}
else {
// Single select: add a 'none' option for non-required fields,
// and a 'select a value' option for required fields that do not come
// with a value selected.
if (!$this->required) {
return t('- None -');
}
if (!$this->has_value) {
return t('- Select a value -');
}
}
}
}

View file

@ -0,0 +1,204 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\OptionsWidgetBase.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
/**
* Base class for the 'options_*' widgets.
*
* Field types willing to enable one or several of the widgets defined in
* options.module (select, radios/checkboxes, on/off checkbox) need to
* implement the OptionsProviderInterface to specify the list of options to
* display in the widgets.
*
* @see \Drupal\Core\TypedData\OptionsProviderInterface
*/
abstract class OptionsWidgetBase extends WidgetBase {
/**
* Abstract over the actual field columns, to allow different field types to
* reuse those widgets.
*
* @var string
*/
protected $column;
/**
* {@inheritdoc}
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$property_names = $this->fieldDefinition->getFieldStorageDefinition()->getPropertyNames();
$this->column = $property_names[0];
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
// Prepare some properties for the child methods to build the actual form
// element.
$this->required = $element['#required'];
$this->multiple = $this->fieldDefinition->getFieldStorageDefinition()->isMultiple();
$this->has_value = isset($items[0]->{$this->column});
// Add our custom validator.
$element['#element_validate'][] = array(get_class($this), 'validateElement');
$element['#key_column'] = $this->column;
// The rest of the $element is built by child method implementations.
return $element;
}
/**
* Form validation handler for widget elements.
*
* @param array $element
* The form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
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'])));
}
// Massage submitted form values.
// Drupal\Core\Field\WidgetBase::submit() expects values as
// an array of values keyed by delta first, then by column, while our
// widgets return the opposite.
if (is_array($element['#value'])) {
$values = array_values($element['#value']);
}
else {
$values = array($element['#value']);
}
// Filter out the 'none' option. Use a strict comparison, because
// 0 == 'any string'.
$index = array_search('_none', $values, TRUE);
if ($index !== FALSE) {
unset($values[$index]);
}
// Transpose selections from field => delta to delta => field.
$items = array();
foreach ($values as $value) {
$items[] = array($element['#key_column'] => $value);
}
$form_state->setValueForElement($element, $items);
}
/**
* Returns the array of options for the widget.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity for which to return options.
*
* @return array
* The array of options for the widget.
*/
protected function getOptions(FieldableEntityInterface $entity) {
if (!isset($this->options)) {
// Limit the settable options for the current user account.
$options = $this->fieldDefinition
->getFieldStorageDefinition()
->getOptionsProvider($this->column, $entity)
->getSettableOptions(\Drupal::currentUser());
// Add an empty option if the widget needs one.
if ($empty_label = $this->getEmptyLabel()) {
$options = ['_none' => $empty_label] + $options;
}
$module_handler = \Drupal::moduleHandler();
$context = array(
'fieldDefinition' => $this->fieldDefinition,
'entity' => $entity,
);
$module_handler->alter('options_list', $options, $context);
array_walk_recursive($options, array($this, 'sanitizeLabel'));
// Options might be nested ("optgroups"). If the widget does not support
// nested options, flatten the list.
if (!$this->supportsGroups()) {
$options = OptGroup::flattenOptions($options);
}
$this->options = $options;
}
return $this->options;
}
/**
* Determines selected options from the incoming field values.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values.
* @param int $delta
* (optional) The delta of the item to get options for. Defaults to 0.
*
* @return array
* The array of corresponding selected options.
*/
protected function getSelectedOptions(FieldItemListInterface $items, $delta = 0) {
// We need to check against a flat list of options.
$flat_options = OptGroup::flattenOptions($this->getOptions($items->getEntity()));
$selected_options = array();
foreach ($items as $item) {
$value = $item->{$this->column};
// Keep the value if it actually is in the list of options (needs to be
// checked against the flat list).
if (isset($flat_options[$value])) {
$selected_options[] = $value;
}
}
return $selected_options;
}
/**
* Indicates whether the widgets support optgroups.
*
* @return bool
* TRUE if the widget supports optgroups, FALSE otherwise.
*/
protected function supportsGroups() {
return FALSE;
}
/**
* Sanitizes a string label to display as an option.
*
* @param string $label
* The label to sanitize.
*/
protected function sanitizeLabel(&$label) {
// Allow a limited set of HTML tags.
$label = $this->fieldFilterXss($label);
}
/**
* Returns the empty option label to add to the list of options, if any.
*
* @return string|NULL
* Either a label of the empty option, or NULL.
*/
protected function getEmptyLabel() { }
}

View file

@ -0,0 +1,87 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextareaWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'string_textarea' widget.
*
* @FieldWidget(
* id = "string_textarea",
* label = @Translation("Text area (multiple rows)"),
* field_types = {
* "string_long"
* }
* )
*/
class StringTextareaWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'rows' => '5',
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['rows'] = array(
'#type' => 'number',
'#title' => t('Rows'),
'#default_value' => $this->getSetting('rows'),
'#required' => TRUE,
'#min' => 1,
);
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$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));
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'textarea',
'#default_value' => $items[$delta]->value,
'#rows' => $this->getSetting('rows'),
'#placeholder' => $this->getSetting('placeholder'),
'#attributes' => array('class' => array('js-text-full', 'text-full')),
);
return $element;
}
}

View file

@ -0,0 +1,88 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'string_textfield' widget.
*
* @FieldWidget(
* id = "string_textfield",
* label = @Translation("Textfield"),
* field_types = {
* "string"
* }
* )
*/
class StringTextfieldWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'size' => 60,
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['size'] = array(
'#type' => 'number',
'#title' => t('Size of textfield'),
'#default_value' => $this->getSetting('size'),
'#required' => TRUE,
'#min' => 1,
);
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$summary[] = t('Textfield size: !size', array('!size' => $this->getSetting('size')));
$placeholder = $this->getSetting('placeholder');
if (!empty($placeholder)) {
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'textfield',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
'#size' => $this->getSetting('size'),
'#placeholder' => $this->getSetting('placeholder'),
'#maxlength' => $this->getFieldSetting('max_length'),
'#attributes' => array('class' => array('js-text-full', 'text-full')),
);
return $element;
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\UriWidget.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'uri' widget.
*
* @FieldWidget(
* id = "uri",
* label = @Translation("URI field"),
* field_types = {
* "uri",
* }
* )
*/
class UriWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array(
'size' => 60,
'placeholder' => '',
) + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['size'] = array(
'#type' => 'number',
'#title' => $this->t('Size of URI field'),
'#default_value' => $this->getSetting('size'),
'#required' => TRUE,
'#min' => 1,
);
$element['placeholder'] = array(
'#type' => 'textfield',
'#title' => $this->t('Placeholder'),
'#default_value' => $this->getSetting('placeholder'),
'#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = array();
$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));
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + array(
'#type' => 'url',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
'#size' => $this->getSetting('size'),
'#placeholder' => $this->getSetting('placeholder'),
'#maxlength' => $this->getFieldSetting('max_length'),
);
return $element;
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\PluginSettingsBase.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Plugin\PluginBase;
/**
* Base class for the Field API plugins.
*
* This class handles lazy replacement of default settings values.
*/
abstract class PluginSettingsBase extends PluginBase implements PluginSettingsInterface, DependentPluginInterface {
/**
* The plugin settings.
*
* @var array
*/
protected $settings = array();
/**
* The plugin settings injected by third party modules.
*
* @see hooks
*
* @var array
*/
protected $thirdPartySettings = array();
/**
* Whether default settings have been merged into the current $settings.
*
* @var bool
*/
protected $defaultSettingsMerged = FALSE;
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return array();
}
/**
* {@inheritdoc}
*/
public function getSettings() {
// Merge defaults before returning the array.
if (!$this->defaultSettingsMerged) {
$this->mergeDefaults();
}
return $this->settings;
}
/**
* {@inheritdoc}
*/
public function getSetting($key) {
// Merge defaults if we have no value for the key.
if (!$this->defaultSettingsMerged && !array_key_exists($key, $this->settings)) {
$this->mergeDefaults();
}
return isset($this->settings[$key]) ? $this->settings[$key] : NULL;
}
/**
* Merges default settings values into $settings.
*/
protected function mergeDefaults() {
$this->settings += static::defaultSettings();
$this->defaultSettingsMerged = TRUE;
}
/**
* {@inheritdoc}
*/
public function setSettings(array $settings) {
$this->settings = $settings;
$this->defaultSettingsMerged = FALSE;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSetting($key, $value) {
$this->settings[$key] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function getThirdPartySetting($module, $key, $default = NULL) {
return isset($this->thirdPartySettings[$module][$key]) ? $this->thirdPartySettings[$module][$key] : $default;
}
/**
* {@inheritdoc}
*/
public function setThirdPartySetting($module, $key, $value) {
$this->thirdPartySettings[$module][$key] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
if (!empty($this->thirdPartySettings)) {
// Create dependencies on any modules providing third party settings.
return array(
'module' => array_keys($this->thirdPartySettings)
);
}
return array();
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\PluginSettingsInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Interface definition for plugin with settings.
*/
interface PluginSettingsInterface extends PluginInspectionInterface {
/**
* Defines the default settings for this plugin.
*
* @return array
* A list of default settings, keyed by the setting name.
*/
public static function defaultSettings();
/**
* Returns the array of settings, including defaults for missing settings.
*
* @return array
* The array of settings.
*/
public function getSettings();
/**
* Returns the value of a setting, or its default value if absent.
*
* @param string $key
* The setting name.
*
* @return mixed
* The setting value.
*/
public function getSetting($key);
/**
* Sets the settings for the plugin.
*
* @param array $settings
* The array of settings, keyed by setting names. Missing settings will be
* assigned their default values.
*
* @return $this
*/
public function setSettings(array $settings);
/**
* Sets the value of a setting for the plugin.
*
* @param string $key
* The setting name.
* @param mixed $value
* The setting value.
*
* @return $this
*/
public function setSetting($key, $value);
/**
* Returns the value of a third-party setting, or $default if not set.
*
* @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.
*
* @return mixed|NULL
* The setting value. Returns NULL if the setting does not exist and
* $default is not provided.
*/
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);
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface.
*/
namespace Drupal\Core\Field;
/**
* Defines an interface for exposing "preconfigured" field definitions.
*
* These field definitions will be exposed as additional options in the 'Add
* field' form in Field UI, together with individual field types.
*
* @see \Drupal\Core\Field\FieldTypePluginManager::getUiDefinitions()
* @see \Drupal\field_ui\Form\FieldStorageAddForm::submitForm()
*/
interface PreconfiguredFieldUiOptionsInterface {
/**
* Returns preconfigured field options for a field type.
*
* @return mixed[][]
* A multi-dimensional array with string keys and the following structure:
* - label: The label to show in the field type selection list.
* - category: (optional) The category in which to put the field label.
* Defaults to the category of the field type.
* - field_storage_config: An array with the following supported keys:
* - cardinality: The field cardinality.
* - settings: Field-type specific storage settings.
* - field_config: An array with the following supported keys:
* - required: Indicates whether the field is required.
* - settings: Field-type specific settings.
* - entity_form_display: An array with the following supported keys:
* - type: The widget to be used in the 'default' form mode.
* - entity_view_display: An array with the following supported keys:
* - type: The formatter to be used in the 'default' view mode.
*
* @see \Drupal\field\Entity\FieldStorageConfig
* @see \Drupal\field\Entity\FieldConfig
* @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent()
*/
public static function getPreconfiguredOptions();
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\TypedData\FieldItemDataDefinition.
*/
namespace Drupal\Core\Field\TypedData;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* A typed data definition class for defining field items.
*
* This class is just a small wrapper around field definitions to expose
* metadata about field item's via the Typed Data API. As the work is done
* by the field definitions, this class does not benefit and thus does not
* extend from MapDefinition or ComplexDataDefinitionBase.
*/
class FieldItemDataDefinition extends DataDefinition implements ComplexDataDefinitionInterface {
/**
* The field definition the item definition belongs to.
*
* @var \Drupal\Core\Field\FieldDefinitionInterface
*/
protected $fieldDefinition;
/**
* {@inheritdoc}
*/
public static function createFromDataType($data_type) {
// The data type of a field item is in the form of "field_item:$field_type".
$parts = explode(':', $data_type, 2);
if ($parts[0] != 'field_item') {
throw new \InvalidArgumentException('Data type must be in the form of "field_item:FIELD_TYPE".');
}
$field_definition = BaseFieldDefinition::create($parts[1]);
return $field_definition->getItemDefinition();
}
/**
* Creates a new field item definition.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition the item definition belongs to.
*
* @return static
*/
public static function create($field_definition) {
$definition['type'] = 'field_item:' . $field_definition->getType();
$item_definition = new static($definition);
$item_definition->fieldDefinition = $field_definition;
return $item_definition;
}
/**
* {@inheritdoc}
*/
public function getPropertyDefinition($name) {
return $this->fieldDefinition->getFieldStorageDefinition()->getPropertyDefinition($name);
}
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
return $this->fieldDefinition->getFieldStorageDefinition()->getPropertyDefinitions();
}
/**
* {@inheritdoc}
*/
public function getMainPropertyName() {
return $this->fieldDefinition->getFieldStorageDefinition()->getMainPropertyName();
}
/**
* Gets the field item's field definition.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition for this field item.
*/
public function getFieldDefinition() {
return $this->fieldDefinition;
}
}

View file

@ -0,0 +1,572 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\WidgetBase.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\SortArray;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
/**
* Base class for 'Field widget' plugin implementations.
*
* @ingroup field_widget
*/
abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface {
use AllowedTagsXssTrait;
/**
* The field definition.
*
* @var \Drupal\Core\Field\FieldDefinitionInterface
*/
protected $fieldDefinition;
/**
* The widget settings.
*
* @var array
*/
protected $settings;
/**
* Constructs a WidgetBase object.
*
* @param array $plugin_id
* The plugin_id for the widget.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the widget is associated.
* @param array $settings
* The widget settings.
* @param array $third_party_settings
* Any third party settings.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings) {
parent::__construct(array(), $plugin_id, $plugin_definition);
$this->fieldDefinition = $field_definition;
$this->settings = $settings;
$this->thirdPartySettings = $third_party_settings;
}
/**
* {@inheritdoc}
*/
public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
$field_name = $this->fieldDefinition->getName();
$parents = $form['#parents'];
// Store field information in $form_state.
if (!static::getWidgetState($parents, $field_name, $form_state)) {
$field_state = array(
'items_count' => count($items),
'array_parents' => array(),
);
static::setWidgetState($parents, $field_name, $form_state, $field_state);
}
// Collect widget elements.
$elements = array();
// If the widget is handling multiple values (e.g Options), or if we are
// displaying an individual element, just get a single form element and make
// it the $delta value.
if ($this->handlesMultipleValues() || isset($get_delta)) {
$delta = isset($get_delta) ? $get_delta : 0;
$element = array(
'#title' => SafeMarkup::checkPlain($this->fieldDefinition->getLabel()),
'#description' => $this->fieldFilterXss(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
);
$element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
if ($element) {
if (isset($get_delta)) {
// If we are processing a specific delta value for a field where the
// field module handles multiples, set the delta in the result.
$elements[$delta] = $element;
}
else {
// For fields that handle their own processing, we cannot make
// assumptions about how the field is structured, just merge in the
// returned element.
$elements = $element;
}
}
}
// If the widget does not handle multiple values itself, (and we are not
// displaying an individual element), process the multiple value form.
else {
$elements = $this->formMultipleElements($items, $form, $form_state);
}
// Populate the 'array_parents' information in $form_state->get('field')
// after the form is built, so that we catch changes in the form structure
// performed in alter() hooks.
$elements['#after_build'][] = array(get_class($this), 'afterBuild');
$elements['#field_name'] = $field_name;
$elements['#field_parents'] = $parents;
// Enforce the structure of submitted values.
$elements['#parents'] = array_merge($parents, array($field_name));
// Most widgets need their internal structure preserved in submitted values.
$elements += array('#tree' => TRUE);
return array(
// Aid in theming of widgets by rendering a classified container.
'#type' => 'container',
// Assign a different parent, to keep the main id for the widget itself.
'#parents' => array_merge($parents, array($field_name . '_wrapper')),
'#attributes' => array(
'class' => array(
'field-type-' . Html::getClass($this->fieldDefinition->getType()),
'field-name-' . Html::getClass($field_name),
'field-widget-' . Html::getClass($this->getPluginId()),
),
),
'widget' => $elements,
);
}
/**
* Special handling to create form elements for multiple values.
*
* Handles generic features for multiple fields:
* - number of widgets
* - AHAH-'add more' button
* - table display and drag-n-drop value reordering
*/
protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition->getName();
$cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
$parents = $form['#parents'];
// Determine the number of widgets to display.
switch ($cardinality) {
case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED:
$field_state = static::getWidgetState($parents, $field_name, $form_state);
$max = $field_state['items_count'];
$is_multiple = TRUE;
break;
default:
$max = $cardinality - 1;
$is_multiple = ($cardinality > 1);
break;
}
$title = SafeMarkup::checkPlain($this->fieldDefinition->getLabel());
$description = $this->fieldFilterXss(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
$elements = array();
for ($delta = 0; $delta <= $max; $delta++) {
// Add a new empty item if it doesn't exist yet at this delta.
if (!isset($items[$delta])) {
$items->appendItem();
}
// For multiple fields, title and description are handled by the wrapping
// table.
if ($is_multiple) {
$element = [
'#title' => $title . ' ' . $this->t('(value @number)', ['@number' => $delta + 1]),
'#title_display' => 'invisible',
'#description' => '',
];
}
else {
$element = [
'#title' => $title,
'#title_display' => 'before',
'#description' => $description,
];
}
$element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
if ($element) {
// Input field for the delta (drag-n-drop reordering).
if ($is_multiple) {
// We name the element '_weight' to avoid clashing with elements
// defined by widget.
$element['_weight'] = array(
'#type' => 'weight',
'#title' => $this->t('Weight for row @number', array('@number' => $delta + 1)),
'#title_display' => 'invisible',
// Note: this 'delta' is the FAPI #type 'weight' element's property.
'#delta' => $max,
'#default_value' => $items[$delta]->_weight ?: $delta,
'#weight' => 100,
);
}
$elements[$delta] = $element;
}
}
if ($elements) {
$elements += array(
'#theme' => 'field_multiple_value_form',
'#field_name' => $field_name,
'#cardinality' => $cardinality,
'#cardinality_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(),
'#required' => $this->fieldDefinition->isRequired(),
'#title' => $title,
'#description' => $description,
'#max_delta' => $max,
);
// Add 'add more' button, if not working with a programmed form.
if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && !$form_state->isProgrammed()) {
$id_prefix = implode('-', array_merge($parents, array($field_name)));
$wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper');
$elements['#prefix'] = '<div id="' . $wrapper_id . '">';
$elements['#suffix'] = '</div>';
$elements['add_more'] = array(
'#type' => 'submit',
'#name' => strtr($id_prefix, '-', '_') . '_add_more',
'#value' => t('Add another item'),
'#attributes' => array('class' => array('field-add-more-submit')),
'#limit_validation_errors' => array(array_merge($parents, array($field_name))),
'#submit' => array(array(get_class($this), 'addMoreSubmit')),
'#ajax' => array(
'callback' => array(get_class($this), 'addMoreAjax'),
'wrapper' => $wrapper_id,
'effect' => 'fade',
),
);
}
}
return $elements;
}
/**
* After-build handler for field elements in a form.
*
* This stores the final location of the field within the form structure so
* that flagErrors() can assign validation errors to the right form element.
*/
public static function afterBuild(array $element, FormStateInterface $form_state) {
$parents = $element['#field_parents'];
$field_name = $element['#field_name'];
$field_state = static::getWidgetState($parents, $field_name, $form_state);
$field_state['array_parents'] = $element['#array_parents'];
static::setWidgetState($parents, $field_name, $form_state, $field_state);
return $element;
}
/**
* Submission handler for the "Add another item" button.
*/
public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
// Go one level up in the form, to the widgets container.
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
$field_name = $element['#field_name'];
$parents = $element['#field_parents'];
// Increment the items count.
$field_state = static::getWidgetState($parents, $field_name, $form_state);
$field_state['items_count']++;
static::setWidgetState($parents, $field_name, $form_state, $field_state);
$form_state->setRebuild();
}
/**
* Ajax callback for the "Add another item" button.
*
* This returns the new page content to replace the page content made obsolete
* by the form submission.
*/
public static function addMoreAjax(array $form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
// Go one level up in the form, to the widgets container.
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
// Ensure the widget allows adding additional items.
if ($element['#cardinality'] != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
return;
}
// Add a DIV around the delta receiving the Ajax effect.
$delta = $element['#max_delta'];
$element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
$element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
return $element;
}
/**
* Generates the form element for a single copy of the widget.
*/
protected function formSingleElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$entity = $items->getEntity();
$element += array(
'#field_parents' => $form['#parents'],
// Only the first widget should be required.
'#required' => $delta == 0 && $this->fieldDefinition->isRequired(),
'#delta' => $delta,
'#weight' => $delta,
);
$element = $this->formElement($items, $delta, $element, $form, $form_state);
if ($element) {
// Allow modules to alter the field widget form element.
$context = array(
'form' => $form,
'widget' => $this,
'items' => $items,
'delta' => $delta,
'default' => $this->isDefaultValueWidget($form_state),
);
\Drupal::moduleHandler()->alter(array('field_widget_form', 'field_widget_' . $this->getPluginId() . '_form'), $element, $form_state, $context);
}
return $element;
}
/**
* {@inheritdoc}
*/
public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition->getName();
// Extract the values from $form_state->getValues().
$path = array_merge($form['#parents'], array($field_name));
$key_exists = NULL;
$values = NestedArray::getValue($form_state->getValues(), $path, $key_exists);
if ($key_exists) {
// Account for drag-and-drop reordering if needed.
if (!$this->handlesMultipleValues()) {
// Remove the 'value' of the 'add more' button.
unset($values['add_more']);
// The original delta, before drag-and-drop reordering, is needed to
// route errors to the correct form element.
foreach ($values as $delta => &$value) {
$value['_original_delta'] = $delta;
}
usort($values, function ($a, $b) {
return SortArray::sortByKeyInt($a, $b, '_weight');
});
}
// Let the widget massage the submitted values.
$values = $this->massageFormValues($values, $form, $form_state);
// Assign the values and remove the empty ones.
$items->setValue($values);
$items->filterEmptyItems();
// Put delta mapping in $form_state, so that flagErrors() can use it.
$field_state = static::getWidgetState($form['#parents'], $field_name, $form_state);
foreach ($items as $delta => $item) {
$field_state['original_deltas'][$delta] = isset($item->_original_delta) ? $item->_original_delta : $delta;
unset($item->_original_delta, $item->_weight);
}
static::setWidgetState($form['#parents'], $field_name, $form_state, $field_state);
}
}
/**
* {@inheritdoc}
*/
public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition->getName();
$field_state = static::getWidgetState($form['#parents'], $field_name, $form_state);
if ($violations->count()) {
$form_builder = \Drupal::formBuilder();
// Locate the correct element in the form.
$element = NestedArray::getValue($form_state->getCompleteForm(), $field_state['array_parents']);
// Do not report entity-level validation errors if Form API errors have
// already been reported for the field.
// @todo Field validation should not be run on fields with FAPI errors to
// begin with. See https://www.drupal.org/node/2070429.
$element_path = implode('][', $element['#parents']);
if ($reported_errors = $form_state->getErrors()) {
foreach (array_keys($reported_errors) as $error_path) {
if (strpos($error_path, $element_path) === 0) {
return;
}
}
}
// Only set errors if the element is accessible.
if (!isset($element['#access']) || $element['#access']) {
$handles_multiple = $this->handlesMultipleValues();
$violations_by_delta = array();
foreach ($violations as $violation) {
// Separate violations by delta.
$property_path = explode('.', $violation->getPropertyPath());
$delta = array_shift($property_path);
$violations_by_delta[$delta][] = $violation;
$violation->arrayPropertyPath = $property_path;
}
/** @var \Symfony\Component\Validator\ConstraintViolationInterface[] $delta_violations */
foreach ($violations_by_delta as $delta => $delta_violations) {
// Pass violations to the main element:
// - if this is a multiple-value widget,
// - or if the violations are at the ItemList level.
if ($handles_multiple || !is_numeric($delta)) {
$delta_element = $element;
}
// Otherwise, pass errors by delta to the corresponding sub-element.
else {
$original_delta = $field_state['original_deltas'][$delta];
$delta_element = $element[$original_delta];
}
foreach ($delta_violations as $violation) {
// @todo: Pass $violation->arrayPropertyPath as property path.
$error_element = $this->errorElement($delta_element, $violation, $form, $form_state);
if ($error_element !== FALSE) {
$form_state->setError($error_element, $violation->getMessage());
}
}
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getWidgetState(array $parents, $field_name, FormStateInterface $form_state) {
return NestedArray::getValue($form_state->getStorage(), static::getWidgetStateParents($parents, $field_name));
}
/**
* {@inheritdoc}
*/
public static function setWidgetState(array $parents, $field_name, FormStateInterface $form_state, array $field_state) {
NestedArray::setValue($form_state->getStorage(), static::getWidgetStateParents($parents, $field_name), $field_state);
}
/**
* Returns the location of processing information within $form_state.
*
* @param array $parents
* The array of #parents where the widget lives in the form.
* @param string $field_name
* The field name.
*
* @return array
* The location of processing information within $form_state.
*/
protected static function getWidgetStateParents(array $parents, $field_name) {
// Field processing data is placed at
// $form_state->get(['field_storage', '#parents', ...$parents..., '#fields', $field_name]),
// to avoid clashes between field names and $parents parts.
return array_merge(array('field_storage', '#parents'), $parents, array('#fields', $field_name));
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
return array();
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
return array();
}
/**
* {@inheritdoc}
*/
public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
return $element;
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
return $values;
}
/**
* Returns the array of field settings.
*
* @return array
* The array of settings.
*/
protected function getFieldSettings() {
return $this->fieldDefinition->getSettings();
}
/**
* Returns the value of a field setting.
*
* @param string $setting_name
* The setting name.
*
* @return mixed
* The setting value.
*/
protected function getFieldSetting($setting_name) {
return $this->fieldDefinition->getSetting($setting_name);
}
/**
* Returns whether the widget handles multiple values.
*
* @return bool
* TRUE if a single copy of formElement() can handle multiple field values,
* FALSE if multiple values require separate copies of formElement().
*/
protected function handlesMultipleValues() {
$definition = $this->getPluginDefinition();
return $definition['multiple_values'];
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
// By default, widgets are available for all fields.
return TRUE;
}
/**
* Returns whether the widget used for default value form.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return bool
* TRUE if a widget used to input default value, FALSE otherwise.
*/
protected function isDefaultValueWidget(FormStateInterface $form_state) {
return (bool) $form_state->get('default_value_widget');
}
}

View file

@ -0,0 +1,112 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\WidgetBaseInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
/**
* Base interface definition for "Field widget" plugins.
*
* This interface details base wrapping methods that most widget implementations
* will want to directly inherit from Drupal\Core\Field\WidgetBase. See
* Drupal\Core\Field\WidgetInterface for methods that will more likely be
* overridden in actual widget implementations.
*/
interface WidgetBaseInterface extends PluginSettingsInterface {
/**
* Creates a form element for a field.
*
* If the entity associated with the form is new (i.e., $entity->isNew() is
* TRUE), the 'default value', if any, is pre-populated. Also allows other
* modules to alter the form element by implementing their own hooks.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* An array of the field values. When creating a new entity this may be NULL
* or an empty array to use default values.
* @param array $form
* An array representing the form that the editing element will be attached
* to.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param int $get_delta
* Used to get only a specific delta value of a multiple value field.
*
* @return array
* The form element array created for this field.
*/
public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL);
/**
* Extracts field values from submitted form values.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values. This parameter is altered by reference to receive the
* incoming form values.
* @param array $form
* The form structure where field elements are attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state);
/**
* Reports field-level validation errors against actual form elements.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values.
* @param \Symfony\Component\Validator\ConstraintViolationListInterface $violations
* A list of constraint violations to flag.
* @param array $form
* The form structure where field elements are attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state);
/**
* Retrieves processing information about the widget from $form_state.
*
* This method is static so that is can be used in static Form API callbacks.
*
* @param array $parents
* The array of #parents where the field lives in the form.
* @param string $field_name
* The field name.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* An array with the following key/value pairs:
* - items_count: The number of widgets to display for the field.
* - array_parents: The location of the field's widgets within the $form
* structure. This entry is populated at '#after_build' time.
*/
public static function getWidgetState(array $parents, $field_name, FormStateInterface $form_state);
/**
* Stores processing information about the widget in $form_state.
*
* This method is static so that is can be used in static Form API #callbacks.
*
* @param array $parents
* The array of #parents where the widget lives in the form.
* @param string $field_name
* The field name.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
* @param array $field_state
* The array of data to store. See getWidgetState() for the structure and
* content of the array.
*/
public static function setWidgetState(array $parents, $field_name, FormStateInterface $form_state, array $field_state);
}

View file

@ -0,0 +1,166 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\WidgetInterface.
*/
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
/**
* Interface definition for field widget plugins.
*
* This interface details the methods that most plugin implementations will want
* to override. See Drupal\Core\Field\WidgetBaseInterface for base
* wrapping methods that should most likely be inherited directly from
* Drupal\Core\Field\WidgetBase..
*
* @ingroup field_widget
*/
interface WidgetInterface extends WidgetBaseInterface {
/**
* Returns a form to configure settings for the widget.
*
* Invoked from \Drupal\field_ui\Form\EntityDisplayFormBase to allow
* administrators to configure the widget. The field_ui module takes care of
* handling submitted form values.
*
* @param array $form
* The form where the settings form is being included in.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form definition for the widget settings.
*/
public function settingsForm(array $form, FormStateInterface $form_state);
/**
* Returns a short summary for the current widget settings.
*
* If an empty result is returned, a UI can still be provided to display
* a settings form in case the widget has configurable settings.
*
* @return array
* A short summary of the widget settings.
*/
public function settingsSummary();
/**
* Returns the form for a single field widget.
*
* Field widget form elements should be based on the passed-in $element, which
* contains the base form element properties derived from the field
* configuration.
*
* The BaseWidget methods will set the weight, field name and delta values for
* each form element. If there are multiple values for this field, the
* formElement() method will be called as many times as needed.
*
* Other modules may alter the form element provided by this function using
* hook_field_widget_form_alter() or
* hook_field_widget_WIDGET_TYPE_form_alter().
*
* The FAPI element callbacks (such as #process, #element_validate,
* #value_callback, etc.) used by the widget do not have access to the
* original $field_definition passed to the widget's constructor. Therefore,
* if any information is needed from that definition by those callbacks, the
* widget implementing this method, or a
* hook_field_widget[_WIDGET_TYPE]_form_alter() implementation, must extract
* the needed properties from the field definition and set them as ad-hoc
* $element['#custom'] properties, for later use by its element callbacks.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* Array of default values for this field.
* @param int $delta
* The order of this item in the array of sub-elements (0, 1, 2, etc.).
* @param array $element
* A form element array containing basic properties for the widget:
* - #field_parents: The 'parents' space for the field in the form. Most
* widgets can simply overlook this property. This identifies the
* location where the field values are placed within
* $form_state->getValues(), and is used to access processing
* information for the field through the getWidgetState() and
* setWidgetState() methods.
* - #title: The sanitized element label for the field, ready for output.
* - #description: The sanitized element description for the field, ready
* for output.
* - #required: A Boolean indicating whether the element value is required;
* for required multiple value fields, only the first widget's values are
* required.
* - #delta: The order of this item in the array of sub-elements; see $delta
* above.
* @param array $form
* The form structure where widgets are being attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form elements for a single widget for this field.
*
* @see hook_field_widget_form_alter()
* @see hook_field_widget_WIDGET_TYPE_form_alter()
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state);
/**
* Assigns a field-level validation error to the right widget sub-element.
*
* Depending on the widget's internal structure, a field-level validation
* error needs to be flagged on the right sub-element.
*
* @param array $element
* An array containing the form element for the widget, as generated by
* formElement().
* @param \Symfony\Component\Validator\ConstraintViolationInterface $violation
* A constraint violation reported during the validation phase.
* @param array $form
* The form structure where field elements are attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array|bool
* The element on which the error should be flagged, or FALSE to completely
* ignore the violation (use with care!).
*/
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, FormStateInterface $form_state);
/**
* Massages the form values into the format expected for field values.
*
* @param array $values
* The submitted form values produced by the widget.
* - If the widget does not manage multiple values itself, the array holds
* the values generated by the multiple copies of the $element generated
* by the formElement() method, keyed by delta.
* - If the widget manages multiple values, the array holds the values
* of the form element generated by the formElement() method.
* @param array $form
* The form structure where field elements are attached to. This might be a
* full form structure, or a sub-element of a larger form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* An array of field values, keyed by delta.
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state);
/**
* Returns if the widget can be used for the provided field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition that should be checked.
*
* @return bool
* TRUE if the widget can be used, FALSE otherwise.
*/
public static function isApplicable(FieldDefinitionInterface $field_definition);
}

View file

@ -0,0 +1,217 @@
<?php
/**
* @file
* Contains \Drupal\Core\Field\WidgetPluginManager.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Plugin type manager for field widgets.
*
* @ingroup field_widget
*/
class WidgetPluginManager extends DefaultPluginManager {
/**
* The field type manager to define field.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypeManager;
/**
* An array of widget options for each field type.
*
* @var array
*/
protected $widgetOptions;
/**
* Constructs a WidgetPluginManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The 'field type' plugin manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, FieldTypePluginManagerInterface $field_type_manager) {
parent::__construct('Plugin/Field/FieldWidget', $namespaces, $module_handler, 'Drupal\Core\Field\WidgetInterface', 'Drupal\Core\Field\Annotation\FieldWidget');
$this->setCacheBackend($cache_backend, 'field_widget_types_plugins');
$this->alterInfo('field_widget_info');
$this->fieldTypeManager = $field_type_manager;
}
/**
* Overrides PluginManagerBase::getInstance().
*
* @param array $options
* An array with the following key/value pairs:
* - field_definition: (FieldDefinitionInterface) The field definition.
* - form_mode: (string) The form mode.
* - prepare: (bool, optional) Whether default values should get merged in
* the 'configuration' array. Defaults to TRUE.
* - configuration: (array) the configuration for the widget. The
* following key value pairs are allowed, and are all optional if
* 'prepare' is TRUE:
* - type: (string) The widget to use. Defaults to the
* 'default_widget' for the field type. The default widget will also be
* used if the requested widget is not available.
* - settings: (array) Settings specific to the widget. Each setting
* defaults to the default value specified in the widget definition.
* - third_party_settings: (array) Settings provided by other extensions
* through hook_field_formatter_third_party_settings_form().
*
* @return \Drupal\Core\Field\WidgetInterface|null
* A Widget object or NULL when plugin is not found.
*/
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->getType();
// Fill in default configuration if needed.
if ($options['prepare']) {
$configuration = $this->prepareConfiguration($field_type, $configuration);
}
$plugin_id = $configuration['type'];
// Switch back to default widget if either:
// - the configuration does not specify a widget class
// - the field type is not allowed for the widget
// - the widget is not applicable to the field definition.
$definition = $this->getDefinition($configuration['type'], FALSE);
if (!isset($definition['class']) || !in_array($field_type, $definition['field_types']) || !$definition['class']::isApplicable($field_definition)) {
// Grab the default widget for the field type.
$field_type_definition = $this->fieldTypeManager->getDefinition($field_type);
if (empty($field_type_definition['default_widget'])) {
return NULL;
}
$plugin_id = $field_type_definition['default_widget'];
}
$configuration += array(
'field_definition' => $field_definition,
);
return $this->createInstance($plugin_id, $configuration);
}
/**
* {@inheritdoc}
*/
public function createInstance($plugin_id, array $configuration = array()) {
$plugin_definition = $this->getDefinition($plugin_id);
$plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
// If the plugin provides a factory method, pass the container to it.
if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
}
return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings']);
}
/**
* Merges default values for widget configuration.
*
* @param string $field_type
* The field type.
* @param array $configuration
* An array of widget configuration.
*
* @return array
* The display properties with defaults added.
*/
public function prepareConfiguration($field_type, array $configuration) {
// Fill in defaults for missing properties.
$configuration += array(
'settings' => array(),
'third_party_settings' => array(),
);
// 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'];
}
// Filter out unknown settings, and fill in defaults for missing settings.
$default_settings = $this->getDefaultSettings($configuration['type']);
$configuration['settings'] = array_intersect_key($configuration['settings'], $default_settings) + $default_settings;
return $configuration;
}
/**
* Returns an array of widget type options for a field type.
*
* @param string|null $field_type
* (optional) The name of a field type, or NULL to retrieve all widget
* options. Defaults to NULL.
*
* @return array
* If no field type is provided, returns a nested array of all widget types,
* keyed by field type human name.
*/
public function getOptions($field_type = NULL) {
if (!isset($this->widgetOptions)) {
$options = array();
$field_types = $this->fieldTypeManager->getDefinitions();
$widget_types = $this->getDefinitions();
uasort($widget_types, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
foreach ($widget_types as $name => $widget_type) {
foreach ($widget_type['field_types'] as $widget_field_type) {
// Check that the field type exists.
if (isset($field_types[$widget_field_type])) {
$options[$widget_field_type][$name] = $widget_type['label'];
}
}
}
$this->widgetOptions = $options;
}
if (isset($field_type)) {
return !empty($this->widgetOptions[$field_type]) ? $this->widgetOptions[$field_type] : array();
}
return $this->widgetOptions;
}
/**
* Returns the default settings of a field widget.
*
* @param string $type
* A field widget type name.
*
* @return array
* The widget type's default settings, as provided by the plugin
* definition, or an empty array if type or settings are undefined.
*/
public function getDefaultSettings($type) {
$plugin_definition = $this->getDefinition($type, FALSE);
if (!empty($plugin_definition['class'])) {
$plugin_class = DefaultFactory::getPluginClass($type, $plugin_definition);
return $plugin_class::defaultSettings();
}
return array();
}
}