Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
62
core/lib/Drupal/Core/Field/AllowedTagsXssTrait.php
Normal file
62
core/lib/Drupal/Core/Field/AllowedTagsXssTrait.php
Normal 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()) . '>';
|
||||
}
|
||||
|
||||
}
|
80
core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php
Normal file
80
core/lib/Drupal/Core/Field/Annotation/FieldFormatter.php
Normal 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;
|
||||
|
||||
}
|
99
core/lib/Drupal/Core/Field/Annotation/FieldType.php
Normal file
99
core/lib/Drupal/Core/Field/Annotation/FieldType.php
Normal 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;
|
||||
|
||||
}
|
85
core/lib/Drupal/Core/Field/Annotation/FieldWidget.php
Normal file
85
core/lib/Drupal/Core/Field/Annotation/FieldWidget.php
Normal 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;
|
||||
|
||||
}
|
732
core/lib/Drupal/Core/Field/BaseFieldDefinition.php
Normal file
732
core/lib/Drupal/Core/Field/BaseFieldDefinition.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
53
core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php
Normal file
53
core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php
Normal 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')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
25
core/lib/Drupal/Core/Field/ChangedFieldItemList.php
Normal file
25
core/lib/Drupal/Core/Field/ChangedFieldItemList.php
Normal 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');
|
||||
}
|
||||
}
|
248
core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php
Normal file
248
core/lib/Drupal/Core/Field/Entity/BaseFieldOverride.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
120
core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php
Normal file
120
core/lib/Drupal/Core/Field/EntityReferenceFieldItemList.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
574
core/lib/Drupal/Core/Field/FieldConfigBase.php
Normal file
574
core/lib/Drupal/Core/Field/FieldConfigBase.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
246
core/lib/Drupal/Core/Field/FieldConfigInterface.php
Normal file
246
core/lib/Drupal/Core/Field/FieldConfigInterface.php
Normal 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();
|
||||
|
||||
}
|
46
core/lib/Drupal/Core/Field/FieldConfigStorageBase.php
Normal file
46
core/lib/Drupal/Core/Field/FieldConfigStorageBase.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
225
core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
Normal file
225
core/lib/Drupal/Core/Field/FieldDefinitionInterface.php
Normal 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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
16
core/lib/Drupal/Core/Field/FieldException.php
Normal file
16
core/lib/Drupal/Core/Field/FieldException.php
Normal 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 {}
|
282
core/lib/Drupal/Core/Field/FieldItemBase.php
Normal file
282
core/lib/Drupal/Core/Field/FieldItemBase.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
424
core/lib/Drupal/Core/Field/FieldItemInterface.php
Normal file
424
core/lib/Drupal/Core/Field/FieldItemInterface.php
Normal 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);
|
||||
|
||||
}
|
409
core/lib/Drupal/Core/Field/FieldItemList.php
Normal file
409
core/lib/Drupal/Core/Field/FieldItemList.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
274
core/lib/Drupal/Core/Field/FieldItemListInterface.php
Normal file
274
core/lib/Drupal/Core/Field/FieldItemListInterface.php
Normal 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);
|
||||
|
||||
}
|
70
core/lib/Drupal/Core/Field/FieldModuleUninstallValidator.php
Normal file
70
core/lib/Drupal/Core/Field/FieldModuleUninstallValidator.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
63
core/lib/Drupal/Core/Field/FieldStorageDefinitionEvent.php
Normal file
63
core/lib/Drupal/Core/Field/FieldStorageDefinitionEvent.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
66
core/lib/Drupal/Core/Field/FieldStorageDefinitionEvents.php
Normal file
66
core/lib/Drupal/Core/Field/FieldStorageDefinitionEvents.php
Normal 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';
|
||||
|
||||
}
|
335
core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php
Normal file
335
core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php
Normal 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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
166
core/lib/Drupal/Core/Field/FieldTypePluginManager.php
Normal file
166
core/lib/Drupal/Core/Field/FieldTypePluginManager.php
Normal 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'];
|
||||
}
|
||||
|
||||
}
|
103
core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php
Normal file
103
core/lib/Drupal/Core/Field/FieldTypePluginManagerInterface.php
Normal 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);
|
||||
|
||||
}
|
160
core/lib/Drupal/Core/Field/FormatterBase.php
Normal file
160
core/lib/Drupal/Core/Field/FormatterBase.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
101
core/lib/Drupal/Core/Field/FormatterInterface.php
Normal file
101
core/lib/Drupal/Core/Field/FormatterInterface.php
Normal 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);
|
||||
|
||||
}
|
215
core/lib/Drupal/Core/Field/FormatterPluginManager.php
Normal file
215
core/lib/Drupal/Core/Field/FormatterPluginManager.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
26
core/lib/Drupal/Core/Field/Plugin/DataType/FieldItem.php
Normal file
26
core/lib/Drupal/Core/Field/Plugin/DataType/FieldItem.php
Normal 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 {
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
|
||||
}
|
|
@ -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()) : '';
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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)]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 === '';
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
122
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
Normal file
122
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 '€ '. 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
103
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
Normal file
103
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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'];
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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'];
|
||||
}
|
||||
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 -');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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() { }
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
125
core/lib/Drupal/Core/Field/PluginSettingsBase.php
Normal file
125
core/lib/Drupal/Core/Field/PluginSettingsBase.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
98
core/lib/Drupal/Core/Field/PluginSettingsInterface.php
Normal file
98
core/lib/Drupal/Core/Field/PluginSettingsInterface.php
Normal 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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
572
core/lib/Drupal/Core/Field/WidgetBase.php
Normal file
572
core/lib/Drupal/Core/Field/WidgetBase.php
Normal 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');
|
||||
}
|
||||
|
||||
}
|
112
core/lib/Drupal/Core/Field/WidgetBaseInterface.php
Normal file
112
core/lib/Drupal/Core/Field/WidgetBaseInterface.php
Normal 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);
|
||||
|
||||
}
|
166
core/lib/Drupal/Core/Field/WidgetInterface.php
Normal file
166
core/lib/Drupal/Core/Field/WidgetInterface.php
Normal 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);
|
||||
|
||||
}
|
217
core/lib/Drupal/Core/Field/WidgetPluginManager.php
Normal file
217
core/lib/Drupal/Core/Field/WidgetPluginManager.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue