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