Grav's Markdown Twig filter line mode

grav

Not interested in explanations? Fast travel to the tl;dr đźš€

In Grav, we can process YAML front matter from Twig templates. This can be very useful for elegantly handling list of items:

---
title: Foo
things:
  - bar
  - baz
---
<ul class="things">
{% for thing in page.header.things %}
  <li>{{ thing|raw }}</li>
{% endfor %}
</ul>

Result:

<ul class="things">
  <li>bar</li>
  <li>baz</li>
</ul>

What if we wanted to add some Markdown?

We could try using Grav’s Markdown Twig filter:

---
title: Foo
things:
  - "**bar**"
  - baz
---
<ul class="things">
{% for thing in page.header.things %}
  <li>{{ thing|markdown }}</li>
{% endfor %}
</ul>

However, it wraps items with <p> tags, which will probably break rendering and is definitely not what we want:

<ul class="things">
  <li><p><strong>bar</strong></p></li>
  <li><p>baz</p></li>
</ul>

I initially worked around it by registering a custom linemarkdown filter to force parsing using Parsedown::line function:

<?php

namespace Grav\Theme;

use Grav\Common\Markdown\ParsedownExtra;

class CustomQuark extends Quark
{
  public function onTwigInitialized()
  {
    parent::onTwigInitialized();
    $this->grav['twig']->twig()->addFilter(
      new \Twig_SimpleFilter('linemarkdown', [$this, 'lineMarkdownFunction'])
    );
  }

  public function lineMarkdownFunction($string)
  {
    $parsedown = new ParsedownExtra(null);
    return $parsedown->line($string);
  }
}

But after looking at how Grav was registering its markdown filter, I noticed markdownFunction had an optional $block parameter, that was then used in Utils::processMarkdown to determine whether to use Parsedown::text (default case) or Parsedown::line (what I wanted).

Fun fact: after digging a bit using git blame, this was actually implemented back in 1.2.0. It just never was documented! Opened a PR for this, but in the meantime… Welp, that was a quick merge.

tl;dr

When processing Markdown variables in Twig using markdown filter, pass false to the filter to use line mode (no wrapping) rather than the default text mode (<p>-wrapped):

---
title: Foo
things:
  - "**bar**"
  - baz
---
<ul class="things">
{% for thing in page.header.things %}
  <li>{{ thing|markdown(false) }}</li>
{% endfor %}
</ul>

Result:

<ul class="things">
  <li><strong>bar</strong></li>
  <li>baz</li>
</ul>

Previous Post Next Post