notification improvements

pull/567/head
tidusjar 8 years ago
parent 5816ddef98
commit 9c789363f6

@ -1,147 +1,147 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AuthenticationSettingsTests.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using NUnit.Framework;
using PlexRequests.Core.Models;
using PlexRequests.Core.Notification;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.Core.Tests
{
[TestFixture]
public class NotificationMessageResolverTests
{
[TestCaseSource(nameof(MessageBodyResolver))]
public string ResolveBody(string body, NotificationMessageCurlys param)
{
var n = new NotificationMessageResolver();
var s = new NotificationSettings
{
Message = new List<Notification.NotificationMessage> { new Notification.NotificationMessage { NotificationType = NotificationType.NewRequest, Body = body } }
};
var result = n.ParseMessage(s, NotificationType.NewRequest, param);
return result.Body;
}
[TestCaseSource(nameof(MessageSubjectResolver))]
public string ResolveSubject(string subject, NotificationMessageCurlys param)
{
var n = new NotificationMessageResolver();
var s = new NotificationSettings
{
Message = new List<Notification.NotificationMessage> { new Notification.NotificationMessage { NotificationType = NotificationType.NewRequest, Subject = subject }}
};
var result = n.ParseMessage(s, NotificationType.NewRequest, param);
return result.Subject;
}
private static IEnumerable<TestCaseData> MessageSubjectResolver
{
get
{
yield return new TestCaseData(
"{Username} has requested a {Type}",
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns("Jamie has requested a Movie").SetName("Subject Curlys");
yield return new TestCaseData(
null,
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns(string.Empty).SetName("Empty Subject");
yield return new TestCaseData(
"New Request Incoming!",
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns("New Request Incoming!").SetName("No curlys");
yield return new TestCaseData(
"%$R£%$£^%$&{Username}@{}:§",
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns("%$R£%$£^%$&Jamie@{}:§").SetName("Special Chars");
}
}
private static IEnumerable<TestCaseData> MessageBodyResolver
{
get
{
yield return new TestCaseData(
"There has been a new request from {Username}, Title: {Title} for {Type}",
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns("There has been a new request from Jamie, Title: Finding Dory for Movie").SetName("FindingDory");
yield return new TestCaseData(
null,
new NotificationMessageCurlys(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty))
.Returns(string.Empty)
.SetName("Empty Message");
yield return new TestCaseData(
"{{Wowwzer}} Damn}{{Username}}}}",
new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, string.Empty))
.Returns("{{Wowwzer}} Damn}{HEY!}}}")
.SetName("Multiple Curlys");
yield return new TestCaseData(
"This is a message with no curlys",
new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
.Returns("This is a message with no curlys")
.SetName("No Curlys");
yield return new TestCaseData(
new string(')', 5000),
new NotificationMessageCurlys(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty))
.Returns(new string(')', 5000))
.SetName("Long String");
yield return new TestCaseData(
"This is a {Username} and {Username} Because {Issue}{Issue}",
new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, "Bob"))
.Returns("This is a HEY! and HEY! Because BobBob")
.SetName("Double Curly");
yield return new TestCaseData(
"This is a {username} and {username} Because {Issue}{Issue}",
new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, "Bob"))
.Returns("This is a {username} and {username} Because BobBob")
.SetName("Case sensitive");
yield return new TestCaseData(
"{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}",
new NotificationMessageCurlys("HEY!", string.Empty, "b", string.Empty, "Bob"))
.Returns("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
.SetName("Lots of curlys");
}
}
}
}
//#region Copyright
//// /************************************************************************
//// Copyright (c) 2016 Jamie Rees
//// File: AuthenticationSettingsTests.cs
//// Created By: Jamie Rees
////
//// Permission is hereby granted, free of charge, to any person obtaining
//// a copy of this software and associated documentation files (the
//// "Software"), to deal in the Software without restriction, including
//// without limitation the rights to use, copy, modify, merge, publish,
//// distribute, sublicense, and/or sell copies of the Software, and to
//// permit persons to whom the Software is furnished to do so, subject to
//// the following conditions:
////
//// The above copyright notice and this permission notice shall be
//// included in all copies or substantial portions of the Software.
////
//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
//// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
//// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//// ************************************************************************/
//#endregion
//using System;
//using System.Collections.Generic;
//using NUnit.Framework;
//using PlexRequests.Core.Models;
//using PlexRequests.Core.Notification;
//using PlexRequests.Core.SettingModels;
//namespace PlexRequests.Core.Tests
//{
// [TestFixture]
// public class NotificationMessageResolverTests
// {
// [TestCaseSource(nameof(MessageBodyResolver))]
// public string ResolveBody(string body, NotificationMessageCurlys param)
// {
// var n = new NotificationMessageResolver();
// var s = new NotificationSettings
// {
// Message = new List<Notification.NotificationMessage> { new Notification.NotificationMessage { NotificationType = NotificationType.NewRequest, Body = body } }
// };
// var result = n.ParseMessage(s, NotificationType.NewRequest, param, TransportType.Email);
// return result.Body;
// }
// [TestCaseSource(nameof(MessageSubjectResolver))]
// public string ResolveSubject(string subject, NotificationMessageCurlys param)
// {
// var n = new NotificationMessageResolver();
// var s = new NotificationSettings
// {
// Message = new List<Notification.NotificationMessage> { new Notification.NotificationMessage { NotificationType = NotificationType.NewRequest, Subject = subject }}
// };
// var result = n.ParseMessage(s, NotificationType.NewRequest, param, TransportType.Email);
// return result.Subject;
// }
// private static IEnumerable<TestCaseData> MessageSubjectResolver
// {
// get
// {
// yield return new TestCaseData(
// "{Username} has requested a {Type}",
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns("Jamie has requested a Movie").SetName("Subject Curlys");
// yield return new TestCaseData(
// null,
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns(string.Empty).SetName("Empty Subject");
// yield return new TestCaseData(
// "New Request Incoming!",
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns("New Request Incoming!").SetName("No curlys");
// yield return new TestCaseData(
// "%$R£%$£^%$&{Username}@{}:§",
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns("%$R£%$£^%$&Jamie@{}:§").SetName("Special Chars");
// }
// }
// private static IEnumerable<TestCaseData> MessageBodyResolver
// {
// get
// {
// yield return new TestCaseData(
// "There has been a new request from {Username}, Title: {Title} for {Type}",
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns("There has been a new request from Jamie, Title: Finding Dory for Movie").SetName("FindingDory");
// yield return new TestCaseData(
// null,
// new NotificationMessageCurlys(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty))
// .Returns(string.Empty)
// .SetName("Empty Message");
// yield return new TestCaseData(
// "{{Wowwzer}} Damn}{{Username}}}}",
// new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, string.Empty))
// .Returns("{{Wowwzer}} Damn}{HEY!}}}")
// .SetName("Multiple Curlys");
// yield return new TestCaseData(
// "This is a message with no curlys",
// new NotificationMessageCurlys("Jamie", "Finding Dory", DateTime.Now.ToString(), "Movie", string.Empty))
// .Returns("This is a message with no curlys")
// .SetName("No Curlys");
// yield return new TestCaseData(
// new string(')', 5000),
// new NotificationMessageCurlys(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty))
// .Returns(new string(')', 5000))
// .SetName("Long String");
// yield return new TestCaseData(
// "This is a {Username} and {Username} Because {Issue}{Issue}",
// new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, "Bob"))
// .Returns("This is a HEY! and HEY! Because BobBob")
// .SetName("Double Curly");
// yield return new TestCaseData(
// "This is a {username} and {username} Because {Issue}{Issue}",
// new NotificationMessageCurlys("HEY!", string.Empty, string.Empty, string.Empty, "Bob"))
// .Returns("This is a {username} and {username} Because BobBob")
// .SetName("Case sensitive");
// yield return new TestCaseData(
// "{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}{Date}",
// new NotificationMessageCurlys("HEY!", string.Empty, "b", string.Empty, "Bob"))
// .Returns("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
// .SetName("Lots of curlys");
// }
// }
// }
//}

@ -24,6 +24,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
@ -50,16 +52,37 @@ namespace PlexRequests.Core.Notification
/// <param name="notification">The notification.</param>
/// <param name="type">The type.</param>
/// <param name="c">The c.</param>
/// <param name="transportType">Type of the transport.</param>
/// <returns></returns>
public NotificationMessageContent ParseMessage<T>(T notification, NotificationType type, NotificationMessageCurlys c) where T : NotificationSettings
public NotificationMessageContent ParseMessage(NotificationSettingsV2 notification, NotificationType type, NotificationMessageCurlys c, TransportType transportType)
{
var content = notification.Message.FirstOrDefault(x => x.NotificationType == type);
IEnumerable<NotificationMessage> content = null;
switch (transportType)
{
case TransportType.Email:
content = notification.EmailNotification;
break;
case TransportType.Pushbullet:
content = notification.PushbulletNotification;
break;
case TransportType.Pushover:
content = notification.PushoverNotification;
break;
case TransportType.Slack:
content = notification.SlackNotification;
break;
default:
throw new ArgumentOutOfRangeException(nameof(transportType), transportType, null);
}
if (content == null)
{
return new NotificationMessageContent();
}
return Resolve(content.Body, content.Subject, c.Curlys);
var message = content.FirstOrDefault(x => x.NotificationType == type) ?? new NotificationMessage();
return Resolve(message.Body, message.Subject, c.Curlys);
}
/// <summary>
@ -78,7 +101,7 @@ namespace PlexRequests.Core.Notification
body = ReplaceFields(bodyFields, parameters, body);
subject = ReplaceFields(subjectFields, parameters, subject);
return new NotificationMessageContent { Body = body ?? string.Empty, Subject = subject ?? string.Empty };
return new NotificationMessageContent {Body = body ?? string.Empty, Subject = subject ?? string.Empty};
}
/// <summary>
@ -123,7 +146,6 @@ namespace PlexRequests.Core.Notification
{
currentWord += c.ToString(); // Add the character onto the word.
}
}
return fields;

@ -0,0 +1,189 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Plex Requests .Net</title>
<style media="all" type="text/css">
@media all {
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
@media all {
.btn-secondary a:hover {
border-color: #34495e !important;
color: #34495e !important;
}
}
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] h2 {
font-size: 22px !important;
margin-bottom: 10px !important;
}
table[class=body] h3 {
font-size: 16px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .header {
margin-bottom: 10px !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
table[class=body] .alert td {
border-radius: 0 !important;
padding: 10px !important;
}
table[class=body] .span-2,
table[class=body] .span-3 {
max-width: none !important;
width: 100% !important;
}
table[class=body] .receipt {
width: 100% !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
}
</style>
</head>
<body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td align="center">
<img src="http://i.imgur.com/s4nswSA.png?" width="400px" text-align="center" />
</td>
</tr>
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi there!</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@SUBJECT}</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{@BODY}</p>
</td>
</tr>
<tr>
<td align="center">
<img src="{@IMGSRC}" width="400px" text-align="center" />
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
<tr>
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
Powered by <a href="https://github.com/tidusjar/PlexRequests.Net" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Plex Requests .Net</a>
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">&nbsp;</td>
</tr>
</table>
</body>
</html>

@ -0,0 +1,70 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EmailBasicTemplate.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NLog;
using PlexRequests.Core.Models;
using PlexRequests.Core.SettingModels;
namespace PlexRequests.Core.Notification.Templates
{
public class EmailBasicTemplate : IEmailBasicTemplate
{
public string TemplateLocation => Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, "Notification", "Templates", "BasicRequestTemplate.html");
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private const string SubjectKey = "{@SUBJECT}";
private const string BodyKey = "{@BODY}";
private const string ImgSrc = "{@IMGSRC}";
private const string DateKey = "{@DATENOW}";
public string LoadTemplate(string subject, string body, string imgSrc)
{
try
{
var sb = new StringBuilder(File.ReadAllText(TemplateLocation));
sb.Replace(SubjectKey, subject);
sb.Replace(BodyKey, body);
sb.Replace(ImgSrc, imgSrc);
sb.Replace(DateKey, DateTime.Now.ToString("f"));
return sb.ToString();
}
catch (Exception e)
{
Log.Error(e);
return string.Empty;
}
}
}
}

@ -0,0 +1,37 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IEmailBasicTemplate.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Threading.Tasks;
namespace PlexRequests.Core.Notification.Templates
{
public interface IEmailBasicTemplate
{
string LoadTemplate(string subject, string body, string imgSrc);
string TemplateLocation { get; }
}
}

@ -0,0 +1,36 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TransportType.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace PlexRequests.Core.Notification
{
public enum TransportType
{
Email,
Pushbullet,
Pushover,
Slack
}
}

@ -44,6 +44,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -81,11 +82,15 @@
<Compile Include="Models\NotificationType.cs" />
<Compile Include="Models\StatusModel.cs" />
<Compile Include="Models\UserProperties.cs" />
<Compile Include="Notification\Templates\EmailBasicTemplate.cs" />
<Compile Include="Notification\Templates\IEmailBasicTemplate.cs" />
<Compile Include="Notification\TransportType.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.cs" />
<Compile Include="SettingModels\HeadphonesSettings.cs" />
<Compile Include="SettingModels\LandingPageSettings.cs" />
<Compile Include="SettingModels\NotificationSettings.cs" />
<Compile Include="SettingModels\NotificationSettingsV2.cs" />
<Compile Include="SettingModels\RequestSettings.cs" />
<Compile Include="SettingModels\ScheduledJobsSettings.cs" />
<Compile Include="SettingModels\SlackNotificationSettings.cs" />
@ -134,7 +139,11 @@
<Name>PlexRequests.Store</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Notification\Templates\BasicRequestTemplate.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

@ -34,7 +34,7 @@ namespace PlexRequests.Core.SettingModels
public string EmailSender { get; set; }
public string EmailUsername { get; set; }
public bool Enabled { get; set; }
public bool Authentication { get; set; }
public bool Authentication { get; set; }
public bool EnableUserEmailNotifications { get; set; }
public string RecipientEmail { get; set; }
}

@ -0,0 +1,62 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: NotificationSettingsV2.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
using PlexRequests.Core.Models;
using PlexRequests.Core.Notification;
namespace PlexRequests.Core.SettingModels
{
public class NotificationSettingsV2 : Settings
{
public NotificationSettingsV2()
{
EmailNotification = new List<NotificationMessage>
{
new NotificationMessage
{
Body = "BODY",
NotificationType = NotificationType.NewRequest,
Subject = "SUB"
},
new NotificationMessage
{
NotificationType = NotificationType.Issue,
Body = "issue",
Subject = "issuesub"
}
};
SlackNotification = new List<NotificationMessage>();
PushoverNotification = new List<NotificationMessage>();
PushbulletNotification = new List<NotificationMessage>();
}
public List<NotificationMessage> EmailNotification { get; set; }
public List<NotificationMessage> SlackNotification { get; set; }
public List<NotificationMessage> PushbulletNotification { get; set; }
public List<NotificationMessage> PushoverNotification { get; set; }
}
}

@ -25,18 +25,13 @@
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using MailKit.Security;
using MimeKit;
using NLog;
using PlexRequests.Core;
using PlexRequests.Core.Models;
using PlexRequests.Core.Notification.Templates;
using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
@ -109,7 +104,7 @@ namespace PlexRequests.Services.Notification
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
{
return false;
}
}
}
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
{
@ -129,13 +124,16 @@ namespace PlexRequests.Services.Notification
private async Task EmailNewRequest(NotificationModel model, EmailNotificationSettings settings)
{
//var r = new NotificationMessageCurlys(model.User, model.Title, DateTime.Now.ToString(), model.RequestType.ToString(), string.Empty);
//var resolver = new NotificationMessageResolver();
//var bodyResult = resolver.ParseMessage(settings, NotificationType.NewRequest, r);
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
$"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, };
var message = new MimeMessage
{
Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" },
Body = body.ToMessageBody(),
Subject = $"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
@ -147,9 +145,16 @@ namespace PlexRequests.Services.Notification
private async Task EmailIssue(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Plex Requests: New issue for {model.Title}!",
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, };
var message = new MimeMessage
{
Body = new TextPart("plain") { Text = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!" },
Body = body.ToMessageBody(),
Subject = $"Plex Requests: New issue for {model.Title}!"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
@ -165,10 +170,16 @@ namespace PlexRequests.Services.Notification
{
await Task.FromResult(false);
}
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
$"Plex Requests: {model.Title} is now available!",
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, };
var message = new MimeMessage
{
Body = new TextPart("plain") { Text = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" },
Body = body.ToMessageBody(),
Subject = $"Plex Requests: {model.Title} is now available!"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
@ -206,10 +217,15 @@ namespace PlexRequests.Services.Notification
private async Task EmailTest(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Test Message",
"This is just a test! Success!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, };
var message = new MimeMessage
{
Body = new TextPart("plain") { Text = "This is just a test! Success!" },
Subject = "Plex Requests: Test Message!",
Body = body.ToMessageBody()
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));

@ -75,7 +75,7 @@ namespace PlexRequests.Services.Notification
if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
{
Log.Info("This user is the Plex server owner");
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath);
return;
}
@ -88,7 +88,7 @@ namespace PlexRequests.Services.Notification
}
Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath);
}
}
}
@ -117,7 +117,7 @@ namespace PlexRequests.Services.Notification
if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
{
Log.Info("This user is the Plex server owner");
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath);
return;
}
@ -130,7 +130,7 @@ namespace PlexRequests.Services.Notification
}
Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath);
}
}
catch (Exception e)
@ -139,14 +139,15 @@ namespace PlexRequests.Services.Notification
}
}
private async Task PublishUserNotification(string username, string email, string title)
private async Task PublishUserNotification(string username, string email, string title, string img)
{
var notificationModel = new NotificationModel
{
User = username,
UserEmail = email,
NotificationType = NotificationType.RequestAvailable,
Title = title
Title = title,
ImgSrc = img
};
// Send the notification to the user.

@ -40,5 +40,6 @@ namespace PlexRequests.Services.Notification
public string User { get; set; }
public string UserEmail { get; set; }
public RequestType RequestType { get; set; }
public string ImgSrc { get; set; }
}
}

@ -80,6 +80,7 @@ namespace PlexRequests.UI.Tests
private Mock<ISettingsService<LandingPageSettings>> LandingPageSettings { get; set; }
private Mock<ISlackApi> SlackApi { get; set; }
private Mock<IAnalytics> Analytics { get; set; }
private Mock<ISettingsService<NotificationSettingsV2>> NotifyV2 { get; set; }
private ConfigurableBootstrapper Bootstrapper { get; set; }
@ -120,6 +121,7 @@ namespace PlexRequests.UI.Tests
ScheduledJobsSettingsMock = new Mock<ISettingsService<ScheduledJobsSettings>>();
RecorderMock = new Mock<IJobRecord>();
Analytics = new Mock<IAnalytics>();
NotifyV2= new Mock<ISettingsService<NotificationSettingsV2>>();
Bootstrapper = new ConfigurableBootstrapper(with =>
@ -140,6 +142,7 @@ namespace PlexRequests.UI.Tests
with.Dependency(LogRepo.Object);
with.Dependency(PushoverSettings.Object);
with.Dependency(PushoverApi.Object);
with.Dependency(NotifyV2.Object);
with.Dependency(NotificationService.Object);
with.Dependency(Analytics.Object);
with.Dependency(HeadphonesSettings.Object);

@ -94,6 +94,7 @@ namespace PlexRequests.UI.Modules
private ISlackApi SlackApi { get; }
private IJobRecord JobRecorder { get; }
private IAnalytics Analytics { get; }
private ISettingsService<NotificationSettingsV2> NotifySettings { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
public AdminModule(ISettingsService<PlexRequestSettings> prService,
@ -116,7 +117,8 @@ namespace PlexRequests.UI.Modules
ISettingsService<LogSettings> logs,
ICacheProvider cache, ISettingsService<SlackNotificationSettings> slackSettings,
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics) : base("admin", prService)
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
ISettingsService<NotificationSettingsV2> notifyService) : base("admin", prService)
{
PrService = prService;
CpService = cpService;
@ -143,6 +145,7 @@ namespace PlexRequests.UI.Modules
ScheduledJobSettings = scheduler;
JobRecorder = rec;
Analytics = analytics;
NotifySettings = notifyService;
this.RequiresClaims(UserClaims.Admin);
@ -210,6 +213,9 @@ namespace PlexRequests.UI.Modules
Post["/scheduledjobs", true] = async (x, ct) => await SaveScheduledJobs();
Post["/clearlogs", true] = async (x, ct) => await ClearLogs();
Get["/notificationsettings", true] = async (x, ct) => await NotificationSettings();
Post["/notificationsettings", true] = async (x, ct) => await SaveNotificationSettings();
}
private async Task<Negotiator> Authentication()
@ -489,7 +495,8 @@ namespace PlexRequests.UI.Modules
var notificationModel = new NotificationModel
{
NotificationType = NotificationType.Test,
DateTime = DateTime.Now
DateTime = DateTime.Now,
ImgSrc = "http://3.bp.blogspot.com/-EFM-XoKoZ0o/UznF567wCRI/AAAAAAAAALM/6ut7MCF2LrU/s1600/xkcd.png"
};
try
{
@ -966,5 +973,17 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
}
}
private async Task<Negotiator> NotificationSettings()
{
var s = await NotifySettings.GetSettingsAsync();
return View["NotificationSettings", s];
}
private async Task<Negotiator> SaveNotificationSettings()
{
var model = this.Bind<NotificationSettingsV2>();
return View["NotificationSettings", model];
}
}
}

@ -707,6 +707,9 @@
<Content Include="Views\UserWizard\Index.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\Admin\NotificationSettings.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Web.Debug.config">
<DependentUpon>web.config</DependentUpon>
</None>

@ -0,0 +1,84 @@
@using System.Linq
@using PlexRequests.Core.Models
@using PlexRequests.UI.Helpers
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<PlexRequests.Core.SettingModels.NotificationSettingsV2>
@Html.Partial("_Sidebar")
<div class="col-sm-8 col-sm-push-1">
<form class="form-horizontal" method="POST" id="mainForm">
<fieldset>
<legend>Notification Settings</legend>
<!--Accordion Item-->
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="0headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#0collapseOne" aria-controls="0collapseOne">
New Request
</a>
</h4>
</div>
<div id="0collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="0headingOne">
<div class="panel-body">
<div class="form-group">
<label for="EmailNotification[0].Subject" class="control-label">Subject</label>
<div>
<input type="text" class="form-control form-control-custom " id="EmailNotification[0].Subject" name="EmailNotification0.Subject" value="@(Model.EmailNotification[0].Subject)">
</div>
</div>
<div class="form-group">
<label for="EmailNotification[0].Body" class="control-label">Body</label>
<div>
<input type="text" class="form-control form-control-custom " id="EmailNotification[0].Body" name="EmailNotification0.Body" value="@(Model.EmailNotification[0].Body)">
</div>
</div>
</div>
</div>
</div>
}
</div>
<div class="form-group">
<div>
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</fieldset>
</form>
</div>
<script>
$(function () {
var base = '@Html.GetBaseUrl()';
$('#save').click(function (e) {
e.preventDefault();
var $form = $("#mainForm");
var data = $form.serialize();
$.ajax({
type: $form.prop("method"),
data: data,
url: $form.prop("action"),
dataType: "json",
success: function (response) {
if (response.result === true) {
generateNotify(response.message, "success");
} else {
generateNotify(response.message, "warning");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
}
});
});
});
</script>

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlexRequests.UI", "PlexRequests.UI\PlexRequests.UI.csproj", "{68F5F5F3-B8BB-4911-875F-6F00AAE04EA6}"
EndProject

Loading…
Cancel
Save