Open paulrgn opened 6 years ago
In the same vein of these tight coupling with Web + API, would be cool to get a link to the document in the front end
Wondering if the answer is some kind of internal publish fields in API? Similar to Craft and Wordpress
I'm personally not a fan of the preview link route because I think it creates dangerous precedents in terms of coupling and, most importantly, it makes a lot of bad assumptions.
For Publish to generate a preview URL, it needs to know where the final content is published, so it needs to learn about where Web (or similar) lives. And that assumes that the content will be published to one single location, but the reality is that a single API entry can end up in various different channels. Ultimately, I don't think Publish should know or care where the content will end up.
It assumes that it's even possible to generate a preview URL. What if you're not publishing for the Web and your article will be rendered on a native app or similar? And what if you want to protect the preview URLs behind some sort of authentication system, so that the people can't access content that isn't meant to be published? We're talking about using an external source, so your session with Publish won't be of any use.
Relying on an external service to generate the previews doesn't feel compatible with the level of responsiveness we've added to the UI. I would expect a real-time visualisation of the content as I'm creating it – if I need to wait for a round-trip to an externa server and for the resulting page to be rendered, we sort of lose that effect.
Finally, using an external service means that only content that exists in API can be previewed. If I'm working on content that is only saved locally on my machine, it will be impossible to preview. Sure, we can introduce a "published state" field that flags documents as not ready for public consumption, but again that's assuming a lot. Not only that concept doesn't exist in API, but also I might not want to have that in my API installation at all.
My preference would be for Publish to have the concept of preview templates, whereby users have the ability to supply, in their workspace directories, one or more templates for each of their collections, defining how they are rendered for preview purposes. This could be a faithful representation of the layout used in the live website, or it could be some sort of iPhone simulator chrome that shows how the article will be rendered on a native app.
So presumably that's what we're going for - there'll be a user-themed file and a placeholder for where the content gets rendered. A tutorial on how to do that for the specific collection could be shown in a sidebar if one does not exist, otherwise the sidebar could show the information in a resizable component in a sort of "here's what it'll look like when you're done" similar to a live-view markdown preview in a text editor. Is that the plan?
Template files
I guess, single file won't be enough for the template, we'll need at least the main .html file, style.css file, probably some header/footer images, probably some more. I suggest these are uploaded via FieldFile
, which should be like our current FieldImage
, but allowing arbitrary file uploads, showing filenames instead of thumbnails and not allowing to select existing files. Perhaps FieldImage
should be replaced with FieldFile
with the relevant props.
Uploading templates
I suppose uploading templates separately for each article is a bad idea. So they should be made into a separate collection and selected by reference in the article's Meta
(or Details
?).
Previewing
A "Preview" button at the bottom of the article editing interface will link to /articles/article_id/preview
, where the article preview with the chosen template will be shown.
Conventions
When there are multiple files uploaded, we need to figure out which file to use as the main template file. If there's only one .html
-- use it, or template.html
if there's more than one, show error otherwise.
The article will be injected into the element with id='article'
. We can make it customizable later if necessary.
If this makes sense, I'll break it up into smaller parts and proceed.
Thanks for your input, guys.
I'm not sure that uploading these templates via Publish is the best approach. Where would these files be uploaded to? Currently, Publish will only upload files to API and in the case of document previews we're trying to make Publish as independent as possible from other applications.
Also, bear in mind that the application ships with a compiled bundle. It's not trivial to make it import user-generated files in order to process/compile them. For this reason, I suggest a templating layer that is fully decoupled from the application core.
I envision this as factory functions attached to the global window object, capable of taking a candidate document for a given collection and returning the HTML for a fully-rendered preview. The Publish core is not responsible for rendering these templates, it's just responsible for looking for them on the global scope, calling them and adding the generated output to the preview window.
// Given a candidate document `testDocument` and a collection with path `collectionPath`:
if (window.previewTemplates[collectionPath] {
let previewHTML = window.previewTemplates[collectionPath](document)
renderInAPreactComponent(previewHTML)
}
This approach means that Publish is not prescriptive as to what templating language is used. If users are into React, they can build React components (and a separate transpilation/build pipeline); if they're into something like Pug or Dust.js, they can have that too. As long as templates export the right functions, Publish is fine with it.
As a side note, I would like to use a very similar approach for #299.
I get the point that we need to allow usage of any framework/template language the user likes.
That means that the article document can contain arbitrary markup language rather than just HTML - is that right?
But how does user provide the previewTemplates
function and the templating layer in general? Do we have a mechanism for that?
I started putting a prototype together, but since it involves passing down information from the host application (i.e. the application on user land that includes the @dadi/publish
npm module and calls app.run()
) it's a bit of a pain. Instead, I'm describing the various steps involved.
Users would be asked to place their preview template files inside workspace/preview
in the host application. They should follow the same path as the API collections they relate to – for example, the preview template for /1.0/cloud/articles
should be placed on workspace/preview/1.0/cloud/articles/index.js
.
The entry point for a template is always a JavaScript file called index.js
that calls a global registerPreviewTemplate
function with the name of the collection and the templating function as arguments.
workspace/preview/1.0/cloud/articles/index.js
window.registerPreviewTemplate('1.0/cloud/articles', document => {
return `<html><body><h1>${document.title}</h1></body></html>`
})
The function above could be as complex as needed, including markup for CSS files, additional JavaScript files or even require
calls for templating engine modules (note that any bundling required would be handled by the user, not by Publish).
Here we are augmenting the index.html
file with some global variables that the Preact application will read, such as routes and configuration parameters. In here, we would need to add our global registerPreviewTemplate
function, which in an initial release could be as simple as taking calls and adding them to a global hash map for preview templates.
window.registerPreviewTemplate = function (collection, factoryFn) {
window.previewTemplates[collection] = factoryFn
}
We could also add a function for rendering a preview template, if available. Yes, we could directly access the hash map both for reading and writing, but having getter/setter functions makes it more future-proof.
window.renderPreviewTemplate = function (collection, document) {
return window.previewTemplates[collection] && window.previewTemplates[collection](document)
}
Whenever we need to render a preview for a collection in our Preact components, we can call the globally-available window.renderPreviewTemplate
method, passing it the name of the collection we're trying to preview and the candidate document.
containers/DocumentEdit.jsx
render () {
return (
<div ref={el => this.previewWrapper} />
)
}
renderPreview (document) {
let previewHTML = window.renderPreviewTemplate(document) || 'No preview available'
this.previewWrapper.innerHTML = previewHTML
}
Does that make sense?
/cc @jimlambie @abovedave
The above makes sense to me. There's not going to be a case of not having access to the app which require()s Publish (so any hopes of just passing plain Publish a config file via npx or something are dashed), so that's where local Publish files should go. This might also make it easier to pipeline build.
I'd like to make sure I understand how things operate overall. Correct me if I'm wrong:
There are two issues I can see, though they are not critical and probably acceptable:
Edit: I've had a look at Dust.js and pug, and it looks like using synchronous functions should be ok in most cases. So the issue below is not relevant.
[2. Very likely, there will be some bundling involved, so I suppose the template preview functions should be asynchronous. Thus handling the preview rendering will be trickier than let previewHTML = window.renderPreviewTemplate(document)
.
On the positive side this means that if we aren't too concerned about responsiveness, we can even simply query the web app to get the generated html.]
I'll build a prototype for this.
As an editor I would like to be able to preview articles before publishing so that I can check content presentation. This does not need to be a literal interpretation of the page, just enough to see how the copy is presented with all markdown styling/elements applied. It would also be useful to see at least an approximation of presentation on different devices (perhaps by adjusting the browser window, for example).
Should this be a built-in feature of Publish (like a split screen markdown editor) or should Publish offer the easy plug-in of such a system for developers building a site using our tech?
Discuss.