Open agrigg opened 10 years ago
Seems like an interesting suggestion. I'll do a research about to see what can be done and I will get back to you :)
Cool, looks like there is already a .NET project, PreMailer.Net that can do CSS inlining. Seems like we could just add an option to enable CSS inlining then process the resulting html through PreMailer.Net before assigning it to the email body.
message.Body = PreMailer.Net.PreMailer.MoveCssInline(message.Body).Html;
I'm actually already using PreMailer.Net to do this in conjunction with ActionMailer.Net (and now ActionMailerNext). I'm using the MVC implementation, but to to get this setup, simply add this to your class that inherits from MailerBase.
protected override void OnMailSending(MailSendingContext context)
{
var alternateview = context.Mail.AlternateViews[0];
//Only attempt to process styles if the email's content type is HTML. If it's plain text, processing the contents will insert a wrapping <html> tag with <head> and <body> elements as well which will show as plain text.
if (alternateview.ContentType.MediaType.Contains("html"))
{
//Convert original email contents stream to a string
string html;
using (var memoryStream = new MemoryStream())
{
((MemoryStream)alternateview.ContentStream).WriteTo(memoryStream);
html = Encoding.UTF8.GetString(memoryStream.ToArray());
}
//Use PreMailer to parse the contents and convert <style> contents to inline style="" attributes
var inlineResult = PreMailer.Net.PreMailer.MoveCssInline(html, true);
//see inlineResult.Warnings for problems parsing
html = inlineResult.Html;
//Replace the original email contents with the newly modified contents
context.Mail.AlternateViews.Clear();
context.Mail.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, Encoding.UTF8, alternateview.ContentType.MediaType));
}
base.OnMailSending(context);
}
Nice, thanks @yarx! I'll try that out.
Oh, and just a quick heads up, I found a bug with sending multiple emails in my code that I had to add correction for, in my MemoryStream logic in the using
block. The position on the alternateview.ContentStream
doesn't get reset to 0 for some reason when I go to send a second email on the same request. So if you have already copied what I had above, you may want to grab that one line's change that changed it from using the Stream.CopyTo()
method to instead using MemoryStream.WriteTo()
UPDATE: Actually it seems there is a more systemic change somewhere in the ActionMailerNext that is causing problems when I try to send multiple emails using the same controller... not sure what it is yet.
UPDATE: Yeah, I am not sure what it is, but ActionMailerNext doesn't appear to support sending multiple emails, each with their own view, model, attachments, etc. The source looks fine but when I try to send multiple emails, they are all just copies of the first one I sent. I'm rolling back to ActionMailer.Net for now. I don't have time to debug ActionMAilerNext right now. Either way, the code I have above for using PreMailer to inline the CSS works fine.
It would be great to be able to have the ability to automatically inline css styles as part of sending out an email. I would be open to helping develop this functionality if someone could point me in the right direction for where the best place to hook that in would be.