Commit 15fded0c authored by catch's avatar catch
Browse files

Issue #1988612 by effulgentsia, yched, Wim Leers, Berdir, Pancho: Apply...

Issue #1988612 by effulgentsia, yched, Wim Leers, Berdir, Pancho: Apply formatters and widgets to rendered entity base fields, starting with node.title.
parent b20755f4
...@@ -708,6 +708,14 @@ function entity_get_render_display(EntityInterface $entity, $view_mode) { ...@@ -708,6 +708,14 @@ function entity_get_render_display(EntityInterface $entity, $view_mode) {
$display = entity_get_display($entity_type, $bundle, $render_view_mode); $display = entity_get_display($entity_type, $bundle, $render_view_mode);
$display->originalMode = $view_mode; $display->originalMode = $view_mode;
// Let modules alter the display.
$display_context = array(
'entity_type' => $entity_type,
'bundle' => $bundle,
'view_mode' => $view_mode,
);
drupal_alter('entity_display', $display, $display_context);
return $display; return $display;
} }
...@@ -808,6 +816,14 @@ function entity_get_render_form_display(EntityInterface $entity, $form_mode) { ...@@ -808,6 +816,14 @@ function entity_get_render_form_display(EntityInterface $entity, $form_mode) {
$form_display = entity_get_form_display($entity_type, $bundle, $render_form_mode); $form_display = entity_get_form_display($entity_type, $bundle, $render_form_mode);
$form_display->originalMode = $form_mode; $form_display->originalMode = $form_mode;
// Let modules alter the form display.
$form_display_context = array(
'entity_type' => $entity_type,
'bundle' => $bundle,
'form_mode' => $form_mode,
);
drupal_alter('entity_form_display', $form_display, $form_display_context);
return $form_display; return $form_display;
} }
......
...@@ -125,15 +125,6 @@ protected function init(array &$form_state) { ...@@ -125,15 +125,6 @@ protected function init(array &$form_state) {
$this->prepareEntity(); $this->prepareEntity();
$form_display = entity_get_render_form_display($this->entity, $this->getOperation()); $form_display = entity_get_render_form_display($this->entity, $this->getOperation());
// Let modules alter the form display.
$form_display_context = array(
'entity_type' => $this->entity->entityType(),
'bundle' => $this->entity->bundle(),
'form_mode' => $this->getOperation(),
);
$this->moduleHandler->alter('entity_form_display', $form_display, $form_display_context);
$this->setFormDisplay($form_display, $form_state); $this->setFormDisplay($form_display, $form_state);
// Invoke the prepare form hooks. // Invoke the prepare form hooks.
......
...@@ -208,20 +208,9 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la ...@@ -208,20 +208,9 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
// Store entities for rendering by view_mode. // Store entities for rendering by view_mode.
$view_modes[$entity_view_mode][$entity->id()] = $entity; $view_modes[$entity_view_mode][$entity->id()] = $entity;
// Load the corresponding display settings if not stored yet. // Get the corresponding display settings.
if (!isset($displays[$entity_view_mode][$bundle])) { if (!isset($displays[$entity_view_mode][$bundle])) {
// Get the display object for this bundle and view mode. $displays[$entity_view_mode][$bundle] = entity_get_render_display($entity, $entity_view_mode);
$display = entity_get_render_display($entity, $entity_view_mode);
// Let modules alter the display.
$display_context = array(
'entity_type' => $this->entityType,
'bundle' => $bundle,
'view_mode' => $entity_view_mode,
);
drupal_alter('entity_display', $display, $display_context);
$displays[$entity_view_mode][$bundle] = $display;
} }
} }
......
...@@ -328,9 +328,24 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f ...@@ -328,9 +328,24 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f
$field_state = field_form_get_state($form['#parents'], $field_name, $form_state); $field_state = field_form_get_state($form['#parents'], $field_name, $form_state);
if (!empty($field_state['constraint_violations'])) { if (!empty($field_state['constraint_violations'])) {
$form_builder = \Drupal::formBuilder();
// Locate the correct element in the the form. // Locate the correct element in the the form.
$element = NestedArray::getValue($form_state['complete_form'], $field_state['array_parents']); $element = NestedArray::getValue($form_state['complete_form'], $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://drupal.org/node/2070429.
$element_path = implode('][', $element['#parents']);
if ($reported_errors = $form_builder->getErrors($form_state)) {
foreach (array_keys($reported_errors) as $error_path) {
if (strpos($error_path, $element_path) === 0) {
return;
}
}
}
// Only set errors if the element is accessible. // Only set errors if the element is accessible.
if (!isset($element['#access']) || $element['#access']) { if (!isset($element['#access']) || $element['#access']) {
$definition = $this->getPluginDefinition(); $definition = $this->getPluginDefinition();
...@@ -341,16 +356,22 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f ...@@ -341,16 +356,22 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f
// Separate violations by delta. // Separate violations by delta.
$property_path = explode('.', $violation->getPropertyPath()); $property_path = explode('.', $violation->getPropertyPath());
$delta = array_shift($property_path); $delta = array_shift($property_path);
$violation->arrayPropertyPath = $property_path; // Violations at the ItemList level are not associated to any delta,
// we file them under $delta NULL.
$delta = is_numeric($delta) ? $delta : NULL;
$violations_by_delta[$delta][] = $violation; $violations_by_delta[$delta][] = $violation;
$violation->arrayPropertyPath = $property_path;
} }
foreach ($violations_by_delta as $delta => $delta_violations) { foreach ($violations_by_delta as $delta => $delta_violations) {
// For a multiple-value widget, pass all errors to the main widget. // Pass violations to the main element:
// For single-value widgets, pass errors by delta. // - if this is a multiple-value widget,
if ($is_multiple) { // - or if the violations are at the ItemList level.
if ($is_multiple || $delta === NULL) {
$delta_element = $element; $delta_element = $element;
} }
// Otherwise, pass errors by delta to the corresponding sub-element.
else { else {
$original_delta = $field_state['original_deltas'][$delta]; $original_delta = $field_state['original_deltas'][$delta];
$delta_element = $element[$original_delta]; $delta_element = $element[$original_delta];
...@@ -359,7 +380,7 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f ...@@ -359,7 +380,7 @@ public function flagErrors(FieldItemListInterface $items, array $form, array &$f
// @todo: Pass $violation->arrayPropertyPath as property path. // @todo: Pass $violation->arrayPropertyPath as property path.
$error_element = $this->errorElement($delta_element, $violation, $form, $form_state); $error_element = $this->errorElement($delta_element, $violation, $form, $form_state);
if ($error_element !== FALSE) { if ($error_element !== FALSE) {
form_error($error_element, $form_state, $violation->getMessage()); $form_builder->setError($error_element, $form_state, $violation->getMessage());
} }
} }
} }
......
...@@ -20,6 +20,9 @@ class AllowedValuesConstraintValidator extends ChoiceValidator { ...@@ -20,6 +20,9 @@ class AllowedValuesConstraintValidator extends ChoiceValidator {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function validate($value, Constraint $constraint) { public function validate($value, Constraint $constraint) {
if (!isset($value)) {
return;
}
if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) { if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
$account = \Drupal::currentUser(); $account = \Drupal::currentUser();
$allowed_values = $this->context->getMetadata()->getTypedData()->getSettableValues($account); $allowed_values = $this->context->getMetadata()->getTypedData()->getSettableValues($account);
......
...@@ -359,7 +359,7 @@ function createSampleNodes($count = 5) { ...@@ -359,7 +359,7 @@ function createSampleNodes($count = 5) {
// Post $count article nodes. // Post $count article nodes.
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$edit = array(); $edit = array();
$edit['title'] = $this->randomName(); $edit['title[0][value]'] = $this->randomName();
$edit['body[0][value]'] = $this->randomName(); $edit['body[0][value]'] = $this->randomName();
$this->drupalPostForm('node/add/article', $edit, t('Save')); $this->drupalPostForm('node/add/article', $edit, t('Save'));
} }
......
...@@ -243,7 +243,7 @@ function createBookNode($book_nid, $parent = NULL) { ...@@ -243,7 +243,7 @@ function createBookNode($book_nid, $parent = NULL) {
static $number = 0; // Used to ensure that when sorted nodes stay in same order. static $number = 0; // Used to ensure that when sorted nodes stay in same order.
$edit = array(); $edit = array();
$edit["title"] = $number . ' - SimpleTest test node ' . $this->randomName(10); $edit['title[0][value]'] = $number . ' - SimpleTest test node ' . $this->randomName(10);
$edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomName(32) . ' ' . $this->randomName(32); $edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomName(32) . ' ' . $this->randomName(32);
$edit['book[bid]'] = $book_nid; $edit['book[bid]'] = $book_nid;
...@@ -258,7 +258,7 @@ function createBookNode($book_nid, $parent = NULL) { ...@@ -258,7 +258,7 @@ function createBookNode($book_nid, $parent = NULL) {
} }
// Check to make sure the book node was created. // Check to make sure the book node was created.
$node = $this->drupalGetNodeByTitle($edit['title']); $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.'); $this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
$number++; $number++;
......
...@@ -95,7 +95,7 @@ function testCommentLanguage() { ...@@ -95,7 +95,7 @@ function testCommentLanguage() {
// Create "Article" content. // Create "Article" content.
$title = $this->randomName(); $title = $this->randomName();
$edit = array( $edit = array(
'title' => $title, 'title[0][value]' => $title,
'body[0][value]' => $this->randomName(), 'body[0][value]' => $this->randomName(),
'langcode' => $node_langcode, 'langcode' => $node_langcode,
'comment[0][status]' => COMMENT_OPEN, 'comment[0][status]' => COMMENT_OPEN,
......
...@@ -309,7 +309,7 @@ private function doNode($type) { ...@@ -309,7 +309,7 @@ private function doNode($type) {
// Create a node using the form in order to generate an add content event // Create a node using the form in order to generate an add content event
// (which is not triggered by drupalCreateNode). // (which is not triggered by drupalCreateNode).
$edit = $this->getContent($type); $edit = $this->getContent($type);
$title = $edit["title"]; $title = $edit['title[0][value]'];
$this->drupalPostForm('node/add/' . $type, $edit, t('Save')); $this->drupalPostForm('node/add/' . $type, $edit, t('Save'));
$this->assertResponse(200); $this->assertResponse(200);
// Retrieve the node object. // Retrieve the node object.
...@@ -369,7 +369,7 @@ private function getContent($type) { ...@@ -369,7 +369,7 @@ private function getContent($type) {
switch ($type) { switch ($type) {
case 'forum': case 'forum':
$content = array( $content = array(
'title' => $this->randomName(8), 'title[0][value]' => $this->randomName(8),
'taxonomy_forums' => array(1), 'taxonomy_forums' => array(1),
'body[0][value]' => $this->randomName(32), 'body[0][value]' => $this->randomName(32),
); );
...@@ -377,7 +377,7 @@ private function getContent($type) { ...@@ -377,7 +377,7 @@ private function getContent($type) {
default: default:
$content = array( $content = array(
'title' => $this->randomName(8), 'title[0][value]' => $this->randomName(8),
'body[0][value]' => $this->randomName(32), 'body[0][value]' => $this->randomName(32),
); );
break; break;
......
...@@ -4,7 +4,7 @@ services: ...@@ -4,7 +4,7 @@ services:
arguments: ['@container.namespaces'] arguments: ['@container.namespaces']
access_check.edit.entity_field: access_check.edit.entity_field:
class: Drupal\edit\Access\EditEntityFieldAccessCheck class: Drupal\edit\Access\EditEntityFieldAccessCheck
arguments: ['@entity.manager', '@field.info'] arguments: ['@entity.manager']
tags: tags:
- { name: access_check } - { name: access_check }
access_check.edit.entity: access_check.edit.entity:
......
...@@ -21,7 +21,13 @@ Drupal.edit.editors.plain_text = Drupal.edit.EditorView.extend({ ...@@ -21,7 +21,13 @@ Drupal.edit.editors.plain_text = Drupal.edit.EditorView.extend({
var fieldModel = this.fieldModel; var fieldModel = this.fieldModel;
// Store the original value of this field. Necessary for reverting changes. // Store the original value of this field. Necessary for reverting changes.
var $textElement = this.$textElement = this.$el.find('.field-item:first'); var $textElement;
if (this.$el.is(':has(.field-item)')) {
$textElement = this.$textElement = this.$el.find('.field-item:first');
}
else {
$textElement = this.$textElement = this.$el;
}
editorModel.set('originalValue', $.trim(this.$textElement.text())); editorModel.set('originalValue', $.trim(this.$textElement.text()));
// Sets the state to 'changed' whenever the value changes // Sets the state to 'changed' whenever the value changes
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\field\FieldInfo;
/** /**
* Access check for editing entity fields. * Access check for editing entity fields.
...@@ -29,24 +28,14 @@ class EditEntityFieldAccessCheck implements StaticAccessCheckInterface, EditEnti ...@@ -29,24 +28,14 @@ class EditEntityFieldAccessCheck implements StaticAccessCheckInterface, EditEnti
*/ */
protected $entityManager; protected $entityManager;
/**
* The field info.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/** /**
* Constructs a EditEntityFieldAccessCheck object. * Constructs a EditEntityFieldAccessCheck object.
* *
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager. * The entity manager.
* @param \Drupal\field\FieldInfo $field_info
* The field info.
*/ */
public function __construct(EntityManagerInterface $entity_manager, FieldInfo $field_info) { public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager; $this->entityManager = $entity_manager;
$this->fieldInfo = $field_info;
} }
/** /**
...@@ -95,7 +84,7 @@ protected function validateAndUpcastRequestAttributes(Request $request) { ...@@ -95,7 +84,7 @@ protected function validateAndUpcastRequestAttributes(Request $request) {
// Validate the field name and language. // Validate the field name and language.
$field_name = $request->attributes->get('field_name'); $field_name = $request->attributes->get('field_name');
if (!$field_name || !$this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name)) { if (!$field_name || !$entity->hasField($field_name)) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
$langcode = $request->attributes->get('langcode'); $langcode = $request->attributes->get('langcode');
......
...@@ -145,7 +145,7 @@ public function metadata(Request $request) { ...@@ -145,7 +145,7 @@ public function metadata(Request $request) {
} }
// Validate the field name and language. // Validate the field name and language.
if (!$field_name || !($instance = $this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name))) { if (!$field_name || !$entity->hasField($field_name)) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
if (!$langcode || (field_valid_language($langcode) !== $langcode)) { if (!$langcode || (field_valid_language($langcode) !== $langcode)) {
...@@ -158,7 +158,8 @@ public function metadata(Request $request) { ...@@ -158,7 +158,8 @@ public function metadata(Request $request) {
$metadata[$entity_id] = $this->metadataGenerator->generateEntity($entity, $langcode); $metadata[$entity_id] = $this->metadataGenerator->generateEntity($entity, $langcode);
} }
$metadata[$field] = $this->metadataGenerator->generateField($entity, $instance, $langcode, $view_mode); $field_definition = $entity->get($field_name)->getFieldDefinition();
$metadata[$field] = $this->metadataGenerator->generateField($entity, $field_definition, $langcode, $view_mode);
} }
return new JsonResponse($metadata); return new JsonResponse($metadata);
......
...@@ -128,18 +128,8 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name ...@@ -128,18 +128,8 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name
$form_state['field_name'] = $field_name; $form_state['field_name'] = $field_name;
// @todo Allow the usage of different form modes by exposing a hook and the // @todo Allow the usage of different form modes by exposing a hook and the
// UI for them. // UI for them.
$form_display = entity_get_render_form_display($entity, 'default'); $form_state['form_display'] = entity_get_render_form_display($entity, 'default');
// Let modules alter the form display.
$form_display_context = array(
'entity_type' => $entity->entityType(),
'bundle' => $entity->bundle(),
'form_mode' => 'default',
);
$this->moduleHandler->alter('entity_form_display', $form_display, $form_display_context);
$form_state['form_display'] = $form_display;
} }
/** /**
...@@ -180,14 +170,14 @@ public function submitForm(array &$form, array &$form_state) { ...@@ -180,14 +170,14 @@ public function submitForm(array &$form, array &$form_state) {
*/ */
protected function buildEntity(array $form, array &$form_state) { protected function buildEntity(array $form, array &$form_state) {
$entity = clone $form_state['entity']; $entity = clone $form_state['entity'];
$field_name = $form_state['field_name'];
field_attach_extract_form_values($entity, $form, $form_state, array('field_name' => $form_state['field_name'])); field_attach_extract_form_values($entity, $form, $form_state, array('field_name' => $field_name));
// @todo Refine automated log messages and abstract them to all entity // @todo Refine automated log messages and abstract them to all entity
// types: http://drupal.org/node/1678002. // types: http://drupal.org/node/1678002.
if ($entity->entityType() == 'node' && $entity->isNewRevision() && !isset($entity->log)) { if ($entity->entityType() == 'node' && $entity->isNewRevision() && !isset($entity->log)) {
$instance = field_info_instance($entity->entityType(), $form_state['field_name'], $entity->bundle()); $entity->log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $entity->get($field_name)->getFieldDefinition()->getFieldLabel()));
$entity->log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $instance->getFieldLabel()));
} }
return $entity; return $entity;
......
...@@ -40,13 +40,6 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { ...@@ -40,13 +40,6 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase {
*/ */
protected $entityManager; protected $entityManager;
/**
* The mocked field info.
*
* @var \Drupal\field\FieldInfo|\PHPUnit_Framework_MockObject_MockObject
*/
protected $fieldInfo;
/** /**
* The mocked entity storage controller. * The mocked entity storage controller.
* *
...@@ -71,11 +64,7 @@ protected function setUp() { ...@@ -71,11 +64,7 @@ protected function setUp() {
->method('getStorageController') ->method('getStorageController')
->will($this->returnValue($this->entityStorageController)); ->will($this->returnValue($this->entityStorageController));
$this->fieldInfo = $this->getMockBuilder('Drupal\field\FieldInfo') $this->editAccessCheck = new EditEntityFieldAccessCheck($this->entityManager);
->disableOriginalConstructor()
->getMock();
$this->editAccessCheck = new EditEntityFieldAccessCheck($this->entityManager, $this->fieldInfo);
} }
/** /**
...@@ -91,16 +80,12 @@ public function testAppliesTo() { ...@@ -91,16 +80,12 @@ public function testAppliesTo() {
* @see \Drupal\edit\Tests\edit\Access\EditEntityFieldAccessCheckTest::testAccess() * @see \Drupal\edit\Tests\edit\Access\EditEntityFieldAccessCheckTest::testAccess()
*/ */
public function providerTestAccess() { public function providerTestAccess() {
$editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest') $editable_entity = $this->createMockEntity();
->disableOriginalConstructor()
->getMock();
$editable_entity->expects($this->any()) $editable_entity->expects($this->any())
->method('access') ->method('access')
->will($this->returnValue(TRUE)); ->will($this->returnValue(TRUE));
$non_editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest') $non_editable_entity = $this->createMockEntity();
->disableOriginalConstructor()
->getMock();
$non_editable_entity->expects($this->any()) $non_editable_entity->expects($this->any())
->method('access') ->method('access')
->will($this->returnValue(FALSE)); ->will($this->returnValue(FALSE));
...@@ -146,22 +131,15 @@ public function testAccess(EntityInterface $entity, FieldInterface $field = NULL ...@@ -146,22 +131,15 @@ public function testAccess(EntityInterface $entity, FieldInterface $field = NULL
$entity_with_field = clone $entity; $entity_with_field = clone $entity;
$entity_with_field->expects($this->any()) $entity_with_field->expects($this->any())
->method('get') ->method('get')
->with('valid')
->will($this->returnValue($field)); ->will($this->returnValue($field));
// Prepare the request to be valid. // Prepare the request to be valid.
$request->attributes->set('entity', $entity_with_field);
$request->attributes->set('entity_type', 'test_entity'); $request->attributes->set('entity_type', 'test_entity');
$request->attributes->set('field_name', 'example'); $request->attributes->set('entity', $entity_with_field);
$request->attributes->set('field_name', 'valid');
$request->attributes->set('langcode', Language::LANGCODE_NOT_SPECIFIED); $request->attributes->set('langcode', Language::LANGCODE_NOT_SPECIFIED);
$this->fieldInfo->expects($this->any())
->method('getInstance')
->will($this->returnValue(array(
'example' => array(
'field_name' => 'example',
)
)));