Templated Emails

Topics: Customizing Orchard, General
Mar 25, 2011 at 1:17 PM
Edited Mar 25, 2011 at 1:18 PM

I'm currently working on a Content Notifications module which will allow to customise content types so you get automatically emailed when a particular content type is updated.

Usage scenarios are;

- Receive notification when someone has added a comment

- Certain content types might require validation / moderation and you want to know whenever one has been published / amended

- (Possible application with some extra work) an "email me when someone else posts a comment" checkbox for any commenters

So I've been looking into the messaging system for how emails are currently formatted for things like user registration and authorisation. It involves implementing an IMessageEventHandler to modify the message.

I'm seeing code like this (from Orchard.Users.Handlers.UserMessagesAlteration.cs):

 

                    context.MailMessage.Subject = T("New account").Text;
                    context.MailMessage.Body =
                        T("The user <b>{0}</b> with email <b>{1}</b> has requested a new account. This user won't be able to log while his account has not been approved.",
                            context.Properties["UserName"], context.Properties["Email"]).Text;

 

Then this nice little nugget in Orchard.Email.Services.EmailMessagingChannel.cs:

 

                context.MailMessage.IsBodyHtml = context.MailMessage.Body != null && context.MailMessage.Body.Contains("<") && context.MailMessage.Body.Contains(">");

 

There are some obvious problems here...

1. Customising the emails is pretty unintuitive. You have to dig thru code to find out what string properties are available for a particular message type.

2. There's no support for all the wonderful Orchard features of Shapes, dynamic models, nor MVC helpers.

3. It's perfectly possible for a text-only email to contain both "<" and ">" characters.

4. I might want to include both text and HTML bodies

5. No support for Localisation!

So... what I'm proposing and wanting to discuss are some changes to the messaging system. Obviously some of what I'm suggesting could potentially break modules that currently depend on Messaging or Email, but I'm sure there are ways to maintain compatibility.

Here are my ideas:

- Emails generated from views. I'm thinking these could be located in Views/EmailTemplates alongside DisplayTemplates and EditorTemplates. Names would follow the existing conventions for layouts, shapes, etc. - with overrides based on the message type string. This would require perhaps some rethinking of the format of the string which currently typically looks like: public const string Moderation = "ORCHARD_USERS_MODERATION". Or maybe for that particular email we should be looking for Body.Orchard-Users-Moderation.cshtml for the email body. It would also be possible to have Document.cshtml, Layout.cshtml etc. in EmailTemplates which would define the overall layout of all emails. (Obviously these would omit some usual things like script includes)

- Text forms of emails. These could be in Views/EmailTextTemplates and have the same filenames as the html EmailTemplates. Where a text template was not found (as an example, for a content shape) we could use the normal HTML view and strip out all tags, replacing <br> and <p> with \n and \n\n respectively.

This would solve most of the issues, and bring full localisation support to the emails, along with all the helpers etc.

Finally I'm thinking that:

- Instead of MessageContext.Properties, we need a MessageContext.Shape dynamic object so we can pass whatever items we want into the model, not restricted to just string types.

What are general thoughts on all the above, and are there already plans for anything like this?

Cheers,

Pete

Mar 25, 2011 at 2:39 PM
Wondering if you've considered how the mvcMailer project approaches co-support for email and screen?

http://www.hanselman.com/blog/NuGetPackageOfTheWeek2MvcMailerSendsMailsWithASPNETMVCRazorViewsAndScaffolding.aspx

On Fri, Mar 25, 2011 at 7:17 AM, randompete <notifications@codeplex.com> wrote:

What are general thoughts on all the above, and are there already plans for anything like this?


Mar 25, 2011 at 3:00 PM
Edited Mar 25, 2011 at 3:01 PM

I hadn't seen that project but it looks really neat. Although that article implies it's difficult to do normally in MVC.

In an MVC3 website I recently built I have the following code (in a Controller):

 

      // Build and send confirmation emails
            string viewName = "Confirmation";

            ViewData.Model = model;

            string email;
            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
                ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
                viewResult.View.Render(viewContext, sw);

                email = sw.GetStringBuilder().ToString();
            }

            // Send email
            var subject = String.Format(ConfirmationSubjectTemplate, model.BookingReference);
            var message = new MailMessage(
                "no-reply@mywebsite.com",
                model.Customer.Person.EmailAddress,
                subject,
                email)
            {
                IsBodyHtml = true
            };
            // Send it
            try
            {
                // send success email over smtp
                SmtpClient client = new SmtpClient("LOCALHOST");
                client.Send(message);
            }

 

 

As you can see, it's really easy to build an email from a strongly-typed view. It seems that MvcMailer is basically wrapping that into an extension method but it's not hard. Obviously he adds a whole load of scaffolding stuff which is nice, but I'm more concerned about the ability to hand-code the HTML in a parallel way to my other views.

I think from an Orchard point of view, it's best to look at being able to use the existing Shapes / Clay functionality in templated email views - then maybe add some codegen features to scaffold emails if desired.

Although I'd definitely like to see more integration of T4/MvcScaffolding in Orchard (which would perhaps then pave the way for MvcMailer scaffolding). And also integration of the Orchard command-line into the NuGet Package Manager Console. But that's another story :)

Mar 25, 2011 at 3:24 PM
Edited Mar 25, 2011 at 3:25 PM
justSteve wrote:
Wondering if you've considered how the mvcMailer project approaches co-support for email and screen?

Ah - realised I missed the gist of your comment :)

So MvcMailer has a _Layout.cshtml and a _Layout.text.cshtml for the text-only version, which is another convention that occurred to me - but the problem with that is that using Layout.{Something}.cshtml can have so many other meanings in Orchard due to all the shape conventions and so on. So I personally thought that separating them out into EmailTemplates / EmailTextTemplates folders is more in line with the current DisplayTemplates / EditorTemplates paradigm. But either way, it's the same kind of principal - generating the text versions using a convention.