Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support both Text & HTML in the same email #14715

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d492809
Support both Text & HTML in the same email
hishamco Nov 17, 2023
fefd6ff
Remove extra whitespace
hishamco Nov 25, 2023
57403f1
Introduce MainMessageBody
hishamco Nov 26, 2023
9d56318
Cleanup
hishamco Nov 26, 2023
9f52deb
Revert some changes
hishamco Nov 26, 2023
5843a39
Content -> Body
hishamco Nov 27, 2023
a587476
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 20, 2024
dd37fd1
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 20, 2024
a0bc7a6
Remove obsolete memebers
hishamco Jan 20, 2024
3c079dd
Add release notes docs
hishamco Jan 20, 2024
4f8b04f
Merge branch 'main' into hishamco/email-message-format
hishamco Feb 4, 2024
2d953d7
Merge branch 'main' into hishamco/email-message-format
hishamco Mar 14, 2024
b8beb21
Fix merge conflict
hishamco Mar 14, 2024
0ea8cd1
Update src/docs/releases/1.9.0.md
hishamco Mar 22, 2024
c3a546d
Merge branch 'main' into hishamco/email-message-format
hishamco Mar 22, 2024
5952079
Merge branch 'main' into hishamco/email-message-format
hishamco Jun 17, 2024
c86399f
Add implicit operator to MailMessageBody
hishamco Jun 17, 2024
d1f6ce7
Text -> PlainText
hishamco Jun 17, 2024
279ae07
Merge branch 'main' into hishamco/email-message-format
hishamco Jun 19, 2024
55aa5d6
Merge branch 'main' into hishamco/email-message-format
hishamco Jul 19, 2024
024a39b
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 4, 2025
e964ff9
Fix the build
hishamco Jan 4, 2025
0fbca09
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 21, 2025
c065933
Move docs to 3.0.0
hishamco Jan 21, 2025
1bdc389
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 25, 2025
8127588
Fix message formats dropdown list
hishamco Jan 25, 2025
cc4604e
Merge branch 'main' into hishamco/email-message-format
hishamco Jan 31, 2025
e11b9ab
Backward compatibility
hishamco Jan 31, 2025
daaf9dc
Update src/OrchardCore.Modules/OrchardCore.Email/Views/Items/EmailTas…
hishamco Jan 31, 2025
d228031
Update src/OrchardCore.Modules/OrchardCore.Email/Views/Items/EmailTas…
hishamco Jan 31, 2025
e817142
Change docs
hishamco Feb 1, 2025
24eb630
Split into TextBody & HtmlBody properties
hishamco Feb 8, 2025
e250ee4
Add unit test
hishamco Feb 8, 2025
3a5bca5
Update src/docs/releases/3.0.0.md
hishamco Feb 9, 2025
fa5d5ba
Address feedback
hishamco Feb 14, 2025
5e48b15
Address feedback
hishamco Feb 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,11 @@ private EmailMessage FromMailMessage(MailMessage message, Dictionary<string, ILi
}
}

var content = new EmailContent(message.Subject);
if (message.IsHtmlBody)
var content = new EmailContent(message.Subject)
{
content.Html = message.Body;
}
else
{
content.PlainText = message.Body;
}
PlainText = message.TextBody,
Html = message.HtmlBody
};

var emailMessage = new EmailMessage(
message.From,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,11 @@ private MimeMessage GetMimeMessage(MailMessage message)

mimeMessage.Subject = message.Subject;

var body = new BodyBuilder();

if (message.IsHtmlBody)
{
body.HtmlBody = message.Body;
}
else
var body = new BodyBuilder
{
body.TextBody = message.Body;
}
TextBody = message.TextBody,
HtmlBody = message.HtmlBody
};

foreach (var attachment in message.Attachments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private static MailMessage GetMessage(EmailTestViewModel testSettings)

if (!string.IsNullOrWhiteSpace(testSettings.Body))
{
message.Body = testSettings.Body;
message.TextBody = testSettings.Body;
}

return message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
@using OrchardCore.Email.Workflows.ViewModels
@model EmailTaskViewModel
@functions
{
private enum MailMessageFormat
{
Text,
Html,
All
}
}

<div class="mb-3" asp-validation-class-for="AuthorExpression">
<label asp-for="AuthorExpression" class="form-label">@T["From"]</label>
Expand Down Expand Up @@ -51,17 +60,25 @@
</div>

<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="IsHtmlBody" />
<label class="form-check-label" asp-for="IsHtmlBody">@T["Does the Body contain HTML?"]</label>
<span class="hint dashed">@T["If checked, indicates the body of the email message will be sent as HTML."]</span>
</div>
<label>@T["Format"]</label>
<select class="form-select">
<option value="@MailMessageFormat.Text">@T["Text"]</option>
<option value="@MailMessageFormat.Html">@T["Html"]</option>
<option value="@MailMessageFormat.All">@T["All"]</option>
</select>
<span class="hint">@T["The format of the email message."]</span>
</div>

<div class="mb-3" id="body">
<label asp-for="Body" class="form-label">@T["Body"]</label>
<textarea asp-for="Body" rows="5" class="form-control"></textarea>
<span class="hint">@T["The body of the email message. With Liquid support."]</span>
<div class="mb-3" id="textBody">
<label asp-for="TextBody">@T["Text Body"]</label>
<textarea asp-for="TextBody" rows="5" class="form-control"></textarea>
<span class="hint">@T["The plain text body of the email message. With Liquid support."]</span>
</div>

<div class="mb-3 d-none" id="htmlBody">
<label asp-for="HtmlBody">@T["HTML Body"]</label>
<textarea asp-for="HtmlBody" rows="5" class="form-control"></textarea>
<span class="hint">@T["The HTML body of the email message. With Liquid support."]</span>
</div>

<style asp-name="codemirror"></style>
Expand All @@ -73,12 +90,36 @@
<script asp-src="~/OrchardCore.Liquid/codemirror/liquid.js" at="Foot"></script>

<script at="Foot">
$(function () {
var bodyEditor = CodeMirror.fromTextArea(document.getElementById('@Html.IdFor(x => x.Body)'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: { name: "liquid" },
$(function () {
var textBodyEditor = CodeMirror.fromTextArea(document.getElementById('@Html.IdFor(x => x.TextBody)'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: { name: "liquid" },
});
var htmlBodyEditor = CodeMirror.fromTextArea(document.getElementById('@Html.IdFor(x => x.HtmlBody)'), {
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: { name: "liquid" },
});

$('select').on('change', function () {
var selectedValue = $(this).val();
switch (selectedValue) {
case '@MailMessageFormat.Text':
$('#textBody').removeClass('d-none');
$('#htmlBody').addClass('d-none');
break;
case '@MailMessageFormat.Html':
$('#textBody').addClass('d-none');
$('#htmlBody').removeClass('d-none');
break;
case '@MailMessageFormat.All':
$('#textBody').removeClass('d-none');
$('#htmlBody').removeClass('d-none');
break;
}
});
});
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,52 @@ public WorkflowExpression<string> Subject
set => SetProperty(value);
}

[Obsolete("This property is deprecated, please use TextBody & HtmlBody instead.")]
public WorkflowExpression<string> Body
{
get => GetProperty(() => new WorkflowExpression<string>());
set => SetProperty(value);
}

[Obsolete("This property is deprecated, please use TextBody & HtmlBody instead.")]
public bool IsHtmlBody
{
get => GetProperty(() => true);
set => SetProperty(value);
}

public WorkflowExpression<string> TextBody
{
get
{
var textBody = GetProperty<WorkflowExpression<string>>();

if (textBody == null && !GetProperty(() => true, "IsHtmlBody"))
{
textBody = GetProperty(() => new WorkflowExpression<string>(), "Body");
}

return textBody ?? new WorkflowExpression<string>();
}
set => SetProperty(value);
}

public WorkflowExpression<string> HtmlBody
{
get
{
var htmlBody = GetProperty<WorkflowExpression<string>>();

if (htmlBody == null && GetProperty(() => true, "IsHtmlBody"))
{
htmlBody = GetProperty(() => new WorkflowExpression<string>(), "Body");
}

return htmlBody ?? new WorkflowExpression<string>();
}
set => SetProperty(value);
}

public override IEnumerable<Outcome> GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext)
{
return Outcomes(S["Done"], S["Failed"]);
Expand All @@ -100,7 +134,8 @@ public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecuti
var cc = await _expressionEvaluator.EvaluateAsync(Cc, workflowContext, null);
var bcc = await _expressionEvaluator.EvaluateAsync(Bcc, workflowContext, null);
var subject = await _expressionEvaluator.EvaluateAsync(Subject, workflowContext, null);
var body = await _expressionEvaluator.EvaluateAsync(Body, workflowContext, IsHtmlBody ? _htmlEncoder : null);
var textBody = await _expressionEvaluator.EvaluateAsync(TextBody, workflowContext, null);
var htmlBody = await _expressionEvaluator.EvaluateAsync(HtmlBody, workflowContext, _htmlEncoder);

var message = new MailMessage
{
Expand All @@ -112,8 +147,8 @@ public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecuti
// Email reply-to header https://tools.ietf.org/html/rfc4021#section-2.1.4
ReplyTo = replyTo?.Trim(),
Subject = subject?.Trim(),
Body = body?.Trim(),
IsHtmlBody = IsHtmlBody
HtmlBody = htmlBody?.Trim(),
TextBody = textBody?.Trim()
};

if (!string.IsNullOrWhiteSpace(sender))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ protected override void EditActivity(EmailTask activity, EmailTaskViewModel mode
model.RecipientsExpression = activity.Recipients.Expression;
model.ReplyToExpression = activity.ReplyTo.Expression;
model.SubjectExpression = activity.Subject.Expression;
model.Body = activity.Body.Expression;
model.IsHtmlBody = activity.IsHtmlBody;
model.TextBody = activity.TextBody.Expression;
model.HtmlBody = activity.HtmlBody.Expression;
model.BccExpression = activity.Bcc.Expression;
model.CcExpression = activity.Cc.Expression;
}
Expand All @@ -27,8 +27,8 @@ protected override void UpdateActivity(EmailTaskViewModel model, EmailTask activ
activity.Recipients = new WorkflowExpression<string>(model.RecipientsExpression);
activity.ReplyTo = new WorkflowExpression<string>(model.ReplyToExpression);
activity.Subject = new WorkflowExpression<string>(model.SubjectExpression);
activity.Body = new WorkflowExpression<string>(model.Body);
activity.IsHtmlBody = model.IsHtmlBody;
activity.TextBody = new WorkflowExpression<string>(model.TextBody);
activity.HtmlBody = new WorkflowExpression<string>(model.HtmlBody);
activity.Bcc = new WorkflowExpression<string>(model.BccExpression);
activity.Cc = new WorkflowExpression<string>(model.CcExpression);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class EmailTaskViewModel

public string SubjectExpression { get; set; }

public string Body { get; set; }
public string TextBody { get; set; }

public bool IsHtmlBody { get; set; }
public string HtmlBody { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,44 @@ public static class EmailServiceExtensions
/// <param name="emailService">The <see cref="IEmailService"/>.</param>
/// <param name="to">The email recipients.</param>
/// <param name="subject">The email subject.</param>
/// <param name="body">The email body.</param>
/// <param name="isHtmlBody">Whether the <paramref name="body"/> is in HTML format or not. Defaults to <c>true</c>.</param>
/// <returns></returns>
/// <param name="htmlBody">The email body in HTML format.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments should say the values are optional (can be null). Maybe use the ? operator to be specific.

/// <param name="textBody">The email body in Text format.</param>
/// <exception cref="System.ArgumentException"></exception>
public static Task<EmailResult> SendAsync(this IEmailService emailService, string to, string subject, string body, bool isHtmlBody = true)
public static Task<EmailResult> SendAsync(this IEmailService emailService, string to, string subject, string htmlBody, string textBody)
{
var message = new MailMessage
{
To = to,
Subject = subject,
Body = body,
IsHtmlBody = isHtmlBody
HtmlBody = htmlBody,
TextBody = textBody
};

return emailService.SendAsync(message);
}

/// <summary>
/// Sends the specified message to an SMTP server for delivery.
/// </summary>
/// <param name="emailService">The <see cref="IEmailService"/>.</param>
/// <param name="to">The email recipients.</param>
/// <param name="subject">The email subject.</param>
/// <param name="body">The email body.</param>
/// <param name="isHtmlBody">Whether the <paramref name="body"/> is in HTML format or not. Defaults to <c>true</c>.</param>
/// <exception cref="System.ArgumentException"></exception>
public static async Task<EmailResult> SendAsync(this IEmailService emailService, string to, string subject, string body, bool isHtmlBody = true)
{
string htmlBody = default;
string textBody = default;
if (isHtmlBody)
{
htmlBody = body;
}
else
{
textBody = body;
}

return await emailService.SendAsync(to, subject, htmlBody, textBody);
}
}
15 changes: 10 additions & 5 deletions src/OrchardCore/OrchardCore.Email.Abstractions/MailMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,21 @@ public class MailMessage
/// </summary>
public string Subject { get; set; }

[Obsolete("This property has been deprecated, please use the HtmlBody or TextBody property instead.")]
public bool IsHtmlBody { get; set; }

[Obsolete("This property has been deprecated, please use the HtmlBody or TextBody property instead.")]
public string Body { get; set; }

/// <summary>
/// Gets or sets the message content aka body.
/// Gets or sets the message content in HTML format.
/// </summary>
/// <remarks>This property is work in conjunction with <see cref="IsHtmlBody"/> to determine the body type..</remarks>
public string Body { get; set; }
gvkries marked this conversation as resolved.
Show resolved Hide resolved
public string HtmlBody { get; set; }

/// <summary>
/// Gets or sets whether the message body is an HTML or not. Default is <c>false</c> which is plain text.
/// Gets or sets the message content in plain text format.
/// </summary>
public bool IsHtmlBody { get; set; }
public string TextBody { get; set; }

/// <summary>
/// The collection of message attachments.
Expand Down
8 changes: 8 additions & 0 deletions src/docs/releases/3.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ Before upgrading from version 2 to v3, it is important to first compile your pro

## Breaking Changes

### Email Module

Previously, emails sent from Orchard Core could have either a plain text body, or an HTML body, but not both. Now, they can have both. This also brings some code-level API changes, see below.

When interacting with email-related services from code, `MailMessage`, the class representing an e-mail, exposed a `string` `Body` property. This could contain either plain text or HTML, which was indicated by `IsHtmlBody`.

The new `Body` property returns `MailMessageBody` type, which contains a plain text and/or HTML body.
hishamco marked this conversation as resolved.
Show resolved Hide resolved

### GraphQL Module

#### GraphQL Library Upgrade
Expand Down
Loading