Skip to content
This repository has been archived by the owner on Jan 13, 2021. It is now read-only.

Feature: Templated Page Creation In Dashboard #158

Closed
amiller-gh opened this issue Oct 30, 2019 · 7 comments
Closed

Feature: Templated Page Creation In Dashboard #158

amiller-gh opened this issue Oct 30, 2019 · 7 comments
Labels

Comments

@amiller-gh
Copy link
Contributor

amiller-gh commented Oct 30, 2019

Problem

Vapid's data model is restrictive for users that need the ability to create new standalone pages for their site. From the dashboard, users should be able to:

  • Create new pages using page templates
  • Create new collections of content to display on a page.

Proposed Solution

With a few small changes to the internal data model, we can enable these two features while maintaining full backward compatibility with current site structures.

Additionally – if we choose to go this syntax route – we can set up the framework for a breaking change down the line that will reduce the template syntax surface area, simplifying the developer experience even further. These new syntax features can be added now, and deprecation warnings can be added to the current compiler to help encourage migration. Because Vapid is statically analyzable, code mods for migration assistance are also possible.

In order to talk about these changes, we need to talk about it in the context of Vapid's current data model and templates constructs, and then map them to a model that will work in a multi-page world.

Current State of the World

Vapid data model actually already has a somewhat implicit understanding of pages, but it is not surfaced in the dashboard and there is no way for users to create new ones.

This is because with Vapid's current "everything is a {{section}}" data model, most projects end up with sections serving split concerns: some sections are more like site-wide settings, while others only appear in a single html file and operate as page content. We can codify this split responsibility of sections into the data model and template syntax.

For clarity in this proposal, I will be using some new language to better describe both the current framework behavior and the new proposed behavior to enable more granular user editing:

  • Page: A single rendered page on the site. These are currently non-partial .html files in the site root, have no dedicated instance data, are not exposed in the dashboard, and are currently singletons. Often pages will have one or more dedicated sections that serve as their page content.
  • Collection: A collection of Records. These are currently designated by the multi-section {{#section <name> multiple=true}} handlebar tag.
  • Record: An individual item in a Collection. Records may have an optional standalone template to render a single record at a permalinked URL, currently represented by a magically named partial in the site root.
  • Setting: Any handlebar tag – in the general scope or a specific {{section}} – that is shared between more than one Page and/or Record template.

Vapid's current behavior can be described as followed:

  • Vapid Page Sections
    • Each non-partial .html file is a Page.
    • Users to not have the ability to make additional pages. By default, a single page instance is created per non-partial .html file.
    • Pages have autogenerated metadata fields for _permalink (currently the file name), _created_at and _updated_at (file timestamp info) but they are not exposed to the template system, not configurable in the dashboard, and not actually stored in the database.
    • Pages may choose to display multiple Collection Records using the multi-section syntax (see below).
    • The default generatePage singleton will automatically use the default generate Collection singleton (see below).
  • Vapid Collection Sections and Records
    • By default, a single Collection instance is created for every multi-section type discovered in the site's Page templates, or by the presence of a standalone Record template.
    • A developer may define a Record template to render a single Record from the Collection – currently, this is a specially named partial in the root of the site www directory (see Passing information to partials #66 for additional thoughts).
    • Each multi-section can be considered a named Collection of designated sub-page Records.
    • Users may not create additional Collection instances (Ex: I want a second blog collection that uses the same Record templates and overall data model, but is an entirely separate list of posts).
    • By default, zero Record instances are created for each generated Collection.
    • A Record has autogenerated metadata fields _permalink, _created_at and _updated_at that are exposed to the templating system but are not configurable in the dashboard.
    • Users may create additional Records in any Collection. By default, there are zero sub-pages present in a newly created Collection.
  • Vapid Setting Sections
    • Settings are shared configuration values that are meant to span more than one Page or Record. Non-multiple sections that appear in more than one Page or Collection item template are considered a shared site Setting.

Required Data Updates

To meet the stated needs of this issue, the following data changes are required:

  • Create a new pages sqlite table. Structure should be
CREATE TABLE pages (
  id int,
  created_at date,
  updated_at date,
  name varchar(255),
  content json
);
  • Move all multiple=true sections from sections to a new table called collections The multiple column may be dropped from the new collections table. The sortable and multiple columns may be dropped from the old sections table
  • Rename the sections table to settings
  • Rename the records.section_id column to records.collection_id
  • Add pages.template and collections.template columns. Fill them in with the same value that is in pages.name and collections.name. These will let Vapid know what template file to use. The name column will determine the page slug and dashboard page title.

Required Dashboard Updates

To meet the stated needs of this issue, the following dashboard changes are required:

  • Display Pages in the admin dashboard. By default, there is a single page created per template, as is currently done.
  • Allow users to create new Pages, selecting from the project Page templates as data structure options.
  • Allow users to delete Pages, including the default created ones. (this in part addresses Feature Request: Drafts #8. The ability to edit metadata for each Page or Record will finish it off.)
  • Allow users to create new Collection instances, selecting from the available templates. By default one Collection will be created for each unique multi-section discovered in the templates, as is the current behavior.
  • Allow users to delete Collection instances – including the default Collection created.
  • Because there may be multiple of any given Collection, Page dashboard pages must allow users to select the Collection they want to render on the page. Effectively, this means that {{#section collectionName multiple=true}} becomes a kind of directive, where users can select the Collection instance they want to render on the given page. This should be presented in the dashboard as a dropdown of all the Collection instances in the project that match that Collection type (eg: I have two pages on my site for my pets, one for dogs, and one for cats. I can code a single pets.html page, create two instances of it – one called Dogs and one called Cats – and two Collections – one called Dogs and one called Cats – and choose to render the correct collection on each given page.)
  • Consider all values in the general or a named {{#section}} to be a Setting and render it in the dashboard appropriately.

Required Syntax Updates

I have one major outstanding question for this feature is: How do we differentiate between the general scope, and a Page's specific content? Currently, the general scope is – by convention – un-prefixed in vapid templates. This poses a problem, since we need a way for developers to specify certain fields as Page content to be edited in the dashboard under appropreate page side menu item. Do do this, we need a way to differentiate between general site Settings, and Page specific content.

I see three options here:

  1. Choose a reserved word prefix for Page content.

    Glimmer has chosen to prefix all component specific data values with this. We can use this syntax to differentiate Page specific content in the template by prefixing it with this. So:

    {{title}} {{!-- The general site title Setting --}}
    {{this.title}} {{!-- The Page specific title --}}
    {{#section social}}
      {{facebook}} {{!-- The social section facebook Setting --}}
      {{this.facebook}} {{!-- A Page specific facebook value --}}
    {{/section
  2. Require all general references to be prefixed with general.

    Static Site Builds #155 introduces the ability to reference the general scope from any context with {{general.value}}. We could add this as a hard and fast rule and mandate that all general refernces either be prefixed, or wrapped with {{#section general}} (presumably with the ability to reference any other Setting through dot syntax as well, ex: {{social.facebook}})

    Because the concept of Page specific values is new to this change, we can easily apply a code mod on upgrade to prefix all general values with general. to maintain backward compatibility.

  3. Attempt to infer what is general and what is a Page value.

    Presumably, Page values will be unique to their .html file. This means that if the same {{value}} appears across two different .html files (either in a Page or a Record template) we can assume it is a shared setting and belongs in general. This approach has several downsides though:

    • It's ✨magic ✨and relatively opaque – there are no syntax hints as to what context a value will be injected in to.
    • Accidental name conflicts in large projects are totally possible and can cause accidental breakage as you edit .html templates in far-flung sections of the site.

Personal Syntax Recommendation

If I had my 'druthers, I'd pick both solutions 1 and 2. This would mean that:

  • all Settings values must be referenced by {{dot.syntax}}, and

  • all Page values must be referenced via {{this.value}}.

  • Bare values (ex: {{sectionValue}}) would still reference the general scope but log a deprecation warning and support for bare values would be removed at a later date.

  • Most contentiously – the {{#section}} block would log a deprecation warning and instead encourage users to reference settings values via {{dot.syntax}}.

    Many modern templating languages (including the next evolution of Handlebars, Glimmer) have moved away from context unwrapping constructs because it becomes very hard to manage inherited scope – both for the compiler and the developer. With the ability to reference Setting scopes via dot syntax, the {{#section}} block becomes vestigial. (Related, see my comments in Passing information to partials #66 about scope management in templates).

The above four syntax updates would clear up the un-prefixed namespace for use by template helpers that – although not currently used – may be a valuable syntax to reserve for the future.

Optional Changes

These are things that are not entirely necessicary to deliver the features, but are nice to haves since we'd be touching bit swaths of project structure anyway 🙂

  • Deprecate {{#section name multiple=true}} in favor of a {{#collection name as |localName|}} directive, which would be functionally equivalent but more descriptive. The compiler will log deprecation warnings until we're ready to remove {{#section}} entirely, and we can provide an optional code mod to ease migration. This change would not effect the current Collection list rendering for Record templates in the proposal as drafted. Unlike the {{#section}} block, the {{#collection}} block would not unwrap the context so there are no accidental conflicts in the general namespace. The as |localName| syntax is a new Glimmer construct (next gen handlebars) to allow defining new local block scope values.
  • As mentioned in Passing information to partials #66, we can move our magic Record template partials to a new /www/collections directory, remove the _ prefix, and not require a {{#section}} wrapper. This will reduce the (granted, already minimal) framework boilerplate, and align naming conventions if we go with the new {{#collection name}}block mentioned above. We can maintain backward compatibility by falling back to the old magic_partial.html` templates and logging a deprecation.
  • Because {{#section collectionName multiple=true}} is interpreted as a directive type in Page dashboards, it opens up the option to override the order, limit and query settings for any given page. This may be a very handy feature.
  • The ability to select multiple collections of the same type to render on the page. Eg: I want to create a blog page that shows both cats and dogs collections.

Describe alternatives you've considered

None... this was the one data model that could maintain backward compatibility with old sites, while still introducing the ability for ad-hoc user page creation in the site. Open to alternatives!

Additional context

This change would open up the dashboard to a number of new features that have been kicking around for a while:

  • Fully featured, Page and Record metadata panels to provide custom page slugs, social media metadata, page draft/publishing settings, authorship accreditation, code injection, etc
  • Dashboard hosted nav bar management for a user-configured site navbar.
@amiller-gh
Copy link
Contributor Author

Wrote a novel 😅 Didn't know how else to present the idea though! Let me know any thoughts – I think this could be a powerful addition to the platform.

@amiller-gh
Copy link
Contributor Author

amiller-gh commented Oct 30, 2019

This is a simple dashboard side menu concept that could accommodate this change. Would obvs need to mock out a Page and Collection creation flow / settings menu so users can set the page name and select the associated template.
Screen Shot 2019-10-30 at 12 23 25 AM

@amiller-gh
Copy link
Contributor Author

@srobbin, began playing with this over in universe#1 🙂 It feels really good... and is fully backward compatible! You should be able to pull down and play with any existing site (back up the DB first though).

@srobbin
Copy link
Contributor

srobbin commented Nov 3, 2019

@amiller-gh This is really interesting, but I'm a little hesitant off the bat and need some time to digest. The biggest reason is that right now there's a clear separation of concerns: site structure is controlled by those who have access to the templates, and the dashboard is for editing pre-determined content. Giving dashboard users the ability to add pages blurs that line.

I agree that the current model of creating permalinked record pages isn't great, though. Let me think on this one, and I'll reply again shortly.

@amiller-gh
Copy link
Contributor Author

amiller-gh commented Nov 3, 2019

No worries! I think there's a lot of good in that experimental PR even if we don't allow the ability to create multiple pages beyond the default created ones – the ability to reason about page-scoped values is a mental model that I think will resonate with clients more. Ex: You're not editing the "Company Description" section that may or may not be used elsewhere on the site, your editing the "About Us" page. If something is a "Setting" you know it's pretty darn likely to be used elsewhere.

The internal mental model for multiple instances of a page is like a special case of collections (aka multiple=true sections). Allowing users to create multiple standalone pages should be no more complicated rendering standalone pages for collections.

Specific use case example: A user needs to make special one-off donation pages for events with special copy and a dedicated URL for each page. This could be done with a {{collection}}, but if a site needs to have a global setting for multiple donation levels (ex: $1 Supporter, $30 Sponsor, $100 Co-Host) then this page needs a nested collection associated with it. From a user's perspective, grouping these all into "Sections" will not be as manageable since there is no communicated relationship between the "Donation Page" collection and the "Supporter Levels" collection. With the page abstraction this relationship becomes more clear in the UI and in the internal data model.

Beyond the page data model changes I think the most important change in there is swapping out the goatee renderer with Handlebars propper. By leaning on it for AST generation and rendering ( with the appropriate server-side runtime helpers for page creation) we get all of the nice features that come with handlebars templates without the parser maintenance :)

Nifty side effect of the new parser: Partials have full support now, including passing scope, rendering block contexts (layout partials work now!!) and we don't have to maintain any parsing code for hash values and content excaping. By working directly with the AST we can transform templates as they go through the parser (ex: I enforce Vapid's distinction between un-prefixed paths and this. paths, when the default hbs renderer looks both up in the same local scope) and log deprecation warnings (ex: log _ pseudo-private paths as deprecated in favor of the language-native @ prefixed data paths). Restrictive language features in Vapid can be implemented in our AST crawler by throwing custom errors instead of doing it at parse time and assuming a huge maintenance burden.

@amiller-gh
Copy link
Contributor Author

(Also, PR is very experimental still – section sorting / limit directives aren't implemented, tests won't pass, the @data page meta aren't implemented, and there are likely a slew of other bugs – but its a good conversation starter! 😄)

@stale
Copy link

stale bot commented Feb 1, 2020

This issue has been automatically marked as stale after 90 days of inactivity. It will be closed if no further activity occurs.

@stale stale bot added the stale label Feb 1, 2020
@stale stale bot closed this as completed Feb 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants