From f403ccb9c5d1c7f63fb1bca64751940b8d5f58b2 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Thu, 15 Dec 2016 09:36:41 +0000 Subject: [PATCH] Issue #2548713 by yongt9412, larowlan, Berdir, swentel, subhojit777, effulgentsia: Only one additional new value saved unlimited field and no non-field values are restored after preview --- .../Plugin/Field/FieldWidget/FileWidget.php | 15 +++ .../modules/node/src/Form/NodePreviewForm.php | 2 +- core/modules/node/src/NodeForm.php | 27 ++--- .../node/src/Tests/PagePreviewTest.php | 113 +++++++++++++++++- .../tests/modules/node_test/node_test.module | 15 +++ 5 files changed, 152 insertions(+), 20 deletions(-) diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php index b2386b489d3e..d0f747457746 100644 --- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php +++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php @@ -291,6 +291,21 @@ class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface { return $new_values; } + /** + * {@inheritdoc} + */ + public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) { + parent::extractFormValues($items, $form, $form_state); + + // Update reference to 'items' stored during upload to take into account + // changes to values like 'alt' etc. + // @see \Drupal\file\Plugin\Field\FieldWidget\FileWidget::submit() + $field_name = $this->fieldDefinition->getName(); + $field_state = static::getWidgetState($form['#parents'], $field_name, $form_state); + $field_state['items'] = $items->getValue(); + static::setWidgetState($form['#parents'], $field_name, $form_state, $field_state); + } + /** * Form API callback. Retrieves the value for the file_generic field element. * diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php index 4027fdb85413..8a4cce9339ce 100644 --- a/core/modules/node/src/Form/NodePreviewForm.php +++ b/core/modules/node/src/Form/NodePreviewForm.php @@ -72,7 +72,7 @@ class NodePreviewForm extends FormBase { public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $node = NULL) { $view_mode = $node->preview_view_mode; - $query_options = $node->isNew() ? array('query' => array('uuid' => $node->uuid())) : array(); + $query_options = array('query' => array('uuid' => $node->uuid())); $form['backlink'] = array( '#type' => 'link', '#title' => $this->t('Back to content editing'), diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php index 7a0471af38e7..3a9ed5432063 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -62,31 +62,28 @@ class NodeForm extends ContentEntityForm { public function form(array $form, FormStateInterface $form_state) { // Try to restore from temp store, this must be done before calling // parent::form(). - $uuid = $this->entity->uuid(); $store = $this->tempStoreFactory->get('node_preview'); - // If the user is creating a new node, the UUID is passed in the request. - if ($request_uuid = \Drupal::request()->query->get('uuid')) { - $uuid = $request_uuid; - } - - if ($preview = $store->get($uuid)) { + // Attempt to load from preview when the uuid is present unless we are + // rebuilding the form. + $request_uuid = \Drupal::request()->query->get('uuid'); + if (!$form_state->isRebuilding() && $request_uuid && $preview = $store->get($request_uuid)) { /** @var $preview \Drupal\Core\Form\FormStateInterface */ - foreach ($preview->getValues() as $name => $value) { - $form_state->setValue($name, $value); - } + $form_state->setStorage($preview->getStorage()); + $form_state->setUserInput($preview->getUserInput()); // Rebuild the form. $form_state->setRebuild(); + + // The combination of having user input and rebuilding the form means + // that it will attempt to cache the form state which will fail if it is + // a GET request. + $form_state->setRequestMethod('POST'); + $this->entity = $preview->getFormObject()->getEntity(); $this->entity->in_preview = NULL; - // Remove the stale temp store entry for existing nodes. - if (!$this->entity->isNew()) { - $store->delete($uuid); - } - $this->hasBeenPreviewed = TRUE; } diff --git a/core/modules/node/src/Tests/PagePreviewTest.php b/core/modules/node/src/Tests/PagePreviewTest.php index 2bfd2e71f8ec..2afcc8d2df7d 100644 --- a/core/modules/node/src/Tests/PagePreviewTest.php +++ b/core/modules/node/src/Tests/PagePreviewTest.php @@ -28,7 +28,7 @@ class PagePreviewTest extends NodeTestBase { * * @var array */ - public static $modules = array('node', 'taxonomy', 'comment', 'image', 'file'); + public static $modules = array('node', 'taxonomy', 'comment', 'image', 'file', 'text', 'node_test', 'menu_ui'); /** * The name of the created field. @@ -41,7 +41,7 @@ class PagePreviewTest extends NodeTestBase { parent::setUp(); $this->addDefaultCommentField('node', 'page'); - $web_user = $this->drupalCreateUser(array('edit own page content', 'create page content')); + $web_user = $this->drupalCreateUser(array('edit own page content', 'create page content', 'administer menu')); $this->drupalLogin($web_user); // Add a vocabulary so we can test different view modes. @@ -124,6 +124,34 @@ class PagePreviewTest extends NodeTestBase { entity_get_display('node', 'page', 'default') ->setComponent('field_image') ->save(); + + // Create a multi-value text field. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_test_multi', + 'entity_type' => 'node', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'type' => 'text', + 'settings' => [ + 'max_length' => 50, + ] + ]); + $field_storage->save(); + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => 'page', + ])->save(); + + entity_get_form_display('node', 'page', 'default') + ->setComponent('field_test_multi', array( + 'type' => 'text_textfield', + )) + ->save(); + + entity_get_display('node', 'page', 'default') + ->setComponent('field_test_multi', array( + 'type' => 'string', + )) + ->save(); } /** @@ -176,8 +204,11 @@ class PagePreviewTest extends NodeTestBase { $this->clickLink(t('Back to content editing')); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); - $this->assertFieldByName($term_key, $edit[$term_key] . ' (' . $this->term->id() . ')', 'Term field displayed.'); + $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); $this->assertFieldByName('field_image[0][alt]', 'Picture of llamas'); + $this->drupalPostAjaxForm(NULL, array(), array('field_test_multi_add_more' => t('Add another item')), NULL, array(), array(), 'node-page-form'); + $this->assertFieldByName('field_test_multi[0][value]'); + $this->assertFieldByName('field_test_multi[1][value]'); // Return to page preview to check everything is as expected. $this->drupalPostForm(NULL, array(), t('Preview')); @@ -191,7 +222,7 @@ class PagePreviewTest extends NodeTestBase { $this->drupalGet('node/add/page', array('query' => array('uuid' => $uuid))); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); - $this->assertFieldByName($term_key, $edit[$term_key] . ' (' . $this->term->id() . ')', 'Term field displayed.'); + $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); // Save the node - this is a new POST, so we need to upload the image. $this->drupalPostForm('node/add/page', $edit, t('Upload')); @@ -260,6 +291,80 @@ class PagePreviewTest extends NodeTestBase { $this->drupalPostForm('node/add/page', array($title_key => 'Preview'), t('Preview')); $this->clickLink(t('Back to content editing')); $this->assertRaw('edit-submit'); + + // Assert multiple items can be added and are not lost when previewing. + $test_image_1 = current($this->drupalGetTestFiles('image', 39325)); + $edit_image_1['files[field_image_0][]'] = drupal_realpath($test_image_1->uri); + $test_image_2 = current($this->drupalGetTestFiles('image', 39325)); + $edit_image_2['files[field_image_1][]'] = drupal_realpath($test_image_2->uri); + $edit['field_image[0][alt]'] = 'Alt 1'; + + $this->drupalPostForm('node/add/page', $edit_image_1, t('Upload')); + $this->drupalPostForm(NULL, $edit, t('Preview')); + $this->clickLink(t('Back to content editing')); + $this->assertFieldByName('files[field_image_1][]'); + $this->drupalPostForm(NULL, $edit_image_2, t('Upload')); + $this->assertNoFieldByName('files[field_image_1][]'); + + $title = 'node_test_title'; + $example_text_1 = 'example_text_preview_1'; + $example_text_2 = 'example_text_preview_2'; + $example_text_3 = 'example_text_preview_3'; + $this->drupalGet('node/add/page'); + $edit = [ + 'title[0][value]' => $title, + 'field_test_multi[0][value]' => $example_text_1, + ]; + $this->assertRaw('Storage is not set'); + $this->drupalPostForm(NULL, $edit, t('Preview')); + $this->clickLink(t('Back to content editing')); + $this->assertRaw('Storage is set'); + $this->assertFieldByName('field_test_multi[0][value]'); + $this->drupalPostForm(NULL, [], t('Save')); + $this->assertText('Basic page ' . $title . ' has been created.'); + $node = $this->drupalGetNodeByTitle($title); + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->drupalPostAjaxForm(NULL, [], array('field_test_multi_add_more' => t('Add another item'))); + $this->drupalPostAjaxForm(NULL, [], array('field_test_multi_add_more' => t('Add another item'))); + $edit = [ + 'field_test_multi[1][value]' => $example_text_2, + 'field_test_multi[2][value]' => $example_text_3, + ]; + $this->drupalPostForm(NULL, $edit, t('Preview')); + $this->clickLink(t('Back to content editing')); + $this->drupalPostForm(NULL, $edit, t('Preview')); + $this->clickLink(t('Back to content editing')); + $this->assertFieldByName('field_test_multi[0][value]', $example_text_1); + $this->assertFieldByName('field_test_multi[1][value]', $example_text_2); + $this->assertFieldByName('field_test_multi[2][value]', $example_text_3); + + // Now save the node and make sure all values got saved. + $this->drupalPostForm(NULL, [], t('Save')); + $this->assertText($example_text_1); + $this->assertText($example_text_2); + $this->assertText($example_text_3); + + // Edit again, change the menu_ui settings and click on preview. + $this->drupalGet('node/' . $node->id() . '/edit'); + $edit = [ + 'menu[enabled]' => TRUE, + 'menu[title]' => 'Changed title', + ]; + $this->drupalPostForm(NULL, $edit, t('Preview')); + $this->clickLink(t('Back to content editing')); + $this->assertFieldChecked('edit-menu-enabled', 'Menu option is still checked'); + $this->assertFieldByName('menu[title]', 'Changed title', 'Menu link title is correct after preview'); + + // Save, change the title while saving and make sure that it is correctly + // saved. + $edit = [ + 'menu[enabled]' => TRUE, + 'menu[title]' => 'Second title change', + ]; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertFieldByName('menu[title]', 'Second title change', 'Menu link title is correct after saving'); + } /** diff --git a/core/modules/node/tests/modules/node_test/node_test.module b/core/modules/node/tests/modules/node_test/node_test.module index 00cff3b95476..866be199c75d 100644 --- a/core/modules/node/tests/modules/node_test/node_test.module +++ b/core/modules/node/tests/modules/node_test/node_test.module @@ -10,9 +10,11 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\node\NodeInterface; + /** * Implements hook_ENTITY_TYPE_view() for node entities. */ @@ -175,3 +177,16 @@ function node_test_node_insert(NodeInterface $node) { $node->save(); } } + +/** + * Implements hook_form_alter(). + */ +function node_test_form_alter(&$form, FormStateInterface $form_state, $form_id) { + if (!$form_state->get('node_test_form_alter')) { + drupal_set_message('Storage is not set'); + $form_state->set('node_test_form_alter', TRUE); + } + else { + drupal_set_message('Storage is set'); + } +}