diff --git a/src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs b/src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs
index 276f601771..5e5f2c27f7 100644
--- a/src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs
+++ b/src/Umbraco.Core/Models/Identity/IIdentityUserLogin.cs
@@ -6,19 +6,16 @@ namespace Umbraco.Core.Models.Identity
{
///
/// The login provider for the login (i.e. Facebook, Google)
- ///
///
string LoginProvider { get; set; }
///
/// Key representing the login for the provider
- ///
///
string ProviderKey { get; set; }
///
/// User Id for the user who owns this login
- ///
///
int UserId { get; set; }
}
diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs
index 71e0dcf4a9..0d2af2118a 100644
--- a/src/Umbraco.Web/Editors/UsersController.cs
+++ b/src/Umbraco.Web/Editors/UsersController.cs
@@ -361,38 +361,8 @@ namespace Umbraco.Web.Editors
user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);
var userMgr = TryGetOwinContext().Result.GetBackOfficeUserManager();
- var inviteArgs = new UserInviteEventArgs(
- Request.TryGetHttpContext().Result.GetCurrentRequestIpAddress(),
- performingUser: Security.GetUserId().Result,
- userSave);
- userMgr.RaiseSendingUserInvite(inviteArgs);
-
- // If the event is handled then return the data
- if (inviteArgs.InviteHandled)
- {
- // if no local user was created then map the args manually for the UI
- if (inviteArgs.User == null)
- {
- return new UserDisplay
- {
- Name = userSave.Name,
- Email = userSave.Email,
- Username = userSave.Username
- };
- }
- else
- {
- //map the save info over onto the user
- user = Mapper.Map(userSave, user);
- //ensure the invited date is set
- user.InvitedDate = DateTime.Now;
- //Save the updated user
- Services.UserService.Save(user);
- return Mapper.Map(user);
- }
- }
-
- if (EmailSender.CanSendRequiredEmail == false)
+
+ if (!EmailSender.CanSendRequiredEmail && !userMgr.HasSendingUserInviteEventHandler)
{
throw new HttpResponseException(
Request.CreateNotificationValidationErrorResponse("No Email server is configured"));
@@ -430,16 +400,48 @@ namespace Umbraco.Web.Editors
//ensure the invited date is set
user.InvitedDate = DateTime.Now;
- //Save the updated user
+ //Save the updated user (which will process the user groups too)
Services.UserService.Save(user);
var display = Mapper.Map(user);
- //send the email
+ var inviteArgs = new UserInviteEventArgs(
+ Request.TryGetHttpContext().Result.GetCurrentRequestIpAddress(),
+ performingUser: Security.GetUserId().Result,
+ userSave,
+ user);
- await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message);
+ try
+ {
+ userMgr.RaiseSendingUserInvite(inviteArgs);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "An error occured in a custom event handler while inviting the user");
+ throw new HttpResponseException(
+ Request.CreateNotificationValidationErrorResponse($"An error occured inviting the user (check logs for more info): {ex.Message}"));
+ }
+
+ // If the event is handled then no need to send the email
+ if (inviteArgs.InviteHandled)
+ {
+ // if no user result was created then map the minimum args manually for the UI
+ if (!inviteArgs.ShowUserResult)
+ {
+ display = new UserDisplay
+ {
+ Name = userSave.Name,
+ Email = userSave.Email,
+ Username = userSave.Username
+ };
+ }
+ }
+ else
+ {
+ //send the email
+ await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message);
+ }
display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name }));
-
return display;
}
diff --git a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs
index 7564b670cb..7266e688c5 100644
--- a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs
+++ b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs
@@ -78,7 +78,7 @@ namespace Umbraco.Web
{
foreach (var error in externalLoginErrors.Errors)
{
- sb.AppendFormat(@"errors.push(""{0}"");", error).AppendLine();
+ sb.AppendFormat(@"errors.push(""{0}"");", error.ToSingleLine()).AppendLine();
}
}
diff --git a/src/Umbraco.Web/Security/BackOfficeUserManager.cs b/src/Umbraco.Web/Security/BackOfficeUserManager.cs
index b0e7125195..f91c128cde 100644
--- a/src/Umbraco.Web/Security/BackOfficeUserManager.cs
+++ b/src/Umbraco.Web/Security/BackOfficeUserManager.cs
@@ -634,6 +634,7 @@ namespace Umbraco.Web.Security
}
internal void RaiseSendingUserInvite(UserInviteEventArgs args) => OnSendingUserInvite(args);
+ internal bool HasSendingUserInviteEventHandler => SendingUserInvite != null;
// TODO: Not sure why these are not strongly typed events?? They should be in netcore!
public static event EventHandler AccountLocked;
diff --git a/src/Umbraco.Web/Security/UserInviteEventArgs.cs b/src/Umbraco.Web/Security/UserInviteEventArgs.cs
index 2cd0994c3c..9fb53a44c0 100644
--- a/src/Umbraco.Web/Security/UserInviteEventArgs.cs
+++ b/src/Umbraco.Web/Security/UserInviteEventArgs.cs
@@ -5,12 +5,16 @@ namespace Umbraco.Web.Security
{
public class UserInviteEventArgs : IdentityAuditEventArgs
{
- public UserInviteEventArgs(string ipAddress, int performingUser, UserInvite invitedUser, string comment = null)
+ public UserInviteEventArgs(string ipAddress, int performingUser, UserInvite invitedUser, IUser localUser, string comment = null)
: base(AuditEvent.SendingUserInvite, ipAddress, comment, performingUser)
{
InvitedUser = invitedUser ?? throw new System.ArgumentNullException(nameof(invitedUser));
+ User = localUser;
}
+ ///
+ /// The model used to invite the user
+ ///
public UserInvite InvitedUser { get; }
///
@@ -19,12 +23,14 @@ namespace Umbraco.Web.Security
public bool InviteHandled { get; set; }
///
- /// If the event handler has created a local user then this is the result which is used to return the details to the UI
+ /// The local user that has been created that is pending the invite
+ ///
+ public IUser User { get; }
+
+ ///
+ /// if set to true will show the edit user button in the UI, else it will not be shown
///
- ///
- /// It is optional to create a local user in this event. In many cases the custom invite flow will be for external logins and then local users will
- /// be created via the auto-linking process.
- ///
- public IUser User { get; set; }
+ public bool ShowUserResult { get; set; }
+
}
}