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

RFC: More reliable Edit Button for static sites #94

Open
angeloashmore opened this issue Feb 2, 2022 · 7 comments
Open

RFC: More reliable Edit Button for static sites #94

angeloashmore opened this issue Feb 2, 2022 · 7 comments
Labels
rfc Request for comments

Comments

@angeloashmore
Copy link
Member

angeloashmore commented Feb 2, 2022

Overview

This RFC proposes a way to improve the Prismic Toolbar’s Edit Button by more accurately reporting documents used on a page in static sites.

What and/or who is affected by this RFC?

  • Prismic users using static site generators (such as Next.js, Nuxt, and Gatsby) along with the Prismic Toolbar.
  • Prismic users using any website architecture that separates data fetching and presentation into separate contexts. For example, Next.js’ getServerSideProps runs in a different context than the page’s React component.

Static site generators and architectures that separate data fetching from presentation will be referred to as “static sites” for the rest of the RFC.

What problem does this RFC solve?

The Edit Button appears as part of the Prismic Toolbar. It is designed to give editors and developers a quick way to edit content that appears on a page. It does this by listing Prismic documents that the Prismic Toolbar thinks is on the page with a direct link to their pages in the Writing Room.

The toolbar often shows an inaccurate list of documents on static sites. Usually, this list will only show a subset of the documents used. In some cases, the toolbar does not detect any documents on the page. In that case, the edit button is not displayed at all.

This RFC proposes an improved algorithm that should result in a more accurate document list for most static sites.

Note: This proposal does not attempt to cover 100% of the possible cases. Doing so would require extensive engineering which is not possible at this time. That will happen some time in the future.

What prompted this RFC?

The growing adoption of static site frameworks, especially among Prismic users, has magnified the Edit Button’s limitations. Since more users are encountering this issue, the issue should be fixed.

Are there any related discussions from elsewhere?

I am not aware of any discussions. That’s not to say there have not been any!

Proposal

The problem can be addressed using two strategies:

  1. Allow users to tell the Prismic Toolbar which documents appear on the page.
  2. Detect at least the page’s primary page by detecting a document’s UID in the page’s URL.

Both strategies should be added to the toolbar’s existing algorithm. The new strategies take priority over the existing algorithm in the order they are listed above.

Due to Strategy #2, the Edit Button should produce more positive results without extra effort from a developer.

If a developer wants 100% accuracy, they can manually declare which documents appear on the page with Strategy #1.

Details for each are provided below.

Strategy 1: Allow users to tell the Prismic Toolbar which documents appear on the page

In many cases, a developer programmatically knows which documents are used on a page. Content from the Prismic API includes a document ID. These document IDs can be included in a page’s HTML which can be read by the Prismic Toolbar.

This can handle cases where secondary content is queried in areas like a page’s layout. This could include a Navigation document in the header, or a Settings document to control metadata. The IDs of these documents can easily be fetched.

Developers can declare which documents appear on the page through a special Prismic-specific <meta> element named prismic-documents in the page’s <head>.

<meta name="prismic-documents" content="U0V72AEAAE4netWJ,YfmNTRIAAC8AiccC" />

Multiple instances of this <meta> element can be present to ease the declaration process. For example, a developer can add a <meta> element on a global level, such as in a Layout component, and further down the tree in a specific component, such as in a Blog Post component.

<head> manager APIs built in to frameworks can facilitate this process. Next.js, for example, provides a <Head> component to control elements in the <head>.

The toolbar will read these document IDs to produce the list displayed in the Edit Button’s interface.

Communication Logic

The Prismic Toolbar iframe cannot read these elements directly due to security limitations. Instead, it needs to communicate with the page via a messaging channel and an on-page script. The toolbar is added to a site using a <script> element which allows us to embed the communication logic outside the iframe.

Code within the toolbar’s bootstrap script should be kept minimal. It should only listen for a request from the toolbar’s iframe to fetch a list of document IDs. It should respond with the list using a minimal amount of code. All other logic, such as parsing the document ID list, should be done within the iframe.

To ensure the toolbar’s list is always up to date and accounts for any lazily added document IDs, some form of pushing updates to the toolbar should be included. This could be implemented with a MutationObserver or polling.

Limitations

  1. Requires a user to change their code to declare used documents. This must be documented and taught.

Strategy 2: Detect at least the page’s primary page by detecting a document’s UID in the page’s URL

In many cases, a page’s URL contains a document’s unique ID (UID). Of those cases, the UID is usually the right-most segment of a URL path.

/about-us
/people/jane-doe

In the above cases, about-us and jane-doe may be document UIDs. The toolbar can read the URL and determine if those segments have a matching document.

UIDs are only unique within a Custom Type, so the UID must be queried against every Custom Type in the site’s Prismic repository. The toolbar can query the Prismic repository’s /api/v2 endpoint to retrieve a list of its Custom Types. Note that this query may require an access token, which the toolbar currently does not require.

Limitations

  1. Making the /api/v2 request may require an access token. Users would need to provide this somehow in the browser, potentially negating the purpose of the access token.
  2. If a Prismic repository uses the same UID in more than one Custom Type, false positives can occur. There is no method to determine which Custom Type takes priority.
  3. Custom URLs that do not include a UID cannot be matched to a document. For example, a home page’s URL is / which does not have any segments to query against.
  4. Custom URLs that transform UIDs cannot be reliably matched to a document. For example, a URL that appends a document’s ID to its UID would need some logic to separate the UID (e.g. /blog/hello-world-YfmNTRIAAC8AiccC.
  5. False positives can occur if a custom URL is used that happens to match with a document’s UID. For example, a URL of /about may match with a document even if that document is not used on the page.

Rejected strategies

The two above strategies were selected for their relative ease of implementation and possible timely deployment. The following ideas were rejected within this scope:

  • Automatically build the list of document IDs described in Strategy 1 by spying on Prismic clients (or, in Gatsby’s case, GraphQL APIs).
  • Provide Prismic with a site’s Route Resolver object or Link Resolver function to resolve a document’s URL on the server. This can be used to compare a page’s URL to a list of resolved document URLs.

These ideas may be revisited during a more extensive Prismic Toolbar refresh.

How to provide feedback

Anyone can provide feedback on anything addressed in this RFC. To provide feedback, please comment on this issue.

While everything is open for feedback, the following questions are most important:

  1. Do you see any other limitations or challenges with Strategy 1?
  2. Do you see any other limitations or challenges with Strategy 2?
  3. Should both strategies be implemented?
  4. Is there another strategy that should be considered (keeping in mind this is potentially a short-term solution)?

Thanks! 🙂

@angeloashmore angeloashmore added the rfc Request for comments label Feb 2, 2022
@a-trost
Copy link

a-trost commented Feb 2, 2022

Thanks for writing this up @angeloashmore !

For strategy 1, is there a decent way we could add it to our kits? Is it possible that by using a in React, we can use Context to inform a component that a certain document has been requested? Then automatically build out that information on the page?

I don't think this would work with either Gatsby or Next's data fetching methods but wanted to raise the idea.

Or a function that takes in your Prismic response and pulls out all the IDs included? We might get some false positives if we're not careful, but any way we can make it as simple and foolproof to implement would help with Strategy 1.

Overall I think Strategy 1 is the best, least prone to mistakes, and if we're already teaching them how to do things like installing previews in Next, this doesn't seem like a ton more work. Still, if we can automate it in any way, that'd be a win.

Strategy 2 as a fallback makes sense as a second-best option.

@arnaudlewis
Copy link
Member

The method that I experimented with Gatsby was simply to resolve all links between documents and url at build time on the kit level. Since you programmatically build your pages along with their url, you can build the edit button pretty accurately

@phillysnow
Copy link

Strategy 1 seems the most robust, for me the number of issues that we might not even see yet with strategy 2 makes it much less viable (Also scary for me as a support agent).

Can I ask why you decided against the route resolver strategy, other than the time to wait for updating the route resolver functionality to match all the use cases of the linkresolver?

@arnaudlewis
Copy link
Member

Strategy 1 is more robust but it's also what we avoided and actually got rid of from the first version. Having to do it manually but if it's an extra step not mandatory to get better accuracy it's probably for the best. But still at build time it's probably the better timing for a static app

@MarcMcIntosh
Copy link
Contributor

There's un-merged PR that attempted strategy 2
https://github.com/prismicio/prismic-toolbar/pull/79/files
src/iframe/prediction.js

For strategy one,

Requires a user to change their code to declare used documents. This must be documented and taught.
I guess a meta tag would work, or a data attribute,
I think a data-attribute would be easier for the developer to add, like <div data-prismic="documentId">....</div>

@lihbr
Copy link
Member

lihbr commented Feb 3, 2022

My vision of the issue when we discussed the above with Angelo:

First, the goal is to support the main document prediction for our main frameworks (Nuxt, Next, Gatsby) without/with very minimal user action required (at most, upgrading to the latest version of a kit). Getting non-main document predictions to work is purely a bonus and not something we particularly aimed for when considering that reduced scope.

Second, I don't want our SDKs to be (heavily) impacted in order to propose more accurate predictions, two reasons for that:

  • We explored with Angelo how to achieve such results on all 3 frameworks: all relied on hacky/unconventional patterns that would have impacted their relative SDKs, and in some cases, user's querying workflow.
  • The above solutions are deemed to be temporary and short term. Therefore I don't want kits to be impacted with code that is meant to be deprecated in a near future.

I strongly believe that a long-term solution to the edit button issue involves fixing the route resolver and having the Prismic repository be aware of it (by defining it within it perhaps/uploading it from code/Slice Machine). Such long-term solution involves development to happen on the product side, which the product team doesn't have bandwidth for as of talking (afaik, cc @phillysnow), therefore the above solutions can also be seen as more realistic solutions that we can roll out within a short time frame without relying on the product team.


To me, the two strategies propose an interesting synergy:

  • Strategy 2 (guessing the main document from page context) will enable the edit button for most uses cases and help grow awareness about it;
  • Strategy 1 (meta-tagging documents) will allow users who want to invest more in the edit button (after seeing it working with strategy 2) to have full accuracy in their predictions.

This could be framed in the documentation in that fashion:

By default, the edit button tries its best to find active documents on the page. However, the edit button might not succeed at finding active documents every time or be fully accurate.
If you want the edit button to have better accuracy, you can provide active documents to the edit button by appending appropriate meta tags on your page.


Regarding strategy 1:

If we want to allow multiple meta tags, then we need a convention for defining the main document (otherwise we could just have said "first in the array is the main document"), except if we want to pass all those documents to the existing sorter ( https://github.com/prismicio/prismic-toolbar/blob/master/src/iframe/prediction.js#L10-L25, which comes with limitations/accuracy impact).

Random suggestions (no real preferences):

Separate meta tag (clean but maybe not convenient)

<meta name="prismic-main-document" content="U0V72AEAAE4netWJ" />
<meta name="prismic-documents" content="U0V72AEAAE4netWJ,YfmNTRIAAC8AiccC" />

Data attribute (quite dirty IMO)

<meta name="prismic-documents" data-main="U0V72AEAAE4netWJ" content="U0V72AEAAE4netWJ,YfmNTRIAAC8AiccC" />

Prefix (I think it's the best compromise between clean & convenience)

<meta name="prismic-documents" content="main:U0V72AEAAE4netWJ,YfmNTRIAAC8AiccC" />

JSON (while still supporting CSV format, too unconveninet I think)

<meta name="prismic-documents" content='{"main": "U0V72AEAAE4netWJ", "others": ["YfmNTRIAAC8AiccC"] }' />

Strategy 1 also suffers from the access token limitation of strategy 2 as the toolbar would need to get summaries/titles of those documents which are currently supplied by the /toolbar/predict endpoint: https://github.com/prismicio/prismic-toolbar/blob/master/src/iframe/prediction.js#L11


Regarding strategy 2 limitations:

  1. If a Prismic repository uses the same UID in more than one Custom Type, false positives can occur. There is no method to determine which Custom Type takes priority.

I think strategy 2 should rely on the UID and the document H1 if any, we could perform a full text search using the H1 on the API when the UID is not found or ambiguous.

  1. Custom URLs that do not include a UID cannot be matched to a document. For example, a home page’s URL is / which does not have any segments to query against.

To deal with home pages, I think we could handle the / route differently, by looking for singletons named "index" or "home", or documents with UID of "home" or "index". Full text search using the document H1 might also help here.

  1. Custom URLs that transform UIDs cannot be reliably matched to a document. For example, a URL that appends a document’s ID to its UID would need some logic to separate the UID (e.g. /blog/hello-world-YfmNTRIAAC8AiccC.

Let's not support that use case yeah.

  1. False positives can occur if a custom URL is used that happens to match with a document’s UID. For example, a URL of /about may match with a document even if that document is not used on the page.

Same, let's keep those as false positives.


Regarding the shared access token limitation:

Again, ideally, this could be bypassed by the product but just providing the data attributes of strategy 1 and the document URL and H1 from strategy 2 to the /toolbar/predict endpoint which could then perform the prediction/computation server side. But I'm not sure we want to touch that endpoint ourselves: https://github.com/prismicio/wroom/blob/main/subprojects/preview-prediction/app/src/main/scala/Prediction.scala

I'm not a fan of asking users to add their access token as a data-token attribute on the toolbar script. This could do it although. Or we could just not support private repositories (@Duaner do we have a ratio/idea of the percentage of private vs public repo?)

@angeloashmore
Copy link
Member Author

angeloashmore commented Feb 9, 2022

Thanks for the comments everyone!

@arnaudlewis Performing this computation at build-time is similar to the idea of providing a Route Resolver to Prismic. It allows a manifest of URLs mapped to their documents which could be saved to a public file or sent to Prismic. @lihbr and I discussed this idea, but for reasons stated in the above comment, it would require specific implementations for each framework. Someone using 11ty, for example, would not be able to take advantage of the improved Edit Button accuracy until there was an official integration.

The manifest concept is still a great idea which, theoretically, can be achieved for all users through an improved Route Resolver integration.

@lihbr I agree with everything said there. The two ideas proposed here are solutions we can implement quickly and scale to all sites. The long-term solution of improving and uploading a Route Resolver will provide a better experience overall. Strategy 1 (declaring documents with <meta> elements) is still valid and builds upon what an improved Route Resolver solution could provide.

If we want to allow multiple meta tags, then we need a convention for defining the main document

I think a separate meta element is the simplest. It's a little more verbose than other ideas like main: and data-main, but it should be the easiest to construct and is the most straightforward to understand. (Imagine needing to prepend main: vs passing just document.id to the element)

<meta name="prismic-main-document" content="U0V72AEAAE4netWJ" />
<meta name="prismic-documents" content="U0V72AEAAE4netWJ,YfmNTRIAAC8AiccC" />

Strategy 1 also suffers from the access token limitation of strategy 2

You're right; both solutions rely on access to /api/v2. I agree that requiring an Access Token in the HTML/JS to support the toolbar is not a good idea. It defeats the purpose of having the Access Token.

It is not a good idea to exclude private repositories as well (I believe doing this would also break when using Releases). We would be recommending users make all their content public which may not be acceptable for some users.

Since the Edit Button only appears for logged in users, could we have a special Toolbar-specific endpoint that is authenticated via cookies rather than Access Token? We would need to come up with a spec for these new endpoints.

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

No branches or pull requests

6 participants