diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php new file mode 100644 index 0000000..5ff9247 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php @@ -0,0 +1,107 @@ +field = $field; + $this->definition = $field->getDefinition() + \Drupal::typedData()->getDefinition($field->getType()); + } + + /** + * {@inheritdoc} + */ + public function getFieldName() { + return $this->field->getName(); + } + + /** + * {@inheritdoc} + */ + public function getFieldType() { + return $this->definition['field_type']; + } + + /** + * {@inheritdoc} + */ + public function getFieldSettings() { + return $this->definition['settings']; + } + + /** + * {@inheritdoc} + */ + public function getFieldSetting($setting_name) { + return $this->definition['settings'][$setting_name]; + } + + /** + * {@inheritdoc} + */ + public function getFieldPropertyNames() { + return array_keys($this->field->getPropertyDefinitions()); + } + + /** + * {@inheritdoc} + */ + public function isFieldTranslatable() { + return !empty($this->definition['translatable']); + } + + /** + * {@inheritdoc} + */ + public function getFieldLabel() { + return $this->definition['label']; + } + + /** + * {@inheritdoc} + */ + public function getFieldDescription() { + return $this->definition['description']; + } + + /** + * {@inheritdoc} + */ + public function getFieldCardinality() { + return isset($this->definition['cardinality']) ? $this->definition['cardinality'] : 1; + } + + /** + * {@inheritdoc} + */ + public function isFieldRequired() { + return !empty($this->definition['required']); + } + +} diff --git a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php index c2dfbdb..4db0c48 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php @@ -28,6 +28,13 @@ interface FieldInterface extends ListInterface, AccessibleInterface { /** + * Gets the field definition. + * + * @return \Drupal\Core\Entity\Field\FieldDefinitionInterface + */ + public function getFieldDefinition(); + + /** * Filters out empty field items and re-numbers the item deltas. */ public function filterEmptyValues(); diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php index 46a66b9..20b25cc 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php @@ -37,6 +37,13 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface } /** + * {@inheritdoc} + */ + public function getFieldDefinition() { + return $this->getParent()->getFieldDefinition(); + } + + /** * Overrides \Drupal\Core\TypedData\TypedData::setValue(). * * @param array|null $values diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php index 059a477..5d380a2 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php @@ -24,6 +24,13 @@ interface FieldItemInterface extends ComplexDataInterface { /** + * Gets the field definition. + * + * @return \Drupal\Core\Entity\Field\FieldDefinitionInterface + */ + public function getFieldDefinition(); + + /** * Magic method: Gets a property value. * * @param $property_name diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php index 4d53c18..4b8186d 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity\Field\Type; +use Drupal\Core\Entity\Field\FieldDefinition; use Drupal\Core\Entity\Field\FieldInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\TypedDataInterface; @@ -37,6 +38,13 @@ class Field extends ItemList implements FieldInterface { protected $list = array(); /** + * The field definition. + * + * @var \Drupal\Core\Entity\Field\FieldDefinitionInterface + */ + protected $fieldDefinition; + + /** * Overrides TypedData::__construct(). */ public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { @@ -51,6 +59,16 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface /** * {@inheritdoc} */ + public function getFieldDefinition() { + if (!isset($this->fieldDefinition)) { + $this->fieldDefinition = new FieldDefinition($this); + } + return $this->fieldDefinition; + } + + /** + * {@inheritdoc} + */ public function filterEmptyValues() { if (isset($this->list)) { $this->list = array_values(array_filter($this->list, function($item) { diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php index 504e427..81fc77f 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php @@ -43,6 +43,16 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { $definition['list class'] = $definition['list_class']; unset($definition['list_class']); + // Provide easy access to the field type without requiring consuming code + // to parse it from the full data type. + $definition['field_type'] = $plugin_id; + + // The distinction between 'settings' and 'instance_settings' is only + // meaningful at the field type plugin level. At the Typed data API level, + // merge them. + $definition['settings'] = $definition['instance_settings'] + $definition['settings']; + unset($definition['instance_settings']); + $this->derivatives[$plugin_id] = $definition; } return $this->derivatives; diff --git a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php index e139d0e..b09da96 100644 --- a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php +++ b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php @@ -41,7 +41,7 @@ public function access(Route $route, Request $request) { * Implements EntityFieldAccessCheckInterface::accessEditEntityField(). */ public function accessEditEntityField(EntityInterface $entity, $field_name) { - return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity); + return $entity->access('update'); // && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity); } /** @@ -64,13 +64,16 @@ protected function validateAndUpcastRequestAttributes(Request $request) { // Validate the field name and language. $field_name = $request->attributes->get('field_name'); - if (!$field_name || !field_info_instance($entity->entityType(), $field_name, $entity->bundle())) { + if (!$field_name) { throw new NotFoundHttpException(); } $langcode = $request->attributes->get('langcode'); if (!$langcode || (field_valid_language($langcode) !== $langcode)) { throw new NotFoundHttpException(); } + if (!($field_definition = $entity->getTranslation($langcode)->get($field_name)->getFieldDefinition())) { + throw new NotFoundHttpException(); + } } } diff --git a/core/modules/edit/lib/Drupal/edit/EditController.php b/core/modules/edit/lib/Drupal/edit/EditController.php index 9ac62b4..bfdd800 100644 --- a/core/modules/edit/lib/Drupal/edit/EditController.php +++ b/core/modules/edit/lib/Drupal/edit/EditController.php @@ -54,14 +54,17 @@ public function metadata(Request $request) { } // Validate the field name and language. - if (!$field_name || !($instance = field_info_instance($entity->entityType(), $field_name, $entity->bundle()))) { + if (!$field_name) { throw new NotFoundHttpException(); } if (!$langcode || (field_valid_language($langcode) !== $langcode)) { throw new NotFoundHttpException(); } + if (!($field_definition = $entity->getTranslation($langcode)->get($field_name)->getFieldDefinition())) { + throw new NotFoundHttpException(); + } - $metadata[$field] = $metadataGenerator->generate($entity, $instance, $langcode, $view_mode); + $metadata[$field] = $metadataGenerator->generate($entity, $field_definition, $langcode, $view_mode); } return new JsonResponse($metadata); diff --git a/core/modules/edit/lib/Drupal/edit/EditPluginInterface.php b/core/modules/edit/lib/Drupal/edit/EditPluginInterface.php index a568278..1dcc642 100644 --- a/core/modules/edit/lib/Drupal/edit/EditPluginInterface.php +++ b/core/modules/edit/lib/Drupal/edit/EditPluginInterface.php @@ -8,7 +8,7 @@ namespace Drupal\edit; use Drupal\Component\Plugin\PluginInspectionInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines an interface for in-place editors (Create.js PropertyEditor widgets). @@ -21,15 +21,15 @@ /** * Checks whether this editor is compatible with a given field instance. * - * @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance - * The field instance of the field being edited. + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition of the field being edited. * @param array $items * The field's item values. * * @return bool * TRUE if it is compatible, FALSE otherwise. */ - public function isCompatible(FieldInstance $instance, array $items); + public function isCompatible(FieldDefinitionInterface $field_definition, array $items); /** * Generates metadata that is needed specifically for this editor. @@ -37,8 +37,8 @@ public function isCompatible(FieldInstance $instance, array $items); * Will only be called by \Drupal\edit\MetadataGeneratorInterface::generate() * when the passed in field instance & item values will use this editor. * - * @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance - * The field instance of the field being edited. + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition of the field being edited. * @param array $items * The field's item values. * @@ -46,7 +46,7 @@ public function isCompatible(FieldInstance $instance, array $items); * A keyed array with metadata. Each key should be prefixed with the plugin * ID of the editor. */ - public function getMetadata(FieldInstance $instance, array $items); + public function getMetadata(FieldDefinitionInterface $field_definition, array $items); /** * Returns the attachments for this editor. diff --git a/core/modules/edit/lib/Drupal/edit/EditorBase.php b/core/modules/edit/lib/Drupal/edit/EditorBase.php index 39abaee..10b2f02 100644 --- a/core/modules/edit/lib/Drupal/edit/EditorBase.php +++ b/core/modules/edit/lib/Drupal/edit/EditorBase.php @@ -9,7 +9,7 @@ use Drupal\Component\Plugin\PluginBase; use Drupal\edit\EditPluginInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines a base editor implementation. @@ -17,9 +17,9 @@ abstract class EditorBase extends PluginBase implements EditPluginInterface { /** - * Implements \Drupal\edit\EditPluginInterface::getMetadata(). + * {@inheritdoc} */ - function getMetadata(FieldInstance $instance, array $items) { + function getMetadata(FieldDefinitionInterface $field_definition, array $items) { return array(); } diff --git a/core/modules/edit/lib/Drupal/edit/EditorSelector.php b/core/modules/edit/lib/Drupal/edit/EditorSelector.php index 232a52f..2636f38 100644 --- a/core/modules/edit/lib/Drupal/edit/EditorSelector.php +++ b/core/modules/edit/lib/Drupal/edit/EditorSelector.php @@ -9,7 +9,7 @@ use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Component\Utility\NestedArray; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Selects an in-place editor (an Editor plugin) for a field. @@ -43,7 +43,7 @@ public function __construct(PluginManagerInterface $editor_manager) { /** * {@inheritdoc} */ - public function getEditor($formatter_type, FieldInstance $instance, array $items) { + public function getEditor($formatter_type, FieldDefinitionInterface $field_definition, array $items) { // Build a static cache of the editors that have registered themselves as // alternatives to a certain editor. if (!isset($this->alternatives)) { @@ -80,7 +80,7 @@ public function getEditor($formatter_type, FieldInstance $instance, array $items // Make a choice. foreach ($editor_choices as $editor_id) { $editor = $this->editorManager->createInstance($editor_id); - if ($editor->isCompatible($instance, $items)) { + if ($editor->isCompatible($field_definition, $items)) { return $editor_id; } } diff --git a/core/modules/edit/lib/Drupal/edit/EditorSelectorInterface.php b/core/modules/edit/lib/Drupal/edit/EditorSelectorInterface.php index 990c207..d36083a 100644 --- a/core/modules/edit/lib/Drupal/edit/EditorSelectorInterface.php +++ b/core/modules/edit/lib/Drupal/edit/EditorSelectorInterface.php @@ -7,7 +7,7 @@ namespace Drupal\edit; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Interface for selecting an in-place editor (an Editor plugin) for a field. @@ -19,15 +19,15 @@ * * @param string $formatter_type * The field's formatter type name. - * @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance - * The field's instance info. + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition. * @param array $items * The field's item values. * * @return string|NULL * The editor to use, or NULL to not enable in-place editing. */ - public function getEditor($formatter_type, FieldInstance $instance, array $items); + public function getEditor($formatter_type, FieldDefinitionInterface $instance, array $items); /** * Returns the attachments for all editors. diff --git a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php index 8930ace..f1fa703 100644 --- a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php +++ b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php @@ -9,9 +9,9 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Component\Plugin\PluginManagerInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; use Drupal\edit\Access\EditEntityFieldAccessCheckInterface; - +use Drupal\field\FieldInstanceInterface; /** * Generates in-place editing metadata for an entity field. @@ -56,10 +56,10 @@ public function __construct(EditEntityFieldAccessCheckInterface $access_checker, } /** - * Implements \Drupal\edit\MetadataGeneratorInterface::generate(). + * {@inheritdoc} */ - public function generate(EntityInterface $entity, FieldInstance $instance, $langcode, $view_mode) { - $field_name = $instance['field_name']; + public function generate(EntityInterface $entity, FieldDefinitionInterface $field_definition, $langcode, $view_mode) { + $field_name = $field_definition->getFieldName(); // Early-return if user does not have access. $access = $this->accessChecker->accessEditEntityField($entity, $field_name); @@ -68,15 +68,21 @@ public function generate(EntityInterface $entity, FieldInstance $instance, $lang } // Early-return if no editor is available. - $formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($instance['field_name'])->getPluginId(); + if ($field_definition instanceof FieldInstanceInterface) { + $formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($field_name)->getPluginId(); + } + else { + $field_type_info = field_info_field_types($field_definition->getFieldType()); + $formatter_id = $field_type_info['default_formatter']; + } $items = $entity->getTranslation($langcode, FALSE)->get($field_name)->getValue(); - $editor_id = $this->editorSelector->getEditor($formatter_id, $instance, $items); + $editor_id = $this->editorSelector->getEditor($formatter_id, $field_definition, $items); if (!isset($editor_id)) { return array('access' => FALSE); } // Gather metadata, allow the editor to add additional metadata of its own. - $label = $instance['label']; + $label = $field_definition->getFieldLabel(); $editor = $this->editorManager->createInstance($editor_id); $metadata = array( 'label' => check_plain($label), @@ -84,7 +90,7 @@ public function generate(EntityInterface $entity, FieldInstance $instance, $lang 'editor' => $editor_id, 'aria' => t('Entity @type @id, field @field', array('@type' => $entity->entityType(), '@id' => $entity->id(), '@field' => $label)), ); - $custom_metadata = $editor->getMetadata($instance, $items); + $custom_metadata = $editor->getMetadata($field_definition, $items); if (count($custom_metadata)) { $metadata['custom'] = $custom_metadata; } diff --git a/core/modules/edit/lib/Drupal/edit/MetadataGeneratorInterface.php b/core/modules/edit/lib/Drupal/edit/MetadataGeneratorInterface.php index 16db770..89f1feb 100644 --- a/core/modules/edit/lib/Drupal/edit/MetadataGeneratorInterface.php +++ b/core/modules/edit/lib/Drupal/edit/MetadataGeneratorInterface.php @@ -8,7 +8,7 @@ namespace Drupal\edit; use Drupal\Core\Entity\EntityInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Interface for generating in-place editing metadata for an entity field. @@ -20,8 +20,8 @@ * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity being edited. - * @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance - * The field instance of the field being edited. + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition of the field being edited. * @param string $langcode * The name of the language for which the field is being edited. * @param string $view_mode @@ -34,6 +34,6 @@ * - aria: the ARIA label. * - custom: (optional) any additional metadata that the editor provides. */ - public function generate(EntityInterface $entity, FieldInstance $instance, $langcode, $view_mode); + public function generate(EntityInterface $entity, FieldDefinitionInterface $field_definition, $langcode, $view_mode); } diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/DirectEditor.php b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/DirectEditor.php index 39176d1..6a8f16b 100644 --- a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/DirectEditor.php +++ b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/DirectEditor.php @@ -9,7 +9,7 @@ use Drupal\edit\EditorBase; use Drupal\edit\Annotation\InPlaceEditor; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines the direct editor. @@ -21,20 +21,18 @@ class DirectEditor extends EditorBase { /** - * Implements \Drupal\edit\EditPluginInterface::isCompatible(). + * {@inheritdoc} * * @todo The processed text logic is too coupled to text fields. Figure out * how to generalize to other textual field types. */ - function isCompatible(FieldInstance $instance, array $items) { - $field = field_info_field($instance['field_name']); - + function isCompatible(FieldDefinitionInterface $field_definition, array $items) { // This editor is incompatible with multivalued fields. - if ($field['cardinality'] != 1) { + if ($field_definition->getFieldCardinality() != 1) { return FALSE; } // This editor is incompatible with processed ("rich") text fields. - elseif (!empty($instance['settings']['text_processing'])) { + elseif ($field_definition->getFieldSetting('text_processing')) { return FALSE; } else { diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/FormEditor.php b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/FormEditor.php index ec8d406..4c71db2 100644 --- a/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/FormEditor.php +++ b/core/modules/edit/lib/Drupal/edit/Plugin/InPlaceEditor/FormEditor.php @@ -9,7 +9,7 @@ use Drupal\edit\EditorBase; use Drupal\edit\Annotation\InPlaceEditor; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines the form editor. @@ -21,9 +21,9 @@ class FormEditor extends EditorBase { /** - * Implements \Drupal\edit\EditPluginInterface::isCompatible(). + * {@inheritdoc} */ - function isCompatible(FieldInstance $instance, array $items) { + function isCompatible(FieldDefinitionInterface $field_definition, array $items) { return TRUE; } diff --git a/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/InPlaceEditor/WysiwygEditor.php b/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/InPlaceEditor/WysiwygEditor.php index 59fe895..61be894 100644 --- a/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/InPlaceEditor/WysiwygEditor.php +++ b/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/InPlaceEditor/WysiwygEditor.php @@ -9,7 +9,7 @@ use Drupal\edit\EditorBase; use Drupal\edit\Annotation\InPlaceEditor; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines the wysiwyg editor. @@ -22,19 +22,17 @@ class WysiwygEditor extends EditorBase { /** - * Implements \Drupal\edit\EditPluginInterface::isCompatible(). + * {@inheritdoc} */ - function isCompatible(FieldInstance $instance, array $items) { - $field = field_info_field($instance['field_name']); - + function isCompatible(FieldDefinitionInterface $field_definition, array $items) { // This editor is incompatible with multivalued fields. - if ($field['cardinality'] != 1) { + if ($field_definition->getFieldCardinality() != 1) { return FALSE; } // This editor is compatible with processed ("rich") text fields; but only // if there is a currently active text format and that text format is the // 'full_html' text format. - elseif (!empty($instance['settings']['text_processing'])) { + elseif ($field_definition->getFieldSetting('text_processing')) { $format_id = $items[0]['format']; if (isset($format_id) && $format_id === 'full_html') { return TRUE; @@ -44,9 +42,9 @@ function isCompatible(FieldInstance $instance, array $items) { } /** - * Implements \Drupal\edit\EditPluginInterface::getMetadata(). + * {@inheritdoc} */ - function getMetadata(FieldInstance $instance, array $items) { + function getMetadata(FieldDefinitionInterface $field_definition, array $items) { $format_id = $items[0]['format']; $metadata['format'] = $format_id; return $metadata; diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/InPlaceEditor/Editor.php b/core/modules/editor/lib/Drupal/editor/Plugin/InPlaceEditor/Editor.php index 7c281c0..1a4981a 100644 --- a/core/modules/editor/lib/Drupal/editor/Plugin/InPlaceEditor/Editor.php +++ b/core/modules/editor/lib/Drupal/editor/Plugin/InPlaceEditor/Editor.php @@ -11,7 +11,7 @@ use Drupal\edit\Annotation\InPlaceEditor; use Drupal\Core\Annotation\Translation; use Drupal\edit\EditPluginInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; /** * Defines the formatted text editor. @@ -24,19 +24,17 @@ class Editor extends PluginBase implements EditPluginInterface { /** - * Implements \Drupal\edit\Plugin\EditPluginInterface::isCompatible(). + * {@inheritdoc} */ - function isCompatible(FieldInstance $instance, array $items) { - $field = field_info_field($instance['field_name']); - + function isCompatible(FieldDefinitionInterface $field_definition, array $items) { // This editor is incompatible with multivalued fields. - if ($field['cardinality'] != 1) { + if ($field_definition->getFieldCardinality() != 1) { return FALSE; } // This editor is compatible with processed ("rich") text fields; but only // if there is a currently active text format, that text format has an // associated editor and that editor supports inline editing. - elseif (!empty($instance['settings']['text_processing'])) { + elseif ($field_definition->getFieldSetting('text_processing')) { $format_id = $items[0]['format']; if (isset($format_id) && $editor = editor_load($format_id)) { $definition = \Drupal::service('plugin.manager.editor')->getDefinition($editor->editor); @@ -50,9 +48,9 @@ function isCompatible(FieldInstance $instance, array $items) { } /** - * Implements \Drupal\edit\Plugin\EditPluginInterface::getMetadata(). + * {@inheritdoc} */ - function getMetadata(FieldInstance $instance, array $items) { + function getMetadata(FieldDefinitionInterface $field_definition, array $items) { $format_id = $items[0]['format']; $metadata['format'] = $format_id; $metadata['formatHasTransformations'] = $this->textFormatHasTransformationFilters($format_id); diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 19bf6ed..0fa26dc 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -173,6 +173,9 @@ function field_theme() { 'field' => array( 'render element' => 'element', ), + 'field__title' => array( + 'base hook' => 'field', + ), 'field_multiple_value_form' => array( 'render element' => 'element', ), @@ -1099,6 +1102,13 @@ function theme_field($variables) { } /** + * @todo Document. + */ +function theme_field__title($variables) { + return '' . drupal_render($variables['items']) . ''; +} + +/** * Assembles a partial entity structure with initial IDs. * * @param stdClass $ids diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php index 3134160..1e356b3 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php @@ -24,6 +24,13 @@ class ConfigField extends Field implements ConfigFieldInterface { protected $instance; /** + * @todo Document. + * + * @var array + */ + protected $instances; + + /** * {@inheritdoc} */ public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { @@ -37,9 +44,14 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface * {@inheritdoc} */ public function getInstance() { - if (!isset($this->instance) && $parent = $this->getParent()) { - $instances = FieldAPI::fieldInfo()->getBundleInstances($parent->entityType(), $parent->bundle()); - $this->instance = $instances[$this->getName()]; + if (!isset($this->instance)) { + if (!isset($this->instances) && $parent = $this->getParent()) { + $this->instances = FieldAPI::fieldInfo()->getBundleInstances($parent->entityType(), $parent->bundle()); + } + $field_name = $this->getName(); + if (isset($this->instances[$field_name])) { + $this->instance = $this->instances[$field_name]; + } } return $this->instance; } @@ -47,18 +59,25 @@ public function getInstance() { /** * {@inheritdoc} */ + public function getFieldDefinition() { + return $this->getInstance() ?: parent::getFieldDefinition(); + } + + /** + * {@inheritdoc} + */ public function getConstraints() { $constraints = array(); // 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->getInstance()->getField()->cardinality; + $cardinality = $this->getFieldDefinition()->getFieldCardinality(); if ($cardinality != FIELD_CARDINALITY_UNLIMITED) { $constraints[] = \Drupal::typedData() ->getValidationConstraintManager() ->create('Count', array( 'max' => $cardinality, - 'maxMessage' => t('%name: this field cannot hold more than @count values.', array('%name' => $this->getInstance()->label, '@count' => $cardinality)), + 'maxMessage' => t('%name: this field cannot hold more than @count values.', array('%name' => $this->getFieldDefinition()->getFieldLabel(), '@count' => $cardinality)), )); } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php index fa780a5..b6e70f7 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php @@ -134,7 +134,7 @@ protected function checkFieldAccess($op, $entity) { return field_access($op, $field, $entity->entityType(), $entity); } else { - return FALSE; + return TRUE; } } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php index 39deee9..3235a6d 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php @@ -12,7 +12,7 @@ use Drupal\Core\Plugin\Discovery\CacheDecorator; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; use Drupal\Core\Plugin\Discovery\AlterDecorator; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\Core\Entity\Field\FieldInterface; /** * Plugin type manager for field formatters. @@ -175,4 +175,31 @@ public function getDefaultSettings($type) { return isset($info['settings']) ? $info['settings'] : array(); } + /** + * @todo Document. + */ + public function viewBaseField(FieldInterface $field) { + $options = array( + 'field_definition' => $field->getFieldDefinition(), + 'view_mode' => 'default', + 'configuration' => array( + 'label' => 'hidden', + ), + ); + $formatter = $this->getInstance($options); + + $entity = $field->getParent()->getBCEntity(); + $entity_id = $entity->id(); + $langcode = $entity->language()->langcode; + $items = $field->getValue(); + + $items_multi = array($entity_id => $items); + $formatter->prepareView(array($entity_id => $entity), $langcode, $items_multi); + $items = $items_multi[$entity_id]; + + $result = $formatter->view($entity, $langcode, $items); + $field_name = $field->getName(); + return isset($result[$field_name]) ? $result[$field_name] : array(); + } + } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php index 654cbbe..e0c2432 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php @@ -449,7 +449,7 @@ protected function checkFieldAccess($op, $entity) { return field_access($op, $field, $entity->entityType(), $entity); } else { - return FALSE; + return TRUE; } } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php index b8213ca..dfca5eb 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php @@ -12,6 +12,7 @@ use Drupal\Core\Plugin\Discovery\CacheDecorator; use Drupal\Core\Plugin\Discovery\AlterDecorator; use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery; +use Drupal\Core\Entity\Field\FieldInterface; /** * Plugin type manager for field widgets. @@ -176,4 +177,48 @@ public function getDefaultSettings($type) { return isset($info['settings']) ? $info['settings'] : array(); } + /** + * @todo Document. + */ + public function baseFieldForm(FieldInterface $field, array &$form, array &$form_state, $langcode) { + $options = array( + 'field_definition' => $field->getFieldDefinition(), + 'form_mode' => 'default', + 'configuration' => array(), + ); + if (($field_data_definition = $field->getDefinition()) && isset($field_data_definition['default_widget'])) { + $options['configuration']['type'] = $field_data_definition['default_widget']; + } + $widget = $this->getInstance($options); + + $entity = $field->getParent()->getBCEntity(); + $items = $field->getValue(); + + $form += array('#parents' => array()); + $result = $widget->form($entity, $langcode, $items, $form, $form_state); + $field_name = $field->getName(); + return isset($result[$field_name]) ? $result[$field_name] : array(); + } + + /** + * @todo Document. + */ + public function baseFieldExtractFormValues(FieldInterface $field, array &$form, array &$form_state, $langcode) { + $options = array( + 'field_definition' => $field->getFieldDefinition(), + 'form_mode' => 'default', + 'configuration' => array(), + ); + if (($field_data_definition = $field->getDefinition()) && isset($field_data_definition['default_widget'])) { + $options['configuration']['type'] = $field_data_definition['default_widget']; + } + $widget = $this->getInstance($options); + + $entity = $field->getParent()->getBCEntity(); + $items = $field->getValue(); + + $widget->extractFormValues($entity, $langcode, $items, $form, $form_state); + $field->setValue($items); + } + } diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index e55e056..3b8be33 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -101,14 +101,8 @@ public function form(array $form, array &$form_state) { $node_type = node_type_load($node->type); if ($node_type->has_title) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => check_plain($node_type->title_label), - '#required' => TRUE, - '#default_value' => $node->title, - '#maxlength' => 255, - '#weight' => -5, - ); + $form['title'] = \Drupal::service('plugin.manager.field.widget')->baseFieldForm($node->getNGEntity()->title, $form, $form_state, $this->getFormLangcode($form_state)); + $form['title']['#weight'] = -5; } $language_configuration = module_invoke('language', 'get_default_configuration', 'node', $node->type); @@ -322,6 +316,15 @@ protected function actions(array $form, array &$form_state) { } /** + * {@inheritdoc} + */ + public function buildEntity(array $form, array &$form_state) { + $node = parent::buildEntity($form, $form_state); + \Drupal::service('plugin.manager.field.widget')->baseFieldExtractFormValues($node->getNGEntity()->title, $form, $form_state, $this->getFormLangcode($form_state)); + return $node; + } + + /** * Overrides Drupal\Core\Entity\EntityFormController::validate(). */ public function validate(array $form, array &$form_state) { diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php index 524036b..91b4e35 100644 --- a/core/modules/node/lib/Drupal/node/NodeStorageController.php +++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php @@ -154,7 +154,9 @@ public function baseFieldDefinitions() { $properties['title'] = array( 'label' => t('Title'), 'description' => t('The title of this node, always treated as non-markup plain text.'), - 'type' => 'string_field', + 'type' => 'field_item:text', + 'required' => TRUE, + 'default_widget' => 'node_title', ); $properties['uid'] = array( 'label' => t('User ID'), diff --git a/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php b/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php new file mode 100644 index 0000000..b5710f2 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php @@ -0,0 +1,79 @@ +fieldDefinition->getFieldName(); + + // @todo Make EntityManager::getFieldDefinitions() allow for per-bundle + // definitions of base fields, so that here, we could just call + // $this->fieldDefinition->getFieldLabel() instead. + if ($entity->entityType() == 'node' && $field_name == 'title') { + $node_type = node_type_load($entity->bundle()); + $label = $node_type->title_label; + } + else { + $label = $this->fieldDefinition->getFieldLabel(); + } + + $addition[$field_name] = array( + '#type' => 'textfield', + '#title' => check_plain($label), + '#required' => $this->fieldDefinition->isFieldRequired(), + '#default_value' => isset($items[0]['value']) ? $items[0]['value'] : '', + '#maxlength' => $this->fieldDefinition->getFieldSetting('max_length'), + ); + return $addition; + } + + /** + * {@inheritdoc} + */ + public function extractFormValues(EntityInterface $entity, $langcode, array &$items, array $form, array &$form_state) { + $field_name = $this->fieldDefinition->getFieldName(); + + // Extract the values from $form_state['values']. + $path = array_merge($form['#parents'], array($field_name)); + $key_exists = NULL; + $value = NestedArray::getValue($form_state['values'], $path, $key_exists); + + if ($key_exists) { + $items = array(array('value' => $value)); + } + } + + /** + * {@inheritdoc} + */ + public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) { + return array(); + } +} diff --git a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php index 31d515f..6ee7d48 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php @@ -14,7 +14,7 @@ */ class NodeConditionTest extends DrupalUnitTestBase { - public static $modules = array('system', 'node', 'field'); + public static $modules = array('system', 'node', 'field', 'text'); public static function getInfo() { return array( diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php index e03e4b0..0deff22 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php @@ -59,7 +59,7 @@ function testNodeTitle() { $this->assertEqual(current($this->xpath($xpath)), $node->label(), 'Node breadcrumb is equal to node title.', 'Node'); // Test node title in comment preview. - $this->assertEqual(current($this->xpath('//article[@id=:id]/h2/a', array(':id' => 'node-' . $node->nid))), $node->label(), 'Node preview title is equal to node title.', 'Node'); + $this->assertEqual(current($this->xpath('//article[@id=:id]/h2/a/span', array(':id' => 'node-' . $node->nid))), $node->label(), 'Node preview title is equal to node title.', 'Node'); // Test node title is clickable on teaser list (/node). $this->drupalGet('node'); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index d01b8ae..db21dcc 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -747,7 +747,7 @@ function template_preprocess_node(&$variables) { $uri = $node->uri(); $variables['node_url'] = url($uri['path'], $uri['options']); - $variables['label'] = check_plain($node->label()); + $variables['label'] = Drupal::service('plugin.manager.field.formatter')->viewBaseField($node->getNGEntity()->title); $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node); // Helpful $content variable for templates. diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 526ef66..bb29703 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -315,7 +315,7 @@ function rdf_preprocess_node(&$variables) { $element = array( '#tag' => 'meta', '#attributes' => array( - 'content' => $variables['label'], + 'content' => $variables['node']->label(), 'about' => $variables['node_url'], ), ); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 8e295b3..1ca7dbe 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -2048,7 +2048,7 @@ protected function assertNoLinkByHref($href, $message = '', $group = 'Other') { */ protected function clickLink($label, $index = 0) { $url_before = $this->getUrl(); - $urls = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label)); + $urls = $this->xpath('//a[normalize-space()=:label]', array(':label' => $label)); if (isset($urls[$index])) { $url_target = $this->getAbsoluteUrl($urls[$index]['href']); diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php index b04e2d8..cb8ec55 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php @@ -17,7 +17,7 @@ */ class ContextPluginTest extends DrupalUnitTestBase { - public static $modules = array('system', 'user', 'node'); + public static $modules = array('system', 'user', 'node', 'text'); public static function getInfo() { return array( diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php index 26326fc..1c79cf7 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItem.php @@ -63,6 +63,27 @@ public static function schema(Field $field) { /** * {@inheritdoc} */ + public function getConstraints() { + $constraint_manager = \Drupal::typedData()->getValidationConstraintManager(); + $constraints = parent::getConstraints(); + + if ($max_length = $this->getFieldDefinition()->getFieldSetting('max_length')) { + $constraints[] = $constraint_manager->create('ComplexData', array( + 'value' => array( + 'Length' => array( + 'max' => $max_length, + 'maxMessage' => t('%name: the text may not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getFieldLabel(), '@max' => $max_length)), + ) + ), + )); + } + + return $constraints; + } + + /** + * {@inheritdoc} + */ public function settingsForm(array $form, array &$form_state) { $element = array(); $field = $this->getInstance()->getField(); diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php index 17f2ba3..e16f961 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php @@ -70,38 +70,17 @@ public function isEmpty() { /** * {@inheritdoc} */ - public function getConstraints() { - $constraint_manager = \Drupal::typedData()->getValidationConstraintManager(); - $constraints = parent::getConstraints(); - - if (!empty($this->getInstance()->getField()->settings['max_length'])) { - $max_length = $this->getInstance()->getField()->settings['max_length']; - $constraints[] = $constraint_manager->create('ComplexData', array( - 'value' => array( - 'Length' => array( - 'max' => $max_length, - 'maxMessage' => t('%name: the text may not be longer than @max characters.', array('%name' => $this->getInstance()->label, '@max' => $max_length)), - ) - ), - )); - } - - return $constraints; - } - - /** - * {@inheritdoc} - */ public function prepareCache() { // Where possible, generate the sanitized version of each field early so // that it is cached in the field cache. This avoids the need to look up the // field in the filter cache separately. - if (!$this->getInstance()->settings['text_processing'] || filter_format_allowcache($this->get('format')->getValue())) { + $text_processing = $this->getFieldDefinition()->getFieldSetting('text_processing'); + if (!$text_processing || filter_format_allowcache($this->get('format')->getValue())) { $itemBC = $this->getValue(); $langcode = $this->getParent()->getParent()->language()->langcode; - $this->set('safe_value', text_sanitize($this->getInstance()->settings['text_processing'], $langcode, $itemBC, 'value')); + $this->set('safe_value', text_sanitize($text_processing, $langcode, $itemBC, 'value')); if ($this->getType() == 'field_item:text_with_summary') { - $this->set('safe_summary', text_sanitize($this->getInstance()->settings['text_processing'], $langcode, $itemBC, 'summary')); + $this->set('safe_summary', text_sanitize($text_processing, $langcode, $itemBC, 'summary')); } } } diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php index 4d9030d..7aecf67 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextWithSummaryItem.php @@ -106,28 +106,6 @@ public function isEmpty() { /** * {@inheritdoc} */ - public function getConstraints() { - $constraint_manager = \Drupal::typedData()->getValidationConstraintManager(); - $constraints = parent::getConstraints(); - - if (!empty($this->getInstance()->getField()->settings['max_length'])) { - $max_length = $this->getInstance()->getField()->settings['max_length']; - $constraints[] = $constraint_manager->create('ComplexData', array( - 'summary' => array( - 'Length' => array( - 'max' => $max_length, - 'maxMessage' => t('%name: the summary may not be longer than @max characters.', array('%name' => $this->getInstance()->label, '@max' => $max_length)), - ) - ), - )); - } - - return $constraints; - } - - /** - * {@inheritdoc} - */ public function instanceSettingsForm(array $form, array &$form_state) { $element = array();