Messages

Sending Single plain-text Emails

Messages lets you craft and send emails to a single contact which can be sent immediately or saved as a draft so you can review the HTML rendered email and send later.

It also lists all available emails that can be sent which are any APIs that inherit the CreateEmailBase base class which contains the minimum contact fields required in each email:

public abstract class CreateEmailBase
{
    [ValidateNotEmpty]
    [Input(Type="EmailInput")]
    public string Email { get; set; }
    [ValidateNotEmpty]
    [FieldCss(Field = "col-span-6 lg:col-span-3")]
    public string FirstName { get; set; }
    [ValidateNotEmpty]
    [FieldCss(Field = "col-span-6 lg:col-span-3")]
    public string LastName { get; set; }
}

Plain text emails can be sent with the SimpleTextEmail API:

[Renderer(typeof(RenderSimpleText))]
[Tag(Tag.Mail), ValidateIsAdmin]
[Description("Simple Text Email")]
public class SimpleTextEmail : CreateEmailBase, IPost, IReturn<MailMessage>
{
    [ValidateNotEmpty]
    [FieldCss(Field = "col-span-12")]
    public string Subject { get; set; }

    [ValidateNotEmpty]
    [Input(Type = "textarea"), FieldCss(Field = "col-span-12", Input = "h-36")]
    public string Body { get; set; }
    public bool? Draft { get; set; }
}

Email UI

Which are rendered using the Vue AutoForm component from the API definition where the SimpleTextEmail Request DTO renders the new Email UI:

Which uses the custom EmailInput component to search for contacts and populates their Email, First and Last name fields.

The implementation for sending single emails are defined in EmailServices.cs which uses EmailRenderer.cs to save and send non draft emails which follow the pattern below:

public EmailRenderer Renderer { get; set; }

public async Task<object> Any(SimpleTextEmail request)
{
    var contact = await Db.GetOrCreateContact(request);
    var viewRequest = request.ConvertTo<RenderSimpleText>().FromContact(contact);
    var bodyText = (string) await Gateway.SendAsync(typeof(string), viewRequest);
    
    var email = await Renderer.CreateMessageAsync(Db, new MailMessage
    {
        Draft = request.Draft ?? false,
        Message = new EmailMessage
        {
            To = contact.ToMailTos(),
            Subject = request.Subject,
            Body = request.Body,
            BodyText = bodyText,
        },
    }.FromRequest(request));
    return email;
}

Live previews are generated and Emails rendered with renderer APIs that inherit RenderEmailBase e.g:

[Tag(Tag.Mail), ValidateIsAdmin, ExcludeMetadata]
public class RenderSimpleText : RenderEmailBase, IGet, IReturn<string>
{
    public string Body { get; set; }
}

Which renders the Request DTO inside a #Script email context:

public async Task<object> Any(RenderSimpleText request)
{
    var ctx = Renderer.CreateScriptContext();
    return await ctx.RenderScriptAsync(request.Body,request.ToObjectDictionary());
}

Sending Custom HTML Emails

CustomHtmlEmail is a configurable API for sending HTML emails utilizing custom Email Layout and Templates from populated dropdowns configured with available Templates in /emails:

[Renderer(typeof(RenderCustomHtml))]
[Tag(Tag.Mail), ValidateIsAdmin]
[Icon(Svg = Icons.RichHtml)]
[Description("Custom HTML Email")]
public class CustomHtmlEmail : CreateEmailBase, IPost, IReturn<MailMessage>
{
    [ValidateNotEmpty]
    [Input(Type = "combobox", EvalAllowableValues = "AppData.EmailLayoutOptions")]
    public string Layout { get; set; }
    
    [ValidateNotEmpty]
    [Input(Type = "combobox", EvalAllowableValues = "AppData.EmailTemplateOptions")]
    public string Template { get; set; }
    
    [ValidateNotEmpty]
    [FieldCss(Field = "col-span-12")]
    public string Subject { get; set; }

    [Input(Type = "MarkdownEmailInput", Label = ""), FieldCss(Field = "col-span-12", Input = "h-56")]
    public string? Body { get; set; }
    public bool? Draft { get; set; }
}

Custom HTML Implementation

It follows the same pattern as other email implementations where it uses the EmailRenderer to create and send emails:

public async Task<object> Any(CustomHtmlEmail request)
{
    var contact = await Db.GetOrCreateContact(request);
    var viewRequest = request.ConvertTo<RenderCustomHtml>().FromContact(contact);
    var bodyHtml = (string) await Gateway.SendAsync(typeof(string), viewRequest);

    var email = await Renderer.CreateMessageAsync(Db, new MailMessage
    {
        Draft = request.Draft ?? false,
        Message = new EmailMessage
        {
            To = contact.ToMailTos(),
            Subject = request.Subject,
            Body = request.Body,
            BodyHtml = bodyHtml,
        },
    }.FromRequest(viewRequest));
    return email;
}

Which uses the RenderCustomHtml to render the HTML and Live Previews which executes the populated Request DTO with the Email #Script context configured to use the selected Email Layout and Template:

public async Task<object> Any(RenderCustomHtml request)
{
    var context = Renderer.CreateMailContext(layout:request.Layout, page:request.Template);
    var evalBody = !string.IsNullOrEmpty(request.Body) 
        ? await context.RenderScriptAsync(request.Body, request.ToObjectDictionary())
        : string.Empty;

    return await Renderer.RenderToHtmlResultAsync(Db, context, request, 
        args:new() {
            ["body"] = evalBody,
        });
}

CreatorKit.Extensions

Any additional services should be maintained in CreatorKit.Extensions project with any custom email implementations added to CustomEmailServices.cs.

Sending HTML Markdown Emails

MarkdownEmail.cs is an example of a more user-friendly custom HTML Email you may want to send, which is pre-configured to use the basic.html Layout and the empty.html Email Template to allow sending plain HTML Emails with a custom Markdown Email body:

[Renderer(typeof(RenderCustomHtml), Layout = "basic", Template="empty")]
[Tag(Tag.Mail), ValidateIsAdmin]
[Icon(Svg = Icons.TextMarkup)]
[Description("Markdown Email")]
public class MarkdownEmail : CreateEmailBase, IPost, IReturn<MailMessage>
{
    [ValidateNotEmpty]
    [FieldCss(Field = "col-span-12")]
    public string Subject { get; set; }

    [ValidateNotEmpty]
    [Input(Type="MarkdownEmailInput",Label=""), FieldCss(Field="col-span-12",Input="h-56")]
    public string? Body { get; set; }
    public bool? Draft { get; set; }
}

As defined, this DTO renders the form utilizing a custom MarkdownEmailInput rich text editor which provides an optimal UX for authoring Markdown content with icons to assist with discovery of Markdown's different formatting syntax.

Template Variables

The editor also includes a dropdown to provide convenient access to your Template Variables:

The implementation of MarkdownEmail just sends a Custom HTML Email configured to use the basic Layout with the empty Email Template:

public async Task<object> Any(MarkdownEmail request)
{
    var contact = await Db.GetOrCreateContact(request);
    var viewRequest = request.ConvertTo<RenderCustomHtml>().FromContact(contact);
    viewRequest.Layout = "basic";
    viewRequest.Template = "empty";
    var bodyHtml = (string) await Gateway.SendAsync(typeof(string), viewRequest);

    var email = await Renderer.CreateMessageAsync(Db, new MailMessage
    {
        Draft = request.Draft ?? false,
        Message = new EmailMessage
        {
            To = contact.ToMailTos(),
            Subject = request.Subject,
            Body = request.Body,
            BodyHtml = bodyHtml,
        },
    }.FromRequest(viewRequest));
    return email;
}