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

Custom Post Templates #9060

Closed
20 tasks done
kevinansfield opened this issue Sep 26, 2017 · 11 comments
Closed
20 tasks done

Custom Post Templates #9060

kevinansfield opened this issue Sep 26, 2017 · 11 comments
Assignees
Labels
affects:admin Anything relating to Ghost Admin feature [triage] New features we're planning or working on server / core Issues relating to the server or core of Ghost

Comments

@kevinansfield
Copy link
Member

kevinansfield commented Sep 26, 2017

Feature overview

Some blogs have various "post types" that should be rendered differently to standard blog posts.

It's currently possible to do this in a round-a-bout way by using (internal) tags, partials, and conditionals in post.hbs but it's not ideal for certain site structures and can become unwieldy with many post types. When using such a setup the availability of different post types is hidden from authors by overloading the tags feature with theme/site-specific functionality.

With the Custom Post Templates feature theme authors will be able to create individual templates that are recognised as custom templates and exposed in the admin interface for selection by post authors.

Design

UI:

  • display a "Template" section with a select dropdown (only when custom templates are available) after the page/feature checkboxes in the PSM

ghost-custom-template

Technical:

  • custom templates will be identified by the format custom-{{dasherized-name}}.hbs
    • they will be applicable to both posts and pages for now
    • the dasherized-name will be titleized to Dasherized Name for display in the UI
    • later iterations could incorporate front-matter in the templates themselves to provide theme authors a way to define a different name for the UI or to limit the template to posts or pages
  • list of available custom templates for each theme provided as an attribute on the /themes/ and /themes/:name/ endpoints
    • client should ensure this info is cached to avoid unnecessary requests each time a post is opened
    • a differentiation needs to be made between post/page-{{slug}}.hbs templates and "shared" custom templates so that the client dropdown can display only the shared templates
    • available templates should also be returned when uploading/activating a theme
  • if a post-{{slug}}.hbs or page-{{slug}}.hbs template exists and matches the post slug then that template has precedence and will override any custom template selection
    • if the client detects that the post slug matches a template then it should disable the custom template select with a message indicating which template will be used
  • if a post has a custom template selected but that template is not available in the theme then rendering should fall back to the default post.hbs or page.hbs template

/themes/ & /themes/:name/ endpoint format

When fetching multiple or single themes the available templates will be included as an attribute on each member in the collection. Example fetching themes where one theme is installed and it contains a single custom, post, and page template:

GET /themes/

{
    "themes": [
        {
            "active": true,
            "name": "example-theme",
            "package": {
                "name": "Example Theme",
                "version": "0.1"
            },
            "templates": [
                {
                    "filename": "custom-news-bulletin.hbs",
                    "name": "News Bulletin",
                    "for": ["post", "page"],
                    "slug": null
                },
                {
                    "filename": "post-one.hbs",
                    "name": "One",
                    "for": ["post"],
                    "slug": "one"
                }
                {
                    "filename": "page-about.hbs",
                    "name": "About",
                    "for": ["page"],
                    "slug": "about"
                }
            ]
        }
    ]
}

custom-*.hbs templates are automatically assigned to both posts and pages
post-*.hbs templates are automatically assigned to posts, with the slug calculated from the filename
page-*.hbs templates are automatically assigned to pages, with the slug calculated from the filename

The filename attribute acts as a unique identifier.

The name attribute will be auto-generated from the filename by titleizing after removing the custom/post/page- prefix and file extension.

The for attribute will be used by the client to determine when custom templates aren't available due to a slug-based template (post-*.hbs, page-*.hbs) taking precedence.

TODOs

  • server
    • update GScan to recognise and expose custom templates
    • return templates attribute containing list of custom templates and any post/page-* templates as part of the /themes/ and /themes/:name/ responses
    • add custom-template field to the post model
    • allow custom-template to be set during saving
    • render custom template on front-end if specified and template exists
    • finish TODO's from ✨ Custom post templates #9073
    • cache clear happens?
  • client
    • fetch active theme with templates when editor is loaded - do not block rendering
    • display list of available templates in PSM when templates are available
    • disable template select if post slug matches known post/page template
    • test against server implementation + check styles
  • documentation
    • publish new version on themes.ghost.org
      • update templates list on Structure page
      • update templates list and add custom templates description on Templates page
      • update changelog
    • notify Sarah (help.ghost.org)
    • dev blog post
@kevinansfield kevinansfield added affects:admin Anything relating to Ghost Admin feature [triage] New features we're planning or working on server / core Issues relating to the server or core of Ghost labels Sep 26, 2017
@kevinansfield
Copy link
Member Author

kevinansfield commented Sep 26, 2017

Updated to reflect changes in API design:

  • /templates/ endpoint no longer exists (it was strange to have theme-specific details exposed as a "top-level" collection)
  • members returned via the /themes/ and /themes/:name/ endpoints will now contain a templates attribute that lists all custom templates
  • members returned via the /themes/upload/ and /themes/:name/activate/ endpoints will also contain the templates attribute to ensure client caches aren't overridden with empty template lists
  • id attr for templates has been changed to filename - now that we're not dealing with a top-level resource there's no need for simple unique IDs but the full filename could be useful for display purposes

The /themes/ endpoint will not be fetched very often by the client but it may be useful to test response times with many installed themes. If it proves to be a bottleneck we may want to add an ?include=templates param so that we can be more selective in when to fetch template lists.

For reference the client will fetch /themes/ in the following circumstances:

  • when opening the editor if theme details are not already in memory
  • when accessing the "Design" screen

The request made when opening the editor could be optimised if it's possible to query /themes/?active=true so that only the active theme is returned.

kirrg001 added a commit to kirrg001/gscan that referenced this issue Sep 27, 2017
refs TryGhost/Ghost#9060

- extract `templates` object
  - all templates
  - custom templates
kirrg001 added a commit to kirrg001/gscan that referenced this issue Sep 27, 2017
refs TryGhost/Ghost#9060

- extract `templates` object
  - all templates
  - custom templates
kevinansfield added a commit to TryGhost/Admin that referenced this issue Sep 28, 2017
refs TryGhost/Ghost#9060, requires TryGhost/Ghost#9073
- add `{{gh-psm-template-select}}` component
  - allows selection of a custom template for a post if the active theme has custom templates
  - loads themes on render, only hitting the server if not already in the store
  - disables select if post slug matches a `post-*.hbs` or `page-*.hbs` template
- adds `customTemplate` attr to `Post` model
- adds `templates` attr to `Theme` model with CPs to pull out custom vs post/page override templates
- add `.gh-select.disabled` styles to make disabled selects look visually disabled
kevinansfield pushed a commit to TryGhost/gscan that referenced this issue Sep 28, 2017
refs TryGhost/Ghost#9060

- extract `templates` object
  - all templates
  - custom templates
@pascalandy
Copy link

This is really awesome!!

@ErisDS
Copy link
Member

ErisDS commented Oct 4, 2017

I see a couple of small problems with the spec/implementation.

From the spec:

/themes/ & /themes/:name/ endpoint format
When fetching multiple or single themes the available templates will be included as an attribute on each member in the collection.

Doing my best to articulate what the problems are, but they are a little bit circular:

  • The spec says available templates, but only certain templates are returned
  • The logic for which templates lives in gscan, rather than in Ghost - this is a minor issue, but it stood out to me as not feeling right
  • Spec also says templates are returned on each member, but it is only returned on the active theme
  • Implementing this on each member would be a huge overhead as we process this information from gscan
  • I'm not sure where /theme/:name/ gets used, but it has been implemented as a browse method rather than a read method
  • It's a bit strange to me that we need to grant browse to authors and editors, when what we want is read for active themes
  • It feels like what we're missing, fundamentally, is a specific endpoint for getting the active theme, that can include the templates and have specific permissions
  • Ghost definitely needs access to all templates from gscan, and there are some pieces of meta data that would be useful, like a "type" being custom, slug, error, core.
  • Then I think the API format itself ought to be determined by Ghost, rather than by gscan?
  • Finally, I think this is potentially hinting at the fact we may need to, in future, split the theme read logic out of gscan into a module shared by Ghost and GScan...

I don't think these are big problems to address, but I do feel they'd be harder to fix later.

@kevinansfield
Copy link
Member Author

The spec says available templates, but only certain templates are returned

This is an issue with the spec wording, "available templates" was meant as "available custom templates", I'd unintentionally omitted the clarification in places as the spec was only dealing with custom templates.

Spec also says templates are returned on each member, but it is only returned on the active theme

This was a later decision made during implementation, the spec wasn't updated to match.

I'm not sure where /theme/:name/ gets used, but it has been implemented as a browse method rather than a read method

It doesn't get used by the client, I thought it already existed which is why it's in the spec. It can safely be removed if there are no use cases.

It feels like what we're missing, fundamentally, is a specific endpoint for getting the active theme, that can include the templates and have specific permissions

Earlier versions of the spec did have /themes/active/ but using magic names in place of the ID param can get a bit tricky, there needs to be special handling in the client for each occurrence and we need safeguards against themes using reserved names. The only place we have this pattern at the moment is /users/me/.

kirrg001 added a commit to TryGhost/gscan that referenced this issue Oct 6, 2017
@kirrg001
Copy link
Contributor

kirrg001 commented Oct 6, 2017

I'm not sure where /theme/:name/ gets used, but it has been implemented as a browse method rather than a read method
It doesn't get used by the client, I thought it already existed which is why it's in the spec. It can safely be removed if there are no use cases.

Was removed. I thought it's used. See.

Finally, I think this is potentially hinting at the fact we may need to, in future, split the theme read logic out of gscan into a module shared by Ghost and GScan...

I've added a TODO here. We discussed that we don't know where we should filter custom templates at the moment. We have to wait for a second use case. For now, as soon as you receive your theme response from GScan, it will tell you which of your templates are custom.

It's a bit strange to me that we need to grant browse to authors and editors, when what we want is read for active themes

If we add an endpoint to fetch the active theme e.g. /themes/active, we need a special permission in the db (e.g. READ_ACTIVE_THEME), plus the format of the endpoint does not follow a common API pattern. These kind of requests should be fetched via query params /themes/?active=true.

If we keep /themes/:name or use query params, we have to add a hardcoded permission condition to say "only author and editor can read the active theme". It's the same if you would like to fetch a post with the status "scheduled" and you don't want authors to be able to read them. Furthermore, themes don't live in the database. They are not able to redefine permissions in the permissible fn. So the concept of attribute based permissions does only exists for models who redefine permissions in the permissible fn.

I have added a comment to describe the browse theme endpoint a bit better, see.

kevinansfield added a commit to TryGhost/Admin that referenced this issue Oct 10, 2017
refs TryGhost/Ghost#9060, requires TryGhost/Ghost#9073
- add `{{gh-psm-template-select}}` component
  - allows selection of a custom template for a post if the active theme has custom templates
  - loads themes on render, only hitting the server if not already in the store
  - disables select if post slug matches a `post-*.hbs` or `page-*.hbs` template
- adds `customTemplate` attr to `Post` model
- adds `templates` attr to `Theme` model with CPs to pull out custom vs post/page override templates
- add `.gh-select.disabled` styles to make disabled selects look visually disabled
kirrg001 pushed a commit to TryGhost/Admin that referenced this issue Oct 10, 2017
refs TryGhost/Ghost#9060

- add `{{gh-psm-template-select}}` component
  - allows selection of a custom template for a post if the active theme has custom templates
  - loads themes on render, only hitting the server if not already in the store
  - disables select if post slug matches a `post-*.hbs` or `page-*.hbs` template
- adds `customTemplate` attr to `Post` model
- adds `templates` attr to `Theme` model with CPs to pull out custom vs post/page override templates
- add `.gh-select.disabled` styles to make disabled selects look visually disabled
@edarioq
Copy link

edarioq commented Feb 6, 2018

So is this feature available now in production build?

@kevinansfield
Copy link
Member Author

kevinansfield commented Feb 7, 2018

@edarioq Yes, it was released with Ghost 1.13.0

@DINESHKARPE
Copy link

notgeeting
Not getting Template Dropdown

@kevinansfield
Copy link
Member Author

@DINESHKARPE sorry to hear you're having trouble, make sure you have some custom templates available in your theme, the template dropdown won't appear unless you do. If you're still having issues please swing by our Slack channel for support.

@oit63
Copy link

oit63 commented Jan 25, 2019

Hi,brother, is this feature available in ghost v2.10?
I have created a custom-{{dasherized-name}}.hbs in my .../themes/my-ghost-theme/ path, but the menu not shown.

@oit63
Copy link

oit63 commented Jan 25, 2019

I find the reason, seems docker has cache the site, I restart docker container, it works!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects:admin Anything relating to Ghost Admin feature [triage] New features we're planning or working on server / core Issues relating to the server or core of Ghost
Projects
None yet
Development

No branches or pull requests

7 participants