From #1510544: Allow to preview content in an actual live environment

I did accidentally find one other bug in testing: if you're at admin/content, and select "Edit" next to a node in the list, then click "Preview," instead of being shown the preview, you will be taken back to admin/content and your changes lost. I suspect that's because the URL you end up on is http://8.x.local/node/1/edit?destination=admin/content and this somehow screws with the preview system's use of URLs? Hm.

Oh yes, the destination parameter, we somehow lost that in the patch somewhere, I remember we used to store that property as well.
It's not /that/ hard to implement, but I'm AFK until monday. If we can wait that's fine, unless someone else takes that up. Also more than happy to fix that in a follow up. You can even assign it on me immediately :)

This has this bewildering effect:

  • On admin/content, edit a node.
  • Change stuff, click Preview.
  • You are returned to admin/content. Edit the node again.
  • The changes appear to have been saved by preview, but totally were not.
CommentFileSizeAuthor
#106 interdiff-101-to-106.txt568 bytesclaudiu.cristea
#106 2325463-106.patch6.15 KBclaudiu.cristea
#101 interdiff-99-to-101.txt589 bytesclaudiu.cristea
#101 2325463-101-8.2.x.patch6.15 KBclaudiu.cristea
#99 interdiff-91-to-99.txt939 bytesclaudiu.cristea
#99 2325463-99-8.2.x.patch5.77 KBclaudiu.cristea
#91 2325463-90-8.2.x.patch5.85 KBclaudiu.cristea
#90 interdiff-87-to-90.txt3.02 KBclaudiu.cristea
#90 2325463-90.patch5.84 KBclaudiu.cristea
#87 interdiff-85-to-86.txt4.59 KBclaudiu.cristea
#87 2325463-86.patch5.07 KBclaudiu.cristea
#85 2325463-85-interdiff.txt1.55 KBBerdir
#85 2325463-85.patch8.32 KBBerdir
#82 2325463-82.patch9.03 KByogeshmpawar
#80 destination_breaks_preview-2325463-80.patch763 bytesaloknarwaria
#77 destination_breaks_preview-2325463-77.patch1.05 KBaloknarwaria
#70 2325463-70.patch8.52 KBclaudiu.cristea
#70 interdiff.txt8.27 KBclaudiu.cristea
#63 interdiff-57-63.txt7.06 KBmohit_aghera
#63 2325463-63.patch8.81 KBmohit_aghera
#57 2325463-57.patch5.32 KBbircher
#57 2325463-57-test-only.patch2.27 KBbircher
#57 interdiff-2325463-53-57.txt5.2 KBbircher
#53 interdiff.txt1012 bytesyanniboi
#53 2325463-53-node-preview-workflow-fixed.patch6.86 KByanniboi
#49 2325463-49-node-preview-workflow-fixed.patch6.87 KBBarisW
#42 2325463-42-node-preview-workflow-fixed.patch6.87 KBAnonymous (not verified)
#38 2325463-38-node-preview-destination.patch5.29 KBAnonymous (not verified)
#35 2325463-35-interdiff.txt1.34 KBlokapujya
#35 2325463-35.patch5.75 KBlokapujya
#34 2325463-33-node-preview-destination.patch3.79 KBAnonymous (not verified)
#32 2325463-node-preview-retain-passed-GET-parameters.patch1.92 KBAnonymous (not verified)
#26 2325463-26.patch3.21 KBswentel
#20 2325463-20b.patch3.09 KBmgifford
#16 2325463-16.patch3.09 KBswentel
#2 2325463-2.patch3.42 KBswentel
#2 2325463-2-fail.patch1.67 KBswentel
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jibran’s picture

swentel’s picture

Status: Active » Needs review
FileSize
1.67 KB
3.42 KB

The last submitted patch, 2: 2325463-2-fail.patch, failed testing.

dawehner’s picture

Just a quick drive by review.

  1. +++ b/core/modules/node/src/NodeForm.php
    @@ -404,6 +402,11 @@ public function submit(array $form, FormStateInterface $form_state) {
    +    if ($destination = \Drupal::request()->query->get('destination')) {
    +      \Drupal::request()->query->remove('destination');
    +      $this->entity->preview_destination = $destination;
    +    }
    
    @@ -488,7 +491,10 @@ public function save(array $form, FormStateInterface $form_state) {
    +      if (!empty($this->entity->preview_destination)) {
    +        \Drupal::request()->query->set('destination', $this->entity->preview_destination);
    +      }
    

    You can use $this->request() instead. PS: It is odd to put arbitrary values onto the entity. Do you have an idea how to fix that on the longrun?

  2. +++ b/core/modules/node/src/Tests/PagePreviewTest.php
    @@ -30,11 +30,16 @@ class PagePreviewTest extends NodeTestBase {
    +  protected $web_user;
    ...
    -    $web_user = $this->drupalCreateUser(array('edit own page content', 'create page content'));
    -    $this->drupalLogin($web_user);
    +    $this->web_user = $this->drupalCreateUser(array('edit own page content', 'create page content'));
    +    $this->drupalLogin($this->web_user);
    

    You can directly camelCase it, if possible.

swentel’s picture

Those arbitrary values have been bugging me as well. in_preview is another one. We can add those as properties on Node with useful getters and setters.

dawehner’s picture

Well, this works for sure in core, but how can we ensure horizontal extensibility?

jibran’s picture

+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -192,6 +197,15 @@ function testPagePreview() {
+    $this->drupalPostForm('node/' . $node->id() . '/edit', array(), t('Preview'), array('query' => array('destination' => 'user')));

Why uid is not included here in the destination?

swentel’s picture

@jibran it's not the uuid that we want to store, it's the possible destination where you want to go back after saving a node.
Say you click on the edit link on admin/content, 'destination=admin/content' will be available in the url. Currently when clicking on 'Preview', you would be redirect to the admin/content page again instead of the preview page. This patch fixes that by storing 'admin/content' (or any destination), going to preview, then going back and if you then press save, you'll go back to admin/content. Note that you won't see 'admin/content' in the url when going back from preview to the edit form.

@dawehner so yeah, horizontal extensibility would be nice, but not really something for this patch :)

jibran’s picture

@swentel thank you for the explanation but I was not asking about the uuid I was asking about uid ($this->web_user->id()

)
+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -192,6 +197,15 @@ function testPagePreview() {
+    $this->assertEqual($this->getUrl(), url('user/' . $this->web_user->id(), array('absolute' => TRUE)));

Here we are checking the user/uid url.

+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -192,6 +197,15 @@ function testPagePreview() {
+    $this->drupalPostForm('node/' . $node->id() . '/edit', array(), t('Preview'), array('query' => array('destination' => 'user')));

But here we are only adding 'user' to destination. I am just asking shouldn't it be 'user' . $this->web_user->id().
Sorry for the confusion.

swentel’s picture

ah lol :)

Right, so when you are authenticated, Drupal redirects you to user/{id} automatically, that's why destination=user was good here :)

webchick’s picture

Priority: Normal » Major

I just accidentally realized this happens on contextual links as well (e.g. editing the node from the teaser view on the home page) which makes this at least major, since we're tossing out peolpes' edits.

webchick queued 2: 2325463-2.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 2: 2325463-2.patch, failed testing.

Bojhan’s picture

Lol, wait. So you can engage contextual links on a preview?

webchick’s picture

Oh. That too. ;) But I was talking about being on the front page, hovering to get a contextual link, and choosing "Edit" from the list. Any edits you make from there before clicking "Preview" are lost, because you just get redirected back to the front page instead of the preview.

swentel’s picture

Status: Needs work » Needs review
FileSize
3.09 KB

rerolled

jibran queued 16: 2325463-16.patch for re-testing.

mgifford’s picture

Status: Needs review » Reviewed & tested by the community

This still makes the bot happy.

I confirmed the bug still exists without the patch, but it works as needed with the patch.

Both preview link goes to the preview page rather than back to admin/content and the destination seems to be preserved.

Let's get this fixed.

I should note that the code looks fine to me and the tests are in place too.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/node/src/NodeForm.php
@@ -424,7 +429,10 @@ public function save(array $form, FormStateInterface $form_state) {
+      if (!empty($this->entity->preview_destination)) {
+        \Drupal::request()->query->set('destination', $this->entity->preview_destination);
+      }

I think we should use $this->getRequest().

mgifford’s picture

Status: Needs work » Needs review
FileSize
3.09 KB

Ok.. I think this is the right format, so re-rolling:

+      if (!empty($this->entity->preview_destination)) {
+        $this->getRequest()->query->set('destination', $this->entity->preview_destination);
+      }
swentel’s picture

Status: Needs review » Reviewed & tested by the community

Right, thanks!

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/node/src/NodeForm.php
@@ -346,6 +346,11 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
+      $this->entity->preview_destination = $destination;

@@ -424,7 +429,10 @@ public function save(array $form, FormStateInterface $form_state) {
+        $this->getRequest()->query->set('destination', $this->entity->preview_destination);

Isn't storing arbitrary properties on entities a problem?
Wouldn't we then need to remove it later?

Why not just store it in the $form_state?

swentel’s picture

Hmm right, in_preview is also being unset at some point. I'll have a look if I can move both to $form_state.

swentel’s picture

@tim.plunkett Been playing with form_state and either set/setValue/setTemporaryValue - but it seems I'm losing those values in NodeForm::save()

Will dig some more later.

mgifford’s picture

Assigned: swentel » Unassigned

Just unassigning issues that haven't been developed for a bit in the D8 queue.

swentel’s picture

Status: Needs work » Needs review
FileSize
3.21 KB

first a reroll - working further now to modernize - apparently, the patch even fails now too, so needs works apparently.

Status: Needs review » Needs work

The last submitted patch, 26: 2325463-26.patch, failed testing.

Status: Needs work » Needs review

swentel queued 26: 2325463-26.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 26: 2325463-26.patch, failed testing.

Status: Needs work » Needs review

grisendo queued 26: 2325463-26.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 26: 2325463-26.patch, failed testing.

Anonymous’s picture

Solution whereby destination is handed across in the query

Status: Needs review » Needs work

The last submitted patch, 32: 2325463-node-preview-retain-passed-GET-parameters.patch, failed testing.

Anonymous’s picture

Status: Needs work » Needs review
FileSize
3.79 KB

Added function FormHelper::redirectOptionsPassThroughDestination() to help using FormStateInterface::setRedirect() without losing current query destination.

lokapujya’s picture

Adding the test from #26, even though I know it's going to fail, so that the test can be discussed.

lokapujya’s picture

+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -273,10 +273,12 @@ function testPagePreview() {
     $this->drupalPostForm('node/' . $node->id() . '/edit', array(), t('Preview'), array('query' => array('destination' => 'user')));
<?php
array('query' => array('destination' => 'user'))
?>

Should this be chopped off? Is it really possible to have a destination here?

Status: Needs review » Needs work

The last submitted patch, 35: 2325463-35.patch, failed testing.

Anonymous’s picture

The test in this patch better checks the user journey

  /**
   * Checks the user journey from the content overview page.
   */
  public function testAdminContentEditUserJourney() {
    // Add a node to preview.
    $node = $this->drupalCreateNode(array());
    $edit = ['title[0][value]' => 'Test node'];

    // Create a user that can access the content overview page, and node edit
    // operation.
    $web_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'edit any page content', 'create page content'));
    $this->drupalLogin($web_user);

    // Goto the content overview page and click Edit.
    $this->drupalGet('admin/content');
    $this->clickLink(t('Edit'));

    // Preview the node.
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->assertLink(t('Back to content editing'));

    // Go back and save.
    $this->clickLink(t('Back to content editing'));
    $this->drupalPostForm(NULL, $edit, t('Save and keep published'));

    // We should have returned to admin/content via passed destination param.
    $this->assertUrl('admin/content');
  }
lokapujya’s picture

OK, I think it should stay in PagePreviewTest though. The user doesn't always start out at admin/content; The user may have clicked a contextual link.

Anonymous’s picture

Status: Needs work » Needs review
lokapujya’s picture

Status: Needs review » Needs work

For moving the test into PagePreviewTest. By being in a separate test, a whole new Drupal gets installed within simpletest.

Anonymous’s picture

I had tried moving the test into the PagePreviewTest::testPagePreview() method, however the test failed. I've spent tonight looking into it properly; unfortunately it seems this issue needed more work around how the NodeForm keeps track of whether or not a node has been previewed. The reason why the test was failing was due to the code:

    $node_type = NodeType::load('page');
    $node_type->setPreviewMode(DRUPAL_REQUIRED);
    $node_type->save();

The following test then checks to ensure that the save button only appears on the form after you have use the preview step. However, the test does not check to see if the button actually works. If you click the submit button, it actually takes you back to the preview page. It does appear that the content type required-preview workflow does not work at all? (Related issue 254242 I think)

The reason why it does not work seems to be due to how the form API handles #access => FALSE submit elements. The save button #access relies on the property $this->hasBeenPreviewed being set to TRUE. This is set when the form loads, and is able to load from the node_preview store, on the PrivateTempStoreFactory (session open with the current user).

    if ($preview = $store->get($uuid)) {
      ...

      // Rebuild the form.
      $form_state->setRebuild();

      ...

      // Remove the stale temp store entry for existing nodes.
      if (!$this->entity->isNew()) {
        $store->delete($uuid);
      }

      $this->hasBeenPreviewed = TRUE;
    }

The problem is that it then deletes the stale temp store entry; when you press the submit button, it will rebuild the form on the following page request, and this code will not be executed. Before executing the submission handlers, the form API seems to make the decision somewhere that you could not have pressed submit, as the save button's #access property is FALSE. It instead assumes you must have pressed the preview button, and then sets that as the triggering element, and fires the relevant submission handlers.

What needs to happen, is the $form_state needs to be cached, rather than rebuilt, so that the information can be persisted across the page request. I've attached a patch that uses the previous test, in the context of a preview required workflow, which demonstrates that the workflow works, in addition to destination parameter being correctly handed through to submission.

Anonymous’s picture

Status: Needs work » Needs review
lokapujya’s picture

lokapujya’s picture

Adding an issue that might be related.

chr.fritsch’s picture

IMHO the patch looks good and works as expected. Perhaps we shouldnt introduce that new helper function when that will be fixed in the related issus

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

nicholas.alipaz’s picture

Just tried this on the 8.1.0 release and it worked a treat!

BarisW’s picture

Tested it too, and confirming that it solved the issue. Here's a reroll to keep up with head.

Status: Needs review » Needs work

The last submitted patch, 49: 2325463-49-node-preview-workflow-fixed.patch, failed testing.

BarisW’s picture

Status: Needs work » Needs review
harings_rob’s picture

Issue tags: +DevDaysMilan
+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -260,6 +260,31 @@ function testPagePreview() {
+    $web_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'edit any page content', 'access user profiles'));

Shouldnt we use short array syntax here?

I have checked the functionality and it seems to work fine. Only the remark I have put above.

yanniboi’s picture

Version: 8.1.x-dev » 8.2.x-dev
FileSize
6.86 KB
1012 bytes

I fixed up the array shorthand, and tested the patch on 8.2.x in the UI. Works for me.

tim.plunkett’s picture

  1. +++ b/core/lib/Drupal/Core/Form/FormHelper.php
    @@ -67,4 +67,25 @@ protected static function processStatesArray(array &$conditions, $search, $repla
    +  public static function redirectOptionsPassThroughDestination($options = []) {
    

    Is this the best place for this method, and does it need to be static? Also it's essentially directly manipulating a global, that seems very bad.

    Finally, should have an array typehint.

  2. +++ b/core/lib/Drupal/Core/Form/FormHelper.php
    @@ -67,4 +67,25 @@ protected static function processStatesArray(array &$conditions, $search, $repla
    +    if ($destination = \Drupal::request()->query->get('destination')) {
    
    +++ b/core/modules/node/src/Form/NodePreviewForm.php
    @@ -72,7 +73,14 @@ public function getFormId() {
    +    if ($destination = $this->getRequest()->query->get('destination')) {
    

    I would think this would use the \Drupal\Core\Routing\RedirectDestination service.

Anonymous’s picture

@tim.plunkett I agree, using a helper function here is a terrible idea; I didn't realise at the time that \Drupal:: is mostly making calls directly to the dependency injection container. It does highlight the problem, but isn't the solution.

I'm thinking a better solution, is to set up a FormDestination service, that the destination service needs to be injected into. Then you could inject that service into the form, and call something like:

$this->formDestination
  ->setFormRedirectPassThroughDestination($form_state, 'entity.node.preview', $route_params, $options);

That service would do what the helper function is doing, and unset the current destination, and also call $form_state->setRedirect($destination, $route_params, $options); with the previous destination carried through in the $options.

Anonymous’s picture

Status: Needs review » Needs work
bircher’s picture

I removed the static form helper and made the test simple again as in 26.
tim.plunkett's suggestion with RedirectDestination was not taken into consideration yet, though.

The test-only patch has both test variants and will fail for the same reason.
I took the less verbose one, but I guess it is a matter of taste.

mohit_aghera’s picture

Status: Needs work » Needs review

The last submitted patch, 57: 2325463-57-test-only.patch, failed testing.

claudiu.cristea’s picture

Version: 8.2.x-dev » 8.1.x-dev
Status: Needs review » Needs work
Issue tags: -DevDaysMilan
  1. +++ b/core/modules/node/src/Form/NodePreviewForm.php
    @@ -72,7 +72,14 @@ public function getFormId() {
    +    if ($destination = $this->getRequest()->query->get('destination')) {
    
    @@ -116,10 +123,16 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
    +    if ($destination = $this->getRequest()->query->get('destination')) {
    
    +++ b/core/modules/node/src/NodeForm.php
    @@ -341,10 +338,18 @@ public function preview(array $form, FormStateInterface $form_state) {
    +    if ($destination = $this->getRequest()->query->get('destination')) {
    

    #54.2 should be addressed.

  2. +++ b/core/modules/node/src/Form/NodePreviewForm.php
    @@ -116,10 +123,16 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
    +    $options = $this->getRequest()->query->all();
    

    This is wrong. Query item should be under $options['query'], so probably:

    $options = [ 'query' => $this->getRequest()->query->all()];
    
  3. +++ b/core/modules/node/src/Tests/PagePreviewTest.php
    @@ -260,6 +260,16 @@ function testPagePreview() {
    +    $this->drupalPostForm('node/' . $node->id() . '/edit', array(), t('Preview'), array('query' => array('destination' => $destination)));
    +    $this->assertEqual($this->getUrl(), \Drupal::url('entity.node.preview', array('node_preview' => $node->uuid(), 'view_mode_id' => 'default'), array('absolute' => TRUE, 'query' => array('destination' => $destination))));
    ...
    +    $this->drupalPostForm(NULL, array(), t('Save'));
    

    Let's be consistent and use the modern array square brackets syntax.

  4. +++ b/core/modules/node/src/Tests/PagePreviewTest.php
    @@ -260,6 +260,16 @@ function testPagePreview() {
    +    $this->assertEqual($this->getUrl(), \Drupal::url('entity.node.preview', array('node_preview' => $node->uuid(), 'view_mode_id' => 'default'), array('absolute' => TRUE, 'query' => array('destination' => $destination))));
    

    Use $this->assertUrl(); instead assertEqual().

And I think is still 8.1.x.

chumphrey84’s picture

Still broken!

Useful info: Use edit from within contextual-links. You can preview from that user journey.

Oh, but what are contextual links? These can be many things. In this context they are your edit content button, (it looks like a pencil). Edit and preview from there. Note the correct destination parameter.

chumphrey84’s picture

Actually. Above suggestion by me does not work. I have just tried on a development server. Can a core committer advise on resolution scope?

mohit_aghera’s picture

Status: Needs work » Needs review
FileSize
8.81 KB
7.06 KB

Fixing issues mentioned by @tim.plunkett and @claudiu.cristea

Status: Needs review » Needs work

The last submitted patch, 63: 2325463-63.patch, failed testing.

xjm’s picture

Version: 8.1.x-dev » 8.3.x-dev

The bugfix includes internal API changes so for that reason is targeted against the development minor, which is currently 8.3.x. Since this is a major bugfix, we could consider it for RC target triage if it is ready within the release candidate phase of 8.2.x. Otherwise, though, it should be fixed in 8.3.x or whatever the development minor is when it is ready. Thanks!

bircher’s picture

+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -129,7 +142,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
+    if ($destination = $this->redirectDestination->get()) {

It is not that simple.

The injected redirectDestination service always returns a destination.

So to demonstrate why the last patch failed:

// from patch #57
if ($destination = $this->getRequest()->query->get('destination')) {
...
}
// in patch #63
if ($destination = $this->redirectDestination->get()) {
...
}
// but what it really meant in patch #57
if ($this->getRequest()->query->has('destination')) {
$destination = $this->getRequest()->query->get('destination')
...
}

It worked because \Symfony\Component\HttpFoundation\ParameterBag::get() returns NULL when the key is not set and thus the if statement is not evaluated. (This is why patch #63 fails seemingly unrelated tests).

We also still use $this->getRequest()->query

+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -117,10 +137,16 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
+    $options = $this->getRequest()->query->all();


+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -117,10 +137,16 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
+      $this->getRequest()->query->remove('destination');

So I am not sure if we really gain anything.

cilefen’s picture

Title: Retain the destination url after preview » Destination URL breaks preview
Issue summary: View changes
Issue tags: +Triaged core major

Discussed with @xjm, @alexpott, @amateescu, @fago, @berdir and @plach. We
agreed that this is major because it causes user input to be lost.

xjm’s picture

Also the bug is currently worse than it sounds in the issue summary: If you have a destination URL and click preview, the preview does not work at all, and it might appear the node has been saved.

md88’s picture

Is there a reason why the destination is always added to the operations for nodes?
The content view has a configuration option to have (or not have) the destination in the links, but it has no effect as the destination is already set in the query attribute of the operations.

I tried looking into the matter (to the NodeListBuilder's getDefaultOperations method) but could not understand the reason. I'm quite new to Drupal so it would be cool if you could explain it to me.

Anonymous’s picture

I think this is the right way to go, but isn't this a deeper issue with multistep forms, and the destination service? Should we not consider pushing the fix to the root, rather than just treating the symptom? Otherwise we're going to end up with code duplication anywhere there are multistep forms that need to ignore the destination until the last step.

claudiu.cristea’s picture

@GroovyCarrot, I think it's impossible to predict how other multistep forms will behave.

kapetan’s picture

EDIT: Problem below seems to be related to Rename Admin Paths module, after disabling module, everything works as expected.
----
I have another problem which may be related to this issue and I think was introduced with 8.2.0, same thing is present with 8.2.1 When clicking on Edit button on admin/content page the following (example) link is generated:

http://www.site.com/node/999/edit?destination=/admin/content

However, the following error is generated when accessing above url:

The website encountered an unexpected error. Please try again later.

The following log entry is generated:

Location: http://www.site.com/node/999/edit?destination=%2Fadmin%2Fcontent
Referrer: http://www.site.com/admin/content
Message: UnexpectedValueException: Invalid Host "www.site.com?destination=" in Symfony\Component\HttpFoundation\Request->getHost() (line 1238 of C:\inetpub\wwwroot\sites\site.com\vendor\symfony\http-foundation\Request.php).

Site is running under Windows Server (IIS).

For some reason, host name is parsed as "www.site.com?destination=" and not "www.site.com" and this results in invalid host error.

Encoded url works. Above 'Location' url can be opened in browser without an error.

If %2f is replaced with '/' error is generated.

Error is generated in any case there is '/' after ? character, not only in case of 'destination' parameter.

Example:

http://www.site.com/node/999/edit?test=/admin/content

I can also confirm the problem with preview button which is present since the first release of Drupal 8.

Michelle’s picture

The patch in #70 fixed the issue for me on previewing.

There is another issue with this destination as well in that it interferes with custom redirects added to the form. I don't know if fixing that should be included here or in a new issue.

pixelmord’s picture

Issue tags: +dcmuc16
Berdir’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/node/src/NodeForm.php
    @@ -82,8 +84,8 @@ public function form(array $form, FormStateInterface $form_state) {
    -      // Rebuild the form.
    -      $form_state->setRebuild();
    +      // Cache the form state for submission request.
    +      $form_state->setRequestMethod('POST')->setCached();
           $this->entity = $preview->getFormObject()->getEntity();
    

    Looks like this overlaps a bit with #2548713: Only one additional new value saved unlimited field and no non-field values are restored after preview .

  2. +++ b/core/modules/node/src/NodeForm.php
    @@ -341,10 +343,19 @@ public function preview(array $form, FormStateInterface $form_state) {
    +    if ($query->has('destination')) {
    +      $options['query'] = $this->redirectDestination->getAsArray();
    +      $query->remove('destination');
    +    }
    +    $form_state->setRedirect('entity.node.preview', $route_parameters, $options);
    

    is this the only reason we need the redirectDestination service here? Since we access query already directly and only do it if we have an actual destination, can't we just read it from $query? Then we don't have to inject another service that possibly breaks subclasses that people might have created.

    Especially since I think this would fail if no service is injected as we don't all back to \Drupal::service(). We have to add that fall back if we are going to use it unconditioally.

Needs work atleast for making sure the optional service/argument is really optional/has a default.

To answer some recent questions, overriding a a defined redirect is in purpose, by the time it happens, there is no such thing as a custom or a non-custom form state redirect, they both look exactly the same. If you want to override, you need to to this yourself too, as storing and handling the destination will usually vary.

aloknarwaria’s picture

Hi Team,

Please find the my patch to fix the preview issue.

cilefen’s picture

There is a non-core change in the patch.

+++ b/modules/custom/domain_site_settings
@@ -1 +1 @@
-Subproject commit 9ad07fb244835b70485139cda05f1c91262cfc19
+Subproject commit 9ad07fb244835b70485139cda05f1c91262cfc19-dirty

It is not obvious to me why this patch is so much smaller than prior patches (it could be a more elegant solution). However, there is no reason to have removed the test code.

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea
Issue tags: -dcmuc16

Assigning to fix #76.

aloknarwaria’s picture

FileSize
763 bytes

@cilefen
sorry for my last patch.
Please find the updated patch to fix the issue.

Berdir’s picture

@aloknarwaria: Thanks but here already is a working patch that has test coverage and properly keeps the destination, so it actually works again *after* preview.

Just use the patch in #70. Which probably now needs a reroll, as the referenced issue is in.

yogeshmpawar’s picture

Status: Needs work » Needs review
FileSize
9.03 KB

Thanks @Berdir, I have rerolled the patch #70 against 8.3.x branch.

Status: Needs review » Needs work

The last submitted patch, 82: 2325463-82.patch, failed testing.

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned

Looks like this overlaps a bit with #2548713

@Berdir, after #2548713: Only one additional new value saved unlimited field and no non-field values are restored after preview, the patch doesn't work anymore and I cannot find a good reason.

Berdir’s picture

Status: Needs work » Needs review
FileSize
8.32 KB
1.55 KB

There were some errors in the rebase, two that I could find. We don't have to change anything anymore in the node form preview logic, there was still one change that caused an immediate redirect back. The second was that the uuid now needs to be always sent back, even for existing nodes.

Now PagePreviewTest passes, lets see about the other fails.

Status: Needs review » Needs work

The last submitted patch, 85: 2325463-85.patch, failed testing.

claudiu.cristea’s picture

Fixing also #76.2. This is ready for RTBC now.

Status: Needs review » Needs work

The last submitted patch, 87: 2325463-86.patch, failed testing.

Berdir’s picture

+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -116,10 +121,12 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
-    ));
+    ];
+    $options = ['query' => $this->getRedirectDestination()->getAsArray()];
+    $form_state->setRedirect('entity.node.preview', $route_parameters, $options);

this must be conditional as well. the redirect destination stuff falls back to the current url for the destination.

To reproduce:

1. Edit, without destination
2. Preview
3. Switch view mode
4. Now you get a destination to the current preview URL
5. Go back, save, => 404

Just forget about redirect destination stuff, simply use request query like in buildForm, with the basically the same logic.

We should also have a test for this.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
5.84 KB
3.02 KB

Implemented #89.

claudiu.cristea’s picture

A 8.2.x version of #90.

Status: Needs review » Needs work

The last submitted patch, 91: 2325463-90-8.2.x.patch, failed testing.

Berdir’s picture

Status: Needs work » Reviewed & tested by the community

Ok, I think this looks great now.

I uploaded one patch here as well, but technically, I just removed some lines of code to make it pass. So I think I can still RTBC this, I reviewed this multiple times and tested it pretty extensively.

Btw, another preview bug that I'd love some help with: #2834316: Node preview shows and defaults to "Default" instead of "Full" view mode (will conflict with this)

alexpott’s picture

Version: 8.3.x-dev » 8.2.x-dev

Committed c7da19c and pushed to 8.3.x. Thanks!

I think that the current patch does not have the internal API changes that the old patch in #65 had so might be eligible for 8.2.x too.

  • alexpott committed c7da19c on 8.3.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...
xjm’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/modules/node/src/NodeForm.php
    @@ -21,11 +21,6 @@ class NodeForm extends ContentEntityForm {
    -   * Whether this node has been previewed or not.
    -   */
    -  protected $hasBeenPreviewed = FALSE;
    -
    -  /**
    

    This property is protected so it makes sense to clean it up in 8.3.x as we have, but I think we could also backport the patch to 8.2.x if we provided a small BC layer. Instead of removing the property, mark it as deprecated for removal in 8.3.0 with intstructions to use the form state flag instead.

  2. +++ b/core/modules/node/src/NodeForm.php
    @@ -89,7 +84,7 @@ public function form(array $form, FormStateInterface $form_state) {
    -      $this->hasBeenPreviewed = TRUE;
    +      $form_state->set('has_been_previewed', TRUE);
    

    Here, we would retain both these lines. We would also need to add a small check to the form constructor to set the property from $form_state.

NR to see if this suggestion makes sense. Thanks!

alexpott’s picture

Re-Committed 830934d and pushed to 8.3.x to adjust the issue credits. Thanks!

@aloknarwaria, thanks for your interest in contributing to fix this bug! Next time, in order to get issue credit, please be sure to look at the existing patches and reviews on the issue, and collaborate with others on the solution

  • alexpott committed 26ae527 on 8.3.x
    Revert "Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot,...
  • alexpott committed 830934d on 8.3.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...
claudiu.cristea’s picture

FileSize
5.77 KB
939 bytes

@xjm, yes, it makes sense.

but...

We would also need to add a small check to the form constructor to set the property from $form_state

I'm not sure I understood this part.

xjm’s picture

Sorry, in the form "builder" in D7 lingo (NodeForm::form()), not the class constructor. In order to ensure that the protected property matches the value in $form_state, we need to set it from $form_state, since that can persist across requests. No?

claudiu.cristea’s picture

The last submitted patch, 99: 2325463-99-8.2.x.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 101: 2325463-101-8.2.x.patch, failed testing.

claudiu.cristea’s picture

Status: Needs work » Needs review

@xjm, here's the BC.

Berdir’s picture

Status: Needs review » Needs work
+++ b/core/modules/node/src/NodeForm.php
@@ -22,6 +22,9 @@ class NodeForm extends ContentEntityForm {
    * Whether this node has been previewed or not.
+   *
+   * @deprecated Scheduled for removal in Drupal 9.0.x. Use the form state
+   *   property 'has_been_previewed' instead.
    */
   protected $hasBeenPreviewed = FALSE;
 

we already removed it in 8.3, so this is not correct.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
6.15 KB
568 bytes

Fixed the @deprecated message.

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Ok, looks fine now I think.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 106: 2325463-106.patch, failed testing.

Berdir’s picture

Status: Needs work » Reviewed & tested by the community

Random test fails.. undefined index 'database', multiple times.

  • catch committed 7e087cf on 8.2.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...
catch’s picture

Status: Reviewed & tested by the community » Fixed

That looks fine for bc and the deprecation message is correct now. Committed/pushed to 8.2.x, thanks!

  • alexpott committed 26ae527 on 8.4.x
    Revert "Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot,...
  • alexpott committed 830934d on 8.4.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...
  • alexpott committed c7da19c on 8.4.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...

  • alexpott committed 26ae527 on 8.4.x
    Revert "Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot,...
  • alexpott committed 830934d on 8.4.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...
  • alexpott committed c7da19c on 8.4.x
    Issue #2325463 by claudiu.cristea, swentel, GroovyCarrot, bircher,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.