();
- }
-
- private static void FormatEmailBody(MessageContext context) {
- context.MailMessage.Body = "" + context.MailMessage.Body;
- context.MailMessage.Body += "
";
- }
-
- public void Sent(MessageContext context) {
- }
- }
-}
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj b/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj
index 793b9391b..53be99315 100644
--- a/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj
+++ b/src/Orchard.Web/Modules/Orchard.Users/Orchard.Users.csproj
@@ -49,6 +49,10 @@
+
+ False
+ ..\..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll
+
3.5
@@ -73,7 +77,6 @@
-
@@ -120,6 +123,10 @@
{9916839C-39FC-4CEB-A5AF-89CA7E87119F}
Orchard.Core
+
+ {085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}
+ Orchard.Messaging
+
@@ -151,6 +158,15 @@
Designer
+
+
+
+
+
+
+
+
+
10.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs
index 1187127a3..b9cb972ca 100644
--- a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs
@@ -4,6 +4,8 @@ using System.Security.Cryptography;
using System.Text;
using System.Web.Security;
using JetBrains.Annotations;
+using Newtonsoft.Json;
+using Orchard.DisplayManagement;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.ContentManagement;
@@ -18,15 +20,26 @@ namespace Orchard.Users.Services {
[UsedImplicitly]
public class MembershipService : IMembershipService {
private readonly IOrchardServices _orchardServices;
- private readonly IMessageManager _messageManager;
+ private readonly IMessageService _messageService;
private readonly IEnumerable _userEventHandlers;
private readonly IEncryptionService _encryptionService;
+ private readonly IShapeFactory _shapeFactory;
+ private readonly IShapeDisplay _shapeDisplay;
- public MembershipService(IOrchardServices orchardServices, IMessageManager messageManager, IEnumerable userEventHandlers, IClock clock, IEncryptionService encryptionService) {
+ public MembershipService(
+ IOrchardServices orchardServices,
+ IMessageService messageService,
+ IEnumerable userEventHandlers,
+ IClock clock,
+ IEncryptionService encryptionService,
+ IShapeFactory shapeFactory,
+ IShapeDisplay shapeDisplay) {
_orchardServices = orchardServices;
- _messageManager = messageManager;
+ _messageService = messageService;
_userEventHandlers = userEventHandlers;
_encryptionService = encryptionService;
+ _shapeFactory = shapeFactory;
+ _shapeDisplay = shapeDisplay;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
@@ -51,7 +64,7 @@ namespace Orchard.Users.Services {
user.Record.Email = createUserParams.Email;
user.Record.NormalizedUserName = createUserParams.Username.ToLowerInvariant();
user.Record.HashAlgorithm = "SHA1";
- SetPassword(user.Record, createUserParams.Password);
+ SetPassword(user, createUserParams.Password);
if ( registrationSettings != null ) {
user.Record.RegistrationStatus = registrationSettings.UsersAreModerated ? UserStatus.Pending : UserStatus.Approved;
@@ -81,7 +94,10 @@ namespace Orchard.Users.Services {
}
}
- if ( registrationSettings != null && registrationSettings.UsersAreModerated && registrationSettings.NotifyModeration && !createUserParams.IsApproved ) {
+ if ( registrationSettings != null
+ && registrationSettings.UsersAreModerated
+ && registrationSettings.NotifyModeration
+ && !createUserParams.IsApproved ) {
var usernames = String.IsNullOrWhiteSpace(registrationSettings.NotificationsRecipients)
? new string[0]
: registrationSettings.NotificationsRecipients.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
@@ -91,8 +107,15 @@ namespace Orchard.Users.Services {
continue;
}
var recipient = GetUser(userName);
- if (recipient != null)
- _messageManager.Send(recipient.ContentItem.Record, MessageTypes.Moderation, "email" , new Dictionary { { "UserName", createUserParams.Username}, { "Email" , createUserParams.Email } });
+ if (recipient != null) {
+ var template = _shapeFactory.Create("Template_User_Moderated", Arguments.From(createUserParams));
+ var payload = new {
+ Subject = T("New account").Text,
+ Body = _shapeDisplay.Display(template),
+ Recipients = new [] { recipient.Email }
+ };
+ _messageService.Send("Email", JsonConvert.SerializeObject(payload));
+ }
}
}
@@ -113,7 +136,7 @@ namespace Orchard.Users.Services {
if (user == null)
user = _orchardServices.ContentManager.Query().Where(u => u.Email == lowerName).List().FirstOrDefault();
- if ( user == null || ValidatePassword(user.As().Record, password) == false )
+ if ( user == null || ValidatePassword(user.As(), password) == false )
return null;
if ( user.EmailStatus != UserStatus.Approved )
@@ -129,54 +152,50 @@ namespace Orchard.Users.Services {
if (!user.Is())
throw new InvalidCastException();
- var userRecord = user.As().Record;
- SetPassword(userRecord, password);
- }
+ var userPart = user.As();
-
- void SetPassword(UserPartRecord partRecord, string password) {
switch (GetSettings().PasswordFormat) {
case MembershipPasswordFormat.Clear:
- SetPasswordClear(partRecord, password);
+ SetPasswordClear(userPart, password);
break;
case MembershipPasswordFormat.Hashed:
- SetPasswordHashed(partRecord, password);
+ SetPasswordHashed(userPart, password);
break;
case MembershipPasswordFormat.Encrypted:
- SetPasswordEncrypted(partRecord, password);
+ SetPasswordEncrypted(userPart, password);
break;
default:
throw new ApplicationException(T("Unexpected password format value").ToString());
}
}
- private bool ValidatePassword(UserPartRecord partRecord, string password) {
+ private bool ValidatePassword(UserPart userPart, string password) {
// Note - the password format stored with the record is used
// otherwise changing the password format on the site would invalidate
// all logins
- switch (partRecord.PasswordFormat) {
+ switch (userPart.PasswordFormat) {
case MembershipPasswordFormat.Clear:
- return ValidatePasswordClear(partRecord, password);
+ return ValidatePasswordClear(userPart, password);
case MembershipPasswordFormat.Hashed:
- return ValidatePasswordHashed(partRecord, password);
+ return ValidatePasswordHashed(userPart, password);
case MembershipPasswordFormat.Encrypted:
- return ValidatePasswordEncrypted(partRecord, password);
+ return ValidatePasswordEncrypted(userPart, password);
default:
throw new ApplicationException("Unexpected password format value");
}
}
- private static void SetPasswordClear(UserPartRecord partRecord, string password) {
- partRecord.PasswordFormat = MembershipPasswordFormat.Clear;
- partRecord.Password = password;
- partRecord.PasswordSalt = null;
+ private static void SetPasswordClear(UserPart userPart, string password) {
+ userPart.PasswordFormat = MembershipPasswordFormat.Clear;
+ userPart.Password = password;
+ userPart.PasswordSalt = null;
}
- private static bool ValidatePasswordClear(UserPartRecord partRecord, string password) {
- return partRecord.Password == password;
+ private static bool ValidatePasswordClear(UserPart userPart, string password) {
+ return userPart.Password == password;
}
- private static void SetPasswordHashed(UserPartRecord partRecord, string password) {
+ private static void SetPasswordHashed(UserPart userPart, string password) {
var saltBytes = new byte[0x10];
using (var random = new RNGCryptoServiceProvider()) {
@@ -188,39 +207,39 @@ namespace Orchard.Users.Services {
var combinedBytes = saltBytes.Concat(passwordBytes).ToArray();
byte[] hashBytes;
- using (var hashAlgorithm = HashAlgorithm.Create(partRecord.HashAlgorithm)) {
+ using (var hashAlgorithm = HashAlgorithm.Create(userPart.HashAlgorithm)) {
hashBytes = hashAlgorithm.ComputeHash(combinedBytes);
}
- partRecord.PasswordFormat = MembershipPasswordFormat.Hashed;
- partRecord.Password = Convert.ToBase64String(hashBytes);
- partRecord.PasswordSalt = Convert.ToBase64String(saltBytes);
+ userPart.PasswordFormat = MembershipPasswordFormat.Hashed;
+ userPart.Password = Convert.ToBase64String(hashBytes);
+ userPart.PasswordSalt = Convert.ToBase64String(saltBytes);
}
- private static bool ValidatePasswordHashed(UserPartRecord partRecord, string password) {
+ private static bool ValidatePasswordHashed(UserPart userPart, string password) {
- var saltBytes = Convert.FromBase64String(partRecord.PasswordSalt);
+ var saltBytes = Convert.FromBase64String(userPart.PasswordSalt);
var passwordBytes = Encoding.Unicode.GetBytes(password);
var combinedBytes = saltBytes.Concat(passwordBytes).ToArray();
byte[] hashBytes;
- using (var hashAlgorithm = HashAlgorithm.Create(partRecord.HashAlgorithm)) {
+ using (var hashAlgorithm = HashAlgorithm.Create(userPart.HashAlgorithm)) {
hashBytes = hashAlgorithm.ComputeHash(combinedBytes);
}
-
- return partRecord.Password == Convert.ToBase64String(hashBytes);
+
+ return userPart.Password == Convert.ToBase64String(hashBytes);
}
- private void SetPasswordEncrypted(UserPartRecord partRecord, string password) {
- partRecord.Password = Convert.ToBase64String(_encryptionService.Encode(Encoding.UTF8.GetBytes(password)));
- partRecord.PasswordSalt = null;
- partRecord.PasswordFormat = MembershipPasswordFormat.Encrypted;
+ private void SetPasswordEncrypted(UserPart userPart, string password) {
+ userPart.Password = Convert.ToBase64String(_encryptionService.Encode(Encoding.UTF8.GetBytes(password)));
+ userPart.PasswordSalt = null;
+ userPart.PasswordFormat = MembershipPasswordFormat.Encrypted;
}
- private bool ValidatePasswordEncrypted(UserPartRecord partRecord, string password) {
- return String.Equals(password, Encoding.UTF8.GetString(_encryptionService.Decode(Convert.FromBase64String(partRecord.Password))), StringComparison.Ordinal);
+ private bool ValidatePasswordEncrypted(UserPart userPart, string password) {
+ return String.Equals(password, Encoding.UTF8.GetString(_encryptionService.Decode(Convert.FromBase64String(userPart.Password))), StringComparison.Ordinal);
}
}
}
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
index 5e3212912..06a8266e8 100644
--- a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs
@@ -1,9 +1,12 @@
using System;
using System.Linq;
-using System.Collections.Generic;
using JetBrains.Annotations;
+using Newtonsoft.Json;
+using Orchard.DisplayManagement;
+using Orchard.Localization;
using Orchard.Logging;
using Orchard.ContentManagement;
+using Orchard.Settings;
using Orchard.Users.Models;
using Orchard.Security;
using System.Xml.Linq;
@@ -22,19 +25,36 @@ namespace Orchard.Users.Services {
private readonly IContentManager _contentManager;
private readonly IMembershipService _membershipService;
private readonly IClock _clock;
- private readonly IMessageManager _messageManager;
+ private readonly IMessageService _messageService;
private readonly IEncryptionService _encryptionService;
+ private readonly IShapeFactory _shapeFactory;
+ private readonly IShapeDisplay _shapeDisplay;
+ private readonly ISiteService _siteService;
- public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings, IEncryptionService encryptionService) {
+ public UserService(
+ IContentManager contentManager,
+ IMembershipService membershipService,
+ IClock clock,
+ IMessageService messageService,
+ ShellSettings shellSettings,
+ IEncryptionService encryptionService,
+ IShapeFactory shapeFactory,
+ IShapeDisplay shapeDisplay,
+ ISiteService siteService
+ ) {
_contentManager = contentManager;
_membershipService = membershipService;
_clock = clock;
- _messageManager = messageManager;
+ _messageService = messageService;
_encryptionService = encryptionService;
+ _shapeFactory = shapeFactory;
+ _shapeDisplay = shapeDisplay;
+ _siteService = siteService;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
+ public Localizer T { get; set; }
public bool VerifyUserUnicity(string userName, string email) {
string normalizedUserName = userName.ToLowerInvariant();
@@ -111,7 +131,24 @@ namespace Orchard.Users.Services {
public void SendChallengeEmail(IUser user, Func createUrl) {
string nonce = CreateNonce(user, DelayToValidate);
string url = createUrl(nonce);
- _messageManager.Send(user.ContentItem.Record, MessageTypes.Validation, "email", new Dictionary { { "ChallengeUrl", url } });
+
+ if (user != null) {
+ var site = _siteService.GetSiteSettings();
+
+ var template = _shapeFactory.Create("Template_User_Validated", Arguments.From(new {
+ RegisteredWebsite = site.As().ValidateEmailRegisteredWebsite,
+ ContactEmail = site.As().ValidateEmailContactEMail,
+ ChallengeUrl = url
+ }));
+
+ var payload = new {
+ Subject = T("Verification E-Mail").Text,
+ Body = _shapeDisplay.Display(template),
+ Recipients = new[] { user.Email }
+ };
+
+ _messageService.Send("Email", JsonConvert.SerializeObject(payload));
+ }
}
public bool SendLostPasswordEmail(string usernameOrEmail, Func createUrl) {
@@ -122,7 +159,18 @@ namespace Orchard.Users.Services {
string nonce = CreateNonce(user, DelayToResetPassword);
string url = createUrl(nonce);
- _messageManager.Send(user.ContentItem.Record, MessageTypes.LostPassword, "email", new Dictionary { { "LostPasswordUrl", url } });
+ var template = _shapeFactory.Create("Template_User_LostPassword", Arguments.From(new {
+ User = user,
+ LostPasswordUrl = url
+ }));
+
+ var payload = new {
+ Subject = T("Lost password").Text,
+ Body = _shapeDisplay.Display(template),
+ Recipients = new[] { user.Email }
+ };
+
+ _messageService.Send("Email", JsonConvert.SerializeObject(payload));
return true;
}
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.LostPassword.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.LostPassword.cshtml
new file mode 100644
index 000000000..105d00c7a
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.LostPassword.cshtml
@@ -0,0 +1 @@
+@T("Dear {0}, please click here to change your password.", Model.User.UserName, Model.LostPasswordUrl)
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Moderated.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Moderated.cshtml
new file mode 100644
index 000000000..dee49e5ab
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Moderated.cshtml
@@ -0,0 +1 @@
+@T("The user {0} with email {1} has requested a new account. This user won't be able to log while his account has not been approved.", Model.Username, Model.Email)
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Validated.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Validated.cshtml
new file mode 100644
index 000000000..8e04109ba
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Template.User.Validated.cshtml
@@ -0,0 +1,4 @@
+@T("Thank you for registering with {0}.
Final Step
To verify that you own this e-mail address, please click the following link:
{1}
Troubleshooting:
If clicking on the link above does not work, try the following:
Select and copy the entire link.
Open a browser window and paste the link in the address bar.
Click Go or, on your keyboard, press Enter or Return.", Model.RegisteredWebsite, Model.ChallengeUrl)
+@if (!String.IsNullOrWhiteSpace(Model.ContactEmail)) {
+ @T("
If you continue to have access problems or want to report other issues, please Contact Us.",Model.ContactEmail)
+}