craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.28k stars 635 forks source link

Live Preview for Categories is broken #10395

Closed denisyilmaz closed 2 years ago

denisyilmaz commented 2 years ago

Description

We have a Craft CMS multi-domain setup where the preview for entries is working as expected, but for categories I got an CORS error:

Access to XMLHttpRequest at 'https://www.example.com/category/example-category?token=3ip6c7LmXFu_7xAQmR9ebuopa9gh6xbW' from origin 'https://cms.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

I found this issue about categories using a legacy LivePreviewController.php https://github.com/craftcms/cms/issues/7851 and this comment https://github.com/craftcms/cms/issues/7851#issuecomment-904620944 by @brandonkelly . I added this to our ProjectModule.php which seems to resolve the CORS issue but the live preview stays broken.

        Event::on(
            LivePreviewController::class,
            Controller::EVENT_BEFORE_ACTION,
            function(ActionEvent $event) {
                if ($event->action->id === 'preview') {
                    Craft::$app->response->headers->set('Access-Control-Allow-Origin', 'https://cms.example.com');
                }
            }
        );

I checked the iFrames and found that the URL das the Live preview is calling for categories has a different format then for entries:

Entry: https://www.example.com/example-entry?x-craft-live-preview=iEgnWb7OCU (preview is working like charm) Category: https://www.example.com/categories/example-category?token=LHatsGHKaNrdrnDDAet2jE0o54dWDrLm (broken)

In the logs i now can find following error:

2022-01-21 10:41:13 [-][1][sd87mmoj5da4tc404n2m8llsvq][error][TypeError] TypeError: Argument 1 passed to craft\web\Controller::renderTemplate() must be of the type string, null given, called in /home/project-folder/vendor/craftcms/cms/src/controllers/CategoriesController.php on line 786 and defined in /home/project-folder/vendor/craftcms/cms/src/web/Controller.php:188
Stack trace:
#0 /home/project-folder/vendor/craftcms/cms/src/controllers/CategoriesController.php(786): craft\web\Controller->renderTemplate()
#1 /home/project-folder/vendor/craftcms/cms/src/controllers/CategoriesController.php(424): craft\controllers\CategoriesController->_showCategory()
#2 [internal function]: craft\controllers\CategoriesController->actionPreviewCategory()
#3 /home/project-folder/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#4 /home/project-folder/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams()
#5 /home/project-folder/vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction()
#6 /home/project-folder/vendor/craftcms/cms/src/web/Application.php(287): yii\base\Module->runAction()
#7 /home/project-folder/vendor/craftcms/cms/src/controllers/LivePreviewController.php(113): craft\web\Application->runAction()
#8 [internal function]: craft\controllers\LivePreviewController->actionPreview()
#9 /home/project-folder/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#10 /home/project-folder/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams()
#11 /home/project-folder/vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction()
#12 /home/project-folder/vendor/craftcms/cms/src/web/Application.php(287): yii\base\Module->runAction()
#13 /home/project-folder/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction()
#14 /home/project-folder/vendor/craftcms/cms/src/web/Application.php(272): yii\web\Application->handleRequest()
#15 /home/project-folder/vendor/yiisoft/yii2/base/Application.php(384): craft\web\Application->handleRequest()
#16 /home/project-folder/web/index.php(22): yii\base\Application->run()
#17 {main}

Additional info

brandonkelly commented 2 years ago

This is a known issue. Categories still use a legacy Live Preview implementation, which only supports previewing across sub-domains, not completely separate domains.

denisyilmaz commented 2 years ago

Yes, I saw that. But what I described is actually on a subdomain (CMS.example.com) where the frontend is delivered by craft via www.example.com. So it's the same domain, but still not working.

brandonkelly commented 2 years ago

Sorry, just took a closer look at the error. It looks like maybe the category group doesn’t actually have a template defined for the site in question. Can you check your category group’s settings?

timkelty commented 2 years ago

@denisyilmaz FWIW, I tested your use case (live preview for categories and entries with a subdomain site) with your module code, and live preview worked fine.

denisyilmaz commented 2 years ago

I have added the index.html to the template and checked the network tab for details what happens when categories preview is loaded. The difference i found is that for entries the iFrame loads the page with a GET request (containing the x-craft-preview-token and token), as the src is set to the preview url: image image

This way angular (which we are using for the frontend) is able to get the window.location.search from that iFrame and use it to forward it to the GraphQL requests.

With categories on the other hand i found out that the preview is created via a POST request without setting the src attribute for the iFrame: image image

Because there is no src on the iFrame, window.location.search has the context of the Craft CMS CP instead of the iFrame. And we are not able to retrieve the token parameter to forward to the GraphQL endpoint.

I understand that Categories is using a legacy Preview functionality compared to Entries. Is this something that can be addressed anyway? We need to have a working preview for our project and its (at this point) disproportionate to switch the category types to be entries.

So basically these questions:

  1. Is this described behavior "by design" of the legacy preview functionality?
  2. Can this issue be addressed within this legacy preview?
  3. When will categories get the new preview functionality?

As we have this issue with several project running on Craft CMS Pro licences it would be great that this issue gets addressed.

timkelty commented 2 years ago

@denisyilmaz I see…so if I'm hearing correctly, this isn't really a multi-site/multi-domain issue at all. Your frontend (in legacy live preview) is breaking because window isn't the context you expect.

That sound correct?

timkelty commented 2 years ago

@denisyilmaz can you try testing with your craftcms/cms composer dependency to dev-fix-document-for-legacy-lp?

denisyilmaz commented 2 years ago

@timkelty i switched to dev-fix-document-for-legacy-lp and checked but nothing changed. I can see seeveral issues that might help finding a working solution/fix:

  1. In the network tab I can see that the assignment of the location fails (unfortunately there are no errors in the console): image
  2. A log of window.location from my frontend reveils that the location is still the Backend, not the site within the iFrame: image
  3. I can see in the source-code that previewUrl has no token assigned. On https://github.com/craftcms/cms/blob/46614167cf8d48b230cbf41333c77ca45e2c5323/src/web/assets/cp/src/js/LivePreview.js#L342 i can see that the token is added in an extra step. This means even if the assignment location.assign(this.previewUrl) would actually work it would not include the previewToken needed to be forwarded to GraphQL
timkelty commented 2 years ago

@denisyilmaz can you composer update and try again (I just pushed a tweak to that same branch). I'm now using the full url (with token) and history.pushState, as we don't want it to actually trigger a reload with that URL.

denisyilmaz commented 2 years ago

Alright. We are getting there. Now the location is set correctly, but i get a CORS error from the API…

image

I will check if getting rid of the subdomain will do the trick here. Or do you have an idea how to solve this? The CORS issue does not appear in the LivePreview for entries.

denisyilmaz commented 2 years ago

after switching to same domain i can now successfully take the token from the iframe and forward it to the /api, but i get the following error. POST Request with gql query to https://www.material.guide/api?token=4y7tyyXQCWjqaZgMrbkQjOb1O6jEBHqO&fetchPolicy=no-cache:

{
  "error": "Request missing required body param"
}
timkelty commented 2 years ago

Hmmmm… @denisyilmaz the legacy live preview relies on the POST data being present to render the preview content, so I'm not sure if there's a way around that. At the point where you're making the request, the preview content is lost. The best you could do is preview the saved version of the entry.

denisyilmaz commented 2 years ago

ok. hm, there is no plan to replace the legacy live preview with the new live preview? Is the reason the planned removal of categories and integration into the "structure" section described in https://github.com/craftcms/cms/issues/1949?

If so, i guess before is now work around a deprecated live preview feature I would take the extra work and migrate the categories into a structure section. is there a way to migrate this automatically? Will there be a migration once Craft CMS 4 is out? When to expect Craft CMS 4 with this new feature?

timkelty commented 2 years ago

@denisyilmaz Craft 4 will have the new (headless compatible) live preview for Categories (and all elements).

Craft 4 is imminent: Beta planned for Q1 2022, Stable in Q2.

Hopefully that is feasible for your project, if not – you'd have have to convert your Categories to a Structure and migrate the data.

What is described in #1949 wont take place until Craft 5. Starting with the Craft 4 release, we will also be on a yearly major release cycle, so you could expect that in Q2 2023.

https://craftcms.com/knowledge-base/preparing-for-craft-4