diff --git a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php index 6faa0c9..cd6fe8e 100644 --- a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php +++ b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php @@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\user\TempStoreFactory; +use Drupal\Core\Entity\EntityChangedInterface; +use Drupal\Core\Language\Language; /** * Builds and process a form for editing a single entity field. @@ -86,7 +88,30 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name */ public function validate(array $form, array &$form_state) { $entity = $this->buildEntity($form, $form_state); - field_attach_form_validate($entity, $form, $form_state, array('field_name' => $form_state['field_name'])); + //field_attach_form_validate($entity, $form, $form_state, array('field_name' => $form_state['field_name'])); + + $changedFieldName = $this->getChangedFieldName($entity); + + $violations = array(); + foreach ($entity as $field_name => $field) { + if ((!empty($changedFieldName) && $field_name == $changedFieldName) || ($field_name == $form_state['field_name'])) { + $field_violations = $field->validate(); + if (is_array($field_violations) && count($field_violations)) { + $violations = array_merge($violations, $field_violations); + } + } + } + + if (count($violations)) { + // Place violations in $form_state. + $langcode = field_is_translatable($entity->entityType(), field_info_field($entity->entityType(), $field_name)) ? $entity->language()->id : Language::LANGCODE_NOT_SPECIFIED; + $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state); + $field_state['constraint_violations'] = $field_violations; + field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state); + + // Map errors back to form elements. + field_invoke_method('flagErrors', _field_invoke_widget_target($form_state['form_display']), $entity, $form, $form_state, array('field_name' => $form_state['field_name'])); + } } /** @@ -161,4 +186,20 @@ protected function simplify(array &$form, array &$form_state) { } } + /** + * Find the field name for the field carrying the changed timestamp, if any. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + */ + protected function getChangedFieldName(EntityInterface $entity) { + foreach ($entity as $field_name => $field) { + $definition = $field->getDefinition(); + if (isset($definition['property_constraints']['value']['EntityChanged'])) { + return $field_name; + } + } + return NULL; + } + } diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php b/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php index d3e474c..631c936 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php @@ -222,6 +222,49 @@ function testUserWithPermission() { } /** + * Tests Edit with concurrent node / Edit use. + */ + function testConcurrentEdit() { + $this->drupalLogin($this->editor_user); + + $response = $this->retrieveFieldForm('node/1/body/und/full'); + debug($response); + $ajax_commands = drupal_json_decode($response); + + // Prepare form values for submission. drupalPostAJAX() is not suitable + // for handling pages with JSON responses, so we need our own solution + // here. + $form_tokens_found = preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); + $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); + + if ($form_tokens_found) { + $edit = array(); + $edit['form_id'] = 'edit_field_form'; + $edit['form_token'] = $token_match[1]; + $edit['form_build_id'] = $build_id_match[1]; + $edit['body[und][0][summary]'] = ''; + $edit['body[und][0][value]'] = '

Fine thanks.

'; + $edit['body[und][0][format]'] = 'filtered_html'; + $edit['op'] = t('Save'); + + // Save the node on the regular node edit form. + $this->drupalPost('node/1/edit', array(), t('Save')); + sleep(2); + + // Submit field form and check response. Should throw a validation error + // because the node was changed in the meantime. + $response = $this->submitFieldForm('node/1/body/und/full', $edit); + debug($response); + $this->assertResponse(200); + $ajax_commands = drupal_json_decode($response); + debug($ajax_commands); + $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); + $this->assertIdentical('editFieldFormValidationErrors', $ajax_commands[0]['command'], 'The first AJAX command is an editFieldFormValidationErrors command.'); + $this->assertTrue(strpos($ajax_commands[0]['data'], t('The copy of the content being edited is outdated. Reload the page to edit an up-to-date version.')), 'Error message returned to user.'); + } + } + + /** * Retrieve Edit metadata from the server. May also result in additional * JavaScript settings and CSS/JS being loaded. *