This project is read-only.

Sending email with attachment

Topics: Administration, Core, Customizing Orchard, General, Localization, Troubleshooting, Writing modules, Writing themes
Dec 21, 2012 at 2:44 PM

I have contact form and need to send email with some attachments. For that I implemented IMessageEventHandler interface like following:


public class MailActionEventHandler : IMessageEventHandler
        const string Picture = @"C:\Users\Public\Pictures\Sample Pictures\desert.jpg";

        public void Sending(MessageContext context)
            //We know that the type will be "ActionEmail" when coming from the MailActions rule.    
            if (context.Type == "ActionEmail")
                var attachment = new Attachment(Picture);

        public void Sent(MessageContext context)

Everything works ok. But... Besides I have a custom field and one of its property contains collection of paths to files which I would like to attach to my email. How can I get this paths collection from this custom field in IMessageEventHandler implementation mentioned-above.

Is it possible?

Dec 21, 2012 at 10:35 PM

If the MailActions class passed in the content item, you would be able to do it. However, that is not the case, and there seems to be no way to get to the submitted content item from your MailActionEventHandler as it is not part of the MessageContext nor its bag of properties.

Dec 22, 2012 at 9:16 AM
Edited Dec 22, 2012 at 9:16 AM
sfmskywalker wrote:

If the MailActions class passed in the content item, you would be able to do it. 

What does it mean? Could you explain it in detail, please? How to do that?

Dec 22, 2012 at 9:57 AM

I mean, the content item that you need is available in the MailActions class (Orchard.Email\Rules\MailActions.cs) that sends the message. If that piece of code would pass through the content item to the call to _messageManager.Send, you would receive that content item as part of the context argument in your MailActionEventHandler. However, this is not happening. What you could do is file a bug about it. We can then triage it and determine if we need to make a change.

Until then, I think your best bet would be to implement your own email sending class. I had a similar issue with the shortcomings of the current system, and implemented my own EmailService (I didn't go as far as abstracting the channels; all I wanted to do is send an email):

using System;
using System.Net;
using System.Net.Mail;
using System.Web.Mvc;
using System.Web.Routing;
using DarkSky.OrchardMarket.Models;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Email.Models;
using Orchard.Logging;

namespace DarkSky.OrchardMarket.Services {
    public interface IEmailService : IDependency {
        void Send(MailMessage message, string templatePath = null, dynamic model = null, IContent content = null);

        /// <summary>
        /// Generates a fully qualified url based on the specified action, controller and routevalues.
        /// </summary>
        string Action(string actionName, string controllerName, object routeValues);

    public class EmailService : Component, IEmailService {
        private readonly IOrchardServices _orchardServices;
        private readonly IEmailTemplateService _emailTemplateService;
        private readonly RequestContext _requestContext;

        public EmailService(IOrchardServices orchardServices, IEmailTemplateService emailTemplateService, RequestContext requestContext) {
            _orchardServices = orchardServices;
            _emailTemplateService = emailTemplateService;
            _requestContext = requestContext;

        public void Send(MailMessage message, string templatePath = null, dynamic model = null, IContent content = null) {
            var smtpSettings = _orchardServices.WorkContext.CurrentSite.As<SmtpSettingsPart>();

            // Can't process emails if the Smtp settings have not yet been set
            if (smtpSettings == null || !smtpSettings.IsValid()) {

            if (string.IsNullOrWhiteSpace(message.Body) && ! string.IsNullOrWhiteSpace(templatePath)) {
                var template = _emailTemplateService.GetTemplate(templatePath);
                var body = _emailTemplateService.ProcessTemplate(template, model, content);

                message.Body = body;
                message.IsBodyHtml = true;

            if (message.From == null) {
                message.From = new MailAddress(smtpSettings.Address);

            using (var smtpClient = new SmtpClient()) {
                smtpClient.UseDefaultCredentials = !smtpSettings.RequireCredentials;
                if (!smtpClient.UseDefaultCredentials && !String.IsNullOrWhiteSpace(smtpSettings.UserName)) {
                    smtpClient.Credentials = new NetworkCredential(smtpSettings.UserName, smtpSettings.Password);

                if (smtpSettings.Host != null)
                    smtpClient.Host = smtpSettings.Host;

                smtpClient.Port = smtpSettings.Port;
                smtpClient.EnableSsl = smtpSettings.EnableSsl;
                smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;

                try {
                    Logger.Debug("Message sent to {0}", message.To[0].Address);
                catch (Exception e) {
                    Logger.Error(e, "An unexpected error while sending a message to {0}", message.To[0].Address);

        public string Action(string actionName, string controllerName, object routeValues) {
            var urlHelper = new UrlHelper(_requestContext);
            var url = _requestContext.HttpContext.Request.Url;
            var scheme = url.Scheme;
            var host = url.Host;
            return urlHelper.Action(actionName, controllerName, new RouteValueDictionary(routeValues), scheme, host);


I'll include the EmailTemplateService for completeness sake (it's responsible for processing an HTML email template using Antlr's StringTemplate):

using System;
using System.IO;
using Antlr4.StringTemplate;
using Orchard;
using Orchard.Caching;
using Orchard.ContentManagement;
using Orchard.FileSystems.VirtualPath;
using Orchard.Logging;

namespace DarkSky.OrchardMarket.Services {
    public interface IEmailTemplateService : IDependency {
        string GetTemplate(string relativePath);
        string ProcessTemplate(string template, dynamic model, IContent content = null);

    public class EmailTemplateService : Component, IEmailTemplateService {
        private readonly ICacheManager _cacheManager;
        private readonly IVirtualPathMonitor _virtualPathMonitor;
        private readonly IOrchardServices _services;

        public EmailTemplateService(ICacheManager cacheManager, IVirtualPathMonitor virtualPathMonitor, IOrchardServices services) {
            _cacheManager = cacheManager;
            _virtualPathMonitor = virtualPathMonitor;
            _services = services;

        public string GetTemplate(string relativePath) {
            var key = string.Format("EmailTemplate_{0}", relativePath);
            return _cacheManager.Get(key, context => {
                var path = _services.WorkContext.HttpContext.Server.MapPath(relativePath);
                return File.ReadAllText(path);

        public string ProcessTemplate(string templateText, dynamic model, IContent content = null) {
            var text = string.Empty;
            if (!string.IsNullOrEmpty(templateText)) {
                try {
                    var template = new Template(templateText, '$', '$');

                    template.Add("CurrentSite", _services.WorkContext.CurrentSite);
                    template.Add("Model", model);
                    text = template.Render();
                catch (Exception ex) {
                    text = ex.ToString();
                    Logger.Error(ex, "Error while sending email");
            return text;

These two classes basically allow you to provide HTML email templates using the ANTLR templating syntax (I tried RazorEngine, but it gave me nasty Template Compilation errors that wasted 2 days of my time, even after updating it to WebPages and Razor 2, so I dumped it).