diff --git a/Modules/UserManager/App_LocalResources/Impersonate.resx b/Modules/UserManager/App_LocalResources/Impersonate.resx new file mode 100644 index 0000000..c760b64 --- /dev/null +++ b/Modules/UserManager/App_LocalResources/Impersonate.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Are you sure you want to impersonate this user account? + + + Delete User Account + + + Impersonate + + + You are about to impersonate another user account. <b>If you confirm</b>, the website will attempt to log you in as if the user themselves logged in. You will need to logout and log back in to restore your superuser access. <b>If you cancel</b>, nothing will happen. + + + <b>Technical Limitation:</b> If you are using another authentication provider other than the built-in DNN provider, this feature may or may not work, depending on the type of authentication you have installed/enabled. + + + Impersonate User Account + + \ No newline at end of file diff --git a/Modules/UserManager/App_LocalResources/Index.resx b/Modules/UserManager/App_LocalResources/Index.resx index 192399b..d8e37dd 100644 --- a/Modules/UserManager/App_LocalResources/Index.resx +++ b/Modules/UserManager/App_LocalResources/Index.resx @@ -127,7 +127,7 @@ Create User - Bulk Delete User + Bulk Delete Users Create Role @@ -187,7 +187,7 @@ Username - User Roles + User's Roles USERS diff --git a/Modules/UserManager/App_LocalResources/Shared.resx b/Modules/UserManager/App_LocalResources/Shared.resx index 2eae841..9073c02 100644 --- a/Modules/UserManager/App_LocalResources/Shared.resx +++ b/Modules/UserManager/App_LocalResources/Shared.resx @@ -180,4 +180,7 @@ Only a superuser is allowed to bulk delete user accounts + + Cancel + \ No newline at end of file diff --git a/Modules/UserManager/Controllers/UserManageController.cs b/Modules/UserManager/Controllers/UserManageController.cs index b765e53..deaa9c0 100644 --- a/Modules/UserManager/Controllers/UserManageController.cs +++ b/Modules/UserManager/Controllers/UserManageController.cs @@ -534,5 +534,27 @@ public ActionResult PasswordResetLink(int itemId) TempData["Message"] = UserRepository.SendPasswordResetLink(portalId, itemId, portalSettings); return RedirectToAction("Index"); } + + /// + /// Impersonate a user + /// + /// + /// + public ActionResult ImpersonateUserById(int itemId) + { + if (_currentUser.IsSuperUser) + { + var user = UserController.GetUserById(PortalSettings.PortalId, itemId); + if (user != null) + { + // Perform impersonation + UserController.UserLogin(PortalSettings.PortalId, user, PortalSettings.PortalName, Request.UserHostAddress, false); + } + return Redirect(Url.Content("~/")); + } + string errorMessage = Localization.GetString("NotPermissions.Text", ResourceFile); + ViewBag.ErrorMessage = errorMessage; + return View("Error"); + } } } diff --git a/Modules/UserManager/Module.css b/Modules/UserManager/Module.css index a1a9207..87c7d6a 100644 --- a/Modules/UserManager/Module.css +++ b/Modules/UserManager/Module.css @@ -206,6 +206,7 @@ .checkbox { font-size: 16px; } + .admin-manager .checkbox { margin-top: 20px; } @@ -320,6 +321,11 @@ margin-bottom: 25px; } +.mtb-2 { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + .pb10 { padding-bottom: 10px !important; } @@ -726,4 +732,8 @@ line-height: 26px; border-top-left-radius: 0; border-bottom-left-radius: 0; +} + +.AM-white, .AM-white a, .AM-white a:visited { + color: white; } \ No newline at end of file diff --git a/Modules/UserManager/Properties/AssemblyInfo.cs b/Modules/UserManager/Properties/AssemblyInfo.cs index 0badaa7..a4dfad1 100644 --- a/Modules/UserManager/Properties/AssemblyInfo.cs +++ b/Modules/UserManager/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ // Build Number // Revision // -[assembly: AssemblyVersion("01.04.01")] -[assembly: AssemblyFileVersion("01.04.01")] +[assembly: AssemblyVersion("01.05.00")] +[assembly: AssemblyFileVersion("01.05.00")] diff --git a/Modules/UserManager/Providers/DataProviders/SqlDataProvider/01.05.00.SqlDataProvider b/Modules/UserManager/Providers/DataProviders/SqlDataProvider/01.05.00.SqlDataProvider new file mode 100644 index 0000000..81a367f --- /dev/null +++ b/Modules/UserManager/Providers/DataProviders/SqlDataProvider/01.05.00.SqlDataProvider @@ -0,0 +1,104 @@ +/************************************************************/ +/***** SqlDataProvider *****/ +/************************************************************/ + +IF OBJECT_ID('{databaseOwner}{objectQualifier}UUM_GetUsers', 'P') IS NOT NULL +BEGIN + EXEC('DROP PROCEDURE ' + '{databaseOwner}{objectQualifier}UUM_GetUsers'); +END +GO + +CREATE PROCEDURE {databaseOwner}{objectQualifier}UUM_GetUsers + @PageIndex INT, + @PageSize INT, + @SearchTerm NVARCHAR(100) = NULL, + @SortColumn NVARCHAR(50) = 'UserID', + @SortOrder NVARCHAR(4) = 'ASC', + @IsSuperUser BIT = NULL, + @Authorised BIT = NULL, + @PortalId INT = NULL, + @Deleted BIT = NULL, + @AllUsers BIT = NULL, + @TotalRecords INT OUTPUT +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @Offset INT; + SET @Offset = (@PageIndex - 1) * @PageSize; + + DECLARE @SortExpression NVARCHAR(100); + SET @SortExpression = QUOTENAME(@SortColumn) + ' ' + @SortOrder; + + DECLARE @Results TABLE + ( + UserID INT, + FirstName NVARCHAR(100), + Username NVARCHAR(100), + Email NVARCHAR(100), + DisplayName NVARCHAR(100), + IsSuperUser BIT, + RowNumber INT + ); + + -- Perform total count + IF @AllUsers = 0 + BEGIN + SELECT @TotalRecords = COUNT(*) + FROM [dbo].[Users] u + LEFT JOIN [dbo].[UserPortals] up ON u.UserID = up.UserID + WHERE (LOWER(u.[UserName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[FirstName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[Email]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[DisplayName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%') + AND u.IsDeleted = 0 + AND (@IsSuperUser IS NULL OR u.IsSuperUser = @IsSuperUser) + AND (@Deleted IS NULL OR up.IsDeleted = @Deleted) + AND (@Authorised IS NULL OR (up.Authorised = @Authorised AND up.IsDeleted = 0)) + AND (@PortalId IS NULL OR up.PortalId = @PortalId); + END + ELSE + BEGIN + SELECT @TotalRecords = COUNT(*) + FROM [dbo].[Users] u + LEFT JOIN [dbo].[UserPortals] up ON u.UserID = up.UserID + WHERE (LOWER(u.[UserName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[FirstName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[Email]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[DisplayName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%') + AND (@PortalId IS NULL OR up.PortalId = @PortalId); + END; + + -- Get paginated results + INSERT INTO @Results (UserID, FirstName, Username, Email, DisplayName, IsSuperUser, RowNumber) + SELECT u.UserID, u.FirstName, u.Username, u.Email, u.DisplayName, u.IsSuperUser, + ROW_NUMBER() OVER ( + ORDER BY + CASE WHEN @SortOrder = 'ASC' AND @SortColumn = 'FirstName' THEN u.FirstName END ASC, + CASE WHEN @SortOrder = 'DESC' AND @SortColumn = 'FirstName' THEN u.FirstName END DESC, + CASE WHEN @SortOrder = 'ASC' AND @SortColumn = 'Username' THEN u.UserName END ASC, + CASE WHEN @SortOrder = 'DESC' AND @SortColumn = 'Username' THEN u.UserName END DESC, + CASE WHEN @SortOrder = 'ASC' AND @SortColumn = 'Email' THEN u.Email END ASC, + CASE WHEN @SortOrder = 'DESC' AND @SortColumn = 'Email' THEN u.Email END DESC, + CASE WHEN @SortOrder = 'ASC' AND @SortColumn = 'DisplayName' THEN u.DisplayName END ASC, + CASE WHEN @SortOrder = 'DESC' AND @SortColumn = 'DisplayName' THEN u.DisplayName END DESC + ) AS RowNumber + FROM [dbo].[Users] u + LEFT JOIN [dbo].[UserPortals] up ON u.UserID = up.UserID + WHERE + (LOWER(u.[UserName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[FirstName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[Email]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%' OR + LOWER(u.[DisplayName]) COLLATE Latin1_General_CI_AI LIKE '%' + LOWER(@SearchTerm) COLLATE Latin1_General_CI_AI + '%') + AND u.IsDeleted = 0 + AND (@IsSuperUser IS NULL OR u.IsSuperUser = @IsSuperUser) + AND (@Deleted IS NULL OR up.IsDeleted = @Deleted) + AND (@Authorised IS NULL OR (up.Authorised = @Authorised AND up.IsDeleted = 0)) + AND (@PortalId IS NULL OR up.PortalId = @PortalId) + ORDER BY RowNumber; + + -- Return paginated results + SELECT UserID, FirstName, Username, Email, DisplayName, IsSuperUser + FROM @Results + WHERE RowNumber BETWEEN (@Offset + 1) AND (@Offset + @PageSize); +END; \ No newline at end of file diff --git a/Modules/UserManager/Upendo.Modules.UserManager.csproj b/Modules/UserManager/Upendo.Modules.UserManager.csproj index 231dde6..5d525fc 100644 --- a/Modules/UserManager/Upendo.Modules.UserManager.csproj +++ b/Modules/UserManager/Upendo.Modules.UserManager.csproj @@ -1,4 +1,4 @@ - + @@ -13,7 +13,7 @@ Properties Upendo.Modules.UserManager Upendo.Modules.UserManager - v4.7.2 + v4.8 false @@ -75,10 +75,6 @@ - - - - @@ -176,6 +172,7 @@ + @@ -186,6 +183,9 @@ + + + 14.0 diff --git a/Modules/UserManager/Upendo.UserManager.dnn b/Modules/UserManager/Upendo.UserManager.dnn index ce6de2d..b42899a 100644 --- a/Modules/UserManager/Upendo.UserManager.dnn +++ b/Modules/UserManager/Upendo.UserManager.dnn @@ -1,7 +1,7 @@  - + Upendo DNN User Manager The Upendo DNN User Manager empowers authorized end-users in your DNN website to be able to manage user accounts and their assigned security roles.

]]>
DesktopModules/MVC/Upendo.Modules.UserManager/Images/logo.png @@ -60,7 +60,7 @@ Upendo.Modules.UserManager.Components.UserManagerController, Upendo.Modules.UserManager [DESKTOPMODULEID] - 01.00.00,01.01.00,01.01.01,01.02.00,01.03.00,01.04.00,01.04.01 + 01.00.00,01.01.00,01.01.01,01.02.00,01.03.00,01.04.00,01.04.01,01.05.00 @@ -69,7 +69,7 @@ Upendo.Modules.UserManager.dll bin - 01.04.01 + 01.05.00 @@ -89,10 +89,15 @@ 01.01.01.SqlDataProvider 01.01.01 + diff --git a/Modules/UserManager/Upendo.UserManager_Symbols.dnn b/Modules/UserManager/Upendo.UserManager_Symbols.dnn index d2efd6e..4f68b39 100644 --- a/Modules/UserManager/Upendo.UserManager_Symbols.dnn +++ b/Modules/UserManager/Upendo.UserManager_Symbols.dnn @@ -1,7 +1,7 @@  - + Upendo DNN User Manager Symbols @@ -14,7 +14,7 @@ True - Upendo.Modules.UserManager + Upendo.Modules.UserManager diff --git a/Modules/UserManager/Utility/Functions.cs b/Modules/UserManager/Utility/Functions.cs index 40a203f..2e97bcc 100644 --- a/Modules/UserManager/Utility/Functions.cs +++ b/Modules/UserManager/Utility/Functions.cs @@ -31,6 +31,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using DotNetNuke.Instrumentation; using DotNetNuke.Services.Localization; using DotNetNuke.Security.Roles; +using System.Globalization; +using System.Text; namespace Upendo.Modules.UserManager.Utility { @@ -127,7 +129,7 @@ public static DataTableResponse GetUsersProcedure(Pagination pagination, using (var connection = new SqlConnection(connectionString)) { var pageIndex = pagination.PageIndex == 0 ? 1 : pagination.PageIndex; - var search = string.IsNullOrWhiteSpace(pagination.Search) ? "" : pagination.Search; + var search = RemoveDiacritics(string.IsNullOrWhiteSpace(pagination.Search) ? "" : pagination.Search.Replace(" ", string.Empty).ToLower()); var totalRecordsParameter = new SqlParameter("@TotalRecords", SqlDbType.Int); totalRecordsParameter.Direction = ParameterDirection.Output; var command = new SqlCommand("UUM_GetUsers", connection); @@ -254,5 +256,22 @@ public static bool HasPermission(DotNetNuke.UI.Modules.ModuleInstanceContext Mod ModulePermissionCollection permissions = ModulePermissionController.GetModulePermissions(moduleId, tabId); return ModulePermissionController.HasModulePermission(permissions, "EDIT"); } + + private static string RemoveDiacritics(string text) + { + var normalizedString = text.Normalize(NormalizationForm.FormD); + var stringBuilder = new StringBuilder(); + + foreach (var c in normalizedString) + { + var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); + if (unicodeCategory != UnicodeCategory.NonSpacingMark) + { + stringBuilder.Append(c); + } + } + + return stringBuilder.ToString().Normalize(NormalizationForm.FormC); + } } } \ No newline at end of file diff --git a/Modules/UserManager/Utility/UserRepository.cs b/Modules/UserManager/Utility/UserRepository.cs index 6d04d96..550eeea 100644 --- a/Modules/UserManager/Utility/UserRepository.cs +++ b/Modules/UserManager/Utility/UserRepository.cs @@ -286,7 +286,6 @@ public static void EditUser(int portalId, UserViewModel user) userInfo.FirstName = user.FirstName; userInfo.LastName = user.LastName; userInfo.Email = user.Email; - userInfo.Username = user.Username; userInfo.DisplayName = user.DisplayName; userInfo.PortalID = portalId; userInfo.IsSuperUser = currentUser.IsSuperUser ? user.IsSuperUser : false; @@ -302,6 +301,10 @@ public static void EditUser(int portalId, UserViewModel user) userInfo.Roles[userInfo.Roles.Count() - 1] = user.NewUserRol; } UserController.UpdateUser(portalId, userInfo); + if (userInfo.Username != user.Username) + { + UserController.ChangeUsername(user.UserId, user.Username); + } } /// diff --git a/Modules/UserManager/Views/UserManage/Index.cshtml b/Modules/UserManager/Views/UserManage/Index.cshtml index 143d849..0b3ffe0 100644 --- a/Modules/UserManager/Views/UserManage/Index.cshtml +++ b/Modules/UserManager/Views/UserManage/Index.cshtml @@ -26,10 +26,11 @@