localized usermanager email messages and message formatting to include site name and link #3794
This commit is contained in:
parent
df2aac3946
commit
b68fc6187f
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
|
@ -29,8 +30,10 @@ namespace Oqtane.Managers
|
||||||
private readonly ISettingRepository _settings;
|
private readonly ISettingRepository _settings;
|
||||||
private readonly ISyncManager _syncManager;
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
|
private readonly IStringLocalizer<UserManager> _localizer;
|
||||||
|
private readonly ISiteRepository _siteRepo;
|
||||||
|
|
||||||
public UserManager(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, IFileRepository files, IProfileRepository profiles, ISettingRepository settings, ISyncManager syncManager, ILogManager logger)
|
public UserManager(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, IFileRepository files, IProfileRepository profiles, ISettingRepository settings, ISyncManager syncManager, ILogManager logger, IStringLocalizer<UserManager> localizer, ISiteRepository siteRepo)
|
||||||
{
|
{
|
||||||
_users = users;
|
_users = users;
|
||||||
_roles = roles;
|
_roles = roles;
|
||||||
|
@ -45,6 +48,8 @@ namespace Oqtane.Managers
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_syncManager = syncManager;
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_localizer = localizer;
|
||||||
|
_siteRepo = siteRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User GetUser(int userid, int siteid)
|
public User GetUser(int userid, int siteid)
|
||||||
|
@ -148,21 +153,33 @@ namespace Oqtane.Managers
|
||||||
|
|
||||||
if (User != null)
|
if (User != null)
|
||||||
{
|
{
|
||||||
|
string siteName = _siteRepo.GetSite(user.SiteId).Name;
|
||||||
if (!user.EmailConfirmed)
|
if (!user.EmailConfirmed)
|
||||||
{
|
{
|
||||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
string subject = _localizer["VerificationEmailSubject"];
|
||||||
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
|
string body = _localizer["VerificationEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
var notification = new Notification(alias.SiteId, User, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!user.SuppressNotification)
|
if (!user.SuppressNotification)
|
||||||
{
|
{
|
||||||
string url = alias.Protocol + alias.Name;
|
string url = alias.Protocol + alias.Name + "/login";
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You With The Username " + user.Username + ". Please Visit " + url + " And Use The Login Option To Sign In. If You Do Not Know Your Password, Use The Forgot Password Option On The Login Page To Reset Your Account.\n\nThank You!";
|
string subject = _localizer["NoVerificationEmailSubject"];
|
||||||
var notification = new Notification(user.SiteId, User, "User Account Notification", body);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
|
string body = _localizer["NoVerificationEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
body = body.Replace("[Username]", user.Username);
|
||||||
|
var notification = new Notification(alias.SiteId, User, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,11 +324,17 @@ namespace Oqtane.Managers
|
||||||
user.TwoFactorCode = token;
|
user.TwoFactorCode = token;
|
||||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nYou requested a secure verification code to log in to your account. Please enter the secure verification code on the site:\n\n" + token +
|
string url = alias.Protocol + alias.Name;
|
||||||
"\n\nPlease note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site." +
|
string siteName = _siteRepo.GetSite(alias.SiteId).Name;
|
||||||
"\n\nThank You!";
|
string subject = _localizer["TwoFactorEmailSubject"];
|
||||||
var notification = new Notification(user.SiteId, user, "User Verification Code", body);
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
|
string body = _localizer["TwoFactorEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
body = body.Replace("[Token]", token);
|
||||||
|
var notification = new Notification(alias.SiteId, user, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
||||||
|
@ -355,10 +378,14 @@ namespace Oqtane.Managers
|
||||||
user = _users.GetUser(user.Username);
|
user = _users.GetUser(user.Username);
|
||||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||||
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
string siteName = _siteRepo.GetSite(alias.SiteId).Name;
|
||||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
string subject = _localizer["UserLockoutEmailSubject"];
|
||||||
"\n\nThank You!";
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
var notification = new Notification(user.SiteId, user, "User Lockout", body);
|
string body = _localizer["UserLockoutEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
var notification = new Notification(alias.SiteId, user, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
||||||
}
|
}
|
||||||
|
@ -404,12 +431,14 @@ namespace Oqtane.Managers
|
||||||
user = _users.GetUser(user.Username);
|
user = _users.GetUser(user.Username);
|
||||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||||
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
string siteName = _siteRepo.GetSite(alias.SiteId).Name;
|
||||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
string subject = _localizer["ForgotPasswordEmailSubject"];
|
||||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
subject = subject.Replace("[SiteName]", siteName);
|
||||||
"\n\nThank You!";
|
string body = _localizer["ForgotPasswordEmailBody"].Value;
|
||||||
|
body = body.Replace("[UserDisplayName]", user.DisplayName);
|
||||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, "User Password Reset", body);
|
body = body.Replace("[URL]", url);
|
||||||
|
body = body.Replace("[SiteName]", siteName);
|
||||||
|
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, subject, body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||||
}
|
}
|
||||||
|
|
150
Oqtane.Server/Resources/Managers.UserManager.resx
Normal file
150
Oqtane.Server/Resources/Managers.UserManager.resx
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="ForgotPasswordEmailBody" xml:space="preserve">
|
||||||
|
<value>Dear [UserDisplayName]<br><br>You recently requested to reset your password. Please use the link below to complete the process: <b><a href="[URL]"><br><br>Click here to Reset Password</a></b><br><br>Please note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site.<br><br>If you did not request to reset your password you can safely ignore this message.<br><br>Thank You!<br>[SiteName] team</value>
|
||||||
|
</data>
|
||||||
|
<data name="ForgotPasswordEmailSubject" xml:space="preserve">
|
||||||
|
<value>Password Reset Notification Sent For [SiteName]</value>
|
||||||
|
</data>
|
||||||
|
<data name="NoVerificationEmailBody" xml:space="preserve">
|
||||||
|
<value>Dear [UserDisplayName],<br><br>A user account has been successfully created for you with the username <b>[Username]</b>. Please <b><a href="[URL]">click here to login</a></b>. If you do not know your password, use the forgot password option on the login page to reset your account.<br><br>Thank You!<br>[SiteName] Team</value>
|
||||||
|
</data>
|
||||||
|
<data name="NoVerificationEmailSubject" xml:space="preserve">
|
||||||
|
<value>User Account Notification for [SiteName]</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactorEmailBody" xml:space="preserve">
|
||||||
|
<value>Dear [UserDisplayName] + ",<br><br>You requested a secure verification code to log in to your account. Please enter the secure verification code on the site:<br><br><b>[Token] </b><br><br>Please note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the [Alias].<br><br>Thank You!<br>[SiteName] Team"</value>
|
||||||
|
</data>
|
||||||
|
<data name="TwoFactorEmailSubject" xml:space="preserve">
|
||||||
|
<value>User Verification Code for [SiteName]</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserLockoutEmailBody" xml:space="preserve">
|
||||||
|
<value>Dear [UserDisplayName], <br><br>You attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:<br><br> <b><a href=[URL]>Reset Password</a></b><br><br>Please note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site <a href="[SiteURL]">[SiteName]</a>.<br><br>Thank You!<br>[SiteName] Team</value>
|
||||||
|
</data>
|
||||||
|
<data name="UserLockoutEmailSubject" xml:space="preserve">
|
||||||
|
<value>User Lockout Notification for [SiteName]</value>
|
||||||
|
</data>
|
||||||
|
<data name="VerificationEmailBody" xml:space="preserve">
|
||||||
|
<value>Dear [UserDisplayName],<br><br>In order to verify the email address associated to your user account, please click the link below:<br><br> <b><a href="[URL]">Click Here To Verify</a></b> <br><br>If the link is not displayed please copy and paste the following link to your browser <br><br> [URL] <br><br>Thank You!<br>[SiteName] Team</value>
|
||||||
|
</data>
|
||||||
|
<data name="VerificationEmailSubject" xml:space="preserve">
|
||||||
|
<value>Email Verification for [SiteName]</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
Loading…
Reference in New Issue
Block a user