Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Support For Live Preview / Preview Links #31

Open
edolyne opened this issue Nov 30, 2017 · 30 comments
Open

Enhancement: Support For Live Preview / Preview Links #31

edolyne opened this issue Nov 30, 2017 · 30 comments

Comments

@edolyne
Copy link

edolyne commented Nov 30, 2017

It would be great if there was a way to enable graphql to work via live preview and preview links.

For preview links, potentially leveraging the token as an argument to pass which can then fetch the draft or return an error if the token has expired.

For example:

{
  entry(slug: "test", token: "gO9vhGh4WgicL-bjUr78VKxT7MMw2zqW") {
    title
    id
  }
}

For live preview, I'm not quite sure about a proposed solution as I'm not sure if or how craft stores the live preview.

@markhuot
Copy link
Owner

I’ll need to look into this. The last I checked, at least in Craft 2, preview data was not exposed through the API. It was only sent as POST data directly to the entry template, which we don’t have access to query directly.

@markhuot
Copy link
Owner

After looking in to this more I don't think it's possible for CraftQL, alone, to manage live preview URLs. The way Craft POSTs preview data directly to a template makes it impossible for CraftQL to intercept. You, could, however use the drafts field on and EntryConnection to pull Draft's out of the database to approximate this experience.

@timkelty
Copy link
Contributor

timkelty commented Aug 2, 2018

I looks like Live Preview will become tokenized in the future, which may help: craftcms/cms#1521

@markhuot
Copy link
Owner

markhuot commented Aug 2, 2018

Wider token based authentication for Craft would be a welcome addition indeed. There's a lot of token management and validation I'd love to rip out of CraftQL. This is only half the battle though. Right now live previews are not stored in the database, they're POST data sent to the twig view directly. For CraftQL to be able to read preview data it has to be stored somewhere more permanent. I'm excited for tokens, but there's still a ways to go before this is possible.

All that said, I'll browse through the https://github.com/craftcms/cms issue list and see if moving previews away from POST data is on the horizon and if not I'll open an issue to ask about it.

@timkelty
Copy link
Contributor

timkelty commented Aug 7, 2018

I'll browse through the https://github.com/craftcms/cms issue list and see if moving previews away from POST data is on the horizon and if not I'll open an issue to ask about it.

See @narration-sd's reply to my comment: craftcms/cms#1521 (comment)

@oddnavy
Copy link

oddnavy commented Feb 27, 2019

Is it now possible to access live preview content using the "Tokenized Live Preview authorization" added in craftcms/cms#1521?

Testing locally it seems like if I add a valid X-Craft-Token header, the graphql api attempts to return the twig template for the entry rather than the JSON. If I remove the X-Craft-Token header the request works prefectly.

@markhuot
Copy link
Owner

I’m looking in to this more. I don’t think the X-Craft-Token gets us preview content, that content is still hidden away in a POST request. But I’m working on an update to CraftQL that’ll store that POST data and make it available to GraphQL when the header token is present.

I’m going to re-open this ticket because I have an idea on how to implement this.

@markhuot markhuot reopened this Feb 27, 2019
@narration-sd
Copy link
Contributor

@oddnavy @markhuot Actually, that's just right, Mark. There is no data present for CraftQL to see during live updates.

I've got a full solution about ready to go out the door. It takes much more than you may imagine to do this right -- essential safety, efficiency, and flexibility as well as correctness, and then get it hooked in a useful fashion to SPA or embedded-component sites.

And the data availability side is far from the only issue in providing something others can readily work with in practice.

What I've got has properly had a long gestation. It's called Live Vue, is a Craft plugin which will be priced like yours, and will go out initially for Gatsby, which has its own issues, and drew me into one more reconstitution of the architecture,

I'm doing it this way for all platforms now because I like the design result Gatsby teased me into. Much for the developer using it is simplified, achieving that goal.

This will finally close off a large area of issues which I had solved and refined to a limit, but still concerned I feel rather accurately about how much trouble it would be in terms of handholding, to field the original.

Which has long been working solidly on Vue, where it was first worked up, for many months. The Gatsby effort gets me also to quite solid working with React as well. I'm proceeding through a limited beta as much for community 'chiefs' confidence as much as to see that readiness is there. Not long now.

@narration-sd
Copy link
Contributor

narration-sd commented Feb 27, 2019

...and by the way, I should say thanks again, for CraftQL. This thing started out on Element API, and then had some 'interesting' and potential abilities, but once I saw how clear it was to put together many tasks persons ordinarily want to do, I was sold on your path. It'll be a boon to many, and has already been here.

Element API remains also a part of Live Vue, so both are going to be covered.

@monachilada
Copy link

The tokenized preview feature has now been added to the Craft 3.2 alphas! :) See commit: craftcms/cms@197ce31

From @brandonkelly's description:

Preview requests will have a token query string parameter, and as long as you pass that along to your Element API (or other API) request, the draft content will be returned instead of the main entry content.

This works great in terms of what gets passed to the Live Preview iframe, but I'm struggling to figure out in which way that token needs to be passed through my CraftQL queries or requests to get it to work, or even whether changes need to be made to the plugin itself to enable it. Can you chime in @markhuot?

I'd be happy to help out if I can, just let me know.

@brandonkelly
Copy link
Collaborator

brandonkelly commented Jun 5, 2019

@monachilada Add something like this to your JS:

// Get the preview token from the URL
let m = document.location.href.match(/\btoken=([^&]+)/);
let token = m ? m[1] : '';

// Then forward that on whenever you are sending a CraftQL API request
let url = `...?token=${token}`;
// ...

If you’ve set a custom tokenParam config setting, use that instead of token here.

CraftQL uses element queries internally to fetch its content, so in theory this will just work, and you’ll start seeing draft content sent back when a token is present.

@luke-underwood
Copy link

luke-underwood commented Jun 18, 2019

@brandonkelly 🤔 I'm getting CORS issues when forwarding the token to my SPA. Happening on both 3.2 beta and alpha

Access to fetch at 'https://localhost/api?token=MtitB8j722_Qwo45bYOAdDXKQmfM-hJT'
 from origin 'https://localhost:3000' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
If an opaque response serves your needs, set the request's mode to 'no-cors' 
to fetch the resource with CORS disabled.

I already have allowedOrigins set in my craftql.php config:

<?php

// Set allowed origins for CraftQL
return [
    'allowedOrigins' => [
        'https://localhost:3000'
    ]
];

Works fine when I don't supply the token

EDIT: Now working since I've disabled CSRF protection in general config file:
'enableCsrfProtection' => false,

@brandonkelly
Copy link
Collaborator

brandonkelly commented Jun 18, 2019

@SnarkieDesign I’ve just disabled CSRF validation for preview requests, for the next 3.2 Beta release (craftcms/cms@54831ac).

To get the fix early, change your craftcms/cms requirement in composer.json to:

"require": {
  "craftcms/cms": "3.2.x-dev#54831ac139c954f2499fa81b977fed70803f8792 as 3.2.0-beta.2",
  "...": "..."
}

Then run composer update. And then you should be able to re-enable your enableCsrfProtection config setting.

@narration-sd
Copy link
Contributor

@codyjames
Copy link

@brandonkelly Is the goal here that we won't have to write specific draft queries, but rather we'd just have to include the token in the request and it'd return the associated draft?

@brandonkelly
Copy link
Collaborator

@codyjames Yep that’s the goal. It’s not perfect yet for unsaved/disabled entries (see craftcms/cms#4581), but we’re getting there.

@codyjames
Copy link

codyjames commented Jul 18, 2019

@brandonkelly Okay great, that'll be so awesome. Yeah, I was running into the unsaved/disabled issue. It'd be fantastic if the token made it so the associated entry was considered "enabled" when the token was present.

@brandonkelly
Copy link
Collaborator

Yeah, that’s the general idea of how we could solve this. For unsaved drafts it’s a little more complicated because we also have to let it ignore the draft status for that one element.

@brandonkelly
Copy link
Collaborator

@codyjames Craft 3.2.5 should fix this. Details and info on how you can test here: craftcms/cms#4581 (comment)

@codyjames
Copy link

@brandonkelly Okay, this is working for me. I love it. We're using it for a Next.js app and it is working like a charm.

@brandonkelly
Copy link
Collaborator

Awesome!

@brandonkelly
Copy link
Collaborator

By the way, the official 3.2.5 release is out now, which fixes a bug introduced by my original commit referenced above. So make sure you’re running that.

@Jones-S
Copy link

Jones-S commented Aug 16, 2019

I see there has been some progress on this issue. But what I don't really know:

  1. is it possible to get the newest draft via GraphQL by adding this new token from craft
  2. If so, how does a request look like. I can't my head get around how I should send that token. Also because my apollo client already sends the Bearer token to get access to the graphql service.

Thanks in advance.
cheers

@lukehmu
Copy link

lukehmu commented Aug 21, 2019

I see there has been some progress on this issue. But what I don't really know:

1. is it possible to get the newest draft via GraphQL by adding this new token from craft

2. If so, how does a request look like. I can't my head get around how I should send that token. Also because my apollo client already sends the Bearer token to get access to the graphql service.

Thanks in advance.
cheers

Hi @Jones-S - I'm working on a Vue project where I'm using the CraftQL plugin. I have just got preview working correctly.

As @brandonkelly said here you need to apend the craft token as a query parameter onto the request URL.

Excuse the syntax, I'm using Vue Router to get my query params & I've rewritten it a little to be more readable - it's a ternary in my code:

// grab the API URL from my env file
let postURL = process.env.API_URL

// if my app is requested with the x-craft-preview query param
if (router.currentRoute.query['x-craft-preview']) {
  // update the URL I am going to use in my POST to include the token from the query params.
  const postURL = `${process.env.API_URL}?token=${router.currentRoute.query.token}`
}

Then I send it off via Axios in the regular way (I tried using Axios params, but it didn't work, hence changing the URL)

await axios.post(postURL, {
      query: myGraphQLQuery,
    }).then // other stuff

Lnk to where I do this in my code, hope this helps!

@Jones-S
Copy link

Jones-S commented Aug 22, 2019

@lukehmu
Thanks for your hint.
I myself got some kind of a working thing.
I checked for the x-craft-preview param as well and would then send another graphql query.
Where I would normally fetch a specific post I woudl this time go for a draft:

gql`query getDraft($slug: String!) {
      entriesConnection(slug: $slug) {
        edges {
          node {
            id
            title
          }
          drafts {
            edges {
              draftInfo {
                draftId
                name
              }
              node {
                id
                title
                ... on News {
                  body {
                    totalPages
                    content
                  }
                }
              }
            }
          }
        }
      }
    }`;

Here I fetch drafts and then I would dive into the node (that's where the actual data lies).
After getting that data I would just take the latest of the drafts.

If your option works, this would be great of course and much nicer.
So if I understand correctly:
By sending a graphql request via axios, while passing on the token, I could use the normal query for my page and I would automatically get the latest draft?...

@lukehmu
Copy link

lukehmu commented Aug 22, 2019

@Jones-S I started to go down this route, too.

However, passing the token as a query string in the request URL works perfectly for me.

You can test this easily by previewing your entry, inspecting the preview, looking at the request URL and getting the token. Then hardcode the token in your app and you'll see the draft version! Then refactor to do it dynamically as per my example above.

Cheers.

@Jones-S
Copy link

Jones-S commented Aug 28, 2019

@lukehmu
I see what you are doing and I am just trying to replicate in my setup.
My setup though makes use of apollo (nuxt apollo module). Would you have any hints on how to approach that?

Because normally if someone enters an url it would fetch the page via apollo normally.
Now I added some middleware that checks for the crafts preview token.
If present I want to get the draft data by adding the craft token, but I don't know how I would do that with an apollo call...

--- Edit:
I now built a setup where I just use axios to fetch the data if I see a preview token and otherwise I use apollo.

Anyhow I see that when you create a new entry and there is no saved version of it, the iframe does not have a slug to call making it impossible to fetch the specific data:

<iframe class="lp-preview" src="http://localhost:3000/__temp_7PaFsnSy7PLRPZi2aE0S9GvgTdWjnBuGrqR6?x-craft-preview=ovNpBZAv7Q&amp;token=sjujuqH8_VUTO4LKPB8AIEoPrWgonhsR" frameborder="0"></iframe>
@lukehmu Do you have a solution for that too? I would somehow need a way to send the slug...

@ca-miked
Copy link

ca-miked commented Oct 9, 2019

@Jones-S I am running into the same issue.

@Jones-S
Copy link

Jones-S commented Oct 14, 2019

@ca-miked You might want to check out craft cms 3.3 with graphl in its core. It will change a few things. You can set a headlessMode and in 3.4 there will be preview Targets: craftcms/cms#4520

I hope it will solve the thing about the slugs of newly created posts. I am currently trying out the new setup.

@Jones-S
Copy link

Jones-S commented Oct 14, 2019

@ca-miked indeed it is fixed. With Craft 3.4.x-dev (maybe even before) I can create a new entry, and as soon as I have set a title (and therefore a slug) it will open the preview with the correct slug, even if I haved not saved it before. 👍🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests