mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 12:59:39 -07:00
Merge pull request #2519 from tidusjar/feature/user-notification-improvements
Feature/user notification improvements
This commit is contained in:
commit
1664fc5bca
29 changed files with 1730 additions and 350 deletions
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models.UI
|
||||||
public int EpisodeRequestLimit { get; set; }
|
public int EpisodeRequestLimit { get; set; }
|
||||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||||
|
public int MusicRequestLimit { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ClaimCheckboxes
|
public class ClaimCheckboxes
|
||||||
|
|
|
@ -20,8 +20,9 @@ namespace Ombi.Notifications.Agents
|
||||||
{
|
{
|
||||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn,
|
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn,
|
||||||
ILogger<DiscordNotification> log, INotificationTemplatesRepository r,
|
ILogger<DiscordNotification> log, INotificationTemplatesRepository r,
|
||||||
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music)
|
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
: base(sn, r, m, t, s, log, sub, music)
|
IRepository<UserNotificationPreferences> userPref)
|
||||||
|
: base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -22,7 +22,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public class EmailNotification : BaseNotification<EmailNotificationSettings>, IEmailNotification
|
public class EmailNotification : BaseNotification<EmailNotificationSettings>, IEmailNotification
|
||||||
{
|
{
|
||||||
public EmailNotification(ISettingsService<EmailNotificationSettings> settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService<CustomizationSettings> c,
|
public EmailNotification(ISettingsService<EmailNotificationSettings> settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService<CustomizationSettings> c,
|
||||||
ILogger<EmailNotification> log, UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(settings, r, m, t, c, log, sub, music)
|
ILogger<EmailNotification> log, UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(settings, r, m, t, c, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
EmailProvider = prov;
|
EmailProvider = prov;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public class MattermostNotification : BaseNotification<MattermostNotificationSettings>, IMattermostNotification
|
public class MattermostNotification : BaseNotification<MattermostNotificationSettings>, IMattermostNotification
|
||||||
{
|
{
|
||||||
public MattermostNotification(IMattermostApi api, ISettingsService<MattermostNotificationSettings> sn, ILogger<MattermostNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
public MattermostNotification(IMattermostApi api, ISettingsService<MattermostNotificationSettings> sn, ILogger<MattermostNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t, s, log, sub, music)
|
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -22,7 +22,8 @@ namespace Ombi.Notifications.Agents
|
||||||
{
|
{
|
||||||
public MobileNotification(IOneSignalApi api, ISettingsService<MobileNotificationSettings> sn, ILogger<MobileNotification> log, INotificationTemplatesRepository r,
|
public MobileNotification(IOneSignalApi api, ISettingsService<MobileNotificationSettings> sn, ILogger<MobileNotification> log, INotificationTemplatesRepository r,
|
||||||
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<NotificationUserId> notification,
|
IMovieRequestRepository m, ITvRequestRepository t, ISettingsService<CustomizationSettings> s, IRepository<NotificationUserId> notification,
|
||||||
UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t, s, log, sub, music)
|
UserManager<OmbiUser> um, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_logger = log;
|
_logger = log;
|
||||||
|
|
|
@ -17,7 +17,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public class PushbulletNotification : BaseNotification<PushbulletSettings>, IPushbulletNotification
|
public class PushbulletNotification : BaseNotification<PushbulletSettings>, IPushbulletNotification
|
||||||
{
|
{
|
||||||
public PushbulletNotification(IPushbulletApi api, ISettingsService<PushbulletSettings> sn, ILogger<PushbulletNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
public PushbulletNotification(IPushbulletApi api, ISettingsService<PushbulletSettings> sn, ILogger<PushbulletNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t, s, log, sub, music)
|
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public class PushoverNotification : BaseNotification<PushoverSettings>, IPushoverNotification
|
public class PushoverNotification : BaseNotification<PushoverSettings>, IPushoverNotification
|
||||||
{
|
{
|
||||||
public PushoverNotification(IPushoverApi api, ISettingsService<PushoverSettings> sn, ILogger<PushoverNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
public PushoverNotification(IPushoverApi api, ISettingsService<PushoverSettings> sn, ILogger<PushoverNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t, s, log, sub, music)
|
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public class SlackNotification : BaseNotification<SlackNotificationSettings>, ISlackNotification
|
public class SlackNotification : BaseNotification<SlackNotificationSettings>, ISlackNotification
|
||||||
{
|
{
|
||||||
public SlackNotification(ISlackApi api, ISettingsService<SlackNotificationSettings> sn, ILogger<SlackNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
public SlackNotification(ISlackApi api, ISettingsService<SlackNotificationSettings> sn, ILogger<SlackNotification> log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t,
|
||||||
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t, s, log, sub, music)
|
ISettingsService<CustomizationSettings> s, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t, s, log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -19,7 +19,8 @@ namespace Ombi.Notifications.Agents
|
||||||
public TelegramNotification(ITelegramApi api, ISettingsService<TelegramSettings> sn, ILogger<TelegramNotification> log,
|
public TelegramNotification(ITelegramApi api, ISettingsService<TelegramSettings> sn, ILogger<TelegramNotification> log,
|
||||||
INotificationTemplatesRepository r, IMovieRequestRepository m,
|
INotificationTemplatesRepository r, IMovieRequestRepository m,
|
||||||
ITvRequestRepository t, ISettingsService<CustomizationSettings> s
|
ITvRequestRepository t, ISettingsService<CustomizationSettings> s
|
||||||
, IRepository<RequestSubscription> sub, IMusicRequestRepository music) : base(sn, r, m, t,s,log, sub, music)
|
, IRepository<RequestSubscription> sub, IMusicRequestRepository music,
|
||||||
|
IRepository<UserNotificationPreferences> userPref) : base(sn, r, m, t,s,log, sub, music, userPref)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
|
|
@ -19,7 +19,8 @@ namespace Ombi.Notifications.Interfaces
|
||||||
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
||||||
{
|
{
|
||||||
protected BaseNotification(ISettingsService<T> settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv,
|
protected BaseNotification(ISettingsService<T> settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv,
|
||||||
ISettingsService<CustomizationSettings> customization, ILogger<BaseNotification<T>> log, IRepository<RequestSubscription> sub, IMusicRequestRepository album)
|
ISettingsService<CustomizationSettings> customization, ILogger<BaseNotification<T>> log, IRepository<RequestSubscription> sub, IMusicRequestRepository album,
|
||||||
|
IRepository<UserNotificationPreferences> notificationUserPreferences)
|
||||||
{
|
{
|
||||||
Settings = settings;
|
Settings = settings;
|
||||||
TemplateRepository = templateRepo;
|
TemplateRepository = templateRepo;
|
||||||
|
@ -31,6 +32,7 @@ namespace Ombi.Notifications.Interfaces
|
||||||
RequestSubscription = sub;
|
RequestSubscription = sub;
|
||||||
_log = log;
|
_log = log;
|
||||||
AlbumRepository = album;
|
AlbumRepository = album;
|
||||||
|
UserNotificationPreferences = notificationUserPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ISettingsService<T> Settings { get; }
|
protected ISettingsService<T> Settings { get; }
|
||||||
|
@ -40,6 +42,7 @@ namespace Ombi.Notifications.Interfaces
|
||||||
protected IMusicRequestRepository AlbumRepository { get; }
|
protected IMusicRequestRepository AlbumRepository { get; }
|
||||||
protected CustomizationSettings Customization { get; set; }
|
protected CustomizationSettings Customization { get; set; }
|
||||||
protected IRepository<RequestSubscription> RequestSubscription { get; set; }
|
protected IRepository<RequestSubscription> RequestSubscription { get; set; }
|
||||||
|
protected IRepository<UserNotificationPreferences> UserNotificationPreferences { get; set; }
|
||||||
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
private ISettingsService<CustomizationSettings> CustomizationSettings { get; }
|
||||||
private readonly ILogger<BaseNotification<T>> _log;
|
private readonly ILogger<BaseNotification<T>> _log;
|
||||||
|
|
||||||
|
@ -167,7 +170,7 @@ namespace Ombi.Notifications.Interfaces
|
||||||
{
|
{
|
||||||
return new NotificationMessageContent { Disabled = true };
|
return new NotificationMessageContent { Disabled = true };
|
||||||
}
|
}
|
||||||
var parsed = Parse(model, template);
|
var parsed = Parse(model, template, agent);
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
@ -178,25 +181,32 @@ namespace Ombi.Notifications.Interfaces
|
||||||
return subs.Select(x => x.User);
|
return subs.Select(x => x.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template)
|
protected UserNotificationPreferences GetUserPreference(string userId, NotificationAgent agent)
|
||||||
|
{
|
||||||
|
return UserNotificationPreferences.GetAll()
|
||||||
|
.FirstOrDefault(x => x.Enabled && x.Agent == agent && x.UserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent)
|
||||||
{
|
{
|
||||||
var resolver = new NotificationMessageResolver();
|
var resolver = new NotificationMessageResolver();
|
||||||
var curlys = new NotificationMessageCurlys();
|
var curlys = new NotificationMessageCurlys();
|
||||||
|
var preference = GetUserPreference(model.UserId, agent);
|
||||||
if (model.RequestType == RequestType.Movie)
|
if (model.RequestType == RequestType.Movie)
|
||||||
{
|
{
|
||||||
_log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization);
|
_log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization);
|
||||||
|
|
||||||
curlys.Setup(model, MovieRequest, Customization);
|
curlys.Setup(model, MovieRequest, Customization, preference);
|
||||||
}
|
}
|
||||||
else if (model.RequestType == RequestType.TvShow)
|
else if (model.RequestType == RequestType.TvShow)
|
||||||
{
|
{
|
||||||
_log.LogDebug("Notification options: {@model}, Req: {@TvRequest}, Settings: {@Customization}", model, TvRequest, Customization);
|
_log.LogDebug("Notification options: {@model}, Req: {@TvRequest}, Settings: {@Customization}", model, TvRequest, Customization);
|
||||||
curlys.Setup(model, TvRequest, Customization);
|
curlys.Setup(model, TvRequest, Customization, preference);
|
||||||
}
|
}
|
||||||
else if (model.RequestType == RequestType.Album)
|
else if (model.RequestType == RequestType.Album)
|
||||||
{
|
{
|
||||||
_log.LogDebug("Notification options: {@model}, Req: {@AlbumRequest}, Settings: {@Customization}", model, AlbumRequest, Customization);
|
_log.LogDebug("Notification options: {@model}, Req: {@AlbumRequest}, Settings: {@Customization}", model, AlbumRequest, Customization);
|
||||||
curlys.Setup(model, AlbumRequest, Customization);
|
curlys.Setup(model, AlbumRequest, Customization, preference);
|
||||||
}
|
}
|
||||||
var parsed = resolver.ParseMessage(template, curlys);
|
var parsed = resolver.ParseMessage(template, curlys);
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@ namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
public class NotificationMessageCurlys
|
public class NotificationMessageCurlys
|
||||||
{
|
{
|
||||||
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s)
|
public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
|
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
@ -58,9 +59,10 @@ namespace Ombi.Notifications
|
||||||
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
AdditionalInformation = opts?.AdditionalInformation ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s)
|
public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
|
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
@ -101,9 +103,10 @@ namespace Ombi.Notifications
|
||||||
Alias = username.Alias.HasValue() ? username.Alias : username.UserName;
|
Alias = username.Alias.HasValue() ? username.Alias : username.UserName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s)
|
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||||
{
|
{
|
||||||
LoadIssues(opts);
|
LoadIssues(opts);
|
||||||
|
UserPreference = pref.Enabled ? pref.Value : string.Empty;
|
||||||
string title;
|
string title;
|
||||||
if (req == null)
|
if (req == null)
|
||||||
{
|
{
|
||||||
|
@ -221,6 +224,7 @@ namespace Ombi.Notifications
|
||||||
public string IssueStatus { get; set; }
|
public string IssueStatus { get; set; }
|
||||||
public string IssueSubject { get; set; }
|
public string IssueSubject { get; set; }
|
||||||
public string NewIssueComment { get; set; }
|
public string NewIssueComment { get; set; }
|
||||||
|
public string UserPreference { get; set; }
|
||||||
|
|
||||||
// System Defined
|
// System Defined
|
||||||
private string LongDate => DateTime.Now.ToString("D");
|
private string LongDate => DateTime.Now.ToString("D");
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace Ombi.Store.Entities
|
||||||
public string UserAccessToken { get; set; }
|
public string UserAccessToken { get; set; }
|
||||||
|
|
||||||
public List<NotificationUserId> NotificationUserIds { get; set; }
|
public List<NotificationUserId> NotificationUserIds { get; set; }
|
||||||
|
public List<UserNotificationPreferences> UserNotificationPreferences { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue();
|
public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue();
|
||||||
|
|
19
src/Ombi.Store/Entities/UserNotificationPreferences.cs
Normal file
19
src/Ombi.Store/Entities/UserNotificationPreferences.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Entities
|
||||||
|
{
|
||||||
|
[Table(nameof(UserNotificationPreferences))]
|
||||||
|
public class UserNotificationPreferences : Entity
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public NotificationAgent Agent { get; set; }
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(UserId))]
|
||||||
|
[JsonIgnore]
|
||||||
|
public OmbiUser User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
1118
src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.Designer.cs
generated
Normal file
1118
src/Ombi.Store/Migrations/20180919073124_UserNotificationPreferences.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,43 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Ombi.Store.Migrations
|
||||||
|
{
|
||||||
|
public partial class UserNotificationPreferences : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserNotificationPreferences",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
UserId = table.Column<string>(nullable: true),
|
||||||
|
Agent = table.Column<int>(nullable: false),
|
||||||
|
Enabled = table.Column<bool>(nullable: false),
|
||||||
|
Value = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserNotificationPreferences", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserNotificationPreferences_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserNotificationPreferences_UserId",
|
||||||
|
table: "UserNotificationPreferences",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserNotificationPreferences");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
{
|
{
|
||||||
|
@ -870,6 +870,26 @@ namespace Ombi.Store.Migrations
|
||||||
b.ToTable("Tokens");
|
b.ToTable("Tokens");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("Agent");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserNotificationPreferences");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
@ -1068,6 +1088,13 @@ namespace Ombi.Store.Migrations
|
||||||
.HasForeignKey("UserId");
|
.HasForeignKey("UserId");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||||
|
.WithMany("UserNotificationPreferences")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
|
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
|
||||||
|
|
|
@ -13,6 +13,7 @@ export interface IUser {
|
||||||
hasLoggedIn: boolean;
|
hasLoggedIn: boolean;
|
||||||
movieRequestLimit: number;
|
movieRequestLimit: number;
|
||||||
episodeRequestLimit: number;
|
episodeRequestLimit: number;
|
||||||
|
musicRequestLimit: number;
|
||||||
userAccessToken: string;
|
userAccessToken: string;
|
||||||
|
|
||||||
// FOR UI
|
// FOR UI
|
||||||
|
@ -70,3 +71,22 @@ export interface IMassEmailModel {
|
||||||
body: string;
|
body: string;
|
||||||
users: IUser[];
|
users: IUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface INotificationPreferences {
|
||||||
|
id: number;
|
||||||
|
userId: string;
|
||||||
|
agent: INotificationAgent;
|
||||||
|
enabled: boolean;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum INotificationAgent {
|
||||||
|
Email = 0,
|
||||||
|
Discord = 1,
|
||||||
|
Pushbullet = 2,
|
||||||
|
Pushover = 3,
|
||||||
|
Telegram = 4,
|
||||||
|
Slack = 5,
|
||||||
|
Mattermost = 6,
|
||||||
|
Mobile = 7,
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Injectable } from "@angular/core";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces";
|
import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces";
|
||||||
import { ServiceHelpers } from "./service.helpers";
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -43,6 +43,11 @@ export class IdentityService extends ServiceHelpers {
|
||||||
public updateUser(user: IUser): Observable<IIdentityResult> {
|
public updateUser(user: IUser): Observable<IIdentityResult> {
|
||||||
return this.http.put<IIdentityResult>(this.url, JSON.stringify(user), {headers: this.headers});
|
return this.http.put<IIdentityResult>(this.url, JSON.stringify(user), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateNotificationPreferences(pref: INotificationPreferences[]): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}NotificationPreferences`, JSON.stringify(pref), {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public updateLocalUser(user: IUpdateLocalUser): Observable<IIdentityResult> {
|
public updateLocalUser(user: IUpdateLocalUser): Observable<IIdentityResult> {
|
||||||
return this.http.put<IIdentityResult>(this.url + "local", JSON.stringify(user), {headers: this.headers});
|
return this.http.put<IIdentityResult>(this.url + "local", JSON.stringify(user), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
@ -67,6 +72,13 @@ export class IdentityService extends ServiceHelpers {
|
||||||
return this.http.post<any>(`${this.url}welcomeEmail`, JSON.stringify(user), {headers: this.headers});
|
return this.http.post<any>(`${this.url}welcomeEmail`, JSON.stringify(user), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getNotificationPreferences(): Observable<INotificationPreferences[]> {
|
||||||
|
return this.http.get<INotificationPreferences[]>(`${this.url}notificationpreferences`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
public getNotificationPreferencesForUser(userId: string): Observable<INotificationPreferences[]> {
|
||||||
|
return this.http.get<INotificationPreferences[]>(`${this.url}notificationpreferences/${userId}`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public hasRole(role: string): boolean {
|
public hasRole(role: string): boolean {
|
||||||
const roles = localStorage.getItem("roles") as string[] | null;
|
const roles = localStorage.getItem("roles") as string[] | null;
|
||||||
if (roles) {
|
if (roles) {
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
|
|
||||||
<h3>Create User</h3>
|
|
||||||
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
|
|
||||||
|
|
||||||
<div class="modal-body" style="margin-top: 45px;">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>User Details</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>Roles</h4>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="username" class="control-label">Username</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.userName" class="form-control form-control-custom " id="username" name="username" value="{{user?.userName}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="alias" class="control-label">Alias</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="emailAddress" class="control-label">Email Address</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password" class="control-label">Password</label>
|
|
||||||
<div>
|
|
||||||
<input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password" name="password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="confirmPass" class="control-label">Confirm Password</label>
|
|
||||||
<div>
|
|
||||||
<input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass" name="confirmPass">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div *ngFor="let c of availableClaims">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled">
|
|
||||||
<label for="create{{c.value}}">{{c.value | humanize}}</label>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom " id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom " id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<button type="button" class="btn btn-danger-outline" (click)="create()">Create</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { ICheckbox, IUser, UserType } from "../interfaces";
|
|
||||||
import { IdentityService, NotificationService } from "../services";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: "./usermanagement-add.component.html",
|
|
||||||
})
|
|
||||||
export class UserManagementAddComponent implements OnInit {
|
|
||||||
public user: IUser;
|
|
||||||
public availableClaims: ICheckbox[];
|
|
||||||
public confirmPass: "";
|
|
||||||
|
|
||||||
constructor(private identityService: IdentityService,
|
|
||||||
private notificationSerivce: NotificationService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
public ngOnInit() {
|
|
||||||
this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x);
|
|
||||||
this.user = {
|
|
||||||
alias: "",
|
|
||||||
claims: [],
|
|
||||||
emailAddress: "",
|
|
||||||
id: "",
|
|
||||||
password: "",
|
|
||||||
userName: "",
|
|
||||||
userType: UserType.LocalUser,
|
|
||||||
checked: false,
|
|
||||||
hasLoggedIn: false,
|
|
||||||
lastLoggedIn: new Date(),
|
|
||||||
episodeRequestLimit: 0,
|
|
||||||
movieRequestLimit: 0,
|
|
||||||
userAccessToken: "",
|
|
||||||
episodeRequestQuota: null,
|
|
||||||
movieRequestQuota: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public create() {
|
|
||||||
this.user.claims = this.availableClaims;
|
|
||||||
|
|
||||||
if (this.user.password) {
|
|
||||||
if (this.user.password !== this.confirmPass) {
|
|
||||||
this.notificationSerivce.error("Passwords do not match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const hasClaims = this.availableClaims.some((item) => {
|
|
||||||
if (item.enabled) { return true; }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasClaims) {
|
|
||||||
this.notificationSerivce.error("Please assign a role");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.identityService.createUser(this.user).subscribe(x => {
|
|
||||||
if (x.successful) {
|
|
||||||
this.notificationSerivce.success(`The user ${this.user.userName} has been created successfully`);
|
|
||||||
this.router.navigate(["usermanagement"]);
|
|
||||||
} else {
|
|
||||||
x.errors.forEach((val) => {
|
|
||||||
this.notificationSerivce.error(val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
<div *ngIf="user">
|
|
||||||
<div class="user-details">
|
|
||||||
<h3>User: {{user.userName}}</h3>
|
|
||||||
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
|
|
||||||
|
|
||||||
|
|
||||||
<p-confirmDialog></p-confirmDialog>
|
|
||||||
|
|
||||||
<div class="modal-body" style="margin-top: 45px;">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>User Details</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>Roles</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="username" class="control-label">Username</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.userName" [readonly]="true" class="form-control form-control-custom " id="username" name="username" value="{{user?.userName}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="alias" class="control-label">Alias</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="emailAddress" class="control-label">Email Address</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}" [disabled]="user?.userType == 2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div *ngFor="let c of user.claims">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled">
|
|
||||||
<label for="create{{c.value}}">{{c.value | humanize}}</label>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom " id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label>
|
|
||||||
<div>
|
|
||||||
<input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom " id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button type="button" class="btn btn-primary-outline" (click)="update()">Update</button>
|
|
||||||
<button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button>
|
|
||||||
<button type="button" style="float:right;" class="btn btn-info-outline" (click)="resetPassword()" pTooltip="You need your SMTP settings setup">Send Reset Password Link</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,95 +0,0 @@
|
||||||
import { Component } from "@angular/core";
|
|
||||||
import { Router } from "@angular/router";
|
|
||||||
import { ConfirmationService } from "primeng/primeng";
|
|
||||||
|
|
||||||
import { ActivatedRoute } from "@angular/router";
|
|
||||||
import { IUser } from "../interfaces";
|
|
||||||
import { IdentityService } from "../services";
|
|
||||||
import { NotificationService } from "../services";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: "./usermanagement-edit.component.html",
|
|
||||||
})
|
|
||||||
export class UserManagementEditComponent {
|
|
||||||
public user: IUser;
|
|
||||||
public userId: string;
|
|
||||||
|
|
||||||
constructor(private identityService: IdentityService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private notificationService: NotificationService,
|
|
||||||
private router: Router,
|
|
||||||
private confirmationService: ConfirmationService) {
|
|
||||||
this.route.params
|
|
||||||
.subscribe((params: any) => {
|
|
||||||
this.userId = params.id;
|
|
||||||
|
|
||||||
this.identityService.getUserById(this.userId).subscribe(x => {
|
|
||||||
this.user = x;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public delete() {
|
|
||||||
|
|
||||||
this.confirmationService.confirm({
|
|
||||||
message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.",
|
|
||||||
header: "Are you sure?",
|
|
||||||
icon: "fa fa-trash",
|
|
||||||
accept: () => {
|
|
||||||
this.identityService.deleteUser(this.user).subscribe(x => {
|
|
||||||
if (x.successful) {
|
|
||||||
this.notificationService.success(`The user ${this.user.userName} was deleted`);
|
|
||||||
this.router.navigate(["usermanagement"]);
|
|
||||||
} else {
|
|
||||||
x.errors.forEach((val) => {
|
|
||||||
this.notificationService.error(val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
reject: () => {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public resetPassword() {
|
|
||||||
this.identityService.submitResetPassword(this.user.emailAddress).subscribe(x => {
|
|
||||||
if (x.successful) {
|
|
||||||
this.notificationService.success(`Sent reset password email to ${this.user.emailAddress}`);
|
|
||||||
this.router.navigate(["usermanagement"]);
|
|
||||||
} else {
|
|
||||||
x.errors.forEach((val) => {
|
|
||||||
this.notificationService.error(val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public update() {
|
|
||||||
const hasClaims = this.user.claims.some((item) => {
|
|
||||||
if (item.enabled) { return true; }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasClaims) {
|
|
||||||
this.notificationService.error("Please assign a role");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.identityService.updateUser(this.user).subscribe(x => {
|
|
||||||
if (x.successful) {
|
|
||||||
this.notificationService.success(`The user ${this.user.userName} has been updated successfully`);
|
|
||||||
this.router.navigate(["usermanagement"]);
|
|
||||||
} else {
|
|
||||||
x.errors.forEach((val) => {
|
|
||||||
this.notificationService.error(val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
<h3 *ngIf="!edit">Create User</h3>
|
||||||
|
<h3 *ngIf="edit && user"> User: {{user.userName}}</h3>
|
||||||
|
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
|
||||||
|
<div *ngIf="!edit || edit && user">
|
||||||
|
|
||||||
|
<div class="modal-body" style="margin-top: 45px;">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4>User Details</h4>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h4>User Settings</h4>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" class="control-label">Username</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.userName" class="form-control form-control-custom " id="username"
|
||||||
|
name="username" value="{{user?.userName}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="alias" class="control-label">Alias</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias"
|
||||||
|
name="alias" value="{{user?.alias}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="emailAddress" class="control-label">Email Address</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress"
|
||||||
|
name="emailAddress" value="{{user?.emailAddress}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="control-label">Password</label>
|
||||||
|
<div>
|
||||||
|
<input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password"
|
||||||
|
name="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngIf="!edit">
|
||||||
|
<label for="confirmPass" class="control-label">Confirm Password</label>
|
||||||
|
<div>
|
||||||
|
<input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass"
|
||||||
|
name="confirmPass">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ngb-accordion [closeOthers]="true" activeIds="0-header">
|
||||||
|
<ngb-panel title="Roles">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="panel panel-default a">
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
<div *ngIf="!edit">
|
||||||
|
<div *ngFor="let c of availableClaims">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}"
|
||||||
|
[attr.name]="'create' + c.value" ng-checked="c.enabled">
|
||||||
|
<label for="create{{c.value}}">{{c.value | humanize}}</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="edit">
|
||||||
|
<div *ngFor="let c of user.claims">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}"
|
||||||
|
[attr.name]="'create' + c.value" ng-checked="c.enabled">
|
||||||
|
<label for="create{{c.value}}">{{c.value | humanize}}</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
<ngb-panel title="Request Limits">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="panel panel-default a">
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom "
|
||||||
|
id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom "
|
||||||
|
id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="musicRequestLimit" class="control-label">Music Request Limit</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="user.musicRequestLimit" class="form-control form-small form-control-custom "
|
||||||
|
id="musicRequestLimit" name="musicRequestLimit" value="{{user?.musicRequestLimit}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
<ngb-panel title="Notification Preferences" *ngIf="notificationPreferences">
|
||||||
|
<ng-template ngbPanelContent>
|
||||||
|
<div class="panel panel-default a">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div *ngFor="let pref of notificationPreferences">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{pref.agent}}" class="control-label">{{NotificationAgent[pref.agent] | humanize}}</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" [(ngModel)]="pref.value" class="form-control form-control-custom"
|
||||||
|
name="{{pref.agent}}" value="{{pref?.value}}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
</ngb-panel>
|
||||||
|
</ngb-accordion>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<button *ngIf="!edit" type="button" class="btn btn-danger-outline" (click)="create()">Create</button>
|
||||||
|
<div *ngIf="edit">
|
||||||
|
<button type="button" class="btn btn-primary-outline" (click)="update()">Update</button>
|
||||||
|
<button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button>
|
||||||
|
<button type="button" style="float:right;" class="btn btn-info-outline" (click)="resetPassword()"
|
||||||
|
pTooltip="You need your SMTP settings setup">Send Reset Password Link</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { ICheckbox, INotificationAgent, INotificationPreferences, IUser, UserType } from "../interfaces";
|
||||||
|
import { IdentityService, NotificationService } from "../services";
|
||||||
|
|
||||||
|
import { ConfirmationService } from "primeng/primeng";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./usermanagement-user.component.html",
|
||||||
|
})
|
||||||
|
export class UserManagementUserComponent implements OnInit {
|
||||||
|
|
||||||
|
public user: IUser;
|
||||||
|
public userId: string;
|
||||||
|
public availableClaims: ICheckbox[];
|
||||||
|
public confirmPass: "";
|
||||||
|
public notificationPreferences: INotificationPreferences[];
|
||||||
|
|
||||||
|
public NotificationAgent = INotificationAgent;
|
||||||
|
public edit: boolean;
|
||||||
|
|
||||||
|
constructor(private identityService: IdentityService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private confirmationService: ConfirmationService) {
|
||||||
|
this.route.params
|
||||||
|
.subscribe((params: any) => {
|
||||||
|
if(params.id) {
|
||||||
|
this.userId = params.id;
|
||||||
|
this.edit = true;
|
||||||
|
this.identityService.getUserById(this.userId).subscribe(x => {
|
||||||
|
this.user = x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x);
|
||||||
|
if(this.edit) {
|
||||||
|
this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x);
|
||||||
|
} else {
|
||||||
|
this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x);
|
||||||
|
}
|
||||||
|
if(!this.edit) {
|
||||||
|
this.user = {
|
||||||
|
alias: "",
|
||||||
|
claims: [],
|
||||||
|
emailAddress: "",
|
||||||
|
id: "",
|
||||||
|
password: "",
|
||||||
|
userName: "",
|
||||||
|
userType: UserType.LocalUser,
|
||||||
|
checked: false,
|
||||||
|
hasLoggedIn: false,
|
||||||
|
lastLoggedIn: new Date(),
|
||||||
|
episodeRequestLimit: 0,
|
||||||
|
movieRequestLimit: 0,
|
||||||
|
userAccessToken: "",
|
||||||
|
musicRequestLimit: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public create() {
|
||||||
|
this.user.claims = this.availableClaims;
|
||||||
|
|
||||||
|
if (this.user.password) {
|
||||||
|
if (this.user.password !== this.confirmPass) {
|
||||||
|
this.notificationService.error("Passwords do not match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const hasClaims = this.availableClaims.some((item) => {
|
||||||
|
if (item.enabled) { return true; }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasClaims) {
|
||||||
|
this.notificationService.error("Please assign a role");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.identityService.createUser(this.user).subscribe(x => {
|
||||||
|
if (x.successful) {
|
||||||
|
this.notificationService.success(`The user ${this.user.userName} has been created successfully`);
|
||||||
|
this.router.navigate(["usermanagement"]);
|
||||||
|
} else {
|
||||||
|
x.errors.forEach((val) => {
|
||||||
|
this.notificationService.error(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete() {
|
||||||
|
|
||||||
|
this.confirmationService.confirm({
|
||||||
|
message: "Are you sure that you want to delete this user? If this user has any requests they will also be deleted.",
|
||||||
|
header: "Are you sure?",
|
||||||
|
icon: "fa fa-trash",
|
||||||
|
accept: () => {
|
||||||
|
this.identityService.deleteUser(this.user).subscribe(x => {
|
||||||
|
if (x.successful) {
|
||||||
|
this.notificationService.success(`The user ${this.user.userName} was deleted`);
|
||||||
|
this.router.navigate(["usermanagement"]);
|
||||||
|
} else {
|
||||||
|
x.errors.forEach((val) => {
|
||||||
|
this.notificationService.error(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reject: () => {
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetPassword() {
|
||||||
|
this.identityService.submitResetPassword(this.user.emailAddress).subscribe(x => {
|
||||||
|
if (x.successful) {
|
||||||
|
this.notificationService.success(`Sent reset password email to ${this.user.emailAddress}`);
|
||||||
|
this.router.navigate(["usermanagement"]);
|
||||||
|
} else {
|
||||||
|
x.errors.forEach((val) => {
|
||||||
|
this.notificationService.error(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public update() {
|
||||||
|
const hasClaims = this.user.claims.some((item) => {
|
||||||
|
if (item.enabled) { return true; }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasClaims) {
|
||||||
|
this.notificationService.error("Please assign a role");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.identityService.updateUser(this.user).subscribe(x => {
|
||||||
|
if (x.successful) {
|
||||||
|
this.identityService.updateNotificationPreferences(this.notificationPreferences).subscribe();
|
||||||
|
this.notificationService.success(`The user ${this.user.userName} has been updated successfully`);
|
||||||
|
this.router.navigate(["usermanagement"]);
|
||||||
|
} else {
|
||||||
|
x.errors.forEach((val) => {
|
||||||
|
this.notificationService.error(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-success-outline" [routerLink]="['/usermanagement/add']">Add User To Ombi</button>
|
<button type="button" class="btn btn-success-outline" [routerLink]="['/usermanagement/user']">Add User To Ombi</button>
|
||||||
|
|
||||||
<button type="button" style="float:right;" class="btn btn-primary-outline"(click)="showBulkEdit = !showBulkEdit" [disabled]="!hasChecked()">Bulk Edit</button>
|
<button type="button" style="float:right;" class="btn btn-primary-outline"(click)="showBulkEdit = !showBulkEdit" [disabled]="!hasChecked()">Bulk Edit</button>
|
||||||
<div *ngIf="plexEnabled">
|
<div *ngIf="plexEnabled">
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
<span *ngIf="u.userType === 3">Emby User</span>
|
<span *ngIf="u.userType === 3">Emby User</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a [routerLink]="['/usermanagement/edit/' + u.id]" class="btn btn-sm btn-info-outline">Details/Edit</a>
|
<a [routerLink]="['/usermanagement/user/' + u.id]" class="btn btn-sm btn-info-outline">Details/Edit</a>
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="customizationSettings">
|
<td *ngIf="customizationSettings">
|
||||||
<button *ngIf="!u.hasLoggedIn" (click)="welcomeEmail(u)" [disabled]="!customizationSettings.applicationUrl" class="btn btn-sm btn-info-outline">Send Welcome Email</button>
|
<button *ngIf="!u.hasLoggedIn" (click)="welcomeEmail(u)" [disabled]="!customizationSettings.applicationUrl" class="btn btn-sm btn-info-outline">Send Welcome Email</button>
|
||||||
|
|
|
@ -7,9 +7,8 @@ import { ConfirmationService, ConfirmDialogModule, MultiSelectModule, SidebarMod
|
||||||
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
|
||||||
import { UpdateDetailsComponent } from "./updatedetails.component";
|
import { UpdateDetailsComponent } from "./updatedetails.component";
|
||||||
import { UserManagementAddComponent } from "./usermanagement-add.component";
|
|
||||||
import { UserManagementEditComponent } from "./usermanagement-edit.component";
|
|
||||||
import { UserManagementComponent } from "./usermanagement.component";
|
import { UserManagementComponent } from "./usermanagement.component";
|
||||||
|
import { UserManagementUserComponent } from "./usermanagement-user.component";
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { IdentityService, PlexService } from "../services";
|
import { IdentityService, PlexService } from "../services";
|
||||||
|
@ -23,8 +22,8 @@ import { SharedModule } from "../shared/shared.module";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
|
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "add", component: UserManagementAddComponent, canActivate: [AuthGuard] },
|
{ path: "user", component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "edit/:id", component: UserManagementEditComponent, canActivate: [AuthGuard] },
|
{ path: "user/:id", component: UserManagementUserComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "updatedetails", component: UpdateDetailsComponent, canActivate: [AuthGuard] },
|
{ path: "updatedetails", component: UpdateDetailsComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -45,10 +44,9 @@ const routes: Routes = [
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
UserManagementComponent,
|
UserManagementComponent,
|
||||||
UserManagementAddComponent,
|
|
||||||
UserManagementEditComponent,
|
|
||||||
UpdateDetailsComponent,
|
UpdateDetailsComponent,
|
||||||
AddPlexUserComponent,
|
AddPlexUserComponent,
|
||||||
|
UserManagementUserComponent,
|
||||||
],
|
],
|
||||||
entryComponents:[
|
entryComponents:[
|
||||||
AddPlexUserComponent,
|
AddPlexUserComponent,
|
||||||
|
|
|
@ -62,6 +62,8 @@ namespace Ombi.Controllers
|
||||||
IRepository<NotificationUserId> notificationRepository,
|
IRepository<NotificationUserId> notificationRepository,
|
||||||
IRepository<RequestSubscription> subscriptionRepository,
|
IRepository<RequestSubscription> subscriptionRepository,
|
||||||
ISettingsService<UserManagementSettings> umSettings,
|
ISettingsService<UserManagementSettings> umSettings,
|
||||||
|
IRepository<UserNotificationPreferences> notificationPreferences,
|
||||||
|
IMusicRequestRepository musicRepo),
|
||||||
IMovieRequestEngine movieRequestEngine,
|
IMovieRequestEngine movieRequestEngine,
|
||||||
ITvRequestEngine tvRequestEngine)
|
ITvRequestEngine tvRequestEngine)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +75,7 @@ namespace Ombi.Controllers
|
||||||
CustomizationSettings = c;
|
CustomizationSettings = c;
|
||||||
WelcomeEmail = welcome;
|
WelcomeEmail = welcome;
|
||||||
MovieRepo = m;
|
MovieRepo = m;
|
||||||
|
MusicRepo = musicRepo;
|
||||||
TvRepo = t;
|
TvRepo = t;
|
||||||
_log = l;
|
_log = l;
|
||||||
_plexApi = plexApi;
|
_plexApi = plexApi;
|
||||||
|
@ -86,6 +89,7 @@ namespace Ombi.Controllers
|
||||||
_userManagementSettings = umSettings;
|
_userManagementSettings = umSettings;
|
||||||
TvRequestEngine = tvRequestEngine;
|
TvRequestEngine = tvRequestEngine;
|
||||||
MovieRequestEngine = movieRequestEngine;
|
MovieRequestEngine = movieRequestEngine;
|
||||||
|
_userNotificationPreferences = notificationPreferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OmbiUserManager UserManager { get; }
|
private OmbiUserManager UserManager { get; }
|
||||||
|
@ -101,6 +105,7 @@ namespace Ombi.Controllers
|
||||||
private ITvRequestRepository TvRepo { get; }
|
private ITvRequestRepository TvRepo { get; }
|
||||||
private IMovieRequestEngine MovieRequestEngine { get; }
|
private IMovieRequestEngine MovieRequestEngine { get; }
|
||||||
private ITvRequestEngine TvRequestEngine { get; }
|
private ITvRequestEngine TvRequestEngine { get; }
|
||||||
|
private IMusicRequestRepository MusicRepo { get; }
|
||||||
private readonly ILogger<IdentityController> _log;
|
private readonly ILogger<IdentityController> _log;
|
||||||
private readonly IPlexApi _plexApi;
|
private readonly IPlexApi _plexApi;
|
||||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||||
|
@ -109,6 +114,7 @@ namespace Ombi.Controllers
|
||||||
private readonly IRepository<RequestLog> _requestLogRepository;
|
private readonly IRepository<RequestLog> _requestLogRepository;
|
||||||
private readonly IRepository<NotificationUserId> _notificationRepository;
|
private readonly IRepository<NotificationUserId> _notificationRepository;
|
||||||
private readonly IRepository<RequestSubscription> _requestSubscriptionRepository;
|
private readonly IRepository<RequestSubscription> _requestSubscriptionRepository;
|
||||||
|
private readonly IRepository<UserNotificationPreferences> _userNotificationPreferences;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is what the Wizard will call when creating the user for the very first time.
|
/// This is what the Wizard will call when creating the user for the very first time.
|
||||||
|
@ -128,7 +134,7 @@ namespace Ombi.Controllers
|
||||||
if (users.Any(x => !x.UserName.Equals("api", StringComparison.InvariantCultureIgnoreCase)))
|
if (users.Any(x => !x.UserName.Equals("api", StringComparison.InvariantCultureIgnoreCase)))
|
||||||
{
|
{
|
||||||
// No one should be calling this. Only the wizard
|
// No one should be calling this. Only the wizard
|
||||||
return new SaveWizardResult{ Result = false, Errors = new List<string> {"Looks like there is an existing user!"} };
|
return new SaveWizardResult { Result = false, Errors = new List<string> { "Looks like there is an existing user!" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.UsePlexAdminAccount)
|
if (user.UsePlexAdminAccount)
|
||||||
|
@ -298,7 +304,8 @@ namespace Ombi.Controllers
|
||||||
LastLoggedIn = user.LastLoggedIn,
|
LastLoggedIn = user.LastLoggedIn,
|
||||||
HasLoggedIn = user.LastLoggedIn.HasValue,
|
HasLoggedIn = user.LastLoggedIn.HasValue,
|
||||||
EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0,
|
EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0,
|
||||||
MovieRequestLimit = user.MovieRequestLimit ?? 0
|
MovieRequestLimit = user.MovieRequestLimit ?? 0,
|
||||||
|
MusicRequestLimit = user.MusicRequestLimit ?? 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var role in userRoles)
|
foreach (var role in userRoles)
|
||||||
|
@ -360,6 +367,7 @@ namespace Ombi.Controllers
|
||||||
UserType = UserType.LocalUser,
|
UserType = UserType.LocalUser,
|
||||||
MovieRequestLimit = user.MovieRequestLimit,
|
MovieRequestLimit = user.MovieRequestLimit,
|
||||||
EpisodeRequestLimit = user.EpisodeRequestLimit,
|
EpisodeRequestLimit = user.EpisodeRequestLimit,
|
||||||
|
MusicRequestLimit = user.MusicRequestLimit,
|
||||||
UserAccessToken = Guid.NewGuid().ToString("N"),
|
UserAccessToken = Guid.NewGuid().ToString("N"),
|
||||||
};
|
};
|
||||||
var userResult = await UserManager.CreateAsync(ombiUser, user.Password);
|
var userResult = await UserManager.CreateAsync(ombiUser, user.Password);
|
||||||
|
@ -503,6 +511,7 @@ namespace Ombi.Controllers
|
||||||
user.Email = ui.EmailAddress;
|
user.Email = ui.EmailAddress;
|
||||||
user.MovieRequestLimit = ui.MovieRequestLimit;
|
user.MovieRequestLimit = ui.MovieRequestLimit;
|
||||||
user.EpisodeRequestLimit = ui.EpisodeRequestLimit;
|
user.EpisodeRequestLimit = ui.EpisodeRequestLimit;
|
||||||
|
user.MusicRequestLimit = ui.MusicRequestLimit;
|
||||||
var updateResult = await UserManager.UpdateAsync(user);
|
var updateResult = await UserManager.UpdateAsync(user);
|
||||||
if (!updateResult.Succeeded)
|
if (!updateResult.Succeeded)
|
||||||
{
|
{
|
||||||
|
@ -575,7 +584,8 @@ namespace Ombi.Controllers
|
||||||
// We need to delete all the requests first
|
// We need to delete all the requests first
|
||||||
var moviesUserRequested = MovieRepo.GetAll().Where(x => x.RequestedUserId == userId);
|
var moviesUserRequested = MovieRepo.GetAll().Where(x => x.RequestedUserId == userId);
|
||||||
var tvUserRequested = TvRepo.GetChild().Where(x => x.RequestedUserId == userId);
|
var tvUserRequested = TvRepo.GetChild().Where(x => x.RequestedUserId == userId);
|
||||||
|
var musicRequested = MusicRepo.GetAll().Where(x => x.RequestedUserId == userId);
|
||||||
|
|
||||||
if (moviesUserRequested.Any())
|
if (moviesUserRequested.Any())
|
||||||
{
|
{
|
||||||
await MovieRepo.DeleteRange(moviesUserRequested);
|
await MovieRepo.DeleteRange(moviesUserRequested);
|
||||||
|
@ -585,6 +595,11 @@ namespace Ombi.Controllers
|
||||||
await TvRepo.DeleteChildRange(tvUserRequested);
|
await TvRepo.DeleteChildRange(tvUserRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (musicRequested.Any())
|
||||||
|
{
|
||||||
|
await MusicRepo.DeleteRange(musicRequested);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete any issues and request logs
|
// Delete any issues and request logs
|
||||||
var issues = _issuesRepository.GetAll().Where(x => x.UserReportedId == userId);
|
var issues = _issuesRepository.GetAll().Where(x => x.UserReportedId == userId);
|
||||||
var issueComments = _issueCommentsRepository.GetAll().Where(x => x.UserId == userId);
|
var issueComments = _issueCommentsRepository.GetAll().Where(x => x.UserId == userId);
|
||||||
|
@ -601,9 +616,9 @@ namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
await _issueCommentsRepository.DeleteRange(issueComments);
|
await _issueCommentsRepository.DeleteRange(issueComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the Subscriptions and mobile notification ids
|
// Delete the Subscriptions and mobile notification ids
|
||||||
var subs = _requestSubscriptionRepository.GetAll().Where(x => x.UserId == userId);
|
var subs = _requestSubscriptionRepository.GetAll().Where(x => x.UserId == userId);
|
||||||
var mobileIds = _notificationRepository.GetAll().Where(x => x.UserId == userId);
|
var mobileIds = _notificationRepository.GetAll().Where(x => x.UserId == userId);
|
||||||
if (subs.Any())
|
if (subs.Any())
|
||||||
{
|
{
|
||||||
|
@ -803,6 +818,92 @@ namespace Ombi.Controllers
|
||||||
return user.UserAccessToken;
|
return user.UserAccessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("notificationpreferences")]
|
||||||
|
public async Task<List<UserNotificationPreferences>> GetUserPreferences()
|
||||||
|
{
|
||||||
|
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
|
||||||
|
return await GetPreferences(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("notificationpreferences/{userId}")]
|
||||||
|
public async Task<List<UserNotificationPreferences>> GetUserPreferences(string userId)
|
||||||
|
{
|
||||||
|
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
|
||||||
|
return await GetPreferences(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<NotificationAgent> _excludedAgents = new List<NotificationAgent>
|
||||||
|
{
|
||||||
|
NotificationAgent.Email,
|
||||||
|
NotificationAgent.Mobile
|
||||||
|
};
|
||||||
|
private async Task<List<UserNotificationPreferences>> GetPreferences(OmbiUser user)
|
||||||
|
{
|
||||||
|
var userPreferences = await _userNotificationPreferences.GetAll().Where(x => x.UserId == user.Id).ToListAsync();
|
||||||
|
|
||||||
|
var agents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>().Where(x => !_excludedAgents.Contains(x));
|
||||||
|
foreach (var a in agents)
|
||||||
|
{
|
||||||
|
var agent = userPreferences.FirstOrDefault(x => x.Agent == a);
|
||||||
|
if (agent == null)
|
||||||
|
{
|
||||||
|
// Create the default
|
||||||
|
userPreferences.Add(new UserNotificationPreferences
|
||||||
|
{
|
||||||
|
Agent = a,
|
||||||
|
UserId = user.Id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("NotificationPreferences")]
|
||||||
|
public async Task<IActionResult> AddUserNotificationPreference([FromBody] List<AddNotificationPreference> preferences)
|
||||||
|
{
|
||||||
|
foreach (var pref in preferences)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Make sure the user exists
|
||||||
|
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == pref.UserId);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
// Check if we are editing a different user than ourself, if we are then we need to power user role
|
||||||
|
var me = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
|
||||||
|
if (!me.Id.Equals(user.Id, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var isPowerUser = await UserManager.IsInRoleAsync(me, OmbiRoles.PowerUser);
|
||||||
|
var isAdmin = await UserManager.IsInRoleAsync(me, OmbiRoles.Admin);
|
||||||
|
if (!isPowerUser && !isAdmin)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't already have a preference for this agent
|
||||||
|
var existingPreference = await _userNotificationPreferences.GetAll()
|
||||||
|
.FirstOrDefaultAsync(x => x.UserId == user.Id && x.Agent == pref.Agent);
|
||||||
|
if (existingPreference != null)
|
||||||
|
{
|
||||||
|
// Update it
|
||||||
|
existingPreference.Value = pref.Value;
|
||||||
|
existingPreference.Enabled = pref.Enabled;
|
||||||
|
}
|
||||||
|
await _userNotificationPreferences.Add(new UserNotificationPreferences
|
||||||
|
{
|
||||||
|
Agent = pref.Agent,
|
||||||
|
Enabled = pref.Enabled,
|
||||||
|
UserId = pref.UserId,
|
||||||
|
Value = pref.Value
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
return Json(true);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<IdentityResult>> AddRoles(IEnumerable<ClaimCheckboxes> roles, OmbiUser ombiUser)
|
private async Task<List<IdentityResult>> AddRoles(IEnumerable<ClaimCheckboxes> roles, OmbiUser ombiUser)
|
||||||
{
|
{
|
||||||
var roleResult = new List<IdentityResult>();
|
var roleResult = new List<IdentityResult>();
|
||||||
|
|
12
src/Ombi/Models/Identity/AddNotificationPreference.cs
Normal file
12
src/Ombi/Models/Identity/AddNotificationPreference.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
|
namespace Ombi.Models.Identity
|
||||||
|
{
|
||||||
|
public class AddNotificationPreference
|
||||||
|
{
|
||||||
|
public NotificationAgent Agent { get; set; }
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
<FileVersion>$(SemVer)</FileVersion>
|
<FileVersion>$(SemVer)</FileVersion>
|
||||||
<Version>$(FullVer)</Version>
|
<Version>$(FullVer)</Version>
|
||||||
<PackageVersion></PackageVersion>
|
<PackageVersion></PackageVersion>
|
||||||
<TypeScriptToolsVersion>2.8</TypeScriptToolsVersion>
|
<TypeScriptToolsVersion>3.0</TypeScriptToolsVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue