Lektor: Included markdown file in content files

Created on 18 May 2018  路  7Comments  路  Source: lektor/lektor

This was originally mentioned in the comments of #439, and later I mentioned it again in #532. This is a different topic though, so it gets its own issue.

There is a common desire to in some way include a markdown file by reference in a content field of a contents.lr So instead of a markdown body field whose section in a contents.lr looks like

body:

**Some Markdown here**
and here.

The value for the body field would point to an _external_ .md file. The ability to separate out a (large) field's value can make it easier to maintain that field (it could be outside of a project repo, it could be "hot swappable", etc), and break apart a large contents.lr into more conceptually digestible chunks.

This could be done for a variety of different field types, and potentially arbitrary field types, though I think it makes the most sense for multi-line string-like types.

So how do we accomplish this? An option is to allow for attachments of other various types, detected by file extension, and then allowing the ability to select attachments via the CMS. Another option is to add a way for someone to input a relative path in the CMS / contents.lr that can point to any file. Do we let users define file types in models? Do we force them? There are lots of ways this could be done and discussion is welcomed.

enhancement

Most helpful comment

I'm not talking about treating a contents.lr as a markdown file, but more like the comment that follows the one you cite - including an external file by reference in an contents.lr. My question is, what method is best?

I realized that one way this could work is by virtue of my plugin Jinja Content. For instance, with this, you could actually reference any other file and pull it in with a standard jinja include. E.g.

body:

{% include "my_content.md" %}

This nearly works right now, but I think Lektor would need a couple small updates to https://github.com/lektor/lektor/blob/master/lektor/environment.py, and the plugin would need to add some paths to support "templates" being in the contents dir.

Some points about this approach:

  • This won't require a ton of effort I think. I hardcoded a few things to test it quickly, and the proof of concept works.
  • Standard jinja tagging, which is nice
  • More flexible than the idea of having either markdown in contents.lr directly or included by reference, being mutually exclusive. With this, you could mix and match, or even have multiple includes in the same field.
  • I was originally envisioning some helpers in the CMS. A file select widget for instance to find and select a file. This is just coded in manually, which is kind of less friendly, but on the other hand it enforces working only in certain "template" paths, which is good. That means someone can't reference something outside their project.

If that's the route we take, Lektor itself would only be changed to allow the functionality by the plugin, and the plugin's doing most of the work.

All 7 comments

Hi @nixjdm , can you add some concrete examples
and explain how to use your proposed solution with it?

As I listed here, the typical use cases are:

  1. When the file.md is good enought, not need any extra-formating or local directives: it is important to set it into the sitemap, the CMS must use the file.md as legitimate content, processing it as *.lr file.

  2. When the file.md is incomplete: need eg. extra-formating. In this case another content_hub.lr file need to include the file.md, and, of course, the sitemap must ignore it and use only content_hub.lr.

  3. When the file.md is only a snippet, that will be reused in many content.lr files. In this case the role of the include directive is like a transclusion.

I'm not talking about treating a contents.lr as a markdown file, but more like the comment that follows the one you cite - including an external file by reference in an contents.lr. My question is, what method is best?

I realized that one way this could work is by virtue of my plugin Jinja Content. For instance, with this, you could actually reference any other file and pull it in with a standard jinja include. E.g.

body:

{% include "my_content.md" %}

This nearly works right now, but I think Lektor would need a couple small updates to https://github.com/lektor/lektor/blob/master/lektor/environment.py, and the plugin would need to add some paths to support "templates" being in the contents dir.

Some points about this approach:

  • This won't require a ton of effort I think. I hardcoded a few things to test it quickly, and the proof of concept works.
  • Standard jinja tagging, which is nice
  • More flexible than the idea of having either markdown in contents.lr directly or included by reference, being mutually exclusive. With this, you could mix and match, or even have multiple includes in the same field.
  • I was originally envisioning some helpers in the CMS. A file select widget for instance to find and select a file. This is just coded in manually, which is kind of less friendly, but on the other hand it enforces working only in certain "template" paths, which is good. That means someone can't reference something outside their project.

If that's the route we take, Lektor itself would only be changed to allow the functionality by the plugin, and the plugin's doing most of the work.

I'd be a really useful functionality for me too.
For now I was trying to insert a html snippet. Tried it using a flow block and the template/block/snippet.html as:

{% include {{ this.name }} %}

Didn't work at frist glance. And since I am not good to debug, then I've got frustrated very fast.

@nixjdm: Do you think that should have worked? That I should try to find some thing else that is breaking things up?

Evidently, is a better solution the jinja2 plugin you are planning. But maybe this is a solution for now, inserting html snippets. What do you think?

thanks

I am not proposing any changes to templates, so modifying anything in templates/, like template/block/snippet.html, is not really related to this feature.

Also, none of this works right now. How context and pathing works for content-fields-as-templates needs some work still.

Though, you did make me realize that instead of the above example contents.lr snippet of

body:

{% include "my_content.md" %}

you could also use the attachment machinery as in

body:

{% include this.attachments.first()._id %}

That should work once the context and pathing is improved.

As far as contents inclusion is concerned I'd like to see content automatically picked-up, e.g. given a models that contains

[fields.body]
type = markdown

a file named body.md would be automatically picked up as the content of the field.

Per-request content inclusion should be a separate feature that could be provided either by core or by a plugin.

That feels like too much magic, I would rather have it be explicit

[fields.body]
type = file

Or something different and then provide the rendering based on available renderers or plugins (md, rst)

Or if this is too broad the

[fields.body]
type = markdown-file

That feels like to much magic, I would rather have it be explicit

Yes, it shouldn't be auto-magic. I was thinking it could be a global setting, or per-field:

[fields.body]
type = markdown
include = true

Then we'd have a single content-type for any given format, and the only change needed to the existing types would be to give them a set of extensions to look for where appropriate. Or (also) make it explicit in the model:

[fields.body]
type = markdown
; this could be implied if any of the below are true: 
include = true
; I don't particularly like this one:
include-file = body.md
; I'd rather have the filename implied
include-extensions = md, markdown
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ollieh picture ollieh  路  5Comments

goanpeca picture goanpeca  路  4Comments

geekpete picture geekpete  路  7Comments

lafrech picture lafrech  路  7Comments

tonnydourado picture tonnydourado  路  4Comments