Confusion between MarkdownPageFormat and Markdown Razor

We’ve been using ServiceStacks markdown features to generate text and email notification text / html. So far the implementation was simple. We pass in pre-defined properties (that were the same for each type of notification). There was no variance (We passed user first name, last name, email, phone, title, etc…).

Now we need to pass a “Property Bag” of information (maybe a dictionary or a list of some custom object) so that different entities generate customized text. But when I try to use the @foreach, it just prints the literal @foreach in the email that is sent out.

Maybe I am misunderstanding the context in which it’s running in. Or the feature that I’m using (MarkdownPagFormat vs Markdown Razor?). Here is how our app is setup:

First note that this might have been built before the new Razor Markdown was released (it was about a year ago, maybe a little less)

In Startup, we register TemplatePagesFeature

Plugins.Add(new TemplatePagesFeature()) // I don't remember why I had to do this, but it seems like this was required, even though the main Startup app doesn't really generate any of this, the Logic project does

(Ignore the obsolescence for now, just know that everything works as expected. I realize it has been replaced with SharpPagesFeature).

In our LENSSX.Logic (core business logic) project, we have a NotificationBuilder.cs, and a Templates folder where we house the HTML / md templates.

NotificationBuilder.cs

namespace LENSSX.Logic.Services
{
    public class NotificationBuilder
    {
        private ScriptContext Context { get; }

        public NotificationBuilder()
        {
            Context = new ScriptContext()
            {
                PageFormats = {new MarkdownPageFormat() },
                VirtualFiles = new FileSystemVirtualFiles("Templates".MapNetCorePath())
            }.Init();
        }

        public string GetTextNotification(Notification notification)
        {
            var textPage = Context.GetPage("EntityNotifications\\EntityNotificationText");

            var text = new PageResult(textPage)
            {
                Args =
                {
                    ["notification"] = notification.Data.Notification,
                    ["triggeringUser"] = notification.Data.TriggeringUser
                }
            }.Result;

            return text;
        }

        public class DataBag
        {
            public string Key { get; set; }
            public string Value { get; set; }
        }

        public IEmailTemplate GetEmailNotification(Notification notification)
        {
            var emailPage = Context.GetPage("EntityNotifications\\EntityNotificationEmail");

            var textEmail = new PageResult(emailPage)
            {
                Args =
                {
                    ["notification"] = notification.Data.Notification,
                    ["triggeringUser"] = notification.Data.TriggeringUser
                }
            }.Result;

            var htmlEmail = new PageResult(emailPage)
            {
                Layout = "Layout",
                PageTransformers = {MarkdownPageFormat.TransformToHtml},
                Args =
                {
                    ["notification"] = notification.Data.Notification,
                    ["triggeringUser"] = notification.Data.TriggeringUser,
                    ["dataBag"] = new List<DataBag>()
                    {
                        new DataBag()
                        {
                            Key = "Test Key 1",
                            Value = "Test Value 1"
                        },
                        new DataBag()
                        {
                            Key = "Test Key 1",
                            Value = "Test Value 1"
                        }
                    }
                }
            }.Result;

            return new NotificationEmailTemplateModel()
            {
                PlainText = textEmail,
                Html = htmlEmail
            };
        }
    }
}

Example MD where I’m attempting to use the dataBag (EntityNotificationsEmail.md)

# Notification Information
A(n) {{ notification.EntityType }} you are subscribed to was {{ notification.ActionVerbiage | otherwise(notification.Action) }}.

## Triggering User
Name: **{{ triggeringUser.FullName }}**  
Phone: **{{ triggeringUser.UserPhone }}**  
Email: **{{ triggeringUser.UserEmail }}**  

## From Agency
Agency: **{{ triggeringUser.AgencyName }}**  
Agency Phone: **{{ triggeringUser.AgencyPhone }}**  

@foreach bag in dataBag {
	@bag.Key: @bag.Value
}


## Link
[Click here to open]({{notification.Url}})

I feel like I’m mixing two different technologies. How can I use the @foreach syntax in my Markdown email / text templates?

Template Pages has been renamed to #Script, the v5.5 release notes shows how to migrate, where you should use SharpPagesFeature instead, e.g:

Plugins.Add(new SharpPagesFeature());

You can use the {{#each}}` block to iterate over a collection, there are lots of examples in LINQ Examples.

Looks like you’re basing off the HTML/Markdown Email Example Use Case.

Note: if you only need to do HTML Emails you can also use the new {{#markdown}} block to render HTML from markdown.

1 Like

Ok gotcha, so what is Markdown Razor?

Markdown Razor was a razor-like + markdown hybrid that’s now obsolete, #Script is far more flexible, powerful and full-featured.

It was essentially a view engine to dynamically generate HTML content using markdown but this is just a feature in #Script that’s can be enabled with output and page transformers using its generically pluggable support of multiple page formats.

Although I find using the markdown filter transformers and {{#markdown}} blocks a more flexible way to embed and render markdown content when needed:

{{ `## Heading` | markdown }}

{{ 'doc.md' | includeFile | markdown }}

{{#markdown}}
### Heading
> Static Markdown Example
{{/markdown }}

{{#capture md}}
### Dynamic Markdown Example
{{#each i in range(1,5)}}
 - {{i}}
{{/each}}
{{/capture}}
{{ md | markdown }}

The http://blog.web-app.io is an example of a Sharp App that builds a multi-user markdown powered blog App in pure #Script i.e. no builds/compilation required, here’s an example of a page with Markdown:

http://blog.web-app.io/posts/markdown-example

Source Code: https://github.com/sharp-apps/blog

1 Like

This is great stuff, thank you!