diff --git a/src/Postmark.Tests/ClientTemplateTests.cs b/src/Postmark.Tests/ClientTemplateTests.cs
index 08e5df7..145ab9f 100644
--- a/src/Postmark.Tests/ClientTemplateTests.cs
+++ b/src/Postmark.Tests/ClientTemplateTests.cs
@@ -3,13 +3,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
+using PostmarkDotNet.Model;
namespace Postmark.Tests
{
public class ClientTemplateTests : ClientBaseFixture, IDisposable
{
+ private readonly string _layoutContentPlaceholder = "{{{@content}}}";
+
protected override void Setup()
{
_client = new PostmarkClient(WRITE_TEST_SERVER_TOKEN, BASE_URL);
@@ -20,14 +22,27 @@ public async void ClientCanCreateTemplate()
{
var name = Guid.NewGuid().ToString();
var subject = "A subject: " + Guid.NewGuid();
- var htmlbody = "Hello, {{name}}";
+ var htmlBody = "Hello, {{name}}";
var textBody = "Hello, {{name}}!";
- var newTemplate = await _client.CreateTemplateAsync(name, subject, htmlbody, textBody);
+ var newTemplate = await _client.CreateTemplateAsync(name, subject, htmlBody, textBody);
Assert.Equal(name, newTemplate.Name);
}
+ [Fact]
+ public async void ClientCanCreateLayoutTemplates()
+ {
+ var newLayoutTemplate = await GenerateLayoutTemplate();
+ Assert.Equal(TemplateType.Layout, newLayoutTemplate.TemplateType);
+
+ // Creating a standard template that uses the layout template above
+ var newStandardTemplate = await GenerateStandardTemplate(newLayoutTemplate.Alias);
+
+ Assert.Equal(TemplateType.Standard, newStandardTemplate.TemplateType);
+ Assert.Equal(newLayoutTemplate.Alias, newStandardTemplate.LayoutTemplate);
+ }
+
[Fact]
public async void ClientCanEditTemplate()
{
@@ -50,6 +65,22 @@ public async void ClientCanEditTemplate()
Assert.Equal(existingTemplate.TextBody + existingTemplate.TextBody, updatedTemplate.TextBody);
}
+ [Fact]
+ public async void ClientCanEditLayoutTemplateProperty()
+ {
+ var newLayoutTemplate = await GenerateLayoutTemplate();
+ var newStandardTemplate = await GenerateStandardTemplate(newLayoutTemplate.Alias);
+
+ // Setting the LayoutTemplate to null
+ var templateWithNoLayoutTemplate = await _client.EditTemplateAsync(newStandardTemplate.TemplateId, layoutTemplate: "");
+ Assert.Null(templateWithNoLayoutTemplate.LayoutTemplate);
+
+ // Setting the LayoutTemplate back to the layout template that was created
+ var templateWithLayoutTemplate = await _client.EditTemplateAsync(newStandardTemplate.TemplateId,
+ layoutTemplate: newLayoutTemplate.Alias);
+ Assert.Equal(newLayoutTemplate.Alias, templateWithLayoutTemplate.LayoutTemplate);
+ }
+
[Fact]
public async void ClientCanDeleteTemplate()
{
@@ -84,6 +115,8 @@ public async void ClientCanGetTemplate()
Assert.True(result.Active);
Assert.True(result.AssociatedServerId > 0);
Assert.Equal(newTemplate.TemplateId, result.TemplateId);
+ Assert.Equal(TemplateType.Standard, result.TemplateType);
+ Assert.Null(result.LayoutTemplate);
}
[Fact]
@@ -103,7 +136,36 @@ public async void ClientCanGetListTemplates()
Assert.False(result.Templates.FirstOrDefault(k => k.TemplateId == toDelete) != null);
var offsetResults = await _client.GetTemplatesAsync(5);
Assert.True(result.Templates.Skip(5).Select(k => k.TemplateId).SequenceEqual(offsetResults.Templates.Select(k => k.TemplateId)));
+ }
+
+ [Fact]
+ public async void GetTemplatesReturnsProperResults()
+ {
+ var newLayoutTemplate = await GenerateLayoutTemplate();
+ var newStandardTemplate = await GenerateStandardTemplate(newLayoutTemplate.Alias);
+
+ var result = await _client.GetTemplatesAsync();
+ Assert.Equal(2, result.TotalCount);
+
+ var standardTemplateFromResult = result.Templates.First(t => t.TemplateId == newStandardTemplate.TemplateId);
+ Assert.Equal(newStandardTemplate.TemplateId, standardTemplateFromResult.TemplateId);
+ Assert.Equal(newStandardTemplate.Alias, standardTemplateFromResult.Alias);
+ Assert.Equal(newStandardTemplate.Name, standardTemplateFromResult.Name);
+ Assert.Equal(TemplateType.Standard, standardTemplateFromResult.TemplateType);
+ Assert.Equal(newLayoutTemplate.Alias, standardTemplateFromResult.LayoutTemplate);
+
+ var layoutTemplateFromResult = result.Templates.First(t => t.TemplateId == newLayoutTemplate.TemplateId);
+ Assert.Equal(newLayoutTemplate.TemplateId, layoutTemplateFromResult.TemplateId);
+ Assert.Equal(newLayoutTemplate.Alias, layoutTemplateFromResult.Alias);
+ Assert.Equal(newLayoutTemplate.Name, layoutTemplateFromResult.Name);
+ Assert.Equal(TemplateType.Layout, layoutTemplateFromResult.TemplateType);
+ Assert.Null(layoutTemplateFromResult.LayoutTemplate);
+
+ var filteredResultByType = await _client.GetTemplatesAsync(0, 100, TemplateTypeFilter.Layout);
+ Assert.Equal(1, filteredResultByType.TotalCount);
+ var filteredResultByLayoutAlias = await _client.GetTemplatesAsync(0, 100, TemplateTypeFilter.All, newLayoutTemplate.Alias);
+ Assert.Equal(1, filteredResultByLayoutAlias.TotalCount);
}
[Fact]
@@ -121,6 +183,23 @@ public async void ClientCanValidateTemplate()
Assert.Equal(3, products.Length);
}
+ [Fact]
+ public async void ClientCanUseLayoutTemplatesWhenValidating()
+ {
+ var layoutTemplate = await GenerateLayoutTemplate();
+
+ var content = "Mr. Jones";
+ var result = await _client.ValidateTemplateAsync("Subject", null, content, new { }, true, TemplateType.Standard, layoutTemplate.Alias);
+
+ Assert.True(result.AllContentIsValid);
+ Assert.True(result.TextBody.ContentIsValid);
+ Assert.True(result.Subject.ContentIsValid);
+
+ // Testing to see that indeed the validation has used the LayoutTemplate
+ var expectedTextBody = layoutTemplate.TextBody.Replace(_layoutContentPlaceholder, content);
+ Assert.Equal(expectedTextBody, result.TextBody.RenderedContent);
+ }
+
[Fact]
public async void ClientCanSendWithTemplate()
{
@@ -137,7 +216,8 @@ public async void ClientCanSendTemplateWithStringModel()
Assert.NotEqual(Guid.Empty, sendResult.MessageID);
}
- private Task Cleanup(){
+ private Task Cleanup()
+ {
return Task.Run(async () =>
{
try
@@ -155,6 +235,29 @@ private Task Cleanup(){
});
}
+ private async Task GenerateLayoutTemplate()
+ {
+ var layoutName = Guid.NewGuid().ToString();
+ var layoutHtmlBody = $"header {_layoutContentPlaceholder} footer";
+ var layoutTextBody = $"header {_layoutContentPlaceholder} footer";
+
+ var newLayoutTemplate = await _client.CreateTemplateAsync(layoutName, null, layoutHtmlBody, layoutTextBody, null, TemplateType.Layout);
+ return await _client.GetTemplateAsync(newLayoutTemplate.TemplateId);
+ }
+
+ private async Task GenerateStandardTemplate(string layoutTemplateAlias = null)
+ {
+ var name = Guid.NewGuid().ToString();
+ var subject = "A subject: " + Guid.NewGuid();
+ var htmlBody = "Hello, {{name}}!";
+ var textBody = "Hello, {{name}}!";
+
+ var newStandardTemplate = await _client.CreateTemplateAsync(name, subject, htmlBody, textBody, null,
+ TemplateType.Standard, layoutTemplateAlias);
+
+ return await _client.GetTemplateAsync(newStandardTemplate.TemplateId);
+ }
+
public void Dispose()
{
Cleanup().Wait();
diff --git a/src/Postmark/Model/PostmarkTemplate.cs b/src/Postmark/Model/PostmarkTemplate.cs
index 8532fda..d8595de 100644
--- a/src/Postmark/Model/PostmarkTemplate.cs
+++ b/src/Postmark/Model/PostmarkTemplate.cs
@@ -1,10 +1,13 @@
-namespace PostmarkDotNet.Model
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace PostmarkDotNet.Model
{
public class PostmarkTemplate
{
public long TemplateId { get; set; }
- public string Alias { get;set; }
+ public string Alias { get; set; }
public string Subject { get; set; }
@@ -18,5 +21,9 @@ public class PostmarkTemplate
public bool Active { get; set; }
+ [JsonConverter(typeof(StringEnumConverter))]
+ public TemplateType TemplateType { get; set; }
+
+ public string LayoutTemplate { get; set; }
}
}
diff --git a/src/Postmark/Model/PostmarkTemplateListingResponse.cs b/src/Postmark/Model/PostmarkTemplateListingResponse.cs
index c8add25..eae2002 100644
--- a/src/Postmark/Model/PostmarkTemplateListingResponse.cs
+++ b/src/Postmark/Model/PostmarkTemplateListingResponse.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace PostmarkDotNet.Model
{
@@ -13,8 +15,16 @@ public class PostmarkTemplateListingResponse
public class BasicTemplateInformation
{
public bool IsActive { get; set; }
+
public String Name { get; set; }
+
public long TemplateId { get; set; }
+
public string Alias { get; set; }
+
+ [JsonConverter(typeof(StringEnumConverter))]
+ public TemplateType TemplateType { get; set; }
+
+ public string LayoutTemplate { get; set; }
}
}
diff --git a/src/Postmark/Model/TemplateType.cs b/src/Postmark/Model/TemplateType.cs
new file mode 100644
index 0000000..48df308
--- /dev/null
+++ b/src/Postmark/Model/TemplateType.cs
@@ -0,0 +1,8 @@
+namespace PostmarkDotNet.Model
+{
+ public enum TemplateType
+ {
+ Standard,
+ Layout
+ }
+}
\ No newline at end of file
diff --git a/src/Postmark/Model/TemplateTypeFilter.cs b/src/Postmark/Model/TemplateTypeFilter.cs
new file mode 100644
index 0000000..5c10107
--- /dev/null
+++ b/src/Postmark/Model/TemplateTypeFilter.cs
@@ -0,0 +1,9 @@
+namespace PostmarkDotNet.Model
+{
+ public enum TemplateTypeFilter
+ {
+ Standard,
+ Layout,
+ All
+ }
+}
\ No newline at end of file
diff --git a/src/Postmark/PostmarkClient.cs b/src/Postmark/PostmarkClient.cs
index 88e1948..611b40c 100644
--- a/src/Postmark/PostmarkClient.cs
+++ b/src/Postmark/PostmarkClient.cs
@@ -186,8 +186,10 @@ public async Task GetOutboundMessagesAsync(int offs
parameters["fromdate"] = fromDate;
parameters["status"] = status.ToString().ToLower();
- if(metadata != null){
- foreach(var a in metadata){
+ if (metadata != null)
+ {
+ foreach (var a in metadata)
+ {
parameters[$"metadata_{a.Key}"] = a.Value;
}
}
@@ -232,7 +234,7 @@ public async Task GetOutboundMessageDumpAsync(string messageID)
/// Get messages on or before YYYY-MM-DD.
/// PostmarkInboundMessageList
public async Task GetInboundMessagesAsync(int offset = 0, int count = 100,
- string recipient = null, string fromemail = null, string subject = null,
+ string recipient = null, string fromemail = null, string subject = null,
string mailboxhash = null, InboundMessageStatus? status = InboundMessageStatus.Processed, String toDate = null, String fromDate = null)
{
var parameters = new Dictionary();
@@ -245,7 +247,7 @@ public async Task GetInboundMessagesAsync(int offset
parameters["todate"] = toDate;
parameters["fromdate"] = fromDate;
parameters["status"] = status.ToString().ToLower();
-
+
return await ProcessNoBodyRequestAsync("/messages/inbound", parameters);
}
@@ -302,7 +304,7 @@ public async Task GetServerAsync()
public async Task EditServerAsync(String name = null, string color = null,
bool? rawEmailEnabled = null, bool? smtpApiActivated = null, string inboundHookUrl = null,
string bounceHookUrl = null, string openHookUrl = null, bool? postFirstOpenOnly = null,
- bool? trackOpens = null, string inboundDomain = null, int? inboundSpamThreshold = null,
+ bool? trackOpens = null, string inboundDomain = null, int? inboundSpamThreshold = null,
LinkTrackingOptions? trackLinks = null,
string clickHookUrl = null, string deliveryHookUrl = null)
{
@@ -829,21 +831,25 @@ public async Task GetTemplateAsync(string alias)
}
///
- /// Get a listing of templates, optionally including deleted templates.
+ /// Get a listing of templates.
///
- ///
- ///
+ /// The number of templates to return. Defaults to 0.
+ /// The number of templates to "skip" before returning results. Defaults to 100.
+ /// Filter the resulting templates by their TemplateType. Defaults to: All
+ /// Filter results by layout template alias.
///
- public async Task GetTemplatesAsync(int offset = 0, int count = 100)
+ public async Task GetTemplatesAsync(int offset = 0, int count = 100, TemplateTypeFilter templateType = TemplateTypeFilter.All,
+ string layoutTemplate = null)
{
var query = new Dictionary();
query["Count"] = count;
query["Offset"] = offset;
+ query["TemplateType"] = Enum.GetName(typeof(TemplateTypeFilter), templateType);
+ query["LayoutTemplate"] = layoutTemplate;
return await ProcessNoBodyRequestAsync("/templates/", query, HttpMethod.Get);
}
-
///
/// Store a new template associated with this server.
///
@@ -852,8 +858,11 @@ public async Task GetTemplatesAsync(int offset
/// The HTMLBody to be used when sending with this template. Optional if TextBody is specified.
/// The TextBody to be used when sending with this template. Optional if HtmlBody is specified.
/// A friendly name to use for this template to access it, or to send with it.
+ /// The type of the template to create.
+ /// The alias of the Layout template that you want to use as layout for this Standard template.
///
- public async Task CreateTemplateAsync(string name, string subject, string htmlBody = null, string textBody = null, string alias = null)
+ public async Task CreateTemplateAsync(string name, string subject, string htmlBody = null, string textBody = null, string alias = null,
+ TemplateType templateType = TemplateType.Standard, string layoutTemplate = null)
{
var body = new Dictionary();
body["Name"] = name;
@@ -861,22 +870,27 @@ public async Task CreateTemplateAsync(string name, str
body["TextBody"] = textBody;
body["Subject"] = subject;
body["Alias"] = alias;
+ body["TemplateType"] = Enum.GetName(typeof(TemplateType), templateType);
+ body["LayoutTemplate"] = layoutTemplate;
return await ProcessRequestAsync, BasicTemplateInformation>("/templates/", HttpMethod.Post, body);
}
- public async Task EditTemplateAsync(string alias, string name = null, string subject = null, string htmlBody = null, string textBody = null)
+ public async Task EditTemplateAsync(string alias, string name = null, string subject = null, string htmlBody = null, string textBody = null,
+ string layoutTemplate = null)
{
var body = new Dictionary();
body["Name"] = name;
body["HTMLBody"] = htmlBody;
body["TextBody"] = textBody;
body["Subject"] = subject;
-
+ body["LayoutTemplate"] = layoutTemplate;
+
return await ProcessRequestAsync, BasicTemplateInformation>("/templates/" + alias, HttpMethod.Put, body);
}
- public async Task EditTemplateAsync(long templateId, string name = null, string subject = null, string htmlBody = null, string textBody = null, string alias = null)
+ public async Task EditTemplateAsync(long templateId, string name = null, string subject = null, string htmlBody = null, string textBody = null, string alias = null,
+ string layoutTemplate = null)
{
var body = new Dictionary();
body["Name"] = name;
@@ -884,6 +898,7 @@ public async Task EditTemplateAsync(long templateId, s
body["TextBody"] = textBody;
body["Subject"] = subject;
body["Alias"] = alias;
+ body["LayoutTemplate"] = layoutTemplate;
return await ProcessRequestAsync, BasicTemplateInformation>("/templates/" + templateId, HttpMethod.Put, body);
}
@@ -953,11 +968,14 @@ private async Task InternalSendEmailWithTemplateAsync(objec
IDictionary metadata = null,
params PostmarkMessageAttachment[] attachments)
{
-
+
var email = new TemplatedPostmarkMessage();
- if(templateReference is long){
+ if (templateReference is long)
+ {
email.TemplateId = (long)templateReference;
- }else{
+ }
+ else
+ {
email.TemplateAlias = (string)templateReference;
}
email.TemplateModel = templateModel;
@@ -986,15 +1004,30 @@ private async Task InternalSendEmailWithTemplateAsync(objec
return await SendEmailWithTemplateAsync(email);
}
- public async Task ValidateTemplateAsync(string subject = null, string htmlbody = null,
- string textBody = null, T testRenderModel = default(T), bool inlineCssForHtmlTestRender = true)
+ ///
+ /// Validate a template.
+ ///
+ ///
+ /// The subject content to validate.
+ /// The HTML body content to validate.
+ /// The plain text body content to validate.
+ /// The template model to be used when rendering test content.
+ /// Controls whether style blocks will be inlined as style attributes on matching html elements in HtmlBody.
+ /// Validate templates based on template type.
+ /// An optional string to specify which layout template alias to use to validate a standard template.
+ ///
+ public async Task ValidateTemplateAsync(string subject = null, string htmlBody = null,
+ string textBody = null, T testRenderModel = default(T), bool inlineCssForHtmlTestRender = true,
+ TemplateType templateType = TemplateType.Standard, string layoutTemplate = null)
{
var body = new Dictionary();
body["TestRenderModel"] = testRenderModel;
body["Subject"] = subject;
- body["HtmlBody"] = htmlbody;
+ body["HtmlBody"] = htmlBody;
body["TextBody"] = textBody;
body["InlineCssForHtmlTestRender"] = inlineCssForHtmlTestRender;
+ body["TemplateType"] = Enum.GetName(typeof(TemplateType), templateType);
+ body["LayoutTemplate"] = layoutTemplate;
return await ProcessRequestAsync, TemplateValidationResponse>("/templates/validate", HttpMethod.Post, body);
}