mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-13 18:16:55 -07:00
Merge pull request #110 from Drewster727/dev
misc changes (sorting, ui, requesting, testing, other)
This commit is contained in:
commit
0b1a589a70
31 changed files with 895 additions and 231 deletions
|
@ -33,7 +33,7 @@ namespace PlexRequests.Core
|
|||
public interface IRequestService
|
||||
{
|
||||
long AddRequest(RequestedModel model);
|
||||
bool CheckRequest(int providerId);
|
||||
RequestedModel CheckRequest(int providerId);
|
||||
void DeleteRequest(RequestedModel request);
|
||||
bool UpdateRequest(RequestedModel model);
|
||||
RequestedModel Get(int id);
|
||||
|
|
|
@ -58,10 +58,11 @@ namespace PlexRequests.Core
|
|||
return result ? id : -1;
|
||||
}
|
||||
|
||||
public bool CheckRequest(int providerId)
|
||||
public RequestedModel CheckRequest(int providerId)
|
||||
{
|
||||
var blobs = Repo.GetAll();
|
||||
return blobs.Any(x => x.ProviderId == providerId);
|
||||
var blob = blobs.FirstOrDefault(x => x.ProviderId == providerId);
|
||||
return blob != null ? ByteConverterHelper.ReturnObject<RequestedModel>(blob.Content) : null;
|
||||
}
|
||||
|
||||
public void DeleteRequest(RequestedModel request)
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PlexRequests.Core.SettingModels
|
||||
{
|
||||
public class PlexRequestSettings : Settings
|
||||
|
@ -36,6 +40,29 @@ namespace PlexRequests.Core.SettingModels
|
|||
public bool RequireMovieApproval { get; set; }
|
||||
public bool RequireTvShowApproval { get; set; }
|
||||
public bool RequireMusicApproval { get; set; }
|
||||
public bool UsersCanViewOnlyOwnRequests { get; set; }
|
||||
public int WeeklyRequestLimit { get; set; }
|
||||
public string NoApprovalUsers { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public List<string> NoApprovalUserList
|
||||
{
|
||||
get
|
||||
{
|
||||
var users = new List<string>();
|
||||
if (string.IsNullOrEmpty(NoApprovalUsers))
|
||||
{
|
||||
return users;
|
||||
}
|
||||
|
||||
var splitUsers = NoApprovalUsers.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var user in splitUsers)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(user))
|
||||
users.Add(user.Trim());
|
||||
}
|
||||
return users;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using PlexRequests.Services.Notification;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
|
||||
namespace PlexRequests.Services.Interfaces
|
||||
{
|
||||
|
@ -35,5 +36,7 @@ namespace PlexRequests.Services.Interfaces
|
|||
string NotificationName { get; }
|
||||
|
||||
Task NotifyAsync(NotificationModel model);
|
||||
|
||||
Task NotifyAsync(NotificationModel model, Settings settings);
|
||||
}
|
||||
}
|
|
@ -27,12 +27,14 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using PlexRequests.Services.Notification;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
|
||||
namespace PlexRequests.Services.Interfaces
|
||||
{
|
||||
public interface INotificationService
|
||||
{
|
||||
Task Publish(NotificationModel model);
|
||||
Task Publish(NotificationModel model, Settings settings);
|
||||
void Subscribe(INotification notification);
|
||||
void UnSubscribe(INotification notification);
|
||||
|
||||
|
|
|
@ -46,24 +46,29 @@ namespace PlexRequests.Services.Notification
|
|||
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private ISettingsService<EmailNotificationSettings> EmailNotificationSettings { get; }
|
||||
private EmailNotificationSettings Settings => GetConfiguration();
|
||||
public string NotificationName => "EmailMessageNotification";
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model)
|
||||
{
|
||||
var configuration = GetConfiguration();
|
||||
if (!ValidateConfiguration(configuration))
|
||||
{
|
||||
return;
|
||||
}
|
||||
await NotifyAsync(model, configuration);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||
{
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
var emailSettings = (EmailNotificationSettings)settings;
|
||||
|
||||
if (!ValidateConfiguration(emailSettings)) return;
|
||||
|
||||
switch (model.NotificationType)
|
||||
{
|
||||
case NotificationType.NewRequest:
|
||||
await EmailNewRequest(model);
|
||||
await EmailNewRequest(model, emailSettings);
|
||||
break;
|
||||
case NotificationType.Issue:
|
||||
await EmailIssue(model);
|
||||
await EmailIssue(model, emailSettings);
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
throw new NotImplementedException();
|
||||
|
@ -74,6 +79,10 @@ namespace PlexRequests.Services.Notification
|
|||
case NotificationType.AdminNote:
|
||||
throw new NotImplementedException();
|
||||
|
||||
case NotificationType.Test:
|
||||
await EmailTest(model, emailSettings);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
@ -100,23 +109,23 @@ namespace PlexRequests.Services.Notification
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task EmailNewRequest(NotificationModel model)
|
||||
private async Task EmailNewRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||
{
|
||||
var message = new MailMessage
|
||||
{
|
||||
IsBodyHtml = true,
|
||||
To = { new MailAddress(Settings.RecipientEmail) },
|
||||
To = { new MailAddress(settings.RecipientEmail) },
|
||||
Body = $"Hello! The user '{model.User}' has requested {model.Title}! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}",
|
||||
From = new MailAddress(Settings.EmailSender),
|
||||
From = new MailAddress(settings.EmailSender),
|
||||
Subject = $"Plex Requests: New request for {model.Title}!"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using (var smtp = new SmtpClient(Settings.EmailHost, Settings.EmailPort))
|
||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
||||
{
|
||||
smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword);
|
||||
smtp.EnableSsl = Settings.Ssl;
|
||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
||||
smtp.EnableSsl = settings.Ssl;
|
||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@ -130,23 +139,53 @@ namespace PlexRequests.Services.Notification
|
|||
}
|
||||
}
|
||||
|
||||
private async Task EmailIssue(NotificationModel model)
|
||||
private async Task EmailIssue(NotificationModel model, EmailNotificationSettings settings)
|
||||
{
|
||||
var message = new MailMessage
|
||||
{
|
||||
IsBodyHtml = true,
|
||||
To = { new MailAddress(Settings.RecipientEmail) },
|
||||
To = { new MailAddress(settings.RecipientEmail) },
|
||||
Body = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
|
||||
From = new MailAddress(Settings.RecipientEmail),
|
||||
From = new MailAddress(settings.RecipientEmail),
|
||||
Subject = $"Plex Requests: New issue for {model.Title}!"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using (var smtp = new SmtpClient(Settings.EmailHost, Settings.EmailPort))
|
||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
||||
{
|
||||
smtp.Credentials = new NetworkCredential(Settings.EmailUsername, Settings.EmailPassword);
|
||||
smtp.EnableSsl = Settings.Ssl;
|
||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
||||
smtp.EnableSsl = settings.Ssl;
|
||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (SmtpException smtp)
|
||||
{
|
||||
Log.Error(smtp);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EmailTest(NotificationModel model, EmailNotificationSettings settings)
|
||||
{
|
||||
var message = new MailMessage
|
||||
{
|
||||
IsBodyHtml = true,
|
||||
To = { new MailAddress(settings.RecipientEmail) },
|
||||
Body = "This is just a test! Success!",
|
||||
From = new MailAddress(settings.RecipientEmail),
|
||||
Subject = "Plex Requests: Test Message!"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using (var smtp = new SmtpClient(settings.EmailHost, settings.EmailPort))
|
||||
{
|
||||
smtp.Credentials = new NetworkCredential(settings.EmailUsername, settings.EmailPassword);
|
||||
smtp.EnableSsl = settings.Ssl;
|
||||
await smtp.SendMailAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ using System.Threading.Tasks;
|
|||
using NLog;
|
||||
|
||||
using PlexRequests.Services.Interfaces;
|
||||
using PlexRequests.Core.SettingModels;
|
||||
|
||||
namespace PlexRequests.Services.Notification
|
||||
{
|
||||
|
@ -47,6 +48,13 @@ namespace PlexRequests.Services.Notification
|
|||
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task Publish(NotificationModel model, Settings settings)
|
||||
{
|
||||
var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings));
|
||||
|
||||
await Task.WhenAll(notificationTasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Subscribe(INotification notification)
|
||||
{
|
||||
Observers.TryAdd(notification.NotificationName, notification);
|
||||
|
@ -67,6 +75,19 @@ namespace PlexRequests.Services.Notification
|
|||
{
|
||||
Log.Error(ex, $"Notification '{notification.NotificationName}' failed with exception");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static async Task NotifyAsync(INotification notification, NotificationModel model, Settings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
await notification.NotifyAsync(model, settings).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Notification '{notification.NotificationName}' failed with exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,5 +33,6 @@ namespace PlexRequests.Services.Notification
|
|||
RequestAvailable,
|
||||
RequestApproved,
|
||||
AdminNote,
|
||||
Test
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,18 +51,25 @@ namespace PlexRequests.Services.Notification
|
|||
public string NotificationName => "PushbulletNotification";
|
||||
public async Task NotifyAsync(NotificationModel model)
|
||||
{
|
||||
if (!ValidateConfiguration())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var configuration = GetSettings();
|
||||
await NotifyAsync(model, configuration);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||
{
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
var pushSettings = (PushbulletNotificationSettings)settings;
|
||||
|
||||
if (!ValidateConfiguration(pushSettings)) return;
|
||||
|
||||
switch (model.NotificationType)
|
||||
{
|
||||
case NotificationType.NewRequest:
|
||||
await PushNewRequestAsync(model);
|
||||
await PushNewRequestAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.Issue:
|
||||
await PushIssueAsync(model);
|
||||
await PushIssueAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
break;
|
||||
|
@ -70,18 +77,21 @@ namespace PlexRequests.Services.Notification
|
|||
break;
|
||||
case NotificationType.AdminNote:
|
||||
break;
|
||||
case NotificationType.Test:
|
||||
await PushTestAsync(model, pushSettings);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateConfiguration()
|
||||
private bool ValidateConfiguration(PushbulletNotificationSettings settings)
|
||||
{
|
||||
if (!Settings.Enabled)
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(Settings.AccessToken))
|
||||
if (string.IsNullOrEmpty(settings.AccessToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -93,13 +103,13 @@ namespace PlexRequests.Services.Notification
|
|||
return SettingsService.GetSettings();
|
||||
}
|
||||
|
||||
private async Task PushNewRequestAsync(NotificationModel model)
|
||||
private async Task PushNewRequestAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
||||
{
|
||||
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||
var pushTitle = $"Plex Requests: {model.Title} has been requested!";
|
||||
try
|
||||
{
|
||||
var result = await PushbulletApi.PushAsync(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier);
|
||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
||||
if (result == null)
|
||||
{
|
||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
||||
|
@ -111,13 +121,31 @@ namespace PlexRequests.Services.Notification
|
|||
}
|
||||
}
|
||||
|
||||
private async Task PushIssueAsync(NotificationModel model)
|
||||
private async Task PushIssueAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
||||
{
|
||||
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||
var pushTitle = $"Plex Requests: A new issue has been reported for {model.Title}";
|
||||
try
|
||||
{
|
||||
var result = await PushbulletApi.PushAsync(Settings.AccessToken, pushTitle, message, Settings.DeviceIdentifier);
|
||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
||||
if (result != null)
|
||||
{
|
||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PushTestAsync(NotificationModel model, PushbulletNotificationSettings settings)
|
||||
{
|
||||
var message = "This is just a test! Success!";
|
||||
var pushTitle = "Plex Requests: Test Message!";
|
||||
try
|
||||
{
|
||||
var result = await PushbulletApi.PushAsync(settings.AccessToken, pushTitle, message, settings.DeviceIdentifier);
|
||||
if (result != null)
|
||||
{
|
||||
Log.Error("Pushbullet api returned a null value, the notification did not get pushed");
|
||||
|
|
|
@ -51,18 +51,25 @@ namespace PlexRequests.Services.Notification
|
|||
public string NotificationName => "PushoverNotification";
|
||||
public async Task NotifyAsync(NotificationModel model)
|
||||
{
|
||||
if (!ValidateConfiguration())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var configuration = GetSettings();
|
||||
await NotifyAsync(model, configuration);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model, Settings settings)
|
||||
{
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
var pushSettings = (PushoverNotificationSettings)settings;
|
||||
|
||||
if (!ValidateConfiguration(pushSettings)) return;
|
||||
|
||||
switch (model.NotificationType)
|
||||
{
|
||||
case NotificationType.NewRequest:
|
||||
await PushNewRequestAsync(model);
|
||||
await PushNewRequestAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.Issue:
|
||||
await PushIssueAsync(model);
|
||||
await PushIssueAsync(model, pushSettings);
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
break;
|
||||
|
@ -70,18 +77,21 @@ namespace PlexRequests.Services.Notification
|
|||
break;
|
||||
case NotificationType.AdminNote:
|
||||
break;
|
||||
case NotificationType.Test:
|
||||
await PushTestAsync(model, pushSettings);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateConfiguration()
|
||||
private bool ValidateConfiguration(PushoverNotificationSettings settings)
|
||||
{
|
||||
if (!Settings.Enabled)
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(Settings.AccessToken) || string.IsNullOrEmpty(Settings.UserToken))
|
||||
if (string.IsNullOrEmpty(settings.AccessToken) || string.IsNullOrEmpty(settings.UserToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -93,12 +103,12 @@ namespace PlexRequests.Services.Notification
|
|||
return SettingsService.GetSettings();
|
||||
}
|
||||
|
||||
private async Task PushNewRequestAsync(NotificationModel model)
|
||||
private async Task PushNewRequestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||
{
|
||||
var message = $"Plex Requests: {model.Title} has been requested by user: {model.User}";
|
||||
try
|
||||
{
|
||||
var result = await PushoverApi.PushAsync(Settings.AccessToken, message, Settings.UserToken);
|
||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
||||
if (result?.status != 1)
|
||||
{
|
||||
Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
|
||||
|
@ -110,12 +120,29 @@ namespace PlexRequests.Services.Notification
|
|||
}
|
||||
}
|
||||
|
||||
private async Task PushIssueAsync(NotificationModel model)
|
||||
private async Task PushIssueAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||
{
|
||||
var message = $"Plex Requests: A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||
try
|
||||
{
|
||||
var result = await PushoverApi.PushAsync(Settings.AccessToken, message, Settings.UserToken);
|
||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
||||
if (result?.status != 1)
|
||||
{
|
||||
Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PushTestAsync(NotificationModel model, PushoverNotificationSettings settings)
|
||||
{
|
||||
var message = $"Plex Requests: Test Message!";
|
||||
try
|
||||
{
|
||||
var result = await PushoverApi.PushAsync(settings.AccessToken, message, settings.UserToken);
|
||||
if (result?.status != 1)
|
||||
{
|
||||
Log.Error("Pushover api returned a status that was not 1, the notification did not get pushed");
|
||||
|
|
|
@ -2,12 +2,20 @@
|
|||
using System.Security.Cryptography;
|
||||
|
||||
using Dapper.Contrib.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PlexRequests.Store
|
||||
{
|
||||
[Table("Requested")]
|
||||
public class RequestedModel : Entity
|
||||
{
|
||||
public RequestedModel()
|
||||
{
|
||||
RequestedUsers = new List<string>();
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
public int ProviderId { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
|
@ -18,7 +26,10 @@ namespace PlexRequests.Store
|
|||
public RequestType Type { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
|
||||
[Obsolete("Use RequestedUsers")]
|
||||
public string RequestedBy { get; set; }
|
||||
|
||||
public DateTime RequestedDate { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public IssueState Issues { get; set; }
|
||||
|
@ -27,6 +38,40 @@ namespace PlexRequests.Store
|
|||
public int[] SeasonList { get; set; }
|
||||
public int SeasonCount { get; set; }
|
||||
public string SeasonsRequested { get; set; }
|
||||
public List<string> RequestedUsers { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public List<string> AllUsers
|
||||
{
|
||||
get
|
||||
{
|
||||
var u = new List<string>();
|
||||
if (!string.IsNullOrEmpty(RequestedBy))
|
||||
{
|
||||
u.Add(RequestedBy);
|
||||
}
|
||||
|
||||
if (RequestedUsers.Any())
|
||||
{
|
||||
u.AddRange(RequestedUsers.Where(requestedUser => requestedUser != RequestedBy));
|
||||
}
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool CanApprove
|
||||
{
|
||||
get
|
||||
{
|
||||
return !Approved && !Available;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UserHasRequested(string username)
|
||||
{
|
||||
return AllUsers.Any(x => x.Equals(username, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
public enum RequestType
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
|
||||
.form-control-custom {
|
||||
background-color: #4e5d6c !important;
|
||||
color: white !important; }
|
||||
color: white !important;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 0 !important; }
|
||||
|
||||
h1 {
|
||||
font-size: 3.5rem !important;
|
||||
|
@ -40,6 +42,18 @@ label {
|
|||
margin-bottom: 0.5rem !important;
|
||||
font-size: 16px !important; }
|
||||
|
||||
.nav-tabs > li.active > a,
|
||||
.nav-tabs > li.active > a:hover,
|
||||
.nav-tabs > li.active > a:focus {
|
||||
background: #4e5d6c; }
|
||||
|
||||
.navbar .nav a .fa {
|
||||
font-size: 130%;
|
||||
top: 1px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 5px; }
|
||||
|
||||
.btn-danger-outline {
|
||||
color: #d9534f !important;
|
||||
background-color: transparent;
|
||||
|
@ -162,3 +176,14 @@ label {
|
|||
.scroll-top-wrapper i.fa {
|
||||
line-height: inherit; }
|
||||
|
||||
.no-search-results {
|
||||
text-align: center; }
|
||||
|
||||
.no-search-results .no-search-results-icon {
|
||||
font-size: 10em;
|
||||
color: #4e5d6c; }
|
||||
|
||||
.no-search-results .no-search-results-text {
|
||||
margin: 20px 0;
|
||||
color: #ccc; }
|
||||
|
||||
|
|
2
PlexRequests.UI/Content/custom.min.css
vendored
2
PlexRequests.UI/Content/custom.min.css
vendored
|
@ -1 +1 @@
|
|||
@media(min-width:768px){.row{position:relative;}.bottom-align-text{position:absolute;bottom:0;right:0;}}@media(max-width:48em){.home{padding-top:1rem;}}@media(min-width:48em){.home{padding-top:4rem;}}.btn{border-radius:.25rem !important;}.multiSelect{background-color:#4e5d6c;}.form-control-custom{background-color:#4e5d6c !important;color:#fff !important;}h1{font-size:3.5rem !important;font-weight:600 !important;}.request-title{margin-top:0 !important;font-size:1.9rem !important;}p{font-size:1.1rem !important;}label{display:inline-block !important;margin-bottom:.5rem !important;font-size:16px !important;}.btn-danger-outline{color:#d9534f !important;background-color:transparent;background-image:none;border-color:#d9534f !important;}.btn-danger-outline:focus,.btn-danger-outline.focus,.btn-danger-outline:active,.btn-danger-outline.active,.btn-danger-outline:hover,.open>.btn-danger-outline.dropdown-toggle{color:#fff !important;background-color:#d9534f !important;border-color:#d9534f !important;}.btn-primary-outline{color:#ff761b !important;background-color:transparent;background-image:none;border-color:#ff761b !important;}.btn-primary-outline:focus,.btn-primary-outline.focus,.btn-primary-outline:active,.btn-primary-outline.active,.btn-primary-outline:hover,.open>.btn-primary-outline.dropdown-toggle{color:#fff !important;background-color:#df691a !important;border-color:#df691a !important;}.btn-info-outline{color:#5bc0de !important;background-color:transparent;background-image:none;border-color:#5bc0de !important;}.btn-info-outline:focus,.btn-info-outline.focus,.btn-info-outline:active,.btn-info-outline.active,.btn-info-outline:hover,.open>.btn-info-outline.dropdown-toggle{color:#fff !important;background-color:#5bc0de !important;border-color:#5bc0de !important;}.btn-warning-outline{color:#f0ad4e !important;background-color:transparent;background-image:none;border-color:#f0ad4e !important;}.btn-warning-outline:focus,.btn-warning-outline.focus,.btn-warning-outline:active,.btn-warning-outline.active,.btn-warning-outline:hover,.open>.btn-warning-outline.dropdown-toggle{color:#fff !important;background-color:#f0ad4e !important;border-color:#f0ad4e !important;}.btn-success-outline{color:#5cb85c !important;background-color:transparent;background-image:none;border-color:#5cb85c !important;}.btn-success-outline:focus,.btn-success-outline.focus,.btn-success-outline:active,.btn-success-outline.active,.btn-success-outline:hover,.open>.btn-success-outline.dropdown-toggle{color:#fff !important;background-color:#5cb85c !important;border-color:#5cb85c !important;}#movieList .mix{display:none;}#tvList .mix{display:none;}.scroll-top-wrapper{position:fixed;opacity:0;visibility:hidden;overflow:hidden;text-align:center;z-index:99999999;background-color:#4e5d6c;color:#eee;width:50px;height:48px;line-height:48px;right:30px;bottom:30px;padding-top:2px;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;}.scroll-top-wrapper:hover{background-color:#637689;}.scroll-top-wrapper.show{visibility:visible;cursor:pointer;opacity:1;}.scroll-top-wrapper i.fa{line-height:inherit;}
|
||||
@media(min-width:768px){.row{position:relative;}.bottom-align-text{position:absolute;bottom:0;right:0;}}@media(max-width:48em){.home{padding-top:1rem;}}@media(min-width:48em){.home{padding-top:4rem;}}.btn{border-radius:.25rem !important;}.multiSelect{background-color:#4e5d6c;}.form-control-custom{background-color:#4e5d6c !important;color:#fff !important;border-radius:0;box-shadow:0 0 0 !important;}h1{font-size:3.5rem !important;font-weight:600 !important;}.request-title{margin-top:0 !important;font-size:1.9rem !important;}p{font-size:1.1rem !important;}label{display:inline-block !important;margin-bottom:.5rem !important;font-size:16px !important;}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{background:#4e5d6c;}.navbar .nav a .fa{font-size:130%;top:1px;position:relative;display:inline-block;margin-right:5px;}.btn-danger-outline{color:#d9534f !important;background-color:transparent;background-image:none;border-color:#d9534f !important;}.btn-danger-outline:focus,.btn-danger-outline.focus,.btn-danger-outline:active,.btn-danger-outline.active,.btn-danger-outline:hover,.open>.btn-danger-outline.dropdown-toggle{color:#fff !important;background-color:#d9534f !important;border-color:#d9534f !important;}.btn-primary-outline{color:#ff761b !important;background-color:transparent;background-image:none;border-color:#ff761b !important;}.btn-primary-outline:focus,.btn-primary-outline.focus,.btn-primary-outline:active,.btn-primary-outline.active,.btn-primary-outline:hover,.open>.btn-primary-outline.dropdown-toggle{color:#fff !important;background-color:#df691a !important;border-color:#df691a !important;}.btn-info-outline{color:#5bc0de !important;background-color:transparent;background-image:none;border-color:#5bc0de !important;}.btn-info-outline:focus,.btn-info-outline.focus,.btn-info-outline:active,.btn-info-outline.active,.btn-info-outline:hover,.open>.btn-info-outline.dropdown-toggle{color:#fff !important;background-color:#5bc0de !important;border-color:#5bc0de !important;}.btn-warning-outline{color:#f0ad4e !important;background-color:transparent;background-image:none;border-color:#f0ad4e !important;}.btn-warning-outline:focus,.btn-warning-outline.focus,.btn-warning-outline:active,.btn-warning-outline.active,.btn-warning-outline:hover,.open>.btn-warning-outline.dropdown-toggle{color:#fff !important;background-color:#f0ad4e !important;border-color:#f0ad4e !important;}.btn-success-outline{color:#5cb85c !important;background-color:transparent;background-image:none;border-color:#5cb85c !important;}.btn-success-outline:focus,.btn-success-outline.focus,.btn-success-outline:active,.btn-success-outline.active,.btn-success-outline:hover,.open>.btn-success-outline.dropdown-toggle{color:#fff !important;background-color:#5cb85c !important;border-color:#5cb85c !important;}#movieList .mix{display:none;}#tvList .mix{display:none;}.scroll-top-wrapper{position:fixed;opacity:0;visibility:hidden;overflow:hidden;text-align:center;z-index:99999999;background-color:#4e5d6c;color:#eee;width:50px;height:48px;line-height:48px;right:30px;bottom:30px;padding-top:2px;border-top-left-radius:10px;border-top-right-radius:10px;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;}.scroll-top-wrapper:hover{background-color:#637689;}.scroll-top-wrapper.show{visibility:visible;cursor:pointer;opacity:1;}.scroll-top-wrapper i.fa{line-height:inherit;}.no-search-results{text-align:center;}.no-search-results .no-search-results-icon{font-size:10em;color:#4e5d6c;}.no-search-results .no-search-results-text{margin:20px 0;color:#ccc;}
|
|
@ -6,7 +6,9 @@ $info-colour: #5bc0de;
|
|||
$warning-colour: #f0ad4e;
|
||||
$danger-colour: #d9534f;
|
||||
$success-colour: #5cb85c;
|
||||
$i:!important;
|
||||
$i:
|
||||
!important
|
||||
;
|
||||
|
||||
@media (min-width: 768px ) {
|
||||
.row {
|
||||
|
@ -43,6 +45,8 @@ $i:!important;
|
|||
.form-control-custom {
|
||||
background-color: $form-color $i;
|
||||
color: white $i;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 0 !important;
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,6 +70,20 @@ label {
|
|||
font-size: 16px $i;
|
||||
}
|
||||
|
||||
.nav-tabs > li.active > a,
|
||||
.nav-tabs > li.active > a:hover,
|
||||
.nav-tabs > li.active > a:focus {
|
||||
background: #4e5d6c;
|
||||
}
|
||||
|
||||
.navbar .nav a .fa {
|
||||
font-size: 130%;
|
||||
top: 1px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.btn-danger-outline {
|
||||
color: $danger-colour $i;
|
||||
background-color: transparent;
|
||||
|
@ -157,10 +175,11 @@ label {
|
|||
border-color: $success-colour $i;
|
||||
}
|
||||
|
||||
#movieList .mix{
|
||||
#movieList .mix {
|
||||
display: none;
|
||||
}
|
||||
#tvList .mix{
|
||||
|
||||
#tvList .mix {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -202,4 +221,18 @@ $border-radius: 10px;
|
|||
.scroll-top-wrapper i.fa {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.no-search-results {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.no-search-results .no-search-results-icon {
|
||||
font-size: 10em;
|
||||
color: $form-color;
|
||||
}
|
||||
|
||||
.no-search-results .no-search-results-text {
|
||||
margin: 20px 0;
|
||||
color: #ccc;
|
||||
}
|
|
@ -9,68 +9,122 @@ var searchSource = $("#search-template").html();
|
|||
var searchTemplate = Handlebars.compile(searchSource);
|
||||
var movieTimer = 0;
|
||||
var tvimer = 0;
|
||||
var mixItUpDefault = {
|
||||
animation: { enable: true },
|
||||
load: {
|
||||
filter: 'all',
|
||||
sort: 'requestorder:desc'
|
||||
},
|
||||
layout: {
|
||||
display: 'block'
|
||||
},
|
||||
callbacks: {
|
||||
onMixStart: function (state, futureState) {
|
||||
$('.mix', this).removeAttr('data-bound').removeData('bound'); // fix for animation issues in other tabs
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
movieLoad();
|
||||
tvLoad();
|
||||
initLoad();
|
||||
|
||||
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr('href');
|
||||
var activeState = "";
|
||||
if (target === "#TvShowTab") {
|
||||
if ($('#movieList').mixItUp('isLoaded')) {
|
||||
activeState = $('#movieList').mixItUp('getState');
|
||||
$('#movieList').mixItUp('destroy');
|
||||
}
|
||||
if (!$('#tvList').mixItUp('isLoaded')) {
|
||||
$('#tvList').mixItUp({
|
||||
load: {
|
||||
filter: activeState.activeFilter || 'all',
|
||||
sort: activeState.activeSort || 'default:asc'
|
||||
},
|
||||
layout: {
|
||||
display: 'block'
|
||||
}
|
||||
|
||||
});
|
||||
var $ml = $('#movieList');
|
||||
var $tvl = $('#tvList');
|
||||
|
||||
$('.approve-category').hide();
|
||||
if (target === "#TvShowTab") {
|
||||
$('#approveTVShows').show();
|
||||
if ($ml.mixItUp('isLoaded')) {
|
||||
activeState = $ml.mixItUp('getState');
|
||||
$ml.mixItUp('destroy');
|
||||
}
|
||||
if ($tvl.mixItUp('isLoaded')) $tvl.mixItUp('destroy');
|
||||
$tvl.mixItUp(mixItUpConfig(activeState)); // init or reinit
|
||||
}
|
||||
if (target === "#MoviesTab") {
|
||||
if ($('#tvList').mixItUp('isLoaded')) {
|
||||
activeState = $('#tvList').mixItUp('getState');
|
||||
$('#tvList').mixItUp('destroy');
|
||||
}
|
||||
if (!$('#movieList').mixItUp('isLoaded')) {
|
||||
$('#movieList').mixItUp({
|
||||
load: {
|
||||
filter: activeState.activeFilter || 'all',
|
||||
sort: activeState.activeSort || 'default:asc'
|
||||
},
|
||||
layout: {
|
||||
display: 'block'
|
||||
}
|
||||
});
|
||||
$('#approveMovies').show();
|
||||
if ($tvl.mixItUp('isLoaded')) {
|
||||
activeState = $tvl.mixItUp('getState');
|
||||
$tvl.mixItUp('destroy');
|
||||
}
|
||||
if ($ml.mixItUp('isLoaded')) $ml.mixItUp('destroy');
|
||||
$ml.mixItUp(mixItUpConfig(activeState)); // init or reinit
|
||||
}
|
||||
//$('.mix[data-bound]').removeAttr('data-bound');
|
||||
});
|
||||
|
||||
// Approve all
|
||||
$('#approveAll').click(function () {
|
||||
$('#approveMovies').click(function (e) {
|
||||
e.preventDefault();
|
||||
var buttonId = e.target.id;
|
||||
var origHtml = $(this).html();
|
||||
|
||||
if ($('#' + buttonId).text() === " Loading...") {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingButton(buttonId, "success");
|
||||
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: '/approval/approveall',
|
||||
url: '/approval/approveallmovies',
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
if (checkJsonResponse(response)) {
|
||||
generateNotify("Success! All requests approved!", "success");
|
||||
generateNotify("Success! All Movie requests approved!", "success");
|
||||
movieLoad();
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
},
|
||||
complete: function (e) {
|
||||
finishLoading(buttonId, "success", origHtml)
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#approveTVShows').click(function (e) {
|
||||
e.preventDefault();
|
||||
var buttonId = e.target.id;
|
||||
var origHtml = $(this).html();
|
||||
|
||||
if ($('#' + buttonId).text() === " Loading...") {
|
||||
return;
|
||||
}
|
||||
|
||||
loadingButton(buttonId, "success");
|
||||
|
||||
$.ajax({
|
||||
type: 'post',
|
||||
url: '/approval/approvealltvshows',
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
if (checkJsonResponse(response)) {
|
||||
generateNotify("Success! All TV Show requests approved!", "success");
|
||||
tvLoad();
|
||||
}
|
||||
},
|
||||
error: function (e) {
|
||||
console.log(e);
|
||||
generateNotify("Something went wrong!", "danger");
|
||||
},
|
||||
complete: function (e) {
|
||||
finishLoading(buttonId, "success", origHtml)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// filtering/sorting
|
||||
$('.filter,.sort', '.dropdown-menu').click(function (e) {
|
||||
var $this = $(this);
|
||||
$('.fa-square', $this.parents('.dropdown-menu:first')).removeClass('fa-square').addClass('fa-square-o');
|
||||
$this.children('.fa').first().removeClass('fa-square-o').addClass('fa-square');
|
||||
});
|
||||
|
||||
|
||||
// Report Issue
|
||||
|
@ -315,36 +369,54 @@ $(document).on("click", ".change", function (e) {
|
|||
|
||||
});
|
||||
|
||||
function mixItUpConfig(activeState) {
|
||||
var conf = mixItUpDefault;
|
||||
|
||||
if (activeState) {
|
||||
if (activeState.activeFilter) conf['load']['filter'] = activeState.activeFilter;
|
||||
if (activeState.activeSort) conf['load']['sort'] = activeState.activeSort;
|
||||
}
|
||||
return conf;
|
||||
};
|
||||
|
||||
function initLoad() {
|
||||
movieLoad();
|
||||
tvLoad();
|
||||
}
|
||||
|
||||
function movieLoad() {
|
||||
$("#movieList").html("");
|
||||
var $ml = $('#movieList');
|
||||
if ($ml.mixItUp('isLoaded')) {
|
||||
activeState = $ml.mixItUp('getState');
|
||||
$ml.mixItUp('destroy');
|
||||
}
|
||||
$ml.html("");
|
||||
|
||||
$.ajax("/requests/movies/").success(function (results) {
|
||||
results.forEach(function (result) {
|
||||
var context = buildRequestContext(result, "movie");
|
||||
|
||||
var html = searchTemplate(context);
|
||||
$("#movieList").append(html);
|
||||
});
|
||||
$('#movieList').mixItUp({
|
||||
layout: {
|
||||
display: 'block'
|
||||
},
|
||||
load: {
|
||||
filter: 'all'
|
||||
}
|
||||
$ml.append(html);
|
||||
});
|
||||
$ml.mixItUp(mixItUpConfig());
|
||||
});
|
||||
};
|
||||
|
||||
function tvLoad() {
|
||||
$("#tvList").html("");
|
||||
var $tvl = $('#tvList');
|
||||
if ($tvl.mixItUp('isLoaded')) {
|
||||
activeState = $tvl.mixItUp('getState');
|
||||
$tvl.mixItUp('destroy');
|
||||
}
|
||||
$tvl.html("");
|
||||
|
||||
$.ajax("/requests/tvshows/").success(function (results) {
|
||||
results.forEach(function (result) {
|
||||
var context = buildRequestContext(result, "tv");
|
||||
var html = searchTemplate(context);
|
||||
$("#tvList").append(html);
|
||||
$tvl.append(html);
|
||||
});
|
||||
$tvl.mixItUp(mixItUpConfig());
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -359,9 +431,11 @@ function buildRequestContext(result, type) {
|
|||
type: type,
|
||||
status: result.status,
|
||||
releaseDate: result.releaseDate,
|
||||
releaseDateTicks: result.releaseDateTicks,
|
||||
approved: result.approved,
|
||||
requestedBy: result.requestedBy,
|
||||
requestedUsers: result.requestedUsers ? result.requestedUsers.join(', ') : '',
|
||||
requestedDate: result.requestedDate,
|
||||
requestedDateTicks: result.requestedDateTicks,
|
||||
available: result.available,
|
||||
admin: result.admin,
|
||||
issues: result.issues,
|
||||
|
@ -373,16 +447,4 @@ function buildRequestContext(result, type) {
|
|||
};
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
function startFilter(elementId) {
|
||||
$('#'+element).mixItUp({
|
||||
load: {
|
||||
filter: activeState.activeFilter || 'all',
|
||||
sort: activeState.activeSort || 'default:asc'
|
||||
},
|
||||
layout: {
|
||||
display: 'block'
|
||||
}
|
||||
});
|
||||
}
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
var searchSource = $("#search-template").html();
|
||||
var searchTemplate = Handlebars.compile(searchSource);
|
||||
var noResultsHtml = "<div class='no-search-results'>" +
|
||||
"<i class='fa fa-film no-search-results-icon'></i><div class='no-search-results-text'>Sorry, we didn't find any results!</div></div>";
|
||||
var movieTimer = 0;
|
||||
var tvimer = 0;
|
||||
|
||||
|
@ -124,6 +126,9 @@ function movieSearch() {
|
|||
$("#movieList").append(html);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$("#movieList").html(noResultsHtml);
|
||||
}
|
||||
$('#movieSearchButton').attr("class","fa fa-search");
|
||||
});
|
||||
};
|
||||
|
@ -140,6 +145,9 @@ function tvSearch() {
|
|||
$("#tvList").append(html);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$("#tvList").html(noResultsHtml);
|
||||
}
|
||||
$('#tvSearchButton').attr("class", "fa fa-search");
|
||||
});
|
||||
};
|
||||
|
|
|
@ -37,11 +37,13 @@ namespace PlexRequests.UI.Models
|
|||
public string Title { get; set; }
|
||||
public string PosterPath { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public long ReleaseDateTicks { get; set; }
|
||||
public RequestType Type { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
public string RequestedBy { get; set; }
|
||||
public string[] RequestedUsers { get; set; }
|
||||
public string RequestedDate { get; set; }
|
||||
public long RequestedDateTicks { get; set; }
|
||||
public string ReleaseYear { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public bool Admin { get; set; }
|
||||
|
|
|
@ -53,6 +53,7 @@ using PlexRequests.Store.Models;
|
|||
using PlexRequests.Store.Repository;
|
||||
using PlexRequests.UI.Helpers;
|
||||
using PlexRequests.UI.Models;
|
||||
using System;
|
||||
|
||||
namespace PlexRequests.UI.Modules
|
||||
{
|
||||
|
@ -144,13 +145,16 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
Get["/emailnotification"] = _ => EmailNotifications();
|
||||
Post["/emailnotification"] = _ => SaveEmailNotifications();
|
||||
Post["/testemailnotification"] = _ => TestEmailNotifications();
|
||||
Get["/status"] = _ => Status();
|
||||
|
||||
Get["/pushbulletnotification"] = _ => PushbulletNotifications();
|
||||
Post["/pushbulletnotification"] = _ => SavePushbulletNotifications();
|
||||
Post["/testpushbulletnotification"] = _ => TestPushbulletNotifications();
|
||||
|
||||
Get["/pushovernotification"] = _ => PushoverNotifications();
|
||||
Post["/pushovernotification"] = _ => SavePushoverNotifications();
|
||||
Post["/testpushovernotification"] = _ => TestPushoverNotifications();
|
||||
|
||||
Get["/logs"] = _ => Logs();
|
||||
Get["/loglevel"] = _ => GetLogLevels();
|
||||
|
@ -380,6 +384,37 @@ namespace PlexRequests.UI.Modules
|
|||
return View["EmailNotifications", settings];
|
||||
}
|
||||
|
||||
private Response TestEmailNotifications()
|
||||
{
|
||||
var settings = this.Bind<EmailNotificationSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
var notificationModel = new NotificationModel
|
||||
{
|
||||
NotificationType = NotificationType.Test,
|
||||
DateTime = DateTime.Now
|
||||
};
|
||||
try
|
||||
{
|
||||
NotificationService.Subscribe(new EmailMessageNotification(EmailService));
|
||||
settings.Enabled = true;
|
||||
NotificationService.Publish(notificationModel, settings);
|
||||
Log.Info("Sent email notification test");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Error("Failed to subscribe and publish test Email Notification");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NotificationService.UnSubscribe(new EmailMessageNotification(EmailService));
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Email Notification!" });
|
||||
}
|
||||
|
||||
private Response SaveEmailNotifications()
|
||||
{
|
||||
var settings = this.Bind<EmailNotificationSettings>();
|
||||
|
@ -448,6 +483,37 @@ namespace PlexRequests.UI.Modules
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private Response TestPushbulletNotifications()
|
||||
{
|
||||
var settings = this.Bind<PushbulletNotificationSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
var notificationModel = new NotificationModel
|
||||
{
|
||||
NotificationType = NotificationType.Test,
|
||||
DateTime = DateTime.Now
|
||||
};
|
||||
try
|
||||
{
|
||||
NotificationService.Subscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
||||
settings.Enabled = true;
|
||||
NotificationService.Publish(notificationModel, settings);
|
||||
Log.Info("Sent pushbullet notification test");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Error("Failed to subscribe and publish test Pushbullet Notification");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NotificationService.UnSubscribe(new PushbulletNotification(PushbulletApi, PushbulletService));
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushbullet Notification!" });
|
||||
}
|
||||
|
||||
private Negotiator PushoverNotifications()
|
||||
{
|
||||
var settings = PushoverService.GetSettings();
|
||||
|
@ -480,6 +546,37 @@ namespace PlexRequests.UI.Modules
|
|||
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
|
||||
}
|
||||
|
||||
private Response TestPushoverNotifications()
|
||||
{
|
||||
var settings = this.Bind<PushoverNotificationSettings>();
|
||||
var valid = this.Validate(settings);
|
||||
if (!valid.IsValid)
|
||||
{
|
||||
return Response.AsJson(valid.SendJsonError());
|
||||
}
|
||||
var notificationModel = new NotificationModel
|
||||
{
|
||||
NotificationType = NotificationType.Test,
|
||||
DateTime = DateTime.Now
|
||||
};
|
||||
try
|
||||
{
|
||||
NotificationService.Subscribe(new PushoverNotification(PushoverApi, PushoverService));
|
||||
settings.Enabled = true;
|
||||
NotificationService.Publish(notificationModel, settings);
|
||||
Log.Info("Sent pushover notification test");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Error("Failed to subscribe and publish test Pushover Notification");
|
||||
}
|
||||
finally
|
||||
{
|
||||
NotificationService.UnSubscribe(new PushoverNotification(PushoverApi, PushoverService));
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Pushover Notification!" });
|
||||
}
|
||||
|
||||
private Response GetCpProfiles()
|
||||
{
|
||||
var settings = this.Bind<CouchPotatoSettings>();
|
||||
|
|
|
@ -61,6 +61,8 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
Post["/approve"] = parameters => Approve((int)Request.Form.requestid);
|
||||
Post["/approveall"] = x => ApproveAll();
|
||||
Post["/approveallmovies"] = x => ApproveAllMovies();
|
||||
Post["/approvealltvshows"] = x => ApproveAllTVShows();
|
||||
}
|
||||
|
||||
private IRequestService Service { get; }
|
||||
|
@ -216,6 +218,56 @@ namespace PlexRequests.UI.Modules
|
|||
});
|
||||
}
|
||||
|
||||
private Response ApproveAllMovies()
|
||||
{
|
||||
if (!Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You are not an Admin, so you cannot approve any requests." });
|
||||
}
|
||||
|
||||
var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.Movie);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no movie requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return UpdateRequests(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
private Response ApproveAllTVShows()
|
||||
{
|
||||
if (!Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You are not an Admin, so you cannot approve any requests." });
|
||||
}
|
||||
|
||||
var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.TvShow);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no tv show requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return UpdateRequests(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves all.
|
||||
/// </summary>
|
||||
|
@ -227,23 +279,35 @@ namespace PlexRequests.UI.Modules
|
|||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You are not an Admin, so you cannot approve any requests." });
|
||||
}
|
||||
|
||||
var requests = Service.GetAll().Where(x => x.Approved == false);
|
||||
var requests = Service.GetAll().Where(x => x.CanApprove);
|
||||
var requestedModels = requests as RequestedModel[] ?? requests.ToArray();
|
||||
if (!requestedModels.Any())
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return UpdateRequests(requestedModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Response UpdateRequests(RequestedModel[] requestedModels)
|
||||
{
|
||||
var cpSettings = CpService.GetSettings();
|
||||
|
||||
|
||||
var updatedRequests = new List<RequestedModel>();
|
||||
foreach (var r in requestedModels)
|
||||
{
|
||||
if (r.Type == RequestType.Movie)
|
||||
{
|
||||
var result = SendMovie(cpSettings, r, CpApi);
|
||||
if (result)
|
||||
var res = SendMovie(cpSettings, r, CpApi);
|
||||
if (res)
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
|
@ -260,8 +324,8 @@ namespace PlexRequests.UI.Modules
|
|||
var sonarr = SonarrSettings.GetSettings();
|
||||
if (sr.Enabled)
|
||||
{
|
||||
var result = sender.SendToSickRage(sr, r);
|
||||
if (result?.result == "success")
|
||||
var res = sender.SendToSickRage(sr, r);
|
||||
if (res?.result == "success")
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
|
@ -269,14 +333,14 @@ namespace PlexRequests.UI.Modules
|
|||
else
|
||||
{
|
||||
Log.Error("Could not approve and send the TV {0} to SickRage!", r.Title);
|
||||
Log.Error("SickRage Message: {0}", result?.message);
|
||||
Log.Error("SickRage Message: {0}", res?.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (sonarr.Enabled)
|
||||
{
|
||||
var result = sender.SendToSonarr(sonarr, r);
|
||||
if (!string.IsNullOrEmpty(result?.title))
|
||||
var res = sender.SendToSonarr(sonarr, r);
|
||||
if (!string.IsNullOrEmpty(res?.title))
|
||||
{
|
||||
r.Approved = true;
|
||||
updatedRequests.Add(r);
|
||||
|
@ -284,7 +348,7 @@ namespace PlexRequests.UI.Modules
|
|||
else
|
||||
{
|
||||
Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title);
|
||||
Log.Error("Error message: {0}", result?.ErrorMessage);
|
||||
Log.Error("Error message: {0}", res?.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,17 +356,16 @@ namespace PlexRequests.UI.Modules
|
|||
try
|
||||
{
|
||||
|
||||
var result = Service.BatchUpdate(updatedRequests); return Response.AsJson(result
|
||||
var result = Service.BatchUpdate(updatedRequests);
|
||||
return Response.AsJson(result
|
||||
? new JsonResponseModel { Result = true }
|
||||
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Fatal(e);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
|
||||
|
|
|
@ -33,16 +33,30 @@ namespace PlexRequests.UI.Modules
|
|||
{
|
||||
public class BaseModule : NancyModule
|
||||
{
|
||||
private string _username;
|
||||
|
||||
protected string Username
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_username))
|
||||
{
|
||||
_username = Session[SessionKeys.UsernameKey].ToString();
|
||||
}
|
||||
return _username;
|
||||
}
|
||||
}
|
||||
|
||||
public BaseModule()
|
||||
{
|
||||
Before += (ctx)=> CheckAuth();
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
public BaseModule(string modulePath) : base(modulePath)
|
||||
{
|
||||
Before += (ctx) => CheckAuth();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Response CheckAuth()
|
||||
{
|
||||
|
|
|
@ -79,28 +79,38 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
private Response GetMovies()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
var isAdmin = Context.CurrentUser.IsAuthenticated();
|
||||
var dbMovies = Service.GetAll().Where(x => x.Type == RequestType.Movie);
|
||||
var viewModel = dbMovies.Select(movie => new RequestViewModel
|
||||
if (settings.UsersCanViewOnlyOwnRequests && !isAdmin)
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
Type = movie.Type,
|
||||
Status = movie.Status,
|
||||
ImdbId = movie.ImdbId,
|
||||
Id = movie.Id,
|
||||
PosterPath = movie.PosterPath,
|
||||
ReleaseDate = movie.ReleaseDate.Humanize(),
|
||||
RequestedDate = movie.RequestedDate.Humanize(),
|
||||
Approved = movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedBy = movie.RequestedBy,
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = isAdmin,
|
||||
Issues = movie.Issues.Humanize(LetterCasing.Title),
|
||||
OtherMessage = movie.OtherMessage,
|
||||
AdminNotes = movie.AdminNote
|
||||
dbMovies = dbMovies.Where(x => x.UserHasRequested(Username));
|
||||
}
|
||||
|
||||
var viewModel = dbMovies.Select(movie => {
|
||||
return new RequestViewModel
|
||||
{
|
||||
ProviderId = movie.ProviderId,
|
||||
Type = movie.Type,
|
||||
Status = movie.Status,
|
||||
ImdbId = movie.ImdbId,
|
||||
Id = movie.Id,
|
||||
PosterPath = movie.PosterPath,
|
||||
ReleaseDate = movie.ReleaseDate.Humanize(),
|
||||
ReleaseDateTicks = movie.ReleaseDate.Ticks,
|
||||
RequestedDate = movie.RequestedDate.Humanize(),
|
||||
RequestedDateTicks = movie.RequestedDate.Ticks,
|
||||
Approved = movie.Available || movie.Approved,
|
||||
Title = movie.Title,
|
||||
Overview = movie.Overview,
|
||||
RequestedUsers = isAdmin ? movie.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = movie.ReleaseDate.Year.ToString(),
|
||||
Available = movie.Available,
|
||||
Admin = isAdmin,
|
||||
Issues = movie.Issues.Humanize(LetterCasing.Title),
|
||||
OtherMessage = movie.OtherMessage,
|
||||
AdminNotes = movie.AdminNote
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
|
@ -108,29 +118,39 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
private Response GetTvShows()
|
||||
{
|
||||
var settings = PrSettings.GetSettings();
|
||||
var isAdmin = Context.CurrentUser.IsAuthenticated();
|
||||
var dbTv = Service.GetAll().Where(x => x.Type == RequestType.TvShow);
|
||||
var viewModel = dbTv.Select(tv => new RequestViewModel
|
||||
if (settings.UsersCanViewOnlyOwnRequests && !isAdmin)
|
||||
{
|
||||
ProviderId = tv.ProviderId,
|
||||
Type = tv.Type,
|
||||
Status = tv.Status,
|
||||
ImdbId = tv.ImdbId,
|
||||
Id = tv.Id,
|
||||
PosterPath = tv.PosterPath,
|
||||
ReleaseDate = tv.ReleaseDate.Humanize(),
|
||||
RequestedDate = tv.RequestedDate.Humanize(),
|
||||
Approved = tv.Approved,
|
||||
Title = tv.Title,
|
||||
Overview = tv.Overview,
|
||||
RequestedBy = tv.RequestedBy,
|
||||
ReleaseYear = tv.ReleaseDate.Year.ToString(),
|
||||
Available = tv.Available,
|
||||
Admin = isAdmin,
|
||||
Issues = tv.Issues.Humanize(LetterCasing.Title),
|
||||
OtherMessage = tv.OtherMessage,
|
||||
AdminNotes = tv.AdminNote,
|
||||
TvSeriesRequestType = tv.SeasonsRequested
|
||||
dbTv = dbTv.Where(x => x.UserHasRequested(Username));
|
||||
}
|
||||
|
||||
var viewModel = dbTv.Select(tv => {
|
||||
return new RequestViewModel
|
||||
{
|
||||
ProviderId = tv.ProviderId,
|
||||
Type = tv.Type,
|
||||
Status = tv.Status,
|
||||
ImdbId = tv.ImdbId,
|
||||
Id = tv.Id,
|
||||
PosterPath = tv.PosterPath,
|
||||
ReleaseDate = tv.ReleaseDate.Humanize(),
|
||||
ReleaseDateTicks = tv.ReleaseDate.Ticks,
|
||||
RequestedDate = tv.RequestedDate.Humanize(),
|
||||
RequestedDateTicks = tv.RequestedDate.Ticks,
|
||||
Approved = tv.Available || tv.Approved,
|
||||
Title = tv.Title,
|
||||
Overview = tv.Overview,
|
||||
RequestedUsers = isAdmin ? tv.AllUsers.ToArray() : new string[] { },
|
||||
ReleaseYear = tv.ReleaseDate.Year.ToString(),
|
||||
Available = tv.Available,
|
||||
Admin = isAdmin,
|
||||
Issues = tv.Issues.Humanize(LetterCasing.Title),
|
||||
OtherMessage = tv.OtherMessage,
|
||||
AdminNotes = tv.AdminNote,
|
||||
TvSeriesRequestType = tv.SeasonsRequested
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Response.AsJson(viewModel);
|
||||
|
@ -165,7 +185,7 @@ namespace PlexRequests.UI.Modules
|
|||
}
|
||||
originalRequest.Issues = issue;
|
||||
originalRequest.OtherMessage = !string.IsNullOrEmpty(comment)
|
||||
? $"{Session[SessionKeys.UsernameKey]} - {comment}"
|
||||
? $"{Username} - {comment}"
|
||||
: string.Empty;
|
||||
|
||||
|
||||
|
@ -173,7 +193,7 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
var model = new NotificationModel
|
||||
{
|
||||
User = Session[SessionKeys.UsernameKey].ToString(),
|
||||
User = Username,
|
||||
NotificationType = NotificationType.Issue,
|
||||
Title = originalRequest.Title,
|
||||
DateTime = DateTime.Now,
|
||||
|
|
|
@ -179,11 +179,20 @@ namespace PlexRequests.UI.Modules
|
|||
Log.Trace(movieInfo.DumpJson);
|
||||
//#if !DEBUG
|
||||
|
||||
var settings = PrService.GetSettings();
|
||||
|
||||
// check if the movie has already been requested
|
||||
Log.Info("Requesting movie with id {0}", movieId);
|
||||
if (RequestService.CheckRequest(movieId))
|
||||
var existingRequest = RequestService.CheckRequest(movieId);
|
||||
if (existingRequest != null)
|
||||
{
|
||||
Log.Trace("movie with id {0} exists", movieId);
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullMovieName} has already been requested!" });
|
||||
// check if the current user is already marked as a requester for this movie, if not, add them
|
||||
if (!existingRequest.UserHasRequested(Username))
|
||||
{
|
||||
existingRequest.RequestedUsers.Add(Username);
|
||||
RequestService.UpdateRequest(existingRequest);
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} was successfully added!" : $"{fullMovieName} has already been requested!" });
|
||||
}
|
||||
|
||||
Log.Debug("movie with id {0} doesnt exists", movieId);
|
||||
|
@ -213,14 +222,12 @@ namespace PlexRequests.UI.Modules
|
|||
Status = movieInfo.Status,
|
||||
RequestedDate = DateTime.Now,
|
||||
Approved = false,
|
||||
RequestedBy = Session[SessionKeys.UsernameKey].ToString(),
|
||||
RequestedUsers = new List<string>() { Username },
|
||||
Issues = IssueState.None,
|
||||
};
|
||||
|
||||
|
||||
var settings = PrService.GetSettings();
|
||||
Log.Trace(settings.DumpJson());
|
||||
if (!settings.RequireMovieApproval)
|
||||
if (!settings.RequireMovieApproval || settings.NoApprovalUserList.Any(x => x.Equals(Username, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var cpSettings = CpService.GetSettings();
|
||||
|
||||
|
@ -247,7 +254,7 @@ namespace PlexRequests.UI.Modules
|
|||
};
|
||||
NotificationService.Publish(notificationModel);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel {Result = true});
|
||||
return Response.AsJson(new JsonResponseModel {Result = true, Message = $"{fullMovieName} was successfully added!" });
|
||||
}
|
||||
return
|
||||
Response.AsJson(new JsonResponseModel
|
||||
|
@ -272,7 +279,7 @@ namespace PlexRequests.UI.Modules
|
|||
};
|
||||
NotificationService.Publish(notificationModel);
|
||||
|
||||
return Response.AsJson(new JsonResponseModel { Result = true });
|
||||
return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,9 +317,20 @@ namespace PlexRequests.UI.Modules
|
|||
string fullShowName = $"{showInfo.name} ({firstAir.Year})";
|
||||
//#if !DEBUG
|
||||
|
||||
if (RequestService.CheckRequest(showId))
|
||||
var settings = PrService.GetSettings();
|
||||
|
||||
// check if the show has already been requested
|
||||
Log.Info("Requesting tv show with id {0}", showId);
|
||||
var existingRequest = RequestService.CheckRequest(showId);
|
||||
if (existingRequest != null)
|
||||
{
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} has already been requested!" });
|
||||
// check if the current user is already marked as a requester for this show, if not, add them
|
||||
if (!existingRequest.UserHasRequested(Username))
|
||||
{
|
||||
existingRequest.RequestedUsers.Add(Username);
|
||||
RequestService.UpdateRequest(existingRequest);
|
||||
}
|
||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullShowName} was successfully added!" : $"{fullShowName} has already been requested!" });
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -340,7 +358,7 @@ namespace PlexRequests.UI.Modules
|
|||
Status = showInfo.status,
|
||||
RequestedDate = DateTime.Now,
|
||||
Approved = false,
|
||||
RequestedBy = Session[SessionKeys.UsernameKey].ToString(),
|
||||
RequestedUsers = new List<string>() { Username },
|
||||
Issues = IssueState.None,
|
||||
ImdbId = showInfo.externals?.imdb ?? string.Empty,
|
||||
SeasonCount = showInfo.seasonCount
|
||||
|
@ -363,8 +381,7 @@ namespace PlexRequests.UI.Modules
|
|||
|
||||
model.SeasonList = seasonsList.ToArray();
|
||||
|
||||
var settings = PrService.GetSettings();
|
||||
if (!settings.RequireTvShowApproval)
|
||||
if (!settings.RequireTvShowApproval || settings.NoApprovalUserList.Any(x => x.Equals(Username, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var sonarrSettings = SonarrService.GetSettings();
|
||||
var sender = new TvSender(SonarrApi, SickrageApi);
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace PlexRequests.UI
|
|||
if (port == -1)
|
||||
port = GetStartupPort();
|
||||
|
||||
var options = new StartOptions( $"http://+:{port}")
|
||||
var options = new StartOptions(Debugger.IsAttached ? $"http://localhost:{port}" : $"http://+:{port}")
|
||||
{
|
||||
ServerFactory = "Microsoft.Owin.Host.HttpListener"
|
||||
};
|
||||
|
|
|
@ -88,6 +88,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testEmail" type="submit" class="btn btn-primary-outline">Test</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
|
@ -128,7 +133,32 @@
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
$('#testEmail').click(function (e) {
|
||||
e.preventDefault();
|
||||
var port = $('#EmailPort').val();
|
||||
if (isNaN(port)) {
|
||||
generateNotify("You must specify a valid Port.", "warning");
|
||||
return;
|
||||
}
|
||||
var $form = $("#mainForm");
|
||||
$.ajax({
|
||||
type: $form.prop("method"),
|
||||
data: $form.serialize(),
|
||||
url: '/admin/testemailnotification',
|
||||
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");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testPushbullet" type="submit" class="btn btn-primary-outline">Test</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
|
||||
|
@ -70,5 +76,28 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushbullet').click(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $form = $("#mainForm");
|
||||
$.ajax({
|
||||
type: $form.prop("method"),
|
||||
data: $form.serialize(),
|
||||
url: '/admin/testpushbulletnotification',
|
||||
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>
|
|
@ -36,6 +36,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testPushover" type="submit" class="btn btn-primary-outline">Test</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
|
||||
|
@ -70,5 +76,28 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushover').click(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $form = $("#mainForm");
|
||||
$.ajax({
|
||||
type: $form.prop("method"),
|
||||
data: $form.serialize(),
|
||||
url: '/admin/testpushovernotification',
|
||||
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>
|
|
@ -111,7 +111,31 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p class="form-group">A comma separated list of users whose requests do not require approval.</p>
|
||||
<div class="form-group">
|
||||
<label for="noApprovalUsers" class="control-label">Users</label>
|
||||
<div>
|
||||
<input type="text" class="form-control-custom form-control " id="NoApprovalUsers" name="NoApprovalUsers" placeholder="e.g. John, Bobby" value="@Model.NoApprovalUsers">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
@if (Model.UsersCanViewOnlyOwnRequests)
|
||||
{
|
||||
<input type="checkbox" id="UsersCanViewOnlyOwnRequests" name="UsersCanViewOnlyOwnRequests" checked="checked"><text>Users can view their own requests only</text>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" id="UsersCanViewOnlyOwnRequests" name="UsersCanViewOnlyOwnRequests"><text>Users can view their own requests only</text>
|
||||
}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@*<div class="form-group">
|
||||
<label for="WeeklyRequestLimit" class="control-label">Weekly Request Limit</label>
|
||||
<div>
|
||||
|
@ -131,4 +155,3 @@
|
|||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Password <input class="form-control form-control-custom" name="Password" type="password"/>
|
||||
<br/>
|
||||
Remember Me <input name="RememberMe" type="checkbox" value="True"/>
|
||||
<br/>
|
||||
<br/><br/>
|
||||
<input class="btn btn-success-outline" type="submit" value="Login"/>
|
||||
</form>
|
||||
@if (!Model.AdminExists)
|
||||
|
|
|
@ -2,12 +2,8 @@
|
|||
<div>
|
||||
<h1>Requests</h1>
|
||||
<h4>Below you can see yours and all other requests, as well as their download and approval status.</h4>
|
||||
@if (Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
<button id="approveAll" class="btn btn-success-outline" type="submit"><i class="fa fa-plus"></i> Approve All</button>
|
||||
<br />
|
||||
<br />
|
||||
}
|
||||
<br />
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
|
||||
@if (Model.SearchForMovies)
|
||||
|
@ -20,40 +16,62 @@
|
|||
}
|
||||
|
||||
</ul>
|
||||
<br />
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content contentList">
|
||||
<div class="btn-group col-sm-push-10">
|
||||
<a href="#" class="btn btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Filter
|
||||
<i class="fa fa-filter"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="filter" data-filter="all">All</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".approved-true">Approved</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".approved-false">Not Approved</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".available-true">Available</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".available-false">Not Available</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group col-sm-push-10">
|
||||
<a href="#" class="btn btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Order
|
||||
<i class="fa fa-sort"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="sort" data-sort="default">Default</a></li>
|
||||
<li><a href="#" class="sort" data-sort="requestorder:asc">Requested Date</a></li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="pull-right">
|
||||
<div class="btn-group">
|
||||
@if (Context.CurrentUser.IsAuthenticated())
|
||||
{
|
||||
@if (Model.SearchForMovies)
|
||||
{
|
||||
<button id="approveMovies" class="btn btn-success-outline approve-category" type="submit"><i class="fa fa-plus"></i> Approve Movies</button>
|
||||
}
|
||||
@if (Model.SearchForTvShows)
|
||||
{
|
||||
<button id="approveTVShows" class="btn btn-success-outline approve-category" type="submit" style="display: none;"><i class="fa fa-plus"></i> Approve TV Shows</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Filter
|
||||
<i class="fa fa-filter"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="filter" data-filter="all"><i class="fa fa-square"></i> All</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".approved-true"><i class="fa fa-square-o"></i> Approved</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".approved-false"><i class="fa fa-square-o"></i> Not Approved</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".available-true"><i class="fa fa-square-o"></i> Available</a></li>
|
||||
<li><a href="#" class="filter" data-filter=".available-false"><i class="fa fa-square-o"></i> Not Available</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
Order
|
||||
<i class="fa fa-sort"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="sort" data-sort="requestorder:desc"><i class="fa fa-square"></i> Recent Requests</a></li>
|
||||
<li><a href="#" class="sort" data-sort="requestorder:asc"><i class="fa fa-square-o"></i> Older Requests</a></li>
|
||||
<li><a href="#" class="sort" data-sort="releaseorder:desc"><i class="fa fa-square-o"></i> Recent Releases</a></li>
|
||||
<li><a href="#" class="sort" data-sort="releaseorder:asc"><i class="fa fa-square-o"></i> Older Releases</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.SearchForMovies)
|
||||
{
|
||||
{
|
||||
|
||||
<!-- Movie tab -->
|
||||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
<!-- Movie content -->
|
||||
<div id="movieList">
|
||||
</div>
|
||||
|
@ -61,12 +79,12 @@
|
|||
}
|
||||
|
||||
@if (Model.SearchForTvShows)
|
||||
{
|
||||
{
|
||||
<!-- TV tab -->
|
||||
<div role="tabpanel" class="tab-pane" id="TvShowTab">
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
<!-- TV content -->
|
||||
<div id="tvList">
|
||||
</div>
|
||||
|
@ -78,7 +96,7 @@
|
|||
|
||||
|
||||
<script id="search-template" type="text/x-handlebars-template">
|
||||
<div id="{{requestId}}Template" class="mix available-{{available}} approved-{{approved}}">
|
||||
<div id="{{requestId}}Template" class="mix available-{{available}} approved-{{approved}}" data-requestorder="{{requestedDateTicks}}" data-releaseorder="{{releaseDateTicks}}">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
{{#if_eq type "movie"}}
|
||||
|
@ -122,7 +140,7 @@
|
|||
{{#if_eq type "tv"}}
|
||||
<div>Series Requested: {{seriesRequested}}</div>
|
||||
{{/if_eq}}
|
||||
<div>Requested By: {{requestedBy}}</div>
|
||||
<div>Requested By: {{requestedUsers}}</div>
|
||||
<div>Requested Date: {{requestedDate}}</div>
|
||||
<div id="issueArea{{requestId}}">
|
||||
{{#if otherMessage}}
|
||||
|
@ -181,7 +199,7 @@
|
|||
<li><a id="{{requestId}}" issue-select="4" class="dropdownIssue" data-identifier="{{requestId}}" href="#" data-toggle="modal" data-target="#myModal">Other</a></li>
|
||||
|
||||
{{#if_eq admin true}}
|
||||
<li><a id="{{requestId}}" issue-select="4" class="note" data-identifier="{{requestId}}" href="#" data-toggle="modal" data-target="#noteModal">Add Note</a></li>
|
||||
<li><a id="{{requestId}}" issue-select="4" class="note" data-identifier="{{requestId}}" href="#" data-toggle="modal" data-target="#noteModal">Add Note</a></li>
|
||||
{{/if_eq}}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<div>
|
||||
<h1>Search</h1>
|
||||
<h4>Want to watch something that is not currently on Plex?! No problem! Just search for it below and request it!</h4>
|
||||
<br />
|
||||
<!-- Nav tabs -->
|
||||
<ul id="nav-tabs" class="nav nav-tabs" role="tablist">
|
||||
@if (Model.SearchForMovies)
|
||||
|
@ -57,7 +58,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
<script id="search-template" type="text/x-handlebars-template">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<title>Plex Requests</title>
|
||||
<!-- Styles -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="~/Content/custom.min.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="~/Content/bootstrap.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="~/Content/custom.min.css" type="text/css" />
|
||||
<link rel="stylesheet" href="~/Content/font-awesome.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="~/Content/pace.min.css" type="text/css"/>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue