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.
Comment | File | Size | Author |
---|---|---|---|
#106 | interdiff-101-to-106.txt | 568 bytes | claudiu.cristea |
#106 | 2325463-106.patch | 6.15 KB | claudiu.cristea |
#90 | 2325463-90.patch | 5.84 KB | claudiu.cristea |
Comments
Comment #1
jibran#1510544: Allow to preview content in an actual live environment is in.
Comment #2
swentel CreditAttribution: swentel commentedComment #4
dawehnerJust a quick drive by review.
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?
You can directly camelCase it, if possible.
Comment #5
swentel CreditAttribution: swentel commentedThose 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.
Comment #6
dawehnerWell, this works for sure in core, but how can we ensure horizontal extensibility?
Comment #7
jibranWhy uid is not included here in the destination?
Comment #8
swentel CreditAttribution: swentel commented@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 :)
Comment #9
jibran@swentel thank you for the explanation but I was not asking about the uuid I was asking about uid (
$this->web_user->id()
Here we are checking the user/uid url.
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.
Comment #10
swentel CreditAttribution: swentel commentedah lol :)
Right, so when you are authenticated, Drupal redirects you to user/{id} automatically, that's why destination=user was good here :)
Comment #11
webchickI 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.
Comment #14
Bojhan CreditAttribution: Bojhan commentedLol, wait. So you can engage contextual links on a preview?
Comment #15
webchickOh. 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.
Comment #16
swentel CreditAttribution: swentel commentedrerolled
Comment #18
mgiffordThis 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.
Comment #19
alexpottI think we should use $this->getRequest().
Comment #20
mgiffordOk.. I think this is the right format, so re-rolling:
Comment #21
swentel CreditAttribution: swentel commentedRight, thanks!
Comment #22
tim.plunkettIsn'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?
Comment #23
swentel CreditAttribution: swentel commentedHmm right, in_preview is also being unset at some point. I'll have a look if I can move both to $form_state.
Comment #24
swentel CreditAttribution: swentel commented@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.
Comment #25
mgiffordJust unassigning issues that haven't been developed for a bit in the D8 queue.
Comment #26
swentel CreditAttribution: swentel commentedfirst a reroll - working further now to modernize - apparently, the patch even fails now too, so needs works apparently.
Comment #32
Anonymous (not verified) CreditAttribution: Anonymous commentedSolution whereby destination is handed across in the query
Comment #34
Anonymous (not verified) CreditAttribution: Anonymous commentedAdded function
FormHelper::redirectOptionsPassThroughDestination()
to help usingFormStateInterface::setRedirect()
without losing current query destination.Comment #35
lokapujyaAdding the test from #26, even though I know it's going to fail, so that the test can be discussed.
Comment #36
lokapujyaShould this be chopped off? Is it really possible to have a destination here?
Comment #38
Anonymous (not verified) CreditAttribution: Anonymous commentedThe test in this patch better checks the user journey
Comment #39
lokapujyaOK, 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.
Comment #40
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #41
lokapujyaFor moving the test into PagePreviewTest. By being in a separate test, a whole new Drupal gets installed within simpletest.
Comment #42
Anonymous (not verified) CreditAttribution: Anonymous commentedI 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: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 toTRUE
. This is set when the form loads, and is able to load from thenode_preview
store, on thePrivateTempStoreFactory
(session open with the current user).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 isFALSE
. 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.Comment #43
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #44
lokapujyaSee: https://www.drupal.org/node/2640672
Comment #45
lokapujyaAdding an issue that might be related.
Comment #46
chr.fritschIMHO 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
Comment #48
nicholas.alipaz CreditAttribution: nicholas.alipaz at L.A. BioMed commentedJust tried this on the 8.1.0 release and it worked a treat!
Comment #49
BarisW CreditAttribution: BarisW at LimoenGroen commentedTested it too, and confirming that it solved the issue. Here's a reroll to keep up with head.
Comment #51
BarisW CreditAttribution: BarisW at LimoenGroen commentedComment #52
harings_rob CreditAttribution: harings_rob commentedShouldnt we use short array syntax here?
I have checked the functionality and it seems to work fine. Only the remark I have put above.
Comment #53
yanniboi CreditAttribution: yanniboi at FreelyGive commentedI fixed up the array shorthand, and tested the patch on 8.2.x in the UI. Works for me.
Comment #54
tim.plunkettIs 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.
I would think this would use the \Drupal\Core\Routing\RedirectDestination service.
Comment #55
Anonymous (not verified) CreditAttribution: Anonymous commented@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:
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
.Comment #56
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #57
bircherI 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.
Comment #58
mohit_aghera CreditAttribution: mohit_aghera as a volunteer and at Axelerant commentedComment #60
claudiu.cristea#54.2 should be addressed.
This is wrong. Query item should be under
$options['query']
, so probably:Let's be consistent and use the modern array square brackets syntax.
Use
$this->assertUrl();
instead assertEqual().And I think is still 8.1.x.
Comment #61
chumphrey84 CreditAttribution: chumphrey84 commentedStill 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.
Comment #62
chumphrey84 CreditAttribution: chumphrey84 commentedActually. Above suggestion by me does not work. I have just tried on a development server. Can a core committer advise on resolution scope?
Comment #63
mohit_aghera CreditAttribution: mohit_aghera as a volunteer and at Axelerant commentedFixing issues mentioned by @tim.plunkett and @claudiu.cristea
Comment #65
xjmThe 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!
Comment #66
bircherIt is not that simple.
The injected redirectDestination service always returns a destination.
So to demonstrate why the last patch failed:
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
So I am not sure if we really gain anything.
Comment #67
cilefen CreditAttribution: cilefen commentedDiscussed with @xjm, @alexpott, @amateescu, @fago, @berdir and @plach. We
agreed that this is major because it causes user input to be lost.
Comment #68
xjmAlso 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.
Comment #69
md88 CreditAttribution: md88 commentedIs 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.
Comment #70
claudiu.cristeaHere we go...
EDIT: interdiff is against #57
Comment #71
Anonymous (not verified) CreditAttribution: Anonymous commentedI 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.
Comment #72
claudiu.cristea@GroovyCarrot, I think it's impossible to predict how other multistep forms will behave.
Comment #73
kapetan CreditAttribution: kapetan commentedEDIT: 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.
Comment #74
MichelleThe 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.
Comment #75
pixelmord CreditAttribution: pixelmord at Wunder for Thunder commentedComment #76
BerdirLooks like this overlaps a bit with #2548713: Only one additional new value saved unlimited field and no non-field values are restored after preview .
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.
Comment #77
aloknarwaria CreditAttribution: aloknarwaria at TO THE NEW commentedHi Team,
Please find the my patch to fix the preview issue.
Comment #78
cilefen CreditAttribution: cilefen commentedThere is a non-core change in the patch.
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.
Comment #79
claudiu.cristeaAssigning to fix #76.
Comment #80
aloknarwaria CreditAttribution: aloknarwaria at TO THE NEW commented@cilefen
sorry for my last patch.
Please find the updated patch to fix the issue.
Comment #81
Berdir@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.
Comment #82
yogeshmpawarThanks @Berdir, I have rerolled the patch #70 against 8.3.x branch.
Comment #84
claudiu.cristea@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.
Comment #85
BerdirThere 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.
Comment #87
claudiu.cristeaFixing also #76.2. This is ready for RTBC now.
Comment #89
Berdirthis 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.
Comment #90
claudiu.cristeaImplemented #89.
Comment #91
claudiu.cristeaA 8.2.x version of #90.
Comment #93
BerdirOk, 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)
Comment #94
alexpottCommitted 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.
Comment #96
xjmThis 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.
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!
Comment #97
alexpottRe-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
Comment #99
claudiu.cristea@xjm, yes, it makes sense.
but...
I'm not sure I understood this part.
Comment #100
xjmSorry, 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?Comment #101
claudiu.cristeaOk.
Comment #104
claudiu.cristea@xjm, here's the BC.
Comment #105
Berdirwe already removed it in 8.3, so this is not correct.
Comment #106
claudiu.cristeaFixed the @deprecated message.
Comment #107
BerdirOk, looks fine now I think.
Comment #109
BerdirRandom test fails.. undefined index 'database', multiple times.
Comment #111
catchThat looks fine for bc and the deprecation message is correct now. Committed/pushed to 8.2.x, thanks!