This is great David ! I’ve tested it in my local environment and it works nicely It works exactly like WordPress, you’re redirected to the Frontity preview after clicking the button but the original post remains the same. You even keep the admin bar. Here you have a quick demo of the proof of concept:
We will have to modify the handler for the root path in order to recognize that kind of link.
I said that tokens last one minute (60 seconds) but they actually last 10 minutes long. I changed it for testing purposes and forgot to change it back.
Yes, that was my idea as well. That link needs to work always, not matter if I just edited the content or it was three days ago. For that, it needs to generate a new token each time it’s visited.
Not sure if it helps but if you are authenticated and you fetch the post X endpoint (not revisions) when it hasn’t been published yet, you could get the content for the preview. You even have the featured image or new categories, so it’d be like a common post.
@david, I don’t like that we rely on post._links["predecessor-version"] to fetch the preview post.
I analyzed a big REST API response and half of its size was due to the _links so removing that field means half the size of the response. If we rely on _links for this, people would not be able to remove them.
Do you know if there is a case where http://embedded.local/wp-json/wp/v2/posts/1/revisions/?per_page=1 is not good enough?
The solution proposed is to send both route (which is the link without the /page/x part) along with the query to the getMatch function, but only match the query for handlers that have a flag, for example, matchQuery: true.
The only thing I’ve not managed to figure out is how to set isHome. I’ll work on that and I’ll try to research the implications of adding this system to the current version of source.
The problem is that /?p=ID is not the home, but /?utm_campaign=sale is. We need to be able to differentiate between them.
I see two solutions for the isHome problem:
Add a whitelist of queries that are used in URLs to distinguish between.
Move the logic to the handlers.
Sadly, the second one is not backward-compatible because any person who has a custom handler for the home and it’s relying on data.isHome in his/her theme will be affected if we remove it.
Regarding handlers, I’m thinking about these two options:
Add support for preview to the postType handlers and add a new hander for ?p=ID.
Add a new handler for ?preview=true that handles both cases: slug (/some-post) and query (?p=ID).
As I mentioned in the PR, I think we should change the name of the token. token is too general. It should start at least with frontity_ to avoid collisions and include more information to allow for other types of tokens and versions.
I’m thinking:
Starts with frontity because all Frontity related queries should start like that.
It’s followed by preview because that its purpose.
It’s followed by jwt because that’s the implementation.
It ends with a version, in case we need to increase it at some point while maintaining the old implementation.
So something like this: frontity_preview_jwt_token_v1.
We are going to finally use frontity_source_auth as discussed in Support for auth header in Source packages because it won’t only contain the token, but the whole header
The preview plugin uses a Bearer token in the Authentication header, so if you are using Apache as your web server you would need to add the following lines in your .htaccess file in order to support it:
If you are using a custom handler for the post types, you need to add support for the preview=true query.
This is an example of the code that is required. Please adapt it to your needs.
// Overwrite properties if is a preview and a token is present in the state.
if (query.preview && state.source.auth) {
// Fetch the latest revision using the auth token.
const response = await libraries.source.api.get({
endpoint: `posts/${data.id}/revisions?per_page=1`,
params: state.source.params,
auth: state.source.auth,
});
// Get modified props from revision and merge them with the entity.
const [{ title, content, excerpt }] = await response.json();
const entity = state.source[data.type][data.id];
Object.assign(entity, { title, content, excerpt });
}