Registration and last login date are now tracked for users, fixes #1296

This commit is contained in:
Lombiq
2015-06-13 23:56:40 +02:00
parent e066e39070
commit de9fc2fb5b
9 changed files with 103 additions and 7 deletions
@@ -155,6 +155,7 @@ namespace Orchard.Users.Controllers {
} }
_authenticationService.SignIn(user, false /* createPersistentCookie */); _authenticationService.SignIn(user, false /* createPersistentCookie */);
_userEventHandler.LoggedIn(user);
return this.RedirectLocal(returnUrl); return this.RedirectLocal(returnUrl);
} }
@@ -87,6 +87,12 @@ namespace Orchard.Users.Controllers {
case UsersOrder.Email: case UsersOrder.Email:
users = users.OrderBy(u => u.Email); users = users.OrderBy(u => u.Email);
break; break;
case UsersOrder.CreatedUtc:
users = users.OrderBy(u => u.CreatedUtc);
break;
case UsersOrder.LastLoginUtc:
users = users.OrderBy(u => u.LastLoginUtc);
break;
} }
var results = users var results = users
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.Security;
using Orchard.Services;
using Orchard.Users.Models;
namespace Orchard.Users.Events {
public class LoginUserEventHandler : IUserEventHandler {
private readonly IClock _clock;
public LoginUserEventHandler(IClock clock) {
_clock = clock;
}
public void Creating(UserContext context) { }
public void Created(UserContext context) { }
public void LoggedIn(IUser user) {
user.As<UserPart>().LastLoginUtc = _clock.UtcNow;
}
public void LoggedOut(IUser user) { }
public void AccessDenied(IUser user) { }
public void ChangedPassword(IUser user) { }
public void SentChallengeEmail(IUser user) { }
public void ConfirmedEmail(IUser user) { }
public void Approved(IUser user) { }
public void LoggingIn(string userNameOrEmail, string password) { }
public void LogInFailed(string userNameOrEmail, string password) { }
}
}
@@ -1,6 +1,7 @@
using Orchard.ContentManagement.MetaData; using System;
using Orchard.Data.Migration; using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions; using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.Users { namespace Orchard.Users {
public class UsersDataMigration : DataMigrationImpl { public class UsersDataMigration : DataMigrationImpl {
@@ -19,9 +20,13 @@ namespace Orchard.Users {
.Column<string>("RegistrationStatus", c => c.WithDefault("Approved")) .Column<string>("RegistrationStatus", c => c.WithDefault("Approved"))
.Column<string>("EmailStatus", c => c.WithDefault("Approved")) .Column<string>("EmailStatus", c => c.WithDefault("Approved"))
.Column<string>("EmailChallengeToken") .Column<string>("EmailChallengeToken")
.Column<DateTime>("CreatedUtc")
.Column<DateTime>("LastLoginUtc")
); );
return 1; ContentDefinitionManager.AlterTypeDefinition("User", cfg => cfg.Creatable(false));
return 3;
} }
public int UpdateFrom1() { public int UpdateFrom1() {
@@ -29,5 +34,15 @@ namespace Orchard.Users {
return 2; return 2;
} }
public int UpdateFrom2() {
SchemaBuilder.AlterTable("UserPartRecord",
table => {
table.AddColumn<DateTime>("CreatedUtc");
table.AddColumn<DateTime>("LastLoginUtc");
});
return 3;
}
} }
} }
@@ -1,13 +1,14 @@
using System.Web.Security; using System;
using System.Web.Security;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Security; using Orchard.Security;
namespace Orchard.Users.Models { namespace Orchard.Users.Models {
public sealed class UserPart : ContentPart<UserPartRecord>, IUser { public sealed class UserPart : ContentPart<UserPartRecord>, IUser {
public const string EmailPattern = public const string EmailPattern =
@"^(?![\.@])(""([^""\r\\]|\\[""\r\\])*""|([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)" @"^(?![\.@])(""([^""\r\\]|\\[""\r\\])*""|([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)"
+ @"@([a-z0-9][\w-]*\.)+[a-z]{2,}$"; + @"@([a-z0-9][\w-]*\.)+[a-z]{2,}$";
public const int MaxUserNameLength = 255; public const int MaxUserNameLength = 255;
public const int MaxEmailLength = 255; public const int MaxEmailLength = 255;
@@ -60,5 +61,15 @@ namespace Orchard.Users.Models {
get { return Retrieve(x => x.EmailStatus); } get { return Retrieve(x => x.EmailStatus); }
set { Store(x => x.EmailStatus, value); } set { Store(x => x.EmailStatus, value); }
} }
public DateTime? CreatedUtc {
get { return Retrieve(x => x.CreatedUtc); }
set { Store(x => x.CreatedUtc, value); }
}
public DateTime? LastLoginUtc {
get { return Retrieve(x => x.LastLoginUtc); }
set { Store(x => x.LastLoginUtc, value); }
}
} }
} }
@@ -1,3 +1,4 @@
using System;
using System.Web.Security; using System.Web.Security;
using Orchard.ContentManagement.Records; using Orchard.ContentManagement.Records;
@@ -15,5 +16,7 @@ namespace Orchard.Users.Models {
public virtual UserStatus RegistrationStatus { get; set; } public virtual UserStatus RegistrationStatus { get; set; }
public virtual UserStatus EmailStatus { get; set; } public virtual UserStatus EmailStatus { get; set; }
public virtual string EmailChallengeToken { get; set; } public virtual string EmailChallengeToken { get; set; }
public virtual DateTime? CreatedUtc { get; set; }
public virtual DateTime? LastLoginUtc { get; set; }
} }
} }
@@ -28,6 +28,7 @@ namespace Orchard.Users.Services {
private readonly IShapeFactory _shapeFactory; private readonly IShapeFactory _shapeFactory;
private readonly IShapeDisplay _shapeDisplay; private readonly IShapeDisplay _shapeDisplay;
private readonly IAppConfigurationAccessor _appConfigurationAccessor; private readonly IAppConfigurationAccessor _appConfigurationAccessor;
private readonly IClock _clock;
public MembershipService( public MembershipService(
IOrchardServices orchardServices, IOrchardServices orchardServices,
@@ -45,6 +46,7 @@ namespace Orchard.Users.Services {
_shapeFactory = shapeFactory; _shapeFactory = shapeFactory;
_shapeDisplay = shapeDisplay; _shapeDisplay = shapeDisplay;
_appConfigurationAccessor = appConfigurationAccessor; _appConfigurationAccessor = appConfigurationAccessor;
_clock = clock;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -69,6 +71,7 @@ namespace Orchard.Users.Services {
user.Email = createUserParams.Email; user.Email = createUserParams.Email;
user.NormalizedUserName = createUserParams.Username.ToLowerInvariant(); user.NormalizedUserName = createUserParams.Username.ToLowerInvariant();
user.HashAlgorithm = PBKDF2; user.HashAlgorithm = PBKDF2;
user.CreatedUtc = _clock.UtcNow;
SetPassword(user, createUserParams.Password); SetPassword(user, createUserParams.Password);
if ( registrationSettings != null ) { if ( registrationSettings != null ) {
@@ -23,7 +23,9 @@ namespace Orchard.Users.ViewModels {
public enum UsersOrder { public enum UsersOrder {
Name, Name,
Email Email,
CreatedUtc,
LastLoginUtc
} }
public enum UsersFilter { public enum UsersFilter {
@@ -1,7 +1,10 @@
@model Orchard.Users.ViewModels.UsersIndexViewModel @model Orchard.Users.ViewModels.UsersIndexViewModel
@using Orchard.Core.Shapes;
@using Orchard.Users.Models; @using Orchard.Users.Models;
@using Orchard.Users.ViewModels; @using Orchard.Users.ViewModels;
@{ @{
var userIndex = 0; var userIndex = 0;
@@ -36,6 +39,8 @@
<select id="sortResults" name="@Html.NameOf(m => m.Options.Order)"> <select id="sortResults" name="@Html.NameOf(m => m.Options.Order)">
@Html.SelectOption(Model.Options.Order, UsersOrder.Name, T("Name").ToString()) @Html.SelectOption(Model.Options.Order, UsersOrder.Name, T("Name").ToString())
@Html.SelectOption(Model.Options.Order, UsersOrder.Email, T("Email").ToString()) @Html.SelectOption(Model.Options.Order, UsersOrder.Email, T("Email").ToString())
@Html.SelectOption(Model.Options.Order, UsersOrder.CreatedUtc, T("Creation Time").ToString())
@Html.SelectOption(Model.Options.Order, UsersOrder.LastLoginUtc, T("Last Login Time").ToString())
</select> </select>
<button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button> <button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
@@ -48,6 +53,8 @@
<th scope="col">@T("Name")</th> <th scope="col">@T("Name")</th>
<th scope="col">@T("Email")</th> <th scope="col">@T("Email")</th>
<th scope="col">@T("Actions")</th> <th scope="col">@T("Actions")</th>
<th scope="col">@T("Creation Time")</th>
<th scope="col">@T("Last Login Time")</th>
</tr> </tr>
</thead> </thead>
@foreach (var entry in Model.Users) { @foreach (var entry in Model.Users) {
@@ -79,6 +86,12 @@
@Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id }, new { itemprop = "UnsafeUrl" }) @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id }, new { itemprop = "UnsafeUrl" })
} }
</td> </td>
<td>
@Display(New.DateTimeRelative(dateTimeUtc: entry.User.CreatedUtc))
</td>
<td>
@Display(New.DateTimeRelative(dateTimeUtc: entry.User.LastLoginUtc))
</td>
</tr> </tr>
userIndex++; userIndex++;
} }