diff --git a/Oqtane.Client/Modules/Admin/Users/Users.razor b/Oqtane.Client/Modules/Admin/Users/Users.razor index 9f6b3401..cc7c6d93 100644 --- a/Oqtane.Client/Modules/Admin/Users/Users.razor +++ b/Oqtane.Client/Modules/Admin/Users/Users.razor @@ -7,16 +7,16 @@
- +
- +

  @SharedLocalizer["Cancel"]  -@Localizer["Template"] +@Localizer["Template"] @code { private FileManager _filemanager; @@ -32,14 +32,17 @@ var fileid = _filemanager.GetFileId(); if (fileid != -1) { - if (await UserService.ImportUsersAsync(PageState.Site.SiteId, fileid)) + ShowProgressIndicator(); + var results = await UserService.ImportUsersAsync(PageState.Site.SiteId, fileid); + if (bool.Parse(results["Success"])) { - AddModuleMessage(Localizer["Message.Import.Success"], MessageType.Success); + AddModuleMessage(string.Format(Localizer["Message.Import.Success"], results["Rows"], results["Users"]), MessageType.Success); } else { AddModuleMessage(Localizer["Message.Import.Failure"], MessageType.Error); } + HideProgressIndicator(); } else { diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Users.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Users.resx index b18df840..86c51ccc 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Users.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Users.resx @@ -117,11 +117,11 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Select or upload a CSV file containing user information. The CSV file must be in the Template format specified. + + Upload or select a tab delimited text file containing user information. The file must be in the Template format specified (Roles can be specified as a comma delimited list). - - User File: + + Import File: Error Importing Users @@ -130,10 +130,10 @@ Import - User Import Failed + User Import Failed. Please Review Your Event Log For More Detailed Information. - Users Imported Successfully + Users Imported Successfully. {0} Rows Processed, {1} Users Imported. You Must Specify A User File For Import diff --git a/Oqtane.Client/Services/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index 04661e8f..7fe46e52 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -148,6 +148,6 @@ namespace Oqtane.Services /// /// ID of a /// - Task ImportUsersAsync(int siteId, int fileId); + Task> ImportUsersAsync(int siteId, int fileId); } } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index 770c4eb1..08700cb8 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -127,9 +127,9 @@ namespace Oqtane.Services return string.Format(passwordValidationCriteriaTemplate, minimumlength, uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement); } - public async Task ImportUsersAsync(int siteId, int fileId) + public async Task> ImportUsersAsync(int siteId, int fileId) { - return await PostJsonAsync($"{Apiurl}/import?siteid={siteId}&fileid={fileId}", true); + return await PostJsonAsync>($"{Apiurl}/import?siteid={siteId}&fileid={fileId}", null); } } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 3788817c..1ec787cf 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -375,7 +375,7 @@ namespace Oqtane.Controllers // POST api//import?siteid=x&fileid=y [HttpPost("import")] [Authorize(Roles = RoleNames.Admin)] - public async Task Import(string siteid, string fileid) + public async Task> Import(string siteid, string fileid) { if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId && int.TryParse(fileid, out int FileId)) { @@ -385,7 +385,7 @@ namespace Oqtane.Controllers { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Import Attempt {SiteId} {FileId}", siteid, fileid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; - return false; + return null; } } } diff --git a/Oqtane.Server/Managers/Interfaces/IUserManager.cs b/Oqtane.Server/Managers/Interfaces/IUserManager.cs index 7eb79e78..1a4c971f 100644 --- a/Oqtane.Server/Managers/Interfaces/IUserManager.cs +++ b/Oqtane.Server/Managers/Interfaces/IUserManager.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Oqtane.Models; @@ -18,6 +19,6 @@ namespace Oqtane.Managers User VerifyTwoFactor(User user, string token); Task LinkExternalAccount(User user, string token, string type, string key, string name); Task ValidatePassword(string password); - Task ImportUsers(int siteId, int fileId); + Task> ImportUsers(int siteId, int fileId); } } diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs index 49f2cf01..fdf4f065 100644 --- a/Oqtane.Server/Managers/UserManager.cs +++ b/Oqtane.Server/Managers/UserManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -461,9 +460,10 @@ namespace Oqtane.Managers return result.Succeeded; } - public async Task ImportUsers(int siteId, int fileId) + public async Task> ImportUsers(int siteId, int fileId) { var success = true; + int rows = 0; int users = 0; var file = _files.GetFile(fileId); @@ -477,127 +477,160 @@ namespace Oqtane.Managers try { - string row; + string row = ""; using (var reader = new StreamReader(path)) { - // get header row - row = reader.ReadLine(); - var header = row.Replace("\"", "").Split(','); - - row = reader.ReadLine(); - while (row != null) + // header row + if (reader.Peek() > -1) { - var values = row.Replace("\"", "").Split(','); + row = reader.ReadLine(); + } - if (values.Length > 3) + if (!string.IsNullOrEmpty(row.Trim())) + { + var header = row.Replace("\"", "").Split('\t'); + + // detail rows + while (reader.Peek() > -1) { - // user - var user = _users.GetUser(values[1], values[0]); - if (user == null) + row = reader.ReadLine(); + rows++; + + if (!string.IsNullOrEmpty(row.Trim())) { - user = new User(); - user.SiteId = siteId; - user.Email = values[0]; - user.Username = (!string.IsNullOrEmpty(values[1])) ? values[1] : user.Email; - user.DisplayName = (!string.IsNullOrEmpty(values[2])) ? values[2] : user.Username; - user = await AddUser(user); + var values = row.Replace("\"", "").Split('\t'); + + // user + var email = (values.Length > 0) ? values[0].Trim() : ""; + var username = (values.Length > 1) ? values[1].Trim() : ""; + var displayname = (values.Length > 2) ? values[2].Trim() : ""; + + var user = _users.GetUser(username, email); if (user == null) { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Creating User {Email}", values[0]); - success = false; - } - } - - if (user != null && !string.IsNullOrEmpty(values[3])) - { - // roles (comma delimited) - foreach (var rolename in values[3].Split(',')) - { - var role = roles.FirstOrDefault(item => item.Name == rolename); - if (role == null) + user = new User(); + user.SiteId = siteId; + user.Email = values[0]; + user.Username = (!string.IsNullOrEmpty(username)) ? username : user.Email; + user.DisplayName = (!string.IsNullOrEmpty(displayname)) ? displayname : user.Username; + user = await AddUser(user); + if (user == null) { - role = new Role(); - role.SiteId = siteId; - role.Name = rolename; - role.Description = rolename; - role = _roles.AddRole(role); - roles.Add(role); + _logger.Log(LogLevel.Error, this, LogFunction.Create, "Error Importing User {Email} {Username} {DisplayName}", email, username, displayname); + success = false; } - if (role != null) + } + else + { + if (!string.IsNullOrEmpty(displayname)) { - var userrole = _userRoles.GetUserRole(user.UserId, role.RoleId, false); - if (userrole == null) + user.DisplayName = displayname; + user = await UpdateUser(user); + } + } + + var rolenames = (values.Length > 3) ? values[3].Trim() : ""; + if (user != null && !string.IsNullOrEmpty(rolenames)) + { + // roles (comma delimited) + foreach (var rolename in rolenames.Split(',')) + { + var role = roles.FirstOrDefault(item => item.Name == rolename.Trim()); + if (role == null) { - userrole = new UserRole(); - userrole.UserId = user.UserId; - userrole.RoleId = role.RoleId; - _userRoles.AddUserRole(userrole); + role = new Role(); + role.SiteId = siteId; + role.Name = rolename.Trim(); + role.Description = rolename.Trim(); + role = _roles.AddRole(role); + roles.Add(role); + } + if (role != null) + { + var userrole = _userRoles.GetUserRole(user.UserId, role.RoleId, false); + if (userrole == null) + { + userrole = new UserRole(); + userrole.UserId = user.UserId; + userrole.RoleId = role.RoleId; + _userRoles.AddUserRole(userrole); + } } } } - } - if (user != null && values.Length > 4) - { - var settings = _settings.GetSettings(EntityNames.User, user.UserId); - for (int index = 4; index < values.Length - 1; index++) + if (user != null && values.Length > 4) { - if (header.Length > index && !string.IsNullOrEmpty(values[index])) + // profiles + var settings = _settings.GetSettings(EntityNames.User, user.UserId); + for (int index = 4; index < values.Length - 1; index++) { - var profile = profiles.FirstOrDefault(item => item.Name == header[index]); - if (profile != null) + if (header.Length > index && !string.IsNullOrEmpty(values[index].Trim())) { - var setting = settings.FirstOrDefault(item => item.SettingName == profile.Name); - if (setting == null) + var profile = profiles.FirstOrDefault(item => item.Name == header[index].Trim()); + if (profile != null) { - setting = new Setting(); - setting.EntityName = EntityNames.User; - setting.EntityId = user.UserId; - setting.SettingName = profile.Name; - setting.SettingValue = values[index]; - _settings.AddSetting(setting); - } - else - { - if (setting.SettingValue != values[index]) + var setting = settings.FirstOrDefault(item => item.SettingName == profile.Name); + if (setting == null) { - setting.SettingValue = values[index]; - _settings.UpdateSetting(setting); + setting = new Setting(); + setting.EntityName = EntityNames.User; + setting.EntityId = user.UserId; + setting.SettingName = profile.Name; + setting.SettingValue = values[index].Trim(); + _settings.AddSetting(setting); + } + else + { + if (setting.SettingValue != values[index].Trim()) + { + setting.SettingValue = values[index].Trim(); + _settings.UpdateSetting(setting); + } } } } } } + + users++; } - - users++; } - - row = reader.ReadLine(); + } + else + { + success = false; + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Import File Contains No Header Row"); } } - _logger.Log(LogLevel.Information, this, LogFunction.Create, "{Users} Users Imported", users); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Import: {Rows} Rows Processed, {Users} Users Imported", rows, users); } catch (Exception ex) { - _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Error Importing User Import File {SiteId} {FileId}", siteId, fileId); success = false; + _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Error Importing User Import File {SiteId} {FileId}", siteId, fileId); } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create,"User Import File Does Not Exist {Path}", path); success = false; + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Import File Does Not Exist {Path}", path); } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Import File Does Not Exist {SiteId} {FileId}", siteId, fileId); success = false; + _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Import File Does Not Exist {SiteId} {FileId}", siteId, fileId); } - return success; + // return results + var result = new Dictionary(); + result.Add("Success", success.ToString()); + result.Add("Rows", rows.ToString()); + result.Add("Users", users.ToString()); + + return result; } } } diff --git a/Oqtane.Server/wwwroot/users.csv b/Oqtane.Server/wwwroot/users.csv deleted file mode 100644 index ff6dcff3..00000000 --- a/Oqtane.Server/wwwroot/users.csv +++ /dev/null @@ -1 +0,0 @@ -Email,Username,DisplayName,Roles,FirstName,LastName,Street,City,Region,Country,PostalCode,Phone diff --git a/Oqtane.Server/wwwroot/users.txt b/Oqtane.Server/wwwroot/users.txt new file mode 100644 index 00000000..b1d55c74 --- /dev/null +++ b/Oqtane.Server/wwwroot/users.txt @@ -0,0 +1 @@ +Email Username DisplayName Roles FirstName LastName Street City Region Country PostalCode Phone