/// <summary>
        /// Invites a user
        /// </summary>
        /// <param name="userSave"></param>
        /// <returns></returns>
        /// <remarks>
        /// This will email the user an invite and generate a token that will be validated in the email
        /// </remarks>
        public async Task <UserDisplay> PostInviteUser(UserInvite userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException("userSave");
            }

            if (userSave.Message.IsNullOrWhiteSpace())
            {
                ModelState.AddModelError("Message", "Message cannot be empty");
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            IUser user;

            if (Current.Configs.Settings().Security.UsernameIsEmail)
            {
                //ensure it's the same
                userSave.Username = userSave.Email;
            }
            else
            {
                //first validate the username if we're showing it
                user = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);
            }
            user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);

            var userMgr = TryGetOwinContext().Result.GetBackOfficeUserManager();

            if (!EmailSender.CanSendRequiredEmail && !userMgr.HasSendingUserInviteEventHandler)
            {
                throw new HttpResponseException(
                          Request.CreateNotificationValidationErrorResponse("No Email server is configured"));
            }

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, user, null, null, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            if (user == null)
            {
                //we want to create the user with the UserManager, this ensures the 'empty' (special) password
                //format is applied without us having to duplicate that logic
                var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage);
                identityUser.Name = userSave.Name;

                var created = await UserManager.CreateAsync(identityUser);

                if (created.Succeeded == false)
                {
                    throw new HttpResponseException(
                              Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
                }

                //now re-look the user back up
                user = Services.UserService.GetByEmail(userSave.Email);
            }

            //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 (which will process the user groups too)
            Services.UserService.Save(user);
            var display = Mapper.Map <UserDisplay>(user);

            var inviteArgs = new UserInviteEventArgs(
                Request.TryGetHttpContext().Result.GetCurrentRequestIpAddress(),
                performingUser: Security.GetUserId().Result,
                userSave,
                user);

            try
            {
                userMgr.RaiseSendingUserInvite(inviteArgs);
            }
            catch (Exception ex)
            {
                Logger.Error <UsersController>(ex, "An error occurred in a custom event handler while inviting the user");
                throw new HttpResponseException(
                          Request.CreateNotificationValidationErrorResponse($"An error occurred 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);
        }
        /// <summary>
        /// Creates a new user
        /// </summary>
        /// <param name="userSave"></param>
        /// <returns></returns>
        public async Task <UserDisplay> PostCreateUser(UserInvite userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException("userSave");
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            if (Current.Configs.Settings().Security.UsernameIsEmail)
            {
                //ensure they are the same if we're using it
                userSave.Username = userSave.Email;
            }
            else
            {
                //first validate the username if were showing it
                CheckUniqueUsername(userSave.Username, null);
            }
            CheckUniqueEmail(userSave.Email, null);

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, null, null, null, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            //we want to create the user with the UserManager, this ensures the 'empty' (special) password
            //format is applied without us having to duplicate that logic
            var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage);

            identityUser.Name = userSave.Name;

            var created = await UserManager.CreateAsync(identityUser);

            if (created.Succeeded == false)
            {
                throw new HttpResponseException(
                          Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
            }

            //we need to generate a password, however we can only do that if the user manager has a password validator that
            //we can read values from
            var passwordValidator = UserManager.PasswordValidator as PasswordValidator;
            var resetPassword     = string.Empty;

            if (passwordValidator != null)
            {
                var password = UserManager.GeneratePassword();

                var result = await UserManager.AddPasswordAsync(identityUser.Id, password);

                if (result.Succeeded == false)
                {
                    throw new HttpResponseException(
                              Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
                }
                resetPassword = password;
            }

            //now re-look the user back up which will now exist
            var user = Services.UserService.GetByEmail(userSave.Email);

            //map the save info over onto the user
            user = Mapper.Map(userSave, user);

            //since the back office user is creating this user, they will be set to approved
            user.IsApproved = true;

            Services.UserService.Save(user);

            var display = Mapper.Map <UserDisplay>(user);

            display.ResetPasswordValue = resetPassword;
            return(display);
        }
        public async Task <UserDisplay> PostSaveUser(UserSave userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException("userSave");
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            var intId = userSave.Id.TryConvertTo <int>();

            if (intId.Success == false)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var found = Services.UserService.GetUserById(intId.Result);

            if (found == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            var hasErrors = false;

            var existing = Services.UserService.GetByEmail(userSave.Email);

            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user with the email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user with the username already exists");
                hasErrors = true;
            }
            // going forward we prefer to align usernames with email, so we should cross-check to make sure
            // the email or username isn't somehow being used by anyone.
            existing = Services.UserService.GetByEmail(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user using this as their email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Email);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user using this as their username already exists");
                hasErrors = true;
            }

            // if the found user has their email for username, we want to keep this synced when changing the email.
            // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here.
            if (Current.Configs.Settings().Security.UsernameIsEmail&& found.Username == found.Email && userSave.Username != userSave.Email)
            {
                userSave.Username = userSave.Email;
            }

            if (hasErrors)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            //merge the save data onto the user
            var user = Mapper.Map(userSave, found);

            Services.UserService.Save(user);

            var display = Mapper.Map <UserDisplay>(user);

            display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved"));
            return(display);
        }
        /// <summary>
        /// Invites a user
        /// </summary>
        /// <param name="userSave"></param>
        /// <returns></returns>
        /// <remarks>
        /// This will email the user an invite and generate a token that will be validated in the email
        /// </remarks>
        public async Task <UserDisplay> PostInviteUser(UserInvite userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException("userSave");
            }

            if (userSave.Message.IsNullOrWhiteSpace())
            {
                ModelState.AddModelError("Message", "Message cannot be empty");
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            if (EmailSender.CanSendRequiredEmail == false)
            {
                throw new HttpResponseException(
                          Request.CreateNotificationValidationErrorResponse("No Email server is configured"));
            }

            IUser user;

            if (Current.Configs.Settings().Security.UsernameIsEmail)
            {
                //ensure it's the same
                userSave.Username = userSave.Email;
            }
            else
            {
                //first validate the username if we're showing it
                user = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default(DateTime) || u.EmailConfirmedDate.HasValue);
            }
            user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default(DateTime) || u.EmailConfirmedDate.HasValue);

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, user, null, null, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            if (user == null)
            {
                //we want to create the user with the UserManager, this ensures the 'empty' (special) password
                //format is applied without us having to duplicate that logic
                var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage);
                identityUser.Name = userSave.Name;

                var created = await UserManager.CreateAsync(identityUser);

                if (created.Succeeded == false)
                {
                    throw new HttpResponseException(
                              Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors)));
                }

                //now re-look the user back up
                user = Services.UserService.GetByEmail(userSave.Email);
            }

            //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);
            var display = Mapper.Map <UserDisplay>(user);

            //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);
        }
        public UserDisplay PostSaveUser(UserSave userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException(nameof(userSave));
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            var intId = userSave.Id.TryConvertTo <int>();

            if (intId.Success == false)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var found = Services.UserService.GetUserById(intId.Result);

            if (found == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            var hasErrors = false;

            // we need to check if there's any Deny Local login providers present, if so we need to ensure that the user's email address cannot be changed
            var owinContext       = Request.TryGetOwinContext().Result;
            var hasDenyLocalLogin = owinContext.Authentication.HasDenyLocalLogin();

            if (hasDenyLocalLogin)
            {
                userSave.Email = found.Email; // it cannot change, this would only happen if people are mucking around with the request
            }

            var existing = Services.UserService.GetByEmail(userSave.Email);

            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user with the email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user with the username already exists");
                hasErrors = true;
            }
            // going forward we prefer to align usernames with email, so we should cross-check to make sure
            // the email or username isn't somehow being used by anyone.
            existing = Services.UserService.GetByEmail(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user using this as their email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Email);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user using this as their username already exists");
                hasErrors = true;
            }

            // if the found user has their email for username, we want to keep this synced when changing the email.
            // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here.
            if (Current.Configs.Settings().Security.UsernameIsEmail&& found.Username == found.Email && userSave.Username != userSave.Email)
            {
                userSave.Username = userSave.Email;
            }

            if (hasErrors)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            //merge the save data onto the user
            var user = Mapper.Map(userSave, found);

            Services.UserService.Save(user);

            var display = Mapper.Map <UserDisplay>(user);

            // determine if the user has changed their own language;
            var userHasChangedOwnLanguage =
                user.Id == Security.CurrentUser.Id && Security.CurrentUser.Language != user.Language;

            var textToLocalise = userHasChangedOwnLanguage ? "speechBubbles/operationSavedHeaderReloadUser" : "speechBubbles/operationSavedHeader";
            var culture        = userHasChangedOwnLanguage
                ? CultureInfo.GetCultureInfo(user.Language)
                : Thread.CurrentThread.CurrentUICulture;

            display.AddSuccessNotification(Services.TextService.Localize(textToLocalise, culture), Services.TextService.Localize("speechBubbles/editUserSaved", culture));
            return(display);
        }
        /// <summary>
        /// Saves a user
        /// </summary>
        /// <param name="userSave"></param>
        /// <returns></returns>
        public async Task <UserDisplay> PostSaveUser(UserSave userSave)
        {
            if (userSave == null)
            {
                throw new ArgumentNullException("userSave");
            }

            if (ModelState.IsValid == false)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            var intId = userSave.Id.TryConvertTo <int>();

            if (intId.Success == false)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var found = Services.UserService.GetUserById(intId.Result);

            if (found == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            //Perform authorization here to see if the current user can actually save this user with the info being requested
            var authHelper  = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
            var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups);

            if (canSaveUser == false)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
            }

            var hasErrors = false;

            var existing = Services.UserService.GetByEmail(userSave.Email);

            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user with the email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user with the username already exists");
                hasErrors = true;
            }
            // going forward we prefer to align usernames with email, so we should cross-check to make sure
            // the email or username isn't somehow being used by anyone.
            existing = Services.UserService.GetByEmail(userSave.Username);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Username", "A user using this as their email already exists");
                hasErrors = true;
            }
            existing = Services.UserService.GetByUsername(userSave.Email);
            if (existing != null && existing.Id != userSave.Id)
            {
                ModelState.AddModelError("Email", "A user using this as their username already exists");
                hasErrors = true;
            }

            // if the found user has his email for username, we want to keep this synced when changing the email.
            // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here.
            if (found.Username == found.Email && userSave.Username != userSave.Email)
            {
                userSave.Username = userSave.Email;
            }

            if (userSave.ChangePassword != null)
            {
                var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);

                var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager);

                if (passwordChangeResult.Success)
                {
                    var userMgr = this.TryGetOwinContext().Result.GetBackOfficeUserManager();

                    //raise the event - NOTE that the ChangePassword.Reset value here doesn't mean it's been 'reset', it means
                    //it's been changed by a back office user
                    if (userSave.ChangePassword.Reset.HasValue && userSave.ChangePassword.Reset.Value)
                    {
                        userMgr.RaisePasswordChangedEvent(intId.Result);
                    }

                    //need to re-get the user
                    found = Services.UserService.GetUserById(intId.Result);
                }
                else
                {
                    hasErrors = true;

                    foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames)
                    {
                        ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
                    }
                }
            }

            if (hasErrors)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
            }

            //merge the save data onto the user
            var user = Mapper.Map(userSave, found);

            Services.UserService.Save(user);

            var display = Mapper.Map <UserDisplay>(user);

            display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved"));
            return(display);
        }