diff --git a/PlexRequests.Api.Models/Plex/PlexAuthentication.cs b/PlexRequests.Api.Models/Plex/PlexAuthentication.cs index 7d4a1a0be..40ff14fab 100644 --- a/PlexRequests.Api.Models/Plex/PlexAuthentication.cs +++ b/PlexRequests.Api.Models/Plex/PlexAuthentication.cs @@ -1,63 +1,64 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: PlexAuthentication.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ -#endregion - -using System.Collections.Generic; - -namespace PlexRequests.Api.Models.Plex -{ - public class PlexAuthentication - { - public User user { get; set; } - } - public class Subscription - { - public bool active { get; set; } - public string status { get; set; } - public object plan { get; set; } - public object features { get; set; } - } - - public class Roles - { - public List roles { get; set; } - } - - public class User - { - public string email { get; set; } - public string joined_at { get; set; } - public string username { get; set; } - public string title { get; set; } - public string authentication_token { get; set; } - public Subscription subscription { get; set; } - public Roles roles { get; set; } - public List entitlements { get; set; } - public object confirmed_at { get; set; } - public int forum_id { get; set; } - } -} - +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexAuthentication.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System.Collections.Generic; + +namespace PlexRequests.Api.Models.Plex +{ + public class PlexAuthentication + { + public User user { get; set; } + } + public class Subscription + { + public bool active { get; set; } + public string status { get; set; } + public object plan { get; set; } + public object features { get; set; } + } + + public class Roles + { + public List roles { get; set; } + } + + public class User + { + public string email { get; set; } + public string uuid { get; set; } + public string joined_at { get; set; } + public string username { get; set; } + public string title { get; set; } + public string authentication_token { get; set; } + public Subscription subscription { get; set; } + public Roles roles { get; set; } + public List entitlements { get; set; } + public object confirmed_at { get; set; } + public int forum_id { get; set; } + } +} + diff --git a/PlexRequests.Core.Migration/Migrations/Version195.cs b/PlexRequests.Core.Migration/Migrations/Version195.cs index 24ddf9ea1..790fef19c 100644 --- a/PlexRequests.Core.Migration/Migrations/Version195.cs +++ b/PlexRequests.Core.Migration/Migrations/Version195.cs @@ -53,12 +53,11 @@ namespace PlexRequests.Core.Migration.Migrations UpdateApplicationSettings(); UpdateDb(con); - UpdateSchema(con, Version); + UpdateSchema(con, Version); } private void UpdateDb(IDbConnection con) { - } private void UpdateApplicationSettings() diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index f172226d0..a9db70741 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -139,7 +139,6 @@ namespace PlexRequests.Core return CreateUser(username, password, new[] { UserClaims.User }, properties); } - public IEnumerable GetAllClaims() { var properties = typeof(UserClaims).GetConstantsValues(); @@ -200,6 +199,5 @@ namespace PlexRequests.Core Guid? CreatePowerUser(string username, string password, UserProperties properties = null); Guid? CreateRegularUser(string username, string password, UserProperties properties = null); void DeleteUser(string userId); - } } diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj index 9c5f69c04..0ffffb4c8 100644 --- a/PlexRequests.Helpers/PlexRequests.Helpers.csproj +++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj @@ -87,6 +87,7 @@ + diff --git a/PlexRequests.Helpers/UserType.cs b/PlexRequests.Helpers/UserType.cs new file mode 100644 index 000000000..b9a3c98a2 --- /dev/null +++ b/PlexRequests.Helpers/UserType.cs @@ -0,0 +1,35 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserType.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +namespace PlexRequests.Helpers +{ + + public enum UserType + { + PlexUser, + LocalUser + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexRequests.Store.csproj b/PlexRequests.Store/PlexRequests.Store.csproj index 78f777dbc..d8cf9514c 100644 --- a/PlexRequests.Store/PlexRequests.Store.csproj +++ b/PlexRequests.Store/PlexRequests.Store.csproj @@ -87,6 +87,7 @@ + diff --git a/PlexRequests.Store/SqlTables.sql b/PlexRequests.Store/SqlTables.sql index 4ac055c24..c0bb6cb27 100644 --- a/PlexRequests.Store/SqlTables.sql +++ b/PlexRequests.Store/SqlTables.sql @@ -11,6 +11,15 @@ CREATE TABLE IF NOT EXISTS Users UserProperties BLOB ); +CREATE TABLE IF NOT EXISTS UserLogins +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + UserId varchar(50) NOT NULL , + Type INTEGER NOT NULL, + LastLoggedIn varchar(100) NOT NULL +); + +CREATE INDEX IF NOT EXISTS UserLogins_UserId ON UserLogins (UserId); CREATE TABLE IF NOT EXISTS GlobalSettings ( diff --git a/PlexRequests.Store/TableCreation.cs b/PlexRequests.Store/TableCreation.cs index 4123ee685..295a3ca53 100644 --- a/PlexRequests.Store/TableCreation.cs +++ b/PlexRequests.Store/TableCreation.cs @@ -57,7 +57,7 @@ namespace PlexRequests.Store } } - public static void AddColumn(this IDbConnection connection, string tableName, string alterType, string newColumn, bool isNullable, string dataType) + public static void AddColumn(this IDbConnection connection, string tableName, string alterType, string newColumn, bool allowNulls, string dataType) { connection.Open(); var result = connection.Query($"PRAGMA table_info({tableName});"); @@ -67,7 +67,7 @@ namespace PlexRequests.Store } var query = $"ALTER TABLE {tableName} {alterType} {newColumn} {dataType}"; - if (isNullable) + if (!allowNulls) { query = query + " NOT NULL"; } diff --git a/PlexRequests.Store/UserLogins.cs b/PlexRequests.Store/UserLogins.cs new file mode 100644 index 000000000..5ade33acd --- /dev/null +++ b/PlexRequests.Store/UserLogins.cs @@ -0,0 +1,41 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserLogins.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using Dapper.Contrib.Extensions; +using PlexRequests.Helpers; + +namespace PlexRequests.Store +{ + [Table("UserLogins")] + public class UserLogins : Entity + { + public string UserId { get; set; } + public UserType Type { get; set; } + public DateTime LastLoggedIn { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/UsersModel.cs b/PlexRequests.Store/UsersModel.cs index 7eb787431..88e04b446 100644 --- a/PlexRequests.Store/UsersModel.cs +++ b/PlexRequests.Store/UsersModel.cs @@ -24,6 +24,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + +using System; using Dapper.Contrib.Extensions; namespace PlexRequests.Store diff --git a/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs b/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs index 21f267fb2..16abe0615 100644 --- a/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs +++ b/PlexRequests.UI/Models/UserManagement/UserManagementUsersViewModel.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Newtonsoft.Json; +using PlexRequests.Helpers; namespace PlexRequests.UI.Models { @@ -18,6 +20,7 @@ namespace PlexRequests.UI.Models public UserManagementPlexInformation PlexInfo { get; set; } public string[] ClaimsArray { get; set; } public List ClaimsItem { get; set; } + public DateTime LastLoggedIn { get; set; } } public class UserManagementPlexInformation @@ -40,11 +43,6 @@ namespace PlexRequests.UI.Models public string NumLibraries { get; set; } } - public enum UserType - { - PlexUser, - LocalUser - } public class UserManagementCreateModel { diff --git a/PlexRequests.UI/Modules/LoginModule.cs b/PlexRequests.UI/Modules/LoginModule.cs index 00c4e0b4a..e6030207f 100644 --- a/PlexRequests.UI/Modules/LoginModule.cs +++ b/PlexRequests.UI/Modules/LoginModule.cs @@ -40,13 +40,15 @@ using Nancy.Security; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; +using PlexRequests.Store; +using PlexRequests.Store.Repository; using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { public class LoginModule : BaseModule { - public LoginModule(ISettingsService pr, ICustomUserMapper m, IResourceLinker linker) + public LoginModule(ISettingsService pr, ICustomUserMapper m, IResourceLinker linker, IRepository userLoginRepo) : base(pr) { UserMapper = m; @@ -101,6 +103,14 @@ namespace PlexRequests.UI.Modules { redirect = !string.IsNullOrEmpty(BaseUrl) ? $"/{BaseUrl}/search" : "/search"; } + + userLoginRepo.Insert(new UserLogins + { + LastLoggedIn = DateTime.UtcNow, + Type = UserType.LocalUser, + UserId = userId.ToString() + }); + return this.LoginAndRedirect(userId.Value, expiry, redirect); }; diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index be7a42f1f..56b583306 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -232,10 +232,18 @@ namespace PlexRequests.UI.Modules var plexMovies = Checker.GetPlexMovies(); var settings = await PrService.GetSettingsAsync(); var viewMovies = new List(); + var counter = 0; foreach (var movie in apiMovies) { - var movieInfoTask = MovieApi.GetMovieInformation(movie.Id).ConfigureAwait(false); // TODO needs to be careful about this, it's adding extra time to search... - // https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2 + var imdbId = string.Empty; + if (counter <= 5) // Let's only do it for the first 5 items + { + var movieInfoTask = await MovieApi.GetMovieInformation(movie.Id).ConfigureAwait(false); // TODO needs to be careful about this, it's adding extra time to search... + // https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2 + imdbId = movieInfoTask.ImdbId; + counter++; + } + var viewMovie = new SearchMovieViewModel { Adult = movie.Adult, @@ -254,8 +262,7 @@ namespace PlexRequests.UI.Modules VoteCount = movie.VoteCount }; var canSee = CanUserSeeThisRequest(viewMovie.Id, settings.UsersCanViewOnlyOwnRequests, dbMovies); - var movieInfo = await movieInfoTask; - var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(), movieInfo.ImdbId); + var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(), imdbId); if (plexMovie != null) { viewMovie.Available = true; diff --git a/PlexRequests.UI/Modules/UserLoginModule.cs b/PlexRequests.UI/Modules/UserLoginModule.cs index d9a0c5327..7ae55f511 100644 --- a/PlexRequests.UI/Modules/UserLoginModule.cs +++ b/PlexRequests.UI/Modules/UserLoginModule.cs @@ -42,6 +42,8 @@ using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Helpers.Analytics; +using PlexRequests.Store; +using PlexRequests.Store.Repository; using PlexRequests.UI.Models; @@ -52,7 +54,7 @@ namespace PlexRequests.UI.Modules public class UserLoginModule : BaseModule { public UserLoginModule(ISettingsService auth, IPlexApi api, ISettingsService plexSettings, ISettingsService pr, - ISettingsService lp, IAnalytics a, IResourceLinker linker) : base("userlogin", pr) + ISettingsService lp, IAnalytics a, IResourceLinker linker, IRepository userLogins) : base("userlogin", pr) { AuthService = auth; LandingPageSettings = lp; @@ -60,13 +62,14 @@ namespace PlexRequests.UI.Modules Api = api; PlexSettings = plexSettings; Linker = linker; + UserLogins = userLogins; Get["UserLoginIndex", "/", true] = async (x, ct) => { if (!string.IsNullOrEmpty(Username) || IsAdmin) { var url = Linker.BuildRelativeUri(Context, "SearchIndex").ToString(); - return Response.AsRedirect(url); + return Response.AsRedirect(url); } var settings = await AuthService.GetSettingsAsync(); return View["Index", settings]; @@ -82,11 +85,13 @@ namespace PlexRequests.UI.Modules private IPlexApi Api { get; } private IResourceLinker Linker { get; } private IAnalytics Analytics { get; } + private IRepository UserLogins { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); private async Task LoginUser() { + var userId = string.Empty; var dateTimeOffset = Request.Form.DateTimeOffset; var username = Request.Form.username.Value; Log.Debug("Username \"{0}\" attempting to login", username); @@ -135,6 +140,7 @@ namespace PlexRequests.UI.Modules authenticated = CheckIfUserIsInPlexFriends(username, plexSettings.PlexAuthToken); Log.Debug("Friends list result = {0}", authenticated); } + userId = signedIn.user.uuid; } } else if (settings.UserAuthentication) // Check against the users in Plex @@ -147,6 +153,11 @@ namespace PlexRequests.UI.Modules authenticated = true; } Log.Debug("Friends list result = {0}", authenticated); + if (authenticated) + { + // Get the user that is authenticated to store in the UserLogins + userId = GetUserIdIsInPlexFriends(username, plexSettings.PlexAuthToken); + } } else if (!settings.UserAuthentication) // No auth, let them pass! { @@ -156,13 +167,13 @@ namespace PlexRequests.UI.Modules if (authenticated) { + UserLogins.Insert(new UserLogins { UserId = userId, Type = UserType.PlexUser, LastLoggedIn = DateTime.UtcNow }); Log.Debug("We are authenticated! Setting session."); // Add to the session (Used in the BaseModules) Session[SessionKeys.UsernameKey] = (string)username; + Session[SessionKeys.ClientDateTimeOffsetKey] = (int)dateTimeOffset; } - Session[SessionKeys.ClientDateTimeOffsetKey] = (int)dateTimeOffset; - if (!authenticated) { var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex"); @@ -214,6 +225,14 @@ namespace PlexRequests.UI.Modules return allUsers != null && allUsers.Any(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase)); } + + private string GetUserIdIsInPlexFriends(string username, string authToken) + { + var users = Api.GetUsers(authToken); + var allUsers = users?.User?.Where(x => !string.IsNullOrEmpty(x.Title)); + return allUsers?.Where(x => x.Title.Equals(username, StringComparison.CurrentCultureIgnoreCase)).Select(x => x.Id).FirstOrDefault(); + } + private bool IsUserInDeniedList(string username, AuthenticationSettings settings) { return settings.DeniedUserList.Any(x => x.Equals(username, StringComparison.CurrentCultureIgnoreCase)); diff --git a/PlexRequests.UI/Modules/UserManagementModule.cs b/PlexRequests.UI/Modules/UserManagementModule.cs index 7a4005026..725120b89 100644 --- a/PlexRequests.UI/Modules/UserManagementModule.cs +++ b/PlexRequests.UI/Modules/UserManagementModule.cs @@ -14,13 +14,14 @@ using PlexRequests.Core.Models; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Store; +using PlexRequests.Store.Repository; using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { public class UserManagementModule : BaseModule { - public UserManagementModule(ISettingsService pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService plex) : base("usermanagement", pr) + public UserManagementModule(ISettingsService pr, ICustomUserMapper m, IPlexApi plexApi, ISettingsService plex, IRepository userLogins) : base("usermanagement", pr) { #if !DEBUG this.RequiresClaims(UserClaims.Admin); @@ -28,6 +29,7 @@ namespace PlexRequests.UI.Modules UserMapper = m; PlexApi = plexApi; PlexSettings = plex; + UserLoginsRepo = userLogins; Get["/"] = x => Load(); @@ -43,6 +45,7 @@ namespace PlexRequests.UI.Modules private ICustomUserMapper UserMapper { get; } private IPlexApi PlexApi { get; } private ISettingsService PlexSettings { get; } + private IRepository UserLoginsRepo { get; } private Negotiator Load() { @@ -55,7 +58,8 @@ namespace PlexRequests.UI.Modules var model = new List(); foreach (var user in localUsers) { - model.Add(MapLocalUser(user)); + var userDb = UserLoginsRepo.Get(user.UserGuid); + model.Add(MapLocalUser(user, userDb.LastLoggedIn)); } var plexSettings = await PlexSettings.GetSettingsAsync(); @@ -66,7 +70,7 @@ namespace PlexRequests.UI.Modules foreach (var u in plexUsers.User) { - + var userDb = UserLoginsRepo.Get(u.Id); model.Add(new UserManagementUsersViewModel { Username = u.Username, @@ -77,7 +81,8 @@ namespace PlexRequests.UI.Modules PlexInfo = new UserManagementPlexInformation { Thumb = u.Thumb - } + }, + LastLoggedIn = userDb.LastLoggedIn, }); } } @@ -105,7 +110,7 @@ namespace PlexRequests.UI.Modules var user = UserMapper.CreateUser(model.Username, model.Password, model.Claims, new UserProperties { EmailAddress = model.EmailAddress }); if (user.HasValue) { - return Response.AsJson(MapLocalUser(UserMapper.GetUser(user.Value))); + return Response.AsJson(MapLocalUser(UserMapper.GetUser(user.Value), DateTime.MinValue)); } return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user" }); @@ -150,8 +155,8 @@ namespace PlexRequests.UI.Modules userFound.UserProperties = ByteConverterHelper.ReturnBytes(currentProps); var user = UserMapper.EditUser(userFound); - - var retUser = MapLocalUser(user); + var dbUser = UserLoginsRepo.Get(user.UserGuid); + var retUser = MapLocalUser(user, dbUser.LastLoggedIn); return Response.AsJson(retUser); } @@ -224,7 +229,7 @@ namespace PlexRequests.UI.Modules return Response.AsJson(retVal); } - private UserManagementUsersViewModel MapLocalUser(UsersModel user) + private UserManagementUsersViewModel MapLocalUser(UsersModel user, DateTime lastLoggedIn) { var claims = ByteConverterHelper.ReturnObject(user.Claims); var claimsString = string.Join(", ", claims); @@ -240,7 +245,8 @@ namespace PlexRequests.UI.Modules EmailAddress = userProps.EmailAddress, Alias = userProps.UserAlias, ClaimsArray = claims, - ClaimsItem = new List() + ClaimsItem = new List(), + LastLoggedIn = lastLoggedIn }; // Add all of the current claims diff --git a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml index 7bb0a415d..2173ecf77 100644 --- a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml +++ b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml @@ -16,7 +16,7 @@ {
@record.Key
-
@record.Value.ToString("O")
+
@record.Value.ToString("R")

} diff --git a/PlexRequests.UI/Views/UserManagement/Index.cshtml b/PlexRequests.UI/Views/UserManagement/Index.cshtml index 7f9547c28..a24eebd9d 100644 --- a/PlexRequests.UI/Views/UserManagement/Index.cshtml +++ b/PlexRequests.UI/Views/UserManagement/Index.cshtml @@ -78,6 +78,14 @@ + + + + Last Logged In + + + + @@ -97,6 +105,9 @@ {{u.type === 1 ? 'Local User' : 'Plex User'}} + + {{u.lastLoggedIn}} + Details/Edit