How to render a snippet inside a custom KirbyTag without `<pre>` tags appearing?

I have a snippet for an article link. That snippet basically creates a box with the title, a content excerpt, and image of a page.

I want to be able to use that snippet inside a KirbyTag aswell. For example, (article: my-article-id) would render that snippet and use the given article for the page. Here’s how the tag looks:

'tags' => [
	'article' => [
		'html' => function ($tag) {
			$posts = site()->children()->template('blog')->children()->template('post');
			$post = $posts->find($tag->value);

			if ($post) {
				return snippet('article', ['page' => $post], true);
			} else {
				return '';
			}
		}
	]
]

The problem is that I have indentation in my snippet and the resulting markup also has it. Then, Kirby parses that markup and renders indented code as a code block with <pre> tags. This breaks the markup. How would I avoid that?

hmmm, that shouldn’t be happening afaik :thinking:

What is inside your article-snippet?

Nothing special:

<a href="<?= $page->url() ?>">
	<div class="article flex justify-between my-8 border">
		<div class="px-6 py-6">
			<h5 class="mb-2 font-bold text-base"><?= $page->title() ?></h5>
			<p class="text-sm"><?= $page->text()->excerpt(140) ?></p>
		</div>

		<?php if (($image = $page->image()) && $image = $image->thumb(['width' => 500])): ?>
			<div class="flex-shrink-0 w-1/3 bg-cover bg-center" data-src="<?= $image->url() ?>" mb-image></div>
		<?php endif ?>
	</div>
</a>

Everything after the blank line is rendered in <pre>. However, the same thing happens if I replace all that with just:

    <strong>test</strong>

If I remove the leading 4 spaces, there is no <pre> tag. Therefore, the problem is not in the snippet.

I just checked. In a recent project I did it like this to make snippets work in custom tags. Don’t remember the exact reasons any more but perhaps this helps.

'tags' => [
    'slider' => [
        'html' => function($tag) {
            $files = new Files();

            foreach (Str::split($tag->value) as $path) {
                if ($file = $tag->file($path)) {
                    $files->append($file);
                }
            }

            $string = snippet('kirbytags/slider', [
                'images' => $files,
                'style' => 'for-article',
            ], true);

            // replace line breaks with spaces to silence the markdown parser
            return str_replace(PHP_EOL, ' ', $string);
        }
    ]
]
2 Likes

I think that removing the newlines makes it work. Whitespace plays an important role in code formatting for markdown. In the snippet that I posted above if I remove the empty new line, the code works.

The problem with that is if you have a <pre> tag in the snippet that you do want to appear, removing the newlines will effectively change the content. Removing newlines might have other side effects, I suppose. I think Kirby should handle this case itself so there are no surprises like that.

Or perhaps we should get a Html::stripWhitespace() method that removes whitespace between tags. However, parsing HTML is always a bad idea and I don’t think that would be the best option.

@texnixe should I open an issue?

Yes, why not, but I think its rather a problem with Parsedown: Deactivate Markdown - #10 by texnixe

That was my assumption as well. But you can make use of the kirbytext:before and kirbytext:after hooks to overcome this issue.

'hooks' => [
    'kirbytext:before' => function ($text) {
        $tags = [
            'article',
        ];

        return preg_replace_callback('!\((' . implode('|', $tags) . ')+:.*?\)!is', function ($match) {
            return '<!--' . substr($match[0], 1, -1) . '-->';
        }, $text);
    },
    'kirbytext:after' => function ($text) {
        $tags = [
            'article',
        ];

        return preg_replace_callback('!<\!--(' . implode('|', $tags) . ')+:.*?-->!is', function ($match) {
            return kirbytags('(' . substr($match[0], 4, -3) . ')');
        }, $text);
    },
]
1 Like